From 95e8c48dd3348503a8c7db5d0498894a1b676395 Mon Sep 17 00:00:00 2001 From: eregon Date: Sun, 7 May 2017 12:04:49 +0000 Subject: Add in-tree mspec and ruby/spec * For easier modifications of ruby/spec by MRI developers. * .gitignore: track changes under spec. * spec/mspec, spec/rubyspec: add in-tree mspec and ruby/spec. These files can therefore be updated like any other file in MRI. Instructions are provided in spec/README. [Feature #13156] [ruby-core:79246] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58595 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- .gitignore | 4 - spec/mspec/.gitignore | 26 + spec/mspec/.travis.yml | 9 + spec/mspec/Gemfile | 4 + spec/mspec/Gemfile.lock | 30 + spec/mspec/LICENSE | 22 + spec/mspec/README.md | 88 + spec/mspec/Rakefile | 7 + spec/mspec/bin/mkspec | 7 + spec/mspec/bin/mkspec.bat | 1 + spec/mspec/bin/mspec | 7 + spec/mspec/bin/mspec-ci | 7 + spec/mspec/bin/mspec-ci.bat | 1 + spec/mspec/bin/mspec-run | 7 + spec/mspec/bin/mspec-run.bat | 1 + spec/mspec/bin/mspec-tag | 7 + spec/mspec/bin/mspec-tag.bat | 1 + spec/mspec/bin/mspec.bat | 1 + spec/mspec/lib/mspec.rb | 20 + spec/mspec/lib/mspec/commands/mkspec.rb | 155 + spec/mspec/lib/mspec/commands/mspec-ci.rb | 79 + spec/mspec/lib/mspec/commands/mspec-run.rb | 87 + spec/mspec/lib/mspec/commands/mspec-tag.rb | 133 + spec/mspec/lib/mspec/commands/mspec.rb | 163 + spec/mspec/lib/mspec/expectations.rb | 2 + spec/mspec/lib/mspec/expectations/expectations.rb | 21 + spec/mspec/lib/mspec/expectations/should.rb | 29 + spec/mspec/lib/mspec/guards.rb | 12 + spec/mspec/lib/mspec/guards/block_device.rb | 18 + spec/mspec/lib/mspec/guards/bug.rb | 30 + spec/mspec/lib/mspec/guards/conflict.rb | 19 + spec/mspec/lib/mspec/guards/endian.rb | 27 + spec/mspec/lib/mspec/guards/feature.rb | 43 + spec/mspec/lib/mspec/guards/guard.rb | 118 + spec/mspec/lib/mspec/guards/platform.rb | 78 + spec/mspec/lib/mspec/guards/quarantine.rb | 13 + spec/mspec/lib/mspec/guards/superuser.rb | 17 + spec/mspec/lib/mspec/guards/support.rb | 16 + spec/mspec/lib/mspec/guards/version.rb | 39 + spec/mspec/lib/mspec/helpers.rb | 12 + spec/mspec/lib/mspec/helpers/argf.rb | 37 + spec/mspec/lib/mspec/helpers/argv.rb | 46 + spec/mspec/lib/mspec/helpers/datetime.rb | 51 + spec/mspec/lib/mspec/helpers/fixture.rb | 26 + spec/mspec/lib/mspec/helpers/flunk.rb | 5 + spec/mspec/lib/mspec/helpers/fs.rb | 62 + spec/mspec/lib/mspec/helpers/io.rb | 113 + spec/mspec/lib/mspec/helpers/mock_to_path.rb | 8 + spec/mspec/lib/mspec/helpers/numeric.rb | 72 + spec/mspec/lib/mspec/helpers/ruby_exe.rb | 178 + spec/mspec/lib/mspec/helpers/scratch.rb | 17 + spec/mspec/lib/mspec/helpers/tmp.rb | 45 + spec/mspec/lib/mspec/matchers.rb | 35 + spec/mspec/lib/mspec/matchers/base.rb | 95 + spec/mspec/lib/mspec/matchers/be_an_instance_of.rb | 26 + spec/mspec/lib/mspec/matchers/be_ancestor_of.rb | 24 + spec/mspec/lib/mspec/matchers/be_close.rb | 27 + spec/mspec/lib/mspec/matchers/be_computed_by.rb | 37 + spec/mspec/lib/mspec/matchers/be_empty.rb | 20 + spec/mspec/lib/mspec/matchers/be_false.rb | 20 + spec/mspec/lib/mspec/matchers/be_kind_of.rb | 24 + spec/mspec/lib/mspec/matchers/be_nan.rb | 20 + spec/mspec/lib/mspec/matchers/be_nil.rb | 20 + spec/mspec/lib/mspec/matchers/be_true.rb | 20 + spec/mspec/lib/mspec/matchers/be_true_or_false.rb | 20 + spec/mspec/lib/mspec/matchers/block_caller.rb | 35 + spec/mspec/lib/mspec/matchers/complain.rb | 56 + spec/mspec/lib/mspec/matchers/eql.rb | 26 + spec/mspec/lib/mspec/matchers/equal.rb | 26 + spec/mspec/lib/mspec/matchers/equal_element.rb | 78 + .../lib/mspec/matchers/have_class_variable.rb | 12 + spec/mspec/lib/mspec/matchers/have_constant.rb | 12 + .../lib/mspec/matchers/have_instance_method.rb | 24 + .../lib/mspec/matchers/have_instance_variable.rb | 12 + spec/mspec/lib/mspec/matchers/have_method.rb | 24 + .../mspec/matchers/have_private_instance_method.rb | 24 + .../lib/mspec/matchers/have_private_method.rb | 24 + .../matchers/have_protected_instance_method.rb | 24 + .../mspec/matchers/have_public_instance_method.rb | 24 + .../lib/mspec/matchers/have_singleton_method.rb | 24 + spec/mspec/lib/mspec/matchers/include.rb | 32 + spec/mspec/lib/mspec/matchers/infinity.rb | 28 + spec/mspec/lib/mspec/matchers/match_yaml.rb | 47 + spec/mspec/lib/mspec/matchers/method.rb | 10 + spec/mspec/lib/mspec/matchers/output.rb | 67 + spec/mspec/lib/mspec/matchers/output_to_fd.rb | 71 + spec/mspec/lib/mspec/matchers/raise_error.rb | 79 + spec/mspec/lib/mspec/matchers/respond_to.rb | 24 + spec/mspec/lib/mspec/matchers/signed_zero.rb | 28 + spec/mspec/lib/mspec/matchers/variable.rb | 24 + spec/mspec/lib/mspec/mocks.rb | 3 + spec/mspec/lib/mspec/mocks/mock.rb | 197 + spec/mspec/lib/mspec/mocks/object.rb | 28 + spec/mspec/lib/mspec/mocks/proxy.rb | 186 + spec/mspec/lib/mspec/runner.rb | 12 + spec/mspec/lib/mspec/runner/actions.rb | 6 + spec/mspec/lib/mspec/runner/actions/filter.rb | 40 + spec/mspec/lib/mspec/runner/actions/leakchecker.rb | 301 + spec/mspec/lib/mspec/runner/actions/tag.rb | 133 + spec/mspec/lib/mspec/runner/actions/taglist.rb | 56 + spec/mspec/lib/mspec/runner/actions/tagpurge.rb | 56 + spec/mspec/lib/mspec/runner/actions/tally.rb | 133 + spec/mspec/lib/mspec/runner/actions/timer.rb | 22 + spec/mspec/lib/mspec/runner/context.rb | 239 + spec/mspec/lib/mspec/runner/evaluate.rb | 54 + spec/mspec/lib/mspec/runner/example.rb | 34 + spec/mspec/lib/mspec/runner/exception.rb | 43 + spec/mspec/lib/mspec/runner/filters.rb | 4 + spec/mspec/lib/mspec/runner/filters/match.rb | 18 + spec/mspec/lib/mspec/runner/filters/profile.rb | 54 + spec/mspec/lib/mspec/runner/filters/regexp.rb | 7 + spec/mspec/lib/mspec/runner/filters/tag.rb | 29 + spec/mspec/lib/mspec/runner/formatters.rb | 12 + spec/mspec/lib/mspec/runner/formatters/describe.rb | 24 + spec/mspec/lib/mspec/runner/formatters/dotted.rb | 117 + spec/mspec/lib/mspec/runner/formatters/file.rb | 19 + spec/mspec/lib/mspec/runner/formatters/html.rb | 81 + spec/mspec/lib/mspec/runner/formatters/junit.rb | 89 + spec/mspec/lib/mspec/runner/formatters/method.rb | 93 + spec/mspec/lib/mspec/runner/formatters/multi.rb | 36 + spec/mspec/lib/mspec/runner/formatters/profile.rb | 70 + spec/mspec/lib/mspec/runner/formatters/specdoc.rb | 41 + spec/mspec/lib/mspec/runner/formatters/spinner.rb | 117 + spec/mspec/lib/mspec/runner/formatters/summary.rb | 11 + spec/mspec/lib/mspec/runner/formatters/unit.rb | 21 + spec/mspec/lib/mspec/runner/formatters/yaml.rb | 42 + spec/mspec/lib/mspec/runner/mspec.rb | 391 + spec/mspec/lib/mspec/runner/object.rb | 28 + spec/mspec/lib/mspec/runner/shared.rb | 12 + spec/mspec/lib/mspec/runner/tag.rb | 38 + spec/mspec/lib/mspec/utils/deprecate.rb | 6 + spec/mspec/lib/mspec/utils/name_map.rb | 128 + spec/mspec/lib/mspec/utils/options.rb | 489 + spec/mspec/lib/mspec/utils/ruby_name.rb | 8 + spec/mspec/lib/mspec/utils/script.rb | 267 + spec/mspec/lib/mspec/utils/version.rb | 52 + spec/mspec/lib/mspec/utils/warnings.rb | 32 + spec/mspec/lib/mspec/version.rb | 5 + spec/mspec/mspec.gemspec | 40 + spec/mspec/spec/commands/fixtures/four.txt | 0 .../spec/commands/fixtures/level2/three_spec.rb | 0 spec/mspec/spec/commands/fixtures/one_spec.rb | 0 spec/mspec/spec/commands/fixtures/three.rb | 0 spec/mspec/spec/commands/fixtures/two_spec.rb | 0 spec/mspec/spec/commands/mkspec_spec.rb | 363 + spec/mspec/spec/commands/mspec_ci_spec.rb | 155 + spec/mspec/spec/commands/mspec_run_spec.rb | 185 + spec/mspec/spec/commands/mspec_spec.rb | 215 + spec/mspec/spec/commands/mspec_tag_spec.rb | 419 + spec/mspec/spec/expectations/expectations_spec.rb | 29 + spec/mspec/spec/expectations/should.rb | 72 + spec/mspec/spec/expectations/should_spec.rb | 61 + spec/mspec/spec/fixtures/a_spec.rb | 15 + spec/mspec/spec/fixtures/b_spec.rb | 7 + spec/mspec/spec/fixtures/config.mspec | 10 + spec/mspec/spec/fixtures/my_ruby | 4 + spec/mspec/spec/fixtures/print_interpreter_spec.rb | 4 + spec/mspec/spec/fixtures/tagging_spec.rb | 16 + spec/mspec/spec/guards/block_device_spec.rb | 46 + spec/mspec/spec/guards/bug_spec.rb | 151 + spec/mspec/spec/guards/conflict_spec.rb | 51 + spec/mspec/spec/guards/endian_spec.rb | 55 + spec/mspec/spec/guards/feature_spec.rb | 80 + spec/mspec/spec/guards/guard_spec.rb | 180 + spec/mspec/spec/guards/platform_spec.rb | 331 + spec/mspec/spec/guards/quarantine_spec.rb | 35 + spec/mspec/spec/guards/superuser_spec.rb | 35 + spec/mspec/spec/guards/support_spec.rb | 69 + spec/mspec/spec/guards/user_spec.rb | 20 + spec/mspec/spec/guards/version_spec.rb | 83 + spec/mspec/spec/helpers/argf_spec.rb | 37 + spec/mspec/spec/helpers/argv_spec.rb | 27 + spec/mspec/spec/helpers/datetime_spec.rb | 44 + spec/mspec/spec/helpers/fixture_spec.rb | 25 + spec/mspec/spec/helpers/flunk_spec.rb | 20 + spec/mspec/spec/helpers/fs_spec.rb | 182 + spec/mspec/spec/helpers/io_spec.rb | 174 + spec/mspec/spec/helpers/mock_to_path_spec.rb | 17 + spec/mspec/spec/helpers/numeric_spec.rb | 25 + spec/mspec/spec/helpers/ruby_exe_spec.rb | 220 + spec/mspec/spec/helpers/scratch_spec.rb | 24 + spec/mspec/spec/helpers/tmp_spec.rb | 27 + spec/mspec/spec/integration/interpreter_spec.rb | 18 + spec/mspec/spec/integration/run_spec.rb | 52 + spec/mspec/spec/integration/tag_spec.rb | 63 + spec/mspec/spec/matchers/base_spec.rb | 225 + spec/mspec/spec/matchers/be_an_instance_of_spec.rb | 50 + spec/mspec/spec/matchers/be_ancestor_of_spec.rb | 28 + spec/mspec/spec/matchers/be_close_spec.rb | 46 + spec/mspec/spec/matchers/be_computed_by_spec.rb | 42 + spec/mspec/spec/matchers/be_empty_spec.rb | 26 + spec/mspec/spec/matchers/be_false_spec.rb | 28 + spec/mspec/spec/matchers/be_kind_of_spec.rb | 31 + spec/mspec/spec/matchers/be_nan_spec.rb | 28 + spec/mspec/spec/matchers/be_nil_spec.rb | 27 + spec/mspec/spec/matchers/be_true_or_false_spec.rb | 19 + spec/mspec/spec/matchers/be_true_spec.rb | 28 + spec/mspec/spec/matchers/block_caller_spec.rb | 13 + spec/mspec/spec/matchers/complain_spec.rb | 52 + spec/mspec/spec/matchers/eql_spec.rb | 33 + spec/mspec/spec/matchers/equal_element_spec.rb | 75 + spec/mspec/spec/matchers/equal_spec.rb | 32 + .../spec/matchers/have_class_variable_spec.rb | 62 + spec/mspec/spec/matchers/have_constant_spec.rb | 37 + .../spec/matchers/have_instance_method_spec.rb | 53 + .../spec/matchers/have_instance_variable_spec.rb | 61 + spec/mspec/spec/matchers/have_method_spec.rb | 55 + .../matchers/have_private_instance_method_spec.rb | 57 + .../spec/matchers/have_private_method_spec.rb | 44 + .../have_protected_instance_method_spec.rb | 57 + .../matchers/have_public_instance_method_spec.rb | 53 + .../spec/matchers/have_singleton_method_spec.rb | 45 + spec/mspec/spec/matchers/include_spec.rb | 37 + spec/mspec/spec/matchers/infinity_spec.rb | 34 + spec/mspec/spec/matchers/match_yaml_spec.rb | 39 + spec/mspec/spec/matchers/output_spec.rb | 74 + spec/mspec/spec/matchers/output_to_fd_spec.rb | 42 + spec/mspec/spec/matchers/raise_error_spec.rb | 108 + spec/mspec/spec/matchers/respond_to_spec.rb | 33 + spec/mspec/spec/matchers/signed_zero_spec.rb | 32 + spec/mspec/spec/mocks/mock_spec.rb | 469 + spec/mspec/spec/mocks/proxy_spec.rb | 405 + spec/mspec/spec/runner/actions/filter_spec.rb | 84 + spec/mspec/spec/runner/actions/tag_spec.rb | 315 + spec/mspec/spec/runner/actions/taglist_spec.rb | 152 + spec/mspec/spec/runner/actions/tagpurge_spec.rb | 154 + spec/mspec/spec/runner/actions/tally_spec.rb | 352 + spec/mspec/spec/runner/actions/timer_spec.rb | 44 + spec/mspec/spec/runner/context_spec.rb | 1041 ++ spec/mspec/spec/runner/example_spec.rb | 117 + spec/mspec/spec/runner/exception_spec.rb | 146 + spec/mspec/spec/runner/filters/a.yaml | 4 + spec/mspec/spec/runner/filters/b.yaml | 11 + spec/mspec/spec/runner/filters/match_spec.rb | 34 + spec/mspec/spec/runner/filters/profile_spec.rb | 117 + spec/mspec/spec/runner/filters/regexp_spec.rb | 13 + spec/mspec/spec/runner/filters/tag_spec.rb | 92 + spec/mspec/spec/runner/formatters/describe_spec.rb | 67 + spec/mspec/spec/runner/formatters/dotted_spec.rb | 285 + spec/mspec/spec/runner/formatters/file_spec.rb | 84 + spec/mspec/spec/runner/formatters/html_spec.rb | 217 + spec/mspec/spec/runner/formatters/junit_spec.rb | 147 + spec/mspec/spec/runner/formatters/method_spec.rb | 178 + spec/mspec/spec/runner/formatters/multi_spec.rb | 68 + spec/mspec/spec/runner/formatters/specdoc_spec.rb | 106 + spec/mspec/spec/runner/formatters/spinner_spec.rb | 83 + spec/mspec/spec/runner/formatters/summary_spec.rb | 26 + spec/mspec/spec/runner/formatters/unit_spec.rb | 74 + spec/mspec/spec/runner/formatters/yaml_spec.rb | 125 + spec/mspec/spec/runner/mspec_spec.rb | 595 + spec/mspec/spec/runner/shared_spec.rb | 88 + spec/mspec/spec/runner/tag_spec.rb | 123 + spec/mspec/spec/runner/tags.txt | 4 + spec/mspec/spec/spec_helper.rb | 54 + spec/mspec/spec/utils/deprecate_spec.rb | 17 + spec/mspec/spec/utils/name_map_spec.rb | 175 + spec/mspec/spec/utils/options_spec.rb | 1309 ++ spec/mspec/spec/utils/script_spec.rb | 473 + spec/mspec/spec/utils/version_spec.rb | 45 + spec/mspec/tool/remove_old_guards.rb | 41 + spec/rubyspec/.gitignore | 5 + spec/rubyspec/.travis.yml | 30 + spec/rubyspec/CHANGES.before-2008-05-10 | 17796 +++++++++++++++++++ spec/rubyspec/CONTRIBUTING.md | 64 + spec/rubyspec/Gemfile | 3 + spec/rubyspec/LICENSE | 22 + spec/rubyspec/README.md | 88 + spec/rubyspec/TODO | 8 + spec/rubyspec/appveyor.yml | 18 + spec/rubyspec/command_line/dash_a_spec.rb | 17 + spec/rubyspec/command_line/dash_c_spec.rb | 13 + spec/rubyspec/command_line/dash_d_spec.rb | 22 + spec/rubyspec/command_line/dash_e_spec.rb | 39 + spec/rubyspec/command_line/dash_n_spec.rb | 34 + spec/rubyspec/command_line/dash_p_spec.rb | 17 + spec/rubyspec/command_line/dash_r_spec.rb | 13 + spec/rubyspec/command_line/dash_s_spec.rb | 52 + spec/rubyspec/command_line/dash_upper_c_spec.rb | 18 + spec/rubyspec/command_line/dash_upper_e_spec.rb | 7 + spec/rubyspec/command_line/dash_upper_f_spec.rb | 11 + spec/rubyspec/command_line/dash_upper_i_spec.rb | 11 + spec/rubyspec/command_line/dash_upper_k_spec.rb | 33 + spec/rubyspec/command_line/dash_upper_u_spec.rb | 41 + spec/rubyspec/command_line/dash_upper_w_spec.rb | 20 + spec/rubyspec/command_line/dash_v_spec.rb | 6 + spec/rubyspec/command_line/dash_w_spec.rb | 6 + spec/rubyspec/command_line/dash_x_spec.rb | 12 + spec/rubyspec/command_line/error_message_spec.rb | 11 + spec/rubyspec/command_line/fixtures/bad_syntax.rb | 1 + .../command_line/fixtures/conditional_range.txt | 5 + .../command_line/fixtures/dash_s_script.rb | 12 + .../command_line/fixtures/dash_upper_c_script.rb | 1 + spec/rubyspec/command_line/fixtures/debug.rb | 10 + spec/rubyspec/command_line/fixtures/debug_info.rb | 11 + .../command_line/fixtures/embedded_ruby.txt | 3 + .../fixtures/freeze_flag_across_files.rb | 3 + .../fixtures/freeze_flag_across_files_diff_enc.rb | 3 + .../fixtures/freeze_flag_one_literal.rb | 2 + .../command_line/fixtures/freeze_flag_required.rb | 1 + .../fixtures/freeze_flag_required_diff_enc.rb | Bin 0 -> 121 bytes .../fixtures/freeze_flag_two_literals.rb | 1 + spec/rubyspec/command_line/fixtures/full_names.txt | 3 + spec/rubyspec/command_line/fixtures/loadpath.rb | 1 + spec/rubyspec/command_line/fixtures/names.txt | 3 + .../rubyspec/command_line/fixtures/passwd_file.txt | 3 + spec/rubyspec/command_line/fixtures/require.rb | 1 + spec/rubyspec/command_line/fixtures/rubyopt.rb | 1 + spec/rubyspec/command_line/fixtures/test_file.rb | 1 + spec/rubyspec/command_line/fixtures/verbose.rb | 1 + spec/rubyspec/command_line/frozen_strings_spec.rb | 30 + spec/rubyspec/command_line/rubyopt_spec.rb | 160 + spec/rubyspec/command_line/shared/verbose.rb | 9 + spec/rubyspec/command_line/syntax_error_spec.rb | 13 + spec/rubyspec/core/argf/argf_spec.rb | 11 + spec/rubyspec/core/argf/argv_spec.rb | 19 + spec/rubyspec/core/argf/binmode_spec.rb | 46 + spec/rubyspec/core/argf/bytes_spec.rb | 6 + spec/rubyspec/core/argf/chars_spec.rb | 6 + spec/rubyspec/core/argf/close_spec.rb | 46 + spec/rubyspec/core/argf/closed_spec.rb | 18 + spec/rubyspec/core/argf/codepoints_spec.rb | 6 + spec/rubyspec/core/argf/each_byte_spec.rb | 6 + spec/rubyspec/core/argf/each_char_spec.rb | 6 + spec/rubyspec/core/argf/each_codepoint_spec.rb | 6 + spec/rubyspec/core/argf/each_line_spec.rb | 6 + spec/rubyspec/core/argf/each_spec.rb | 6 + spec/rubyspec/core/argf/eof_spec.rb | 10 + spec/rubyspec/core/argf/file_spec.rb | 21 + spec/rubyspec/core/argf/filename_spec.rb | 6 + spec/rubyspec/core/argf/fileno_spec.rb | 6 + spec/rubyspec/core/argf/fixtures/bin_file.txt | 2 + spec/rubyspec/core/argf/fixtures/encoding.rb | 5 + spec/rubyspec/core/argf/fixtures/file1.txt | 2 + spec/rubyspec/core/argf/fixtures/file2.txt | 2 + spec/rubyspec/core/argf/fixtures/filename.rb | 3 + spec/rubyspec/core/argf/fixtures/lineno.rb | 5 + spec/rubyspec/core/argf/fixtures/rewind.rb | 5 + spec/rubyspec/core/argf/fixtures/stdin.txt | 2 + spec/rubyspec/core/argf/getc_spec.rb | 20 + spec/rubyspec/core/argf/gets_spec.rb | 51 + spec/rubyspec/core/argf/lineno_spec.rb | 30 + spec/rubyspec/core/argf/lines_spec.rb | 6 + spec/rubyspec/core/argf/path_spec.rb | 6 + spec/rubyspec/core/argf/pos_spec.rb | 38 + spec/rubyspec/core/argf/read_nonblock_spec.rb | 82 + spec/rubyspec/core/argf/read_spec.rb | 87 + spec/rubyspec/core/argf/readchar_spec.rb | 19 + spec/rubyspec/core/argf/readline_spec.rb | 23 + spec/rubyspec/core/argf/readlines_spec.rb | 6 + spec/rubyspec/core/argf/readpartial_spec.rb | 77 + spec/rubyspec/core/argf/rewind_spec.rb | 39 + spec/rubyspec/core/argf/seek_spec.rb | 63 + spec/rubyspec/core/argf/set_encoding_spec.rb | 28 + spec/rubyspec/core/argf/shared/each_byte.rb | 58 + spec/rubyspec/core/argf/shared/each_char.rb | 58 + spec/rubyspec/core/argf/shared/each_codepoint.rb | 58 + spec/rubyspec/core/argf/shared/each_line.rb | 62 + spec/rubyspec/core/argf/shared/eof.rb | 24 + spec/rubyspec/core/argf/shared/filename.rb | 28 + spec/rubyspec/core/argf/shared/fileno.rb | 24 + spec/rubyspec/core/argf/shared/getc.rb | 17 + spec/rubyspec/core/argf/shared/gets.rb | 99 + spec/rubyspec/core/argf/shared/pos.rb | 31 + spec/rubyspec/core/argf/shared/read.rb | 58 + spec/rubyspec/core/argf/shared/readlines.rb | 22 + spec/rubyspec/core/argf/skip_spec.rb | 42 + spec/rubyspec/core/argf/tell_spec.rb | 6 + spec/rubyspec/core/argf/to_a_spec.rb | 6 + spec/rubyspec/core/argf/to_i_spec.rb | 6 + spec/rubyspec/core/argf/to_io_spec.rb | 23 + spec/rubyspec/core/argf/to_s_spec.rb | 14 + spec/rubyspec/core/array/allocate_spec.rb | 19 + spec/rubyspec/core/array/any_spec.rb | 37 + spec/rubyspec/core/array/append_spec.rb | 35 + spec/rubyspec/core/array/array_spec.rb | 7 + spec/rubyspec/core/array/assoc_spec.rb | 40 + spec/rubyspec/core/array/at_spec.rb | 56 + spec/rubyspec/core/array/bsearch_index_spec.rb | 87 + spec/rubyspec/core/array/bsearch_spec.rb | 84 + spec/rubyspec/core/array/clear_spec.rb | 49 + spec/rubyspec/core/array/clone_spec.rb | 31 + spec/rubyspec/core/array/collect_spec.rb | 11 + spec/rubyspec/core/array/combination_spec.rb | 74 + spec/rubyspec/core/array/compact_spec.rb | 77 + spec/rubyspec/core/array/comparison_spec.rb | 97 + spec/rubyspec/core/array/concat_spec.rb | 132 + spec/rubyspec/core/array/constructor_spec.rb | 24 + spec/rubyspec/core/array/count_spec.rb | 15 + spec/rubyspec/core/array/cycle_spec.rb | 101 + spec/rubyspec/core/array/delete_at_spec.rb | 61 + spec/rubyspec/core/array/delete_if_spec.rb | 66 + spec/rubyspec/core/array/delete_spec.rb | 66 + spec/rubyspec/core/array/dig_spec.rb | 54 + spec/rubyspec/core/array/drop_spec.rb | 33 + spec/rubyspec/core/array/drop_while_spec.rb | 15 + spec/rubyspec/core/array/dup_spec.rb | 31 + spec/rubyspec/core/array/each_index_spec.rb | 42 + spec/rubyspec/core/array/each_spec.rb | 32 + spec/rubyspec/core/array/element_reference_spec.rb | 50 + spec/rubyspec/core/array/element_set_spec.rb | 418 + spec/rubyspec/core/array/empty_spec.rb | 10 + spec/rubyspec/core/array/eql_spec.rb | 19 + spec/rubyspec/core/array/equal_value_spec.rb | 51 + spec/rubyspec/core/array/fetch_spec.rb | 55 + spec/rubyspec/core/array/fill_spec.rb | 317 + spec/rubyspec/core/array/find_index_spec.rb | 6 + spec/rubyspec/core/array/first_spec.rb | 93 + spec/rubyspec/core/array/fixtures/classes.rb | 525 + .../core/array/fixtures/encoded_strings.rb | 69 + spec/rubyspec/core/array/flatten_spec.rb | 270 + spec/rubyspec/core/array/frozen_spec.rb | 16 + spec/rubyspec/core/array/hash_spec.rb | 83 + spec/rubyspec/core/array/include_spec.rb | 33 + spec/rubyspec/core/array/index_spec.rb | 6 + spec/rubyspec/core/array/initialize_spec.rb | 156 + spec/rubyspec/core/array/insert_spec.rb | 78 + spec/rubyspec/core/array/inspect_spec.rb | 7 + spec/rubyspec/core/array/intersection_spec.rb | 86 + spec/rubyspec/core/array/join_spec.rb | 48 + spec/rubyspec/core/array/keep_if_spec.rb | 10 + spec/rubyspec/core/array/last_spec.rb | 87 + spec/rubyspec/core/array/length_spec.rb | 7 + spec/rubyspec/core/array/map_spec.rb | 11 + spec/rubyspec/core/array/max_spec.rb | 112 + spec/rubyspec/core/array/min_spec.rb | 117 + spec/rubyspec/core/array/minus_spec.rb | 87 + spec/rubyspec/core/array/multiply_spec.rb | 132 + spec/rubyspec/core/array/new_spec.rb | 122 + spec/rubyspec/core/array/pack/a_spec.rb | 59 + spec/rubyspec/core/array/pack/at_spec.rb | 30 + spec/rubyspec/core/array/pack/b_spec.rb | 105 + spec/rubyspec/core/array/pack/c_spec.rb | 75 + spec/rubyspec/core/array/pack/comment_spec.rb | 25 + spec/rubyspec/core/array/pack/d_spec.rb | 39 + spec/rubyspec/core/array/pack/e_spec.rb | 25 + spec/rubyspec/core/array/pack/empty_spec.rb | 11 + spec/rubyspec/core/array/pack/f_spec.rb | 39 + spec/rubyspec/core/array/pack/g_spec.rb | 25 + spec/rubyspec/core/array/pack/h_spec.rb | 197 + spec/rubyspec/core/array/pack/i_spec.rb | 133 + spec/rubyspec/core/array/pack/j_spec.rb | 222 + spec/rubyspec/core/array/pack/l_spec.rb | 309 + spec/rubyspec/core/array/pack/m_spec.rb | 306 + spec/rubyspec/core/array/pack/n_spec.rb | 25 + spec/rubyspec/core/array/pack/p_spec.rb | 11 + spec/rubyspec/core/array/pack/percent_spec.rb | 7 + spec/rubyspec/core/array/pack/q_spec.rb | 61 + spec/rubyspec/core/array/pack/s_spec.rb | 133 + spec/rubyspec/core/array/pack/shared/basic.rb | 65 + spec/rubyspec/core/array/pack/shared/encodings.rb | 16 + spec/rubyspec/core/array/pack/shared/float.rb | 249 + spec/rubyspec/core/array/pack/shared/integer.rb | 381 + .../core/array/pack/shared/numeric_basic.rb | 44 + spec/rubyspec/core/array/pack/shared/string.rb | 80 + spec/rubyspec/core/array/pack/shared/unicode.rb | 94 + spec/rubyspec/core/array/pack/u_spec.rb | 128 + spec/rubyspec/core/array/pack/v_spec.rb | 25 + spec/rubyspec/core/array/pack/w_spec.rb | 42 + spec/rubyspec/core/array/pack/x_spec.rb | 64 + spec/rubyspec/core/array/pack/z_spec.rb | 32 + spec/rubyspec/core/array/partition_spec.rb | 43 + spec/rubyspec/core/array/permutation_spec.rb | 138 + spec/rubyspec/core/array/plus_spec.rb | 57 + spec/rubyspec/core/array/pop_spec.rb | 168 + spec/rubyspec/core/array/product_spec.rb | 68 + spec/rubyspec/core/array/push_spec.rb | 36 + spec/rubyspec/core/array/rassoc_spec.rb | 38 + spec/rubyspec/core/array/reject_spec.rb | 117 + .../core/array/repeated_combination_spec.rb | 84 + .../core/array/repeated_permutation_spec.rb | 94 + spec/rubyspec/core/array/replace_spec.rb | 7 + spec/rubyspec/core/array/reverse_each_spec.rb | 43 + spec/rubyspec/core/array/reverse_spec.rb | 42 + spec/rubyspec/core/array/rindex_spec.rb | 80 + spec/rubyspec/core/array/rotate_spec.rb | 129 + spec/rubyspec/core/array/sample_spec.rb | 155 + spec/rubyspec/core/array/select_spec.rb | 36 + spec/rubyspec/core/array/shared/clone.rb | 42 + spec/rubyspec/core/array/shared/collect.rb | 136 + spec/rubyspec/core/array/shared/delete_if.rb | 27 + spec/rubyspec/core/array/shared/enumeratorize.rb | 5 + spec/rubyspec/core/array/shared/eql.rb | 92 + spec/rubyspec/core/array/shared/index.rb | 37 + spec/rubyspec/core/array/shared/inspect.rb | 104 + spec/rubyspec/core/array/shared/join.rb | 161 + spec/rubyspec/core/array/shared/keep_if.rb | 60 + spec/rubyspec/core/array/shared/length.rb | 11 + spec/rubyspec/core/array/shared/replace.rb | 60 + spec/rubyspec/core/array/shared/slice.rb | 459 + spec/rubyspec/core/array/shift_spec.rb | 134 + spec/rubyspec/core/array/shuffle_spec.rb | 102 + spec/rubyspec/core/array/size_spec.rb | 7 + spec/rubyspec/core/array/slice_spec.rb | 160 + spec/rubyspec/core/array/sort_by_spec.rb | 52 + spec/rubyspec/core/array/sort_spec.rb | 250 + spec/rubyspec/core/array/take_spec.rb | 27 + spec/rubyspec/core/array/take_while_spec.rb | 15 + spec/rubyspec/core/array/to_a_spec.rb | 24 + spec/rubyspec/core/array/to_ary_spec.rb | 20 + spec/rubyspec/core/array/to_h_spec.rb | 37 + spec/rubyspec/core/array/to_s_spec.rb | 8 + spec/rubyspec/core/array/transpose_spec.rb | 53 + spec/rubyspec/core/array/try_convert_spec.rb | 50 + spec/rubyspec/core/array/union_spec.rb | 82 + spec/rubyspec/core/array/uniq_spec.rb | 221 + spec/rubyspec/core/array/unshift_spec.rb | 50 + spec/rubyspec/core/array/values_at_spec.rb | 63 + spec/rubyspec/core/array/zip_spec.rb | 65 + spec/rubyspec/core/basicobject/__id__spec.rb | 6 + spec/rubyspec/core/basicobject/__send___spec.rb | 10 + spec/rubyspec/core/basicobject/basicobject_spec.rb | 87 + spec/rubyspec/core/basicobject/equal_spec.rb | 52 + spec/rubyspec/core/basicobject/equal_value_spec.rb | 10 + spec/rubyspec/core/basicobject/fixtures/classes.rb | 33 + spec/rubyspec/core/basicobject/fixtures/common.rb | 9 + .../basicobject/fixtures/remove_method_missing.rb | 9 + .../core/basicobject/fixtures/singleton_method.rb | 10 + spec/rubyspec/core/basicobject/initialize_spec.rb | 13 + .../core/basicobject/instance_eval_spec.rb | 180 + .../core/basicobject/instance_exec_spec.rb | 107 + .../core/basicobject/method_missing_spec.rb | 39 + spec/rubyspec/core/basicobject/not_equal_spec.rb | 53 + spec/rubyspec/core/basicobject/not_spec.rb | 11 + .../basicobject/singleton_method_added_spec.rb | 86 + .../basicobject/singleton_method_removed_spec.rb | 24 + .../basicobject/singleton_method_undefined_spec.rb | 24 + spec/rubyspec/core/bignum/abs_spec.rb | 7 + spec/rubyspec/core/bignum/bignum_spec.rb | 7 + spec/rubyspec/core/bignum/bit_and_spec.rb | 50 + spec/rubyspec/core/bignum/bit_length_spec.rb | 33 + spec/rubyspec/core/bignum/bit_or_spec.rb | 41 + spec/rubyspec/core/bignum/bit_xor_spec.rb | 47 + spec/rubyspec/core/bignum/case_compare_spec.rb | 6 + spec/rubyspec/core/bignum/coerce_spec.rb | 65 + spec/rubyspec/core/bignum/comparison_spec.rb | 162 + spec/rubyspec/core/bignum/complement_spec.rb | 9 + spec/rubyspec/core/bignum/div_spec.rb | 21 + spec/rubyspec/core/bignum/divide_spec.rb | 18 + spec/rubyspec/core/bignum/divmod_spec.rb | 81 + .../rubyspec/core/bignum/element_reference_spec.rb | 30 + spec/rubyspec/core/bignum/eql_spec.rb | 22 + spec/rubyspec/core/bignum/equal_value_spec.rb | 6 + spec/rubyspec/core/bignum/even_spec.rb | 19 + spec/rubyspec/core/bignum/exponent_spec.rb | 29 + spec/rubyspec/core/bignum/fdiv_spec.rb | 5 + spec/rubyspec/core/bignum/gt_spec.rb | 20 + spec/rubyspec/core/bignum/gte_spec.rb | 19 + spec/rubyspec/core/bignum/hash_spec.rb | 12 + spec/rubyspec/core/bignum/left_shift_spec.rb | 73 + spec/rubyspec/core/bignum/lt_spec.rb | 22 + spec/rubyspec/core/bignum/lte_spec.rb | 24 + spec/rubyspec/core/bignum/magnitude_spec.rb | 6 + spec/rubyspec/core/bignum/minus_spec.rb | 19 + spec/rubyspec/core/bignum/modulo_spec.rb | 10 + spec/rubyspec/core/bignum/multiply_spec.rb | 20 + spec/rubyspec/core/bignum/odd_spec.rb | 19 + spec/rubyspec/core/bignum/plus_spec.rb | 19 + spec/rubyspec/core/bignum/remainder_spec.rb | 21 + spec/rubyspec/core/bignum/right_shift_spec.rb | 99 + spec/rubyspec/core/bignum/shared/abs.rb | 6 + spec/rubyspec/core/bignum/shared/divide.rb | 27 + spec/rubyspec/core/bignum/shared/equal.rb | 31 + spec/rubyspec/core/bignum/shared/modulo.rb | 29 + spec/rubyspec/core/bignum/size_spec.rb | 16 + spec/rubyspec/core/bignum/to_f_spec.rb | 13 + spec/rubyspec/core/bignum/to_s_spec.rb | 48 + spec/rubyspec/core/bignum/uminus_spec.rb | 11 + spec/rubyspec/core/binding/clone_spec.rb | 7 + spec/rubyspec/core/binding/dup_spec.rb | 7 + spec/rubyspec/core/binding/eval_spec.rb | 27 + spec/rubyspec/core/binding/fixtures/classes.rb | 32 + .../core/binding/local_variable_defined_spec.rb | 46 + .../core/binding/local_variable_get_spec.rb | 45 + .../core/binding/local_variable_set_spec.rb | 59 + spec/rubyspec/core/binding/local_variables_spec.rb | 35 + spec/rubyspec/core/binding/location_spec.rb | 46 + spec/rubyspec/core/binding/receiver_spec.rb | 11 + spec/rubyspec/core/binding/shared/clone.rb | 20 + .../builtin_constants/builtin_constants_spec.rb | 49 + spec/rubyspec/core/class/allocate_spec.rb | 41 + spec/rubyspec/core/class/dup_spec.rb | 64 + spec/rubyspec/core/class/fixtures/classes.rb | 47 + spec/rubyspec/core/class/inherited_spec.rb | 102 + spec/rubyspec/core/class/initialize_spec.rb | 34 + spec/rubyspec/core/class/new_spec.rb | 154 + spec/rubyspec/core/class/superclass_spec.rb | 27 + spec/rubyspec/core/class/to_s_spec.rb | 23 + spec/rubyspec/core/comparable/between_spec.rb | 25 + spec/rubyspec/core/comparable/clamp_spec.rb | 50 + spec/rubyspec/core/comparable/equal_value_spec.rb | 139 + spec/rubyspec/core/comparable/fixtures/classes.rb | 36 + spec/rubyspec/core/comparable/gt_spec.rb | 43 + spec/rubyspec/core/comparable/gte_spec.rb | 47 + spec/rubyspec/core/comparable/lt_spec.rb | 43 + spec/rubyspec/core/comparable/lte_spec.rb | 46 + spec/rubyspec/core/complex/abs2_spec.rb | 5 + spec/rubyspec/core/complex/abs_spec.rb | 5 + spec/rubyspec/core/complex/angle_spec.rb | 7 + spec/rubyspec/core/complex/arg_spec.rb | 7 + spec/rubyspec/core/complex/coerce_spec.rb | 5 + spec/rubyspec/core/complex/conj_spec.rb | 6 + spec/rubyspec/core/complex/conjugate_spec.rb | 6 + spec/rubyspec/core/complex/constants_spec.rb | 5 + spec/rubyspec/core/complex/denominator_spec.rb | 5 + spec/rubyspec/core/complex/divide_spec.rb | 5 + spec/rubyspec/core/complex/eql_spec.rb | 31 + spec/rubyspec/core/complex/equal_value_spec.rb | 5 + spec/rubyspec/core/complex/exponent_spec.rb | 5 + spec/rubyspec/core/complex/fdiv_spec.rb | 129 + spec/rubyspec/core/complex/hash_spec.rb | 6 + spec/rubyspec/core/complex/imag_spec.rb | 5 + spec/rubyspec/core/complex/imaginary_spec.rb | 5 + spec/rubyspec/core/complex/inspect_spec.rb | 5 + spec/rubyspec/core/complex/integer_spec.rb | 9 + spec/rubyspec/core/complex/magnitude_spec.rb | 5 + spec/rubyspec/core/complex/marshal_dump_spec.rb | 11 + spec/rubyspec/core/complex/minus_spec.rb | 5 + spec/rubyspec/core/complex/multiply_spec.rb | 5 + spec/rubyspec/core/complex/negative_spec.rb | 11 + spec/rubyspec/core/complex/numerator_spec.rb | 5 + spec/rubyspec/core/complex/phase_spec.rb | 6 + spec/rubyspec/core/complex/plus_spec.rb | 5 + spec/rubyspec/core/complex/polar_spec.rb | 14 + spec/rubyspec/core/complex/positive_spec.rb | 11 + spec/rubyspec/core/complex/quo_spec.rb | 5 + spec/rubyspec/core/complex/rationalize_spec.rb | 29 + spec/rubyspec/core/complex/real_spec.rb | 23 + spec/rubyspec/core/complex/rect_spec.rb | 9 + spec/rubyspec/core/complex/rectangular_spec.rb | 9 + spec/rubyspec/core/complex/to_f_spec.rb | 41 + spec/rubyspec/core/complex/to_i_spec.rb | 41 + spec/rubyspec/core/complex/to_r_spec.rb | 41 + spec/rubyspec/core/complex/to_s_spec.rb | 5 + spec/rubyspec/core/complex/uminus_spec.rb | 11 + spec/rubyspec/core/dir/chdir_spec.rb | 124 + spec/rubyspec/core/dir/chroot_spec.rb | 47 + spec/rubyspec/core/dir/close_spec.rb | 29 + spec/rubyspec/core/dir/delete_spec.rb | 15 + spec/rubyspec/core/dir/dir_spec.rb | 7 + spec/rubyspec/core/dir/each_spec.rb | 64 + spec/rubyspec/core/dir/element_reference_spec.rb | 33 + spec/rubyspec/core/dir/entries_spec.rb | 70 + spec/rubyspec/core/dir/exist_spec.rb | 15 + spec/rubyspec/core/dir/exists_spec.rb | 15 + spec/rubyspec/core/dir/fileno_spec.rb | 37 + spec/rubyspec/core/dir/fixtures/common.rb | 169 + spec/rubyspec/core/dir/foreach_spec.rb | 56 + spec/rubyspec/core/dir/getwd_spec.rb | 15 + spec/rubyspec/core/dir/glob_spec.rb | 156 + spec/rubyspec/core/dir/home_spec.rb | 24 + spec/rubyspec/core/dir/initialize_spec.rb | 23 + spec/rubyspec/core/dir/inspect_spec.rb | 24 + spec/rubyspec/core/dir/mkdir_spec.rb | 85 + spec/rubyspec/core/dir/open_spec.rb | 15 + spec/rubyspec/core/dir/path_spec.rb | 15 + spec/rubyspec/core/dir/pos_spec.rb | 40 + spec/rubyspec/core/dir/pwd_spec.rb | 39 + spec/rubyspec/core/dir/read_spec.rb | 43 + spec/rubyspec/core/dir/rewind_spec.rb | 36 + spec/rubyspec/core/dir/rmdir_spec.rb | 15 + spec/rubyspec/core/dir/seek_spec.rb | 19 + spec/rubyspec/core/dir/shared/chroot.rb | 41 + spec/rubyspec/core/dir/shared/closed.rb | 9 + spec/rubyspec/core/dir/shared/delete.rb | 59 + spec/rubyspec/core/dir/shared/exist.rb | 56 + spec/rubyspec/core/dir/shared/glob.rb | 328 + spec/rubyspec/core/dir/shared/open.rb | 63 + spec/rubyspec/core/dir/shared/path.rb | 32 + spec/rubyspec/core/dir/shared/pos.rb | 51 + spec/rubyspec/core/dir/shared/pwd.rb | 49 + spec/rubyspec/core/dir/tell_spec.rb | 18 + spec/rubyspec/core/dir/to_path_spec.rb | 15 + spec/rubyspec/core/dir/unlink_spec.rb | 15 + spec/rubyspec/core/encoding/_dump_spec.rb | 5 + spec/rubyspec/core/encoding/_load_spec.rb | 5 + spec/rubyspec/core/encoding/aliases_spec.rb | 45 + .../core/encoding/ascii_compatible_spec.rb | 13 + spec/rubyspec/core/encoding/compatible_spec.rb | 381 + .../converter/asciicompat_encoding_spec.rb | 39 + .../core/encoding/converter/constants_spec.rb | 133 + .../core/encoding/converter/convert_spec.rb | 47 + .../core/encoding/converter/convpath_spec.rb | 65 + .../converter/destination_encoding_spec.rb | 13 + .../core/encoding/converter/finish_spec.rb | 38 + .../core/encoding/converter/insert_output_spec.rb | 5 + .../core/encoding/converter/inspect_spec.rb | 13 + .../core/encoding/converter/last_error_spec.rb | 85 + spec/rubyspec/core/encoding/converter/new_spec.rb | 121 + .../encoding/converter/primitive_convert_spec.rb | 213 + .../encoding/converter/primitive_errinfo_spec.rb | 72 + .../core/encoding/converter/putback_spec.rb | 50 + .../core/encoding/converter/replacement_spec.rb | 74 + .../encoding/converter/search_convpath_spec.rb | 73 + .../encoding/converter/source_encoding_spec.rb | 13 + .../core/encoding/default_external_spec.rb | 74 + .../core/encoding/default_internal_spec.rb | 93 + spec/rubyspec/core/encoding/dummy_spec.rb | 16 + spec/rubyspec/core/encoding/find_spec.rb | 84 + spec/rubyspec/core/encoding/fixtures/classes.rb | 49 + spec/rubyspec/core/encoding/inspect_spec.rb | 21 + .../destination_encoding_name_spec.rb | 20 + .../destination_encoding_spec.rb | 20 + .../error_bytes_spec.rb | 32 + .../incomplete_input_spec.rb | 31 + .../readagain_bytes_spec.rb | 32 + .../source_encoding_name_spec.rb | 30 + .../source_encoding_spec.rb | 35 + spec/rubyspec/core/encoding/list_spec.rb | 43 + spec/rubyspec/core/encoding/locale_charmap_spec.rb | 47 + spec/rubyspec/core/encoding/name_list_spec.rb | 25 + spec/rubyspec/core/encoding/name_spec.rb | 7 + spec/rubyspec/core/encoding/names_spec.rb | 37 + spec/rubyspec/core/encoding/replicate_spec.rb | 48 + spec/rubyspec/core/encoding/shared/name.rb | 15 + spec/rubyspec/core/encoding/to_s_spec.rb | 7 + .../destination_encoding_name_spec.rb | 17 + .../destination_encoding_spec.rb | 17 + .../undefined_conversion_error/error_char_spec.rb | 29 + .../source_encoding_name_spec.rb | 30 + .../source_encoding_spec.rb | 31 + spec/rubyspec/core/enumerable/all_spec.rb | 121 + spec/rubyspec/core/enumerable/any_spec.rb | 141 + spec/rubyspec/core/enumerable/chunk_spec.rb | 100 + spec/rubyspec/core/enumerable/chunk_while_spec.rb | 44 + .../core/enumerable/collect_concat_spec.rb | 7 + spec/rubyspec/core/enumerable/collect_spec.rb | 7 + spec/rubyspec/core/enumerable/count_spec.rb | 59 + spec/rubyspec/core/enumerable/cycle_spec.rb | 104 + spec/rubyspec/core/enumerable/detect_spec.rb | 7 + spec/rubyspec/core/enumerable/drop_spec.rb | 43 + spec/rubyspec/core/enumerable/drop_while_spec.rb | 50 + spec/rubyspec/core/enumerable/each_cons_spec.rb | 99 + spec/rubyspec/core/enumerable/each_entry_spec.rb | 41 + spec/rubyspec/core/enumerable/each_slice_spec.rb | 101 + .../core/enumerable/each_with_index_spec.rb | 53 + .../core/enumerable/each_with_object_spec.rb | 41 + spec/rubyspec/core/enumerable/entries_spec.rb | 7 + spec/rubyspec/core/enumerable/find_all_spec.rb | 7 + spec/rubyspec/core/enumerable/find_index_spec.rb | 89 + spec/rubyspec/core/enumerable/find_spec.rb | 7 + spec/rubyspec/core/enumerable/first_spec.rb | 28 + spec/rubyspec/core/enumerable/fixtures/classes.rb | 331 + spec/rubyspec/core/enumerable/flat_map_spec.rb | 7 + spec/rubyspec/core/enumerable/grep_spec.rb | 52 + spec/rubyspec/core/enumerable/grep_v_spec.rb | 43 + spec/rubyspec/core/enumerable/group_by_spec.rb | 45 + spec/rubyspec/core/enumerable/include_spec.rb | 7 + spec/rubyspec/core/enumerable/inject_spec.rb | 7 + spec/rubyspec/core/enumerable/lazy_spec.rb | 10 + spec/rubyspec/core/enumerable/map_spec.rb | 7 + spec/rubyspec/core/enumerable/max_by_spec.rb | 81 + spec/rubyspec/core/enumerable/max_spec.rb | 119 + spec/rubyspec/core/enumerable/member_spec.rb | 7 + spec/rubyspec/core/enumerable/min_by_spec.rb | 81 + spec/rubyspec/core/enumerable/min_spec.rb | 123 + spec/rubyspec/core/enumerable/minmax_by_spec.rb | 44 + spec/rubyspec/core/enumerable/minmax_spec.rb | 44 + spec/rubyspec/core/enumerable/none_spec.rb | 57 + spec/rubyspec/core/enumerable/one_spec.rb | 49 + spec/rubyspec/core/enumerable/partition_spec.rb | 20 + spec/rubyspec/core/enumerable/reduce_spec.rb | 7 + spec/rubyspec/core/enumerable/reject_spec.rb | 25 + spec/rubyspec/core/enumerable/reverse_each_spec.rb | 26 + spec/rubyspec/core/enumerable/select_spec.rb | 7 + spec/rubyspec/core/enumerable/shared/collect.rb | 32 + .../core/enumerable/shared/collect_concat.rb | 54 + spec/rubyspec/core/enumerable/shared/entries.rb | 24 + .../enumerable/shared/enumerable_enumeratorized.rb | 33 + .../core/enumerable/shared/enumeratorized.rb | 42 + spec/rubyspec/core/enumerable/shared/find.rb | 73 + spec/rubyspec/core/enumerable/shared/find_all.rb | 31 + spec/rubyspec/core/enumerable/shared/include.rb | 34 + spec/rubyspec/core/enumerable/shared/inject.rb | 69 + spec/rubyspec/core/enumerable/shared/take.rb | 63 + spec/rubyspec/core/enumerable/slice_after_spec.rb | 61 + spec/rubyspec/core/enumerable/slice_before_spec.rb | 87 + spec/rubyspec/core/enumerable/slice_when_spec.rb | 54 + spec/rubyspec/core/enumerable/sort_by_spec.rb | 36 + spec/rubyspec/core/enumerable/sort_spec.rb | 54 + spec/rubyspec/core/enumerable/take_spec.rb | 13 + spec/rubyspec/core/enumerable/take_while_spec.rb | 51 + spec/rubyspec/core/enumerable/to_a_spec.rb | 7 + spec/rubyspec/core/enumerable/to_h_spec.rb | 46 + spec/rubyspec/core/enumerable/zip_spec.rb | 42 + spec/rubyspec/core/enumerator/each_spec.rb | 5 + .../core/enumerator/each_with_index_spec.rb | 38 + .../core/enumerator/each_with_object_spec.rb | 6 + spec/rubyspec/core/enumerator/enum_for_spec.rb | 6 + spec/rubyspec/core/enumerator/enumerator_spec.rb | 7 + spec/rubyspec/core/enumerator/feed_spec.rb | 52 + spec/rubyspec/core/enumerator/first_spec.rb | 7 + spec/rubyspec/core/enumerator/fixtures/common.rb | 9 + .../core/enumerator/generator/each_spec.rb | 40 + .../core/enumerator/generator/initialize_spec.rb | 26 + spec/rubyspec/core/enumerator/initialize_spec.rb | 61 + spec/rubyspec/core/enumerator/inject_spec.rb | 15 + spec/rubyspec/core/enumerator/inspect_spec.rb | 5 + .../core/enumerator/lazy/collect_concat_spec.rb | 8 + spec/rubyspec/core/enumerator/lazy/collect_spec.rb | 8 + spec/rubyspec/core/enumerator/lazy/drop_spec.rb | 52 + .../core/enumerator/lazy/drop_while_spec.rb | 60 + .../rubyspec/core/enumerator/lazy/enum_for_spec.rb | 8 + .../rubyspec/core/enumerator/lazy/find_all_spec.rb | 8 + .../core/enumerator/lazy/fixtures/classes.rb | 54 + .../rubyspec/core/enumerator/lazy/flat_map_spec.rb | 8 + spec/rubyspec/core/enumerator/lazy/force_spec.rb | 30 + spec/rubyspec/core/enumerator/lazy/grep_spec.rb | 82 + spec/rubyspec/core/enumerator/lazy/grep_v_spec.rb | 86 + .../core/enumerator/lazy/initialize_spec.rb | 63 + spec/rubyspec/core/enumerator/lazy/lazy_spec.rb | 16 + spec/rubyspec/core/enumerator/lazy/map_spec.rb | 12 + spec/rubyspec/core/enumerator/lazy/reject_spec.rb | 60 + spec/rubyspec/core/enumerator/lazy/select_spec.rb | 8 + .../core/enumerator/lazy/shared/collect.rb | 56 + .../core/enumerator/lazy/shared/collect_concat.rb | 72 + .../rubyspec/core/enumerator/lazy/shared/select.rb | 60 + .../core/enumerator/lazy/shared/to_enum.rb | 50 + spec/rubyspec/core/enumerator/lazy/take_spec.rb | 66 + .../core/enumerator/lazy/take_while_spec.rb | 60 + spec/rubyspec/core/enumerator/lazy/to_enum_spec.rb | 8 + spec/rubyspec/core/enumerator/lazy/zip_spec.rb | 74 + spec/rubyspec/core/enumerator/new_spec.rb | 6 + spec/rubyspec/core/enumerator/next_spec.rb | 6 + spec/rubyspec/core/enumerator/next_values_spec.rb | 55 + spec/rubyspec/core/enumerator/peek_spec.rb | 36 + spec/rubyspec/core/enumerator/peek_values_spec.rb | 57 + spec/rubyspec/core/enumerator/rewind_spec.rb | 38 + spec/rubyspec/core/enumerator/size_spec.rb | 26 + spec/rubyspec/core/enumerator/to_enum_spec.rb | 6 + spec/rubyspec/core/enumerator/with_index_spec.rb | 72 + spec/rubyspec/core/enumerator/with_object_spec.rb | 6 + .../core/enumerator/yielder/append_spec.rb | 35 + .../core/enumerator/yielder/initialize_spec.rb | 18 + .../rubyspec/core/enumerator/yielder/yield_spec.rb | 16 + spec/rubyspec/core/env/assoc_spec.rb | 23 + spec/rubyspec/core/env/clear_spec.rb | 20 + spec/rubyspec/core/env/delete_if_spec.rb | 27 + spec/rubyspec/core/env/delete_spec.rb | 24 + spec/rubyspec/core/env/each_key_spec.rb | 32 + spec/rubyspec/core/env/each_pair_spec.rb | 6 + spec/rubyspec/core/env/each_spec.rb | 6 + spec/rubyspec/core/env/each_value_spec.rb | 32 + spec/rubyspec/core/env/element_reference_spec.rb | 66 + spec/rubyspec/core/env/element_set_spec.rb | 6 + spec/rubyspec/core/env/empty_spec.rb | 23 + spec/rubyspec/core/env/fetch_spec.rb | 35 + spec/rubyspec/core/env/has_key_spec.rb | 6 + spec/rubyspec/core/env/has_value_spec.rb | 6 + spec/rubyspec/core/env/include_spec.rb | 6 + spec/rubyspec/core/env/index_spec.rb | 6 + spec/rubyspec/core/env/indexes_spec.rb | 1 + spec/rubyspec/core/env/indices_spec.rb | 1 + spec/rubyspec/core/env/inspect_spec.rb | 11 + spec/rubyspec/core/env/invert_spec.rb | 16 + spec/rubyspec/core/env/keep_if_spec.rb | 33 + spec/rubyspec/core/env/key_spec.rb | 11 + spec/rubyspec/core/env/keys_spec.rb | 14 + spec/rubyspec/core/env/length_spec.rb | 6 + spec/rubyspec/core/env/member_spec.rb | 6 + spec/rubyspec/core/env/rassoc_spec.rb | 23 + spec/rubyspec/core/env/rehash_spec.rb | 1 + spec/rubyspec/core/env/reject_spec.rb | 77 + spec/rubyspec/core/env/replace_spec.rb | 15 + spec/rubyspec/core/env/select_spec.rb | 39 + spec/rubyspec/core/env/shared/each.rb | 65 + spec/rubyspec/core/env/shared/include.rb | 11 + spec/rubyspec/core/env/shared/key.rb | 15 + spec/rubyspec/core/env/shared/length.rb | 13 + spec/rubyspec/core/env/shared/store.rb | 56 + spec/rubyspec/core/env/shared/to_hash.rb | 22 + spec/rubyspec/core/env/shared/value.rb | 11 + spec/rubyspec/core/env/shift_spec.rb | 59 + spec/rubyspec/core/env/size_spec.rb | 6 + spec/rubyspec/core/env/store_spec.rb | 6 + spec/rubyspec/core/env/to_a_spec.rb | 19 + spec/rubyspec/core/env/to_h_spec.rb | 6 + spec/rubyspec/core/env/to_hash_spec.rb | 6 + spec/rubyspec/core/env/to_s_spec.rb | 7 + spec/rubyspec/core/env/update_spec.rb | 25 + spec/rubyspec/core/env/value_spec.rb | 6 + spec/rubyspec/core/env/values_at_spec.rb | 17 + spec/rubyspec/core/env/values_spec.rb | 21 + spec/rubyspec/core/exception/args_spec.rb | 5 + spec/rubyspec/core/exception/arguments_spec.rb | 11 + spec/rubyspec/core/exception/backtrace_spec.rb | 68 + spec/rubyspec/core/exception/case_compare_spec.rb | 5 + spec/rubyspec/core/exception/cause_spec.rb | 19 + .../exception/destination_encoding_name_spec.rb | 9 + .../core/exception/destination_encoding_spec.rb | 9 + spec/rubyspec/core/exception/equal_value_spec.rb | 68 + spec/rubyspec/core/exception/errno_spec.rb | 48 + spec/rubyspec/core/exception/error_bytes_spec.rb | 5 + spec/rubyspec/core/exception/error_char_spec.rb | 5 + spec/rubyspec/core/exception/exception_spec.rb | 83 + spec/rubyspec/core/exception/exit_value_spec.rb | 5 + spec/rubyspec/core/exception/fixtures/common.rb | 64 + .../core/exception/incomplete_input_spec.rb | 5 + spec/rubyspec/core/exception/initialize_spec.rb | 1 + spec/rubyspec/core/exception/inspect_spec.rb | 20 + spec/rubyspec/core/exception/interrupt_spec.rb | 41 + spec/rubyspec/core/exception/io_error_spec.rb | 51 + spec/rubyspec/core/exception/load_error_spec.rb | 21 + spec/rubyspec/core/exception/message_spec.rb | 27 + spec/rubyspec/core/exception/name_error_spec.rb | 13 + spec/rubyspec/core/exception/name_spec.rb | 61 + spec/rubyspec/core/exception/new_spec.rb | 7 + .../core/exception/no_method_error_spec.rb | 59 + spec/rubyspec/core/exception/range_error_spec.rb | 7 + .../core/exception/readagain_bytes_spec.rb | 5 + spec/rubyspec/core/exception/reason_spec.rb | 5 + spec/rubyspec/core/exception/receiver_spec.rb | 62 + spec/rubyspec/core/exception/result_spec.rb | 29 + spec/rubyspec/core/exception/script_error_spec.rb | 15 + spec/rubyspec/core/exception/set_backtrace_spec.rb | 56 + spec/rubyspec/core/exception/shared/new.rb | 18 + .../core/exception/signal_exception_spec.rb | 74 + spec/rubyspec/core/exception/signm_spec.rb | 5 + spec/rubyspec/core/exception/signo_spec.rb | 5 + .../core/exception/source_encoding_name_spec.rb | 9 + .../core/exception/source_encoding_spec.rb | 9 + .../rubyspec/core/exception/standard_error_spec.rb | 50 + spec/rubyspec/core/exception/status_spec.rb | 5 + spec/rubyspec/core/exception/success_spec.rb | 5 + .../core/exception/system_call_error_spec.rb | 89 + .../core/exception/system_stack_error_spec.rb | 7 + spec/rubyspec/core/exception/to_s_spec.rb | 23 + spec/rubyspec/core/false/and_spec.rb | 11 + spec/rubyspec/core/false/inspect_spec.rb | 7 + spec/rubyspec/core/false/or_spec.rb | 11 + spec/rubyspec/core/false/to_s_spec.rb | 7 + spec/rubyspec/core/false/xor_spec.rb | 11 + spec/rubyspec/core/fiber/new_spec.rb | 41 + spec/rubyspec/core/fiber/resume_spec.rb | 54 + spec/rubyspec/core/fiber/yield_spec.rb | 36 + spec/rubyspec/core/file/absolute_path_spec.rb | 37 + spec/rubyspec/core/file/atime_spec.rb | 55 + spec/rubyspec/core/file/basename_spec.rb | 170 + spec/rubyspec/core/file/birthtime_spec.rb | 56 + spec/rubyspec/core/file/blockdev_spec.rb | 6 + spec/rubyspec/core/file/chardev_spec.rb | 6 + spec/rubyspec/core/file/chmod_spec.rb | 239 + spec/rubyspec/core/file/chown_spec.rb | 152 + .../rubyspec/core/file/constants/constants_spec.rb | 31 + spec/rubyspec/core/file/constants_spec.rb | 141 + spec/rubyspec/core/file/ctime_spec.rb | 51 + spec/rubyspec/core/file/delete_spec.rb | 6 + spec/rubyspec/core/file/directory_spec.rb | 10 + spec/rubyspec/core/file/dirname_spec.rb | 108 + spec/rubyspec/core/file/executable_real_spec.rb | 7 + spec/rubyspec/core/file/executable_spec.rb | 7 + spec/rubyspec/core/file/exist_spec.rb | 6 + spec/rubyspec/core/file/exists_spec.rb | 6 + spec/rubyspec/core/file/expand_path_spec.rb | 242 + spec/rubyspec/core/file/extname_spec.rb | 54 + spec/rubyspec/core/file/file_spec.rb | 16 + spec/rubyspec/core/file/fixtures/common.rb | 22 + spec/rubyspec/core/file/fixtures/do_not_remove | 0 spec/rubyspec/core/file/fixtures/file_types.rb | 65 + spec/rubyspec/core/file/flock_spec.rb | 106 + spec/rubyspec/core/file/fnmatch_spec.rb | 10 + spec/rubyspec/core/file/ftype_spec.rb | 69 + spec/rubyspec/core/file/grpowned_spec.rb | 10 + spec/rubyspec/core/file/identical_spec.rb | 6 + spec/rubyspec/core/file/initialize_spec.rb | 23 + spec/rubyspec/core/file/inspect_spec.rb | 17 + spec/rubyspec/core/file/join_spec.rb | 139 + spec/rubyspec/core/file/lchmod_spec.rb | 42 + spec/rubyspec/core/file/lchown_spec.rb | 63 + spec/rubyspec/core/file/link_spec.rb | 39 + spec/rubyspec/core/file/lstat_spec.rb | 33 + spec/rubyspec/core/file/mkfifo_spec.rb | 53 + spec/rubyspec/core/file/mtime_spec.rb | 51 + spec/rubyspec/core/file/new_spec.rb | 162 + spec/rubyspec/core/file/null_spec.rb | 15 + spec/rubyspec/core/file/open_spec.rb | 676 + spec/rubyspec/core/file/owned_spec.rb | 33 + spec/rubyspec/core/file/path_spec.rb | 29 + spec/rubyspec/core/file/pipe_spec.rb | 32 + spec/rubyspec/core/file/read_spec.rb | 6 + spec/rubyspec/core/file/readable_real_spec.rb | 7 + spec/rubyspec/core/file/readable_spec.rb | 7 + spec/rubyspec/core/file/readlink_spec.rb | 67 + spec/rubyspec/core/file/realdirpath_spec.rb | 100 + spec/rubyspec/core/file/realpath_spec.rb | 80 + spec/rubyspec/core/file/rename_spec.rb | 37 + spec/rubyspec/core/file/reopen_spec.rb | 32 + spec/rubyspec/core/file/setgid_spec.rb | 36 + spec/rubyspec/core/file/setuid_spec.rb | 38 + spec/rubyspec/core/file/shared/fnmatch.rb | 241 + spec/rubyspec/core/file/shared/open.rb | 12 + spec/rubyspec/core/file/shared/read.rb | 15 + spec/rubyspec/core/file/shared/stat.rb | 32 + spec/rubyspec/core/file/shared/unlink.rb | 63 + spec/rubyspec/core/file/size_spec.rb | 119 + spec/rubyspec/core/file/socket_spec.rb | 42 + spec/rubyspec/core/file/split_spec.rb | 63 + spec/rubyspec/core/file/stat/atime_spec.rb | 18 + spec/rubyspec/core/file/stat/birthtime_spec.rb | 27 + spec/rubyspec/core/file/stat/blksize_spec.rb | 27 + spec/rubyspec/core/file/stat/blockdev_spec.rb | 7 + spec/rubyspec/core/file/stat/blocks_spec.rb | 27 + spec/rubyspec/core/file/stat/chardev_spec.rb | 7 + spec/rubyspec/core/file/stat/comparison_spec.rb | 66 + spec/rubyspec/core/file/stat/ctime_spec.rb | 18 + spec/rubyspec/core/file/stat/dev_major_spec.rb | 23 + spec/rubyspec/core/file/stat/dev_minor_spec.rb | 23 + spec/rubyspec/core/file/stat/dev_spec.rb | 15 + spec/rubyspec/core/file/stat/directory_spec.rb | 7 + .../core/file/stat/executable_real_spec.rb | 7 + spec/rubyspec/core/file/stat/executable_spec.rb | 7 + spec/rubyspec/core/file/stat/file_spec.rb | 7 + spec/rubyspec/core/file/stat/fixtures/classes.rb | 5 + spec/rubyspec/core/file/stat/ftype_spec.rb | 65 + spec/rubyspec/core/file/stat/gid_spec.rb | 19 + spec/rubyspec/core/file/stat/grpowned_spec.rb | 7 + spec/rubyspec/core/file/stat/ino_spec.rb | 38 + spec/rubyspec/core/file/stat/inspect_spec.rb | 26 + spec/rubyspec/core/file/stat/mode_spec.rb | 19 + spec/rubyspec/core/file/stat/mtime_spec.rb | 18 + spec/rubyspec/core/file/stat/new_spec.rb | 30 + spec/rubyspec/core/file/stat/nlink_spec.rb | 21 + spec/rubyspec/core/file/stat/owned_spec.rb | 31 + spec/rubyspec/core/file/stat/pipe_spec.rb | 32 + spec/rubyspec/core/file/stat/rdev_major_spec.rb | 31 + spec/rubyspec/core/file/stat/rdev_minor_spec.rb | 31 + spec/rubyspec/core/file/stat/rdev_spec.rb | 15 + spec/rubyspec/core/file/stat/readable_real_spec.rb | 7 + spec/rubyspec/core/file/stat/readable_spec.rb | 7 + spec/rubyspec/core/file/stat/setgid_spec.rb | 11 + spec/rubyspec/core/file/stat/setuid_spec.rb | 11 + spec/rubyspec/core/file/stat/size_spec.rb | 21 + spec/rubyspec/core/file/stat/socket_spec.rb | 11 + spec/rubyspec/core/file/stat/sticky_spec.rb | 11 + spec/rubyspec/core/file/stat/symlink_spec.rb | 7 + spec/rubyspec/core/file/stat/uid_spec.rb | 18 + .../rubyspec/core/file/stat/world_readable_spec.rb | 11 + .../rubyspec/core/file/stat/world_writable_spec.rb | 11 + spec/rubyspec/core/file/stat/writable_real_spec.rb | 7 + spec/rubyspec/core/file/stat/writable_spec.rb | 7 + spec/rubyspec/core/file/stat/zero_spec.rb | 7 + spec/rubyspec/core/file/stat_spec.rb | 45 + spec/rubyspec/core/file/sticky_spec.rb | 50 + spec/rubyspec/core/file/symlink_spec.rb | 57 + spec/rubyspec/core/file/to_path_spec.rb | 49 + spec/rubyspec/core/file/truncate_spec.rb | 177 + spec/rubyspec/core/file/umask_spec.rb | 60 + spec/rubyspec/core/file/unlink_spec.rb | 6 + spec/rubyspec/core/file/utime_spec.rb | 36 + spec/rubyspec/core/file/world_readable_spec.rb | 12 + spec/rubyspec/core/file/world_writable_spec.rb | 12 + spec/rubyspec/core/file/writable_real_spec.rb | 7 + spec/rubyspec/core/file/writable_spec.rb | 7 + spec/rubyspec/core/file/zero_spec.rb | 13 + spec/rubyspec/core/filetest/blockdev_spec.rb | 6 + spec/rubyspec/core/filetest/chardev_spec.rb | 6 + spec/rubyspec/core/filetest/directory_spec.rb | 10 + .../rubyspec/core/filetest/executable_real_spec.rb | 7 + spec/rubyspec/core/filetest/executable_spec.rb | 7 + spec/rubyspec/core/filetest/exist_spec.rb | 6 + spec/rubyspec/core/filetest/exists_spec.rb | 6 + spec/rubyspec/core/filetest/file_spec.rb | 10 + spec/rubyspec/core/filetest/grpowned_spec.rb | 10 + spec/rubyspec/core/filetest/identical_spec.rb | 6 + spec/rubyspec/core/filetest/owned_spec.rb | 10 + spec/rubyspec/core/filetest/pipe_spec.rb | 10 + spec/rubyspec/core/filetest/readable_real_spec.rb | 7 + spec/rubyspec/core/filetest/readable_spec.rb | 7 + spec/rubyspec/core/filetest/setgid_spec.rb | 10 + spec/rubyspec/core/filetest/setuid_spec.rb | 10 + spec/rubyspec/core/filetest/size_spec.rb | 34 + spec/rubyspec/core/filetest/socket_spec.rb | 10 + spec/rubyspec/core/filetest/sticky_spec.rb | 7 + spec/rubyspec/core/filetest/symlink_spec.rb | 10 + spec/rubyspec/core/filetest/world_readable_spec.rb | 5 + spec/rubyspec/core/filetest/world_writable_spec.rb | 5 + spec/rubyspec/core/filetest/writable_real_spec.rb | 7 + spec/rubyspec/core/filetest/writable_spec.rb | 7 + spec/rubyspec/core/filetest/zero_spec.rb | 13 + spec/rubyspec/core/fixnum/abs_spec.rb | 7 + spec/rubyspec/core/fixnum/bit_and_spec.rb | 25 + spec/rubyspec/core/fixnum/bit_length_spec.rb | 42 + spec/rubyspec/core/fixnum/bit_or_spec.rb | 26 + spec/rubyspec/core/fixnum/bit_xor_spec.rb | 24 + spec/rubyspec/core/fixnum/case_compare_spec.rb | 6 + spec/rubyspec/core/fixnum/coerce_spec.rb | 39 + spec/rubyspec/core/fixnum/comparison_spec.rb | 26 + spec/rubyspec/core/fixnum/complement_spec.rb | 10 + spec/rubyspec/core/fixnum/div_spec.rb | 44 + spec/rubyspec/core/fixnum/divide_spec.rb | 35 + spec/rubyspec/core/fixnum/divmod_spec.rb | 35 + .../rubyspec/core/fixnum/element_reference_spec.rb | 80 + spec/rubyspec/core/fixnum/equal_value_spec.rb | 6 + spec/rubyspec/core/fixnum/even_spec.rb | 23 + spec/rubyspec/core/fixnum/exponent_spec.rb | 76 + spec/rubyspec/core/fixnum/fdiv_spec.rb | 49 + spec/rubyspec/core/fixnum/fixnum_spec.rb | 7 + spec/rubyspec/core/fixnum/gt_spec.rb | 19 + spec/rubyspec/core/fixnum/gte_spec.rb | 20 + spec/rubyspec/core/fixnum/hash_spec.rb | 11 + spec/rubyspec/core/fixnum/left_shift_spec.rb | 91 + spec/rubyspec/core/fixnum/lt_spec.rb | 19 + spec/rubyspec/core/fixnum/lte_spec.rb | 20 + spec/rubyspec/core/fixnum/magnitude_spec.rb | 6 + spec/rubyspec/core/fixnum/minus_spec.rb | 29 + spec/rubyspec/core/fixnum/modulo_spec.rb | 10 + spec/rubyspec/core/fixnum/multiply_spec.rb | 27 + spec/rubyspec/core/fixnum/odd_spec.rb | 23 + spec/rubyspec/core/fixnum/plus_spec.rb | 29 + spec/rubyspec/core/fixnum/right_shift_spec.rb | 91 + spec/rubyspec/core/fixnum/shared/abs.rb | 9 + spec/rubyspec/core/fixnum/shared/equal.rb | 24 + spec/rubyspec/core/fixnum/shared/modulo.rb | 42 + spec/rubyspec/core/fixnum/size_spec.rb | 19 + spec/rubyspec/core/fixnum/succ_spec.rb | 15 + spec/rubyspec/core/fixnum/to_f_spec.rb | 9 + spec/rubyspec/core/fixnum/to_s_spec.rb | 50 + spec/rubyspec/core/fixnum/uminus_spec.rb | 16 + spec/rubyspec/core/fixnum/zero_spec.rb | 9 + spec/rubyspec/core/float/abs_spec.rb | 5 + spec/rubyspec/core/float/angle_spec.rb | 5 + spec/rubyspec/core/float/arg_spec.rb | 5 + spec/rubyspec/core/float/case_compare_spec.rb | 6 + spec/rubyspec/core/float/ceil_spec.rb | 13 + spec/rubyspec/core/float/coerce_spec.rb | 18 + spec/rubyspec/core/float/comparison_spec.rb | 36 + spec/rubyspec/core/float/constants_spec.rb | 55 + spec/rubyspec/core/float/denominator_spec.rb | 29 + spec/rubyspec/core/float/divide_spec.rb | 36 + spec/rubyspec/core/float/divmod_spec.rb | 43 + spec/rubyspec/core/float/eql_spec.rb | 16 + spec/rubyspec/core/float/equal_value_spec.rb | 6 + spec/rubyspec/core/float/exponent_spec.rb | 15 + spec/rubyspec/core/float/fdiv_spec.rb | 6 + spec/rubyspec/core/float/finite_spec.rb | 19 + spec/rubyspec/core/float/fixtures/coerce.rb | 15 + spec/rubyspec/core/float/float_spec.rb | 7 + spec/rubyspec/core/float/floor_spec.rb | 13 + spec/rubyspec/core/float/gt_spec.rb | 14 + spec/rubyspec/core/float/gte_spec.rb | 14 + spec/rubyspec/core/float/hash_spec.rb | 11 + spec/rubyspec/core/float/infinite_spec.rb | 19 + spec/rubyspec/core/float/lt_spec.rb | 14 + spec/rubyspec/core/float/lte_spec.rb | 15 + spec/rubyspec/core/float/magnitude_spec.rb | 5 + spec/rubyspec/core/float/minus_spec.rb | 9 + spec/rubyspec/core/float/modulo_spec.rb | 10 + spec/rubyspec/core/float/multiply_spec.rb | 14 + spec/rubyspec/core/float/nan_spec.rb | 9 + spec/rubyspec/core/float/next_float_spec.rb | 49 + spec/rubyspec/core/float/numerator_spec.rb | 39 + spec/rubyspec/core/float/phase_spec.rb | 5 + spec/rubyspec/core/float/plus_spec.rb | 9 + spec/rubyspec/core/float/prev_float_spec.rb | 49 + spec/rubyspec/core/float/quo_spec.rb | 6 + spec/rubyspec/core/float/rationalize_spec.rb | 43 + spec/rubyspec/core/float/round_spec.rb | 87 + spec/rubyspec/core/float/shared/abs.rb | 21 + spec/rubyspec/core/float/shared/equal.rb | 17 + spec/rubyspec/core/float/shared/modulo.rb | 42 + spec/rubyspec/core/float/shared/quo.rb | 59 + spec/rubyspec/core/float/shared/to_i.rb | 10 + spec/rubyspec/core/float/to_f_spec.rb | 9 + spec/rubyspec/core/float/to_i_spec.rb | 6 + spec/rubyspec/core/float/to_int_spec.rb | 6 + spec/rubyspec/core/float/to_r_spec.rb | 5 + spec/rubyspec/core/float/to_s_spec.rb | 120 + spec/rubyspec/core/float/truncate_spec.rb | 6 + spec/rubyspec/core/float/uminus_spec.rb | 28 + spec/rubyspec/core/float/uplus_spec.rb | 9 + spec/rubyspec/core/float/zero_spec.rb | 9 + spec/rubyspec/core/gc/count_spec.rb | 7 + spec/rubyspec/core/gc/disable_spec.rb | 18 + spec/rubyspec/core/gc/enable_spec.rb | 13 + spec/rubyspec/core/gc/garbage_collect_spec.rb | 15 + spec/rubyspec/core/gc/profiler/clear_spec.rb | 5 + spec/rubyspec/core/gc/profiler/disable_spec.rb | 16 + spec/rubyspec/core/gc/profiler/enable_spec.rb | 17 + spec/rubyspec/core/gc/profiler/enabled_spec.rb | 21 + spec/rubyspec/core/gc/profiler/report_spec.rb | 5 + spec/rubyspec/core/gc/profiler/result_spec.rb | 7 + spec/rubyspec/core/gc/profiler/total_time_spec.rb | 7 + spec/rubyspec/core/gc/start_spec.rb | 8 + spec/rubyspec/core/gc/stress_spec.rb | 27 + spec/rubyspec/core/hash/allocate_spec.rb | 15 + spec/rubyspec/core/hash/any_spec.rb | 30 + spec/rubyspec/core/hash/assoc_spec.rb | 50 + spec/rubyspec/core/hash/clear_spec.rb | 32 + spec/rubyspec/core/hash/clone_spec.rb | 13 + spec/rubyspec/core/hash/compact_spec.rb | 61 + .../rubyspec/core/hash/compare_by_identity_spec.rb | 140 + spec/rubyspec/core/hash/constructor_spec.rb | 110 + spec/rubyspec/core/hash/default_proc_spec.rb | 80 + spec/rubyspec/core/hash/default_spec.rb | 46 + spec/rubyspec/core/hash/delete_if_spec.rb | 44 + spec/rubyspec/core/hash/delete_spec.rb | 44 + spec/rubyspec/core/hash/dig_spec.rb | 68 + spec/rubyspec/core/hash/each_key_spec.rb | 23 + spec/rubyspec/core/hash/each_pair_spec.rb | 11 + spec/rubyspec/core/hash/each_spec.rb | 11 + spec/rubyspec/core/hash/each_value_spec.rb | 23 + spec/rubyspec/core/hash/element_reference_spec.rb | 120 + spec/rubyspec/core/hash/element_set_spec.rb | 7 + spec/rubyspec/core/hash/empty_spec.rb | 15 + spec/rubyspec/core/hash/eql_spec.rb | 9 + spec/rubyspec/core/hash/equal_value_spec.rb | 18 + spec/rubyspec/core/hash/fetch_spec.rb | 36 + spec/rubyspec/core/hash/fetch_values_spec.rb | 35 + spec/rubyspec/core/hash/fixtures/classes.rb | 68 + spec/rubyspec/core/hash/flatten_spec.rb | 62 + spec/rubyspec/core/hash/gt_spec.rb | 44 + spec/rubyspec/core/hash/gte_spec.rb | 44 + spec/rubyspec/core/hash/has_key_spec.rb | 8 + spec/rubyspec/core/hash/has_value_spec.rb | 8 + spec/rubyspec/core/hash/hash_spec.rb | 36 + spec/rubyspec/core/hash/include_spec.rb | 7 + spec/rubyspec/core/hash/index_spec.rb | 7 + spec/rubyspec/core/hash/initialize_spec.rb | 40 + spec/rubyspec/core/hash/inspect_spec.rb | 7 + spec/rubyspec/core/hash/invert_spec.rb | 27 + spec/rubyspec/core/hash/keep_if_spec.rb | 37 + spec/rubyspec/core/hash/key_spec.rb | 12 + spec/rubyspec/core/hash/keys_spec.rb | 23 + spec/rubyspec/core/hash/length_spec.rb | 7 + spec/rubyspec/core/hash/lt_spec.rb | 44 + spec/rubyspec/core/hash/lte_spec.rb | 44 + spec/rubyspec/core/hash/member_spec.rb | 7 + spec/rubyspec/core/hash/merge_spec.rb | 77 + spec/rubyspec/core/hash/new_spec.rb | 36 + spec/rubyspec/core/hash/rassoc_spec.rb | 42 + spec/rubyspec/core/hash/rehash_spec.rb | 42 + spec/rubyspec/core/hash/reject_spec.rb | 100 + spec/rubyspec/core/hash/replace_spec.rb | 7 + spec/rubyspec/core/hash/select_spec.rb | 83 + spec/rubyspec/core/hash/shared/comparison.rb | 15 + spec/rubyspec/core/hash/shared/each.rb | 68 + spec/rubyspec/core/hash/shared/eql.rb | 216 + spec/rubyspec/core/hash/shared/equal.rb | 90 + spec/rubyspec/core/hash/shared/greater_than.rb | 23 + spec/rubyspec/core/hash/shared/index.rb | 27 + spec/rubyspec/core/hash/shared/iteration.rb | 19 + spec/rubyspec/core/hash/shared/key.rb | 38 + spec/rubyspec/core/hash/shared/length.rb | 12 + spec/rubyspec/core/hash/shared/less_than.rb | 23 + spec/rubyspec/core/hash/shared/replace.rb | 51 + spec/rubyspec/core/hash/shared/store.rb | 98 + spec/rubyspec/core/hash/shared/to_s.rb | 69 + spec/rubyspec/core/hash/shared/update.rb | 59 + spec/rubyspec/core/hash/shared/value.rb | 14 + spec/rubyspec/core/hash/shared/values_at.rb | 9 + spec/rubyspec/core/hash/shift_spec.rb | 64 + spec/rubyspec/core/hash/size_spec.rb | 7 + spec/rubyspec/core/hash/sort_spec.rb | 17 + spec/rubyspec/core/hash/store_spec.rb | 7 + spec/rubyspec/core/hash/to_a_spec.rb | 37 + spec/rubyspec/core/hash/to_h_spec.rb | 34 + spec/rubyspec/core/hash/to_hash_spec.rb | 14 + spec/rubyspec/core/hash/to_proc_spec.rb | 89 + spec/rubyspec/core/hash/to_s_spec.rb | 7 + spec/rubyspec/core/hash/transform_values_spec.rb | 71 + spec/rubyspec/core/hash/try_convert_spec.rb | 50 + spec/rubyspec/core/hash/update_spec.rb | 7 + spec/rubyspec/core/hash/value_spec.rb | 8 + spec/rubyspec/core/hash/values_at_spec.rb | 7 + spec/rubyspec/core/hash/values_spec.rb | 10 + spec/rubyspec/core/integer/ceil_spec.rb | 6 + spec/rubyspec/core/integer/chr_spec.rb | 239 + spec/rubyspec/core/integer/denominator_spec.rb | 20 + spec/rubyspec/core/integer/downto_spec.rb | 69 + spec/rubyspec/core/integer/even_spec.rb | 20 + spec/rubyspec/core/integer/floor_spec.rb | 6 + spec/rubyspec/core/integer/gcd_spec.rb | 58 + spec/rubyspec/core/integer/gcdlcm_spec.rb | 53 + spec/rubyspec/core/integer/integer_spec.rb | 15 + spec/rubyspec/core/integer/lcm_spec.rb | 58 + spec/rubyspec/core/integer/next_spec.rb | 6 + spec/rubyspec/core/integer/numerator_spec.rb | 18 + spec/rubyspec/core/integer/odd_spec.rb | 18 + spec/rubyspec/core/integer/ord_spec.rb | 17 + spec/rubyspec/core/integer/pred_spec.rb | 11 + spec/rubyspec/core/integer/rationalize_spec.rb | 39 + spec/rubyspec/core/integer/round_spec.rb | 77 + spec/rubyspec/core/integer/shared/next.rb | 25 + spec/rubyspec/core/integer/shared/to_i.rb | 8 + spec/rubyspec/core/integer/succ_spec.rb | 6 + spec/rubyspec/core/integer/times_spec.rb | 79 + spec/rubyspec/core/integer/to_i_spec.rb | 6 + spec/rubyspec/core/integer/to_int_spec.rb | 6 + spec/rubyspec/core/integer/to_r_spec.rb | 26 + spec/rubyspec/core/integer/truncate_spec.rb | 6 + spec/rubyspec/core/integer/upto_spec.rb | 69 + spec/rubyspec/core/io/advise_spec.rb | 96 + spec/rubyspec/core/io/binmode_spec.rb | 60 + spec/rubyspec/core/io/binread_spec.rb | 49 + spec/rubyspec/core/io/binwrite_spec.rb | 8 + spec/rubyspec/core/io/bytes_spec.rb | 43 + spec/rubyspec/core/io/chars_spec.rb | 12 + spec/rubyspec/core/io/close_on_exec_spec.rb | 121 + spec/rubyspec/core/io/close_read_spec.rb | 80 + spec/rubyspec/core/io/close_spec.rb | 82 + spec/rubyspec/core/io/close_write_spec.rb | 84 + spec/rubyspec/core/io/closed_spec.rb | 20 + spec/rubyspec/core/io/codepoints_spec.rb | 25 + spec/rubyspec/core/io/constants_spec.rb | 19 + spec/rubyspec/core/io/copy_stream_spec.rb | 282 + spec/rubyspec/core/io/dup_spec.rb | 69 + spec/rubyspec/core/io/each_byte_spec.rb | 57 + spec/rubyspec/core/io/each_char_spec.rb | 12 + spec/rubyspec/core/io/each_codepoint_spec.rb | 45 + spec/rubyspec/core/io/each_line_spec.rb | 11 + spec/rubyspec/core/io/each_spec.rb | 11 + spec/rubyspec/core/io/eof_spec.rb | 107 + spec/rubyspec/core/io/external_encoding_spec.rb | 218 + spec/rubyspec/core/io/fcntl_spec.rb | 8 + spec/rubyspec/core/io/fdatasync_spec.rb | 5 + spec/rubyspec/core/io/fileno_spec.rb | 12 + spec/rubyspec/core/io/fixtures/bom_UTF-16BE.txt | Bin 0 -> 20 bytes spec/rubyspec/core/io/fixtures/bom_UTF-16LE.txt | Bin 0 -> 20 bytes spec/rubyspec/core/io/fixtures/bom_UTF-32BE.txt | Bin 0 -> 40 bytes spec/rubyspec/core/io/fixtures/bom_UTF-32LE.txt | Bin 0 -> 40 bytes spec/rubyspec/core/io/fixtures/bom_UTF-8.txt | 1 + spec/rubyspec/core/io/fixtures/classes.rb | 178 + spec/rubyspec/core/io/fixtures/copy_stream.txt | 6 + spec/rubyspec/core/io/fixtures/empty.txt | 0 spec/rubyspec/core/io/fixtures/incomplete.txt | 1 + spec/rubyspec/core/io/fixtures/lines.txt | 9 + spec/rubyspec/core/io/fixtures/no_bom_UTF-8.txt | 1 + spec/rubyspec/core/io/fixtures/numbered_lines.txt | 5 + spec/rubyspec/core/io/fixtures/one_byte.txt | 1 + spec/rubyspec/core/io/fixtures/read_binary.txt | 1 + spec/rubyspec/core/io/fixtures/read_euc_jp.txt | 1 + spec/rubyspec/core/io/fixtures/read_text.txt | 1 + spec/rubyspec/core/io/fixtures/reopen_stdout.rb | 3 + spec/rubyspec/core/io/flush_spec.rb | 8 + spec/rubyspec/core/io/for_fd_spec.rb | 10 + spec/rubyspec/core/io/foreach_spec.rb | 81 + spec/rubyspec/core/io/fsync_spec.rb | 24 + spec/rubyspec/core/io/getbyte_spec.rb | 42 + spec/rubyspec/core/io/getc_spec.rb | 42 + spec/rubyspec/core/io/gets_spec.rb | 313 + spec/rubyspec/core/io/initialize_spec.rb | 53 + spec/rubyspec/core/io/inspect_spec.rb | 23 + spec/rubyspec/core/io/internal_encoding_spec.rb | 140 + spec/rubyspec/core/io/io_spec.rb | 11 + spec/rubyspec/core/io/ioctl_spec.rb | 32 + spec/rubyspec/core/io/isatty_spec.rb | 6 + spec/rubyspec/core/io/lineno_spec.rb | 95 + spec/rubyspec/core/io/lines_spec.rb | 42 + spec/rubyspec/core/io/new_spec.rb | 10 + spec/rubyspec/core/io/open_spec.rb | 86 + spec/rubyspec/core/io/output_spec.rb | 27 + spec/rubyspec/core/io/pid_spec.rb | 35 + spec/rubyspec/core/io/pipe_spec.rb | 214 + spec/rubyspec/core/io/popen_spec.rb | 286 + spec/rubyspec/core/io/pos_spec.rb | 12 + spec/rubyspec/core/io/print_spec.rb | 54 + spec/rubyspec/core/io/printf_spec.rb | 32 + spec/rubyspec/core/io/putc_spec.rb | 11 + spec/rubyspec/core/io/puts_spec.rb | 144 + spec/rubyspec/core/io/read_nonblock_spec.rb | 85 + spec/rubyspec/core/io/read_spec.rb | 616 + spec/rubyspec/core/io/readbyte_spec.rb | 26 + spec/rubyspec/core/io/readchar_spec.rb | 44 + spec/rubyspec/core/io/readline_spec.rb | 45 + spec/rubyspec/core/io/readlines_spec.rb | 210 + spec/rubyspec/core/io/readpartial_spec.rb | 96 + spec/rubyspec/core/io/reopen_spec.rb | 302 + spec/rubyspec/core/io/rewind_spec.rb | 38 + spec/rubyspec/core/io/seek_spec.rb | 79 + spec/rubyspec/core/io/select_spec.rb | 115 + spec/rubyspec/core/io/set_encoding_spec.rb | 193 + spec/rubyspec/core/io/shared/binwrite.rb | 78 + spec/rubyspec/core/io/shared/chars.rb | 73 + spec/rubyspec/core/io/shared/codepoints.rb | 54 + spec/rubyspec/core/io/shared/each.rb | 135 + spec/rubyspec/core/io/shared/gets_ascii.rb | 19 + spec/rubyspec/core/io/shared/new.rb | 378 + spec/rubyspec/core/io/shared/pos.rb | 72 + spec/rubyspec/core/io/shared/readlines.rb | 204 + spec/rubyspec/core/io/shared/tty.rb | 25 + spec/rubyspec/core/io/shared/write.rb | 72 + spec/rubyspec/core/io/stat_spec.rb | 24 + spec/rubyspec/core/io/sync_spec.rb | 64 + spec/rubyspec/core/io/sysopen_spec.rb | 50 + spec/rubyspec/core/io/sysread_spec.rb | 82 + spec/rubyspec/core/io/sysseek_spec.rb | 44 + spec/rubyspec/core/io/syswrite_spec.rb | 54 + spec/rubyspec/core/io/tell_spec.rb | 7 + spec/rubyspec/core/io/to_i_spec.rb | 12 + spec/rubyspec/core/io/to_io_spec.rb | 21 + spec/rubyspec/core/io/try_convert_spec.rb | 49 + spec/rubyspec/core/io/tty_spec.rb | 6 + spec/rubyspec/core/io/ungetbyte_spec.rb | 48 + spec/rubyspec/core/io/ungetc_spec.rb | 119 + spec/rubyspec/core/io/write_nonblock_spec.rb | 76 + spec/rubyspec/core/io/write_spec.rb | 157 + spec/rubyspec/core/kernel/Array_spec.rb | 97 + spec/rubyspec/core/kernel/Complex_spec.rb | 6 + spec/rubyspec/core/kernel/Float_spec.rb | 316 + spec/rubyspec/core/kernel/Hash_spec.rb | 57 + spec/rubyspec/core/kernel/Integer_spec.rb | 697 + spec/rubyspec/core/kernel/Rational_spec.rb | 6 + spec/rubyspec/core/kernel/String_spec.rb | 106 + spec/rubyspec/core/kernel/__callee___spec.rb | 48 + spec/rubyspec/core/kernel/__dir___spec.rb | 13 + spec/rubyspec/core/kernel/__method___spec.rb | 40 + spec/rubyspec/core/kernel/abort_spec.rb | 15 + spec/rubyspec/core/kernel/at_exit_spec.rb | 44 + spec/rubyspec/core/kernel/autoload_spec.rb | 122 + spec/rubyspec/core/kernel/backtick_spec.rb | 80 + spec/rubyspec/core/kernel/binding_spec.rb | 51 + spec/rubyspec/core/kernel/block_given_spec.rb | 38 + spec/rubyspec/core/kernel/caller_locations_spec.rb | 32 + spec/rubyspec/core/kernel/caller_spec.rb | 31 + spec/rubyspec/core/kernel/case_compare_spec.rb | 135 + spec/rubyspec/core/kernel/catch_spec.rb | 127 + spec/rubyspec/core/kernel/chomp_spec.rb | 67 + spec/rubyspec/core/kernel/chop_spec.rb | 55 + spec/rubyspec/core/kernel/class_spec.rb | 26 + spec/rubyspec/core/kernel/clone_spec.rb | 108 + spec/rubyspec/core/kernel/comparison_spec.rb | 31 + .../core/kernel/define_singleton_method_spec.rb | 101 + spec/rubyspec/core/kernel/display_spec.rb | 6 + spec/rubyspec/core/kernel/dup_spec.rb | 67 + spec/rubyspec/core/kernel/enum_for_spec.rb | 5 + spec/rubyspec/core/kernel/eql_spec.rb | 11 + spec/rubyspec/core/kernel/equal_value_spec.rb | 15 + spec/rubyspec/core/kernel/eval_spec.rb | 216 + spec/rubyspec/core/kernel/exec_spec.rb | 18 + spec/rubyspec/core/kernel/exit_spec.rb | 27 + spec/rubyspec/core/kernel/extend_spec.rb | 79 + spec/rubyspec/core/kernel/fail_spec.rb | 43 + spec/rubyspec/core/kernel/fixtures/__callee__.rb | 34 + spec/rubyspec/core/kernel/fixtures/__method__.rb | 34 + spec/rubyspec/core/kernel/fixtures/autoload_b.rb | 5 + spec/rubyspec/core/kernel/fixtures/autoload_c.rb | 5 + spec/rubyspec/core/kernel/fixtures/autoload_d.rb | 5 + .../core/kernel/fixtures/autoload_frozen.rb | 7 + spec/rubyspec/core/kernel/fixtures/caller.rb | 7 + .../core/kernel/fixtures/caller_locations.rb | 7 + spec/rubyspec/core/kernel/fixtures/chomp.rb | 4 + spec/rubyspec/core/kernel/fixtures/chomp_f.rb | 4 + spec/rubyspec/core/kernel/fixtures/chop.rb | 4 + spec/rubyspec/core/kernel/fixtures/chop_f.rb | 4 + spec/rubyspec/core/kernel/fixtures/classes.rb | 425 + spec/rubyspec/core/kernel/fixtures/eval_locals.rb | 6 + .../kernel/fixtures/eval_return_with_lambda.rb | 12 + .../kernel/fixtures/eval_return_without_lambda.rb | 14 + spec/rubyspec/core/kernel/fixtures/test.rb | 362 + spec/rubyspec/core/kernel/fork_spec.rb | 15 + spec/rubyspec/core/kernel/format_spec.rb | 14 + spec/rubyspec/core/kernel/freeze_spec.rb | 67 + spec/rubyspec/core/kernel/frozen_spec.rb | 52 + spec/rubyspec/core/kernel/gets_spec.rb | 12 + spec/rubyspec/core/kernel/global_variables_spec.rb | 26 + spec/rubyspec/core/kernel/gsub_spec.rb | 96 + spec/rubyspec/core/kernel/inspect_spec.rb | 31 + spec/rubyspec/core/kernel/instance_of_spec.rb | 40 + .../core/kernel/instance_variable_defined_spec.rb | 41 + .../core/kernel/instance_variable_get_spec.rb | 105 + .../core/kernel/instance_variable_set_spec.rb | 93 + .../core/kernel/instance_variables_spec.rb | 27 + spec/rubyspec/core/kernel/is_a_spec.rb | 6 + spec/rubyspec/core/kernel/iterator_spec.rb | 12 + spec/rubyspec/core/kernel/itself_spec.rb | 10 + spec/rubyspec/core/kernel/kind_of_spec.rb | 6 + spec/rubyspec/core/kernel/lambda_spec.rb | 86 + spec/rubyspec/core/kernel/load_spec.rb | 40 + spec/rubyspec/core/kernel/local_variables_spec.rb | 37 + spec/rubyspec/core/kernel/loop_spec.rb | 81 + spec/rubyspec/core/kernel/match_spec.rb | 14 + spec/rubyspec/core/kernel/method_spec.rb | 37 + spec/rubyspec/core/kernel/methods_spec.rb | 101 + spec/rubyspec/core/kernel/nil_spec.rb | 6 + spec/rubyspec/core/kernel/not_match_spec.rb | 21 + spec/rubyspec/core/kernel/object_id_spec.rb | 6 + spec/rubyspec/core/kernel/open_spec.rb | 141 + spec/rubyspec/core/kernel/p_spec.rb | 79 + spec/rubyspec/core/kernel/print_spec.rb | 12 + spec/rubyspec/core/kernel/printf_spec.rb | 34 + spec/rubyspec/core/kernel/private_methods_spec.rb | 69 + spec/rubyspec/core/kernel/proc_spec.rb | 50 + .../rubyspec/core/kernel/protected_methods_spec.rb | 69 + spec/rubyspec/core/kernel/public_method_spec.rb | 32 + spec/rubyspec/core/kernel/public_methods_spec.rb | 76 + spec/rubyspec/core/kernel/public_send_spec.rb | 108 + spec/rubyspec/core/kernel/putc_spec.rb | 39 + spec/rubyspec/core/kernel/puts_spec.rb | 29 + spec/rubyspec/core/kernel/raise_spec.rb | 17 + spec/rubyspec/core/kernel/rand_spec.rb | 139 + spec/rubyspec/core/kernel/readline_spec.rb | 12 + spec/rubyspec/core/kernel/readlines_spec.rb | 12 + .../core/kernel/remove_instance_variable_spec.rb | 59 + spec/rubyspec/core/kernel/require_relative_spec.rb | 349 + spec/rubyspec/core/kernel/require_spec.rb | 36 + .../core/kernel/respond_to_missing_spec.rb | 100 + spec/rubyspec/core/kernel/respond_to_spec.rb | 73 + spec/rubyspec/core/kernel/select_spec.rb | 20 + spec/rubyspec/core/kernel/send_spec.rb | 68 + spec/rubyspec/core/kernel/set_trace_func_spec.rb | 12 + spec/rubyspec/core/kernel/shared/dup_clone.rb | 125 + spec/rubyspec/core/kernel/shared/kind_of.rb | 44 + spec/rubyspec/core/kernel/shared/lambda.rb | 9 + spec/rubyspec/core/kernel/shared/load.rb | 139 + spec/rubyspec/core/kernel/shared/method.rb | 50 + spec/rubyspec/core/kernel/shared/require.rb | 703 + spec/rubyspec/core/kernel/singleton_class_spec.rb | 27 + .../rubyspec/core/kernel/singleton_methods_spec.rb | 180 + spec/rubyspec/core/kernel/sleep_spec.rb | 52 + spec/rubyspec/core/kernel/spawn_spec.rb | 25 + spec/rubyspec/core/kernel/sprintf_spec.rb | 310 + spec/rubyspec/core/kernel/srand_spec.rb | 61 + spec/rubyspec/core/kernel/sub_spec.rb | 26 + spec/rubyspec/core/kernel/syscall_spec.rb | 12 + spec/rubyspec/core/kernel/system_spec.rb | 107 + spec/rubyspec/core/kernel/taint_spec.rb | 45 + spec/rubyspec/core/kernel/tainted_spec.rb | 12 + spec/rubyspec/core/kernel/tap_spec.rb | 13 + spec/rubyspec/core/kernel/test_spec.rb | 98 + spec/rubyspec/core/kernel/throw_spec.rb | 80 + spec/rubyspec/core/kernel/to_enum_spec.rb | 5 + spec/rubyspec/core/kernel/to_s_spec.rb | 16 + spec/rubyspec/core/kernel/trace_var_spec.rb | 54 + spec/rubyspec/core/kernel/trap_spec.rb | 12 + spec/rubyspec/core/kernel/trust_spec.rb | 25 + spec/rubyspec/core/kernel/untaint_spec.rb | 25 + spec/rubyspec/core/kernel/untrace_var_spec.rb | 12 + spec/rubyspec/core/kernel/untrust_spec.rb | 25 + spec/rubyspec/core/kernel/untrusted_spec.rb | 28 + spec/rubyspec/core/kernel/warn_spec.rb | 79 + spec/rubyspec/core/main/define_method_spec.rb | 28 + spec/rubyspec/core/main/fixtures/classes.rb | 15 + .../rubyspec/core/main/fixtures/wrapped_include.rb | 1 + spec/rubyspec/core/main/include_spec.rb | 16 + spec/rubyspec/core/main/private_spec.rb | 23 + spec/rubyspec/core/main/public_spec.rb | 23 + spec/rubyspec/core/main/to_s_spec.rb | 7 + spec/rubyspec/core/marshal/dump_spec.rb | 573 + .../rubyspec/core/marshal/fixtures/marshal_data.rb | 420 + spec/rubyspec/core/marshal/fixtures/random.dump | Bin 0 -> 2520 bytes spec/rubyspec/core/marshal/float_spec.rb | 77 + spec/rubyspec/core/marshal/load_spec.rb | 6 + spec/rubyspec/core/marshal/major_version_spec.rb | 7 + spec/rubyspec/core/marshal/minor_version_spec.rb | 7 + spec/rubyspec/core/marshal/restore_spec.rb | 6 + spec/rubyspec/core/marshal/shared/load.rb | 830 + spec/rubyspec/core/matchdata/begin_spec.rb | 30 + spec/rubyspec/core/matchdata/captures_spec.rb | 7 + .../core/matchdata/element_reference_spec.rb | 87 + spec/rubyspec/core/matchdata/end_spec.rb | 30 + spec/rubyspec/core/matchdata/eql_spec.rb | 6 + spec/rubyspec/core/matchdata/equal_value_spec.rb | 6 + spec/rubyspec/core/matchdata/hash_spec.rb | 5 + spec/rubyspec/core/matchdata/inspect_spec.rb | 17 + spec/rubyspec/core/matchdata/length_spec.rb | 6 + spec/rubyspec/core/matchdata/names_spec.rb | 33 + spec/rubyspec/core/matchdata/offset_spec.rb | 30 + spec/rubyspec/core/matchdata/post_match_spec.rb | 36 + spec/rubyspec/core/matchdata/pre_match_spec.rb | 36 + spec/rubyspec/core/matchdata/regexp_spec.rb | 13 + spec/rubyspec/core/matchdata/shared/eql.rb | 26 + spec/rubyspec/core/matchdata/shared/length.rb | 5 + spec/rubyspec/core/matchdata/size_spec.rb | 6 + spec/rubyspec/core/matchdata/string_spec.rb | 14 + spec/rubyspec/core/matchdata/to_a_spec.rb | 7 + spec/rubyspec/core/matchdata/to_s_spec.rb | 7 + spec/rubyspec/core/matchdata/values_at_spec.rb | 13 + spec/rubyspec/core/math/acos_spec.rb | 58 + spec/rubyspec/core/math/acosh_spec.rb | 43 + spec/rubyspec/core/math/asin_spec.rb | 50 + spec/rubyspec/core/math/asinh_spec.rb | 42 + spec/rubyspec/core/math/atan2_spec.rb | 54 + spec/rubyspec/core/math/atan_spec.rb | 40 + spec/rubyspec/core/math/atanh_spec.rb | 14 + spec/rubyspec/core/math/cbrt_spec.rb | 27 + spec/rubyspec/core/math/constants_spec.rb | 22 + spec/rubyspec/core/math/cos_spec.rb | 42 + spec/rubyspec/core/math/cosh_spec.rb | 37 + spec/rubyspec/core/math/erf_spec.rb | 44 + spec/rubyspec/core/math/erfc_spec.rb | 43 + spec/rubyspec/core/math/exp_spec.rb | 37 + spec/rubyspec/core/math/fixtures/classes.rb | 28 + spec/rubyspec/core/math/frexp_spec.rb | 37 + spec/rubyspec/core/math/gamma_spec.rb | 69 + spec/rubyspec/core/math/hypot_spec.rb | 41 + spec/rubyspec/core/math/ldexp_spec.rb | 54 + spec/rubyspec/core/math/lgamma_spec.rb | 56 + spec/rubyspec/core/math/log10_spec.rb | 45 + spec/rubyspec/core/math/log2_spec.rb | 41 + spec/rubyspec/core/math/log_spec.rb | 59 + spec/rubyspec/core/math/sin_spec.rb | 39 + spec/rubyspec/core/math/sinh_spec.rb | 37 + spec/rubyspec/core/math/sqrt_spec.rb | 36 + spec/rubyspec/core/math/tan_spec.rb | 42 + spec/rubyspec/core/math/tanh_spec.rb | 39 + spec/rubyspec/core/method/arity_spec.rb | 222 + spec/rubyspec/core/method/call_spec.rb | 7 + spec/rubyspec/core/method/clone_spec.rb | 14 + spec/rubyspec/core/method/curry_spec.rb | 36 + .../rubyspec/core/method/element_reference_spec.rb | 7 + spec/rubyspec/core/method/eql_spec.rb | 6 + spec/rubyspec/core/method/equal_value_spec.rb | 6 + spec/rubyspec/core/method/fixtures/classes.rb | 184 + spec/rubyspec/core/method/hash_spec.rb | 17 + spec/rubyspec/core/method/inspect_spec.rb | 6 + spec/rubyspec/core/method/name_spec.rb | 22 + spec/rubyspec/core/method/owner_spec.rb | 26 + spec/rubyspec/core/method/parameters_spec.rb | 244 + spec/rubyspec/core/method/receiver_spec.rb | 22 + spec/rubyspec/core/method/shared/call.rb | 51 + spec/rubyspec/core/method/shared/eql.rb | 94 + spec/rubyspec/core/method/shared/to_s.rb | 34 + spec/rubyspec/core/method/source_location_spec.rb | 95 + spec/rubyspec/core/method/super_method_spec.rb | 45 + spec/rubyspec/core/method/to_proc_spec.rb | 89 + spec/rubyspec/core/method/to_s_spec.rb | 6 + spec/rubyspec/core/method/unbind_spec.rb | 37 + spec/rubyspec/core/module/alias_method_spec.rb | 150 + spec/rubyspec/core/module/allocate_spec.rb | 14 + spec/rubyspec/core/module/ancestors_spec.rb | 70 + spec/rubyspec/core/module/append_features_spec.rb | 73 + spec/rubyspec/core/module/attr_accessor_spec.rb | 90 + spec/rubyspec/core/module/attr_reader_spec.rb | 64 + spec/rubyspec/core/module/attr_spec.rb | 149 + spec/rubyspec/core/module/attr_writer_spec.rb | 64 + spec/rubyspec/core/module/autoload_spec.rb | 462 + spec/rubyspec/core/module/case_compare_spec.rb | 31 + spec/rubyspec/core/module/class_eval_spec.rb | 7 + spec/rubyspec/core/module/class_exec_spec.rb | 7 + .../core/module/class_variable_defined_spec.rb | 72 + .../core/module/class_variable_get_spec.rb | 76 + .../core/module/class_variable_set_spec.rb | 62 + spec/rubyspec/core/module/class_variables_spec.rb | 26 + spec/rubyspec/core/module/comparison_spec.rb | 36 + spec/rubyspec/core/module/const_defined_spec.rb | 144 + spec/rubyspec/core/module/const_get_spec.rb | 208 + spec/rubyspec/core/module/const_missing_spec.rb | 27 + spec/rubyspec/core/module/const_set_spec.rb | 86 + spec/rubyspec/core/module/constants_spec.rb | 91 + spec/rubyspec/core/module/define_method_spec.rb | 626 + .../core/module/define_singleton_method_spec.rb | 17 + .../core/module/deprecate_constant_spec.rb | 52 + spec/rubyspec/core/module/eql_spec.rb | 7 + spec/rubyspec/core/module/equal_spec.rb | 7 + spec/rubyspec/core/module/equal_value_spec.rb | 7 + spec/rubyspec/core/module/extend_object_spec.rb | 68 + spec/rubyspec/core/module/extended_spec.rb | 44 + spec/rubyspec/core/module/fixtures/autoload.rb | 1 + spec/rubyspec/core/module/fixtures/autoload_abc.rb | 11 + spec/rubyspec/core/module/fixtures/autoload_c.rb | 11 + .../core/module/fixtures/autoload_concur.rb | 9 + spec/rubyspec/core/module/fixtures/autoload_d.rb | 11 + spec/rubyspec/core/module/fixtures/autoload_e.rb | 7 + .../core/module/fixtures/autoload_empty.rb | 1 + spec/rubyspec/core/module/fixtures/autoload_ex1.rb | 16 + spec/rubyspec/core/module/fixtures/autoload_f.rb | 7 + spec/rubyspec/core/module/fixtures/autoload_g.rb | 7 + spec/rubyspec/core/module/fixtures/autoload_h.rb | 7 + spec/rubyspec/core/module/fixtures/autoload_i.rb | 5 + spec/rubyspec/core/module/fixtures/autoload_j.rb | 3 + spec/rubyspec/core/module/fixtures/autoload_k.rb | 7 + spec/rubyspec/core/module/fixtures/autoload_lm.rb | 4 + spec/rubyspec/core/module/fixtures/autoload_o.rb | 1 + spec/rubyspec/core/module/fixtures/autoload_r.rb | 4 + spec/rubyspec/core/module/fixtures/autoload_s.rb | 5 + .../core/module/fixtures/autoload_scope.rb | 8 + .../core/module/fixtures/autoload_subclass.rb | 11 + spec/rubyspec/core/module/fixtures/autoload_t.rb | 3 + spec/rubyspec/core/module/fixtures/autoload_v.rb | 7 + spec/rubyspec/core/module/fixtures/autoload_w.rb | 2 + spec/rubyspec/core/module/fixtures/autoload_w2.rb | 1 + spec/rubyspec/core/module/fixtures/autoload_x.rb | 3 + spec/rubyspec/core/module/fixtures/autoload_z.rb | 5 + spec/rubyspec/core/module/fixtures/classes.rb | 605 + .../core/module/fixtures/constant_unicode.rb | 5 + spec/rubyspec/core/module/fixtures/module.rb | 4 + spec/rubyspec/core/module/fixtures/name.rb | 10 + .../core/module/fixtures/path1/load_path.rb | 9 + .../core/module/fixtures/path2/load_path.rb | 0 .../fixtures/repeated_concurrent_autoload.rb | 8 + spec/rubyspec/core/module/freeze_spec.rb | 6 + spec/rubyspec/core/module/gt_spec.rb | 36 + spec/rubyspec/core/module/gte_spec.rb | 33 + spec/rubyspec/core/module/include_spec.rb | 270 + spec/rubyspec/core/module/included_modules_spec.rb | 12 + spec/rubyspec/core/module/included_spec.rb | 44 + spec/rubyspec/core/module/initialize_copy_spec.rb | 10 + spec/rubyspec/core/module/initialize_spec.rb | 18 + spec/rubyspec/core/module/instance_method_spec.rb | 85 + spec/rubyspec/core/module/instance_methods_spec.rb | 61 + spec/rubyspec/core/module/lt_spec.rb | 36 + spec/rubyspec/core/module/lte_spec.rb | 33 + spec/rubyspec/core/module/method_added_spec.rb | 62 + spec/rubyspec/core/module/method_defined_spec.rb | 49 + spec/rubyspec/core/module/method_removed_spec.rb | 33 + spec/rubyspec/core/module/method_undefined_spec.rb | 33 + spec/rubyspec/core/module/module_eval_spec.rb | 7 + spec/rubyspec/core/module/module_exec_spec.rb | 7 + spec/rubyspec/core/module/module_function_spec.rb | 277 + spec/rubyspec/core/module/name_spec.rb | 68 + spec/rubyspec/core/module/nesting_spec.rb | 31 + spec/rubyspec/core/module/new_spec.rb | 31 + spec/rubyspec/core/module/prepend_features_spec.rb | 76 + spec/rubyspec/core/module/prepend_spec.rb | 345 + spec/rubyspec/core/module/prepended_spec.rb | 25 + .../core/module/private_class_method_spec.rb | 81 + spec/rubyspec/core/module/private_constant_spec.rb | 32 + .../core/module/private_instance_methods_spec.rb | 54 + .../core/module/private_method_defined_spec.rb | 72 + spec/rubyspec/core/module/private_spec.rb | 54 + .../core/module/protected_instance_methods_spec.rb | 57 + .../core/module/protected_method_defined_spec.rb | 72 + spec/rubyspec/core/module/protected_spec.rb | 56 + .../core/module/public_class_method_spec.rb | 80 + spec/rubyspec/core/module/public_constant_spec.rb | 38 + .../core/module/public_instance_method_spec.rb | 65 + .../core/module/public_instance_methods_spec.rb | 61 + .../core/module/public_method_defined_spec.rb | 72 + spec/rubyspec/core/module/public_spec.rb | 44 + .../core/module/remove_class_variable_spec.rb | 44 + spec/rubyspec/core/module/remove_const_spec.rb | 84 + spec/rubyspec/core/module/remove_method_spec.rb | 109 + spec/rubyspec/core/module/shared/class_eval.rb | 115 + spec/rubyspec/core/module/shared/class_exec.rb | 29 + spec/rubyspec/core/module/shared/equal_value.rb | 14 + spec/rubyspec/core/module/shared/set_visibility.rb | 135 + spec/rubyspec/core/module/singleton_class_spec.rb | 27 + spec/rubyspec/core/module/to_s_spec.rb | 18 + spec/rubyspec/core/module/undef_method_spec.rb | 152 + spec/rubyspec/core/mutex/lock_spec.rb | 46 + spec/rubyspec/core/mutex/locked_spec.rb | 36 + spec/rubyspec/core/mutex/owned_spec.rb | 43 + spec/rubyspec/core/mutex/sleep_spec.rb | 74 + spec/rubyspec/core/mutex/synchronize_spec.rb | 27 + spec/rubyspec/core/mutex/try_lock_spec.rb | 32 + spec/rubyspec/core/mutex/unlock_spec.rb | 38 + spec/rubyspec/core/nil/and_spec.rb | 11 + spec/rubyspec/core/nil/inspect_spec.rb | 7 + spec/rubyspec/core/nil/nil_spec.rb | 7 + spec/rubyspec/core/nil/or_spec.rb | 11 + spec/rubyspec/core/nil/rationalize_spec.rb | 16 + spec/rubyspec/core/nil/to_a_spec.rb | 7 + spec/rubyspec/core/nil/to_c_spec.rb | 7 + spec/rubyspec/core/nil/to_f_spec.rb | 11 + spec/rubyspec/core/nil/to_h_spec.rb | 8 + spec/rubyspec/core/nil/to_i_spec.rb | 11 + spec/rubyspec/core/nil/to_r_spec.rb | 7 + spec/rubyspec/core/nil/to_s_spec.rb | 7 + spec/rubyspec/core/nil/xor_spec.rb | 11 + spec/rubyspec/core/numeric/abs2_spec.rb | 34 + spec/rubyspec/core/numeric/abs_spec.rb | 5 + spec/rubyspec/core/numeric/angle_spec.rb | 6 + spec/rubyspec/core/numeric/arg_spec.rb | 6 + spec/rubyspec/core/numeric/ceil_spec.rb | 15 + spec/rubyspec/core/numeric/coerce_spec.rb | 76 + spec/rubyspec/core/numeric/comparison_spec.rb | 48 + spec/rubyspec/core/numeric/conj_spec.rb | 6 + spec/rubyspec/core/numeric/conjugate_spec.rb | 6 + spec/rubyspec/core/numeric/denominator_spec.rb | 24 + spec/rubyspec/core/numeric/div_spec.rb | 22 + spec/rubyspec/core/numeric/divmod_spec.rb | 15 + spec/rubyspec/core/numeric/eql_spec.rb | 22 + spec/rubyspec/core/numeric/fdiv_spec.rb | 32 + spec/rubyspec/core/numeric/fixtures/classes.rb | 17 + spec/rubyspec/core/numeric/floor_spec.rb | 14 + spec/rubyspec/core/numeric/i_spec.rb | 15 + spec/rubyspec/core/numeric/imag_spec.rb | 6 + spec/rubyspec/core/numeric/imaginary_spec.rb | 6 + spec/rubyspec/core/numeric/integer_spec.rb | 8 + spec/rubyspec/core/numeric/magnitude_spec.rb | 5 + spec/rubyspec/core/numeric/modulo_spec.rb | 24 + spec/rubyspec/core/numeric/negative_spec.rb | 43 + spec/rubyspec/core/numeric/nonzero_spec.rb | 18 + spec/rubyspec/core/numeric/numerator_spec.rb | 33 + spec/rubyspec/core/numeric/numeric_spec.rb | 7 + spec/rubyspec/core/numeric/phase_spec.rb | 6 + spec/rubyspec/core/numeric/polar_spec.rb | 6 + spec/rubyspec/core/numeric/positive_spec.rb | 43 + spec/rubyspec/core/numeric/quo_spec.rb | 55 + spec/rubyspec/core/numeric/real_spec.rb | 13 + spec/rubyspec/core/numeric/rect_spec.rb | 6 + spec/rubyspec/core/numeric/rectangular_spec.rb | 6 + spec/rubyspec/core/numeric/remainder_spec.rb | 67 + spec/rubyspec/core/numeric/round_spec.rb | 14 + spec/rubyspec/core/numeric/shared/abs.rb | 19 + spec/rubyspec/core/numeric/shared/quo.rb | 7 + spec/rubyspec/core/numeric/shared/rect.rb | 48 + spec/rubyspec/core/numeric/shared/step.rb | 413 + .../core/numeric/singleton_method_added_spec.rb | 41 + spec/rubyspec/core/numeric/step_spec.rb | 151 + spec/rubyspec/core/numeric/to_c_spec.rb | 45 + spec/rubyspec/core/numeric/to_int_spec.rb | 10 + spec/rubyspec/core/numeric/truncate_spec.rb | 14 + spec/rubyspec/core/numeric/uminus_spec.rb | 31 + spec/rubyspec/core/numeric/uplus_spec.rb | 9 + spec/rubyspec/core/numeric/zero_spec.rb | 18 + spec/rubyspec/core/objectspace/_id2ref_spec.rb | 25 + .../core/objectspace/add_finalizer_spec.rb | 5 + .../core/objectspace/call_finalizer_spec.rb | 5 + .../core/objectspace/count_objects_spec.rb | 5 + .../core/objectspace/define_finalizer_spec.rb | 101 + spec/rubyspec/core/objectspace/each_object_spec.rb | 225 + spec/rubyspec/core/objectspace/finalizers_spec.rb | 5 + spec/rubyspec/core/objectspace/fixtures/classes.rb | 64 + .../core/objectspace/garbage_collect_spec.rb | 22 + .../core/objectspace/remove_finalizer_spec.rb | 5 + .../core/objectspace/undefine_finalizer_spec.rb | 5 + spec/rubyspec/core/proc/allocate_spec.rb | 9 + spec/rubyspec/core/proc/arity_spec.rb | 640 + spec/rubyspec/core/proc/binding_spec.rb | 21 + spec/rubyspec/core/proc/block_pass_spec.rb | 41 + spec/rubyspec/core/proc/call_spec.rb | 16 + spec/rubyspec/core/proc/case_compare_spec.rb | 16 + spec/rubyspec/core/proc/clone_spec.rb | 6 + spec/rubyspec/core/proc/curry_spec.rb | 180 + spec/rubyspec/core/proc/dup_spec.rb | 6 + spec/rubyspec/core/proc/element_reference_spec.rb | 16 + spec/rubyspec/core/proc/eql_spec.rb | 6 + spec/rubyspec/core/proc/equal_value_spec.rb | 6 + spec/rubyspec/core/proc/fixtures/common.rb | 51 + .../rubyspec/core/proc/fixtures/source_location.rb | 55 + spec/rubyspec/core/proc/hash_spec.rb | 17 + spec/rubyspec/core/proc/inspect_spec.rb | 6 + spec/rubyspec/core/proc/lambda_spec.rb | 60 + spec/rubyspec/core/proc/new_spec.rb | 190 + spec/rubyspec/core/proc/parameters_spec.rb | 95 + spec/rubyspec/core/proc/shared/call.rb | 96 + spec/rubyspec/core/proc/shared/call_arguments.rb | 7 + spec/rubyspec/core/proc/shared/dup.rb | 10 + spec/rubyspec/core/proc/shared/equal.rb | 100 + spec/rubyspec/core/proc/shared/to_s.rb | 27 + spec/rubyspec/core/proc/source_location_spec.rb | 72 + spec/rubyspec/core/proc/to_proc_spec.rb | 9 + spec/rubyspec/core/proc/to_s_spec.rb | 6 + spec/rubyspec/core/proc/yield_spec.rb | 16 + spec/rubyspec/core/process/abort_spec.rb | 6 + spec/rubyspec/core/process/constants_spec.rb | 63 + spec/rubyspec/core/process/daemon_spec.rb | 123 + spec/rubyspec/core/process/detach_spec.rb | 46 + spec/rubyspec/core/process/egid_spec.rb | 19 + spec/rubyspec/core/process/euid_spec.rb | 59 + spec/rubyspec/core/process/exec_spec.rb | 216 + spec/rubyspec/core/process/exit_spec.rb | 10 + spec/rubyspec/core/process/fixtures/common.rb | 79 + spec/rubyspec/core/process/fixtures/daemon.rb | 111 + spec/rubyspec/core/process/fixtures/env.rb | 1 + spec/rubyspec/core/process/fixtures/kill.rb | 49 + spec/rubyspec/core/process/fixtures/map_fd.rb | 8 + spec/rubyspec/core/process/fixtures/print.rb | 1 + spec/rubyspec/core/process/fork_spec.rb | 6 + spec/rubyspec/core/process/getpgid_spec.rb | 17 + spec/rubyspec/core/process/getpgrp_spec.rb | 7 + spec/rubyspec/core/process/getpriority_spec.rb | 23 + spec/rubyspec/core/process/getrlimit_spec.rb | 91 + .../core/process/gid/change_privilege_spec.rb | 5 + spec/rubyspec/core/process/gid/eid_spec.rb | 9 + .../core/process/gid/grant_privilege_spec.rb | 5 + spec/rubyspec/core/process/gid/re_exchange_spec.rb | 5 + .../core/process/gid/re_exchangeable_spec.rb | 5 + spec/rubyspec/core/process/gid/rid_spec.rb | 5 + .../core/process/gid/sid_available_spec.rb | 5 + spec/rubyspec/core/process/gid/switch_spec.rb | 5 + spec/rubyspec/core/process/gid_spec.rb | 22 + spec/rubyspec/core/process/groups_spec.rb | 54 + spec/rubyspec/core/process/initgroups_spec.rb | 20 + spec/rubyspec/core/process/kill_spec.rb | 152 + spec/rubyspec/core/process/maxgroups_spec.rb | 19 + spec/rubyspec/core/process/pid_spec.rb | 9 + spec/rubyspec/core/process/ppid_spec.rb | 23 + spec/rubyspec/core/process/set_proctitle_spec.rb | 23 + spec/rubyspec/core/process/setpgid_spec.rb | 28 + spec/rubyspec/core/process/setpgrp_spec.rb | 37 + spec/rubyspec/core/process/setpriority_spec.rb | 60 + spec/rubyspec/core/process/setrlimit_spec.rb | 232 + spec/rubyspec/core/process/setsid_spec.rb | 37 + spec/rubyspec/core/process/spawn_spec.rb | 620 + spec/rubyspec/core/process/status/bit_and_spec.rb | 5 + spec/rubyspec/core/process/status/coredump_spec.rb | 5 + .../core/process/status/equal_value_spec.rb | 5 + spec/rubyspec/core/process/status/exited_spec.rb | 37 + .../core/process/status/exitstatus_spec.rb | 13 + spec/rubyspec/core/process/status/inspect_spec.rb | 5 + spec/rubyspec/core/process/status/pid_spec.rb | 13 + .../core/process/status/right_shift_spec.rb | 5 + spec/rubyspec/core/process/status/signaled_spec.rb | 35 + spec/rubyspec/core/process/status/stopped_spec.rb | 5 + spec/rubyspec/core/process/status/stopsig_spec.rb | 5 + spec/rubyspec/core/process/status/success_spec.rb | 51 + spec/rubyspec/core/process/status/termsig_spec.rb | 39 + spec/rubyspec/core/process/status/to_i_spec.rb | 5 + spec/rubyspec/core/process/status/to_int_spec.rb | 5 + spec/rubyspec/core/process/status/to_s_spec.rb | 5 + spec/rubyspec/core/process/sys/getegid_spec.rb | 5 + spec/rubyspec/core/process/sys/geteuid_spec.rb | 5 + spec/rubyspec/core/process/sys/getgid_spec.rb | 5 + spec/rubyspec/core/process/sys/getuid_spec.rb | 5 + spec/rubyspec/core/process/sys/issetugid_spec.rb | 5 + spec/rubyspec/core/process/sys/setegid_spec.rb | 5 + spec/rubyspec/core/process/sys/seteuid_spec.rb | 5 + spec/rubyspec/core/process/sys/setgid_spec.rb | 5 + spec/rubyspec/core/process/sys/setregid_spec.rb | 5 + spec/rubyspec/core/process/sys/setresgid_spec.rb | 5 + spec/rubyspec/core/process/sys/setresuid_spec.rb | 5 + spec/rubyspec/core/process/sys/setreuid_spec.rb | 5 + spec/rubyspec/core/process/sys/setrgid_spec.rb | 5 + spec/rubyspec/core/process/sys/setruid_spec.rb | 5 + spec/rubyspec/core/process/sys/setuid_spec.rb | 5 + spec/rubyspec/core/process/times_spec.rb | 27 + .../core/process/uid/change_privilege_spec.rb | 5 + spec/rubyspec/core/process/uid/eid_spec.rb | 9 + .../core/process/uid/grant_privilege_spec.rb | 5 + spec/rubyspec/core/process/uid/re_exchange_spec.rb | 5 + .../core/process/uid/re_exchangeable_spec.rb | 5 + spec/rubyspec/core/process/uid/rid_spec.rb | 5 + .../core/process/uid/sid_available_spec.rb | 5 + spec/rubyspec/core/process/uid/switch_spec.rb | 5 + spec/rubyspec/core/process/uid_spec.rb | 84 + spec/rubyspec/core/process/wait2_spec.rb | 29 + spec/rubyspec/core/process/wait_spec.rb | 87 + spec/rubyspec/core/process/waitall_spec.rb | 48 + spec/rubyspec/core/process/waitpid2_spec.rb | 5 + spec/rubyspec/core/process/waitpid_spec.rb | 15 + spec/rubyspec/core/random/bytes_spec.rb | 39 + spec/rubyspec/core/random/equal_value_spec.rb | 37 + spec/rubyspec/core/random/new_seed_spec.rb | 24 + spec/rubyspec/core/random/new_spec.rb | 37 + spec/rubyspec/core/random/rand_spec.rb | 216 + spec/rubyspec/core/random/raw_seed_spec.rb | 9 + spec/rubyspec/core/random/seed_spec.rb | 29 + spec/rubyspec/core/random/shared/urandom.rb | 23 + spec/rubyspec/core/random/srand_spec.rb | 39 + spec/rubyspec/core/random/urandom_spec.rb | 9 + spec/rubyspec/core/range/begin_spec.rb | 6 + spec/rubyspec/core/range/bsearch_spec.rb | 137 + spec/rubyspec/core/range/case_compare_spec.rb | 11 + spec/rubyspec/core/range/cover_spec.rb | 9 + spec/rubyspec/core/range/dup_spec.rb | 15 + spec/rubyspec/core/range/each_spec.rb | 66 + spec/rubyspec/core/range/end_spec.rb | 6 + spec/rubyspec/core/range/eql_spec.rb | 10 + spec/rubyspec/core/range/equal_value_spec.rb | 10 + spec/rubyspec/core/range/exclude_end_spec.rb | 19 + spec/rubyspec/core/range/first_spec.rb | 49 + spec/rubyspec/core/range/fixtures/classes.rb | 65 + spec/rubyspec/core/range/hash_spec.rb | 24 + spec/rubyspec/core/range/include_spec.rb | 10 + spec/rubyspec/core/range/initialize_spec.rb | 41 + spec/rubyspec/core/range/inspect_spec.rb | 26 + spec/rubyspec/core/range/last_spec.rb | 49 + spec/rubyspec/core/range/max_spec.rb | 82 + spec/rubyspec/core/range/member_spec.rb | 10 + spec/rubyspec/core/range/min_spec.rb | 75 + spec/rubyspec/core/range/new_spec.rb | 34 + spec/rubyspec/core/range/range_spec.rb | 7 + spec/rubyspec/core/range/shared/begin.rb | 10 + spec/rubyspec/core/range/shared/cover.rb | 93 + .../core/range/shared/cover_and_include.rb | 66 + spec/rubyspec/core/range/shared/end.rb | 10 + spec/rubyspec/core/range/shared/equal_value.rb | 45 + spec/rubyspec/core/range/shared/include.rb | 91 + spec/rubyspec/core/range/size_spec.rb | 31 + spec/rubyspec/core/range/step_spec.rb | 347 + spec/rubyspec/core/range/to_a_spec.rb | 22 + spec/rubyspec/core/range/to_s_spec.rb | 25 + spec/rubyspec/core/rational/abs_spec.rb | 5 + spec/rubyspec/core/rational/ceil_spec.rb | 5 + spec/rubyspec/core/rational/coerce_spec.rb | 5 + spec/rubyspec/core/rational/comparison_spec.rb | 21 + spec/rubyspec/core/rational/denominator_spec.rb | 5 + spec/rubyspec/core/rational/div_spec.rb | 17 + spec/rubyspec/core/rational/divide_spec.rb | 17 + spec/rubyspec/core/rational/divmod_spec.rb | 13 + spec/rubyspec/core/rational/equal_value_spec.rb | 17 + spec/rubyspec/core/rational/exponent_spec.rb | 5 + spec/rubyspec/core/rational/fdiv_spec.rb | 5 + spec/rubyspec/core/rational/floor_spec.rb | 5 + spec/rubyspec/core/rational/hash_spec.rb | 5 + spec/rubyspec/core/rational/inspect_spec.rb | 5 + spec/rubyspec/core/rational/integer_spec.rb | 9 + spec/rubyspec/core/rational/magnitude_spec.rb | 5 + spec/rubyspec/core/rational/marshal_dump_spec.rb | 11 + spec/rubyspec/core/rational/minus_spec.rb | 5 + spec/rubyspec/core/rational/modulo_spec.rb | 5 + spec/rubyspec/core/rational/multiply_spec.rb | 17 + spec/rubyspec/core/rational/numerator_spec.rb | 5 + spec/rubyspec/core/rational/plus_spec.rb | 16 + spec/rubyspec/core/rational/quo_spec.rb | 5 + spec/rubyspec/core/rational/rational_spec.rb | 7 + spec/rubyspec/core/rational/rationalize_spec.rb | 36 + spec/rubyspec/core/rational/remainder_spec.rb | 5 + spec/rubyspec/core/rational/round_spec.rb | 5 + spec/rubyspec/core/rational/to_f_spec.rb | 5 + spec/rubyspec/core/rational/to_i_spec.rb | 5 + spec/rubyspec/core/rational/to_r_spec.rb | 10 + spec/rubyspec/core/rational/to_s_spec.rb | 5 + spec/rubyspec/core/rational/truncate_spec.rb | 5 + spec/rubyspec/core/rational/zero_spec.rb | 13 + spec/rubyspec/core/regexp/case_compare_spec.rb | 25 + spec/rubyspec/core/regexp/casefold_spec.rb | 8 + spec/rubyspec/core/regexp/compile_spec.rb | 18 + spec/rubyspec/core/regexp/encoding_spec.rb | 58 + spec/rubyspec/core/regexp/eql_spec.rb | 6 + spec/rubyspec/core/regexp/equal_value_spec.rb | 6 + spec/rubyspec/core/regexp/escape_spec.rb | 6 + spec/rubyspec/core/regexp/fixed_encoding_spec.rb | 36 + spec/rubyspec/core/regexp/hash_spec.rb | 20 + spec/rubyspec/core/regexp/initialize_spec.rb | 15 + spec/rubyspec/core/regexp/inspect_spec.rb | 44 + spec/rubyspec/core/regexp/last_match_spec.rb | 14 + spec/rubyspec/core/regexp/match_spec.rb | 148 + spec/rubyspec/core/regexp/named_captures_spec.rb | 35 + spec/rubyspec/core/regexp/names_spec.rb | 29 + spec/rubyspec/core/regexp/new_spec.rb | 30 + spec/rubyspec/core/regexp/options_spec.rb | 54 + spec/rubyspec/core/regexp/quote_spec.rb | 6 + spec/rubyspec/core/regexp/shared/equal_value.rb | 31 + spec/rubyspec/core/regexp/shared/new_ascii.rb | 464 + spec/rubyspec/core/regexp/shared/new_ascii_8bit.rb | 553 + spec/rubyspec/core/regexp/shared/quote.rb | 31 + spec/rubyspec/core/regexp/source_spec.rb | 29 + spec/rubyspec/core/regexp/to_s_spec.rb | 62 + spec/rubyspec/core/regexp/try_convert_spec.rb | 21 + spec/rubyspec/core/regexp/union_spec.rb | 149 + spec/rubyspec/core/signal/list_spec.rb | 64 + spec/rubyspec/core/signal/signame_spec.rb | 23 + spec/rubyspec/core/signal/trap_spec.rb | 135 + spec/rubyspec/core/string/allocate_spec.rb | 19 + spec/rubyspec/core/string/append_spec.rb | 8 + spec/rubyspec/core/string/ascii_only_spec.rb | 85 + spec/rubyspec/core/string/b_spec.rb | 24 + spec/rubyspec/core/string/bytes_spec.rb | 57 + spec/rubyspec/core/string/bytesize_spec.rb | 37 + spec/rubyspec/core/string/byteslice_spec.rb | 29 + spec/rubyspec/core/string/capitalize_spec.rb | 56 + spec/rubyspec/core/string/case_compare_spec.rb | 8 + spec/rubyspec/core/string/casecmp_spec.rb | 111 + spec/rubyspec/core/string/center_spec.rb | 133 + spec/rubyspec/core/string/chars_spec.rb | 11 + spec/rubyspec/core/string/chomp_spec.rb | 387 + spec/rubyspec/core/string/chop_spec.rb | 128 + spec/rubyspec/core/string/chr_spec.rb | 44 + spec/rubyspec/core/string/clear_spec.rb | 39 + spec/rubyspec/core/string/clone_spec.rb | 58 + spec/rubyspec/core/string/codepoints_spec.rb | 20 + spec/rubyspec/core/string/comparison_spec.rb | 108 + spec/rubyspec/core/string/concat_spec.rb | 28 + spec/rubyspec/core/string/count_spec.rb | 105 + spec/rubyspec/core/string/crypt_spec.rb | 75 + spec/rubyspec/core/string/delete_spec.rb | 119 + spec/rubyspec/core/string/downcase_spec.rb | 59 + spec/rubyspec/core/string/dump_spec.rb | 424 + spec/rubyspec/core/string/dup_spec.rb | 52 + spec/rubyspec/core/string/each_byte_spec.rb | 61 + spec/rubyspec/core/string/each_char_spec.rb | 7 + spec/rubyspec/core/string/each_codepoint_spec.rb | 10 + spec/rubyspec/core/string/each_line_spec.rb | 9 + .../rubyspec/core/string/element_reference_spec.rb | 35 + spec/rubyspec/core/string/element_set_spec.rb | 612 + spec/rubyspec/core/string/empty_spec.rb | 12 + spec/rubyspec/core/string/encode_spec.rb | 159 + spec/rubyspec/core/string/encoding_spec.rb | 189 + spec/rubyspec/core/string/end_with_spec.rb | 50 + spec/rubyspec/core/string/eql_spec.rb | 21 + spec/rubyspec/core/string/equal_value_spec.rb | 8 + spec/rubyspec/core/string/fixtures/classes.rb | 49 + .../core/string/fixtures/freeze_magic_comment.rb | 3 + .../core/string/fixtures/iso-8859-9-encoding.rb | 9 + .../core/string/fixtures/utf-8-encoding.rb | 7 + spec/rubyspec/core/string/force_encoding_spec.rb | 53 + spec/rubyspec/core/string/freeze_spec.rb | 18 + spec/rubyspec/core/string/getbyte_spec.rb | 69 + spec/rubyspec/core/string/gsub_spec.rb | 696 + spec/rubyspec/core/string/hash_spec.rb | 9 + spec/rubyspec/core/string/hex_spec.rb | 49 + spec/rubyspec/core/string/include_spec.rb | 28 + spec/rubyspec/core/string/index_spec.rb | 315 + spec/rubyspec/core/string/initialize_spec.rb | 26 + spec/rubyspec/core/string/insert_spec.rb | 84 + spec/rubyspec/core/string/inspect_spec.rb | 492 + spec/rubyspec/core/string/intern_spec.rb | 7 + spec/rubyspec/core/string/length_spec.rb | 7 + spec/rubyspec/core/string/lines_spec.rb | 13 + spec/rubyspec/core/string/ljust_spec.rb | 116 + spec/rubyspec/core/string/lstrip_spec.rb | 50 + spec/rubyspec/core/string/match_spec.rb | 175 + spec/rubyspec/core/string/modulo_spec.rb | 780 + spec/rubyspec/core/string/multiply_spec.rb | 7 + spec/rubyspec/core/string/new_spec.rb | 58 + spec/rubyspec/core/string/next_spec.rb | 11 + spec/rubyspec/core/string/oct_spec.rb | 88 + spec/rubyspec/core/string/ord_spec.rb | 30 + spec/rubyspec/core/string/partition_spec.rb | 38 + spec/rubyspec/core/string/plus_spec.rb | 47 + spec/rubyspec/core/string/prepend_spec.rb | 64 + spec/rubyspec/core/string/replace_spec.rb | 7 + spec/rubyspec/core/string/reverse_spec.rb | 52 + spec/rubyspec/core/string/rindex_spec.rb | 368 + spec/rubyspec/core/string/rjust_spec.rb | 116 + spec/rubyspec/core/string/rpartition_spec.rb | 33 + spec/rubyspec/core/string/rstrip_spec.rb | 52 + spec/rubyspec/core/string/scan_spec.rb | 192 + spec/rubyspec/core/string/scrub_spec.rb | 101 + spec/rubyspec/core/string/setbyte_spec.rb | 105 + spec/rubyspec/core/string/shared/chars.rb | 82 + spec/rubyspec/core/string/shared/codepoints.rb | 56 + spec/rubyspec/core/string/shared/concat.rb | 160 + .../core/string/shared/each_char_without_block.rb | 26 + .../string/shared/each_codepoint_without_block.rb | 33 + spec/rubyspec/core/string/shared/each_line.rb | 136 + .../core/string/shared/each_line_without_block.rb | 17 + spec/rubyspec/core/string/shared/encode.rb | 247 + spec/rubyspec/core/string/shared/eql.rb | 34 + spec/rubyspec/core/string/shared/equal_value.rb | 29 + spec/rubyspec/core/string/shared/length.rb | 28 + spec/rubyspec/core/string/shared/replace.rb | 75 + spec/rubyspec/core/string/shared/slice.rb | 533 + spec/rubyspec/core/string/shared/succ.rb | 88 + spec/rubyspec/core/string/shared/to_a.rb | 9 + spec/rubyspec/core/string/shared/to_s.rb | 18 + spec/rubyspec/core/string/shared/to_sym.rb | 24 + spec/rubyspec/core/string/size_spec.rb | 7 + spec/rubyspec/core/string/slice_spec.rb | 476 + spec/rubyspec/core/string/split_spec.rb | 401 + spec/rubyspec/core/string/squeeze_spec.rb | 113 + spec/rubyspec/core/string/start_with_spec.rb | 45 + spec/rubyspec/core/string/string_spec.rb | 7 + spec/rubyspec/core/string/strip_spec.rb | 60 + spec/rubyspec/core/string/sub_spec.rb | 571 + spec/rubyspec/core/string/succ_spec.rb | 11 + spec/rubyspec/core/string/sum_spec.rb | 22 + spec/rubyspec/core/string/swapcase_spec.rb | 52 + spec/rubyspec/core/string/to_c_spec.rb | 99 + spec/rubyspec/core/string/to_f_spec.rb | 69 + spec/rubyspec/core/string/to_i_spec.rb | 337 + spec/rubyspec/core/string/to_r_spec.rb | 58 + spec/rubyspec/core/string/to_s_spec.rb | 7 + spec/rubyspec/core/string/to_str_spec.rb | 7 + spec/rubyspec/core/string/to_sym_spec.rb | 7 + spec/rubyspec/core/string/tr_s_spec.rb | 136 + spec/rubyspec/core/string/tr_spec.rb | 131 + spec/rubyspec/core/string/try_convert_spec.rb | 50 + spec/rubyspec/core/string/uminus_spec.rb | 21 + .../rubyspec/core/string/unicode_normalize_spec.rb | 116 + .../core/string/unicode_normalized_spec.rb | 74 + spec/rubyspec/core/string/unpack/a_spec.rb | 63 + spec/rubyspec/core/string/unpack/at_spec.rb | 29 + spec/rubyspec/core/string/unpack/b_spec.rb | 190 + spec/rubyspec/core/string/unpack/c_spec.rb | 63 + spec/rubyspec/core/string/unpack/comment_spec.rb | 25 + spec/rubyspec/core/string/unpack/d_spec.rb | 28 + spec/rubyspec/core/string/unpack/e_spec.rb | 14 + spec/rubyspec/core/string/unpack/f_spec.rb | 28 + spec/rubyspec/core/string/unpack/g_spec.rb | 14 + spec/rubyspec/core/string/unpack/h_spec.rb | 124 + spec/rubyspec/core/string/unpack/i_spec.rb | 152 + spec/rubyspec/core/string/unpack/j_spec.rb | 277 + spec/rubyspec/core/string/unpack/l_spec.rb | 376 + spec/rubyspec/core/string/unpack/m_spec.rb | 170 + spec/rubyspec/core/string/unpack/n_spec.rb | 18 + spec/rubyspec/core/string/unpack/p_spec.rb | 21 + spec/rubyspec/core/string/unpack/percent_spec.rb | 7 + spec/rubyspec/core/string/unpack/q_spec.rb | 64 + spec/rubyspec/core/string/unpack/s_spec.rb | 152 + spec/rubyspec/core/string/unpack/shared/basic.rb | 29 + spec/rubyspec/core/string/unpack/shared/float.rb | 271 + spec/rubyspec/core/string/unpack/shared/integer.rb | 339 + spec/rubyspec/core/string/unpack/shared/string.rb | 51 + spec/rubyspec/core/string/unpack/shared/unicode.rb | 60 + spec/rubyspec/core/string/unpack/u_spec.rb | 94 + spec/rubyspec/core/string/unpack/v_spec.rb | 18 + spec/rubyspec/core/string/unpack/w_spec.rb | 25 + spec/rubyspec/core/string/unpack/x_spec.rb | 62 + spec/rubyspec/core/string/unpack/z_spec.rb | 21 + spec/rubyspec/core/string/upcase_spec.rb | 53 + spec/rubyspec/core/string/uplus_spec.rb | 24 + spec/rubyspec/core/string/upto_spec.rb | 98 + spec/rubyspec/core/string/valid_encoding_spec.rb | 129 + spec/rubyspec/core/struct/dig_spec.rb | 44 + spec/rubyspec/core/struct/dup_spec.rb | 20 + spec/rubyspec/core/struct/each_pair_spec.rb | 33 + spec/rubyspec/core/struct/each_spec.rb | 27 + .../rubyspec/core/struct/element_reference_spec.rb | 52 + spec/rubyspec/core/struct/element_set_spec.rb | 29 + spec/rubyspec/core/struct/eql_spec.rb | 13 + spec/rubyspec/core/struct/equal_value_spec.rb | 7 + spec/rubyspec/core/struct/fixtures/classes.rb | 26 + spec/rubyspec/core/struct/hash_spec.rb | 46 + spec/rubyspec/core/struct/initialize_spec.rb | 43 + spec/rubyspec/core/struct/inspect_spec.rb | 18 + .../core/struct/instance_variables_spec.rb | 16 + spec/rubyspec/core/struct/length_spec.rb | 12 + spec/rubyspec/core/struct/members_spec.rb | 13 + spec/rubyspec/core/struct/new_spec.rb | 123 + spec/rubyspec/core/struct/select_spec.rb | 30 + spec/rubyspec/core/struct/shared/accessor.rb | 7 + spec/rubyspec/core/struct/shared/equal_value.rb | 30 + spec/rubyspec/core/struct/shared/inspect.rb | 5 + spec/rubyspec/core/struct/size_spec.rb | 11 + spec/rubyspec/core/struct/struct_spec.rb | 43 + spec/rubyspec/core/struct/tms/cstime_spec.rb | 9 + spec/rubyspec/core/struct/tms/cutime_spec.rb | 9 + .../core/struct/tms/element_reference_spec.rb | 5 + spec/rubyspec/core/struct/tms/members_spec.rb | 5 + spec/rubyspec/core/struct/tms/new_spec.rb | 5 + spec/rubyspec/core/struct/tms/stime_spec.rb | 9 + spec/rubyspec/core/struct/tms/utime_spec.rb | 9 + spec/rubyspec/core/struct/to_a_spec.rb | 12 + spec/rubyspec/core/struct/to_h_spec.rb | 15 + spec/rubyspec/core/struct/to_s_spec.rb | 12 + spec/rubyspec/core/struct/values_at_spec.rb | 16 + spec/rubyspec/core/struct/values_spec.rb | 11 + spec/rubyspec/core/symbol/all_symbols_spec.rb | 14 + spec/rubyspec/core/symbol/capitalize_spec.rb | 49 + spec/rubyspec/core/symbol/case_compare_spec.rb | 11 + spec/rubyspec/core/symbol/casecmp_spec.rb | 74 + spec/rubyspec/core/symbol/comparison_spec.rb | 51 + spec/rubyspec/core/symbol/downcase_spec.rb | 26 + .../rubyspec/core/symbol/element_reference_spec.rb | 6 + spec/rubyspec/core/symbol/empty_spec.rb | 11 + spec/rubyspec/core/symbol/encoding_spec.rb | 23 + spec/rubyspec/core/symbol/equal_value_spec.rb | 14 + spec/rubyspec/core/symbol/fixtures/classes.rb | 3 + spec/rubyspec/core/symbol/id2name_spec.rb | 6 + spec/rubyspec/core/symbol/inspect_spec.rb | 105 + spec/rubyspec/core/symbol/intern_spec.rb | 11 + spec/rubyspec/core/symbol/length_spec.rb | 6 + spec/rubyspec/core/symbol/match_spec.rb | 70 + spec/rubyspec/core/symbol/next_spec.rb | 6 + spec/rubyspec/core/symbol/shared/id2name.rb | 9 + spec/rubyspec/core/symbol/shared/length.rb | 23 + spec/rubyspec/core/symbol/shared/slice.rb | 278 + spec/rubyspec/core/symbol/shared/succ.rb | 18 + spec/rubyspec/core/symbol/size_spec.rb | 6 + spec/rubyspec/core/symbol/slice_spec.rb | 6 + spec/rubyspec/core/symbol/succ_spec.rb | 6 + spec/rubyspec/core/symbol/swapcase_spec.rb | 34 + spec/rubyspec/core/symbol/symbol_spec.rb | 7 + spec/rubyspec/core/symbol/to_proc_spec.rb | 41 + spec/rubyspec/core/symbol/to_s_spec.rb | 6 + spec/rubyspec/core/symbol/to_sym_spec.rb | 9 + spec/rubyspec/core/symbol/upcase_spec.rb | 22 + spec/rubyspec/core/systemexit/initialize_spec.rb | 27 + spec/rubyspec/core/systemexit/success_spec.rb | 13 + .../core/thread/abort_on_exception_spec.rb | 106 + spec/rubyspec/core/thread/add_trace_func_spec.rb | 5 + spec/rubyspec/core/thread/alive_spec.rb | 58 + spec/rubyspec/core/thread/allocate_spec.rb | 9 + .../backtrace/location/absolute_path_spec.rb | 12 + .../thread/backtrace/location/base_label_spec.rb | 12 + .../thread/backtrace/location/fixtures/classes.rb | 17 + .../thread/backtrace/location/fixtures/main.rb | 5 + .../core/thread/backtrace/location/inspect_spec.rb | 13 + .../core/thread/backtrace/location/label_spec.rb | 20 + .../core/thread/backtrace/location/lineno_spec.rb | 13 + .../core/thread/backtrace/location/path_spec.rb | 91 + .../core/thread/backtrace/location/to_s_spec.rb | 13 + spec/rubyspec/core/thread/backtrace_spec.rb | 27 + spec/rubyspec/core/thread/current_spec.rb | 15 + .../rubyspec/core/thread/element_reference_spec.rb | 44 + spec/rubyspec/core/thread/element_set_spec.rb | 52 + spec/rubyspec/core/thread/exclusive_spec.rb | 18 + spec/rubyspec/core/thread/exit_spec.rb | 15 + spec/rubyspec/core/thread/fixtures/classes.rb | 288 + spec/rubyspec/core/thread/fork_spec.rb | 9 + spec/rubyspec/core/thread/group_spec.rb | 5 + spec/rubyspec/core/thread/initialize_spec.rb | 27 + spec/rubyspec/core/thread/inspect_spec.rb | 42 + spec/rubyspec/core/thread/join_spec.rb | 62 + spec/rubyspec/core/thread/key_spec.rb | 53 + spec/rubyspec/core/thread/keys_spec.rb | 44 + spec/rubyspec/core/thread/kill_spec.rb | 21 + spec/rubyspec/core/thread/list_spec.rb | 42 + spec/rubyspec/core/thread/main_spec.rb | 10 + spec/rubyspec/core/thread/name_spec.rb | 56 + spec/rubyspec/core/thread/new_spec.rb | 56 + spec/rubyspec/core/thread/pass_spec.rb | 8 + spec/rubyspec/core/thread/priority_spec.rb | 68 + spec/rubyspec/core/thread/raise_spec.rb | 175 + spec/rubyspec/core/thread/run_spec.rb | 9 + spec/rubyspec/core/thread/set_trace_func_spec.rb | 5 + spec/rubyspec/core/thread/shared/exit.rb | 176 + spec/rubyspec/core/thread/shared/start.rb | 41 + spec/rubyspec/core/thread/shared/wakeup.rb | 61 + spec/rubyspec/core/thread/start_spec.rb | 9 + spec/rubyspec/core/thread/status_spec.rb | 42 + spec/rubyspec/core/thread/stop_spec.rb | 56 + spec/rubyspec/core/thread/terminate_spec.rb | 11 + .../core/thread/thread_variable_get_spec.rb | 25 + .../core/thread/thread_variable_set_spec.rb | 26 + spec/rubyspec/core/thread/thread_variable_spec.rb | 21 + spec/rubyspec/core/thread/thread_variables_spec.rb | 24 + spec/rubyspec/core/thread/value_spec.rb | 18 + spec/rubyspec/core/thread/wakeup_spec.rb | 7 + spec/rubyspec/core/threadgroup/add_spec.rb | 36 + spec/rubyspec/core/threadgroup/default_spec.rb | 11 + spec/rubyspec/core/threadgroup/enclose_spec.rb | 25 + spec/rubyspec/core/threadgroup/enclosed_spec.rb | 14 + spec/rubyspec/core/threadgroup/fixtures/classes.rb | 6 + spec/rubyspec/core/threadgroup/list_spec.rb | 24 + spec/rubyspec/core/time/_dump_spec.rb | 56 + spec/rubyspec/core/time/_load_spec.rb | 54 + spec/rubyspec/core/time/asctime_spec.rb | 6 + spec/rubyspec/core/time/at_spec.rb | 145 + spec/rubyspec/core/time/comparison_spec.rb | 94 + spec/rubyspec/core/time/ctime_spec.rb | 6 + spec/rubyspec/core/time/day_spec.rb | 6 + spec/rubyspec/core/time/dst_spec.rb | 6 + spec/rubyspec/core/time/dup_spec.rb | 39 + spec/rubyspec/core/time/eql_spec.rb | 29 + spec/rubyspec/core/time/fixtures/classes.rb | 12 + spec/rubyspec/core/time/friday_spec.rb | 11 + spec/rubyspec/core/time/getgm_spec.rb | 6 + spec/rubyspec/core/time/getlocal_spec.rb | 98 + spec/rubyspec/core/time/getutc_spec.rb | 6 + spec/rubyspec/core/time/gm_spec.rb | 10 + spec/rubyspec/core/time/gmt_offset_spec.rb | 6 + spec/rubyspec/core/time/gmt_spec.rb | 8 + spec/rubyspec/core/time/gmtime_spec.rb | 6 + spec/rubyspec/core/time/gmtoff_spec.rb | 6 + spec/rubyspec/core/time/hash_spec.rb | 11 + spec/rubyspec/core/time/hour_spec.rb | 17 + spec/rubyspec/core/time/inspect_spec.rb | 6 + spec/rubyspec/core/time/isdst_spec.rb | 6 + spec/rubyspec/core/time/local_spec.rb | 11 + spec/rubyspec/core/time/localtime_spec.rb | 113 + spec/rubyspec/core/time/mday_spec.rb | 6 + spec/rubyspec/core/time/min_spec.rb | 17 + spec/rubyspec/core/time/minus_spec.rb | 103 + spec/rubyspec/core/time/mktime_spec.rb | 11 + spec/rubyspec/core/time/mon_spec.rb | 6 + spec/rubyspec/core/time/monday_spec.rb | 11 + spec/rubyspec/core/time/month_spec.rb | 6 + spec/rubyspec/core/time/new_spec.rb | 99 + spec/rubyspec/core/time/now_spec.rb | 6 + spec/rubyspec/core/time/nsec_spec.rb | 27 + spec/rubyspec/core/time/plus_spec.rb | 100 + spec/rubyspec/core/time/round_spec.rb | 33 + spec/rubyspec/core/time/saturday_spec.rb | 11 + spec/rubyspec/core/time/sec_spec.rb | 7 + spec/rubyspec/core/time/shared/asctime.rb | 6 + spec/rubyspec/core/time/shared/day.rb | 15 + spec/rubyspec/core/time/shared/getgm.rb | 9 + spec/rubyspec/core/time/shared/gm.rb | 22 + spec/rubyspec/core/time/shared/gmt_offset.rb | 53 + spec/rubyspec/core/time/shared/gmtime.rb | 10 + spec/rubyspec/core/time/shared/inspect.rb | 23 + spec/rubyspec/core/time/shared/isdst.rb | 8 + spec/rubyspec/core/time/shared/local.rb | 45 + spec/rubyspec/core/time/shared/month.rb | 15 + spec/rubyspec/core/time/shared/now.rb | 8 + spec/rubyspec/core/time/shared/time_params.rb | 255 + spec/rubyspec/core/time/shared/to_i.rb | 9 + spec/rubyspec/core/time/strftime_spec.rb | 52 + spec/rubyspec/core/time/subsec_spec.rb | 27 + spec/rubyspec/core/time/succ_spec.rb | 19 + spec/rubyspec/core/time/sunday_spec.rb | 11 + spec/rubyspec/core/time/thursday_spec.rb | 11 + spec/rubyspec/core/time/time_spec.rb | 7 + spec/rubyspec/core/time/to_a_spec.rb | 12 + spec/rubyspec/core/time/to_f_spec.rb | 7 + spec/rubyspec/core/time/to_i_spec.rb | 6 + spec/rubyspec/core/time/to_r_spec.rb | 11 + spec/rubyspec/core/time/to_s_spec.rb | 6 + spec/rubyspec/core/time/tuesday_spec.rb | 11 + spec/rubyspec/core/time/tv_nsec_spec.rb | 5 + spec/rubyspec/core/time/tv_sec_spec.rb | 6 + spec/rubyspec/core/time/tv_usec_spec.rb | 5 + spec/rubyspec/core/time/usec_spec.rb | 39 + spec/rubyspec/core/time/utc_offset_spec.rb | 6 + spec/rubyspec/core/time/utc_spec.rb | 21 + spec/rubyspec/core/time/wday_spec.rb | 9 + spec/rubyspec/core/time/wednesday_spec.rb | 11 + spec/rubyspec/core/time/yday_spec.rb | 21 + spec/rubyspec/core/time/year_spec.rb | 17 + spec/rubyspec/core/time/zone_spec.rb | 78 + spec/rubyspec/core/true/and_spec.rb | 11 + spec/rubyspec/core/true/inspect_spec.rb | 7 + spec/rubyspec/core/true/or_spec.rb | 11 + spec/rubyspec/core/true/to_s_spec.rb | 7 + spec/rubyspec/core/true/xor_spec.rb | 11 + spec/rubyspec/core/unboundmethod/arity_spec.rb | 207 + spec/rubyspec/core/unboundmethod/bind_spec.rb | 51 + spec/rubyspec/core/unboundmethod/clone_spec.rb | 12 + spec/rubyspec/core/unboundmethod/eql_spec.rb | 5 + .../core/unboundmethod/equal_value_spec.rb | 101 + .../core/unboundmethod/fixtures/classes.rb | 86 + spec/rubyspec/core/unboundmethod/hash_spec.rb | 17 + spec/rubyspec/core/unboundmethod/inspect_spec.rb | 7 + spec/rubyspec/core/unboundmethod/name_spec.rb | 15 + spec/rubyspec/core/unboundmethod/owner_spec.rb | 26 + .../rubyspec/core/unboundmethod/parameters_spec.rb | 5 + spec/rubyspec/core/unboundmethod/shared/to_s.rb | 25 + .../core/unboundmethod/source_location_spec.rb | 52 + .../core/unboundmethod/super_method_spec.rb | 28 + spec/rubyspec/core/unboundmethod/to_s_spec.rb | 7 + spec/rubyspec/default.mspec | 52 + .../fixtures/basicobject/method_missing.rb | 55 + spec/rubyspec/fixtures/class.rb | 136 + spec/rubyspec/fixtures/class_variables.rb | 58 + spec/rubyspec/fixtures/code/a/load_fixture.bundle | 1 + spec/rubyspec/fixtures/code/a/load_fixture.dll | 1 + spec/rubyspec/fixtures/code/a/load_fixture.so | 1 + spec/rubyspec/fixtures/code/b/load_fixture.rb | 1 + spec/rubyspec/fixtures/code/concurrent.rb | 12 + spec/rubyspec/fixtures/code/concurrent2.rb | 8 + spec/rubyspec/fixtures/code/concurrent3.rb | 2 + spec/rubyspec/fixtures/code/file_fixture.rb | 1 + spec/rubyspec/fixtures/code/gem/load_fixture.rb | 1 + spec/rubyspec/fixtures/code/line_fixture.rb | 5 + spec/rubyspec/fixtures/code/load_ext_fixture.rb | 1 + spec/rubyspec/fixtures/code/load_fixture | 1 + spec/rubyspec/fixtures/code/load_fixture.bundle | 1 + spec/rubyspec/fixtures/code/load_fixture.dll | 1 + spec/rubyspec/fixtures/code/load_fixture.ext | 1 + .../rubyspec/fixtures/code/load_fixture.ext.bundle | 1 + spec/rubyspec/fixtures/code/load_fixture.ext.dll | 1 + spec/rubyspec/fixtures/code/load_fixture.ext.rb | 1 + spec/rubyspec/fixtures/code/load_fixture.ext.so | 1 + spec/rubyspec/fixtures/code/load_fixture.rb | 1 + spec/rubyspec/fixtures/code/load_fixture.so | 1 + .../fixtures/code/load_wrap_method_fixture.rb | 9 + spec/rubyspec/fixtures/code/methods_fixture.rb | 364 + spec/rubyspec/fixtures/code/raise_fixture.rb | 1 + .../fixtures/code/recursive_load_fixture.rb | 5 + .../fixtures/code/recursive_require_fixture.rb | 3 + spec/rubyspec/fixtures/code/symlink/symlink1.rb | 1 + .../fixtures/code/symlink/symlink2/symlink2.rb | 1 + spec/rubyspec/fixtures/code/wrap_fixture.rb | 3 + spec/rubyspec/fixtures/code_loading.rb | 26 + spec/rubyspec/fixtures/constants.rb | 288 + spec/rubyspec/fixtures/enumerator/classes.rb | 15 + spec/rubyspec/fixtures/math/common.rb | 3 + spec/rubyspec/fixtures/rational.rb | 11 + spec/rubyspec/fixtures/reflection.rb | 352 + spec/rubyspec/language/BEGIN_spec.rb | 32 + spec/rubyspec/language/README | 30 + spec/rubyspec/language/alias_spec.rb | 190 + spec/rubyspec/language/and_spec.rb | 80 + spec/rubyspec/language/array_spec.rb | 155 + spec/rubyspec/language/block_spec.rb | 865 + spec/rubyspec/language/break_spec.rb | 334 + spec/rubyspec/language/case_spec.rb | 382 + spec/rubyspec/language/class_spec.rb | 332 + spec/rubyspec/language/class_variable_spec.rb | 84 + spec/rubyspec/language/constants_spec.rb | 613 + spec/rubyspec/language/def_spec.rb | 714 + spec/rubyspec/language/defined_spec.rb | 1130 ++ spec/rubyspec/language/encoding_spec.rb | 36 + spec/rubyspec/language/ensure_spec.rb | 126 + spec/rubyspec/language/execution_spec.rb | 15 + spec/rubyspec/language/file_spec.rb | 29 + spec/rubyspec/language/fixtures/argv_encoding.rb | 1 + spec/rubyspec/language/fixtures/array.rb | 11 + spec/rubyspec/language/fixtures/block.rb | 57 + spec/rubyspec/language/fixtures/break.rb | 263 + .../language/fixtures/break_lambda_toplevel.rb | 9 + .../fixtures/break_lambda_toplevel_block.rb | 23 + .../fixtures/break_lambda_toplevel_method.rb | 17 + spec/rubyspec/language/fixtures/classes.rb | 31 + spec/rubyspec/language/fixtures/coding_us_ascii.rb | 11 + spec/rubyspec/language/fixtures/coding_utf_8.rb | 11 + .../language/fixtures/constant_visibility.rb | 98 + .../rubyspec/language/fixtures/constants_sclass.rb | 54 + spec/rubyspec/language/fixtures/def.rb | 8 + spec/rubyspec/language/fixtures/defined.rb | 296 + spec/rubyspec/language/fixtures/dollar_zero.rb | 6 + spec/rubyspec/language/fixtures/ensure.rb | 72 + spec/rubyspec/language/fixtures/file.rb | 1 + .../fixtures/freeze_magic_comment_across_files.rb | 5 + .../freeze_magic_comment_across_files_diff_enc.rb | 5 + ...freeze_magic_comment_across_files_no_comment.rb | 5 + .../fixtures/freeze_magic_comment_one_literal.rb | 4 + .../fixtures/freeze_magic_comment_required.rb | 3 + .../freeze_magic_comment_required_diff_enc.rb | Bin 0 -> 181 bytes .../freeze_magic_comment_required_no_comment.rb | 1 + .../fixtures/freeze_magic_comment_two_literals.rb | 3 + .../language/fixtures/hash_strings_ascii8bit.rb | 7 + .../language/fixtures/hash_strings_usascii.rb | 7 + .../language/fixtures/hash_strings_utf8.rb | 7 + spec/rubyspec/language/fixtures/match_operators.rb | 9 + spec/rubyspec/language/fixtures/metaclass.rb | 34 + spec/rubyspec/language/fixtures/module.rb | 24 + spec/rubyspec/language/fixtures/next.rb | 134 + spec/rubyspec/language/fixtures/precedence.rb | 16 + spec/rubyspec/language/fixtures/private.rb | 59 + spec/rubyspec/language/fixtures/rescue.rb | 63 + spec/rubyspec/language/fixtures/return.rb | 139 + spec/rubyspec/language/fixtures/send.rb | 140 + .../rubyspec/language/fixtures/squiggly_heredoc.rb | 39 + spec/rubyspec/language/fixtures/super.rb | 569 + spec/rubyspec/language/fixtures/variables.rb | 85 + spec/rubyspec/language/fixtures/yield.rb | 37 + spec/rubyspec/language/for_spec.rb | 171 + spec/rubyspec/language/hash_spec.rb | 154 + spec/rubyspec/language/heredoc_spec.rb | 87 + spec/rubyspec/language/if_spec.rb | 354 + spec/rubyspec/language/lambda_spec.rb | 573 + spec/rubyspec/language/line_spec.rb | 45 + spec/rubyspec/language/loop_spec.rb | 67 + spec/rubyspec/language/magic_comment_spec.rb | 62 + spec/rubyspec/language/match_spec.rb | 74 + spec/rubyspec/language/metaclass_spec.rb | 143 + spec/rubyspec/language/method_spec.rb | 1290 ++ spec/rubyspec/language/module_spec.rb | 91 + spec/rubyspec/language/next_spec.rb | 410 + spec/rubyspec/language/not_spec.rb | 51 + spec/rubyspec/language/numbers_spec.rb | 97 + .../rubyspec/language/optional_assignments_spec.rb | 226 + spec/rubyspec/language/or_spec.rb | 90 + spec/rubyspec/language/order_spec.rb | 75 + spec/rubyspec/language/precedence_spec.rb | 448 + spec/rubyspec/language/predefined/data_spec.rb | 29 + .../rubyspec/language/predefined/fixtures/data1.rb | 4 + .../rubyspec/language/predefined/fixtures/data2.rb | 4 + .../rubyspec/language/predefined/fixtures/data3.rb | 7 + .../rubyspec/language/predefined/fixtures/data4.rb | 4 + .../rubyspec/language/predefined/fixtures/data5.rb | 5 + .../language/predefined/fixtures/data_only.rb | 2 + .../language/predefined/fixtures/print_data.rb | 3 + spec/rubyspec/language/predefined_spec.rb | 1221 ++ spec/rubyspec/language/private_spec.rb | 67 + spec/rubyspec/language/proc_spec.rb | 220 + spec/rubyspec/language/redo_spec.rb | 66 + spec/rubyspec/language/regexp/anchors_spec.rb | 179 + .../language/regexp/back-references_spec.rb | 48 + .../language/regexp/character_classes_spec.rb | 617 + spec/rubyspec/language/regexp/encoding_spec.rb | 103 + spec/rubyspec/language/regexp/escapes_spec.rb | 81 + spec/rubyspec/language/regexp/grouping_spec.rb | 23 + .../rubyspec/language/regexp/interpolation_spec.rb | 58 + spec/rubyspec/language/regexp/modifiers_spec.rb | 110 + spec/rubyspec/language/regexp/repetition_spec.rb | 57 + spec/rubyspec/language/regexp_spec.rb | 150 + spec/rubyspec/language/rescue_spec.rb | 293 + spec/rubyspec/language/retry_spec.rb | 52 + spec/rubyspec/language/return_spec.rb | 245 + spec/rubyspec/language/safe_navigator_spec.rb | 101 + spec/rubyspec/language/send_spec.rb | 521 + spec/rubyspec/language/shared/__FILE__.rb | 23 + spec/rubyspec/language/shared/__LINE__.rb | 15 + spec/rubyspec/language/singleton_class_spec.rb | 293 + spec/rubyspec/language/string_spec.rb | 282 + spec/rubyspec/language/super_spec.rb | 282 + spec/rubyspec/language/symbol_spec.rb | 93 + spec/rubyspec/language/throw_spec.rb | 80 + spec/rubyspec/language/undef_spec.rb | 39 + spec/rubyspec/language/unless_spec.rb | 43 + spec/rubyspec/language/until_spec.rb | 234 + spec/rubyspec/language/variables_spec.rb | 776 + spec/rubyspec/language/while_spec.rb | 344 + spec/rubyspec/language/yield_spec.rb | 179 + spec/rubyspec/library/English/English_spec.rb | 171 + spec/rubyspec/library/abbrev/abbrev_spec.rb | 31 + spec/rubyspec/library/base64/decode64_spec.rb | 9 + spec/rubyspec/library/base64/encode64_spec.rb | 14 + .../library/base64/urlsafe_decode64_spec.rb | 27 + .../library/base64/urlsafe_encode64_spec.rb | 22 + spec/rubyspec/library/bigdecimal/abs_spec.rb | 50 + spec/rubyspec/library/bigdecimal/add_spec.rb | 179 + .../library/bigdecimal/case_compare_spec.rb | 7 + spec/rubyspec/library/bigdecimal/ceil_spec.rb | 104 + spec/rubyspec/library/bigdecimal/coerce_spec.rb | 26 + .../rubyspec/library/bigdecimal/comparison_spec.rb | 81 + spec/rubyspec/library/bigdecimal/div_spec.rb | 102 + spec/rubyspec/library/bigdecimal/divide_spec.rb | 7 + spec/rubyspec/library/bigdecimal/divmod_spec.rb | 180 + .../rubyspec/library/bigdecimal/double_fig_spec.rb | 9 + spec/rubyspec/library/bigdecimal/eql_spec.rb | 6 + .../library/bigdecimal/equal_value_spec.rb | 7 + spec/rubyspec/library/bigdecimal/exponent_spec.rb | 38 + spec/rubyspec/library/bigdecimal/finite_spec.rb | 35 + spec/rubyspec/library/bigdecimal/fix_spec.rb | 57 + .../library/bigdecimal/fixtures/classes.rb | 17 + spec/rubyspec/library/bigdecimal/floor_spec.rb | 100 + spec/rubyspec/library/bigdecimal/frac_spec.rb | 48 + spec/rubyspec/library/bigdecimal/gt_spec.rb | 83 + spec/rubyspec/library/bigdecimal/gte_spec.rb | 87 + spec/rubyspec/library/bigdecimal/infinite_spec.rb | 32 + spec/rubyspec/library/bigdecimal/inspect_spec.rb | 47 + spec/rubyspec/library/bigdecimal/limit_spec.rb | 30 + spec/rubyspec/library/bigdecimal/lt_spec.rb | 81 + spec/rubyspec/library/bigdecimal/lte_spec.rb | 87 + spec/rubyspec/library/bigdecimal/minus_spec.rb | 58 + spec/rubyspec/library/bigdecimal/mode_spec.rb | 36 + spec/rubyspec/library/bigdecimal/modulo_spec.rb | 12 + spec/rubyspec/library/bigdecimal/mult_spec.rb | 24 + spec/rubyspec/library/bigdecimal/multiply_spec.rb | 26 + spec/rubyspec/library/bigdecimal/nan_spec.rb | 23 + spec/rubyspec/library/bigdecimal/new_spec.rb | 109 + spec/rubyspec/library/bigdecimal/nonzero_spec.rb | 29 + spec/rubyspec/library/bigdecimal/plus_spec.rb | 47 + spec/rubyspec/library/bigdecimal/power_spec.rb | 6 + spec/rubyspec/library/bigdecimal/precs_spec.rb | 49 + spec/rubyspec/library/bigdecimal/quo_spec.rb | 13 + spec/rubyspec/library/bigdecimal/remainder_spec.rb | 84 + spec/rubyspec/library/bigdecimal/round_spec.rb | 202 + spec/rubyspec/library/bigdecimal/shared/eql.rb | 61 + spec/rubyspec/library/bigdecimal/shared/modulo.rb | 116 + spec/rubyspec/library/bigdecimal/shared/mult.rb | 97 + spec/rubyspec/library/bigdecimal/shared/power.rb | 72 + spec/rubyspec/library/bigdecimal/shared/quo.rb | 59 + spec/rubyspec/library/bigdecimal/shared/to_int.rb | 16 + spec/rubyspec/library/bigdecimal/sign_spec.rb | 47 + spec/rubyspec/library/bigdecimal/split_spec.rb | 88 + spec/rubyspec/library/bigdecimal/sqrt_spec.rb | 112 + spec/rubyspec/library/bigdecimal/sub_spec.rb | 53 + spec/rubyspec/library/bigdecimal/to_f_spec.rb | 55 + spec/rubyspec/library/bigdecimal/to_i_spec.rb | 7 + spec/rubyspec/library/bigdecimal/to_int_spec.rb | 8 + spec/rubyspec/library/bigdecimal/to_s_spec.rb | 73 + spec/rubyspec/library/bigdecimal/truncate_spec.rb | 81 + spec/rubyspec/library/bigdecimal/uminus_spec.rb | 58 + spec/rubyspec/library/bigdecimal/uplus_spec.rb | 20 + spec/rubyspec/library/bigdecimal/ver_spec.rb | 11 + spec/rubyspec/library/bigdecimal/zero_spec.rb | 28 + spec/rubyspec/library/bigmath/log_spec.rb | 10 + spec/rubyspec/library/cgi/cookie/domain_spec.rb | 23 + spec/rubyspec/library/cgi/cookie/expires_spec.rb | 23 + .../rubyspec/library/cgi/cookie/initialize_spec.rb | 147 + spec/rubyspec/library/cgi/cookie/name_spec.rb | 23 + spec/rubyspec/library/cgi/cookie/parse_spec.rb | 18 + spec/rubyspec/library/cgi/cookie/path_spec.rb | 23 + spec/rubyspec/library/cgi/cookie/secure_spec.rb | 70 + spec/rubyspec/library/cgi/cookie/to_s_spec.rb | 29 + spec/rubyspec/library/cgi/cookie/value_spec.rb | 76 + spec/rubyspec/library/cgi/escapeElement_spec.rb | 20 + spec/rubyspec/library/cgi/escapeHTML_spec.rb | 13 + spec/rubyspec/library/cgi/escape_spec.rb | 14 + spec/rubyspec/library/cgi/htmlextension/a_spec.rb | 49 + .../library/cgi/htmlextension/base_spec.rb | 33 + .../library/cgi/htmlextension/blockquote_spec.rb | 33 + spec/rubyspec/library/cgi/htmlextension/br_spec.rb | 22 + .../library/cgi/htmlextension/caption_spec.rb | 33 + .../cgi/htmlextension/checkbox_group_spec.rb | 76 + .../library/cgi/htmlextension/checkbox_spec.rb | 77 + .../library/cgi/htmlextension/doctype_spec.rb | 27 + .../library/cgi/htmlextension/file_field_spec.rb | 72 + .../library/cgi/htmlextension/fixtures/common.rb | 15 + .../library/cgi/htmlextension/form_spec.rb | 58 + .../library/cgi/htmlextension/frame_spec.rb | 14 + .../library/cgi/htmlextension/frameset_spec.rb | 14 + .../library/cgi/htmlextension/hidden_spec.rb | 59 + .../library/cgi/htmlextension/html_spec.rb | 66 + .../library/cgi/htmlextension/image_button_spec.rb | 69 + .../rubyspec/library/cgi/htmlextension/img_spec.rb | 83 + .../cgi/htmlextension/multipart_form_spec.rb | 64 + .../cgi/htmlextension/password_field_spec.rb | 84 + .../library/cgi/htmlextension/popup_menu_spec.rb | 8 + .../library/cgi/htmlextension/radio_button_spec.rb | 77 + .../library/cgi/htmlextension/radio_group_spec.rb | 77 + .../library/cgi/htmlextension/reset_spec.rb | 57 + .../cgi/htmlextension/scrolling_list_spec.rb | 8 + .../library/cgi/htmlextension/shared/popup_menu.rb | 94 + .../library/cgi/htmlextension/submit_spec.rb | 57 + .../library/cgi/htmlextension/text_field_spec.rb | 84 + .../library/cgi/htmlextension/textarea_spec.rb | 73 + spec/rubyspec/library/cgi/http_header_spec.rb | 8 + spec/rubyspec/library/cgi/initialize_spec.rb | 133 + spec/rubyspec/library/cgi/out_spec.rb | 51 + spec/rubyspec/library/cgi/parse_spec.rb | 24 + spec/rubyspec/library/cgi/pretty_spec.rb | 24 + spec/rubyspec/library/cgi/print_spec.rb | 26 + .../cgi/queryextension/accept_charset_spec.rb | 22 + .../cgi/queryextension/accept_encoding_spec.rb | 22 + .../cgi/queryextension/accept_language_spec.rb | 22 + .../library/cgi/queryextension/accept_spec.rb | 22 + .../library/cgi/queryextension/auth_type_spec.rb | 22 + .../cgi/queryextension/cache_control_spec.rb | 22 + .../cgi/queryextension/content_length_spec.rb | 26 + .../cgi/queryextension/content_type_spec.rb | 22 + .../library/cgi/queryextension/cookies_spec.rb | 10 + .../cgi/queryextension/element_reference_spec.rb | 27 + .../library/cgi/queryextension/from_spec.rb | 22 + .../cgi/queryextension/gateway_interface_spec.rb | 22 + .../library/cgi/queryextension/has_key_spec.rb | 7 + .../library/cgi/queryextension/host_spec.rb | 22 + .../library/cgi/queryextension/include_spec.rb | 7 + .../library/cgi/queryextension/key_spec.rb | 7 + .../library/cgi/queryextension/keys_spec.rb | 20 + .../library/cgi/queryextension/multipart_spec.rb | 40 + .../library/cgi/queryextension/negotiate_spec.rb | 22 + .../library/cgi/queryextension/params_spec.rb | 37 + .../library/cgi/queryextension/path_info_spec.rb | 22 + .../cgi/queryextension/path_translated_spec.rb | 22 + .../library/cgi/queryextension/pragma_spec.rb | 22 + .../cgi/queryextension/query_string_spec.rb | 22 + .../library/cgi/queryextension/raw_cookie2_spec.rb | 22 + .../library/cgi/queryextension/raw_cookie_spec.rb | 22 + .../library/cgi/queryextension/referer_spec.rb | 22 + .../library/cgi/queryextension/remote_addr_spec.rb | 22 + .../library/cgi/queryextension/remote_host_spec.rb | 22 + .../cgi/queryextension/remote_ident_spec.rb | 22 + .../library/cgi/queryextension/remote_user_spec.rb | 22 + .../cgi/queryextension/request_method_spec.rb | 22 + .../library/cgi/queryextension/script_name_spec.rb | 22 + .../library/cgi/queryextension/server_name_spec.rb | 22 + .../library/cgi/queryextension/server_port_spec.rb | 26 + .../cgi/queryextension/server_protocol_spec.rb | 22 + .../cgi/queryextension/server_software_spec.rb | 22 + .../library/cgi/queryextension/shared/has_key.rb | 19 + .../library/cgi/queryextension/user_agent_spec.rb | 22 + spec/rubyspec/library/cgi/rfc1123_date_spec.rb | 10 + spec/rubyspec/library/cgi/shared/http_header.rb | 112 + spec/rubyspec/library/cgi/unescapeElement_spec.rb | 20 + spec/rubyspec/library/cgi/unescapeHTML_spec.rb | 39 + spec/rubyspec/library/cgi/unescape_spec.rb | 15 + spec/rubyspec/library/complex/math/acos_spec.rb | 15 + spec/rubyspec/library/complex/math/acosh_spec.rb | 15 + spec/rubyspec/library/complex/math/asin_spec.rb | 15 + spec/rubyspec/library/complex/math/asinh_spec.rb | 15 + spec/rubyspec/library/complex/math/atan2_spec.rb | 15 + spec/rubyspec/library/complex/math/atan_spec.rb | 15 + spec/rubyspec/library/complex/math/atanh_spec.rb | 17 + spec/rubyspec/library/complex/math/cos_spec.rb | 15 + spec/rubyspec/library/complex/math/cosh_spec.rb | 15 + spec/rubyspec/library/complex/math/exp_spec.rb | 15 + .../library/complex/math/fixtures/classes.rb | 4 + spec/rubyspec/library/complex/math/log10_spec.rb | 15 + spec/rubyspec/library/complex/math/log_spec.rb | 15 + spec/rubyspec/library/complex/math/shared/acos.rb | 41 + spec/rubyspec/library/complex/math/shared/acosh.rb | 37 + spec/rubyspec/library/complex/math/shared/asin.rb | 47 + spec/rubyspec/library/complex/math/shared/asinh.rb | 32 + spec/rubyspec/library/complex/math/shared/atan.rb | 32 + spec/rubyspec/library/complex/math/shared/atan2.rb | 34 + spec/rubyspec/library/complex/math/shared/atanh.rb | 30 + spec/rubyspec/library/complex/math/shared/cos.rb | 30 + spec/rubyspec/library/complex/math/shared/cosh.rb | 28 + spec/rubyspec/library/complex/math/shared/exp.rb | 28 + spec/rubyspec/library/complex/math/shared/log.rb | 39 + spec/rubyspec/library/complex/math/shared/log10.rb | 41 + spec/rubyspec/library/complex/math/shared/sin.rb | 30 + spec/rubyspec/library/complex/math/shared/sinh.rb | 28 + spec/rubyspec/library/complex/math/shared/sqrt.rb | 34 + spec/rubyspec/library/complex/math/shared/tan.rb | 28 + spec/rubyspec/library/complex/math/shared/tanh.rb | 32 + spec/rubyspec/library/complex/math/sin_spec.rb | 15 + spec/rubyspec/library/complex/math/sinh_spec.rb | 15 + spec/rubyspec/library/complex/math/sqrt_spec.rb | 15 + spec/rubyspec/library/complex/math/tan_spec.rb | 15 + spec/rubyspec/library/complex/math/tanh_spec.rb | 15 + spec/rubyspec/library/complex/numeric/im_spec.rb | 3 + .../library/conditionvariable/broadcast_spec.rb | 67 + .../library/conditionvariable/signal_spec.rb | 69 + .../library/conditionvariable/wait_spec.rb | 25 + .../library/coverage/fixtures/second_class.rb | 5 + .../library/coverage/fixtures/some_class.rb | 16 + .../library/coverage/fixtures/spec_helper.rb | 11 + .../library/coverage/fixtures/start_coverage.rb | 3 + spec/rubyspec/library/coverage/peek_result_spec.rb | 67 + spec/rubyspec/library/coverage/result_spec.rb | 78 + spec/rubyspec/library/coverage/start_spec.rb | 6 + .../csv/basicwriter/close_on_terminate_spec.rb | 6 + .../library/csv/basicwriter/initialize_spec.rb | 6 + .../library/csv/basicwriter/terminate_spec.rb | 6 + spec/rubyspec/library/csv/cell/data_spec.rb | 6 + spec/rubyspec/library/csv/cell/initialize_spec.rb | 6 + spec/rubyspec/library/csv/fixtures/one_line.csv | 1 + spec/rubyspec/library/csv/foreach_spec.rb | 6 + spec/rubyspec/library/csv/generate_line_spec.rb | 30 + spec/rubyspec/library/csv/generate_row_spec.rb | 6 + spec/rubyspec/library/csv/generate_spec.rb | 32 + spec/rubyspec/library/csv/iobuf/close_spec.rb | 6 + spec/rubyspec/library/csv/iobuf/initialize_spec.rb | 6 + spec/rubyspec/library/csv/iobuf/read_spec.rb | 6 + spec/rubyspec/library/csv/iobuf/terminate_spec.rb | 6 + .../csv/ioreader/close_on_terminate_spec.rb | 6 + spec/rubyspec/library/csv/ioreader/get_row_spec.rb | 6 + .../library/csv/ioreader/initialize_spec.rb | 6 + .../library/csv/ioreader/terminate_spec.rb | 6 + spec/rubyspec/library/csv/open_spec.rb | 6 + spec/rubyspec/library/csv/parse_spec.rb | 81 + spec/rubyspec/library/csv/read_spec.rb | 6 + spec/rubyspec/library/csv/readlines_spec.rb | 23 + .../rubyspec/library/csv/streambuf/add_buf_spec.rb | 6 + .../library/csv/streambuf/buf_size_spec.rb | 6 + spec/rubyspec/library/csv/streambuf/drop_spec.rb | 6 + .../csv/streambuf/element_reference_spec.rb | 6 + spec/rubyspec/library/csv/streambuf/get_spec.rb | 6 + .../library/csv/streambuf/idx_is_eos_spec.rb | 6 + .../library/csv/streambuf/initialize_spec.rb | 6 + spec/rubyspec/library/csv/streambuf/is_eos_spec.rb | 6 + spec/rubyspec/library/csv/streambuf/read_spec.rb | 6 + .../rubyspec/library/csv/streambuf/rel_buf_spec.rb | 6 + .../library/csv/streambuf/terminate_spec.rb | 6 + .../library/csv/stringreader/get_row_spec.rb | 6 + .../library/csv/stringreader/initialize_spec.rb | 6 + spec/rubyspec/library/csv/writer/add_row_spec.rb | 6 + spec/rubyspec/library/csv/writer/append_spec.rb | 6 + spec/rubyspec/library/csv/writer/close_spec.rb | 6 + spec/rubyspec/library/csv/writer/create_spec.rb | 6 + spec/rubyspec/library/csv/writer/generate_spec.rb | 6 + .../rubyspec/library/csv/writer/initialize_spec.rb | 6 + spec/rubyspec/library/csv/writer/terminate_spec.rb | 6 + spec/rubyspec/library/date/accessor_spec.rb | 91 + spec/rubyspec/library/date/add_month_spec.rb | 38 + spec/rubyspec/library/date/add_spec.rb | 30 + spec/rubyspec/library/date/ajd_spec.rb | 6 + spec/rubyspec/library/date/ajd_to_amjd_spec.rb | 6 + spec/rubyspec/library/date/ajd_to_jd_spec.rb | 6 + spec/rubyspec/library/date/amjd_spec.rb | 6 + spec/rubyspec/library/date/amjd_to_ajd_spec.rb | 6 + spec/rubyspec/library/date/append_spec.rb | 6 + spec/rubyspec/library/date/asctime_spec.rb | 6 + spec/rubyspec/library/date/boat_spec.rb | 24 + spec/rubyspec/library/date/case_compare_spec.rb | 6 + spec/rubyspec/library/date/civil_spec.rb | 12 + spec/rubyspec/library/date/commercial_spec.rb | 18 + .../rubyspec/library/date/commercial_to_jd_spec.rb | 6 + spec/rubyspec/library/date/comparison_spec.rb | 6 + spec/rubyspec/library/date/constants_spec.rb | 44 + spec/rubyspec/library/date/conversions_spec.rb | 43 + spec/rubyspec/library/date/ctime_spec.rb | 6 + spec/rubyspec/library/date/cwday_spec.rb | 6 + spec/rubyspec/library/date/cweek_spec.rb | 6 + spec/rubyspec/library/date/cwyear_spec.rb | 6 + spec/rubyspec/library/date/day_fraction_spec.rb | 6 + .../library/date/day_fraction_to_time_spec.rb | 6 + spec/rubyspec/library/date/day_spec.rb | 6 + spec/rubyspec/library/date/downto_spec.rb | 18 + spec/rubyspec/library/date/england_spec.rb | 6 + spec/rubyspec/library/date/eql_spec.rb | 12 + .../library/date/format/bag/method_missing_spec.rb | 6 + .../library/date/format/bag/to_hash_spec.rb | 6 + spec/rubyspec/library/date/gregorian_leap_spec.rb | 16 + spec/rubyspec/library/date/gregorian_spec.rb | 16 + spec/rubyspec/library/date/hash_spec.rb | 8 + spec/rubyspec/library/date/infinity/abs_spec.rb | 6 + spec/rubyspec/library/date/infinity/coerce_spec.rb | 6 + .../library/date/infinity/comparison_spec.rb | 6 + spec/rubyspec/library/date/infinity/d_spec.rb | 6 + spec/rubyspec/library/date/infinity/finite_spec.rb | 6 + .../library/date/infinity/infinite_spec.rb | 6 + spec/rubyspec/library/date/infinity/nan_spec.rb | 6 + spec/rubyspec/library/date/infinity/uminus_spec.rb | 6 + spec/rubyspec/library/date/infinity/uplus_spec.rb | 6 + spec/rubyspec/library/date/infinity/zero_spec.rb | 6 + spec/rubyspec/library/date/infinity_spec.rb | 67 + spec/rubyspec/library/date/inspect_spec.rb | 6 + spec/rubyspec/library/date/italy_spec.rb | 6 + spec/rubyspec/library/date/jd_spec.rb | 15 + spec/rubyspec/library/date/jd_to_ajd_spec.rb | 6 + spec/rubyspec/library/date/jd_to_civil_spec.rb | 6 + .../rubyspec/library/date/jd_to_commercial_spec.rb | 6 + spec/rubyspec/library/date/jd_to_ld_spec.rb | 6 + spec/rubyspec/library/date/jd_to_mjd_spec.rb | 6 + spec/rubyspec/library/date/jd_to_ordinal_spec.rb | 6 + spec/rubyspec/library/date/jd_to_wday_spec.rb | 6 + spec/rubyspec/library/date/julian_leap_spec.rb | 15 + spec/rubyspec/library/date/julian_spec.rb | 16 + spec/rubyspec/library/date/ld_spec.rb | 6 + spec/rubyspec/library/date/ld_to_jd_spec.rb | 6 + spec/rubyspec/library/date/leap_spec.rb | 10 + spec/rubyspec/library/date/mday_spec.rb | 6 + spec/rubyspec/library/date/minus_month_spec.rb | 34 + spec/rubyspec/library/date/minus_spec.rb | 30 + spec/rubyspec/library/date/mjd_spec.rb | 6 + spec/rubyspec/library/date/mjd_to_jd_spec.rb | 6 + spec/rubyspec/library/date/mon_spec.rb | 6 + spec/rubyspec/library/date/month_spec.rb | 6 + spec/rubyspec/library/date/new_spec.rb | 8 + spec/rubyspec/library/date/new_start_spec.rb | 6 + spec/rubyspec/library/date/next_spec.rb | 6 + spec/rubyspec/library/date/next_year_spec.rb | 12 + spec/rubyspec/library/date/ordinal_spec.rb | 8 + spec/rubyspec/library/date/ordinal_to_jd_spec.rb | 6 + spec/rubyspec/library/date/parse_spec.rb | 137 + spec/rubyspec/library/date/plus_spec.rb | 20 + spec/rubyspec/library/date/prev_year_spec.rb | 12 + spec/rubyspec/library/date/relationship_spec.rb | 20 + spec/rubyspec/library/date/right_shift_spec.rb | 6 + spec/rubyspec/library/date/shared/civil.rb | 57 + spec/rubyspec/library/date/shared/commercial.rb | 39 + spec/rubyspec/library/date/shared/jd.rb | 14 + spec/rubyspec/library/date/shared/new_bang.rb | 14 + spec/rubyspec/library/date/shared/ordinal.rb | 22 + spec/rubyspec/library/date/shared/parse.rb | 54 + spec/rubyspec/library/date/shared/parse_eu.rb | 37 + spec/rubyspec/library/date/shared/parse_us.rb | 36 + spec/rubyspec/library/date/shared/valid_civil.rb | 36 + .../library/date/shared/valid_commercial.rb | 34 + spec/rubyspec/library/date/shared/valid_jd.rb | 15 + spec/rubyspec/library/date/shared/valid_ordinal.rb | 26 + spec/rubyspec/library/date/start_spec.rb | 6 + spec/rubyspec/library/date/step_spec.rb | 56 + spec/rubyspec/library/date/strftime_spec.rb | 40 + spec/rubyspec/library/date/strptime_spec.rb | 149 + spec/rubyspec/library/date/succ_spec.rb | 6 + .../library/date/time_to_day_fraction_spec.rb | 6 + spec/rubyspec/library/date/to_s_spec.rb | 6 + spec/rubyspec/library/date/today_spec.rb | 6 + spec/rubyspec/library/date/upto_spec.rb | 16 + spec/rubyspec/library/date/valid_civil_spec.rb | 10 + .../rubyspec/library/date/valid_commercial_spec.rb | 10 + spec/rubyspec/library/date/valid_date_spec.rb | 7 + spec/rubyspec/library/date/valid_jd_spec.rb | 10 + spec/rubyspec/library/date/valid_ordinal_spec.rb | 10 + spec/rubyspec/library/date/valid_time_spec.rb | 6 + spec/rubyspec/library/date/wday_spec.rb | 6 + spec/rubyspec/library/date/yday_spec.rb | 6 + spec/rubyspec/library/date/year_spec.rb | 6 + spec/rubyspec/library/date/zone_to_diff_spec.rb | 6 + spec/rubyspec/library/datetime/_strptime_spec.rb | 6 + spec/rubyspec/library/datetime/civil_spec.rb | 6 + spec/rubyspec/library/datetime/commercial_spec.rb | 6 + spec/rubyspec/library/datetime/hour_spec.rb | 47 + spec/rubyspec/library/datetime/httpdate_spec.rb | 6 + spec/rubyspec/library/datetime/iso8601_spec.rb | 10 + spec/rubyspec/library/datetime/jd_spec.rb | 6 + spec/rubyspec/library/datetime/jisx0301_spec.rb | 10 + spec/rubyspec/library/datetime/min_spec.rb | 6 + spec/rubyspec/library/datetime/minute_spec.rb | 6 + spec/rubyspec/library/datetime/new_offset_spec.rb | 6 + spec/rubyspec/library/datetime/new_spec.rb | 52 + spec/rubyspec/library/datetime/now_spec.rb | 8 + spec/rubyspec/library/datetime/offset_spec.rb | 6 + spec/rubyspec/library/datetime/ordinal_spec.rb | 6 + spec/rubyspec/library/datetime/parse_spec.rb | 127 + spec/rubyspec/library/datetime/rfc2822_spec.rb | 6 + spec/rubyspec/library/datetime/rfc3339_spec.rb | 10 + spec/rubyspec/library/datetime/rfc822_spec.rb | 6 + .../rubyspec/library/datetime/sec_fraction_spec.rb | 6 + spec/rubyspec/library/datetime/sec_spec.rb | 6 + .../library/datetime/second_fraction_spec.rb | 6 + spec/rubyspec/library/datetime/second_spec.rb | 6 + spec/rubyspec/library/datetime/shared/min.rb | 40 + spec/rubyspec/library/datetime/shared/sec.rb | 45 + spec/rubyspec/library/datetime/strftime_spec.rb | 51 + spec/rubyspec/library/datetime/strptime_spec.rb | 6 + spec/rubyspec/library/datetime/to_date_spec.rb | 6 + spec/rubyspec/library/datetime/to_datetime_spec.rb | 6 + spec/rubyspec/library/datetime/to_s_spec.rb | 6 + spec/rubyspec/library/datetime/to_time_spec.rb | 6 + spec/rubyspec/library/datetime/xmlschema_spec.rb | 10 + spec/rubyspec/library/datetime/zone_spec.rb | 6 + .../delegate_class/instance_method_spec.rb | 52 + .../delegate_class/instance_methods_spec.rb | 26 + .../private_instance_methods_spec.rb | 23 + .../protected_instance_methods_spec.rb | 29 + .../delegate_class/public_instance_methods_spec.rb | 25 + .../delegate_class/respond_to_missing_spec.rb | 23 + .../delegate/delegator/case_compare_spec.rb | 11 + .../library/delegate/delegator/compare_spec.rb | 11 + .../library/delegate/delegator/complement_spec.rb | 11 + .../library/delegate/delegator/eql_spec.rb | 11 + .../library/delegate/delegator/equal_spec.rb | 13 + .../library/delegate/delegator/equal_value_spec.rb | 24 + .../library/delegate/delegator/frozen_spec.rb | 39 + .../library/delegate/delegator/hash_spec.rb | 11 + .../library/delegate/delegator/marshal_spec.rb | 21 + .../library/delegate/delegator/method_spec.rb | 69 + .../library/delegate/delegator/methods_spec.rb | 37 + .../library/delegate/delegator/not_equal_spec.rb | 24 + .../library/delegate/delegator/not_spec.rb | 11 + .../delegate/delegator/private_methods_spec.rb | 20 + .../delegate/delegator/protected_methods_spec.rb | 18 + .../delegate/delegator/public_methods_spec.rb | 18 + .../library/delegate/delegator/send_spec.rb | 26 + .../library/delegate/delegator/taint_spec.rb | 23 + .../library/delegate/delegator/tap_spec.rb | 16 + .../library/delegate/delegator/trust_spec.rb | 22 + .../library/delegate/delegator/untaint_spec.rb | 24 + .../library/delegate/delegator/untrust_spec.rb | 23 + spec/rubyspec/library/delegate/fixtures/classes.rb | 60 + spec/rubyspec/library/digest/bubblebabble_spec.rb | 29 + spec/rubyspec/library/digest/hexencode_spec.rb | 31 + spec/rubyspec/library/digest/md5/append_spec.rb | 7 + .../library/digest/md5/block_length_spec.rb | 12 + .../library/digest/md5/digest_bang_spec.rb | 13 + .../library/digest/md5/digest_length_spec.rb | 12 + spec/rubyspec/library/digest/md5/digest_spec.rb | 32 + spec/rubyspec/library/digest/md5/equal_spec.rb | 38 + spec/rubyspec/library/digest/md5/file_spec.rb | 43 + .../library/digest/md5/hexdigest_bang_spec.rb | 14 + spec/rubyspec/library/digest/md5/hexdigest_spec.rb | 32 + spec/rubyspec/library/digest/md5/inspect_spec.rb | 12 + spec/rubyspec/library/digest/md5/length_spec.rb | 8 + spec/rubyspec/library/digest/md5/reset_spec.rb | 15 + .../library/digest/md5/shared/constants.rb | 16 + spec/rubyspec/library/digest/md5/shared/length.rb | 8 + spec/rubyspec/library/digest/md5/shared/sample.rb | 17 + spec/rubyspec/library/digest/md5/shared/update.rb | 7 + spec/rubyspec/library/digest/md5/size_spec.rb | 8 + spec/rubyspec/library/digest/md5/to_s_spec.rb | 24 + spec/rubyspec/library/digest/md5/update_spec.rb | 7 + spec/rubyspec/library/digest/sha1/digest_spec.rb | 20 + spec/rubyspec/library/digest/sha1/file_spec.rb | 43 + .../library/digest/sha1/shared/constants.rb | 17 + spec/rubyspec/library/digest/sha256/append_spec.rb | 7 + .../library/digest/sha256/block_length_spec.rb | 12 + .../library/digest/sha256/digest_bang_spec.rb | 13 + .../library/digest/sha256/digest_length_spec.rb | 12 + spec/rubyspec/library/digest/sha256/digest_spec.rb | 32 + spec/rubyspec/library/digest/sha256/equal_spec.rb | 37 + spec/rubyspec/library/digest/sha256/file_spec.rb | 43 + .../library/digest/sha256/hexdigest_bang_spec.rb | 14 + .../library/digest/sha256/hexdigest_spec.rb | 32 + .../rubyspec/library/digest/sha256/inspect_spec.rb | 12 + spec/rubyspec/library/digest/sha256/length_spec.rb | 8 + spec/rubyspec/library/digest/sha256/reset_spec.rb | 15 + .../library/digest/sha256/shared/constants.rb | 17 + .../library/digest/sha256/shared/length.rb | 8 + .../library/digest/sha256/shared/update.rb | 7 + spec/rubyspec/library/digest/sha256/size_spec.rb | 8 + spec/rubyspec/library/digest/sha256/to_s_spec.rb | 21 + spec/rubyspec/library/digest/sha256/update_spec.rb | 7 + spec/rubyspec/library/digest/sha384/append_spec.rb | 7 + .../library/digest/sha384/block_length_spec.rb | 12 + .../library/digest/sha384/digest_bang_spec.rb | 13 + .../library/digest/sha384/digest_length_spec.rb | 12 + spec/rubyspec/library/digest/sha384/digest_spec.rb | 32 + spec/rubyspec/library/digest/sha384/equal_spec.rb | 37 + spec/rubyspec/library/digest/sha384/file_spec.rb | 43 + .../library/digest/sha384/hexdigest_bang_spec.rb | 14 + .../library/digest/sha384/hexdigest_spec.rb | 32 + .../rubyspec/library/digest/sha384/inspect_spec.rb | 12 + spec/rubyspec/library/digest/sha384/length_spec.rb | 8 + spec/rubyspec/library/digest/sha384/reset_spec.rb | 15 + .../library/digest/sha384/shared/constants.rb | 18 + .../library/digest/sha384/shared/length.rb | 8 + .../library/digest/sha384/shared/update.rb | 7 + spec/rubyspec/library/digest/sha384/size_spec.rb | 8 + spec/rubyspec/library/digest/sha384/to_s_spec.rb | 21 + spec/rubyspec/library/digest/sha384/update_spec.rb | 7 + spec/rubyspec/library/digest/sha512/append_spec.rb | 7 + .../library/digest/sha512/block_length_spec.rb | 12 + .../library/digest/sha512/digest_bang_spec.rb | 13 + .../library/digest/sha512/digest_length_spec.rb | 12 + spec/rubyspec/library/digest/sha512/digest_spec.rb | 32 + spec/rubyspec/library/digest/sha512/equal_spec.rb | 37 + spec/rubyspec/library/digest/sha512/file_spec.rb | 43 + .../library/digest/sha512/hexdigest_bang_spec.rb | 14 + .../library/digest/sha512/hexdigest_spec.rb | 32 + .../rubyspec/library/digest/sha512/inspect_spec.rb | 12 + spec/rubyspec/library/digest/sha512/length_spec.rb | 8 + spec/rubyspec/library/digest/sha512/reset_spec.rb | 15 + .../library/digest/sha512/shared/constants.rb | 17 + .../library/digest/sha512/shared/length.rb | 8 + .../library/digest/sha512/shared/update.rb | 7 + spec/rubyspec/library/digest/sha512/size_spec.rb | 8 + spec/rubyspec/library/digest/sha512/to_s_spec.rb | 21 + spec/rubyspec/library/digest/sha512/update_spec.rb | 7 + spec/rubyspec/library/drb/config_spec.rb | 1 + spec/rubyspec/library/drb/current_server_spec.rb | 1 + .../library/drb/drbobject/__drbref_spec.rb | 1 + .../library/drb/drbobject/__drburi_spec.rb | 1 + spec/rubyspec/library/drb/drbobject/_dump_spec.rb | 1 + spec/rubyspec/library/drb/drbobject/_load_spec.rb | 1 + spec/rubyspec/library/drb/drbobject/eql_spec.rb | 1 + .../library/drb/drbobject/equal_value_spec.rb | 1 + spec/rubyspec/library/drb/drbobject/hash_spec.rb | 1 + .../library/drb/drbobject/method_missing_spec.rb | 1 + spec/rubyspec/library/drb/drbobject/new_spec.rb | 1 + .../library/drb/drbobject/new_with_spec.rb | 1 + .../library/drb/drbobject/new_with_uri_spec.rb | 1 + .../drb/drbobject/prepare_backtrace_spec.rb | 1 + .../drb/drbobject/pretty_print_cycle_spec.rb | 1 + .../library/drb/drbobject/pretty_print_spec.rb | 1 + .../library/drb/drbobject/respond_to_spec.rb | 1 + .../library/drb/drbobject/with_friend_spec.rb | 1 + spec/rubyspec/library/drb/fetch_server_spec.rb | 1 + spec/rubyspec/library/drb/fixtures/test_server.rb | 8 + spec/rubyspec/library/drb/front_spec.rb | 1 + spec/rubyspec/library/drb/here_spec.rb | 1 + spec/rubyspec/library/drb/install_acl_spec.rb | 1 + spec/rubyspec/library/drb/install_id_conv_spec.rb | 1 + spec/rubyspec/library/drb/primary_server_spec.rb | 1 + spec/rubyspec/library/drb/regist_server_spec.rb | 1 + spec/rubyspec/library/drb/remove_server_spec.rb | 1 + spec/rubyspec/library/drb/start_service_spec.rb | 37 + spec/rubyspec/library/drb/stop_service_spec.rb | 25 + spec/rubyspec/library/drb/thread_spec.rb | 1 + spec/rubyspec/library/drb/to_id_spec.rb | 1 + spec/rubyspec/library/drb/to_obj_spec.rb | 1 + spec/rubyspec/library/drb/uri_spec.rb | 1 + spec/rubyspec/library/erb/def_class_spec.rb | 29 + spec/rubyspec/library/erb/def_method_spec.rb | 26 + spec/rubyspec/library/erb/def_module_spec.rb | 27 + .../library/erb/defmethod/def_erb_method_spec.rb | 63 + spec/rubyspec/library/erb/filename_spec.rb | 40 + spec/rubyspec/library/erb/new_spec.rb | 132 + spec/rubyspec/library/erb/result_spec.rb | 86 + spec/rubyspec/library/erb/run_spec.rb | 97 + spec/rubyspec/library/erb/src_spec.rb | 33 + spec/rubyspec/library/erb/util/h_spec.rb | 7 + spec/rubyspec/library/erb/util/html_escape_spec.rb | 8 + .../library/erb/util/shared/html_escape.rb | 42 + .../rubyspec/library/erb/util/shared/url_encode.rb | 38 + spec/rubyspec/library/erb/util/u_spec.rb | 8 + spec/rubyspec/library/erb/util/url_encode_spec.rb | 7 + spec/rubyspec/library/etc/endgrent_spec.rb | 7 + spec/rubyspec/library/etc/endpwent_spec.rb | 7 + spec/rubyspec/library/etc/getgrent_spec.rb | 7 + spec/rubyspec/library/etc/getgrgid_spec.rb | 70 + spec/rubyspec/library/etc/getgrnam_spec.rb | 30 + spec/rubyspec/library/etc/getlogin_spec.rb | 32 + spec/rubyspec/library/etc/getpwent_spec.rb | 7 + spec/rubyspec/library/etc/getpwnam_spec.rb | 28 + spec/rubyspec/library/etc/getpwuid_spec.rb | 36 + spec/rubyspec/library/etc/group_spec.rb | 18 + spec/rubyspec/library/etc/nprocessors_spec.rb | 11 + spec/rubyspec/library/etc/shared/windows.rb | 7 + spec/rubyspec/library/etc/struct_group_spec.rb | 31 + spec/rubyspec/library/etc/struct_passwd_spec.rb | 43 + spec/rubyspec/library/expect/expect_spec.rb | 62 + spec/rubyspec/library/fiber/alive_spec.rb | 48 + spec/rubyspec/library/fiber/current_spec.rb | 61 + spec/rubyspec/library/fiber/resume_spec.rb | 14 + spec/rubyspec/library/fiber/transfer_spec.rb | 51 + spec/rubyspec/library/find/find_spec.rb | 30 + spec/rubyspec/library/find/fixtures/common.rb | 174 + spec/rubyspec/library/find/prune_spec.rb | 12 + .../library/getoptlong/each_option_spec.rb | 7 + spec/rubyspec/library/getoptlong/each_spec.rb | 7 + .../library/getoptlong/error_message_spec.rb | 23 + .../rubyspec/library/getoptlong/get_option_spec.rb | 7 + spec/rubyspec/library/getoptlong/get_spec.rb | 7 + .../rubyspec/library/getoptlong/initialize_spec.rb | 28 + spec/rubyspec/library/getoptlong/ordering_spec.rb | 38 + .../library/getoptlong/set_options_spec.rb | 98 + spec/rubyspec/library/getoptlong/shared/each.rb | 18 + spec/rubyspec/library/getoptlong/shared/get.rb | 55 + spec/rubyspec/library/getoptlong/terminate_spec.rb | 30 + .../rubyspec/library/getoptlong/terminated_spec.rb | 17 + spec/rubyspec/library/ipaddr/hton_spec.rb | 30 + .../library/ipaddr/ipv4_conversion_spec.rb | 46 + spec/rubyspec/library/ipaddr/new_spec.rb | 93 + spec/rubyspec/library/ipaddr/operator_spec.rb | 80 + spec/rubyspec/library/ipaddr/reverse_spec.rb | 27 + spec/rubyspec/library/ipaddr/to_s_spec.rb | 20 + spec/rubyspec/library/logger/device/close_spec.rb | 22 + spec/rubyspec/library/logger/device/new_spec.rb | 47 + spec/rubyspec/library/logger/device/write_spec.rb | 42 + spec/rubyspec/library/logger/fixtures/common.rb | 9 + spec/rubyspec/library/logger/logger/add_spec.rb | 81 + spec/rubyspec/library/logger/logger/close_spec.rb | 20 + .../library/logger/logger/datetime_format_spec.rb | 60 + spec/rubyspec/library/logger/logger/debug_spec.rb | 52 + spec/rubyspec/library/logger/logger/error_spec.rb | 53 + spec/rubyspec/library/logger/logger/fatal_spec.rb | 53 + spec/rubyspec/library/logger/logger/info_spec.rb | 53 + spec/rubyspec/library/logger/logger/new_spec.rb | 63 + .../rubyspec/library/logger/logger/unknown_spec.rb | 36 + spec/rubyspec/library/logger/logger/warn_spec.rb | 53 + spec/rubyspec/library/logger/severity_spec.rb | 13 + .../rubyspec/library/mathn/bignum/exponent_spec.rb | 21 + .../rubyspec/library/mathn/complex/Complex_spec.rb | 14 + .../rubyspec/library/mathn/fixnum/exponent_spec.rb | 17 + spec/rubyspec/library/mathn/float/exponent_spec.rb | 17 + .../mathn/integer/from_prime_division_spec.rb | 11 + .../library/mathn/integer/prime_division_spec.rb | 21 + .../library/mathn/math/fixtures/classes.rb | 3 + spec/rubyspec/library/mathn/math/rsqrt_spec.rb | 17 + spec/rubyspec/library/mathn/math/shared/rsqrt.rb | 21 + spec/rubyspec/library/mathn/math/shared/sqrt.rb | 25 + spec/rubyspec/library/mathn/math/sqrt_spec.rb | 17 + .../library/mathn/rational/Rational_spec.rb | 14 + .../library/mathn/rational/inspect_spec.rb | 15 + spec/rubyspec/library/matrix/I_spec.rb | 6 + spec/rubyspec/library/matrix/build_spec.rb | 73 + spec/rubyspec/library/matrix/clone_spec.rb | 25 + spec/rubyspec/library/matrix/coerce_spec.rb | 10 + spec/rubyspec/library/matrix/collect_spec.rb | 6 + spec/rubyspec/library/matrix/column_size_spec.rb | 13 + spec/rubyspec/library/matrix/column_spec.rb | 35 + spec/rubyspec/library/matrix/column_vector_spec.rb | 25 + .../rubyspec/library/matrix/column_vectors_spec.rb | 26 + spec/rubyspec/library/matrix/columns_spec.rb | 42 + spec/rubyspec/library/matrix/conj_spec.rb | 6 + spec/rubyspec/library/matrix/conjugate_spec.rb | 6 + spec/rubyspec/library/matrix/constructor_spec.rb | 65 + spec/rubyspec/library/matrix/det_spec.rb | 7 + spec/rubyspec/library/matrix/determinant_spec.rb | 7 + spec/rubyspec/library/matrix/diagonal_spec.rb | 72 + spec/rubyspec/library/matrix/divide_spec.rb | 55 + spec/rubyspec/library/matrix/each_spec.rb | 74 + .../library/matrix/each_with_index_spec.rb | 81 + .../eigenvalue_matrix_spec.rb | 9 + .../eigenvalue_decomposition/eigenvalues_spec.rb | 22 + .../eigenvector_matrix_spec.rb | 20 + .../eigenvalue_decomposition/eigenvectors_spec.rb | 22 + .../eigenvalue_decomposition/initialize_spec.rb | 24 + .../matrix/eigenvalue_decomposition/to_a_spec.rb | 18 + .../library/matrix/element_reference_spec.rb | 23 + spec/rubyspec/library/matrix/empty_spec.rb | 68 + spec/rubyspec/library/matrix/eql_spec.rb | 11 + spec/rubyspec/library/matrix/equal_value_spec.rb | 11 + spec/rubyspec/library/matrix/exponent_spec.rb | 51 + spec/rubyspec/library/matrix/find_index_spec.rb | 146 + spec/rubyspec/library/matrix/fixtures/classes.rb | 7 + spec/rubyspec/library/matrix/hash_spec.rb | 15 + spec/rubyspec/library/matrix/hermitian_spec.rb | 34 + spec/rubyspec/library/matrix/identity_spec.rb | 6 + spec/rubyspec/library/matrix/imag_spec.rb | 6 + spec/rubyspec/library/matrix/imaginary_spec.rb | 6 + spec/rubyspec/library/matrix/inspect_spec.rb | 27 + spec/rubyspec/library/matrix/inv_spec.rb | 7 + spec/rubyspec/library/matrix/inverse_from_spec.rb | 6 + spec/rubyspec/library/matrix/inverse_spec.rb | 7 + .../library/matrix/lower_triangular_spec.rb | 24 + .../matrix/lup_decomposition/determinant_spec.rb | 21 + .../matrix/lup_decomposition/initialize_spec.rb | 13 + .../library/matrix/lup_decomposition/l_spec.rb | 18 + .../library/matrix/lup_decomposition/p_spec.rb | 18 + .../library/matrix/lup_decomposition/solve_spec.rb | 53 + .../library/matrix/lup_decomposition/to_a_spec.rb | 33 + .../library/matrix/lup_decomposition/u_spec.rb | 18 + spec/rubyspec/library/matrix/map_spec.rb | 6 + spec/rubyspec/library/matrix/minor_spec.rb | 85 + spec/rubyspec/library/matrix/minus_spec.rb | 42 + spec/rubyspec/library/matrix/multiply_spec.rb | 68 + spec/rubyspec/library/matrix/new_spec.rb | 8 + spec/rubyspec/library/matrix/normal_spec.rb | 26 + spec/rubyspec/library/matrix/orthogonal_spec.rb | 26 + spec/rubyspec/library/matrix/permutation_spec.rb | 32 + spec/rubyspec/library/matrix/plus_spec.rb | 42 + spec/rubyspec/library/matrix/rank_spec.rb | 19 + spec/rubyspec/library/matrix/real_spec.rb | 42 + spec/rubyspec/library/matrix/rect_spec.rb | 6 + spec/rubyspec/library/matrix/rectangular_spec.rb | 6 + spec/rubyspec/library/matrix/regular_spec.rb | 31 + spec/rubyspec/library/matrix/round_spec.rb | 21 + spec/rubyspec/library/matrix/row_size_spec.rb | 13 + spec/rubyspec/library/matrix/row_spec.rb | 36 + spec/rubyspec/library/matrix/row_vector_spec.rb | 24 + spec/rubyspec/library/matrix/row_vectors_spec.rb | 26 + spec/rubyspec/library/matrix/rows_spec.rb | 41 + spec/rubyspec/library/matrix/scalar/Fail_spec.rb | 6 + spec/rubyspec/library/matrix/scalar/Raise_spec.rb | 6 + spec/rubyspec/library/matrix/scalar/divide_spec.rb | 6 + .../library/matrix/scalar/exponent_spec.rb | 6 + .../library/matrix/scalar/included_spec.rb | 6 + .../library/matrix/scalar/initialize_spec.rb | 6 + spec/rubyspec/library/matrix/scalar/minus_spec.rb | 6 + .../library/matrix/scalar/multiply_spec.rb | 6 + spec/rubyspec/library/matrix/scalar/plus_spec.rb | 6 + spec/rubyspec/library/matrix/scalar_spec.rb | 67 + spec/rubyspec/library/matrix/shared/collect.rb | 26 + spec/rubyspec/library/matrix/shared/conjugate.rb | 20 + spec/rubyspec/library/matrix/shared/determinant.rb | 38 + spec/rubyspec/library/matrix/shared/equal_value.rb | 33 + spec/rubyspec/library/matrix/shared/identity.rb | 19 + spec/rubyspec/library/matrix/shared/imaginary.rb | 20 + spec/rubyspec/library/matrix/shared/inverse.rb | 38 + spec/rubyspec/library/matrix/shared/rectangular.rb | 18 + spec/rubyspec/library/matrix/shared/trace.rb | 12 + spec/rubyspec/library/matrix/shared/transpose.rb | 19 + spec/rubyspec/library/matrix/singular_spec.rb | 31 + spec/rubyspec/library/matrix/spec_helper.rb | 35 + spec/rubyspec/library/matrix/square_spec.rb | 28 + spec/rubyspec/library/matrix/symmetric_spec.rb | 29 + spec/rubyspec/library/matrix/t_spec.rb | 6 + spec/rubyspec/library/matrix/to_a_spec.rb | 11 + spec/rubyspec/library/matrix/to_s_spec.rb | 6 + spec/rubyspec/library/matrix/tr_spec.rb | 7 + spec/rubyspec/library/matrix/trace_spec.rb | 7 + spec/rubyspec/library/matrix/transpose_spec.rb | 6 + spec/rubyspec/library/matrix/unit_spec.rb | 6 + spec/rubyspec/library/matrix/unitary_spec.rb | 28 + .../library/matrix/upper_triangular_spec.rb | 24 + .../library/matrix/vector/cross_product_spec.rb | 14 + spec/rubyspec/library/matrix/vector/each2_spec.rb | 49 + spec/rubyspec/library/matrix/vector/eql_spec.rb | 16 + .../library/matrix/vector/inner_product_spec.rb | 22 + .../library/matrix/vector/normalize_spec.rb | 18 + spec/rubyspec/library/matrix/zero_spec.rb | 52 + spec/rubyspec/library/net/FTPError_spec.rb | 8 + spec/rubyspec/library/net/FTPPermError_spec.rb | 12 + spec/rubyspec/library/net/FTPProtoError_spec.rb | 12 + spec/rubyspec/library/net/FTPReplyError_spec.rb | 12 + spec/rubyspec/library/net/FTPTempError_spec.rb | 12 + spec/rubyspec/library/net/ftp/abort_spec.rb | 62 + spec/rubyspec/library/net/ftp/acct_spec.rb | 58 + spec/rubyspec/library/net/ftp/binary_spec.rb | 24 + spec/rubyspec/library/net/ftp/chdir_spec.rb | 99 + spec/rubyspec/library/net/ftp/close_spec.rb | 30 + spec/rubyspec/library/net/ftp/closed_spec.rb | 21 + spec/rubyspec/library/net/ftp/connect_spec.rb | 49 + spec/rubyspec/library/net/ftp/debug_mode_spec.rb | 23 + .../library/net/ftp/default_passive_spec.rb | 10 + spec/rubyspec/library/net/ftp/delete_spec.rb | 59 + spec/rubyspec/library/net/ftp/dir_spec.rb | 8 + .../library/net/ftp/fixtures/default_passive.rb | 3 + spec/rubyspec/library/net/ftp/fixtures/passive.rb | 2 + .../library/net/ftp/fixtures/putbinaryfile | 3 + spec/rubyspec/library/net/ftp/fixtures/puttextfile | 3 + spec/rubyspec/library/net/ftp/fixtures/server.rb | 276 + spec/rubyspec/library/net/ftp/get_spec.rb | 21 + .../rubyspec/library/net/ftp/getbinaryfile_spec.rb | 8 + spec/rubyspec/library/net/ftp/getdir_spec.rb | 7 + spec/rubyspec/library/net/ftp/gettextfile_spec.rb | 8 + spec/rubyspec/library/net/ftp/help_spec.rb | 66 + spec/rubyspec/library/net/ftp/initialize_spec.rb | 87 + .../library/net/ftp/last_response_code_spec.rb | 8 + .../rubyspec/library/net/ftp/last_response_spec.rb | 25 + spec/rubyspec/library/net/ftp/lastresp_spec.rb | 8 + spec/rubyspec/library/net/ftp/list_spec.rb | 8 + spec/rubyspec/library/net/ftp/login_spec.rb | 195 + spec/rubyspec/library/net/ftp/ls_spec.rb | 8 + spec/rubyspec/library/net/ftp/mdtm_spec.rb | 38 + spec/rubyspec/library/net/ftp/mkdir_spec.rb | 61 + spec/rubyspec/library/net/ftp/mtime_spec.rb | 50 + spec/rubyspec/library/net/ftp/nlst_spec.rb | 92 + spec/rubyspec/library/net/ftp/noop_spec.rb | 38 + spec/rubyspec/library/net/ftp/open_spec.rb | 55 + spec/rubyspec/library/net/ftp/passive_spec.rb | 36 + spec/rubyspec/library/net/ftp/put_spec.rb | 21 + .../rubyspec/library/net/ftp/putbinaryfile_spec.rb | 8 + spec/rubyspec/library/net/ftp/puttextfile_spec.rb | 8 + spec/rubyspec/library/net/ftp/pwd_spec.rb | 53 + spec/rubyspec/library/net/ftp/quit_spec.rb | 33 + spec/rubyspec/library/net/ftp/rename_spec.rb | 94 + spec/rubyspec/library/net/ftp/resume_spec.rb | 23 + spec/rubyspec/library/net/ftp/retrbinary_spec.rb | 30 + spec/rubyspec/library/net/ftp/retrlines_spec.rb | 34 + spec/rubyspec/library/net/ftp/return_code_spec.rb | 24 + spec/rubyspec/library/net/ftp/rmdir_spec.rb | 58 + spec/rubyspec/library/net/ftp/sendcmd_spec.rb | 54 + spec/rubyspec/library/net/ftp/set_socket_spec.rb | 8 + .../library/net/ftp/shared/getbinaryfile.rb | 150 + .../rubyspec/library/net/ftp/shared/gettextfile.rb | 100 + .../library/net/ftp/shared/last_response_code.rb | 25 + spec/rubyspec/library/net/ftp/shared/list.rb | 104 + .../library/net/ftp/shared/putbinaryfile.rb | 167 + .../rubyspec/library/net/ftp/shared/puttextfile.rb | 120 + spec/rubyspec/library/net/ftp/shared/pwd.rb | 3 + spec/rubyspec/library/net/ftp/site_spec.rb | 53 + spec/rubyspec/library/net/ftp/size_spec.rb | 48 + spec/rubyspec/library/net/ftp/spec_helper.rb | 5 + spec/rubyspec/library/net/ftp/status_spec.rb | 63 + spec/rubyspec/library/net/ftp/storbinary_spec.rb | 48 + spec/rubyspec/library/net/ftp/storlines_spec.rb | 43 + spec/rubyspec/library/net/ftp/system_spec.rb | 48 + spec/rubyspec/library/net/ftp/voidcmd_spec.rb | 54 + spec/rubyspec/library/net/ftp/welcome_spec.rb | 25 + .../library/net/http/HTTPBadResponse_spec.rb | 8 + spec/rubyspec/library/net/http/HTTPError_spec.rb | 12 + .../library/net/http/HTTPFatalError_spec.rb | 12 + .../library/net/http/HTTPHeaderSyntaxError_spec.rb | 8 + .../library/net/http/HTTPRetriableError_spec.rb | 12 + .../library/net/http/HTTPServerException_spec.rb | 12 + spec/rubyspec/library/net/http/http/Proxy_spec.rb | 35 + spec/rubyspec/library/net/http/http/active_spec.rb | 8 + .../rubyspec/library/net/http/http/address_spec.rb | 9 + .../net/http/http/close_on_empty_response_spec.rb | 10 + spec/rubyspec/library/net/http/http/copy_spec.rb | 21 + .../library/net/http/http/default_port_spec.rb | 8 + spec/rubyspec/library/net/http/http/delete_spec.rb | 21 + spec/rubyspec/library/net/http/http/finish_spec.rb | 29 + .../library/net/http/http/fixtures/http_server.rb | 90 + spec/rubyspec/library/net/http/http/get2_spec.rb | 8 + .../library/net/http/http/get_print_spec.rb | 30 + .../library/net/http/http/get_response_spec.rb | 30 + spec/rubyspec/library/net/http/http/get_spec.rb | 26 + spec/rubyspec/library/net/http/http/head2_spec.rb | 9 + spec/rubyspec/library/net/http/http/head_spec.rb | 25 + .../net/http/http/http_default_port_spec.rb | 8 + .../net/http/http/https_default_port_spec.rb | 8 + .../library/net/http/http/initialize_spec.rb | 46 + .../rubyspec/library/net/http/http/inspect_spec.rb | 24 + .../library/net/http/http/is_version_1_1_spec.rb | 7 + .../library/net/http/http/is_version_1_2_spec.rb | 7 + spec/rubyspec/library/net/http/http/lock_spec.rb | 21 + spec/rubyspec/library/net/http/http/mkcol_spec.rb | 21 + spec/rubyspec/library/net/http/http/move_spec.rb | 25 + spec/rubyspec/library/net/http/http/new_spec.rb | 86 + spec/rubyspec/library/net/http/http/newobj_spec.rb | 48 + .../library/net/http/http/open_timeout_spec.rb | 35 + .../rubyspec/library/net/http/http/options_spec.rb | 25 + spec/rubyspec/library/net/http/http/port_spec.rb | 9 + spec/rubyspec/library/net/http/http/post2_spec.rb | 8 + .../library/net/http/http/post_form_spec.rb | 22 + spec/rubyspec/library/net/http/http/post_spec.rb | 38 + .../library/net/http/http/propfind_spec.rb | 24 + .../library/net/http/http/proppatch_spec.rb | 24 + .../library/net/http/http/proxy_address_spec.rb | 31 + .../library/net/http/http/proxy_class_spec.rb | 9 + .../library/net/http/http/proxy_pass_spec.rb | 39 + .../library/net/http/http/proxy_port_spec.rb | 39 + .../library/net/http/http/proxy_user_spec.rb | 39 + spec/rubyspec/library/net/http/http/put2_spec.rb | 8 + spec/rubyspec/library/net/http/http/put_spec.rb | 24 + .../library/net/http/http/read_timeout_spec.rb | 24 + .../library/net/http/http/request_get_spec.rb | 8 + .../library/net/http/http/request_head_spec.rb | 8 + .../library/net/http/http/request_post_spec.rb | 8 + .../library/net/http/http/request_put_spec.rb | 8 + .../rubyspec/library/net/http/http/request_spec.rb | 109 + .../library/net/http/http/request_types_spec.rb | 254 + .../library/net/http/http/send_request_spec.rb | 118 + .../library/net/http/http/set_debug_output_spec.rb | 33 + .../library/net/http/http/shared/request_get.rb | 41 + .../library/net/http/http/shared/request_head.rb | 41 + .../library/net/http/http/shared/request_post.rb | 41 + .../library/net/http/http/shared/request_put.rb | 41 + .../library/net/http/http/shared/started.rb | 26 + .../library/net/http/http/shared/version_1_1.rb | 6 + .../library/net/http/http/shared/version_1_2.rb | 6 + .../library/net/http/http/socket_type_spec.rb | 8 + spec/rubyspec/library/net/http/http/start_spec.rb | 111 + .../rubyspec/library/net/http/http/started_spec.rb | 8 + spec/rubyspec/library/net/http/http/trace_spec.rb | 24 + spec/rubyspec/library/net/http/http/unlock_spec.rb | 24 + .../rubyspec/library/net/http/http/use_ssl_spec.rb | 9 + .../library/net/http/http/version_1_1_spec.rb | 7 + .../library/net/http/http/version_1_2_spec.rb | 20 + .../net/http/httpexceptions/fixtures/classes.rb | 5 + .../net/http/httpexceptions/initialize_spec.rb | 17 + .../net/http/httpexceptions/response_spec.rb | 10 + .../net/http/httpgenericrequest/body_exist_spec.rb | 22 + .../net/http/httpgenericrequest/body_spec.rb | 30 + .../http/httpgenericrequest/body_stream_spec.rb | 32 + .../net/http/httpgenericrequest/exec_spec.rb | 131 + .../net/http/httpgenericrequest/inspect_spec.rb | 25 + .../net/http/httpgenericrequest/method_spec.rb | 15 + .../net/http/httpgenericrequest/path_spec.rb | 12 + .../request_body_permitted_spec.rb | 12 + .../response_body_permitted_spec.rb | 12 + .../httpgenericrequest/set_body_internal_spec.rb | 21 + .../library/net/http/httpheader/add_field_spec.rb | 31 + .../library/net/http/httpheader/basic_auth_spec.rb | 14 + .../net/http/httpheader/canonical_each_spec.rb | 8 + .../library/net/http/httpheader/chunked_spec.rb | 22 + .../net/http/httpheader/content_length_spec.rb | 54 + .../net/http/httpheader/content_range_spec.rb | 32 + .../net/http/httpheader/content_type_spec.rb | 26 + .../library/net/http/httpheader/delete_spec.rb | 30 + .../http/httpheader/each_capitalized_name_spec.rb | 35 + .../net/http/httpheader/each_capitalized_spec.rb | 9 + .../net/http/httpheader/each_header_spec.rb | 8 + .../library/net/http/httpheader/each_key_spec.rb | 8 + .../library/net/http/httpheader/each_name_spec.rb | 8 + .../library/net/http/httpheader/each_spec.rb | 8 + .../library/net/http/httpheader/each_value_spec.rb | 35 + .../net/http/httpheader/element_reference_spec.rb | 39 + .../net/http/httpheader/element_set_spec.rb | 41 + .../library/net/http/httpheader/fetch_spec.rb | 68 + .../net/http/httpheader/fixtures/classes.rb | 11 + .../library/net/http/httpheader/form_data_spec.rb | 8 + .../library/net/http/httpheader/get_fields_spec.rb | 39 + .../http/httpheader/initialize_http_header_spec.rb | 22 + .../library/net/http/httpheader/key_spec.rb | 21 + .../library/net/http/httpheader/length_spec.rb | 8 + .../library/net/http/httpheader/main_type_spec.rb | 24 + .../net/http/httpheader/proxy_basic_auth_spec.rb | 14 + .../net/http/httpheader/range_length_spec.rb | 32 + .../library/net/http/httpheader/range_spec.rb | 48 + .../net/http/httpheader/set_content_type_spec.rb | 8 + .../net/http/httpheader/set_form_data_spec.rb | 8 + .../library/net/http/httpheader/set_range_spec.rb | 8 + .../net/http/httpheader/shared/each_capitalized.rb | 31 + .../net/http/httpheader/shared/each_header.rb | 31 + .../net/http/httpheader/shared/each_name.rb | 31 + .../net/http/httpheader/shared/set_content_type.rb | 18 + .../net/http/httpheader/shared/set_form_data.rb | 27 + .../net/http/httpheader/shared/set_range.rb | 89 + .../library/net/http/httpheader/shared/size.rb | 18 + .../library/net/http/httpheader/size_spec.rb | 8 + .../library/net/http/httpheader/sub_type_spec.rb | 32 + .../library/net/http/httpheader/to_hash_spec.rb | 25 + .../net/http/httpheader/type_params_spec.rb | 24 + .../net/http/httprequest/initialize_spec.rb | 45 + .../net/http/httpresponse/body_permitted_spec.rb | 13 + .../library/net/http/httpresponse/body_spec.rb | 7 + .../library/net/http/httpresponse/code_spec.rb | 24 + .../net/http/httpresponse/code_type_spec.rb | 24 + .../library/net/http/httpresponse/entity_spec.rb | 7 + .../library/net/http/httpresponse/error_spec.rb | 24 + .../net/http/httpresponse/error_type_spec.rb | 24 + .../net/http/httpresponse/exception_type_spec.rb | 13 + .../library/net/http/httpresponse/header_spec.rb | 9 + .../net/http/httpresponse/http_version_spec.rb | 12 + .../net/http/httpresponse/initialize_spec.rb | 11 + .../library/net/http/httpresponse/inspect_spec.rb | 15 + .../library/net/http/httpresponse/message_spec.rb | 9 + .../library/net/http/httpresponse/msg_spec.rb | 9 + .../net/http/httpresponse/read_body_spec.rb | 86 + .../net/http/httpresponse/read_header_spec.rb | 9 + .../library/net/http/httpresponse/read_new_spec.rb | 22 + .../net/http/httpresponse/reading_body_spec.rb | 58 + .../library/net/http/httpresponse/response_spec.rb | 9 + .../library/net/http/httpresponse/shared/body.rb | 18 + .../library/net/http/httpresponse/value_spec.rb | 24 + .../rubyspec/library/observer/add_observer_spec.rb | 23 + .../library/observer/count_observers_spec.rb | 23 + .../library/observer/delete_observer_spec.rb | 19 + .../library/observer/delete_observers_spec.rb | 19 + spec/rubyspec/library/observer/fixtures/classes.rb | 17 + .../library/observer/notify_observers_spec.rb | 31 + spec/rubyspec/library/open3/capture2_spec.rb | 6 + spec/rubyspec/library/open3/capture2e_spec.rb | 6 + spec/rubyspec/library/open3/capture3_spec.rb | 6 + spec/rubyspec/library/open3/pipeline_r_spec.rb | 6 + spec/rubyspec/library/open3/pipeline_rw_spec.rb | 6 + spec/rubyspec/library/open3/pipeline_spec.rb | 6 + spec/rubyspec/library/open3/pipeline_start_spec.rb | 6 + spec/rubyspec/library/open3/pipeline_w_spec.rb | 6 + spec/rubyspec/library/open3/popen2_spec.rb | 6 + spec/rubyspec/library/open3/popen2e_spec.rb | 6 + spec/rubyspec/library/open3/popen3_spec.rb | 43 + spec/rubyspec/library/openssl/cipher_spec.rb | 9 + .../rubyspec/library/openssl/config/freeze_spec.rb | 16 + spec/rubyspec/library/openssl/hmac/digest_spec.rb | 16 + .../library/openssl/hmac/hexdigest_spec.rb | 16 + .../library/openssl/random/pseudo_bytes_spec.rb | 8 + .../library/openssl/random/random_bytes_spec.rb | 6 + .../library/openssl/random/shared/random_bytes.rb | 29 + spec/rubyspec/library/openssl/shared/constants.rb | 11 + .../library/openssl/x509/name/parse_spec.rb | 48 + .../library/openstruct/delete_field_spec.rb | 19 + .../library/openstruct/element_reference_spec.rb | 13 + .../library/openstruct/element_set_spec.rb | 13 + .../library/openstruct/equal_value_spec.rb | 28 + .../library/openstruct/fixtures/classes.rb | 4 + spec/rubyspec/library/openstruct/frozen_spec.rb | 38 + .../rubyspec/library/openstruct/initialize_spec.rb | 8 + spec/rubyspec/library/openstruct/inspect_spec.rb | 8 + .../library/openstruct/marshal_dump_spec.rb | 9 + .../library/openstruct/marshal_load_spec.rb | 12 + .../library/openstruct/method_missing_spec.rb | 47 + spec/rubyspec/library/openstruct/new_spec.rb | 20 + spec/rubyspec/library/openstruct/shared/inspect.rb | 20 + spec/rubyspec/library/openstruct/to_h_spec.rb | 29 + spec/rubyspec/library/openstruct/to_s_spec.rb | 8 + spec/rubyspec/library/pathname/absolute_spec.rb | 23 + spec/rubyspec/library/pathname/equal_value_spec.rb | 15 + spec/rubyspec/library/pathname/hash_spec.rb | 15 + spec/rubyspec/library/pathname/join_spec.rb | 40 + spec/rubyspec/library/pathname/new_spec.rb | 28 + spec/rubyspec/library/pathname/parent_spec.rb | 19 + spec/rubyspec/library/pathname/realdirpath_spec.rb | 10 + spec/rubyspec/library/pathname/realpath_spec.rb | 10 + .../library/pathname/relative_path_from_spec.rb | 51 + spec/rubyspec/library/pathname/relative_spec.rb | 23 + spec/rubyspec/library/pathname/root_spec.rb | 27 + spec/rubyspec/library/pathname/sub_spec.rb | 16 + spec/rubyspec/library/pp/pp_spec.rb | 25 + spec/rubyspec/library/prime/each_spec.rb | 167 + spec/rubyspec/library/prime/instance_spec.rb | 21 + .../library/prime/int_from_prime_division_spec.rb | 13 + .../library/prime/integer/each_prime_spec.rb | 13 + .../prime/integer/from_prime_division_spec.rb | 13 + .../library/prime/integer/prime_division_spec.rb | 19 + spec/rubyspec/library/prime/integer/prime_spec.rb | 17 + spec/rubyspec/library/prime/next_spec.rb | 7 + spec/rubyspec/library/prime/prime_division_spec.rb | 25 + spec/rubyspec/library/prime/prime_spec.rb | 17 + spec/rubyspec/library/prime/shared/next.rb | 8 + spec/rubyspec/library/prime/succ_spec.rb | 7 + .../readline/basic_quote_characters_spec.rb | 18 + .../readline/basic_word_break_characters_spec.rb | 16 + .../readline/completer_quote_characters_spec.rb | 16 + .../completer_word_break_characters_spec.rb | 16 + .../readline/completion_append_character_spec.rb | 16 + .../library/readline/completion_case_fold_spec.rb | 18 + .../library/readline/completion_proc_spec.rb | 22 + spec/rubyspec/library/readline/constants_spec.rb | 18 + .../library/readline/emacs_editing_mode_spec.rb | 11 + .../readline/filename_quote_characters_spec.rb | 18 + .../library/readline/history/append_spec.rb | 28 + .../library/readline/history/delete_at_spec.rb | 45 + .../rubyspec/library/readline/history/each_spec.rb | 29 + .../readline/history/element_reference_spec.rb | 40 + .../library/readline/history/element_set_spec.rb | 35 + .../library/readline/history/empty_spec.rb | 13 + .../library/readline/history/history_spec.rb | 9 + .../library/readline/history/length_spec.rb | 9 + spec/rubyspec/library/readline/history/pop_spec.rb | 30 + .../rubyspec/library/readline/history/push_spec.rb | 26 + .../library/readline/history/shared/size.rb | 14 + .../library/readline/history/shift_spec.rb | 30 + .../rubyspec/library/readline/history/size_spec.rb | 9 + .../rubyspec/library/readline/history/to_s_spec.rb | 9 + spec/rubyspec/library/readline/readline_spec.rb | 31 + spec/rubyspec/library/readline/spec_helper.rb | 13 + .../library/readline/vi_editing_mode_spec.rb | 11 + spec/rubyspec/library/resolv/get_address_spec.rb | 21 + spec/rubyspec/library/resolv/get_addresses_spec.rb | 14 + spec/rubyspec/library/resolv/get_name_spec.rb | 19 + spec/rubyspec/library/resolv/get_names_spec.rb | 14 + .../rubyspec/library/rexml/attribute/clone_spec.rb | 11 + .../library/rexml/attribute/element_spec.rb | 23 + .../library/rexml/attribute/equal_value_spec.rb | 18 + spec/rubyspec/library/rexml/attribute/hash_spec.rb | 13 + .../library/rexml/attribute/initialize_spec.rb | 29 + .../library/rexml/attribute/inspect_spec.rb | 20 + .../library/rexml/attribute/namespace_spec.rb | 24 + .../library/rexml/attribute/node_type_spec.rb | 10 + .../library/rexml/attribute/prefix_spec.rb | 18 + .../library/rexml/attribute/remove_spec.rb | 20 + spec/rubyspec/library/rexml/attribute/to_s_spec.rb | 14 + .../library/rexml/attribute/to_string_spec.rb | 15 + .../rubyspec/library/rexml/attribute/value_spec.rb | 15 + .../rubyspec/library/rexml/attribute/write_spec.rb | 23 + .../rubyspec/library/rexml/attribute/xpath_spec.rb | 20 + spec/rubyspec/library/rexml/attributes/add_spec.rb | 7 + .../library/rexml/attributes/append_spec.rb | 7 + .../library/rexml/attributes/delete_all_spec.rb | 31 + .../library/rexml/attributes/delete_spec.rb | 27 + .../rexml/attributes/each_attribute_spec.rb | 25 + .../rubyspec/library/rexml/attributes/each_spec.rb | 25 + .../rexml/attributes/element_reference_spec.rb | 19 + .../library/rexml/attributes/element_set_spec.rb | 26 + .../rexml/attributes/get_attribute_ns_spec.rb | 14 + .../library/rexml/attributes/get_attribute_spec.rb | 29 + .../library/rexml/attributes/initialize_spec.rb | 18 + .../library/rexml/attributes/length_spec.rb | 7 + .../library/rexml/attributes/namespaces_spec.rb | 6 + .../library/rexml/attributes/prefixes_spec.rb | 24 + .../library/rexml/attributes/shared/add.rb | 17 + .../library/rexml/attributes/shared/length.rb | 13 + .../rubyspec/library/rexml/attributes/size_spec.rb | 7 + .../rubyspec/library/rexml/attributes/to_a_spec.rb | 20 + spec/rubyspec/library/rexml/cdata/clone_spec.rb | 10 + .../library/rexml/cdata/initialize_spec.rb | 24 + spec/rubyspec/library/rexml/cdata/shared/to_s.rb | 11 + spec/rubyspec/library/rexml/cdata/to_s_spec.rb | 7 + spec/rubyspec/library/rexml/cdata/value_spec.rb | 7 + .../library/rexml/document/add_element_spec.rb | 31 + spec/rubyspec/library/rexml/document/add_spec.rb | 57 + spec/rubyspec/library/rexml/document/clone_spec.rb | 20 + .../library/rexml/document/doctype_spec.rb | 15 + .../library/rexml/document/encoding_spec.rb | 22 + .../library/rexml/document/expanded_name_spec.rb | 16 + spec/rubyspec/library/rexml/document/new_spec.rb | 36 + .../library/rexml/document/node_type_spec.rb | 8 + spec/rubyspec/library/rexml/document/root_spec.rb | 12 + .../library/rexml/document/stand_alone_spec.rb | 19 + .../library/rexml/document/version_spec.rb | 14 + spec/rubyspec/library/rexml/document/write_spec.rb | 35 + .../library/rexml/document/xml_decl_spec.rb | 15 + .../library/rexml/element/add_attribute_spec.rb | 41 + .../library/rexml/element/add_attributes_spec.rb | 22 + .../library/rexml/element/add_element_spec.rb | 39 + .../library/rexml/element/add_namespace_spec.rb | 24 + .../library/rexml/element/add_text_spec.rb | 24 + .../library/rexml/element/attribute_spec.rb | 17 + .../library/rexml/element/attributes_spec.rb | 19 + spec/rubyspec/library/rexml/element/cdatas_spec.rb | 24 + spec/rubyspec/library/rexml/element/clone_spec.rb | 29 + .../library/rexml/element/comments_spec.rb | 20 + .../library/rexml/element/delete_attribute_spec.rb | 39 + .../library/rexml/element/delete_element_spec.rb | 49 + .../library/rexml/element/delete_namespace_spec.rb | 25 + .../library/rexml/element/document_spec.rb | 18 + .../element/each_element_with_attribute_spec.rb | 35 + .../rexml/element/each_element_with_text_spec.rb | 31 + .../library/rexml/element/get_text_spec.rb | 18 + .../library/rexml/element/has_attributes_spec.rb | 17 + .../library/rexml/element/has_elements_spec.rb | 18 + .../library/rexml/element/has_text_spec.rb | 16 + .../rubyspec/library/rexml/element/inspect_spec.rb | 27 + .../library/rexml/element/instructions_spec.rb | 21 + .../library/rexml/element/namespace_spec.rb | 27 + .../library/rexml/element/namespaces_spec.rb | 32 + spec/rubyspec/library/rexml/element/new_spec.rb | 35 + .../library/rexml/element/next_element_spec.rb | 19 + .../library/rexml/element/node_type_spec.rb | 8 + .../library/rexml/element/prefixes_spec.rb | 23 + .../library/rexml/element/previous_element_spec.rb | 20 + spec/rubyspec/library/rexml/element/raw_spec.rb | 24 + spec/rubyspec/library/rexml/element/root_spec.rb | 28 + spec/rubyspec/library/rexml/element/text_spec.rb | 46 + spec/rubyspec/library/rexml/element/texts_spec.rb | 16 + .../library/rexml/element/whitespace_spec.rb | 23 + .../library/rexml/node/each_recursive_spec.rb | 21 + .../rexml/node/find_first_recursive_spec.rb | 25 + .../library/rexml/node/index_in_parent_spec.rb | 15 + .../library/rexml/node/next_sibling_node_spec.rb | 21 + spec/rubyspec/library/rexml/node/parent_spec.rb | 21 + .../rexml/node/previous_sibling_node_spec.rb | 21 + spec/rubyspec/library/rexml/shared/each_element.rb | 36 + .../rubyspec/library/rexml/shared/elements_to_a.rb | 34 + spec/rubyspec/library/rexml/text/append_spec.rb | 10 + spec/rubyspec/library/rexml/text/clone_spec.rb | 10 + .../rubyspec/library/rexml/text/comparison_spec.rb | 25 + spec/rubyspec/library/rexml/text/empty_spec.rb | 12 + .../library/rexml/text/indent_text_spec.rb | 24 + spec/rubyspec/library/rexml/text/inspect_spec.rb | 8 + spec/rubyspec/library/rexml/text/new_spec.rb | 49 + spec/rubyspec/library/rexml/text/node_type_spec.rb | 8 + spec/rubyspec/library/rexml/text/normalize_spec.rb | 8 + .../rexml/text/read_with_substitution_spec.rb | 13 + spec/rubyspec/library/rexml/text/to_s_spec.rb | 18 + .../library/rexml/text/unnormalize_spec.rb | 8 + spec/rubyspec/library/rexml/text/value_spec.rb | 37 + spec/rubyspec/library/rexml/text/wrap_spec.rb | 21 + .../rexml/text/write_with_substitution_spec.rb | 33 + spec/rubyspec/library/scanf/io/block_scanf_spec.rb | 7 + spec/rubyspec/library/scanf/io/fixtures/date.txt | 4 + .../library/scanf/io/fixtures/helloworld.txt | 1 + spec/rubyspec/library/scanf/io/scanf_spec.rb | 35 + .../library/scanf/io/shared/block_scanf.rb | 28 + .../library/scanf/string/block_scanf_spec.rb | 7 + spec/rubyspec/library/scanf/string/scanf_spec.rb | 53 + .../library/scanf/string/shared/block_scanf.rb | 25 + spec/rubyspec/library/securerandom/base64_spec.rb | 55 + spec/rubyspec/library/securerandom/hex_spec.rb | 54 + .../library/securerandom/random_bytes_spec.rb | 50 + .../library/securerandom/random_number_spec.rb | 47 + spec/rubyspec/library/set/add_spec.rb | 27 + spec/rubyspec/library/set/append_spec.rb | 7 + spec/rubyspec/library/set/classify_spec.rb | 27 + spec/rubyspec/library/set/clear_spec.rb | 17 + spec/rubyspec/library/set/collect_spec.rb | 7 + spec/rubyspec/library/set/constructor_spec.rb | 15 + spec/rubyspec/library/set/delete_if_spec.rb | 38 + spec/rubyspec/library/set/delete_spec.rb | 37 + spec/rubyspec/library/set/difference_spec.rb | 7 + spec/rubyspec/library/set/divide_spec.rb | 34 + spec/rubyspec/library/set/each_spec.rb | 26 + spec/rubyspec/library/set/empty_spec.rb | 10 + .../rubyspec/library/set/enumerable/to_set_spec.rb | 19 + spec/rubyspec/library/set/eql_spec.rb | 15 + spec/rubyspec/library/set/equal_value_spec.rb | 26 + spec/rubyspec/library/set/exclusion_spec.rb | 18 + spec/rubyspec/library/set/flatten_merge_spec.rb | 23 + spec/rubyspec/library/set/flatten_spec.rb | 40 + spec/rubyspec/library/set/hash_spec.rb | 13 + spec/rubyspec/library/set/include_spec.rb | 7 + spec/rubyspec/library/set/initialize_spec.rb | 24 + spec/rubyspec/library/set/inspect_spec.rb | 18 + spec/rubyspec/library/set/intersection_spec.rb | 11 + spec/rubyspec/library/set/keep_if_spec.rb | 38 + spec/rubyspec/library/set/length_spec.rb | 7 + spec/rubyspec/library/set/map_spec.rb | 7 + spec/rubyspec/library/set/member_spec.rb | 7 + spec/rubyspec/library/set/merge_spec.rb | 19 + spec/rubyspec/library/set/minus_spec.rb | 7 + spec/rubyspec/library/set/plus_spec.rb | 7 + .../library/set/pretty_print_cycle_spec.rb | 10 + spec/rubyspec/library/set/pretty_print_spec.rb | 17 + spec/rubyspec/library/set/proper_subset_spec.rb | 34 + spec/rubyspec/library/set/proper_superset_spec.rb | 34 + spec/rubyspec/library/set/reject_spec.rb | 42 + spec/rubyspec/library/set/replace_spec.rb | 17 + spec/rubyspec/library/set/select_spec.rb | 42 + spec/rubyspec/library/set/shared/add.rb | 14 + spec/rubyspec/library/set/shared/collect.rb | 20 + spec/rubyspec/library/set/shared/difference.rb | 15 + spec/rubyspec/library/set/shared/include.rb | 7 + spec/rubyspec/library/set/shared/intersection.rb | 15 + spec/rubyspec/library/set/shared/length.rb | 6 + spec/rubyspec/library/set/shared/union.rb | 15 + spec/rubyspec/library/set/size_spec.rb | 7 + spec/rubyspec/library/set/sortedset/add_spec.rb | 33 + spec/rubyspec/library/set/sortedset/append_spec.rb | 7 + .../library/set/sortedset/classify_spec.rb | 27 + spec/rubyspec/library/set/sortedset/clear_spec.rb | 17 + .../rubyspec/library/set/sortedset/collect_spec.rb | 7 + .../library/set/sortedset/constructor_spec.rb | 15 + .../library/set/sortedset/delete_if_spec.rb | 38 + spec/rubyspec/library/set/sortedset/delete_spec.rb | 37 + .../library/set/sortedset/difference_spec.rb | 7 + spec/rubyspec/library/set/sortedset/divide_spec.rb | 34 + spec/rubyspec/library/set/sortedset/each_spec.rb | 26 + spec/rubyspec/library/set/sortedset/empty_spec.rb | 10 + spec/rubyspec/library/set/sortedset/eql_spec.rb | 16 + .../library/set/sortedset/equal_value_spec.rb | 13 + .../library/set/sortedset/exclusion_spec.rb | 18 + .../library/set/sortedset/flatten_merge_spec.rb | 8 + .../rubyspec/library/set/sortedset/flatten_spec.rb | 44 + spec/rubyspec/library/set/sortedset/hash_spec.rb | 13 + .../rubyspec/library/set/sortedset/include_spec.rb | 7 + .../library/set/sortedset/initialize_spec.rb | 24 + .../rubyspec/library/set/sortedset/inspect_spec.rb | 10 + .../library/set/sortedset/intersection_spec.rb | 11 + .../rubyspec/library/set/sortedset/keep_if_spec.rb | 31 + spec/rubyspec/library/set/sortedset/length_spec.rb | 7 + spec/rubyspec/library/set/sortedset/map_spec.rb | 7 + spec/rubyspec/library/set/sortedset/member_spec.rb | 7 + spec/rubyspec/library/set/sortedset/merge_spec.rb | 19 + spec/rubyspec/library/set/sortedset/minus_spec.rb | 7 + spec/rubyspec/library/set/sortedset/plus_spec.rb | 7 + .../set/sortedset/pretty_print_cycle_spec.rb | 10 + .../library/set/sortedset/pretty_print_spec.rb | 17 + .../library/set/sortedset/proper_subset_spec.rb | 33 + .../library/set/sortedset/proper_superset_spec.rb | 33 + spec/rubyspec/library/set/sortedset/reject_spec.rb | 42 + .../rubyspec/library/set/sortedset/replace_spec.rb | 17 + spec/rubyspec/library/set/sortedset/select_spec.rb | 35 + spec/rubyspec/library/set/sortedset/shared/add.rb | 14 + .../library/set/sortedset/shared/collect.rb | 20 + .../library/set/sortedset/shared/difference.rb | 15 + .../library/set/sortedset/shared/include.rb | 7 + .../library/set/sortedset/shared/intersection.rb | 15 + .../library/set/sortedset/shared/length.rb | 6 + .../rubyspec/library/set/sortedset/shared/union.rb | 15 + spec/rubyspec/library/set/sortedset/size_spec.rb | 7 + spec/rubyspec/library/set/sortedset/subset_spec.rb | 33 + .../library/set/sortedset/subtract_spec.rb | 17 + .../library/set/sortedset/superset_spec.rb | 33 + spec/rubyspec/library/set/sortedset/to_a_spec.rb | 8 + spec/rubyspec/library/set/sortedset/union_spec.rb | 11 + spec/rubyspec/library/set/subset_spec.rb | 34 + spec/rubyspec/library/set/subtract_spec.rb | 17 + spec/rubyspec/library/set/superset_spec.rb | 34 + spec/rubyspec/library/set/to_a_spec.rb | 8 + spec/rubyspec/library/set/union_spec.rb | 11 + .../rubyspec/library/shellwords/shellwords_spec.rb | 29 + spec/rubyspec/library/singleton/allocate_spec.rb | 8 + spec/rubyspec/library/singleton/clone_spec.rb | 8 + spec/rubyspec/library/singleton/dump_spec.rb | 14 + spec/rubyspec/library/singleton/dup_spec.rb | 8 + .../rubyspec/library/singleton/fixtures/classes.rb | 18 + spec/rubyspec/library/singleton/instance_spec.rb | 30 + spec/rubyspec/library/singleton/load_spec.rb | 21 + spec/rubyspec/library/singleton/new_spec.rb | 8 + .../library/socket/addrinfo/afamily_spec.rb | 38 + spec/rubyspec/library/socket/addrinfo/bind_spec.rb | 29 + .../library/socket/addrinfo/canonname_spec.rb | 19 + .../library/socket/addrinfo/initialize_spec.rb | 253 + .../socket/addrinfo/inspect_sockaddr_spec.rb | 25 + .../library/socket/addrinfo/ip_address_spec.rb | 36 + .../library/socket/addrinfo/ip_port_spec.rb | 36 + spec/rubyspec/library/socket/addrinfo/ip_spec.rb | 36 + .../library/socket/addrinfo/ip_unpack_spec.rb | 36 + .../library/socket/addrinfo/ipv4_loopback_spec.rb | 46 + .../library/socket/addrinfo/ipv4_multicast_spec.rb | 46 + .../library/socket/addrinfo/ipv4_private_spec.rb | 41 + spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb | 36 + .../library/socket/addrinfo/ipv6_loopback_spec.rb | 46 + .../library/socket/addrinfo/ipv6_multicast_spec.rb | 46 + spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb | 36 + .../library/socket/addrinfo/pfamily_spec.rb | 38 + .../library/socket/addrinfo/protocol_spec.rb | 38 + .../library/socket/addrinfo/shared/to_sockaddr.rb | 35 + .../library/socket/addrinfo/socktype_spec.rb | 38 + spec/rubyspec/library/socket/addrinfo/tcp_spec.rb | 20 + spec/rubyspec/library/socket/addrinfo/to_s_spec.rb | 7 + .../library/socket/addrinfo/to_sockaddr_spec.rb | 7 + spec/rubyspec/library/socket/addrinfo/udp_spec.rb | 20 + .../library/socket/addrinfo/unix_path_spec.rb | 40 + spec/rubyspec/library/socket/addrinfo/unix_spec.rb | 54 + .../library/socket/basicsocket/close_read_spec.rb | 43 + .../library/socket/basicsocket/close_write_spec.rb | 48 + .../basicsocket/do_not_reverse_lookup_spec.rb | 38 + .../library/socket/basicsocket/for_fd_spec.rb | 21 + .../library/socket/basicsocket/getpeername_spec.rb | 25 + .../library/socket/basicsocket/getsockname_spec.rb | 28 + .../library/socket/basicsocket/getsockopt_spec.rb | 46 + .../library/socket/basicsocket/ioctl_spec.rb | 43 + .../socket/basicsocket/recv_nonblock_spec.rb | 7 + .../library/socket/basicsocket/recv_spec.rb | 93 + .../library/socket/basicsocket/send_spec.rb | 84 + .../library/socket/basicsocket/setsockopt_spec.rb | 213 + .../library/socket/basicsocket/shutdown_spec.rb | 6 + .../library/socket/constants/constants_spec.rb | 90 + spec/rubyspec/library/socket/fixtures/classes.rb | 106 + spec/rubyspec/library/socket/fixtures/send_io.txt | 1 + spec/rubyspec/library/socket/ipsocket/addr_spec.rb | 42 + .../library/socket/ipsocket/getaddress_spec.rb | 27 + .../library/socket/ipsocket/peeraddr_spec.rb | 48 + .../library/socket/ipsocket/recvfrom_spec.rb | 65 + spec/rubyspec/library/socket/option/bool_spec.rb | 25 + .../rubyspec/library/socket/option/inspect_spec.rb | 20 + spec/rubyspec/library/socket/option/int_spec.rb | 28 + spec/rubyspec/library/socket/option/linger_spec.rb | 62 + spec/rubyspec/library/socket/option/new_spec.rb | 35 + .../library/socket/shared/pack_sockaddr.rb | 50 + .../socket/shared/partially_closable_sockets.rb | 13 + .../library/socket/shared/recv_nonblock.rb | 54 + spec/rubyspec/library/socket/shared/socketpair.rb | 23 + .../library/socket/socket/accept_nonblock_spec.rb | 37 + spec/rubyspec/library/socket/socket/accept_spec.rb | 2 + spec/rubyspec/library/socket/socket/bind_spec.rb | 81 + .../library/socket/socket/connect_nonblock_spec.rb | 67 + .../rubyspec/library/socket/socket/connect_spec.rb | 2 + spec/rubyspec/library/socket/socket/for_fd_spec.rb | 30 + .../library/socket/socket/getaddrinfo_spec.rb | 112 + .../library/socket/socket/gethostbyaddr_spec.rb | 2 + .../library/socket/socket/gethostbyname_spec.rb | 17 + .../library/socket/socket/gethostname_spec.rb | 8 + .../library/socket/socket/getnameinfo_spec.rb | 66 + .../library/socket/socket/getservbyname_spec.rb | 24 + spec/rubyspec/library/socket/socket/listen_spec.rb | 22 + spec/rubyspec/library/socket/socket/new_spec.rb | 2 + .../library/socket/socket/pack_sockaddr_in_spec.rb | 7 + .../library/socket/socket/pack_sockaddr_un_spec.rb | 7 + spec/rubyspec/library/socket/socket/pair_spec.rb | 7 + .../socket/socket/recvfrom_nonblock_spec.rb | 2 + .../library/socket/socket/recvfrom_spec.rb | 2 + .../library/socket/socket/sockaddr_in_spec.rb | 7 + .../library/socket/socket/sockaddr_un_spec.rb | 7 + spec/rubyspec/library/socket/socket/socket_spec.rb | 38 + .../library/socket/socket/socketpair_spec.rb | 7 + .../library/socket/socket/sysaccept_spec.rb | 2 + .../socket/socket/unpack_sockaddr_in_spec.rb | 29 + .../socket/socket/unpack_sockaddr_un_spec.rb | 26 + .../socket/tcpserver/accept_nonblock_spec.rb | 49 + .../library/socket/tcpserver/accept_spec.rb | 66 + .../rubyspec/library/socket/tcpserver/gets_spec.rb | 16 + .../library/socket/tcpserver/listen_spec.rb | 18 + spec/rubyspec/library/socket/tcpserver/new_spec.rb | 97 + .../library/socket/tcpserver/output_spec.rb | 9 + .../library/socket/tcpserver/readpartial_spec.rb | 9 + .../library/socket/tcpserver/sysaccept_spec.rb | 31 + .../library/socket/tcpsocket/gethostbyname_spec.rb | 51 + spec/rubyspec/library/socket/tcpsocket/new_spec.rb | 5 + .../rubyspec/library/socket/tcpsocket/open_spec.rb | 5 + .../socket/tcpsocket/partially_closable_spec.rb | 22 + .../library/socket/tcpsocket/recv_nonblock_spec.rb | 36 + .../library/socket/tcpsocket/setsockopt_spec.rb | 48 + .../library/socket/tcpsocket/shared/new.rb | 71 + .../rubyspec/library/socket/udpsocket/bind_spec.rb | 35 + .../library/socket/udpsocket/connect_spec.rb | 2 + spec/rubyspec/library/socket/udpsocket/new_spec.rb | 32 + .../rubyspec/library/socket/udpsocket/open_spec.rb | 13 + .../socket/udpsocket/recvfrom_nonblock_spec.rb | 2 + .../rubyspec/library/socket/udpsocket/send_spec.rb | 58 + .../socket/unixserver/accept_nonblock_spec.rb | 40 + .../library/socket/unixserver/accept_spec.rb | 66 + .../library/socket/unixserver/for_fd_spec.rb | 23 + .../rubyspec/library/socket/unixserver/new_spec.rb | 6 + .../library/socket/unixserver/open_spec.rb | 26 + .../library/socket/unixserver/shared/new.rb | 24 + .../library/socket/unixsocket/addr_spec.rb | 38 + .../library/socket/unixsocket/inspect_spec.rb | 17 + .../rubyspec/library/socket/unixsocket/new_spec.rb | 6 + .../library/socket/unixsocket/open_spec.rb | 27 + .../library/socket/unixsocket/pair_spec.rb | 39 + .../socket/unixsocket/partially_closable_spec.rb | 26 + .../library/socket/unixsocket/path_spec.rb | 30 + .../library/socket/unixsocket/peeraddr_spec.rb | 30 + .../library/socket/unixsocket/recv_io_spec.rb | 44 + .../library/socket/unixsocket/recvfrom_spec.rb | 49 + .../library/socket/unixsocket/send_io_spec.rb | 35 + .../library/socket/unixsocket/shared/new.rb | 24 + spec/rubyspec/library/stringio/append_spec.rb | 84 + spec/rubyspec/library/stringio/binmode_spec.rb | 9 + spec/rubyspec/library/stringio/bytes_spec.rb | 11 + spec/rubyspec/library/stringio/chars_spec.rb | 11 + spec/rubyspec/library/stringio/close_read_spec.rb | 36 + spec/rubyspec/library/stringio/close_spec.rb | 32 + spec/rubyspec/library/stringio/close_write_spec.rb | 36 + spec/rubyspec/library/stringio/closed_read_spec.rb | 12 + spec/rubyspec/library/stringio/closed_spec.rb | 16 + .../rubyspec/library/stringio/closed_write_spec.rb | 12 + spec/rubyspec/library/stringio/codepoints_spec.rb | 9 + spec/rubyspec/library/stringio/each_byte_spec.rb | 11 + spec/rubyspec/library/stringio/each_char_spec.rb | 11 + .../library/stringio/each_codepoint_spec.rb | 10 + spec/rubyspec/library/stringio/each_line_spec.rb | 15 + spec/rubyspec/library/stringio/each_spec.rb | 15 + spec/rubyspec/library/stringio/eof_spec.rb | 11 + .../library/stringio/external_encoding_spec.rb | 21 + spec/rubyspec/library/stringio/fcntl_spec.rb | 8 + spec/rubyspec/library/stringio/fileno_spec.rb | 9 + spec/rubyspec/library/stringio/fixtures/classes.rb | 15 + spec/rubyspec/library/stringio/flush_spec.rb | 9 + spec/rubyspec/library/stringio/fsync_spec.rb | 9 + spec/rubyspec/library/stringio/getbyte_spec.rb | 19 + spec/rubyspec/library/stringio/getc_spec.rb | 19 + spec/rubyspec/library/stringio/getch_spec.rb | 46 + spec/rubyspec/library/stringio/gets_spec.rb | 238 + spec/rubyspec/library/stringio/initialize_spec.rb | 185 + .../library/stringio/internal_encoding_spec.rb | 10 + spec/rubyspec/library/stringio/isatty_spec.rb | 7 + spec/rubyspec/library/stringio/length_spec.rb | 7 + spec/rubyspec/library/stringio/lineno_spec.rb | 30 + spec/rubyspec/library/stringio/lines_spec.rb | 15 + spec/rubyspec/library/stringio/open_spec.rb | 208 + spec/rubyspec/library/stringio/path_spec.rb | 8 + spec/rubyspec/library/stringio/pid_spec.rb | 8 + spec/rubyspec/library/stringio/pos_spec.rb | 28 + spec/rubyspec/library/stringio/print_spec.rb | 100 + spec/rubyspec/library/stringio/printf_spec.rb | 61 + spec/rubyspec/library/stringio/putc_spec.rb | 88 + spec/rubyspec/library/stringio/puts_spec.rb | 159 + .../library/stringio/read_nonblock_spec.rb | 20 + spec/rubyspec/library/stringio/read_spec.rb | 62 + spec/rubyspec/library/stringio/readbyte_spec.rb | 20 + spec/rubyspec/library/stringio/readchar_spec.rb | 20 + spec/rubyspec/library/stringio/readline_spec.rb | 122 + spec/rubyspec/library/stringio/readlines_spec.rb | 92 + spec/rubyspec/library/stringio/readpartial_spec.rb | 80 + spec/rubyspec/library/stringio/reopen_spec.rb | 288 + spec/rubyspec/library/stringio/rewind_spec.rb | 24 + spec/rubyspec/library/stringio/seek_spec.rb | 67 + .../rubyspec/library/stringio/set_encoding_spec.rb | 10 + .../rubyspec/library/stringio/shared/codepoints.rb | 45 + spec/rubyspec/library/stringio/shared/each.rb | 105 + spec/rubyspec/library/stringio/shared/each_byte.rb | 48 + spec/rubyspec/library/stringio/shared/each_char.rb | 36 + spec/rubyspec/library/stringio/shared/eof.rb | 24 + spec/rubyspec/library/stringio/shared/getc.rb | 43 + spec/rubyspec/library/stringio/shared/isatty.rb | 5 + spec/rubyspec/library/stringio/shared/length.rb | 5 + spec/rubyspec/library/stringio/shared/read.rb | 121 + spec/rubyspec/library/stringio/shared/readchar.rb | 29 + spec/rubyspec/library/stringio/shared/sysread.rb | 15 + spec/rubyspec/library/stringio/shared/tell.rb | 12 + spec/rubyspec/library/stringio/shared/write.rb | 87 + spec/rubyspec/library/stringio/size_spec.rb | 7 + spec/rubyspec/library/stringio/string_spec.rb | 50 + spec/rubyspec/library/stringio/stringio_spec.rb | 9 + spec/rubyspec/library/stringio/sync_spec.rb | 19 + spec/rubyspec/library/stringio/sysread_spec.rb | 48 + spec/rubyspec/library/stringio/syswrite_spec.rb | 19 + spec/rubyspec/library/stringio/tell_spec.rb | 7 + spec/rubyspec/library/stringio/truncate_spec.rb | 70 + spec/rubyspec/library/stringio/tty_spec.rb | 7 + spec/rubyspec/library/stringio/ungetbyte_spec.rb | 6 + spec/rubyspec/library/stringio/ungetc_spec.rb | 72 + .../library/stringio/write_nonblock_spec.rb | 19 + spec/rubyspec/library/stringio/write_spec.rb | 19 + spec/rubyspec/library/stringscanner/append_spec.rb | 11 + .../stringscanner/beginning_of_line_spec.rb | 7 + spec/rubyspec/library/stringscanner/bol_spec.rb | 7 + spec/rubyspec/library/stringscanner/check_spec.rb | 16 + .../library/stringscanner/check_until_spec.rb | 15 + spec/rubyspec/library/stringscanner/clear_spec.rb | 20 + spec/rubyspec/library/stringscanner/concat_spec.rb | 11 + spec/rubyspec/library/stringscanner/dup_spec.rb | 39 + .../stringscanner/element_reference_spec.rb | 61 + spec/rubyspec/library/stringscanner/empty_spec.rb | 20 + spec/rubyspec/library/stringscanner/eos_spec.rb | 7 + spec/rubyspec/library/stringscanner/exist_spec.rb | 24 + .../library/stringscanner/get_byte_spec.rb | 7 + .../rubyspec/library/stringscanner/getbyte_spec.rb | 23 + spec/rubyspec/library/stringscanner/getch_spec.rb | 35 + .../library/stringscanner/initialize_spec.rb | 28 + .../rubyspec/library/stringscanner/inspect_spec.rb | 20 + spec/rubyspec/library/stringscanner/match_spec.rb | 28 + .../library/stringscanner/matched_size_spec.rb | 7 + .../rubyspec/library/stringscanner/matched_spec.rb | 41 + .../library/stringscanner/must_C_version_spec.rb | 8 + spec/rubyspec/library/stringscanner/peek_spec.rb | 8 + spec/rubyspec/library/stringscanner/peep_spec.rb | 20 + .../rubyspec/library/stringscanner/pointer_spec.rb | 11 + spec/rubyspec/library/stringscanner/pos_spec.rb | 11 + .../library/stringscanner/post_match_spec.rb | 28 + .../library/stringscanner/pre_match_spec.rb | 41 + spec/rubyspec/library/stringscanner/reset_spec.rb | 15 + .../library/stringscanner/rest_size_spec.rb | 7 + spec/rubyspec/library/stringscanner/rest_spec.rb | 48 + .../library/stringscanner/restsize_spec.rb | 20 + .../library/stringscanner/scan_full_spec.rb | 30 + spec/rubyspec/library/stringscanner/scan_spec.rb | 43 + .../library/stringscanner/scan_until_spec.rb | 23 + .../library/stringscanner/search_full_spec.rb | 30 + spec/rubyspec/library/stringscanner/shared/bol.rb | 25 + .../library/stringscanner/shared/concat.rb | 30 + spec/rubyspec/library/stringscanner/shared/eos.rb | 17 + .../library/stringscanner/shared/extract_range.rb | 22 + .../stringscanner/shared/extract_range_matched.rb | 22 + .../library/stringscanner/shared/get_byte.rb | 29 + .../library/stringscanner/shared/matched_size.rb | 21 + spec/rubyspec/library/stringscanner/shared/peek.rb | 44 + spec/rubyspec/library/stringscanner/shared/pos.rb | 52 + .../library/stringscanner/shared/rest_size.rb | 18 + .../library/stringscanner/shared/terminate.rb | 8 + spec/rubyspec/library/stringscanner/skip_spec.rb | 18 + .../library/stringscanner/skip_until_spec.rb | 18 + spec/rubyspec/library/stringscanner/string_spec.rb | 40 + .../library/stringscanner/terminate_spec.rb | 7 + spec/rubyspec/library/stringscanner/unscan_spec.rb | 28 + spec/rubyspec/library/syslog/alert_spec.rb | 9 + spec/rubyspec/library/syslog/close_spec.rb | 57 + spec/rubyspec/library/syslog/constants_spec.rb | 40 + spec/rubyspec/library/syslog/crit_spec.rb | 9 + spec/rubyspec/library/syslog/debug_spec.rb | 9 + spec/rubyspec/library/syslog/emerg_spec.rb | 15 + spec/rubyspec/library/syslog/err_spec.rb | 9 + spec/rubyspec/library/syslog/facility_spec.rb | 47 + spec/rubyspec/library/syslog/ident_spec.rb | 34 + spec/rubyspec/library/syslog/info_spec.rb | 9 + spec/rubyspec/library/syslog/inspect_spec.rb | 38 + spec/rubyspec/library/syslog/instance_spec.rb | 12 + spec/rubyspec/library/syslog/log_spec.rb | 55 + spec/rubyspec/library/syslog/mask_spec.rb | 112 + spec/rubyspec/library/syslog/notice_spec.rb | 9 + spec/rubyspec/library/syslog/open_spec.rb | 86 + spec/rubyspec/library/syslog/opened_spec.rb | 38 + spec/rubyspec/library/syslog/options_spec.rb | 47 + spec/rubyspec/library/syslog/reopen_spec.rb | 9 + spec/rubyspec/library/syslog/shared/log.rb | 40 + spec/rubyspec/library/syslog/shared/reopen.rb | 40 + spec/rubyspec/library/syslog/warning_spec.rb | 9 + spec/rubyspec/library/tempfile/_close_spec.rb | 21 + spec/rubyspec/library/tempfile/callback_spec.rb | 6 + spec/rubyspec/library/tempfile/close_spec.rb | 57 + spec/rubyspec/library/tempfile/delete_spec.rb | 7 + spec/rubyspec/library/tempfile/initialize_spec.rb | 41 + spec/rubyspec/library/tempfile/length_spec.rb | 7 + spec/rubyspec/library/tempfile/open_spec.rb | 82 + spec/rubyspec/library/tempfile/path_spec.rb | 26 + spec/rubyspec/library/tempfile/shared/length.rb | 21 + spec/rubyspec/library/tempfile/shared/unlink.rb | 12 + spec/rubyspec/library/tempfile/size_spec.rb | 7 + spec/rubyspec/library/tempfile/unlink_spec.rb | 7 + spec/rubyspec/library/thread/exclusive_spec.rb | 12 + spec/rubyspec/library/thread/queue/append_spec.rb | 7 + spec/rubyspec/library/thread/queue/clear_spec.rb | 9 + spec/rubyspec/library/thread/queue/close_spec.rb | 9 + spec/rubyspec/library/thread/queue/closed_spec.rb | 9 + spec/rubyspec/library/thread/queue/deq_spec.rb | 7 + spec/rubyspec/library/thread/queue/empty_spec.rb | 7 + spec/rubyspec/library/thread/queue/enq_spec.rb | 7 + spec/rubyspec/library/thread/queue/length_spec.rb | 7 + .../library/thread/queue/num_waiting_spec.rb | 7 + spec/rubyspec/library/thread/queue/pop_spec.rb | 7 + spec/rubyspec/library/thread/queue/push_spec.rb | 7 + spec/rubyspec/library/thread/queue/shift_spec.rb | 7 + spec/rubyspec/library/thread/queue/size_spec.rb | 7 + spec/rubyspec/library/thread/shared/queue/clear.rb | 10 + spec/rubyspec/library/thread/shared/queue/close.rb | 26 + .../rubyspec/library/thread/shared/queue/closed.rb | 12 + spec/rubyspec/library/thread/shared/queue/deque.rb | 37 + spec/rubyspec/library/thread/shared/queue/empty.rb | 12 + spec/rubyspec/library/thread/shared/queue/enque.rb | 10 + .../rubyspec/library/thread/shared/queue/length.rb | 9 + .../library/thread/shared/queue/num_waiting.rb | 16 + .../library/thread/sizedqueue/append_spec.rb | 12 + .../library/thread/sizedqueue/clear_spec.rb | 9 + .../library/thread/sizedqueue/close_spec.rb | 9 + .../library/thread/sizedqueue/closed_spec.rb | 9 + .../rubyspec/library/thread/sizedqueue/deq_spec.rb | 7 + .../library/thread/sizedqueue/empty_spec.rb | 7 + .../rubyspec/library/thread/sizedqueue/enq_spec.rb | 12 + .../library/thread/sizedqueue/length_spec.rb | 7 + .../rubyspec/library/thread/sizedqueue/max_spec.rb | 52 + .../rubyspec/library/thread/sizedqueue/new_spec.rb | 25 + .../library/thread/sizedqueue/num_waiting_spec.rb | 18 + .../rubyspec/library/thread/sizedqueue/pop_spec.rb | 7 + .../library/thread/sizedqueue/push_spec.rb | 12 + .../library/thread/sizedqueue/shared/enque.rb | 34 + .../library/thread/sizedqueue/shift_spec.rb | 7 + .../library/thread/sizedqueue/size_spec.rb | 7 + spec/rubyspec/library/time/httpdate_spec.rb | 21 + spec/rubyspec/library/time/iso8601_spec.rb | 7 + spec/rubyspec/library/time/rfc2822_spec.rb | 7 + spec/rubyspec/library/time/rfc822_spec.rb | 7 + spec/rubyspec/library/time/shared/rfc2822.rb | 65 + spec/rubyspec/library/time/shared/xmlschema.rb | 53 + spec/rubyspec/library/time/to_date_spec.rb | 42 + spec/rubyspec/library/time/xmlschema_spec.rb | 7 + spec/rubyspec/library/timeout/error_spec.rb | 8 + spec/rubyspec/library/timeout/timeout_spec.rb | 37 + spec/rubyspec/library/tmpdir/dir/mktmpdir_spec.rb | 117 + spec/rubyspec/library/tmpdir/dir/tmpdir_spec.rb | 10 + .../library/uri/decode_www_form_component_spec.rb | 6 + spec/rubyspec/library/uri/decode_www_form_spec.rb | 6 + .../library/uri/encode_www_form_component_spec.rb | 6 + spec/rubyspec/library/uri/encode_www_form_spec.rb | 6 + spec/rubyspec/library/uri/eql_spec.rb | 10 + spec/rubyspec/library/uri/equality_spec.rb | 46 + spec/rubyspec/library/uri/escape/decode_spec.rb | 6 + spec/rubyspec/library/uri/escape/encode_spec.rb | 6 + spec/rubyspec/library/uri/escape/escape_spec.rb | 6 + spec/rubyspec/library/uri/escape/unescape_spec.rb | 6 + spec/rubyspec/library/uri/extract_spec.rb | 86 + spec/rubyspec/library/uri/fixtures/classes.rb | 11 + .../rubyspec/library/uri/fixtures/normalization.rb | 54 + spec/rubyspec/library/uri/ftp/build_spec.rb | 6 + spec/rubyspec/library/uri/ftp/merge_spec.rb | 6 + spec/rubyspec/library/uri/ftp/new2_spec.rb | 6 + spec/rubyspec/library/uri/ftp/path_spec.rb | 26 + spec/rubyspec/library/uri/ftp/set_typecode_spec.rb | 6 + spec/rubyspec/library/uri/ftp/to_s_spec.rb | 15 + spec/rubyspec/library/uri/ftp/typecode_spec.rb | 10 + spec/rubyspec/library/uri/generic/absolute_spec.rb | 10 + spec/rubyspec/library/uri/generic/build2_spec.rb | 6 + spec/rubyspec/library/uri/generic/build_spec.rb | 6 + spec/rubyspec/library/uri/generic/coerce_spec.rb | 6 + .../library/uri/generic/component_ary_spec.rb | 6 + .../rubyspec/library/uri/generic/component_spec.rb | 10 + .../library/uri/generic/default_port_spec.rb | 10 + spec/rubyspec/library/uri/generic/eql_spec.rb | 6 + .../library/uri/generic/equal_value_spec.rb | 6 + spec/rubyspec/library/uri/generic/fragment_spec.rb | 10 + spec/rubyspec/library/uri/generic/hash_spec.rb | 6 + .../library/uri/generic/hierarchical_spec.rb | 6 + spec/rubyspec/library/uri/generic/host_spec.rb | 10 + spec/rubyspec/library/uri/generic/inspect_spec.rb | 6 + spec/rubyspec/library/uri/generic/merge_spec.rb | 10 + spec/rubyspec/library/uri/generic/minus_spec.rb | 6 + .../rubyspec/library/uri/generic/normalize_spec.rb | 10 + spec/rubyspec/library/uri/generic/opaque_spec.rb | 10 + spec/rubyspec/library/uri/generic/password_spec.rb | 10 + spec/rubyspec/library/uri/generic/path_spec.rb | 10 + spec/rubyspec/library/uri/generic/plus_spec.rb | 6 + spec/rubyspec/library/uri/generic/port_spec.rb | 10 + spec/rubyspec/library/uri/generic/query_spec.rb | 10 + spec/rubyspec/library/uri/generic/registry_spec.rb | 10 + spec/rubyspec/library/uri/generic/relative_spec.rb | 6 + .../library/uri/generic/route_from_spec.rb | 6 + spec/rubyspec/library/uri/generic/route_to_spec.rb | 6 + spec/rubyspec/library/uri/generic/scheme_spec.rb | 10 + spec/rubyspec/library/uri/generic/select_spec.rb | 6 + .../library/uri/generic/set_fragment_spec.rb | 6 + spec/rubyspec/library/uri/generic/set_host_spec.rb | 6 + .../library/uri/generic/set_opaque_spec.rb | 6 + .../library/uri/generic/set_password_spec.rb | 6 + spec/rubyspec/library/uri/generic/set_path_spec.rb | 6 + spec/rubyspec/library/uri/generic/set_port_spec.rb | 6 + .../rubyspec/library/uri/generic/set_query_spec.rb | 6 + .../library/uri/generic/set_registry_spec.rb | 6 + .../library/uri/generic/set_scheme_spec.rb | 6 + spec/rubyspec/library/uri/generic/set_user_spec.rb | 6 + .../library/uri/generic/set_userinfo_spec.rb | 6 + spec/rubyspec/library/uri/generic/to_s_spec.rb | 6 + .../library/uri/generic/use_registry_spec.rb | 6 + spec/rubyspec/library/uri/generic/user_spec.rb | 10 + spec/rubyspec/library/uri/generic/userinfo_spec.rb | 10 + spec/rubyspec/library/uri/http/build_spec.rb | 6 + spec/rubyspec/library/uri/http/request_uri_spec.rb | 16 + spec/rubyspec/library/uri/join_spec.rb | 57 + spec/rubyspec/library/uri/ldap/attributes_spec.rb | 10 + spec/rubyspec/library/uri/ldap/build_spec.rb | 6 + spec/rubyspec/library/uri/ldap/dn_spec.rb | 10 + spec/rubyspec/library/uri/ldap/extensions_spec.rb | 10 + spec/rubyspec/library/uri/ldap/filter_spec.rb | 10 + .../rubyspec/library/uri/ldap/hierarchical_spec.rb | 6 + spec/rubyspec/library/uri/ldap/scope_spec.rb | 10 + .../library/uri/ldap/set_attributes_spec.rb | 6 + spec/rubyspec/library/uri/ldap/set_dn_spec.rb | 6 + .../library/uri/ldap/set_extensions_spec.rb | 6 + spec/rubyspec/library/uri/ldap/set_filter_spec.rb | 6 + spec/rubyspec/library/uri/ldap/set_scope_spec.rb | 6 + spec/rubyspec/library/uri/mailto/build_spec.rb | 98 + spec/rubyspec/library/uri/mailto/headers_spec.rb | 10 + .../library/uri/mailto/set_headers_spec.rb | 6 + spec/rubyspec/library/uri/mailto/set_to_spec.rb | 6 + .../library/uri/mailto/to_mailtext_spec.rb | 6 + .../library/uri/mailto/to_rfc822text_spec.rb | 6 + spec/rubyspec/library/uri/mailto/to_s_spec.rb | 6 + spec/rubyspec/library/uri/mailto/to_spec.rb | 10 + spec/rubyspec/library/uri/merge_spec.rb | 20 + spec/rubyspec/library/uri/normalize_spec.rb | 35 + spec/rubyspec/library/uri/parse_spec.rb | 203 + spec/rubyspec/library/uri/parser/escape_spec.rb | 6 + spec/rubyspec/library/uri/parser/extract_spec.rb | 7 + spec/rubyspec/library/uri/parser/inspect_spec.rb | 6 + spec/rubyspec/library/uri/parser/join_spec.rb | 7 + .../library/uri/parser/make_regexp_spec.rb | 6 + spec/rubyspec/library/uri/parser/parse_spec.rb | 7 + spec/rubyspec/library/uri/parser/split_spec.rb | 6 + spec/rubyspec/library/uri/parser/unescape_spec.rb | 6 + spec/rubyspec/library/uri/plus_spec.rb | 459 + spec/rubyspec/library/uri/regexp_spec.rb | 18 + spec/rubyspec/library/uri/route_from_spec.rb | 23 + spec/rubyspec/library/uri/route_to_spec.rb | 26 + spec/rubyspec/library/uri/select_spec.rb | 31 + spec/rubyspec/library/uri/set_component_spec.rb | 47 + spec/rubyspec/library/uri/shared/eql.rb | 17 + spec/rubyspec/library/uri/shared/extract.rb | 83 + spec/rubyspec/library/uri/shared/join.rb | 54 + spec/rubyspec/library/uri/shared/parse.rb | 199 + spec/rubyspec/library/uri/split_spec.rb | 6 + spec/rubyspec/library/uri/uri_spec.rb | 29 + .../library/uri/util/make_components_hash_spec.rb | 6 + spec/rubyspec/library/weakref/__getobj___spec.rb | 17 + spec/rubyspec/library/weakref/fixtures/classes.rb | 24 + spec/rubyspec/library/weakref/send_spec.rb | 37 + .../rubyspec/library/weakref/weakref_alive_spec.rb | 15 + spec/rubyspec/library/win32ole/fixtures/classes.rb | 14 + .../library/win32ole/win32ole/_getproperty_spec.rb | 19 + .../library/win32ole/win32ole/_invoke_spec.rb | 23 + .../library/win32ole/win32ole/codepage_spec.rb | 15 + .../library/win32ole/win32ole/connect_spec.rb | 17 + .../library/win32ole/win32ole/const_load_spec.rb | 34 + .../library/win32ole/win32ole/constants_spec.rb | 44 + .../library/win32ole/win32ole/create_guid_spec.rb | 11 + .../library/win32ole/win32ole/invoke_spec.rb | 19 + .../library/win32ole/win32ole/locale_spec.rb | 31 + .../rubyspec/library/win32ole/win32ole/new_spec.rb | 27 + .../win32ole/win32ole/ole_func_methods_spec.rb | 27 + .../win32ole/win32ole/ole_get_methods_spec.rb | 17 + .../win32ole/win32ole/ole_method_help_spec.rb | 12 + .../library/win32ole/win32ole/ole_method_spec.rb | 12 + .../library/win32ole/win32ole/ole_methods_spec.rb | 27 + .../library/win32ole/win32ole/ole_obj_help_spec.rb | 23 + .../win32ole/win32ole/ole_put_methods_spec.rb | 27 + .../library/win32ole/win32ole/setproperty_spec.rb | 12 + .../library/win32ole/win32ole/shared/ole_method.rb | 25 + .../win32ole/win32ole/shared/setproperty.rb | 25 + .../library/win32ole/win32ole_event/new_spec.rb | 33 + .../win32ole/win32ole_event/on_event_spec.rb | 62 + .../win32ole/win32ole_method/dispid_spec.rb | 20 + .../win32ole_method/event_interface_spec.rb | 26 + .../library/win32ole/win32ole_method/event_spec.rb | 20 + .../win32ole/win32ole_method/helpcontext_spec.rb | 26 + .../win32ole/win32ole_method/helpfile_spec.rb | 20 + .../win32ole/win32ole_method/helpstring_spec.rb | 20 + .../win32ole/win32ole_method/invkind_spec.rb | 20 + .../win32ole/win32ole_method/invoke_kind_spec.rb | 20 + .../library/win32ole/win32ole_method/name_spec.rb | 11 + .../library/win32ole/win32ole_method/new_spec.rb | 33 + .../win32ole/win32ole_method/offset_vtbl_spec.rb | 21 + .../win32ole/win32ole_method/params_spec.rb | 28 + .../win32ole_method/return_type_detail_spec.rb | 21 + .../win32ole/win32ole_method/return_type_spec.rb | 20 + .../win32ole/win32ole_method/return_vtype_spec.rb | 20 + .../win32ole/win32ole_method/shared/name.rb | 20 + .../win32ole_method/size_opt_params_spec.rb | 20 + .../win32ole/win32ole_method/size_params_spec.rb | 20 + .../library/win32ole/win32ole_method/to_s_spec.rb | 11 + .../win32ole/win32ole_method/visible_spec.rb | 20 + .../win32ole/win32ole_param/default_spec.rb | 31 + .../library/win32ole/win32ole_param/input_spec.rb | 21 + .../library/win32ole/win32ole_param/name_spec.rb | 11 + .../win32ole_param/ole_type_detail_spec.rb | 21 + .../win32ole/win32ole_param/ole_type_spec.rb | 21 + .../win32ole/win32ole_param/optional_spec.rb | 21 + .../library/win32ole/win32ole_param/retval_spec.rb | 21 + .../library/win32ole/win32ole_param/shared/name.rb | 21 + .../library/win32ole/win32ole_param/to_s_spec.rb | 11 + .../library/win32ole/win32ole_type/guid_spec.rb | 18 + .../win32ole/win32ole_type/helpcontext_spec.rb | 18 + .../win32ole/win32ole_type/helpfile_spec.rb | 18 + .../win32ole/win32ole_type/helpstring_spec.rb | 18 + .../win32ole/win32ole_type/major_version_spec.rb | 18 + .../win32ole/win32ole_type/minor_version_spec.rb | 18 + .../library/win32ole/win32ole_type/name_spec.rb | 11 + .../library/win32ole/win32ole_type/new_spec.rb | 37 + .../win32ole/win32ole_type/ole_classes_spec.rb | 18 + .../win32ole/win32ole_type/ole_methods_spec.rb | 18 + .../win32ole/win32ole_type/ole_type_spec.rb | 18 + .../library/win32ole/win32ole_type/progid_spec.rb | 18 + .../library/win32ole/win32ole_type/progids_spec.rb | 14 + .../library/win32ole/win32ole_type/shared/name.rb | 19 + .../win32ole/win32ole_type/src_type_spec.rb | 18 + .../library/win32ole/win32ole_type/to_s_spec.rb | 11 + .../win32ole/win32ole_type/typekind_spec.rb | 18 + .../win32ole/win32ole_type/typelibs_spec.rb | 22 + .../win32ole/win32ole_type/variables_spec.rb | 18 + .../library/win32ole/win32ole_type/visible_spec.rb | 18 + .../win32ole/win32ole_variable/name_spec.rb | 11 + .../win32ole_variable/ole_type_detail_spec.rb | 19 + .../win32ole/win32ole_variable/ole_type_spec.rb | 18 + .../win32ole/win32ole_variable/shared/name.rb | 18 + .../win32ole/win32ole_variable/to_s_spec.rb | 11 + .../win32ole/win32ole_variable/value_spec.rb | 19 + .../win32ole_variable/variable_kind_spec.rb | 19 + .../win32ole/win32ole_variable/varkind_spec.rb | 19 + .../win32ole/win32ole_variable/visible_spec.rb | 18 + .../rubyspec/library/yaml/add_builtin_type_spec.rb | 2 + spec/rubyspec/library/yaml/add_domain_type_spec.rb | 2 + .../rubyspec/library/yaml/add_private_type_spec.rb | 2 + spec/rubyspec/library/yaml/add_ruby_type_spec.rb | 2 + spec/rubyspec/library/yaml/detect_implicit_spec.rb | 2 + spec/rubyspec/library/yaml/dump_spec.rb | 47 + spec/rubyspec/library/yaml/dump_stream_spec.rb | 8 + spec/rubyspec/library/yaml/each_node_spec.rb | 2 + spec/rubyspec/library/yaml/emitter_spec.rb | 2 + spec/rubyspec/library/yaml/fixtures/common.rb | 10 + .../library/yaml/fixtures/example_class.rb | 5 + spec/rubyspec/library/yaml/fixtures/strings.rb | 36 + spec/rubyspec/library/yaml/fixtures/test_yaml.yml | 2 + spec/rubyspec/library/yaml/generic_parser_spec.rb | 2 + spec/rubyspec/library/yaml/load_documents_spec.rb | 10 + spec/rubyspec/library/yaml/load_file_spec.rb | 13 + spec/rubyspec/library/yaml/load_spec.rb | 116 + spec/rubyspec/library/yaml/load_stream_spec.rb | 8 + spec/rubyspec/library/yaml/object_maker_spec.rb | 2 + spec/rubyspec/library/yaml/parse_documents_spec.rb | 2 + spec/rubyspec/library/yaml/parse_file_spec.rb | 10 + spec/rubyspec/library/yaml/parse_spec.rb | 22 + spec/rubyspec/library/yaml/parser_spec.rb | 2 + spec/rubyspec/library/yaml/quick_emit_spec.rb | 2 + spec/rubyspec/library/yaml/read_type_class_spec.rb | 2 + spec/rubyspec/library/yaml/shared/each_document.rb | 18 + spec/rubyspec/library/yaml/tagurize_spec.rb | 11 + spec/rubyspec/library/yaml/to_yaml_spec.rb | 99 + spec/rubyspec/library/yaml/transfer_spec.rb | 2 + spec/rubyspec/library/yaml/try_implicit_spec.rb | 2 + spec/rubyspec/library/zlib/adler32_spec.rb | 46 + spec/rubyspec/library/zlib/crc32_spec.rb | 52 + spec/rubyspec/library/zlib/crc_table_spec.rb | 11 + spec/rubyspec/library/zlib/deflate/append_spec.rb | 1 + spec/rubyspec/library/zlib/deflate/deflate_spec.rb | 128 + spec/rubyspec/library/zlib/deflate/flush_spec.rb | 1 + spec/rubyspec/library/zlib/deflate/new_spec.rb | 1 + spec/rubyspec/library/zlib/deflate/params_spec.rb | 18 + .../library/zlib/deflate/set_dictionary_spec.rb | 15 + spec/rubyspec/library/zlib/gzipfile/close_spec.rb | 22 + spec/rubyspec/library/zlib/gzipfile/closed_spec.rb | 17 + .../rubyspec/library/zlib/gzipfile/comment_spec.rb | 27 + spec/rubyspec/library/zlib/gzipfile/crc_spec.rb | 1 + spec/rubyspec/library/zlib/gzipfile/finish_spec.rb | 1 + spec/rubyspec/library/zlib/gzipfile/level_spec.rb | 1 + spec/rubyspec/library/zlib/gzipfile/mtime_spec.rb | 1 + .../library/zlib/gzipfile/orig_name_spec.rb | 27 + .../rubyspec/library/zlib/gzipfile/os_code_spec.rb | 1 + spec/rubyspec/library/zlib/gzipfile/sync_spec.rb | 1 + spec/rubyspec/library/zlib/gzipfile/to_io_spec.rb | 1 + spec/rubyspec/library/zlib/gzipfile/wrap_spec.rb | 1 + .../library/zlib/gzipreader/each_byte_spec.rb | 51 + .../library/zlib/gzipreader/each_line_spec.rb | 5 + spec/rubyspec/library/zlib/gzipreader/each_spec.rb | 5 + spec/rubyspec/library/zlib/gzipreader/eof_spec.rb | 56 + spec/rubyspec/library/zlib/gzipreader/getc_spec.rb | 41 + spec/rubyspec/library/zlib/gzipreader/gets_spec.rb | 1 + .../library/zlib/gzipreader/lineno_spec.rb | 1 + spec/rubyspec/library/zlib/gzipreader/new_spec.rb | 1 + spec/rubyspec/library/zlib/gzipreader/open_spec.rb | 1 + spec/rubyspec/library/zlib/gzipreader/pos_spec.rb | 27 + spec/rubyspec/library/zlib/gzipreader/read_spec.rb | 68 + .../library/zlib/gzipreader/readchar_spec.rb | 1 + .../library/zlib/gzipreader/readline_spec.rb | 1 + .../library/zlib/gzipreader/readlines_spec.rb | 1 + .../library/zlib/gzipreader/rewind_spec.rb | 48 + .../library/zlib/gzipreader/shared/each.rb | 51 + spec/rubyspec/library/zlib/gzipreader/tell_spec.rb | 1 + .../library/zlib/gzipreader/ungetc_spec.rb | 1 + .../library/zlib/gzipreader/unused_spec.rb | 1 + .../library/zlib/gzipwriter/append_spec.rb | 17 + .../library/zlib/gzipwriter/comment_spec.rb | 1 + .../rubyspec/library/zlib/gzipwriter/flush_spec.rb | 1 + .../rubyspec/library/zlib/gzipwriter/mtime_spec.rb | 39 + spec/rubyspec/library/zlib/gzipwriter/new_spec.rb | 1 + spec/rubyspec/library/zlib/gzipwriter/open_spec.rb | 1 + .../library/zlib/gzipwriter/orig_name_spec.rb | 1 + spec/rubyspec/library/zlib/gzipwriter/pos_spec.rb | 1 + .../rubyspec/library/zlib/gzipwriter/print_spec.rb | 1 + .../library/zlib/gzipwriter/printf_spec.rb | 1 + spec/rubyspec/library/zlib/gzipwriter/putc_spec.rb | 1 + spec/rubyspec/library/zlib/gzipwriter/puts_spec.rb | 1 + spec/rubyspec/library/zlib/gzipwriter/tell_spec.rb | 1 + .../rubyspec/library/zlib/gzipwriter/write_spec.rb | 36 + spec/rubyspec/library/zlib/inflate/append_spec.rb | 60 + spec/rubyspec/library/zlib/inflate/finish_spec.rb | 28 + spec/rubyspec/library/zlib/inflate/inflate_spec.rb | 152 + spec/rubyspec/library/zlib/inflate/new_spec.rb | 1 + .../library/zlib/inflate/set_dictionary_spec.rb | 21 + .../library/zlib/inflate/sync_point_spec.rb | 1 + spec/rubyspec/library/zlib/inflate/sync_spec.rb | 1 + spec/rubyspec/library/zlib/zlib_version_spec.rb | 1 + spec/rubyspec/library/zlib/zstream/adler_spec.rb | 11 + .../rubyspec/library/zlib/zstream/avail_in_spec.rb | 9 + .../library/zlib/zstream/avail_out_spec.rb | 9 + spec/rubyspec/library/zlib/zstream/close_spec.rb | 1 + spec/rubyspec/library/zlib/zstream/closed_spec.rb | 1 + .../library/zlib/zstream/data_type_spec.rb | 9 + spec/rubyspec/library/zlib/zstream/end_spec.rb | 1 + spec/rubyspec/library/zlib/zstream/ended_spec.rb | 1 + spec/rubyspec/library/zlib/zstream/finish_spec.rb | 1 + .../rubyspec/library/zlib/zstream/finished_spec.rb | 1 + .../library/zlib/zstream/flush_next_in_spec.rb | 1 + .../library/zlib/zstream/flush_next_out_spec.rb | 16 + spec/rubyspec/library/zlib/zstream/reset_spec.rb | 1 + .../library/zlib/zstream/stream_end_spec.rb | 1 + .../rubyspec/library/zlib/zstream/total_in_spec.rb | 1 + .../library/zlib/zstream/total_out_spec.rb | 1 + spec/rubyspec/optional/capi/README | 16 + spec/rubyspec/optional/capi/array_spec.rb | 463 + spec/rubyspec/optional/capi/bignum_spec.rb | 219 + spec/rubyspec/optional/capi/class_spec.rb | 388 + spec/rubyspec/optional/capi/complex_spec.rb | 45 + spec/rubyspec/optional/capi/constants_spec.rb | 268 + spec/rubyspec/optional/capi/data_spec.rb | 46 + spec/rubyspec/optional/capi/encoding_spec.rb | 485 + spec/rubyspec/optional/capi/enumerator_spec.rb | 39 + spec/rubyspec/optional/capi/exception_spec.rb | 58 + spec/rubyspec/optional/capi/ext/.gitignore | 9 + spec/rubyspec/optional/capi/ext/array_spec.c | 452 + spec/rubyspec/optional/capi/ext/bignum_spec.c | 149 + spec/rubyspec/optional/capi/ext/boolean_spec.c | 34 + .../capi/ext/class_id_under_autoload_spec.c | 5 + spec/rubyspec/optional/capi/ext/class_spec.c | 261 + .../optional/capi/ext/class_under_autoload_spec.c | 5 + spec/rubyspec/optional/capi/ext/complex_spec.c | 76 + spec/rubyspec/optional/capi/ext/constants_spec.c | 646 + spec/rubyspec/optional/capi/ext/data_spec.c | 97 + spec/rubyspec/optional/capi/ext/encoding_spec.c | 424 + spec/rubyspec/optional/capi/ext/enumerator_spec.c | 27 + spec/rubyspec/optional/capi/ext/exception_spec.c | 72 + spec/rubyspec/optional/capi/ext/file_spec.c | 44 + spec/rubyspec/optional/capi/ext/fixnum_spec.c | 36 + spec/rubyspec/optional/capi/ext/float_spec.c | 54 + spec/rubyspec/optional/capi/ext/gc_spec.c | 61 + spec/rubyspec/optional/capi/ext/globals_spec.c | 199 + spec/rubyspec/optional/capi/ext/hash_spec.c | 208 + spec/rubyspec/optional/capi/ext/integer_spec.c | 40 + spec/rubyspec/optional/capi/ext/io_spec.c | 296 + spec/rubyspec/optional/capi/ext/jruby.h | 10 + spec/rubyspec/optional/capi/ext/kernel_spec.c | 404 + spec/rubyspec/optional/capi/ext/marshal_spec.c | 36 + spec/rubyspec/optional/capi/ext/module_spec.c | 252 + .../optional/capi/ext/module_under_autoload_spec.c | 7 + spec/rubyspec/optional/capi/ext/mutex_spec.c | 91 + spec/rubyspec/optional/capi/ext/numeric_spec.c | 166 + spec/rubyspec/optional/capi/ext/object_spec.c | 608 + spec/rubyspec/optional/capi/ext/proc_spec.c | 65 + spec/rubyspec/optional/capi/ext/range_spec.c | 47 + spec/rubyspec/optional/capi/ext/rational_spec.c | 95 + spec/rubyspec/optional/capi/ext/regexp_spec.c | 84 + spec/rubyspec/optional/capi/ext/rubinius.h | 8 + spec/rubyspec/optional/capi/ext/rubyspec.h | 612 + spec/rubyspec/optional/capi/ext/string_spec.c | 664 + spec/rubyspec/optional/capi/ext/struct_spec.c | 131 + spec/rubyspec/optional/capi/ext/symbol_spec.c | 138 + spec/rubyspec/optional/capi/ext/thread_spec.c | 181 + spec/rubyspec/optional/capi/ext/time_spec.c | 127 + spec/rubyspec/optional/capi/ext/truffleruby.h | 6 + spec/rubyspec/optional/capi/ext/typed_data_spec.c | 177 + spec/rubyspec/optional/capi/ext/util_spec.c | 95 + spec/rubyspec/optional/capi/false_spec.rb | 21 + spec/rubyspec/optional/capi/file_spec.rb | 89 + spec/rubyspec/optional/capi/fixnum_spec.rb | 74 + spec/rubyspec/optional/capi/fixtures/class.rb | 82 + spec/rubyspec/optional/capi/fixtures/const_get.rb | 5 + .../optional/capi/fixtures/const_get_at.rb | 5 + .../optional/capi/fixtures/const_get_from.rb | 5 + .../optional/capi/fixtures/const_get_object.rb | 3 + spec/rubyspec/optional/capi/fixtures/encoding.rb | 3 + spec/rubyspec/optional/capi/fixtures/foo.rb | 1 + spec/rubyspec/optional/capi/fixtures/module.rb | 35 + .../optional/capi/fixtures/module_autoload.rb | 4 + .../optional/capi/fixtures/path_to_class.rb | 6 + spec/rubyspec/optional/capi/fixtures/proc.rb | 20 + spec/rubyspec/optional/capi/float_spec.rb | 30 + spec/rubyspec/optional/capi/gc_spec.rb | 44 + spec/rubyspec/optional/capi/globals_spec.rb | 224 + spec/rubyspec/optional/capi/hash_spec.rb | 226 + spec/rubyspec/optional/capi/integer_spec.rb | 275 + spec/rubyspec/optional/capi/io_spec.rb | 342 + spec/rubyspec/optional/capi/kernel_spec.rb | 476 + spec/rubyspec/optional/capi/marshal_spec.rb | 46 + spec/rubyspec/optional/capi/module_spec.rb | 340 + spec/rubyspec/optional/capi/mutex_spec.rb | 88 + spec/rubyspec/optional/capi/numeric_spec.rb | 432 + spec/rubyspec/optional/capi/object_spec.rb | 803 + spec/rubyspec/optional/capi/proc_spec.rb | 98 + spec/rubyspec/optional/capi/rake_helper.rb | 23 + spec/rubyspec/optional/capi/range_spec.rb | 68 + spec/rubyspec/optional/capi/rational_spec.rb | 57 + spec/rubyspec/optional/capi/regexp_spec.rb | 71 + spec/rubyspec/optional/capi/spec_helper.rb | 160 + spec/rubyspec/optional/capi/string_spec.rb | 771 + spec/rubyspec/optional/capi/struct_spec.rb | 209 + spec/rubyspec/optional/capi/symbol_spec.rb | 133 + spec/rubyspec/optional/capi/thread_spec.rb | 120 + spec/rubyspec/optional/capi/time_spec.rb | 296 + spec/rubyspec/optional/capi/true_spec.rb | 21 + spec/rubyspec/optional/capi/typed_data_spec.rb | 56 + spec/rubyspec/optional/capi/util_spec.rb | 191 + spec/rubyspec/security/cve_2011_4815_spec.rb | 41 + spec/rubyspec/security/cve_2013_4164_spec.rb | 19 + spec/rubyspec/security/cve_2014_8080_spec.rb | 32 + spec/rubyspec/shared/basicobject/method_missing.rb | 126 + spec/rubyspec/shared/basicobject/send.rb | 110 + spec/rubyspec/shared/complex/Complex.rb | 133 + spec/rubyspec/shared/complex/abs.rb | 12 + spec/rubyspec/shared/complex/abs2.rb | 12 + spec/rubyspec/shared/complex/arg.rb | 9 + spec/rubyspec/shared/complex/coerce.rb | 70 + spec/rubyspec/shared/complex/conjugate.rb | 8 + spec/rubyspec/shared/complex/constants.rb | 7 + spec/rubyspec/shared/complex/denominator.rb | 13 + spec/rubyspec/shared/complex/divide.rb | 84 + spec/rubyspec/shared/complex/equal_value.rb | 93 + spec/rubyspec/shared/complex/exponent.rb | 61 + spec/rubyspec/shared/complex/float/arg.rb | 38 + spec/rubyspec/shared/complex/hash.rb | 16 + spec/rubyspec/shared/complex/image.rb | 10 + spec/rubyspec/shared/complex/inspect.rb | 14 + spec/rubyspec/shared/complex/minus.rb | 45 + spec/rubyspec/shared/complex/multiply.rb | 49 + spec/rubyspec/shared/complex/numerator.rb | 19 + spec/rubyspec/shared/complex/numeric/arg.rb | 38 + spec/rubyspec/shared/complex/numeric/conj.rb | 20 + spec/rubyspec/shared/complex/numeric/imag.rb | 26 + spec/rubyspec/shared/complex/numeric/polar.rb | 50 + spec/rubyspec/shared/complex/numeric/real.rb | 30 + spec/rubyspec/shared/complex/plus.rb | 45 + spec/rubyspec/shared/complex/polar.rb | 22 + spec/rubyspec/shared/complex/real.rb | 8 + spec/rubyspec/shared/complex/rect.rb | 96 + spec/rubyspec/shared/complex/to_s.rb | 44 + spec/rubyspec/shared/enumerator/each.rb | 89 + spec/rubyspec/shared/enumerator/enum_cons.rb | 12 + spec/rubyspec/shared/enumerator/enum_for.rb | 50 + spec/rubyspec/shared/enumerator/new.rb | 42 + spec/rubyspec/shared/enumerator/next.rb | 28 + spec/rubyspec/shared/enumerator/rewind.rb | 39 + spec/rubyspec/shared/enumerator/with_index.rb | 32 + spec/rubyspec/shared/enumerator/with_object.rb | 42 + spec/rubyspec/shared/fiber/resume.rb | 77 + spec/rubyspec/shared/file/blockdev.rb | 9 + spec/rubyspec/shared/file/chardev.rb | 9 + spec/rubyspec/shared/file/directory.rb | 66 + spec/rubyspec/shared/file/executable.rb | 48 + spec/rubyspec/shared/file/executable_real.rb | 46 + spec/rubyspec/shared/file/exist.rb | 24 + spec/rubyspec/shared/file/file.rb | 45 + spec/rubyspec/shared/file/grpowned.rb | 40 + spec/rubyspec/shared/file/identical.rb | 47 + spec/rubyspec/shared/file/owned.rb | 3 + spec/rubyspec/shared/file/pipe.rb | 3 + spec/rubyspec/shared/file/readable.rb | 30 + spec/rubyspec/shared/file/readable_real.rb | 23 + spec/rubyspec/shared/file/setgid.rb | 2 + spec/rubyspec/shared/file/setuid.rb | 2 + spec/rubyspec/shared/file/size.rb | 124 + spec/rubyspec/shared/file/socket.rb | 3 + spec/rubyspec/shared/file/sticky.rb | 29 + spec/rubyspec/shared/file/symlink.rb | 46 + spec/rubyspec/shared/file/world_readable.rb | 49 + spec/rubyspec/shared/file/world_writable.rb | 49 + spec/rubyspec/shared/file/writable.rb | 26 + spec/rubyspec/shared/file/writable_real.rb | 33 + spec/rubyspec/shared/file/zero.rb | 76 + spec/rubyspec/shared/io/putc.rb | 57 + spec/rubyspec/shared/kernel/equal.rb | 54 + spec/rubyspec/shared/kernel/object_id.rb | 80 + spec/rubyspec/shared/kernel/raise.rb | 68 + spec/rubyspec/shared/math/atanh.rb | 44 + spec/rubyspec/shared/process/abort.rb | 36 + spec/rubyspec/shared/process/exit.rb | 96 + spec/rubyspec/shared/process/fork.rb | 90 + spec/rubyspec/shared/rational/Rational.rb | 103 + spec/rubyspec/shared/rational/abs.rb | 11 + spec/rubyspec/shared/rational/ceil.rb | 45 + spec/rubyspec/shared/rational/coerce.rb | 21 + spec/rubyspec/shared/rational/comparison.rb | 85 + spec/rubyspec/shared/rational/denominator.rb | 14 + spec/rubyspec/shared/rational/div.rb | 54 + spec/rubyspec/shared/rational/divide.rb | 71 + spec/rubyspec/shared/rational/divmod.rb | 42 + spec/rubyspec/shared/rational/equal_value.rb | 39 + spec/rubyspec/shared/rational/exponent.rb | 176 + spec/rubyspec/shared/rational/fdiv.rb | 5 + spec/rubyspec/shared/rational/floor.rb | 45 + spec/rubyspec/shared/rational/hash.rb | 9 + spec/rubyspec/shared/rational/inspect.rb | 12 + spec/rubyspec/shared/rational/marshal_dump.rb | 5 + spec/rubyspec/shared/rational/marshal_load.rb | 5 + spec/rubyspec/shared/rational/minus.rb | 48 + spec/rubyspec/shared/rational/modulo.rb | 43 + spec/rubyspec/shared/rational/multiply.rb | 62 + spec/rubyspec/shared/rational/numerator.rb | 10 + spec/rubyspec/shared/rational/plus.rb | 48 + spec/rubyspec/shared/rational/quo.rb | 5 + spec/rubyspec/shared/rational/remainder.rb | 5 + spec/rubyspec/shared/rational/round.rb | 71 + spec/rubyspec/shared/rational/to_f.rb | 10 + spec/rubyspec/shared/rational/to_i.rb | 12 + spec/rubyspec/shared/rational/to_r.rb | 13 + spec/rubyspec/shared/rational/to_s.rb | 11 + spec/rubyspec/shared/rational/truncate.rb | 45 + spec/rubyspec/shared/string/times.rb | 64 + spec/rubyspec/shared/time/strftime_for_date.rb | 279 + spec/rubyspec/shared/time/strftime_for_time.rb | 173 + spec/rubyspec/spec_helper.rb | 18 + 4645 files changed, 230678 insertions(+), 4 deletions(-) create mode 100644 spec/mspec/.gitignore create mode 100644 spec/mspec/.travis.yml create mode 100644 spec/mspec/Gemfile create mode 100644 spec/mspec/Gemfile.lock create mode 100644 spec/mspec/LICENSE create mode 100644 spec/mspec/README.md create mode 100644 spec/mspec/Rakefile create mode 100755 spec/mspec/bin/mkspec create mode 100755 spec/mspec/bin/mkspec.bat create mode 100755 spec/mspec/bin/mspec create mode 100755 spec/mspec/bin/mspec-ci create mode 100755 spec/mspec/bin/mspec-ci.bat create mode 100755 spec/mspec/bin/mspec-run create mode 100755 spec/mspec/bin/mspec-run.bat create mode 100755 spec/mspec/bin/mspec-tag create mode 100755 spec/mspec/bin/mspec-tag.bat create mode 100755 spec/mspec/bin/mspec.bat create mode 100644 spec/mspec/lib/mspec.rb create mode 100755 spec/mspec/lib/mspec/commands/mkspec.rb create mode 100644 spec/mspec/lib/mspec/commands/mspec-ci.rb create mode 100644 spec/mspec/lib/mspec/commands/mspec-run.rb create mode 100644 spec/mspec/lib/mspec/commands/mspec-tag.rb create mode 100755 spec/mspec/lib/mspec/commands/mspec.rb create mode 100644 spec/mspec/lib/mspec/expectations.rb create mode 100644 spec/mspec/lib/mspec/expectations/expectations.rb create mode 100644 spec/mspec/lib/mspec/expectations/should.rb create mode 100644 spec/mspec/lib/mspec/guards.rb create mode 100644 spec/mspec/lib/mspec/guards/block_device.rb create mode 100644 spec/mspec/lib/mspec/guards/bug.rb create mode 100644 spec/mspec/lib/mspec/guards/conflict.rb create mode 100644 spec/mspec/lib/mspec/guards/endian.rb create mode 100644 spec/mspec/lib/mspec/guards/feature.rb create mode 100644 spec/mspec/lib/mspec/guards/guard.rb create mode 100644 spec/mspec/lib/mspec/guards/platform.rb create mode 100644 spec/mspec/lib/mspec/guards/quarantine.rb create mode 100644 spec/mspec/lib/mspec/guards/superuser.rb create mode 100644 spec/mspec/lib/mspec/guards/support.rb create mode 100644 spec/mspec/lib/mspec/guards/version.rb create mode 100644 spec/mspec/lib/mspec/helpers.rb create mode 100644 spec/mspec/lib/mspec/helpers/argf.rb create mode 100644 spec/mspec/lib/mspec/helpers/argv.rb create mode 100644 spec/mspec/lib/mspec/helpers/datetime.rb create mode 100644 spec/mspec/lib/mspec/helpers/fixture.rb create mode 100644 spec/mspec/lib/mspec/helpers/flunk.rb create mode 100644 spec/mspec/lib/mspec/helpers/fs.rb create mode 100644 spec/mspec/lib/mspec/helpers/io.rb create mode 100644 spec/mspec/lib/mspec/helpers/mock_to_path.rb create mode 100644 spec/mspec/lib/mspec/helpers/numeric.rb create mode 100644 spec/mspec/lib/mspec/helpers/ruby_exe.rb create mode 100644 spec/mspec/lib/mspec/helpers/scratch.rb create mode 100644 spec/mspec/lib/mspec/helpers/tmp.rb create mode 100644 spec/mspec/lib/mspec/matchers.rb create mode 100644 spec/mspec/lib/mspec/matchers/base.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_an_instance_of.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_ancestor_of.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_close.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_computed_by.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_empty.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_false.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_kind_of.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_nan.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_nil.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_true.rb create mode 100644 spec/mspec/lib/mspec/matchers/be_true_or_false.rb create mode 100644 spec/mspec/lib/mspec/matchers/block_caller.rb create mode 100644 spec/mspec/lib/mspec/matchers/complain.rb create mode 100644 spec/mspec/lib/mspec/matchers/eql.rb create mode 100644 spec/mspec/lib/mspec/matchers/equal.rb create mode 100644 spec/mspec/lib/mspec/matchers/equal_element.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_class_variable.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_constant.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_instance_method.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_instance_variable.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_method.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_private_instance_method.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_private_method.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_public_instance_method.rb create mode 100644 spec/mspec/lib/mspec/matchers/have_singleton_method.rb create mode 100644 spec/mspec/lib/mspec/matchers/include.rb create mode 100644 spec/mspec/lib/mspec/matchers/infinity.rb create mode 100644 spec/mspec/lib/mspec/matchers/match_yaml.rb create mode 100644 spec/mspec/lib/mspec/matchers/method.rb create mode 100644 spec/mspec/lib/mspec/matchers/output.rb create mode 100644 spec/mspec/lib/mspec/matchers/output_to_fd.rb create mode 100644 spec/mspec/lib/mspec/matchers/raise_error.rb create mode 100644 spec/mspec/lib/mspec/matchers/respond_to.rb create mode 100644 spec/mspec/lib/mspec/matchers/signed_zero.rb create mode 100644 spec/mspec/lib/mspec/matchers/variable.rb create mode 100644 spec/mspec/lib/mspec/mocks.rb create mode 100644 spec/mspec/lib/mspec/mocks/mock.rb create mode 100644 spec/mspec/lib/mspec/mocks/object.rb create mode 100644 spec/mspec/lib/mspec/mocks/proxy.rb create mode 100644 spec/mspec/lib/mspec/runner.rb create mode 100644 spec/mspec/lib/mspec/runner/actions.rb create mode 100644 spec/mspec/lib/mspec/runner/actions/filter.rb create mode 100644 spec/mspec/lib/mspec/runner/actions/leakchecker.rb create mode 100644 spec/mspec/lib/mspec/runner/actions/tag.rb create mode 100644 spec/mspec/lib/mspec/runner/actions/taglist.rb create mode 100644 spec/mspec/lib/mspec/runner/actions/tagpurge.rb create mode 100644 spec/mspec/lib/mspec/runner/actions/tally.rb create mode 100644 spec/mspec/lib/mspec/runner/actions/timer.rb create mode 100644 spec/mspec/lib/mspec/runner/context.rb create mode 100644 spec/mspec/lib/mspec/runner/evaluate.rb create mode 100644 spec/mspec/lib/mspec/runner/example.rb create mode 100644 spec/mspec/lib/mspec/runner/exception.rb create mode 100644 spec/mspec/lib/mspec/runner/filters.rb create mode 100644 spec/mspec/lib/mspec/runner/filters/match.rb create mode 100644 spec/mspec/lib/mspec/runner/filters/profile.rb create mode 100644 spec/mspec/lib/mspec/runner/filters/regexp.rb create mode 100644 spec/mspec/lib/mspec/runner/filters/tag.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/describe.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/dotted.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/file.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/html.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/junit.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/method.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/multi.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/profile.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/specdoc.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/spinner.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/summary.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/unit.rb create mode 100644 spec/mspec/lib/mspec/runner/formatters/yaml.rb create mode 100644 spec/mspec/lib/mspec/runner/mspec.rb create mode 100644 spec/mspec/lib/mspec/runner/object.rb create mode 100644 spec/mspec/lib/mspec/runner/shared.rb create mode 100644 spec/mspec/lib/mspec/runner/tag.rb create mode 100644 spec/mspec/lib/mspec/utils/deprecate.rb create mode 100644 spec/mspec/lib/mspec/utils/name_map.rb create mode 100644 spec/mspec/lib/mspec/utils/options.rb create mode 100644 spec/mspec/lib/mspec/utils/ruby_name.rb create mode 100644 spec/mspec/lib/mspec/utils/script.rb create mode 100644 spec/mspec/lib/mspec/utils/version.rb create mode 100644 spec/mspec/lib/mspec/utils/warnings.rb create mode 100644 spec/mspec/lib/mspec/version.rb create mode 100644 spec/mspec/mspec.gemspec create mode 100644 spec/mspec/spec/commands/fixtures/four.txt create mode 100644 spec/mspec/spec/commands/fixtures/level2/three_spec.rb create mode 100644 spec/mspec/spec/commands/fixtures/one_spec.rb create mode 100644 spec/mspec/spec/commands/fixtures/three.rb create mode 100644 spec/mspec/spec/commands/fixtures/two_spec.rb create mode 100644 spec/mspec/spec/commands/mkspec_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_ci_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_run_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_spec.rb create mode 100644 spec/mspec/spec/commands/mspec_tag_spec.rb create mode 100644 spec/mspec/spec/expectations/expectations_spec.rb create mode 100644 spec/mspec/spec/expectations/should.rb create mode 100644 spec/mspec/spec/expectations/should_spec.rb create mode 100644 spec/mspec/spec/fixtures/a_spec.rb create mode 100644 spec/mspec/spec/fixtures/b_spec.rb create mode 100644 spec/mspec/spec/fixtures/config.mspec create mode 100755 spec/mspec/spec/fixtures/my_ruby create mode 100644 spec/mspec/spec/fixtures/print_interpreter_spec.rb create mode 100644 spec/mspec/spec/fixtures/tagging_spec.rb create mode 100644 spec/mspec/spec/guards/block_device_spec.rb create mode 100644 spec/mspec/spec/guards/bug_spec.rb create mode 100644 spec/mspec/spec/guards/conflict_spec.rb create mode 100644 spec/mspec/spec/guards/endian_spec.rb create mode 100644 spec/mspec/spec/guards/feature_spec.rb create mode 100644 spec/mspec/spec/guards/guard_spec.rb create mode 100644 spec/mspec/spec/guards/platform_spec.rb create mode 100644 spec/mspec/spec/guards/quarantine_spec.rb create mode 100644 spec/mspec/spec/guards/superuser_spec.rb create mode 100644 spec/mspec/spec/guards/support_spec.rb create mode 100644 spec/mspec/spec/guards/user_spec.rb create mode 100644 spec/mspec/spec/guards/version_spec.rb create mode 100644 spec/mspec/spec/helpers/argf_spec.rb create mode 100644 spec/mspec/spec/helpers/argv_spec.rb create mode 100644 spec/mspec/spec/helpers/datetime_spec.rb create mode 100644 spec/mspec/spec/helpers/fixture_spec.rb create mode 100644 spec/mspec/spec/helpers/flunk_spec.rb create mode 100644 spec/mspec/spec/helpers/fs_spec.rb create mode 100644 spec/mspec/spec/helpers/io_spec.rb create mode 100644 spec/mspec/spec/helpers/mock_to_path_spec.rb create mode 100644 spec/mspec/spec/helpers/numeric_spec.rb create mode 100644 spec/mspec/spec/helpers/ruby_exe_spec.rb create mode 100644 spec/mspec/spec/helpers/scratch_spec.rb create mode 100644 spec/mspec/spec/helpers/tmp_spec.rb create mode 100644 spec/mspec/spec/integration/interpreter_spec.rb create mode 100644 spec/mspec/spec/integration/run_spec.rb create mode 100644 spec/mspec/spec/integration/tag_spec.rb create mode 100644 spec/mspec/spec/matchers/base_spec.rb create mode 100644 spec/mspec/spec/matchers/be_an_instance_of_spec.rb create mode 100644 spec/mspec/spec/matchers/be_ancestor_of_spec.rb create mode 100644 spec/mspec/spec/matchers/be_close_spec.rb create mode 100644 spec/mspec/spec/matchers/be_computed_by_spec.rb create mode 100644 spec/mspec/spec/matchers/be_empty_spec.rb create mode 100644 spec/mspec/spec/matchers/be_false_spec.rb create mode 100644 spec/mspec/spec/matchers/be_kind_of_spec.rb create mode 100644 spec/mspec/spec/matchers/be_nan_spec.rb create mode 100644 spec/mspec/spec/matchers/be_nil_spec.rb create mode 100644 spec/mspec/spec/matchers/be_true_or_false_spec.rb create mode 100644 spec/mspec/spec/matchers/be_true_spec.rb create mode 100644 spec/mspec/spec/matchers/block_caller_spec.rb create mode 100644 spec/mspec/spec/matchers/complain_spec.rb create mode 100644 spec/mspec/spec/matchers/eql_spec.rb create mode 100644 spec/mspec/spec/matchers/equal_element_spec.rb create mode 100644 spec/mspec/spec/matchers/equal_spec.rb create mode 100644 spec/mspec/spec/matchers/have_class_variable_spec.rb create mode 100644 spec/mspec/spec/matchers/have_constant_spec.rb create mode 100644 spec/mspec/spec/matchers/have_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_instance_variable_spec.rb create mode 100644 spec/mspec/spec/matchers/have_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_private_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_private_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_protected_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_public_instance_method_spec.rb create mode 100644 spec/mspec/spec/matchers/have_singleton_method_spec.rb create mode 100644 spec/mspec/spec/matchers/include_spec.rb create mode 100644 spec/mspec/spec/matchers/infinity_spec.rb create mode 100644 spec/mspec/spec/matchers/match_yaml_spec.rb create mode 100644 spec/mspec/spec/matchers/output_spec.rb create mode 100644 spec/mspec/spec/matchers/output_to_fd_spec.rb create mode 100644 spec/mspec/spec/matchers/raise_error_spec.rb create mode 100644 spec/mspec/spec/matchers/respond_to_spec.rb create mode 100644 spec/mspec/spec/matchers/signed_zero_spec.rb create mode 100644 spec/mspec/spec/mocks/mock_spec.rb create mode 100644 spec/mspec/spec/mocks/proxy_spec.rb create mode 100644 spec/mspec/spec/runner/actions/filter_spec.rb create mode 100644 spec/mspec/spec/runner/actions/tag_spec.rb create mode 100644 spec/mspec/spec/runner/actions/taglist_spec.rb create mode 100644 spec/mspec/spec/runner/actions/tagpurge_spec.rb create mode 100644 spec/mspec/spec/runner/actions/tally_spec.rb create mode 100644 spec/mspec/spec/runner/actions/timer_spec.rb create mode 100644 spec/mspec/spec/runner/context_spec.rb create mode 100644 spec/mspec/spec/runner/example_spec.rb create mode 100644 spec/mspec/spec/runner/exception_spec.rb create mode 100644 spec/mspec/spec/runner/filters/a.yaml create mode 100644 spec/mspec/spec/runner/filters/b.yaml create mode 100644 spec/mspec/spec/runner/filters/match_spec.rb create mode 100644 spec/mspec/spec/runner/filters/profile_spec.rb create mode 100644 spec/mspec/spec/runner/filters/regexp_spec.rb create mode 100644 spec/mspec/spec/runner/filters/tag_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/describe_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/dotted_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/file_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/html_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/junit_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/method_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/multi_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/specdoc_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/spinner_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/summary_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/unit_spec.rb create mode 100644 spec/mspec/spec/runner/formatters/yaml_spec.rb create mode 100644 spec/mspec/spec/runner/mspec_spec.rb create mode 100644 spec/mspec/spec/runner/shared_spec.rb create mode 100644 spec/mspec/spec/runner/tag_spec.rb create mode 100644 spec/mspec/spec/runner/tags.txt create mode 100644 spec/mspec/spec/spec_helper.rb create mode 100644 spec/mspec/spec/utils/deprecate_spec.rb create mode 100644 spec/mspec/spec/utils/name_map_spec.rb create mode 100644 spec/mspec/spec/utils/options_spec.rb create mode 100644 spec/mspec/spec/utils/script_spec.rb create mode 100644 spec/mspec/spec/utils/version_spec.rb create mode 100644 spec/mspec/tool/remove_old_guards.rb create mode 100644 spec/rubyspec/.gitignore create mode 100644 spec/rubyspec/.travis.yml create mode 100644 spec/rubyspec/CHANGES.before-2008-05-10 create mode 100644 spec/rubyspec/CONTRIBUTING.md create mode 100644 spec/rubyspec/Gemfile create mode 100644 spec/rubyspec/LICENSE create mode 100644 spec/rubyspec/README.md create mode 100644 spec/rubyspec/TODO create mode 100644 spec/rubyspec/appveyor.yml create mode 100644 spec/rubyspec/command_line/dash_a_spec.rb create mode 100644 spec/rubyspec/command_line/dash_c_spec.rb create mode 100644 spec/rubyspec/command_line/dash_d_spec.rb create mode 100644 spec/rubyspec/command_line/dash_e_spec.rb create mode 100644 spec/rubyspec/command_line/dash_n_spec.rb create mode 100644 spec/rubyspec/command_line/dash_p_spec.rb create mode 100644 spec/rubyspec/command_line/dash_r_spec.rb create mode 100644 spec/rubyspec/command_line/dash_s_spec.rb create mode 100644 spec/rubyspec/command_line/dash_upper_c_spec.rb create mode 100644 spec/rubyspec/command_line/dash_upper_e_spec.rb create mode 100644 spec/rubyspec/command_line/dash_upper_f_spec.rb create mode 100644 spec/rubyspec/command_line/dash_upper_i_spec.rb create mode 100644 spec/rubyspec/command_line/dash_upper_k_spec.rb create mode 100644 spec/rubyspec/command_line/dash_upper_u_spec.rb create mode 100644 spec/rubyspec/command_line/dash_upper_w_spec.rb create mode 100644 spec/rubyspec/command_line/dash_v_spec.rb create mode 100644 spec/rubyspec/command_line/dash_w_spec.rb create mode 100644 spec/rubyspec/command_line/dash_x_spec.rb create mode 100644 spec/rubyspec/command_line/error_message_spec.rb create mode 100644 spec/rubyspec/command_line/fixtures/bad_syntax.rb create mode 100644 spec/rubyspec/command_line/fixtures/conditional_range.txt create mode 100644 spec/rubyspec/command_line/fixtures/dash_s_script.rb create mode 100644 spec/rubyspec/command_line/fixtures/dash_upper_c_script.rb create mode 100644 spec/rubyspec/command_line/fixtures/debug.rb create mode 100644 spec/rubyspec/command_line/fixtures/debug_info.rb create mode 100644 spec/rubyspec/command_line/fixtures/embedded_ruby.txt create mode 100644 spec/rubyspec/command_line/fixtures/freeze_flag_across_files.rb create mode 100644 spec/rubyspec/command_line/fixtures/freeze_flag_across_files_diff_enc.rb create mode 100644 spec/rubyspec/command_line/fixtures/freeze_flag_one_literal.rb create mode 100644 spec/rubyspec/command_line/fixtures/freeze_flag_required.rb create mode 100644 spec/rubyspec/command_line/fixtures/freeze_flag_required_diff_enc.rb create mode 100644 spec/rubyspec/command_line/fixtures/freeze_flag_two_literals.rb create mode 100644 spec/rubyspec/command_line/fixtures/full_names.txt create mode 100644 spec/rubyspec/command_line/fixtures/loadpath.rb create mode 100644 spec/rubyspec/command_line/fixtures/names.txt create mode 100644 spec/rubyspec/command_line/fixtures/passwd_file.txt create mode 100644 spec/rubyspec/command_line/fixtures/require.rb create mode 100644 spec/rubyspec/command_line/fixtures/rubyopt.rb create mode 100644 spec/rubyspec/command_line/fixtures/test_file.rb create mode 100644 spec/rubyspec/command_line/fixtures/verbose.rb create mode 100644 spec/rubyspec/command_line/frozen_strings_spec.rb create mode 100644 spec/rubyspec/command_line/rubyopt_spec.rb create mode 100644 spec/rubyspec/command_line/shared/verbose.rb create mode 100644 spec/rubyspec/command_line/syntax_error_spec.rb create mode 100644 spec/rubyspec/core/argf/argf_spec.rb create mode 100644 spec/rubyspec/core/argf/argv_spec.rb create mode 100644 spec/rubyspec/core/argf/binmode_spec.rb create mode 100644 spec/rubyspec/core/argf/bytes_spec.rb create mode 100644 spec/rubyspec/core/argf/chars_spec.rb create mode 100644 spec/rubyspec/core/argf/close_spec.rb create mode 100644 spec/rubyspec/core/argf/closed_spec.rb create mode 100644 spec/rubyspec/core/argf/codepoints_spec.rb create mode 100644 spec/rubyspec/core/argf/each_byte_spec.rb create mode 100644 spec/rubyspec/core/argf/each_char_spec.rb create mode 100644 spec/rubyspec/core/argf/each_codepoint_spec.rb create mode 100644 spec/rubyspec/core/argf/each_line_spec.rb create mode 100644 spec/rubyspec/core/argf/each_spec.rb create mode 100644 spec/rubyspec/core/argf/eof_spec.rb create mode 100644 spec/rubyspec/core/argf/file_spec.rb create mode 100644 spec/rubyspec/core/argf/filename_spec.rb create mode 100644 spec/rubyspec/core/argf/fileno_spec.rb create mode 100644 spec/rubyspec/core/argf/fixtures/bin_file.txt create mode 100644 spec/rubyspec/core/argf/fixtures/encoding.rb create mode 100644 spec/rubyspec/core/argf/fixtures/file1.txt create mode 100644 spec/rubyspec/core/argf/fixtures/file2.txt create mode 100644 spec/rubyspec/core/argf/fixtures/filename.rb create mode 100644 spec/rubyspec/core/argf/fixtures/lineno.rb create mode 100644 spec/rubyspec/core/argf/fixtures/rewind.rb create mode 100644 spec/rubyspec/core/argf/fixtures/stdin.txt create mode 100644 spec/rubyspec/core/argf/getc_spec.rb create mode 100644 spec/rubyspec/core/argf/gets_spec.rb create mode 100644 spec/rubyspec/core/argf/lineno_spec.rb create mode 100644 spec/rubyspec/core/argf/lines_spec.rb create mode 100644 spec/rubyspec/core/argf/path_spec.rb create mode 100644 spec/rubyspec/core/argf/pos_spec.rb create mode 100644 spec/rubyspec/core/argf/read_nonblock_spec.rb create mode 100644 spec/rubyspec/core/argf/read_spec.rb create mode 100644 spec/rubyspec/core/argf/readchar_spec.rb create mode 100644 spec/rubyspec/core/argf/readline_spec.rb create mode 100644 spec/rubyspec/core/argf/readlines_spec.rb create mode 100644 spec/rubyspec/core/argf/readpartial_spec.rb create mode 100644 spec/rubyspec/core/argf/rewind_spec.rb create mode 100644 spec/rubyspec/core/argf/seek_spec.rb create mode 100644 spec/rubyspec/core/argf/set_encoding_spec.rb create mode 100644 spec/rubyspec/core/argf/shared/each_byte.rb create mode 100644 spec/rubyspec/core/argf/shared/each_char.rb create mode 100644 spec/rubyspec/core/argf/shared/each_codepoint.rb create mode 100644 spec/rubyspec/core/argf/shared/each_line.rb create mode 100644 spec/rubyspec/core/argf/shared/eof.rb create mode 100644 spec/rubyspec/core/argf/shared/filename.rb create mode 100644 spec/rubyspec/core/argf/shared/fileno.rb create mode 100644 spec/rubyspec/core/argf/shared/getc.rb create mode 100644 spec/rubyspec/core/argf/shared/gets.rb create mode 100644 spec/rubyspec/core/argf/shared/pos.rb create mode 100644 spec/rubyspec/core/argf/shared/read.rb create mode 100644 spec/rubyspec/core/argf/shared/readlines.rb create mode 100644 spec/rubyspec/core/argf/skip_spec.rb create mode 100644 spec/rubyspec/core/argf/tell_spec.rb create mode 100644 spec/rubyspec/core/argf/to_a_spec.rb create mode 100644 spec/rubyspec/core/argf/to_i_spec.rb create mode 100644 spec/rubyspec/core/argf/to_io_spec.rb create mode 100644 spec/rubyspec/core/argf/to_s_spec.rb create mode 100644 spec/rubyspec/core/array/allocate_spec.rb create mode 100644 spec/rubyspec/core/array/any_spec.rb create mode 100644 spec/rubyspec/core/array/append_spec.rb create mode 100644 spec/rubyspec/core/array/array_spec.rb create mode 100644 spec/rubyspec/core/array/assoc_spec.rb create mode 100644 spec/rubyspec/core/array/at_spec.rb create mode 100644 spec/rubyspec/core/array/bsearch_index_spec.rb create mode 100644 spec/rubyspec/core/array/bsearch_spec.rb create mode 100644 spec/rubyspec/core/array/clear_spec.rb create mode 100644 spec/rubyspec/core/array/clone_spec.rb create mode 100644 spec/rubyspec/core/array/collect_spec.rb create mode 100644 spec/rubyspec/core/array/combination_spec.rb create mode 100644 spec/rubyspec/core/array/compact_spec.rb create mode 100644 spec/rubyspec/core/array/comparison_spec.rb create mode 100644 spec/rubyspec/core/array/concat_spec.rb create mode 100644 spec/rubyspec/core/array/constructor_spec.rb create mode 100644 spec/rubyspec/core/array/count_spec.rb create mode 100644 spec/rubyspec/core/array/cycle_spec.rb create mode 100644 spec/rubyspec/core/array/delete_at_spec.rb create mode 100644 spec/rubyspec/core/array/delete_if_spec.rb create mode 100644 spec/rubyspec/core/array/delete_spec.rb create mode 100644 spec/rubyspec/core/array/dig_spec.rb create mode 100644 spec/rubyspec/core/array/drop_spec.rb create mode 100644 spec/rubyspec/core/array/drop_while_spec.rb create mode 100644 spec/rubyspec/core/array/dup_spec.rb create mode 100644 spec/rubyspec/core/array/each_index_spec.rb create mode 100644 spec/rubyspec/core/array/each_spec.rb create mode 100644 spec/rubyspec/core/array/element_reference_spec.rb create mode 100644 spec/rubyspec/core/array/element_set_spec.rb create mode 100644 spec/rubyspec/core/array/empty_spec.rb create mode 100644 spec/rubyspec/core/array/eql_spec.rb create mode 100644 spec/rubyspec/core/array/equal_value_spec.rb create mode 100644 spec/rubyspec/core/array/fetch_spec.rb create mode 100644 spec/rubyspec/core/array/fill_spec.rb create mode 100644 spec/rubyspec/core/array/find_index_spec.rb create mode 100644 spec/rubyspec/core/array/first_spec.rb create mode 100644 spec/rubyspec/core/array/fixtures/classes.rb create mode 100644 spec/rubyspec/core/array/fixtures/encoded_strings.rb create mode 100644 spec/rubyspec/core/array/flatten_spec.rb create mode 100644 spec/rubyspec/core/array/frozen_spec.rb create mode 100644 spec/rubyspec/core/array/hash_spec.rb create mode 100644 spec/rubyspec/core/array/include_spec.rb create mode 100644 spec/rubyspec/core/array/index_spec.rb create mode 100644 spec/rubyspec/core/array/initialize_spec.rb create mode 100644 spec/rubyspec/core/array/insert_spec.rb create mode 100644 spec/rubyspec/core/array/inspect_spec.rb create mode 100644 spec/rubyspec/core/array/intersection_spec.rb create mode 100644 spec/rubyspec/core/array/join_spec.rb create mode 100644 spec/rubyspec/core/array/keep_if_spec.rb create mode 100644 spec/rubyspec/core/array/last_spec.rb create mode 100644 spec/rubyspec/core/array/length_spec.rb create mode 100644 spec/rubyspec/core/array/map_spec.rb create mode 100644 spec/rubyspec/core/array/max_spec.rb create mode 100644 spec/rubyspec/core/array/min_spec.rb create mode 100644 spec/rubyspec/core/array/minus_spec.rb create mode 100644 spec/rubyspec/core/array/multiply_spec.rb create mode 100644 spec/rubyspec/core/array/new_spec.rb create mode 100644 spec/rubyspec/core/array/pack/a_spec.rb create mode 100644 spec/rubyspec/core/array/pack/at_spec.rb create mode 100644 spec/rubyspec/core/array/pack/b_spec.rb create mode 100644 spec/rubyspec/core/array/pack/c_spec.rb create mode 100644 spec/rubyspec/core/array/pack/comment_spec.rb create mode 100644 spec/rubyspec/core/array/pack/d_spec.rb create mode 100644 spec/rubyspec/core/array/pack/e_spec.rb create mode 100644 spec/rubyspec/core/array/pack/empty_spec.rb create mode 100644 spec/rubyspec/core/array/pack/f_spec.rb create mode 100644 spec/rubyspec/core/array/pack/g_spec.rb create mode 100644 spec/rubyspec/core/array/pack/h_spec.rb create mode 100644 spec/rubyspec/core/array/pack/i_spec.rb create mode 100644 spec/rubyspec/core/array/pack/j_spec.rb create mode 100644 spec/rubyspec/core/array/pack/l_spec.rb create mode 100644 spec/rubyspec/core/array/pack/m_spec.rb create mode 100644 spec/rubyspec/core/array/pack/n_spec.rb create mode 100644 spec/rubyspec/core/array/pack/p_spec.rb create mode 100644 spec/rubyspec/core/array/pack/percent_spec.rb create mode 100644 spec/rubyspec/core/array/pack/q_spec.rb create mode 100644 spec/rubyspec/core/array/pack/s_spec.rb create mode 100644 spec/rubyspec/core/array/pack/shared/basic.rb create mode 100644 spec/rubyspec/core/array/pack/shared/encodings.rb create mode 100644 spec/rubyspec/core/array/pack/shared/float.rb create mode 100644 spec/rubyspec/core/array/pack/shared/integer.rb create mode 100644 spec/rubyspec/core/array/pack/shared/numeric_basic.rb create mode 100644 spec/rubyspec/core/array/pack/shared/string.rb create mode 100644 spec/rubyspec/core/array/pack/shared/unicode.rb create mode 100644 spec/rubyspec/core/array/pack/u_spec.rb create mode 100644 spec/rubyspec/core/array/pack/v_spec.rb create mode 100644 spec/rubyspec/core/array/pack/w_spec.rb create mode 100644 spec/rubyspec/core/array/pack/x_spec.rb create mode 100644 spec/rubyspec/core/array/pack/z_spec.rb create mode 100644 spec/rubyspec/core/array/partition_spec.rb create mode 100644 spec/rubyspec/core/array/permutation_spec.rb create mode 100644 spec/rubyspec/core/array/plus_spec.rb create mode 100644 spec/rubyspec/core/array/pop_spec.rb create mode 100644 spec/rubyspec/core/array/product_spec.rb create mode 100644 spec/rubyspec/core/array/push_spec.rb create mode 100644 spec/rubyspec/core/array/rassoc_spec.rb create mode 100644 spec/rubyspec/core/array/reject_spec.rb create mode 100644 spec/rubyspec/core/array/repeated_combination_spec.rb create mode 100644 spec/rubyspec/core/array/repeated_permutation_spec.rb create mode 100644 spec/rubyspec/core/array/replace_spec.rb create mode 100644 spec/rubyspec/core/array/reverse_each_spec.rb create mode 100644 spec/rubyspec/core/array/reverse_spec.rb create mode 100644 spec/rubyspec/core/array/rindex_spec.rb create mode 100644 spec/rubyspec/core/array/rotate_spec.rb create mode 100644 spec/rubyspec/core/array/sample_spec.rb create mode 100644 spec/rubyspec/core/array/select_spec.rb create mode 100644 spec/rubyspec/core/array/shared/clone.rb create mode 100644 spec/rubyspec/core/array/shared/collect.rb create mode 100644 spec/rubyspec/core/array/shared/delete_if.rb create mode 100644 spec/rubyspec/core/array/shared/enumeratorize.rb create mode 100644 spec/rubyspec/core/array/shared/eql.rb create mode 100644 spec/rubyspec/core/array/shared/index.rb create mode 100644 spec/rubyspec/core/array/shared/inspect.rb create mode 100644 spec/rubyspec/core/array/shared/join.rb create mode 100644 spec/rubyspec/core/array/shared/keep_if.rb create mode 100644 spec/rubyspec/core/array/shared/length.rb create mode 100644 spec/rubyspec/core/array/shared/replace.rb create mode 100644 spec/rubyspec/core/array/shared/slice.rb create mode 100644 spec/rubyspec/core/array/shift_spec.rb create mode 100644 spec/rubyspec/core/array/shuffle_spec.rb create mode 100644 spec/rubyspec/core/array/size_spec.rb create mode 100644 spec/rubyspec/core/array/slice_spec.rb create mode 100644 spec/rubyspec/core/array/sort_by_spec.rb create mode 100644 spec/rubyspec/core/array/sort_spec.rb create mode 100644 spec/rubyspec/core/array/take_spec.rb create mode 100644 spec/rubyspec/core/array/take_while_spec.rb create mode 100644 spec/rubyspec/core/array/to_a_spec.rb create mode 100644 spec/rubyspec/core/array/to_ary_spec.rb create mode 100644 spec/rubyspec/core/array/to_h_spec.rb create mode 100644 spec/rubyspec/core/array/to_s_spec.rb create mode 100644 spec/rubyspec/core/array/transpose_spec.rb create mode 100644 spec/rubyspec/core/array/try_convert_spec.rb create mode 100644 spec/rubyspec/core/array/union_spec.rb create mode 100644 spec/rubyspec/core/array/uniq_spec.rb create mode 100644 spec/rubyspec/core/array/unshift_spec.rb create mode 100644 spec/rubyspec/core/array/values_at_spec.rb create mode 100644 spec/rubyspec/core/array/zip_spec.rb create mode 100644 spec/rubyspec/core/basicobject/__id__spec.rb create mode 100644 spec/rubyspec/core/basicobject/__send___spec.rb create mode 100644 spec/rubyspec/core/basicobject/basicobject_spec.rb create mode 100644 spec/rubyspec/core/basicobject/equal_spec.rb create mode 100644 spec/rubyspec/core/basicobject/equal_value_spec.rb create mode 100644 spec/rubyspec/core/basicobject/fixtures/classes.rb create mode 100644 spec/rubyspec/core/basicobject/fixtures/common.rb create mode 100644 spec/rubyspec/core/basicobject/fixtures/remove_method_missing.rb create mode 100644 spec/rubyspec/core/basicobject/fixtures/singleton_method.rb create mode 100644 spec/rubyspec/core/basicobject/initialize_spec.rb create mode 100644 spec/rubyspec/core/basicobject/instance_eval_spec.rb create mode 100644 spec/rubyspec/core/basicobject/instance_exec_spec.rb create mode 100644 spec/rubyspec/core/basicobject/method_missing_spec.rb create mode 100644 spec/rubyspec/core/basicobject/not_equal_spec.rb create mode 100644 spec/rubyspec/core/basicobject/not_spec.rb create mode 100644 spec/rubyspec/core/basicobject/singleton_method_added_spec.rb create mode 100644 spec/rubyspec/core/basicobject/singleton_method_removed_spec.rb create mode 100644 spec/rubyspec/core/basicobject/singleton_method_undefined_spec.rb create mode 100644 spec/rubyspec/core/bignum/abs_spec.rb create mode 100644 spec/rubyspec/core/bignum/bignum_spec.rb create mode 100644 spec/rubyspec/core/bignum/bit_and_spec.rb create mode 100644 spec/rubyspec/core/bignum/bit_length_spec.rb create mode 100644 spec/rubyspec/core/bignum/bit_or_spec.rb create mode 100644 spec/rubyspec/core/bignum/bit_xor_spec.rb create mode 100644 spec/rubyspec/core/bignum/case_compare_spec.rb create mode 100644 spec/rubyspec/core/bignum/coerce_spec.rb create mode 100644 spec/rubyspec/core/bignum/comparison_spec.rb create mode 100644 spec/rubyspec/core/bignum/complement_spec.rb create mode 100644 spec/rubyspec/core/bignum/div_spec.rb create mode 100644 spec/rubyspec/core/bignum/divide_spec.rb create mode 100644 spec/rubyspec/core/bignum/divmod_spec.rb create mode 100644 spec/rubyspec/core/bignum/element_reference_spec.rb create mode 100644 spec/rubyspec/core/bignum/eql_spec.rb create mode 100644 spec/rubyspec/core/bignum/equal_value_spec.rb create mode 100644 spec/rubyspec/core/bignum/even_spec.rb create mode 100644 spec/rubyspec/core/bignum/exponent_spec.rb create mode 100644 spec/rubyspec/core/bignum/fdiv_spec.rb create mode 100644 spec/rubyspec/core/bignum/gt_spec.rb create mode 100644 spec/rubyspec/core/bignum/gte_spec.rb create mode 100644 spec/rubyspec/core/bignum/hash_spec.rb create mode 100644 spec/rubyspec/core/bignum/left_shift_spec.rb create mode 100644 spec/rubyspec/core/bignum/lt_spec.rb create mode 100644 spec/rubyspec/core/bignum/lte_spec.rb create mode 100644 spec/rubyspec/core/bignum/magnitude_spec.rb create mode 100644 spec/rubyspec/core/bignum/minus_spec.rb create mode 100644 spec/rubyspec/core/bignum/modulo_spec.rb create mode 100644 spec/rubyspec/core/bignum/multiply_spec.rb create mode 100644 spec/rubyspec/core/bignum/odd_spec.rb create mode 100644 spec/rubyspec/core/bignum/plus_spec.rb create mode 100644 spec/rubyspec/core/bignum/remainder_spec.rb create mode 100644 spec/rubyspec/core/bignum/right_shift_spec.rb create mode 100644 spec/rubyspec/core/bignum/shared/abs.rb create mode 100644 spec/rubyspec/core/bignum/shared/divide.rb create mode 100644 spec/rubyspec/core/bignum/shared/equal.rb create mode 100644 spec/rubyspec/core/bignum/shared/modulo.rb create mode 100644 spec/rubyspec/core/bignum/size_spec.rb create mode 100644 spec/rubyspec/core/bignum/to_f_spec.rb create mode 100644 spec/rubyspec/core/bignum/to_s_spec.rb create mode 100644 spec/rubyspec/core/bignum/uminus_spec.rb create mode 100644 spec/rubyspec/core/binding/clone_spec.rb create mode 100644 spec/rubyspec/core/binding/dup_spec.rb create mode 100644 spec/rubyspec/core/binding/eval_spec.rb create mode 100644 spec/rubyspec/core/binding/fixtures/classes.rb create mode 100644 spec/rubyspec/core/binding/local_variable_defined_spec.rb create mode 100644 spec/rubyspec/core/binding/local_variable_get_spec.rb create mode 100644 spec/rubyspec/core/binding/local_variable_set_spec.rb create mode 100644 spec/rubyspec/core/binding/local_variables_spec.rb create mode 100644 spec/rubyspec/core/binding/location_spec.rb create mode 100644 spec/rubyspec/core/binding/receiver_spec.rb create mode 100644 spec/rubyspec/core/binding/shared/clone.rb create mode 100644 spec/rubyspec/core/builtin_constants/builtin_constants_spec.rb create mode 100644 spec/rubyspec/core/class/allocate_spec.rb create mode 100644 spec/rubyspec/core/class/dup_spec.rb create mode 100644 spec/rubyspec/core/class/fixtures/classes.rb create mode 100644 spec/rubyspec/core/class/inherited_spec.rb create mode 100644 spec/rubyspec/core/class/initialize_spec.rb create mode 100644 spec/rubyspec/core/class/new_spec.rb create mode 100644 spec/rubyspec/core/class/superclass_spec.rb create mode 100644 spec/rubyspec/core/class/to_s_spec.rb create mode 100644 spec/rubyspec/core/comparable/between_spec.rb create mode 100644 spec/rubyspec/core/comparable/clamp_spec.rb create mode 100644 spec/rubyspec/core/comparable/equal_value_spec.rb create mode 100644 spec/rubyspec/core/comparable/fixtures/classes.rb create mode 100644 spec/rubyspec/core/comparable/gt_spec.rb create mode 100644 spec/rubyspec/core/comparable/gte_spec.rb create mode 100644 spec/rubyspec/core/comparable/lt_spec.rb create mode 100644 spec/rubyspec/core/comparable/lte_spec.rb create mode 100644 spec/rubyspec/core/complex/abs2_spec.rb create mode 100644 spec/rubyspec/core/complex/abs_spec.rb create mode 100644 spec/rubyspec/core/complex/angle_spec.rb create mode 100644 spec/rubyspec/core/complex/arg_spec.rb create mode 100644 spec/rubyspec/core/complex/coerce_spec.rb create mode 100644 spec/rubyspec/core/complex/conj_spec.rb create mode 100644 spec/rubyspec/core/complex/conjugate_spec.rb create mode 100644 spec/rubyspec/core/complex/constants_spec.rb create mode 100644 spec/rubyspec/core/complex/denominator_spec.rb create mode 100644 spec/rubyspec/core/complex/divide_spec.rb create mode 100644 spec/rubyspec/core/complex/eql_spec.rb create mode 100644 spec/rubyspec/core/complex/equal_value_spec.rb create mode 100644 spec/rubyspec/core/complex/exponent_spec.rb create mode 100644 spec/rubyspec/core/complex/fdiv_spec.rb create mode 100644 spec/rubyspec/core/complex/hash_spec.rb create mode 100644 spec/rubyspec/core/complex/imag_spec.rb create mode 100644 spec/rubyspec/core/complex/imaginary_spec.rb create mode 100644 spec/rubyspec/core/complex/inspect_spec.rb create mode 100644 spec/rubyspec/core/complex/integer_spec.rb create mode 100644 spec/rubyspec/core/complex/magnitude_spec.rb create mode 100644 spec/rubyspec/core/complex/marshal_dump_spec.rb create mode 100644 spec/rubyspec/core/complex/minus_spec.rb create mode 100644 spec/rubyspec/core/complex/multiply_spec.rb create mode 100644 spec/rubyspec/core/complex/negative_spec.rb create mode 100644 spec/rubyspec/core/complex/numerator_spec.rb create mode 100644 spec/rubyspec/core/complex/phase_spec.rb create mode 100644 spec/rubyspec/core/complex/plus_spec.rb create mode 100644 spec/rubyspec/core/complex/polar_spec.rb create mode 100644 spec/rubyspec/core/complex/positive_spec.rb create mode 100644 spec/rubyspec/core/complex/quo_spec.rb create mode 100644 spec/rubyspec/core/complex/rationalize_spec.rb create mode 100644 spec/rubyspec/core/complex/real_spec.rb create mode 100644 spec/rubyspec/core/complex/rect_spec.rb create mode 100644 spec/rubyspec/core/complex/rectangular_spec.rb create mode 100644 spec/rubyspec/core/complex/to_f_spec.rb create mode 100644 spec/rubyspec/core/complex/to_i_spec.rb create mode 100644 spec/rubyspec/core/complex/to_r_spec.rb create mode 100644 spec/rubyspec/core/complex/to_s_spec.rb create mode 100644 spec/rubyspec/core/complex/uminus_spec.rb create mode 100644 spec/rubyspec/core/dir/chdir_spec.rb create mode 100644 spec/rubyspec/core/dir/chroot_spec.rb create mode 100644 spec/rubyspec/core/dir/close_spec.rb create mode 100644 spec/rubyspec/core/dir/delete_spec.rb create mode 100644 spec/rubyspec/core/dir/dir_spec.rb create mode 100644 spec/rubyspec/core/dir/each_spec.rb create mode 100644 spec/rubyspec/core/dir/element_reference_spec.rb create mode 100644 spec/rubyspec/core/dir/entries_spec.rb create mode 100644 spec/rubyspec/core/dir/exist_spec.rb create mode 100644 spec/rubyspec/core/dir/exists_spec.rb create mode 100644 spec/rubyspec/core/dir/fileno_spec.rb create mode 100644 spec/rubyspec/core/dir/fixtures/common.rb create mode 100644 spec/rubyspec/core/dir/foreach_spec.rb create mode 100644 spec/rubyspec/core/dir/getwd_spec.rb create mode 100644 spec/rubyspec/core/dir/glob_spec.rb create mode 100644 spec/rubyspec/core/dir/home_spec.rb create mode 100644 spec/rubyspec/core/dir/initialize_spec.rb create mode 100644 spec/rubyspec/core/dir/inspect_spec.rb create mode 100644 spec/rubyspec/core/dir/mkdir_spec.rb create mode 100644 spec/rubyspec/core/dir/open_spec.rb create mode 100644 spec/rubyspec/core/dir/path_spec.rb create mode 100644 spec/rubyspec/core/dir/pos_spec.rb create mode 100644 spec/rubyspec/core/dir/pwd_spec.rb create mode 100644 spec/rubyspec/core/dir/read_spec.rb create mode 100644 spec/rubyspec/core/dir/rewind_spec.rb create mode 100644 spec/rubyspec/core/dir/rmdir_spec.rb create mode 100644 spec/rubyspec/core/dir/seek_spec.rb create mode 100644 spec/rubyspec/core/dir/shared/chroot.rb create mode 100644 spec/rubyspec/core/dir/shared/closed.rb create mode 100644 spec/rubyspec/core/dir/shared/delete.rb create mode 100644 spec/rubyspec/core/dir/shared/exist.rb create mode 100644 spec/rubyspec/core/dir/shared/glob.rb create mode 100644 spec/rubyspec/core/dir/shared/open.rb create mode 100644 spec/rubyspec/core/dir/shared/path.rb create mode 100644 spec/rubyspec/core/dir/shared/pos.rb create mode 100644 spec/rubyspec/core/dir/shared/pwd.rb create mode 100644 spec/rubyspec/core/dir/tell_spec.rb create mode 100644 spec/rubyspec/core/dir/to_path_spec.rb create mode 100644 spec/rubyspec/core/dir/unlink_spec.rb create mode 100644 spec/rubyspec/core/encoding/_dump_spec.rb create mode 100644 spec/rubyspec/core/encoding/_load_spec.rb create mode 100644 spec/rubyspec/core/encoding/aliases_spec.rb create mode 100644 spec/rubyspec/core/encoding/ascii_compatible_spec.rb create mode 100644 spec/rubyspec/core/encoding/compatible_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/asciicompat_encoding_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/constants_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/convert_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/convpath_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/destination_encoding_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/finish_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/insert_output_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/inspect_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/last_error_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/new_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/primitive_convert_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/primitive_errinfo_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/putback_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/replacement_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/search_convpath_spec.rb create mode 100644 spec/rubyspec/core/encoding/converter/source_encoding_spec.rb create mode 100644 spec/rubyspec/core/encoding/default_external_spec.rb create mode 100644 spec/rubyspec/core/encoding/default_internal_spec.rb create mode 100644 spec/rubyspec/core/encoding/dummy_spec.rb create mode 100644 spec/rubyspec/core/encoding/find_spec.rb create mode 100644 spec/rubyspec/core/encoding/fixtures/classes.rb create mode 100644 spec/rubyspec/core/encoding/inspect_spec.rb create mode 100644 spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb create mode 100644 spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb create mode 100644 spec/rubyspec/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb create mode 100644 spec/rubyspec/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb create mode 100644 spec/rubyspec/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb create mode 100644 spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb create mode 100644 spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb create mode 100644 spec/rubyspec/core/encoding/list_spec.rb create mode 100644 spec/rubyspec/core/encoding/locale_charmap_spec.rb create mode 100644 spec/rubyspec/core/encoding/name_list_spec.rb create mode 100644 spec/rubyspec/core/encoding/name_spec.rb create mode 100644 spec/rubyspec/core/encoding/names_spec.rb create mode 100644 spec/rubyspec/core/encoding/replicate_spec.rb create mode 100644 spec/rubyspec/core/encoding/shared/name.rb create mode 100644 spec/rubyspec/core/encoding/to_s_spec.rb create mode 100644 spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb create mode 100644 spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_spec.rb create mode 100644 spec/rubyspec/core/encoding/undefined_conversion_error/error_char_spec.rb create mode 100644 spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb create mode 100644 spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_spec.rb create mode 100644 spec/rubyspec/core/enumerable/all_spec.rb create mode 100644 spec/rubyspec/core/enumerable/any_spec.rb create mode 100644 spec/rubyspec/core/enumerable/chunk_spec.rb create mode 100644 spec/rubyspec/core/enumerable/chunk_while_spec.rb create mode 100644 spec/rubyspec/core/enumerable/collect_concat_spec.rb create mode 100644 spec/rubyspec/core/enumerable/collect_spec.rb create mode 100644 spec/rubyspec/core/enumerable/count_spec.rb create mode 100644 spec/rubyspec/core/enumerable/cycle_spec.rb create mode 100644 spec/rubyspec/core/enumerable/detect_spec.rb create mode 100644 spec/rubyspec/core/enumerable/drop_spec.rb create mode 100644 spec/rubyspec/core/enumerable/drop_while_spec.rb create mode 100644 spec/rubyspec/core/enumerable/each_cons_spec.rb create mode 100644 spec/rubyspec/core/enumerable/each_entry_spec.rb create mode 100644 spec/rubyspec/core/enumerable/each_slice_spec.rb create mode 100644 spec/rubyspec/core/enumerable/each_with_index_spec.rb create mode 100644 spec/rubyspec/core/enumerable/each_with_object_spec.rb create mode 100644 spec/rubyspec/core/enumerable/entries_spec.rb create mode 100644 spec/rubyspec/core/enumerable/find_all_spec.rb create mode 100644 spec/rubyspec/core/enumerable/find_index_spec.rb create mode 100644 spec/rubyspec/core/enumerable/find_spec.rb create mode 100644 spec/rubyspec/core/enumerable/first_spec.rb create mode 100644 spec/rubyspec/core/enumerable/fixtures/classes.rb create mode 100644 spec/rubyspec/core/enumerable/flat_map_spec.rb create mode 100644 spec/rubyspec/core/enumerable/grep_spec.rb create mode 100644 spec/rubyspec/core/enumerable/grep_v_spec.rb create mode 100644 spec/rubyspec/core/enumerable/group_by_spec.rb create mode 100644 spec/rubyspec/core/enumerable/include_spec.rb create mode 100644 spec/rubyspec/core/enumerable/inject_spec.rb create mode 100644 spec/rubyspec/core/enumerable/lazy_spec.rb create mode 100644 spec/rubyspec/core/enumerable/map_spec.rb create mode 100644 spec/rubyspec/core/enumerable/max_by_spec.rb create mode 100644 spec/rubyspec/core/enumerable/max_spec.rb create mode 100644 spec/rubyspec/core/enumerable/member_spec.rb create mode 100644 spec/rubyspec/core/enumerable/min_by_spec.rb create mode 100644 spec/rubyspec/core/enumerable/min_spec.rb create mode 100644 spec/rubyspec/core/enumerable/minmax_by_spec.rb create mode 100644 spec/rubyspec/core/enumerable/minmax_spec.rb create mode 100644 spec/rubyspec/core/enumerable/none_spec.rb create mode 100644 spec/rubyspec/core/enumerable/one_spec.rb create mode 100644 spec/rubyspec/core/enumerable/partition_spec.rb create mode 100644 spec/rubyspec/core/enumerable/reduce_spec.rb create mode 100644 spec/rubyspec/core/enumerable/reject_spec.rb create mode 100644 spec/rubyspec/core/enumerable/reverse_each_spec.rb create mode 100644 spec/rubyspec/core/enumerable/select_spec.rb create mode 100644 spec/rubyspec/core/enumerable/shared/collect.rb create mode 100644 spec/rubyspec/core/enumerable/shared/collect_concat.rb create mode 100644 spec/rubyspec/core/enumerable/shared/entries.rb create mode 100644 spec/rubyspec/core/enumerable/shared/enumerable_enumeratorized.rb create mode 100644 spec/rubyspec/core/enumerable/shared/enumeratorized.rb create mode 100644 spec/rubyspec/core/enumerable/shared/find.rb create mode 100644 spec/rubyspec/core/enumerable/shared/find_all.rb create mode 100644 spec/rubyspec/core/enumerable/shared/include.rb create mode 100644 spec/rubyspec/core/enumerable/shared/inject.rb create mode 100644 spec/rubyspec/core/enumerable/shared/take.rb create mode 100644 spec/rubyspec/core/enumerable/slice_after_spec.rb create mode 100644 spec/rubyspec/core/enumerable/slice_before_spec.rb create mode 100644 spec/rubyspec/core/enumerable/slice_when_spec.rb create mode 100644 spec/rubyspec/core/enumerable/sort_by_spec.rb create mode 100644 spec/rubyspec/core/enumerable/sort_spec.rb create mode 100644 spec/rubyspec/core/enumerable/take_spec.rb create mode 100644 spec/rubyspec/core/enumerable/take_while_spec.rb create mode 100644 spec/rubyspec/core/enumerable/to_a_spec.rb create mode 100644 spec/rubyspec/core/enumerable/to_h_spec.rb create mode 100644 spec/rubyspec/core/enumerable/zip_spec.rb create mode 100644 spec/rubyspec/core/enumerator/each_spec.rb create mode 100644 spec/rubyspec/core/enumerator/each_with_index_spec.rb create mode 100644 spec/rubyspec/core/enumerator/each_with_object_spec.rb create mode 100644 spec/rubyspec/core/enumerator/enum_for_spec.rb create mode 100644 spec/rubyspec/core/enumerator/enumerator_spec.rb create mode 100644 spec/rubyspec/core/enumerator/feed_spec.rb create mode 100644 spec/rubyspec/core/enumerator/first_spec.rb create mode 100644 spec/rubyspec/core/enumerator/fixtures/common.rb create mode 100644 spec/rubyspec/core/enumerator/generator/each_spec.rb create mode 100644 spec/rubyspec/core/enumerator/generator/initialize_spec.rb create mode 100644 spec/rubyspec/core/enumerator/initialize_spec.rb create mode 100644 spec/rubyspec/core/enumerator/inject_spec.rb create mode 100644 spec/rubyspec/core/enumerator/inspect_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/collect_concat_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/collect_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/drop_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/drop_while_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/enum_for_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/find_all_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/fixtures/classes.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/flat_map_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/force_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/grep_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/grep_v_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/initialize_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/lazy_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/map_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/reject_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/select_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/shared/collect.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/shared/collect_concat.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/shared/select.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/shared/to_enum.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/take_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/take_while_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/to_enum_spec.rb create mode 100644 spec/rubyspec/core/enumerator/lazy/zip_spec.rb create mode 100644 spec/rubyspec/core/enumerator/new_spec.rb create mode 100644 spec/rubyspec/core/enumerator/next_spec.rb create mode 100644 spec/rubyspec/core/enumerator/next_values_spec.rb create mode 100644 spec/rubyspec/core/enumerator/peek_spec.rb create mode 100644 spec/rubyspec/core/enumerator/peek_values_spec.rb create mode 100644 spec/rubyspec/core/enumerator/rewind_spec.rb create mode 100644 spec/rubyspec/core/enumerator/size_spec.rb create mode 100644 spec/rubyspec/core/enumerator/to_enum_spec.rb create mode 100644 spec/rubyspec/core/enumerator/with_index_spec.rb create mode 100644 spec/rubyspec/core/enumerator/with_object_spec.rb create mode 100644 spec/rubyspec/core/enumerator/yielder/append_spec.rb create mode 100644 spec/rubyspec/core/enumerator/yielder/initialize_spec.rb create mode 100644 spec/rubyspec/core/enumerator/yielder/yield_spec.rb create mode 100644 spec/rubyspec/core/env/assoc_spec.rb create mode 100644 spec/rubyspec/core/env/clear_spec.rb create mode 100644 spec/rubyspec/core/env/delete_if_spec.rb create mode 100644 spec/rubyspec/core/env/delete_spec.rb create mode 100644 spec/rubyspec/core/env/each_key_spec.rb create mode 100644 spec/rubyspec/core/env/each_pair_spec.rb create mode 100644 spec/rubyspec/core/env/each_spec.rb create mode 100644 spec/rubyspec/core/env/each_value_spec.rb create mode 100644 spec/rubyspec/core/env/element_reference_spec.rb create mode 100644 spec/rubyspec/core/env/element_set_spec.rb create mode 100644 spec/rubyspec/core/env/empty_spec.rb create mode 100644 spec/rubyspec/core/env/fetch_spec.rb create mode 100644 spec/rubyspec/core/env/has_key_spec.rb create mode 100644 spec/rubyspec/core/env/has_value_spec.rb create mode 100644 spec/rubyspec/core/env/include_spec.rb create mode 100644 spec/rubyspec/core/env/index_spec.rb create mode 100644 spec/rubyspec/core/env/indexes_spec.rb create mode 100644 spec/rubyspec/core/env/indices_spec.rb create mode 100644 spec/rubyspec/core/env/inspect_spec.rb create mode 100644 spec/rubyspec/core/env/invert_spec.rb create mode 100644 spec/rubyspec/core/env/keep_if_spec.rb create mode 100644 spec/rubyspec/core/env/key_spec.rb create mode 100644 spec/rubyspec/core/env/keys_spec.rb create mode 100644 spec/rubyspec/core/env/length_spec.rb create mode 100644 spec/rubyspec/core/env/member_spec.rb create mode 100644 spec/rubyspec/core/env/rassoc_spec.rb create mode 100644 spec/rubyspec/core/env/rehash_spec.rb create mode 100644 spec/rubyspec/core/env/reject_spec.rb create mode 100644 spec/rubyspec/core/env/replace_spec.rb create mode 100644 spec/rubyspec/core/env/select_spec.rb create mode 100644 spec/rubyspec/core/env/shared/each.rb create mode 100644 spec/rubyspec/core/env/shared/include.rb create mode 100644 spec/rubyspec/core/env/shared/key.rb create mode 100644 spec/rubyspec/core/env/shared/length.rb create mode 100644 spec/rubyspec/core/env/shared/store.rb create mode 100644 spec/rubyspec/core/env/shared/to_hash.rb create mode 100644 spec/rubyspec/core/env/shared/value.rb create mode 100644 spec/rubyspec/core/env/shift_spec.rb create mode 100644 spec/rubyspec/core/env/size_spec.rb create mode 100644 spec/rubyspec/core/env/store_spec.rb create mode 100644 spec/rubyspec/core/env/to_a_spec.rb create mode 100644 spec/rubyspec/core/env/to_h_spec.rb create mode 100644 spec/rubyspec/core/env/to_hash_spec.rb create mode 100644 spec/rubyspec/core/env/to_s_spec.rb create mode 100644 spec/rubyspec/core/env/update_spec.rb create mode 100644 spec/rubyspec/core/env/value_spec.rb create mode 100644 spec/rubyspec/core/env/values_at_spec.rb create mode 100644 spec/rubyspec/core/env/values_spec.rb create mode 100644 spec/rubyspec/core/exception/args_spec.rb create mode 100644 spec/rubyspec/core/exception/arguments_spec.rb create mode 100644 spec/rubyspec/core/exception/backtrace_spec.rb create mode 100644 spec/rubyspec/core/exception/case_compare_spec.rb create mode 100644 spec/rubyspec/core/exception/cause_spec.rb create mode 100644 spec/rubyspec/core/exception/destination_encoding_name_spec.rb create mode 100644 spec/rubyspec/core/exception/destination_encoding_spec.rb create mode 100644 spec/rubyspec/core/exception/equal_value_spec.rb create mode 100644 spec/rubyspec/core/exception/errno_spec.rb create mode 100644 spec/rubyspec/core/exception/error_bytes_spec.rb create mode 100644 spec/rubyspec/core/exception/error_char_spec.rb create mode 100644 spec/rubyspec/core/exception/exception_spec.rb create mode 100644 spec/rubyspec/core/exception/exit_value_spec.rb create mode 100644 spec/rubyspec/core/exception/fixtures/common.rb create mode 100644 spec/rubyspec/core/exception/incomplete_input_spec.rb create mode 100644 spec/rubyspec/core/exception/initialize_spec.rb create mode 100644 spec/rubyspec/core/exception/inspect_spec.rb create mode 100644 spec/rubyspec/core/exception/interrupt_spec.rb create mode 100644 spec/rubyspec/core/exception/io_error_spec.rb create mode 100644 spec/rubyspec/core/exception/load_error_spec.rb create mode 100644 spec/rubyspec/core/exception/message_spec.rb create mode 100644 spec/rubyspec/core/exception/name_error_spec.rb create mode 100644 spec/rubyspec/core/exception/name_spec.rb create mode 100644 spec/rubyspec/core/exception/new_spec.rb create mode 100644 spec/rubyspec/core/exception/no_method_error_spec.rb create mode 100644 spec/rubyspec/core/exception/range_error_spec.rb create mode 100644 spec/rubyspec/core/exception/readagain_bytes_spec.rb create mode 100644 spec/rubyspec/core/exception/reason_spec.rb create mode 100644 spec/rubyspec/core/exception/receiver_spec.rb create mode 100644 spec/rubyspec/core/exception/result_spec.rb create mode 100644 spec/rubyspec/core/exception/script_error_spec.rb create mode 100644 spec/rubyspec/core/exception/set_backtrace_spec.rb create mode 100644 spec/rubyspec/core/exception/shared/new.rb create mode 100644 spec/rubyspec/core/exception/signal_exception_spec.rb create mode 100644 spec/rubyspec/core/exception/signm_spec.rb create mode 100644 spec/rubyspec/core/exception/signo_spec.rb create mode 100644 spec/rubyspec/core/exception/source_encoding_name_spec.rb create mode 100644 spec/rubyspec/core/exception/source_encoding_spec.rb create mode 100644 spec/rubyspec/core/exception/standard_error_spec.rb create mode 100644 spec/rubyspec/core/exception/status_spec.rb create mode 100644 spec/rubyspec/core/exception/success_spec.rb create mode 100644 spec/rubyspec/core/exception/system_call_error_spec.rb create mode 100644 spec/rubyspec/core/exception/system_stack_error_spec.rb create mode 100644 spec/rubyspec/core/exception/to_s_spec.rb create mode 100644 spec/rubyspec/core/false/and_spec.rb create mode 100644 spec/rubyspec/core/false/inspect_spec.rb create mode 100644 spec/rubyspec/core/false/or_spec.rb create mode 100644 spec/rubyspec/core/false/to_s_spec.rb create mode 100644 spec/rubyspec/core/false/xor_spec.rb create mode 100644 spec/rubyspec/core/fiber/new_spec.rb create mode 100644 spec/rubyspec/core/fiber/resume_spec.rb create mode 100644 spec/rubyspec/core/fiber/yield_spec.rb create mode 100644 spec/rubyspec/core/file/absolute_path_spec.rb create mode 100644 spec/rubyspec/core/file/atime_spec.rb create mode 100644 spec/rubyspec/core/file/basename_spec.rb create mode 100644 spec/rubyspec/core/file/birthtime_spec.rb create mode 100644 spec/rubyspec/core/file/blockdev_spec.rb create mode 100644 spec/rubyspec/core/file/chardev_spec.rb create mode 100644 spec/rubyspec/core/file/chmod_spec.rb create mode 100644 spec/rubyspec/core/file/chown_spec.rb create mode 100644 spec/rubyspec/core/file/constants/constants_spec.rb create mode 100644 spec/rubyspec/core/file/constants_spec.rb create mode 100644 spec/rubyspec/core/file/ctime_spec.rb create mode 100644 spec/rubyspec/core/file/delete_spec.rb create mode 100644 spec/rubyspec/core/file/directory_spec.rb create mode 100644 spec/rubyspec/core/file/dirname_spec.rb create mode 100644 spec/rubyspec/core/file/executable_real_spec.rb create mode 100644 spec/rubyspec/core/file/executable_spec.rb create mode 100644 spec/rubyspec/core/file/exist_spec.rb create mode 100644 spec/rubyspec/core/file/exists_spec.rb create mode 100644 spec/rubyspec/core/file/expand_path_spec.rb create mode 100644 spec/rubyspec/core/file/extname_spec.rb create mode 100644 spec/rubyspec/core/file/file_spec.rb create mode 100644 spec/rubyspec/core/file/fixtures/common.rb create mode 100644 spec/rubyspec/core/file/fixtures/do_not_remove create mode 100644 spec/rubyspec/core/file/fixtures/file_types.rb create mode 100644 spec/rubyspec/core/file/flock_spec.rb create mode 100644 spec/rubyspec/core/file/fnmatch_spec.rb create mode 100644 spec/rubyspec/core/file/ftype_spec.rb create mode 100644 spec/rubyspec/core/file/grpowned_spec.rb create mode 100644 spec/rubyspec/core/file/identical_spec.rb create mode 100644 spec/rubyspec/core/file/initialize_spec.rb create mode 100644 spec/rubyspec/core/file/inspect_spec.rb create mode 100644 spec/rubyspec/core/file/join_spec.rb create mode 100644 spec/rubyspec/core/file/lchmod_spec.rb create mode 100644 spec/rubyspec/core/file/lchown_spec.rb create mode 100644 spec/rubyspec/core/file/link_spec.rb create mode 100644 spec/rubyspec/core/file/lstat_spec.rb create mode 100644 spec/rubyspec/core/file/mkfifo_spec.rb create mode 100644 spec/rubyspec/core/file/mtime_spec.rb create mode 100644 spec/rubyspec/core/file/new_spec.rb create mode 100644 spec/rubyspec/core/file/null_spec.rb create mode 100644 spec/rubyspec/core/file/open_spec.rb create mode 100644 spec/rubyspec/core/file/owned_spec.rb create mode 100644 spec/rubyspec/core/file/path_spec.rb create mode 100644 spec/rubyspec/core/file/pipe_spec.rb create mode 100644 spec/rubyspec/core/file/read_spec.rb create mode 100644 spec/rubyspec/core/file/readable_real_spec.rb create mode 100644 spec/rubyspec/core/file/readable_spec.rb create mode 100644 spec/rubyspec/core/file/readlink_spec.rb create mode 100644 spec/rubyspec/core/file/realdirpath_spec.rb create mode 100644 spec/rubyspec/core/file/realpath_spec.rb create mode 100644 spec/rubyspec/core/file/rename_spec.rb create mode 100644 spec/rubyspec/core/file/reopen_spec.rb create mode 100644 spec/rubyspec/core/file/setgid_spec.rb create mode 100644 spec/rubyspec/core/file/setuid_spec.rb create mode 100644 spec/rubyspec/core/file/shared/fnmatch.rb create mode 100644 spec/rubyspec/core/file/shared/open.rb create mode 100644 spec/rubyspec/core/file/shared/read.rb create mode 100644 spec/rubyspec/core/file/shared/stat.rb create mode 100644 spec/rubyspec/core/file/shared/unlink.rb create mode 100644 spec/rubyspec/core/file/size_spec.rb create mode 100644 spec/rubyspec/core/file/socket_spec.rb create mode 100644 spec/rubyspec/core/file/split_spec.rb create mode 100644 spec/rubyspec/core/file/stat/atime_spec.rb create mode 100644 spec/rubyspec/core/file/stat/birthtime_spec.rb create mode 100644 spec/rubyspec/core/file/stat/blksize_spec.rb create mode 100644 spec/rubyspec/core/file/stat/blockdev_spec.rb create mode 100644 spec/rubyspec/core/file/stat/blocks_spec.rb create mode 100644 spec/rubyspec/core/file/stat/chardev_spec.rb create mode 100644 spec/rubyspec/core/file/stat/comparison_spec.rb create mode 100644 spec/rubyspec/core/file/stat/ctime_spec.rb create mode 100644 spec/rubyspec/core/file/stat/dev_major_spec.rb create mode 100644 spec/rubyspec/core/file/stat/dev_minor_spec.rb create mode 100644 spec/rubyspec/core/file/stat/dev_spec.rb create mode 100644 spec/rubyspec/core/file/stat/directory_spec.rb create mode 100644 spec/rubyspec/core/file/stat/executable_real_spec.rb create mode 100644 spec/rubyspec/core/file/stat/executable_spec.rb create mode 100644 spec/rubyspec/core/file/stat/file_spec.rb create mode 100644 spec/rubyspec/core/file/stat/fixtures/classes.rb create mode 100644 spec/rubyspec/core/file/stat/ftype_spec.rb create mode 100644 spec/rubyspec/core/file/stat/gid_spec.rb create mode 100644 spec/rubyspec/core/file/stat/grpowned_spec.rb create mode 100644 spec/rubyspec/core/file/stat/ino_spec.rb create mode 100644 spec/rubyspec/core/file/stat/inspect_spec.rb create mode 100644 spec/rubyspec/core/file/stat/mode_spec.rb create mode 100644 spec/rubyspec/core/file/stat/mtime_spec.rb create mode 100644 spec/rubyspec/core/file/stat/new_spec.rb create mode 100644 spec/rubyspec/core/file/stat/nlink_spec.rb create mode 100644 spec/rubyspec/core/file/stat/owned_spec.rb create mode 100644 spec/rubyspec/core/file/stat/pipe_spec.rb create mode 100644 spec/rubyspec/core/file/stat/rdev_major_spec.rb create mode 100644 spec/rubyspec/core/file/stat/rdev_minor_spec.rb create mode 100644 spec/rubyspec/core/file/stat/rdev_spec.rb create mode 100644 spec/rubyspec/core/file/stat/readable_real_spec.rb create mode 100644 spec/rubyspec/core/file/stat/readable_spec.rb create mode 100644 spec/rubyspec/core/file/stat/setgid_spec.rb create mode 100644 spec/rubyspec/core/file/stat/setuid_spec.rb create mode 100644 spec/rubyspec/core/file/stat/size_spec.rb create mode 100644 spec/rubyspec/core/file/stat/socket_spec.rb create mode 100644 spec/rubyspec/core/file/stat/sticky_spec.rb create mode 100644 spec/rubyspec/core/file/stat/symlink_spec.rb create mode 100644 spec/rubyspec/core/file/stat/uid_spec.rb create mode 100644 spec/rubyspec/core/file/stat/world_readable_spec.rb create mode 100644 spec/rubyspec/core/file/stat/world_writable_spec.rb create mode 100644 spec/rubyspec/core/file/stat/writable_real_spec.rb create mode 100644 spec/rubyspec/core/file/stat/writable_spec.rb create mode 100644 spec/rubyspec/core/file/stat/zero_spec.rb create mode 100644 spec/rubyspec/core/file/stat_spec.rb create mode 100644 spec/rubyspec/core/file/sticky_spec.rb create mode 100644 spec/rubyspec/core/file/symlink_spec.rb create mode 100644 spec/rubyspec/core/file/to_path_spec.rb create mode 100644 spec/rubyspec/core/file/truncate_spec.rb create mode 100644 spec/rubyspec/core/file/umask_spec.rb create mode 100644 spec/rubyspec/core/file/unlink_spec.rb create mode 100644 spec/rubyspec/core/file/utime_spec.rb create mode 100644 spec/rubyspec/core/file/world_readable_spec.rb create mode 100644 spec/rubyspec/core/file/world_writable_spec.rb create mode 100644 spec/rubyspec/core/file/writable_real_spec.rb create mode 100644 spec/rubyspec/core/file/writable_spec.rb create mode 100644 spec/rubyspec/core/file/zero_spec.rb create mode 100644 spec/rubyspec/core/filetest/blockdev_spec.rb create mode 100644 spec/rubyspec/core/filetest/chardev_spec.rb create mode 100644 spec/rubyspec/core/filetest/directory_spec.rb create mode 100644 spec/rubyspec/core/filetest/executable_real_spec.rb create mode 100644 spec/rubyspec/core/filetest/executable_spec.rb create mode 100644 spec/rubyspec/core/filetest/exist_spec.rb create mode 100644 spec/rubyspec/core/filetest/exists_spec.rb create mode 100644 spec/rubyspec/core/filetest/file_spec.rb create mode 100644 spec/rubyspec/core/filetest/grpowned_spec.rb create mode 100644 spec/rubyspec/core/filetest/identical_spec.rb create mode 100644 spec/rubyspec/core/filetest/owned_spec.rb create mode 100644 spec/rubyspec/core/filetest/pipe_spec.rb create mode 100644 spec/rubyspec/core/filetest/readable_real_spec.rb create mode 100644 spec/rubyspec/core/filetest/readable_spec.rb create mode 100644 spec/rubyspec/core/filetest/setgid_spec.rb create mode 100644 spec/rubyspec/core/filetest/setuid_spec.rb create mode 100644 spec/rubyspec/core/filetest/size_spec.rb create mode 100644 spec/rubyspec/core/filetest/socket_spec.rb create mode 100644 spec/rubyspec/core/filetest/sticky_spec.rb create mode 100644 spec/rubyspec/core/filetest/symlink_spec.rb create mode 100644 spec/rubyspec/core/filetest/world_readable_spec.rb create mode 100644 spec/rubyspec/core/filetest/world_writable_spec.rb create mode 100644 spec/rubyspec/core/filetest/writable_real_spec.rb create mode 100644 spec/rubyspec/core/filetest/writable_spec.rb create mode 100644 spec/rubyspec/core/filetest/zero_spec.rb create mode 100644 spec/rubyspec/core/fixnum/abs_spec.rb create mode 100644 spec/rubyspec/core/fixnum/bit_and_spec.rb create mode 100644 spec/rubyspec/core/fixnum/bit_length_spec.rb create mode 100644 spec/rubyspec/core/fixnum/bit_or_spec.rb create mode 100644 spec/rubyspec/core/fixnum/bit_xor_spec.rb create mode 100644 spec/rubyspec/core/fixnum/case_compare_spec.rb create mode 100644 spec/rubyspec/core/fixnum/coerce_spec.rb create mode 100644 spec/rubyspec/core/fixnum/comparison_spec.rb create mode 100644 spec/rubyspec/core/fixnum/complement_spec.rb create mode 100644 spec/rubyspec/core/fixnum/div_spec.rb create mode 100644 spec/rubyspec/core/fixnum/divide_spec.rb create mode 100644 spec/rubyspec/core/fixnum/divmod_spec.rb create mode 100644 spec/rubyspec/core/fixnum/element_reference_spec.rb create mode 100644 spec/rubyspec/core/fixnum/equal_value_spec.rb create mode 100644 spec/rubyspec/core/fixnum/even_spec.rb create mode 100644 spec/rubyspec/core/fixnum/exponent_spec.rb create mode 100644 spec/rubyspec/core/fixnum/fdiv_spec.rb create mode 100644 spec/rubyspec/core/fixnum/fixnum_spec.rb create mode 100644 spec/rubyspec/core/fixnum/gt_spec.rb create mode 100644 spec/rubyspec/core/fixnum/gte_spec.rb create mode 100644 spec/rubyspec/core/fixnum/hash_spec.rb create mode 100644 spec/rubyspec/core/fixnum/left_shift_spec.rb create mode 100644 spec/rubyspec/core/fixnum/lt_spec.rb create mode 100644 spec/rubyspec/core/fixnum/lte_spec.rb create mode 100644 spec/rubyspec/core/fixnum/magnitude_spec.rb create mode 100644 spec/rubyspec/core/fixnum/minus_spec.rb create mode 100644 spec/rubyspec/core/fixnum/modulo_spec.rb create mode 100644 spec/rubyspec/core/fixnum/multiply_spec.rb create mode 100644 spec/rubyspec/core/fixnum/odd_spec.rb create mode 100644 spec/rubyspec/core/fixnum/plus_spec.rb create mode 100644 spec/rubyspec/core/fixnum/right_shift_spec.rb create mode 100644 spec/rubyspec/core/fixnum/shared/abs.rb create mode 100644 spec/rubyspec/core/fixnum/shared/equal.rb create mode 100644 spec/rubyspec/core/fixnum/shared/modulo.rb create mode 100644 spec/rubyspec/core/fixnum/size_spec.rb create mode 100644 spec/rubyspec/core/fixnum/succ_spec.rb create mode 100644 spec/rubyspec/core/fixnum/to_f_spec.rb create mode 100644 spec/rubyspec/core/fixnum/to_s_spec.rb create mode 100644 spec/rubyspec/core/fixnum/uminus_spec.rb create mode 100644 spec/rubyspec/core/fixnum/zero_spec.rb create mode 100644 spec/rubyspec/core/float/abs_spec.rb create mode 100644 spec/rubyspec/core/float/angle_spec.rb create mode 100644 spec/rubyspec/core/float/arg_spec.rb create mode 100644 spec/rubyspec/core/float/case_compare_spec.rb create mode 100644 spec/rubyspec/core/float/ceil_spec.rb create mode 100644 spec/rubyspec/core/float/coerce_spec.rb create mode 100644 spec/rubyspec/core/float/comparison_spec.rb create mode 100644 spec/rubyspec/core/float/constants_spec.rb create mode 100644 spec/rubyspec/core/float/denominator_spec.rb create mode 100644 spec/rubyspec/core/float/divide_spec.rb create mode 100644 spec/rubyspec/core/float/divmod_spec.rb create mode 100644 spec/rubyspec/core/float/eql_spec.rb create mode 100644 spec/rubyspec/core/float/equal_value_spec.rb create mode 100644 spec/rubyspec/core/float/exponent_spec.rb create mode 100644 spec/rubyspec/core/float/fdiv_spec.rb create mode 100644 spec/rubyspec/core/float/finite_spec.rb create mode 100644 spec/rubyspec/core/float/fixtures/coerce.rb create mode 100644 spec/rubyspec/core/float/float_spec.rb create mode 100644 spec/rubyspec/core/float/floor_spec.rb create mode 100644 spec/rubyspec/core/float/gt_spec.rb create mode 100644 spec/rubyspec/core/float/gte_spec.rb create mode 100644 spec/rubyspec/core/float/hash_spec.rb create mode 100644 spec/rubyspec/core/float/infinite_spec.rb create mode 100644 spec/rubyspec/core/float/lt_spec.rb create mode 100644 spec/rubyspec/core/float/lte_spec.rb create mode 100644 spec/rubyspec/core/float/magnitude_spec.rb create mode 100644 spec/rubyspec/core/float/minus_spec.rb create mode 100644 spec/rubyspec/core/float/modulo_spec.rb create mode 100644 spec/rubyspec/core/float/multiply_spec.rb create mode 100644 spec/rubyspec/core/float/nan_spec.rb create mode 100644 spec/rubyspec/core/float/next_float_spec.rb create mode 100644 spec/rubyspec/core/float/numerator_spec.rb create mode 100644 spec/rubyspec/core/float/phase_spec.rb create mode 100644 spec/rubyspec/core/float/plus_spec.rb create mode 100644 spec/rubyspec/core/float/prev_float_spec.rb create mode 100644 spec/rubyspec/core/float/quo_spec.rb create mode 100644 spec/rubyspec/core/float/rationalize_spec.rb create mode 100644 spec/rubyspec/core/float/round_spec.rb create mode 100644 spec/rubyspec/core/float/shared/abs.rb create mode 100644 spec/rubyspec/core/float/shared/equal.rb create mode 100644 spec/rubyspec/core/float/shared/modulo.rb create mode 100644 spec/rubyspec/core/float/shared/quo.rb create mode 100644 spec/rubyspec/core/float/shared/to_i.rb create mode 100644 spec/rubyspec/core/float/to_f_spec.rb create mode 100644 spec/rubyspec/core/float/to_i_spec.rb create mode 100644 spec/rubyspec/core/float/to_int_spec.rb create mode 100644 spec/rubyspec/core/float/to_r_spec.rb create mode 100644 spec/rubyspec/core/float/to_s_spec.rb create mode 100644 spec/rubyspec/core/float/truncate_spec.rb create mode 100644 spec/rubyspec/core/float/uminus_spec.rb create mode 100644 spec/rubyspec/core/float/uplus_spec.rb create mode 100644 spec/rubyspec/core/float/zero_spec.rb create mode 100644 spec/rubyspec/core/gc/count_spec.rb create mode 100644 spec/rubyspec/core/gc/disable_spec.rb create mode 100644 spec/rubyspec/core/gc/enable_spec.rb create mode 100644 spec/rubyspec/core/gc/garbage_collect_spec.rb create mode 100644 spec/rubyspec/core/gc/profiler/clear_spec.rb create mode 100644 spec/rubyspec/core/gc/profiler/disable_spec.rb create mode 100644 spec/rubyspec/core/gc/profiler/enable_spec.rb create mode 100644 spec/rubyspec/core/gc/profiler/enabled_spec.rb create mode 100644 spec/rubyspec/core/gc/profiler/report_spec.rb create mode 100644 spec/rubyspec/core/gc/profiler/result_spec.rb create mode 100644 spec/rubyspec/core/gc/profiler/total_time_spec.rb create mode 100644 spec/rubyspec/core/gc/start_spec.rb create mode 100644 spec/rubyspec/core/gc/stress_spec.rb create mode 100644 spec/rubyspec/core/hash/allocate_spec.rb create mode 100644 spec/rubyspec/core/hash/any_spec.rb create mode 100644 spec/rubyspec/core/hash/assoc_spec.rb create mode 100644 spec/rubyspec/core/hash/clear_spec.rb create mode 100644 spec/rubyspec/core/hash/clone_spec.rb create mode 100644 spec/rubyspec/core/hash/compact_spec.rb create mode 100644 spec/rubyspec/core/hash/compare_by_identity_spec.rb create mode 100644 spec/rubyspec/core/hash/constructor_spec.rb create mode 100644 spec/rubyspec/core/hash/default_proc_spec.rb create mode 100644 spec/rubyspec/core/hash/default_spec.rb create mode 100644 spec/rubyspec/core/hash/delete_if_spec.rb create mode 100644 spec/rubyspec/core/hash/delete_spec.rb create mode 100644 spec/rubyspec/core/hash/dig_spec.rb create mode 100644 spec/rubyspec/core/hash/each_key_spec.rb create mode 100644 spec/rubyspec/core/hash/each_pair_spec.rb create mode 100644 spec/rubyspec/core/hash/each_spec.rb create mode 100644 spec/rubyspec/core/hash/each_value_spec.rb create mode 100644 spec/rubyspec/core/hash/element_reference_spec.rb create mode 100644 spec/rubyspec/core/hash/element_set_spec.rb create mode 100644 spec/rubyspec/core/hash/empty_spec.rb create mode 100644 spec/rubyspec/core/hash/eql_spec.rb create mode 100644 spec/rubyspec/core/hash/equal_value_spec.rb create mode 100644 spec/rubyspec/core/hash/fetch_spec.rb create mode 100644 spec/rubyspec/core/hash/fetch_values_spec.rb create mode 100644 spec/rubyspec/core/hash/fixtures/classes.rb create mode 100644 spec/rubyspec/core/hash/flatten_spec.rb create mode 100644 spec/rubyspec/core/hash/gt_spec.rb create mode 100644 spec/rubyspec/core/hash/gte_spec.rb create mode 100644 spec/rubyspec/core/hash/has_key_spec.rb create mode 100644 spec/rubyspec/core/hash/has_value_spec.rb create mode 100644 spec/rubyspec/core/hash/hash_spec.rb create mode 100644 spec/rubyspec/core/hash/include_spec.rb create mode 100644 spec/rubyspec/core/hash/index_spec.rb create mode 100644 spec/rubyspec/core/hash/initialize_spec.rb create mode 100644 spec/rubyspec/core/hash/inspect_spec.rb create mode 100644 spec/rubyspec/core/hash/invert_spec.rb create mode 100644 spec/rubyspec/core/hash/keep_if_spec.rb create mode 100644 spec/rubyspec/core/hash/key_spec.rb create mode 100644 spec/rubyspec/core/hash/keys_spec.rb create mode 100644 spec/rubyspec/core/hash/length_spec.rb create mode 100644 spec/rubyspec/core/hash/lt_spec.rb create mode 100644 spec/rubyspec/core/hash/lte_spec.rb create mode 100644 spec/rubyspec/core/hash/member_spec.rb create mode 100644 spec/rubyspec/core/hash/merge_spec.rb create mode 100644 spec/rubyspec/core/hash/new_spec.rb create mode 100644 spec/rubyspec/core/hash/rassoc_spec.rb create mode 100644 spec/rubyspec/core/hash/rehash_spec.rb create mode 100644 spec/rubyspec/core/hash/reject_spec.rb create mode 100644 spec/rubyspec/core/hash/replace_spec.rb create mode 100644 spec/rubyspec/core/hash/select_spec.rb create mode 100644 spec/rubyspec/core/hash/shared/comparison.rb create mode 100644 spec/rubyspec/core/hash/shared/each.rb create mode 100644 spec/rubyspec/core/hash/shared/eql.rb create mode 100644 spec/rubyspec/core/hash/shared/equal.rb create mode 100644 spec/rubyspec/core/hash/shared/greater_than.rb create mode 100644 spec/rubyspec/core/hash/shared/index.rb create mode 100644 spec/rubyspec/core/hash/shared/iteration.rb create mode 100644 spec/rubyspec/core/hash/shared/key.rb create mode 100644 spec/rubyspec/core/hash/shared/length.rb create mode 100644 spec/rubyspec/core/hash/shared/less_than.rb create mode 100644 spec/rubyspec/core/hash/shared/replace.rb create mode 100644 spec/rubyspec/core/hash/shared/store.rb create mode 100644 spec/rubyspec/core/hash/shared/to_s.rb create mode 100644 spec/rubyspec/core/hash/shared/update.rb create mode 100644 spec/rubyspec/core/hash/shared/value.rb create mode 100644 spec/rubyspec/core/hash/shared/values_at.rb create mode 100644 spec/rubyspec/core/hash/shift_spec.rb create mode 100644 spec/rubyspec/core/hash/size_spec.rb create mode 100644 spec/rubyspec/core/hash/sort_spec.rb create mode 100644 spec/rubyspec/core/hash/store_spec.rb create mode 100644 spec/rubyspec/core/hash/to_a_spec.rb create mode 100644 spec/rubyspec/core/hash/to_h_spec.rb create mode 100644 spec/rubyspec/core/hash/to_hash_spec.rb create mode 100644 spec/rubyspec/core/hash/to_proc_spec.rb create mode 100644 spec/rubyspec/core/hash/to_s_spec.rb create mode 100644 spec/rubyspec/core/hash/transform_values_spec.rb create mode 100644 spec/rubyspec/core/hash/try_convert_spec.rb create mode 100644 spec/rubyspec/core/hash/update_spec.rb create mode 100644 spec/rubyspec/core/hash/value_spec.rb create mode 100644 spec/rubyspec/core/hash/values_at_spec.rb create mode 100644 spec/rubyspec/core/hash/values_spec.rb create mode 100644 spec/rubyspec/core/integer/ceil_spec.rb create mode 100644 spec/rubyspec/core/integer/chr_spec.rb create mode 100644 spec/rubyspec/core/integer/denominator_spec.rb create mode 100644 spec/rubyspec/core/integer/downto_spec.rb create mode 100644 spec/rubyspec/core/integer/even_spec.rb create mode 100644 spec/rubyspec/core/integer/floor_spec.rb create mode 100644 spec/rubyspec/core/integer/gcd_spec.rb create mode 100644 spec/rubyspec/core/integer/gcdlcm_spec.rb create mode 100644 spec/rubyspec/core/integer/integer_spec.rb create mode 100644 spec/rubyspec/core/integer/lcm_spec.rb create mode 100644 spec/rubyspec/core/integer/next_spec.rb create mode 100644 spec/rubyspec/core/integer/numerator_spec.rb create mode 100644 spec/rubyspec/core/integer/odd_spec.rb create mode 100644 spec/rubyspec/core/integer/ord_spec.rb create mode 100644 spec/rubyspec/core/integer/pred_spec.rb create mode 100644 spec/rubyspec/core/integer/rationalize_spec.rb create mode 100644 spec/rubyspec/core/integer/round_spec.rb create mode 100644 spec/rubyspec/core/integer/shared/next.rb create mode 100644 spec/rubyspec/core/integer/shared/to_i.rb create mode 100644 spec/rubyspec/core/integer/succ_spec.rb create mode 100644 spec/rubyspec/core/integer/times_spec.rb create mode 100644 spec/rubyspec/core/integer/to_i_spec.rb create mode 100644 spec/rubyspec/core/integer/to_int_spec.rb create mode 100644 spec/rubyspec/core/integer/to_r_spec.rb create mode 100644 spec/rubyspec/core/integer/truncate_spec.rb create mode 100644 spec/rubyspec/core/integer/upto_spec.rb create mode 100644 spec/rubyspec/core/io/advise_spec.rb create mode 100644 spec/rubyspec/core/io/binmode_spec.rb create mode 100644 spec/rubyspec/core/io/binread_spec.rb create mode 100644 spec/rubyspec/core/io/binwrite_spec.rb create mode 100644 spec/rubyspec/core/io/bytes_spec.rb create mode 100644 spec/rubyspec/core/io/chars_spec.rb create mode 100644 spec/rubyspec/core/io/close_on_exec_spec.rb create mode 100644 spec/rubyspec/core/io/close_read_spec.rb create mode 100644 spec/rubyspec/core/io/close_spec.rb create mode 100644 spec/rubyspec/core/io/close_write_spec.rb create mode 100644 spec/rubyspec/core/io/closed_spec.rb create mode 100644 spec/rubyspec/core/io/codepoints_spec.rb create mode 100644 spec/rubyspec/core/io/constants_spec.rb create mode 100644 spec/rubyspec/core/io/copy_stream_spec.rb create mode 100644 spec/rubyspec/core/io/dup_spec.rb create mode 100644 spec/rubyspec/core/io/each_byte_spec.rb create mode 100644 spec/rubyspec/core/io/each_char_spec.rb create mode 100644 spec/rubyspec/core/io/each_codepoint_spec.rb create mode 100644 spec/rubyspec/core/io/each_line_spec.rb create mode 100644 spec/rubyspec/core/io/each_spec.rb create mode 100644 spec/rubyspec/core/io/eof_spec.rb create mode 100644 spec/rubyspec/core/io/external_encoding_spec.rb create mode 100644 spec/rubyspec/core/io/fcntl_spec.rb create mode 100644 spec/rubyspec/core/io/fdatasync_spec.rb create mode 100644 spec/rubyspec/core/io/fileno_spec.rb create mode 100644 spec/rubyspec/core/io/fixtures/bom_UTF-16BE.txt create mode 100644 spec/rubyspec/core/io/fixtures/bom_UTF-16LE.txt create mode 100644 spec/rubyspec/core/io/fixtures/bom_UTF-32BE.txt create mode 100644 spec/rubyspec/core/io/fixtures/bom_UTF-32LE.txt create mode 100644 spec/rubyspec/core/io/fixtures/bom_UTF-8.txt create mode 100644 spec/rubyspec/core/io/fixtures/classes.rb create mode 100644 spec/rubyspec/core/io/fixtures/copy_stream.txt create mode 100644 spec/rubyspec/core/io/fixtures/empty.txt create mode 100644 spec/rubyspec/core/io/fixtures/incomplete.txt create mode 100644 spec/rubyspec/core/io/fixtures/lines.txt create mode 100644 spec/rubyspec/core/io/fixtures/no_bom_UTF-8.txt create mode 100644 spec/rubyspec/core/io/fixtures/numbered_lines.txt create mode 100644 spec/rubyspec/core/io/fixtures/one_byte.txt create mode 100644 spec/rubyspec/core/io/fixtures/read_binary.txt create mode 100644 spec/rubyspec/core/io/fixtures/read_euc_jp.txt create mode 100644 spec/rubyspec/core/io/fixtures/read_text.txt create mode 100644 spec/rubyspec/core/io/fixtures/reopen_stdout.rb create mode 100644 spec/rubyspec/core/io/flush_spec.rb create mode 100644 spec/rubyspec/core/io/for_fd_spec.rb create mode 100644 spec/rubyspec/core/io/foreach_spec.rb create mode 100644 spec/rubyspec/core/io/fsync_spec.rb create mode 100644 spec/rubyspec/core/io/getbyte_spec.rb create mode 100644 spec/rubyspec/core/io/getc_spec.rb create mode 100644 spec/rubyspec/core/io/gets_spec.rb create mode 100644 spec/rubyspec/core/io/initialize_spec.rb create mode 100644 spec/rubyspec/core/io/inspect_spec.rb create mode 100644 spec/rubyspec/core/io/internal_encoding_spec.rb create mode 100644 spec/rubyspec/core/io/io_spec.rb create mode 100644 spec/rubyspec/core/io/ioctl_spec.rb create mode 100644 spec/rubyspec/core/io/isatty_spec.rb create mode 100644 spec/rubyspec/core/io/lineno_spec.rb create mode 100644 spec/rubyspec/core/io/lines_spec.rb create mode 100644 spec/rubyspec/core/io/new_spec.rb create mode 100644 spec/rubyspec/core/io/open_spec.rb create mode 100644 spec/rubyspec/core/io/output_spec.rb create mode 100644 spec/rubyspec/core/io/pid_spec.rb create mode 100644 spec/rubyspec/core/io/pipe_spec.rb create mode 100644 spec/rubyspec/core/io/popen_spec.rb create mode 100644 spec/rubyspec/core/io/pos_spec.rb create mode 100644 spec/rubyspec/core/io/print_spec.rb create mode 100644 spec/rubyspec/core/io/printf_spec.rb create mode 100644 spec/rubyspec/core/io/putc_spec.rb create mode 100644 spec/rubyspec/core/io/puts_spec.rb create mode 100644 spec/rubyspec/core/io/read_nonblock_spec.rb create mode 100644 spec/rubyspec/core/io/read_spec.rb create mode 100644 spec/rubyspec/core/io/readbyte_spec.rb create mode 100644 spec/rubyspec/core/io/readchar_spec.rb create mode 100644 spec/rubyspec/core/io/readline_spec.rb create mode 100644 spec/rubyspec/core/io/readlines_spec.rb create mode 100644 spec/rubyspec/core/io/readpartial_spec.rb create mode 100644 spec/rubyspec/core/io/reopen_spec.rb create mode 100644 spec/rubyspec/core/io/rewind_spec.rb create mode 100644 spec/rubyspec/core/io/seek_spec.rb create mode 100644 spec/rubyspec/core/io/select_spec.rb create mode 100644 spec/rubyspec/core/io/set_encoding_spec.rb create mode 100644 spec/rubyspec/core/io/shared/binwrite.rb create mode 100644 spec/rubyspec/core/io/shared/chars.rb create mode 100644 spec/rubyspec/core/io/shared/codepoints.rb create mode 100644 spec/rubyspec/core/io/shared/each.rb create mode 100644 spec/rubyspec/core/io/shared/gets_ascii.rb create mode 100644 spec/rubyspec/core/io/shared/new.rb create mode 100644 spec/rubyspec/core/io/shared/pos.rb create mode 100644 spec/rubyspec/core/io/shared/readlines.rb create mode 100644 spec/rubyspec/core/io/shared/tty.rb create mode 100644 spec/rubyspec/core/io/shared/write.rb create mode 100644 spec/rubyspec/core/io/stat_spec.rb create mode 100644 spec/rubyspec/core/io/sync_spec.rb create mode 100644 spec/rubyspec/core/io/sysopen_spec.rb create mode 100644 spec/rubyspec/core/io/sysread_spec.rb create mode 100644 spec/rubyspec/core/io/sysseek_spec.rb create mode 100644 spec/rubyspec/core/io/syswrite_spec.rb create mode 100644 spec/rubyspec/core/io/tell_spec.rb create mode 100644 spec/rubyspec/core/io/to_i_spec.rb create mode 100644 spec/rubyspec/core/io/to_io_spec.rb create mode 100644 spec/rubyspec/core/io/try_convert_spec.rb create mode 100644 spec/rubyspec/core/io/tty_spec.rb create mode 100644 spec/rubyspec/core/io/ungetbyte_spec.rb create mode 100644 spec/rubyspec/core/io/ungetc_spec.rb create mode 100644 spec/rubyspec/core/io/write_nonblock_spec.rb create mode 100644 spec/rubyspec/core/io/write_spec.rb create mode 100644 spec/rubyspec/core/kernel/Array_spec.rb create mode 100644 spec/rubyspec/core/kernel/Complex_spec.rb create mode 100644 spec/rubyspec/core/kernel/Float_spec.rb create mode 100644 spec/rubyspec/core/kernel/Hash_spec.rb create mode 100644 spec/rubyspec/core/kernel/Integer_spec.rb create mode 100644 spec/rubyspec/core/kernel/Rational_spec.rb create mode 100644 spec/rubyspec/core/kernel/String_spec.rb create mode 100644 spec/rubyspec/core/kernel/__callee___spec.rb create mode 100644 spec/rubyspec/core/kernel/__dir___spec.rb create mode 100644 spec/rubyspec/core/kernel/__method___spec.rb create mode 100644 spec/rubyspec/core/kernel/abort_spec.rb create mode 100644 spec/rubyspec/core/kernel/at_exit_spec.rb create mode 100644 spec/rubyspec/core/kernel/autoload_spec.rb create mode 100644 spec/rubyspec/core/kernel/backtick_spec.rb create mode 100644 spec/rubyspec/core/kernel/binding_spec.rb create mode 100644 spec/rubyspec/core/kernel/block_given_spec.rb create mode 100644 spec/rubyspec/core/kernel/caller_locations_spec.rb create mode 100644 spec/rubyspec/core/kernel/caller_spec.rb create mode 100644 spec/rubyspec/core/kernel/case_compare_spec.rb create mode 100644 spec/rubyspec/core/kernel/catch_spec.rb create mode 100644 spec/rubyspec/core/kernel/chomp_spec.rb create mode 100644 spec/rubyspec/core/kernel/chop_spec.rb create mode 100644 spec/rubyspec/core/kernel/class_spec.rb create mode 100644 spec/rubyspec/core/kernel/clone_spec.rb create mode 100644 spec/rubyspec/core/kernel/comparison_spec.rb create mode 100644 spec/rubyspec/core/kernel/define_singleton_method_spec.rb create mode 100644 spec/rubyspec/core/kernel/display_spec.rb create mode 100644 spec/rubyspec/core/kernel/dup_spec.rb create mode 100644 spec/rubyspec/core/kernel/enum_for_spec.rb create mode 100644 spec/rubyspec/core/kernel/eql_spec.rb create mode 100644 spec/rubyspec/core/kernel/equal_value_spec.rb create mode 100644 spec/rubyspec/core/kernel/eval_spec.rb create mode 100644 spec/rubyspec/core/kernel/exec_spec.rb create mode 100644 spec/rubyspec/core/kernel/exit_spec.rb create mode 100644 spec/rubyspec/core/kernel/extend_spec.rb create mode 100644 spec/rubyspec/core/kernel/fail_spec.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/__callee__.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/__method__.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/autoload_b.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/autoload_c.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/autoload_d.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/autoload_frozen.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/caller.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/caller_locations.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/chomp.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/chomp_f.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/chop.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/chop_f.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/classes.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/eval_locals.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/eval_return_with_lambda.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/eval_return_without_lambda.rb create mode 100644 spec/rubyspec/core/kernel/fixtures/test.rb create mode 100644 spec/rubyspec/core/kernel/fork_spec.rb create mode 100644 spec/rubyspec/core/kernel/format_spec.rb create mode 100644 spec/rubyspec/core/kernel/freeze_spec.rb create mode 100644 spec/rubyspec/core/kernel/frozen_spec.rb create mode 100644 spec/rubyspec/core/kernel/gets_spec.rb create mode 100644 spec/rubyspec/core/kernel/global_variables_spec.rb create mode 100644 spec/rubyspec/core/kernel/gsub_spec.rb create mode 100644 spec/rubyspec/core/kernel/inspect_spec.rb create mode 100644 spec/rubyspec/core/kernel/instance_of_spec.rb create mode 100644 spec/rubyspec/core/kernel/instance_variable_defined_spec.rb create mode 100644 spec/rubyspec/core/kernel/instance_variable_get_spec.rb create mode 100644 spec/rubyspec/core/kernel/instance_variable_set_spec.rb create mode 100644 spec/rubyspec/core/kernel/instance_variables_spec.rb create mode 100644 spec/rubyspec/core/kernel/is_a_spec.rb create mode 100644 spec/rubyspec/core/kernel/iterator_spec.rb create mode 100644 spec/rubyspec/core/kernel/itself_spec.rb create mode 100644 spec/rubyspec/core/kernel/kind_of_spec.rb create mode 100644 spec/rubyspec/core/kernel/lambda_spec.rb create mode 100644 spec/rubyspec/core/kernel/load_spec.rb create mode 100644 spec/rubyspec/core/kernel/local_variables_spec.rb create mode 100644 spec/rubyspec/core/kernel/loop_spec.rb create mode 100644 spec/rubyspec/core/kernel/match_spec.rb create mode 100644 spec/rubyspec/core/kernel/method_spec.rb create mode 100644 spec/rubyspec/core/kernel/methods_spec.rb create mode 100644 spec/rubyspec/core/kernel/nil_spec.rb create mode 100644 spec/rubyspec/core/kernel/not_match_spec.rb create mode 100644 spec/rubyspec/core/kernel/object_id_spec.rb create mode 100644 spec/rubyspec/core/kernel/open_spec.rb create mode 100644 spec/rubyspec/core/kernel/p_spec.rb create mode 100644 spec/rubyspec/core/kernel/print_spec.rb create mode 100644 spec/rubyspec/core/kernel/printf_spec.rb create mode 100644 spec/rubyspec/core/kernel/private_methods_spec.rb create mode 100644 spec/rubyspec/core/kernel/proc_spec.rb create mode 100644 spec/rubyspec/core/kernel/protected_methods_spec.rb create mode 100644 spec/rubyspec/core/kernel/public_method_spec.rb create mode 100644 spec/rubyspec/core/kernel/public_methods_spec.rb create mode 100644 spec/rubyspec/core/kernel/public_send_spec.rb create mode 100644 spec/rubyspec/core/kernel/putc_spec.rb create mode 100644 spec/rubyspec/core/kernel/puts_spec.rb create mode 100644 spec/rubyspec/core/kernel/raise_spec.rb create mode 100644 spec/rubyspec/core/kernel/rand_spec.rb create mode 100644 spec/rubyspec/core/kernel/readline_spec.rb create mode 100644 spec/rubyspec/core/kernel/readlines_spec.rb create mode 100644 spec/rubyspec/core/kernel/remove_instance_variable_spec.rb create mode 100644 spec/rubyspec/core/kernel/require_relative_spec.rb create mode 100644 spec/rubyspec/core/kernel/require_spec.rb create mode 100644 spec/rubyspec/core/kernel/respond_to_missing_spec.rb create mode 100644 spec/rubyspec/core/kernel/respond_to_spec.rb create mode 100644 spec/rubyspec/core/kernel/select_spec.rb create mode 100644 spec/rubyspec/core/kernel/send_spec.rb create mode 100644 spec/rubyspec/core/kernel/set_trace_func_spec.rb create mode 100644 spec/rubyspec/core/kernel/shared/dup_clone.rb create mode 100644 spec/rubyspec/core/kernel/shared/kind_of.rb create mode 100644 spec/rubyspec/core/kernel/shared/lambda.rb create mode 100644 spec/rubyspec/core/kernel/shared/load.rb create mode 100644 spec/rubyspec/core/kernel/shared/method.rb create mode 100644 spec/rubyspec/core/kernel/shared/require.rb create mode 100644 spec/rubyspec/core/kernel/singleton_class_spec.rb create mode 100644 spec/rubyspec/core/kernel/singleton_methods_spec.rb create mode 100644 spec/rubyspec/core/kernel/sleep_spec.rb create mode 100644 spec/rubyspec/core/kernel/spawn_spec.rb create mode 100644 spec/rubyspec/core/kernel/sprintf_spec.rb create mode 100644 spec/rubyspec/core/kernel/srand_spec.rb create mode 100644 spec/rubyspec/core/kernel/sub_spec.rb create mode 100644 spec/rubyspec/core/kernel/syscall_spec.rb create mode 100644 spec/rubyspec/core/kernel/system_spec.rb create mode 100644 spec/rubyspec/core/kernel/taint_spec.rb create mode 100644 spec/rubyspec/core/kernel/tainted_spec.rb create mode 100644 spec/rubyspec/core/kernel/tap_spec.rb create mode 100644 spec/rubyspec/core/kernel/test_spec.rb create mode 100644 spec/rubyspec/core/kernel/throw_spec.rb create mode 100644 spec/rubyspec/core/kernel/to_enum_spec.rb create mode 100644 spec/rubyspec/core/kernel/to_s_spec.rb create mode 100644 spec/rubyspec/core/kernel/trace_var_spec.rb create mode 100644 spec/rubyspec/core/kernel/trap_spec.rb create mode 100644 spec/rubyspec/core/kernel/trust_spec.rb create mode 100644 spec/rubyspec/core/kernel/untaint_spec.rb create mode 100644 spec/rubyspec/core/kernel/untrace_var_spec.rb create mode 100644 spec/rubyspec/core/kernel/untrust_spec.rb create mode 100644 spec/rubyspec/core/kernel/untrusted_spec.rb create mode 100644 spec/rubyspec/core/kernel/warn_spec.rb create mode 100644 spec/rubyspec/core/main/define_method_spec.rb create mode 100644 spec/rubyspec/core/main/fixtures/classes.rb create mode 100644 spec/rubyspec/core/main/fixtures/wrapped_include.rb create mode 100644 spec/rubyspec/core/main/include_spec.rb create mode 100644 spec/rubyspec/core/main/private_spec.rb create mode 100644 spec/rubyspec/core/main/public_spec.rb create mode 100644 spec/rubyspec/core/main/to_s_spec.rb create mode 100644 spec/rubyspec/core/marshal/dump_spec.rb create mode 100644 spec/rubyspec/core/marshal/fixtures/marshal_data.rb create mode 100644 spec/rubyspec/core/marshal/fixtures/random.dump create mode 100644 spec/rubyspec/core/marshal/float_spec.rb create mode 100644 spec/rubyspec/core/marshal/load_spec.rb create mode 100644 spec/rubyspec/core/marshal/major_version_spec.rb create mode 100644 spec/rubyspec/core/marshal/minor_version_spec.rb create mode 100644 spec/rubyspec/core/marshal/restore_spec.rb create mode 100644 spec/rubyspec/core/marshal/shared/load.rb create mode 100644 spec/rubyspec/core/matchdata/begin_spec.rb create mode 100644 spec/rubyspec/core/matchdata/captures_spec.rb create mode 100644 spec/rubyspec/core/matchdata/element_reference_spec.rb create mode 100644 spec/rubyspec/core/matchdata/end_spec.rb create mode 100644 spec/rubyspec/core/matchdata/eql_spec.rb create mode 100644 spec/rubyspec/core/matchdata/equal_value_spec.rb create mode 100644 spec/rubyspec/core/matchdata/hash_spec.rb create mode 100644 spec/rubyspec/core/matchdata/inspect_spec.rb create mode 100644 spec/rubyspec/core/matchdata/length_spec.rb create mode 100644 spec/rubyspec/core/matchdata/names_spec.rb create mode 100644 spec/rubyspec/core/matchdata/offset_spec.rb create mode 100644 spec/rubyspec/core/matchdata/post_match_spec.rb create mode 100644 spec/rubyspec/core/matchdata/pre_match_spec.rb create mode 100644 spec/rubyspec/core/matchdata/regexp_spec.rb create mode 100644 spec/rubyspec/core/matchdata/shared/eql.rb create mode 100644 spec/rubyspec/core/matchdata/shared/length.rb create mode 100644 spec/rubyspec/core/matchdata/size_spec.rb create mode 100644 spec/rubyspec/core/matchdata/string_spec.rb create mode 100644 spec/rubyspec/core/matchdata/to_a_spec.rb create mode 100644 spec/rubyspec/core/matchdata/to_s_spec.rb create mode 100644 spec/rubyspec/core/matchdata/values_at_spec.rb create mode 100644 spec/rubyspec/core/math/acos_spec.rb create mode 100644 spec/rubyspec/core/math/acosh_spec.rb create mode 100644 spec/rubyspec/core/math/asin_spec.rb create mode 100644 spec/rubyspec/core/math/asinh_spec.rb create mode 100644 spec/rubyspec/core/math/atan2_spec.rb create mode 100644 spec/rubyspec/core/math/atan_spec.rb create mode 100644 spec/rubyspec/core/math/atanh_spec.rb create mode 100644 spec/rubyspec/core/math/cbrt_spec.rb create mode 100644 spec/rubyspec/core/math/constants_spec.rb create mode 100644 spec/rubyspec/core/math/cos_spec.rb create mode 100644 spec/rubyspec/core/math/cosh_spec.rb create mode 100644 spec/rubyspec/core/math/erf_spec.rb create mode 100644 spec/rubyspec/core/math/erfc_spec.rb create mode 100644 spec/rubyspec/core/math/exp_spec.rb create mode 100644 spec/rubyspec/core/math/fixtures/classes.rb create mode 100644 spec/rubyspec/core/math/frexp_spec.rb create mode 100644 spec/rubyspec/core/math/gamma_spec.rb create mode 100644 spec/rubyspec/core/math/hypot_spec.rb create mode 100644 spec/rubyspec/core/math/ldexp_spec.rb create mode 100644 spec/rubyspec/core/math/lgamma_spec.rb create mode 100644 spec/rubyspec/core/math/log10_spec.rb create mode 100644 spec/rubyspec/core/math/log2_spec.rb create mode 100644 spec/rubyspec/core/math/log_spec.rb create mode 100644 spec/rubyspec/core/math/sin_spec.rb create mode 100644 spec/rubyspec/core/math/sinh_spec.rb create mode 100644 spec/rubyspec/core/math/sqrt_spec.rb create mode 100644 spec/rubyspec/core/math/tan_spec.rb create mode 100644 spec/rubyspec/core/math/tanh_spec.rb create mode 100644 spec/rubyspec/core/method/arity_spec.rb create mode 100644 spec/rubyspec/core/method/call_spec.rb create mode 100644 spec/rubyspec/core/method/clone_spec.rb create mode 100644 spec/rubyspec/core/method/curry_spec.rb create mode 100644 spec/rubyspec/core/method/element_reference_spec.rb create mode 100644 spec/rubyspec/core/method/eql_spec.rb create mode 100644 spec/rubyspec/core/method/equal_value_spec.rb create mode 100644 spec/rubyspec/core/method/fixtures/classes.rb create mode 100644 spec/rubyspec/core/method/hash_spec.rb create mode 100644 spec/rubyspec/core/method/inspect_spec.rb create mode 100644 spec/rubyspec/core/method/name_spec.rb create mode 100644 spec/rubyspec/core/method/owner_spec.rb create mode 100644 spec/rubyspec/core/method/parameters_spec.rb create mode 100644 spec/rubyspec/core/method/receiver_spec.rb create mode 100644 spec/rubyspec/core/method/shared/call.rb create mode 100644 spec/rubyspec/core/method/shared/eql.rb create mode 100644 spec/rubyspec/core/method/shared/to_s.rb create mode 100644 spec/rubyspec/core/method/source_location_spec.rb create mode 100644 spec/rubyspec/core/method/super_method_spec.rb create mode 100644 spec/rubyspec/core/method/to_proc_spec.rb create mode 100644 spec/rubyspec/core/method/to_s_spec.rb create mode 100644 spec/rubyspec/core/method/unbind_spec.rb create mode 100644 spec/rubyspec/core/module/alias_method_spec.rb create mode 100644 spec/rubyspec/core/module/allocate_spec.rb create mode 100644 spec/rubyspec/core/module/ancestors_spec.rb create mode 100644 spec/rubyspec/core/module/append_features_spec.rb create mode 100644 spec/rubyspec/core/module/attr_accessor_spec.rb create mode 100644 spec/rubyspec/core/module/attr_reader_spec.rb create mode 100644 spec/rubyspec/core/module/attr_spec.rb create mode 100644 spec/rubyspec/core/module/attr_writer_spec.rb create mode 100644 spec/rubyspec/core/module/autoload_spec.rb create mode 100644 spec/rubyspec/core/module/case_compare_spec.rb create mode 100644 spec/rubyspec/core/module/class_eval_spec.rb create mode 100644 spec/rubyspec/core/module/class_exec_spec.rb create mode 100644 spec/rubyspec/core/module/class_variable_defined_spec.rb create mode 100644 spec/rubyspec/core/module/class_variable_get_spec.rb create mode 100644 spec/rubyspec/core/module/class_variable_set_spec.rb create mode 100644 spec/rubyspec/core/module/class_variables_spec.rb create mode 100644 spec/rubyspec/core/module/comparison_spec.rb create mode 100644 spec/rubyspec/core/module/const_defined_spec.rb create mode 100644 spec/rubyspec/core/module/const_get_spec.rb create mode 100644 spec/rubyspec/core/module/const_missing_spec.rb create mode 100644 spec/rubyspec/core/module/const_set_spec.rb create mode 100644 spec/rubyspec/core/module/constants_spec.rb create mode 100644 spec/rubyspec/core/module/define_method_spec.rb create mode 100644 spec/rubyspec/core/module/define_singleton_method_spec.rb create mode 100644 spec/rubyspec/core/module/deprecate_constant_spec.rb create mode 100644 spec/rubyspec/core/module/eql_spec.rb create mode 100644 spec/rubyspec/core/module/equal_spec.rb create mode 100644 spec/rubyspec/core/module/equal_value_spec.rb create mode 100644 spec/rubyspec/core/module/extend_object_spec.rb create mode 100644 spec/rubyspec/core/module/extended_spec.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_abc.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_c.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_concur.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_d.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_e.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_empty.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_ex1.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_f.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_g.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_h.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_i.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_j.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_k.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_lm.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_o.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_r.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_s.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_scope.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_subclass.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_t.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_v.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_w.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_w2.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_x.rb create mode 100644 spec/rubyspec/core/module/fixtures/autoload_z.rb create mode 100644 spec/rubyspec/core/module/fixtures/classes.rb create mode 100644 spec/rubyspec/core/module/fixtures/constant_unicode.rb create mode 100644 spec/rubyspec/core/module/fixtures/module.rb create mode 100644 spec/rubyspec/core/module/fixtures/name.rb create mode 100644 spec/rubyspec/core/module/fixtures/path1/load_path.rb create mode 100644 spec/rubyspec/core/module/fixtures/path2/load_path.rb create mode 100644 spec/rubyspec/core/module/fixtures/repeated_concurrent_autoload.rb create mode 100644 spec/rubyspec/core/module/freeze_spec.rb create mode 100644 spec/rubyspec/core/module/gt_spec.rb create mode 100644 spec/rubyspec/core/module/gte_spec.rb create mode 100644 spec/rubyspec/core/module/include_spec.rb create mode 100644 spec/rubyspec/core/module/included_modules_spec.rb create mode 100644 spec/rubyspec/core/module/included_spec.rb create mode 100644 spec/rubyspec/core/module/initialize_copy_spec.rb create mode 100644 spec/rubyspec/core/module/initialize_spec.rb create mode 100644 spec/rubyspec/core/module/instance_method_spec.rb create mode 100644 spec/rubyspec/core/module/instance_methods_spec.rb create mode 100644 spec/rubyspec/core/module/lt_spec.rb create mode 100644 spec/rubyspec/core/module/lte_spec.rb create mode 100644 spec/rubyspec/core/module/method_added_spec.rb create mode 100644 spec/rubyspec/core/module/method_defined_spec.rb create mode 100644 spec/rubyspec/core/module/method_removed_spec.rb create mode 100644 spec/rubyspec/core/module/method_undefined_spec.rb create mode 100644 spec/rubyspec/core/module/module_eval_spec.rb create mode 100644 spec/rubyspec/core/module/module_exec_spec.rb create mode 100644 spec/rubyspec/core/module/module_function_spec.rb create mode 100644 spec/rubyspec/core/module/name_spec.rb create mode 100644 spec/rubyspec/core/module/nesting_spec.rb create mode 100644 spec/rubyspec/core/module/new_spec.rb create mode 100644 spec/rubyspec/core/module/prepend_features_spec.rb create mode 100644 spec/rubyspec/core/module/prepend_spec.rb create mode 100644 spec/rubyspec/core/module/prepended_spec.rb create mode 100644 spec/rubyspec/core/module/private_class_method_spec.rb create mode 100644 spec/rubyspec/core/module/private_constant_spec.rb create mode 100644 spec/rubyspec/core/module/private_instance_methods_spec.rb create mode 100644 spec/rubyspec/core/module/private_method_defined_spec.rb create mode 100644 spec/rubyspec/core/module/private_spec.rb create mode 100644 spec/rubyspec/core/module/protected_instance_methods_spec.rb create mode 100644 spec/rubyspec/core/module/protected_method_defined_spec.rb create mode 100644 spec/rubyspec/core/module/protected_spec.rb create mode 100644 spec/rubyspec/core/module/public_class_method_spec.rb create mode 100644 spec/rubyspec/core/module/public_constant_spec.rb create mode 100644 spec/rubyspec/core/module/public_instance_method_spec.rb create mode 100644 spec/rubyspec/core/module/public_instance_methods_spec.rb create mode 100644 spec/rubyspec/core/module/public_method_defined_spec.rb create mode 100644 spec/rubyspec/core/module/public_spec.rb create mode 100644 spec/rubyspec/core/module/remove_class_variable_spec.rb create mode 100644 spec/rubyspec/core/module/remove_const_spec.rb create mode 100644 spec/rubyspec/core/module/remove_method_spec.rb create mode 100644 spec/rubyspec/core/module/shared/class_eval.rb create mode 100644 spec/rubyspec/core/module/shared/class_exec.rb create mode 100644 spec/rubyspec/core/module/shared/equal_value.rb create mode 100644 spec/rubyspec/core/module/shared/set_visibility.rb create mode 100644 spec/rubyspec/core/module/singleton_class_spec.rb create mode 100644 spec/rubyspec/core/module/to_s_spec.rb create mode 100644 spec/rubyspec/core/module/undef_method_spec.rb create mode 100644 spec/rubyspec/core/mutex/lock_spec.rb create mode 100644 spec/rubyspec/core/mutex/locked_spec.rb create mode 100644 spec/rubyspec/core/mutex/owned_spec.rb create mode 100644 spec/rubyspec/core/mutex/sleep_spec.rb create mode 100644 spec/rubyspec/core/mutex/synchronize_spec.rb create mode 100644 spec/rubyspec/core/mutex/try_lock_spec.rb create mode 100644 spec/rubyspec/core/mutex/unlock_spec.rb create mode 100644 spec/rubyspec/core/nil/and_spec.rb create mode 100644 spec/rubyspec/core/nil/inspect_spec.rb create mode 100644 spec/rubyspec/core/nil/nil_spec.rb create mode 100644 spec/rubyspec/core/nil/or_spec.rb create mode 100644 spec/rubyspec/core/nil/rationalize_spec.rb create mode 100644 spec/rubyspec/core/nil/to_a_spec.rb create mode 100644 spec/rubyspec/core/nil/to_c_spec.rb create mode 100644 spec/rubyspec/core/nil/to_f_spec.rb create mode 100644 spec/rubyspec/core/nil/to_h_spec.rb create mode 100644 spec/rubyspec/core/nil/to_i_spec.rb create mode 100644 spec/rubyspec/core/nil/to_r_spec.rb create mode 100644 spec/rubyspec/core/nil/to_s_spec.rb create mode 100644 spec/rubyspec/core/nil/xor_spec.rb create mode 100644 spec/rubyspec/core/numeric/abs2_spec.rb create mode 100644 spec/rubyspec/core/numeric/abs_spec.rb create mode 100644 spec/rubyspec/core/numeric/angle_spec.rb create mode 100644 spec/rubyspec/core/numeric/arg_spec.rb create mode 100644 spec/rubyspec/core/numeric/ceil_spec.rb create mode 100644 spec/rubyspec/core/numeric/coerce_spec.rb create mode 100644 spec/rubyspec/core/numeric/comparison_spec.rb create mode 100644 spec/rubyspec/core/numeric/conj_spec.rb create mode 100644 spec/rubyspec/core/numeric/conjugate_spec.rb create mode 100644 spec/rubyspec/core/numeric/denominator_spec.rb create mode 100644 spec/rubyspec/core/numeric/div_spec.rb create mode 100644 spec/rubyspec/core/numeric/divmod_spec.rb create mode 100644 spec/rubyspec/core/numeric/eql_spec.rb create mode 100644 spec/rubyspec/core/numeric/fdiv_spec.rb create mode 100644 spec/rubyspec/core/numeric/fixtures/classes.rb create mode 100644 spec/rubyspec/core/numeric/floor_spec.rb create mode 100644 spec/rubyspec/core/numeric/i_spec.rb create mode 100644 spec/rubyspec/core/numeric/imag_spec.rb create mode 100644 spec/rubyspec/core/numeric/imaginary_spec.rb create mode 100644 spec/rubyspec/core/numeric/integer_spec.rb create mode 100644 spec/rubyspec/core/numeric/magnitude_spec.rb create mode 100644 spec/rubyspec/core/numeric/modulo_spec.rb create mode 100644 spec/rubyspec/core/numeric/negative_spec.rb create mode 100644 spec/rubyspec/core/numeric/nonzero_spec.rb create mode 100644 spec/rubyspec/core/numeric/numerator_spec.rb create mode 100644 spec/rubyspec/core/numeric/numeric_spec.rb create mode 100644 spec/rubyspec/core/numeric/phase_spec.rb create mode 100644 spec/rubyspec/core/numeric/polar_spec.rb create mode 100644 spec/rubyspec/core/numeric/positive_spec.rb create mode 100644 spec/rubyspec/core/numeric/quo_spec.rb create mode 100644 spec/rubyspec/core/numeric/real_spec.rb create mode 100644 spec/rubyspec/core/numeric/rect_spec.rb create mode 100644 spec/rubyspec/core/numeric/rectangular_spec.rb create mode 100644 spec/rubyspec/core/numeric/remainder_spec.rb create mode 100644 spec/rubyspec/core/numeric/round_spec.rb create mode 100644 spec/rubyspec/core/numeric/shared/abs.rb create mode 100644 spec/rubyspec/core/numeric/shared/quo.rb create mode 100644 spec/rubyspec/core/numeric/shared/rect.rb create mode 100644 spec/rubyspec/core/numeric/shared/step.rb create mode 100644 spec/rubyspec/core/numeric/singleton_method_added_spec.rb create mode 100644 spec/rubyspec/core/numeric/step_spec.rb create mode 100644 spec/rubyspec/core/numeric/to_c_spec.rb create mode 100644 spec/rubyspec/core/numeric/to_int_spec.rb create mode 100644 spec/rubyspec/core/numeric/truncate_spec.rb create mode 100644 spec/rubyspec/core/numeric/uminus_spec.rb create mode 100644 spec/rubyspec/core/numeric/uplus_spec.rb create mode 100644 spec/rubyspec/core/numeric/zero_spec.rb create mode 100644 spec/rubyspec/core/objectspace/_id2ref_spec.rb create mode 100644 spec/rubyspec/core/objectspace/add_finalizer_spec.rb create mode 100644 spec/rubyspec/core/objectspace/call_finalizer_spec.rb create mode 100644 spec/rubyspec/core/objectspace/count_objects_spec.rb create mode 100644 spec/rubyspec/core/objectspace/define_finalizer_spec.rb create mode 100644 spec/rubyspec/core/objectspace/each_object_spec.rb create mode 100644 spec/rubyspec/core/objectspace/finalizers_spec.rb create mode 100644 spec/rubyspec/core/objectspace/fixtures/classes.rb create mode 100644 spec/rubyspec/core/objectspace/garbage_collect_spec.rb create mode 100644 spec/rubyspec/core/objectspace/remove_finalizer_spec.rb create mode 100644 spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb create mode 100644 spec/rubyspec/core/proc/allocate_spec.rb create mode 100644 spec/rubyspec/core/proc/arity_spec.rb create mode 100644 spec/rubyspec/core/proc/binding_spec.rb create mode 100644 spec/rubyspec/core/proc/block_pass_spec.rb create mode 100644 spec/rubyspec/core/proc/call_spec.rb create mode 100644 spec/rubyspec/core/proc/case_compare_spec.rb create mode 100644 spec/rubyspec/core/proc/clone_spec.rb create mode 100644 spec/rubyspec/core/proc/curry_spec.rb create mode 100644 spec/rubyspec/core/proc/dup_spec.rb create mode 100644 spec/rubyspec/core/proc/element_reference_spec.rb create mode 100644 spec/rubyspec/core/proc/eql_spec.rb create mode 100644 spec/rubyspec/core/proc/equal_value_spec.rb create mode 100644 spec/rubyspec/core/proc/fixtures/common.rb create mode 100644 spec/rubyspec/core/proc/fixtures/source_location.rb create mode 100644 spec/rubyspec/core/proc/hash_spec.rb create mode 100644 spec/rubyspec/core/proc/inspect_spec.rb create mode 100644 spec/rubyspec/core/proc/lambda_spec.rb create mode 100644 spec/rubyspec/core/proc/new_spec.rb create mode 100644 spec/rubyspec/core/proc/parameters_spec.rb create mode 100644 spec/rubyspec/core/proc/shared/call.rb create mode 100644 spec/rubyspec/core/proc/shared/call_arguments.rb create mode 100644 spec/rubyspec/core/proc/shared/dup.rb create mode 100644 spec/rubyspec/core/proc/shared/equal.rb create mode 100644 spec/rubyspec/core/proc/shared/to_s.rb create mode 100644 spec/rubyspec/core/proc/source_location_spec.rb create mode 100644 spec/rubyspec/core/proc/to_proc_spec.rb create mode 100644 spec/rubyspec/core/proc/to_s_spec.rb create mode 100644 spec/rubyspec/core/proc/yield_spec.rb create mode 100644 spec/rubyspec/core/process/abort_spec.rb create mode 100644 spec/rubyspec/core/process/constants_spec.rb create mode 100644 spec/rubyspec/core/process/daemon_spec.rb create mode 100644 spec/rubyspec/core/process/detach_spec.rb create mode 100644 spec/rubyspec/core/process/egid_spec.rb create mode 100644 spec/rubyspec/core/process/euid_spec.rb create mode 100644 spec/rubyspec/core/process/exec_spec.rb create mode 100644 spec/rubyspec/core/process/exit_spec.rb create mode 100644 spec/rubyspec/core/process/fixtures/common.rb create mode 100644 spec/rubyspec/core/process/fixtures/daemon.rb create mode 100644 spec/rubyspec/core/process/fixtures/env.rb create mode 100644 spec/rubyspec/core/process/fixtures/kill.rb create mode 100644 spec/rubyspec/core/process/fixtures/map_fd.rb create mode 100644 spec/rubyspec/core/process/fixtures/print.rb create mode 100644 spec/rubyspec/core/process/fork_spec.rb create mode 100644 spec/rubyspec/core/process/getpgid_spec.rb create mode 100644 spec/rubyspec/core/process/getpgrp_spec.rb create mode 100644 spec/rubyspec/core/process/getpriority_spec.rb create mode 100644 spec/rubyspec/core/process/getrlimit_spec.rb create mode 100644 spec/rubyspec/core/process/gid/change_privilege_spec.rb create mode 100644 spec/rubyspec/core/process/gid/eid_spec.rb create mode 100644 spec/rubyspec/core/process/gid/grant_privilege_spec.rb create mode 100644 spec/rubyspec/core/process/gid/re_exchange_spec.rb create mode 100644 spec/rubyspec/core/process/gid/re_exchangeable_spec.rb create mode 100644 spec/rubyspec/core/process/gid/rid_spec.rb create mode 100644 spec/rubyspec/core/process/gid/sid_available_spec.rb create mode 100644 spec/rubyspec/core/process/gid/switch_spec.rb create mode 100644 spec/rubyspec/core/process/gid_spec.rb create mode 100644 spec/rubyspec/core/process/groups_spec.rb create mode 100644 spec/rubyspec/core/process/initgroups_spec.rb create mode 100644 spec/rubyspec/core/process/kill_spec.rb create mode 100644 spec/rubyspec/core/process/maxgroups_spec.rb create mode 100644 spec/rubyspec/core/process/pid_spec.rb create mode 100644 spec/rubyspec/core/process/ppid_spec.rb create mode 100644 spec/rubyspec/core/process/set_proctitle_spec.rb create mode 100644 spec/rubyspec/core/process/setpgid_spec.rb create mode 100644 spec/rubyspec/core/process/setpgrp_spec.rb create mode 100644 spec/rubyspec/core/process/setpriority_spec.rb create mode 100644 spec/rubyspec/core/process/setrlimit_spec.rb create mode 100644 spec/rubyspec/core/process/setsid_spec.rb create mode 100644 spec/rubyspec/core/process/spawn_spec.rb create mode 100644 spec/rubyspec/core/process/status/bit_and_spec.rb create mode 100644 spec/rubyspec/core/process/status/coredump_spec.rb create mode 100644 spec/rubyspec/core/process/status/equal_value_spec.rb create mode 100644 spec/rubyspec/core/process/status/exited_spec.rb create mode 100644 spec/rubyspec/core/process/status/exitstatus_spec.rb create mode 100644 spec/rubyspec/core/process/status/inspect_spec.rb create mode 100644 spec/rubyspec/core/process/status/pid_spec.rb create mode 100644 spec/rubyspec/core/process/status/right_shift_spec.rb create mode 100644 spec/rubyspec/core/process/status/signaled_spec.rb create mode 100644 spec/rubyspec/core/process/status/stopped_spec.rb create mode 100644 spec/rubyspec/core/process/status/stopsig_spec.rb create mode 100644 spec/rubyspec/core/process/status/success_spec.rb create mode 100644 spec/rubyspec/core/process/status/termsig_spec.rb create mode 100644 spec/rubyspec/core/process/status/to_i_spec.rb create mode 100644 spec/rubyspec/core/process/status/to_int_spec.rb create mode 100644 spec/rubyspec/core/process/status/to_s_spec.rb create mode 100644 spec/rubyspec/core/process/sys/getegid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/geteuid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/getgid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/getuid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/issetugid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setegid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/seteuid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setgid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setregid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setresgid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setresuid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setreuid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setrgid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setruid_spec.rb create mode 100644 spec/rubyspec/core/process/sys/setuid_spec.rb create mode 100644 spec/rubyspec/core/process/times_spec.rb create mode 100644 spec/rubyspec/core/process/uid/change_privilege_spec.rb create mode 100644 spec/rubyspec/core/process/uid/eid_spec.rb create mode 100644 spec/rubyspec/core/process/uid/grant_privilege_spec.rb create mode 100644 spec/rubyspec/core/process/uid/re_exchange_spec.rb create mode 100644 spec/rubyspec/core/process/uid/re_exchangeable_spec.rb create mode 100644 spec/rubyspec/core/process/uid/rid_spec.rb create mode 100644 spec/rubyspec/core/process/uid/sid_available_spec.rb create mode 100644 spec/rubyspec/core/process/uid/switch_spec.rb create mode 100644 spec/rubyspec/core/process/uid_spec.rb create mode 100644 spec/rubyspec/core/process/wait2_spec.rb create mode 100644 spec/rubyspec/core/process/wait_spec.rb create mode 100644 spec/rubyspec/core/process/waitall_spec.rb create mode 100644 spec/rubyspec/core/process/waitpid2_spec.rb create mode 100644 spec/rubyspec/core/process/waitpid_spec.rb create mode 100644 spec/rubyspec/core/random/bytes_spec.rb create mode 100644 spec/rubyspec/core/random/equal_value_spec.rb create mode 100644 spec/rubyspec/core/random/new_seed_spec.rb create mode 100644 spec/rubyspec/core/random/new_spec.rb create mode 100644 spec/rubyspec/core/random/rand_spec.rb create mode 100644 spec/rubyspec/core/random/raw_seed_spec.rb create mode 100644 spec/rubyspec/core/random/seed_spec.rb create mode 100644 spec/rubyspec/core/random/shared/urandom.rb create mode 100644 spec/rubyspec/core/random/srand_spec.rb create mode 100644 spec/rubyspec/core/random/urandom_spec.rb create mode 100644 spec/rubyspec/core/range/begin_spec.rb create mode 100644 spec/rubyspec/core/range/bsearch_spec.rb create mode 100644 spec/rubyspec/core/range/case_compare_spec.rb create mode 100644 spec/rubyspec/core/range/cover_spec.rb create mode 100644 spec/rubyspec/core/range/dup_spec.rb create mode 100644 spec/rubyspec/core/range/each_spec.rb create mode 100644 spec/rubyspec/core/range/end_spec.rb create mode 100644 spec/rubyspec/core/range/eql_spec.rb create mode 100644 spec/rubyspec/core/range/equal_value_spec.rb create mode 100644 spec/rubyspec/core/range/exclude_end_spec.rb create mode 100644 spec/rubyspec/core/range/first_spec.rb create mode 100644 spec/rubyspec/core/range/fixtures/classes.rb create mode 100644 spec/rubyspec/core/range/hash_spec.rb create mode 100644 spec/rubyspec/core/range/include_spec.rb create mode 100644 spec/rubyspec/core/range/initialize_spec.rb create mode 100644 spec/rubyspec/core/range/inspect_spec.rb create mode 100644 spec/rubyspec/core/range/last_spec.rb create mode 100644 spec/rubyspec/core/range/max_spec.rb create mode 100644 spec/rubyspec/core/range/member_spec.rb create mode 100644 spec/rubyspec/core/range/min_spec.rb create mode 100644 spec/rubyspec/core/range/new_spec.rb create mode 100644 spec/rubyspec/core/range/range_spec.rb create mode 100644 spec/rubyspec/core/range/shared/begin.rb create mode 100644 spec/rubyspec/core/range/shared/cover.rb create mode 100644 spec/rubyspec/core/range/shared/cover_and_include.rb create mode 100644 spec/rubyspec/core/range/shared/end.rb create mode 100644 spec/rubyspec/core/range/shared/equal_value.rb create mode 100644 spec/rubyspec/core/range/shared/include.rb create mode 100644 spec/rubyspec/core/range/size_spec.rb create mode 100644 spec/rubyspec/core/range/step_spec.rb create mode 100644 spec/rubyspec/core/range/to_a_spec.rb create mode 100644 spec/rubyspec/core/range/to_s_spec.rb create mode 100644 spec/rubyspec/core/rational/abs_spec.rb create mode 100644 spec/rubyspec/core/rational/ceil_spec.rb create mode 100644 spec/rubyspec/core/rational/coerce_spec.rb create mode 100644 spec/rubyspec/core/rational/comparison_spec.rb create mode 100644 spec/rubyspec/core/rational/denominator_spec.rb create mode 100644 spec/rubyspec/core/rational/div_spec.rb create mode 100644 spec/rubyspec/core/rational/divide_spec.rb create mode 100644 spec/rubyspec/core/rational/divmod_spec.rb create mode 100644 spec/rubyspec/core/rational/equal_value_spec.rb create mode 100644 spec/rubyspec/core/rational/exponent_spec.rb create mode 100644 spec/rubyspec/core/rational/fdiv_spec.rb create mode 100644 spec/rubyspec/core/rational/floor_spec.rb create mode 100644 spec/rubyspec/core/rational/hash_spec.rb create mode 100644 spec/rubyspec/core/rational/inspect_spec.rb create mode 100644 spec/rubyspec/core/rational/integer_spec.rb create mode 100644 spec/rubyspec/core/rational/magnitude_spec.rb create mode 100644 spec/rubyspec/core/rational/marshal_dump_spec.rb create mode 100644 spec/rubyspec/core/rational/minus_spec.rb create mode 100644 spec/rubyspec/core/rational/modulo_spec.rb create mode 100644 spec/rubyspec/core/rational/multiply_spec.rb create mode 100644 spec/rubyspec/core/rational/numerator_spec.rb create mode 100644 spec/rubyspec/core/rational/plus_spec.rb create mode 100644 spec/rubyspec/core/rational/quo_spec.rb create mode 100644 spec/rubyspec/core/rational/rational_spec.rb create mode 100644 spec/rubyspec/core/rational/rationalize_spec.rb create mode 100644 spec/rubyspec/core/rational/remainder_spec.rb create mode 100644 spec/rubyspec/core/rational/round_spec.rb create mode 100644 spec/rubyspec/core/rational/to_f_spec.rb create mode 100644 spec/rubyspec/core/rational/to_i_spec.rb create mode 100644 spec/rubyspec/core/rational/to_r_spec.rb create mode 100644 spec/rubyspec/core/rational/to_s_spec.rb create mode 100644 spec/rubyspec/core/rational/truncate_spec.rb create mode 100644 spec/rubyspec/core/rational/zero_spec.rb create mode 100644 spec/rubyspec/core/regexp/case_compare_spec.rb create mode 100644 spec/rubyspec/core/regexp/casefold_spec.rb create mode 100644 spec/rubyspec/core/regexp/compile_spec.rb create mode 100644 spec/rubyspec/core/regexp/encoding_spec.rb create mode 100644 spec/rubyspec/core/regexp/eql_spec.rb create mode 100644 spec/rubyspec/core/regexp/equal_value_spec.rb create mode 100644 spec/rubyspec/core/regexp/escape_spec.rb create mode 100644 spec/rubyspec/core/regexp/fixed_encoding_spec.rb create mode 100644 spec/rubyspec/core/regexp/hash_spec.rb create mode 100644 spec/rubyspec/core/regexp/initialize_spec.rb create mode 100644 spec/rubyspec/core/regexp/inspect_spec.rb create mode 100644 spec/rubyspec/core/regexp/last_match_spec.rb create mode 100644 spec/rubyspec/core/regexp/match_spec.rb create mode 100644 spec/rubyspec/core/regexp/named_captures_spec.rb create mode 100644 spec/rubyspec/core/regexp/names_spec.rb create mode 100644 spec/rubyspec/core/regexp/new_spec.rb create mode 100644 spec/rubyspec/core/regexp/options_spec.rb create mode 100644 spec/rubyspec/core/regexp/quote_spec.rb create mode 100644 spec/rubyspec/core/regexp/shared/equal_value.rb create mode 100644 spec/rubyspec/core/regexp/shared/new_ascii.rb create mode 100644 spec/rubyspec/core/regexp/shared/new_ascii_8bit.rb create mode 100644 spec/rubyspec/core/regexp/shared/quote.rb create mode 100644 spec/rubyspec/core/regexp/source_spec.rb create mode 100644 spec/rubyspec/core/regexp/to_s_spec.rb create mode 100644 spec/rubyspec/core/regexp/try_convert_spec.rb create mode 100644 spec/rubyspec/core/regexp/union_spec.rb create mode 100644 spec/rubyspec/core/signal/list_spec.rb create mode 100644 spec/rubyspec/core/signal/signame_spec.rb create mode 100644 spec/rubyspec/core/signal/trap_spec.rb create mode 100644 spec/rubyspec/core/string/allocate_spec.rb create mode 100644 spec/rubyspec/core/string/append_spec.rb create mode 100644 spec/rubyspec/core/string/ascii_only_spec.rb create mode 100644 spec/rubyspec/core/string/b_spec.rb create mode 100644 spec/rubyspec/core/string/bytes_spec.rb create mode 100644 spec/rubyspec/core/string/bytesize_spec.rb create mode 100644 spec/rubyspec/core/string/byteslice_spec.rb create mode 100644 spec/rubyspec/core/string/capitalize_spec.rb create mode 100644 spec/rubyspec/core/string/case_compare_spec.rb create mode 100644 spec/rubyspec/core/string/casecmp_spec.rb create mode 100644 spec/rubyspec/core/string/center_spec.rb create mode 100644 spec/rubyspec/core/string/chars_spec.rb create mode 100644 spec/rubyspec/core/string/chomp_spec.rb create mode 100644 spec/rubyspec/core/string/chop_spec.rb create mode 100644 spec/rubyspec/core/string/chr_spec.rb create mode 100644 spec/rubyspec/core/string/clear_spec.rb create mode 100644 spec/rubyspec/core/string/clone_spec.rb create mode 100644 spec/rubyspec/core/string/codepoints_spec.rb create mode 100644 spec/rubyspec/core/string/comparison_spec.rb create mode 100644 spec/rubyspec/core/string/concat_spec.rb create mode 100644 spec/rubyspec/core/string/count_spec.rb create mode 100644 spec/rubyspec/core/string/crypt_spec.rb create mode 100644 spec/rubyspec/core/string/delete_spec.rb create mode 100644 spec/rubyspec/core/string/downcase_spec.rb create mode 100644 spec/rubyspec/core/string/dump_spec.rb create mode 100644 spec/rubyspec/core/string/dup_spec.rb create mode 100644 spec/rubyspec/core/string/each_byte_spec.rb create mode 100644 spec/rubyspec/core/string/each_char_spec.rb create mode 100644 spec/rubyspec/core/string/each_codepoint_spec.rb create mode 100644 spec/rubyspec/core/string/each_line_spec.rb create mode 100644 spec/rubyspec/core/string/element_reference_spec.rb create mode 100644 spec/rubyspec/core/string/element_set_spec.rb create mode 100644 spec/rubyspec/core/string/empty_spec.rb create mode 100644 spec/rubyspec/core/string/encode_spec.rb create mode 100644 spec/rubyspec/core/string/encoding_spec.rb create mode 100644 spec/rubyspec/core/string/end_with_spec.rb create mode 100644 spec/rubyspec/core/string/eql_spec.rb create mode 100644 spec/rubyspec/core/string/equal_value_spec.rb create mode 100644 spec/rubyspec/core/string/fixtures/classes.rb create mode 100644 spec/rubyspec/core/string/fixtures/freeze_magic_comment.rb create mode 100644 spec/rubyspec/core/string/fixtures/iso-8859-9-encoding.rb create mode 100644 spec/rubyspec/core/string/fixtures/utf-8-encoding.rb create mode 100644 spec/rubyspec/core/string/force_encoding_spec.rb create mode 100644 spec/rubyspec/core/string/freeze_spec.rb create mode 100644 spec/rubyspec/core/string/getbyte_spec.rb create mode 100644 spec/rubyspec/core/string/gsub_spec.rb create mode 100644 spec/rubyspec/core/string/hash_spec.rb create mode 100644 spec/rubyspec/core/string/hex_spec.rb create mode 100644 spec/rubyspec/core/string/include_spec.rb create mode 100644 spec/rubyspec/core/string/index_spec.rb create mode 100644 spec/rubyspec/core/string/initialize_spec.rb create mode 100644 spec/rubyspec/core/string/insert_spec.rb create mode 100644 spec/rubyspec/core/string/inspect_spec.rb create mode 100644 spec/rubyspec/core/string/intern_spec.rb create mode 100644 spec/rubyspec/core/string/length_spec.rb create mode 100644 spec/rubyspec/core/string/lines_spec.rb create mode 100644 spec/rubyspec/core/string/ljust_spec.rb create mode 100644 spec/rubyspec/core/string/lstrip_spec.rb create mode 100644 spec/rubyspec/core/string/match_spec.rb create mode 100644 spec/rubyspec/core/string/modulo_spec.rb create mode 100644 spec/rubyspec/core/string/multiply_spec.rb create mode 100644 spec/rubyspec/core/string/new_spec.rb create mode 100644 spec/rubyspec/core/string/next_spec.rb create mode 100644 spec/rubyspec/core/string/oct_spec.rb create mode 100644 spec/rubyspec/core/string/ord_spec.rb create mode 100644 spec/rubyspec/core/string/partition_spec.rb create mode 100644 spec/rubyspec/core/string/plus_spec.rb create mode 100644 spec/rubyspec/core/string/prepend_spec.rb create mode 100644 spec/rubyspec/core/string/replace_spec.rb create mode 100644 spec/rubyspec/core/string/reverse_spec.rb create mode 100644 spec/rubyspec/core/string/rindex_spec.rb create mode 100644 spec/rubyspec/core/string/rjust_spec.rb create mode 100644 spec/rubyspec/core/string/rpartition_spec.rb create mode 100644 spec/rubyspec/core/string/rstrip_spec.rb create mode 100644 spec/rubyspec/core/string/scan_spec.rb create mode 100644 spec/rubyspec/core/string/scrub_spec.rb create mode 100644 spec/rubyspec/core/string/setbyte_spec.rb create mode 100644 spec/rubyspec/core/string/shared/chars.rb create mode 100644 spec/rubyspec/core/string/shared/codepoints.rb create mode 100644 spec/rubyspec/core/string/shared/concat.rb create mode 100644 spec/rubyspec/core/string/shared/each_char_without_block.rb create mode 100644 spec/rubyspec/core/string/shared/each_codepoint_without_block.rb create mode 100644 spec/rubyspec/core/string/shared/each_line.rb create mode 100644 spec/rubyspec/core/string/shared/each_line_without_block.rb create mode 100644 spec/rubyspec/core/string/shared/encode.rb create mode 100644 spec/rubyspec/core/string/shared/eql.rb create mode 100644 spec/rubyspec/core/string/shared/equal_value.rb create mode 100644 spec/rubyspec/core/string/shared/length.rb create mode 100644 spec/rubyspec/core/string/shared/replace.rb create mode 100644 spec/rubyspec/core/string/shared/slice.rb create mode 100644 spec/rubyspec/core/string/shared/succ.rb create mode 100644 spec/rubyspec/core/string/shared/to_a.rb create mode 100644 spec/rubyspec/core/string/shared/to_s.rb create mode 100644 spec/rubyspec/core/string/shared/to_sym.rb create mode 100644 spec/rubyspec/core/string/size_spec.rb create mode 100644 spec/rubyspec/core/string/slice_spec.rb create mode 100644 spec/rubyspec/core/string/split_spec.rb create mode 100644 spec/rubyspec/core/string/squeeze_spec.rb create mode 100644 spec/rubyspec/core/string/start_with_spec.rb create mode 100644 spec/rubyspec/core/string/string_spec.rb create mode 100644 spec/rubyspec/core/string/strip_spec.rb create mode 100644 spec/rubyspec/core/string/sub_spec.rb create mode 100644 spec/rubyspec/core/string/succ_spec.rb create mode 100644 spec/rubyspec/core/string/sum_spec.rb create mode 100644 spec/rubyspec/core/string/swapcase_spec.rb create mode 100644 spec/rubyspec/core/string/to_c_spec.rb create mode 100644 spec/rubyspec/core/string/to_f_spec.rb create mode 100644 spec/rubyspec/core/string/to_i_spec.rb create mode 100644 spec/rubyspec/core/string/to_r_spec.rb create mode 100644 spec/rubyspec/core/string/to_s_spec.rb create mode 100644 spec/rubyspec/core/string/to_str_spec.rb create mode 100644 spec/rubyspec/core/string/to_sym_spec.rb create mode 100644 spec/rubyspec/core/string/tr_s_spec.rb create mode 100644 spec/rubyspec/core/string/tr_spec.rb create mode 100644 spec/rubyspec/core/string/try_convert_spec.rb create mode 100644 spec/rubyspec/core/string/uminus_spec.rb create mode 100644 spec/rubyspec/core/string/unicode_normalize_spec.rb create mode 100644 spec/rubyspec/core/string/unicode_normalized_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/a_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/at_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/b_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/c_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/comment_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/d_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/e_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/f_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/g_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/h_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/i_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/j_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/l_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/m_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/n_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/p_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/percent_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/q_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/s_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/shared/basic.rb create mode 100644 spec/rubyspec/core/string/unpack/shared/float.rb create mode 100644 spec/rubyspec/core/string/unpack/shared/integer.rb create mode 100644 spec/rubyspec/core/string/unpack/shared/string.rb create mode 100644 spec/rubyspec/core/string/unpack/shared/unicode.rb create mode 100644 spec/rubyspec/core/string/unpack/u_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/v_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/w_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/x_spec.rb create mode 100644 spec/rubyspec/core/string/unpack/z_spec.rb create mode 100644 spec/rubyspec/core/string/upcase_spec.rb create mode 100644 spec/rubyspec/core/string/uplus_spec.rb create mode 100644 spec/rubyspec/core/string/upto_spec.rb create mode 100644 spec/rubyspec/core/string/valid_encoding_spec.rb create mode 100644 spec/rubyspec/core/struct/dig_spec.rb create mode 100644 spec/rubyspec/core/struct/dup_spec.rb create mode 100644 spec/rubyspec/core/struct/each_pair_spec.rb create mode 100644 spec/rubyspec/core/struct/each_spec.rb create mode 100644 spec/rubyspec/core/struct/element_reference_spec.rb create mode 100644 spec/rubyspec/core/struct/element_set_spec.rb create mode 100644 spec/rubyspec/core/struct/eql_spec.rb create mode 100644 spec/rubyspec/core/struct/equal_value_spec.rb create mode 100644 spec/rubyspec/core/struct/fixtures/classes.rb create mode 100644 spec/rubyspec/core/struct/hash_spec.rb create mode 100644 spec/rubyspec/core/struct/initialize_spec.rb create mode 100644 spec/rubyspec/core/struct/inspect_spec.rb create mode 100644 spec/rubyspec/core/struct/instance_variables_spec.rb create mode 100644 spec/rubyspec/core/struct/length_spec.rb create mode 100644 spec/rubyspec/core/struct/members_spec.rb create mode 100644 spec/rubyspec/core/struct/new_spec.rb create mode 100644 spec/rubyspec/core/struct/select_spec.rb create mode 100644 spec/rubyspec/core/struct/shared/accessor.rb create mode 100644 spec/rubyspec/core/struct/shared/equal_value.rb create mode 100644 spec/rubyspec/core/struct/shared/inspect.rb create mode 100644 spec/rubyspec/core/struct/size_spec.rb create mode 100644 spec/rubyspec/core/struct/struct_spec.rb create mode 100644 spec/rubyspec/core/struct/tms/cstime_spec.rb create mode 100644 spec/rubyspec/core/struct/tms/cutime_spec.rb create mode 100644 spec/rubyspec/core/struct/tms/element_reference_spec.rb create mode 100644 spec/rubyspec/core/struct/tms/members_spec.rb create mode 100644 spec/rubyspec/core/struct/tms/new_spec.rb create mode 100644 spec/rubyspec/core/struct/tms/stime_spec.rb create mode 100644 spec/rubyspec/core/struct/tms/utime_spec.rb create mode 100644 spec/rubyspec/core/struct/to_a_spec.rb create mode 100644 spec/rubyspec/core/struct/to_h_spec.rb create mode 100644 spec/rubyspec/core/struct/to_s_spec.rb create mode 100644 spec/rubyspec/core/struct/values_at_spec.rb create mode 100644 spec/rubyspec/core/struct/values_spec.rb create mode 100644 spec/rubyspec/core/symbol/all_symbols_spec.rb create mode 100644 spec/rubyspec/core/symbol/capitalize_spec.rb create mode 100644 spec/rubyspec/core/symbol/case_compare_spec.rb create mode 100644 spec/rubyspec/core/symbol/casecmp_spec.rb create mode 100644 spec/rubyspec/core/symbol/comparison_spec.rb create mode 100644 spec/rubyspec/core/symbol/downcase_spec.rb create mode 100644 spec/rubyspec/core/symbol/element_reference_spec.rb create mode 100644 spec/rubyspec/core/symbol/empty_spec.rb create mode 100644 spec/rubyspec/core/symbol/encoding_spec.rb create mode 100644 spec/rubyspec/core/symbol/equal_value_spec.rb create mode 100644 spec/rubyspec/core/symbol/fixtures/classes.rb create mode 100644 spec/rubyspec/core/symbol/id2name_spec.rb create mode 100644 spec/rubyspec/core/symbol/inspect_spec.rb create mode 100644 spec/rubyspec/core/symbol/intern_spec.rb create mode 100644 spec/rubyspec/core/symbol/length_spec.rb create mode 100644 spec/rubyspec/core/symbol/match_spec.rb create mode 100644 spec/rubyspec/core/symbol/next_spec.rb create mode 100644 spec/rubyspec/core/symbol/shared/id2name.rb create mode 100644 spec/rubyspec/core/symbol/shared/length.rb create mode 100644 spec/rubyspec/core/symbol/shared/slice.rb create mode 100644 spec/rubyspec/core/symbol/shared/succ.rb create mode 100644 spec/rubyspec/core/symbol/size_spec.rb create mode 100644 spec/rubyspec/core/symbol/slice_spec.rb create mode 100644 spec/rubyspec/core/symbol/succ_spec.rb create mode 100644 spec/rubyspec/core/symbol/swapcase_spec.rb create mode 100644 spec/rubyspec/core/symbol/symbol_spec.rb create mode 100644 spec/rubyspec/core/symbol/to_proc_spec.rb create mode 100644 spec/rubyspec/core/symbol/to_s_spec.rb create mode 100644 spec/rubyspec/core/symbol/to_sym_spec.rb create mode 100644 spec/rubyspec/core/symbol/upcase_spec.rb create mode 100644 spec/rubyspec/core/systemexit/initialize_spec.rb create mode 100644 spec/rubyspec/core/systemexit/success_spec.rb create mode 100644 spec/rubyspec/core/thread/abort_on_exception_spec.rb create mode 100644 spec/rubyspec/core/thread/add_trace_func_spec.rb create mode 100644 spec/rubyspec/core/thread/alive_spec.rb create mode 100644 spec/rubyspec/core/thread/allocate_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/absolute_path_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/base_label_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/fixtures/classes.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/fixtures/main.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/inspect_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/label_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/lineno_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/path_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace/location/to_s_spec.rb create mode 100644 spec/rubyspec/core/thread/backtrace_spec.rb create mode 100644 spec/rubyspec/core/thread/current_spec.rb create mode 100644 spec/rubyspec/core/thread/element_reference_spec.rb create mode 100644 spec/rubyspec/core/thread/element_set_spec.rb create mode 100644 spec/rubyspec/core/thread/exclusive_spec.rb create mode 100644 spec/rubyspec/core/thread/exit_spec.rb create mode 100644 spec/rubyspec/core/thread/fixtures/classes.rb create mode 100644 spec/rubyspec/core/thread/fork_spec.rb create mode 100644 spec/rubyspec/core/thread/group_spec.rb create mode 100644 spec/rubyspec/core/thread/initialize_spec.rb create mode 100644 spec/rubyspec/core/thread/inspect_spec.rb create mode 100644 spec/rubyspec/core/thread/join_spec.rb create mode 100644 spec/rubyspec/core/thread/key_spec.rb create mode 100644 spec/rubyspec/core/thread/keys_spec.rb create mode 100644 spec/rubyspec/core/thread/kill_spec.rb create mode 100644 spec/rubyspec/core/thread/list_spec.rb create mode 100644 spec/rubyspec/core/thread/main_spec.rb create mode 100644 spec/rubyspec/core/thread/name_spec.rb create mode 100644 spec/rubyspec/core/thread/new_spec.rb create mode 100644 spec/rubyspec/core/thread/pass_spec.rb create mode 100644 spec/rubyspec/core/thread/priority_spec.rb create mode 100644 spec/rubyspec/core/thread/raise_spec.rb create mode 100644 spec/rubyspec/core/thread/run_spec.rb create mode 100644 spec/rubyspec/core/thread/set_trace_func_spec.rb create mode 100644 spec/rubyspec/core/thread/shared/exit.rb create mode 100644 spec/rubyspec/core/thread/shared/start.rb create mode 100644 spec/rubyspec/core/thread/shared/wakeup.rb create mode 100644 spec/rubyspec/core/thread/start_spec.rb create mode 100644 spec/rubyspec/core/thread/status_spec.rb create mode 100644 spec/rubyspec/core/thread/stop_spec.rb create mode 100644 spec/rubyspec/core/thread/terminate_spec.rb create mode 100644 spec/rubyspec/core/thread/thread_variable_get_spec.rb create mode 100644 spec/rubyspec/core/thread/thread_variable_set_spec.rb create mode 100644 spec/rubyspec/core/thread/thread_variable_spec.rb create mode 100644 spec/rubyspec/core/thread/thread_variables_spec.rb create mode 100644 spec/rubyspec/core/thread/value_spec.rb create mode 100644 spec/rubyspec/core/thread/wakeup_spec.rb create mode 100644 spec/rubyspec/core/threadgroup/add_spec.rb create mode 100644 spec/rubyspec/core/threadgroup/default_spec.rb create mode 100644 spec/rubyspec/core/threadgroup/enclose_spec.rb create mode 100644 spec/rubyspec/core/threadgroup/enclosed_spec.rb create mode 100644 spec/rubyspec/core/threadgroup/fixtures/classes.rb create mode 100644 spec/rubyspec/core/threadgroup/list_spec.rb create mode 100644 spec/rubyspec/core/time/_dump_spec.rb create mode 100644 spec/rubyspec/core/time/_load_spec.rb create mode 100644 spec/rubyspec/core/time/asctime_spec.rb create mode 100644 spec/rubyspec/core/time/at_spec.rb create mode 100644 spec/rubyspec/core/time/comparison_spec.rb create mode 100644 spec/rubyspec/core/time/ctime_spec.rb create mode 100644 spec/rubyspec/core/time/day_spec.rb create mode 100644 spec/rubyspec/core/time/dst_spec.rb create mode 100644 spec/rubyspec/core/time/dup_spec.rb create mode 100644 spec/rubyspec/core/time/eql_spec.rb create mode 100644 spec/rubyspec/core/time/fixtures/classes.rb create mode 100644 spec/rubyspec/core/time/friday_spec.rb create mode 100644 spec/rubyspec/core/time/getgm_spec.rb create mode 100644 spec/rubyspec/core/time/getlocal_spec.rb create mode 100644 spec/rubyspec/core/time/getutc_spec.rb create mode 100644 spec/rubyspec/core/time/gm_spec.rb create mode 100644 spec/rubyspec/core/time/gmt_offset_spec.rb create mode 100644 spec/rubyspec/core/time/gmt_spec.rb create mode 100644 spec/rubyspec/core/time/gmtime_spec.rb create mode 100644 spec/rubyspec/core/time/gmtoff_spec.rb create mode 100644 spec/rubyspec/core/time/hash_spec.rb create mode 100644 spec/rubyspec/core/time/hour_spec.rb create mode 100644 spec/rubyspec/core/time/inspect_spec.rb create mode 100644 spec/rubyspec/core/time/isdst_spec.rb create mode 100644 spec/rubyspec/core/time/local_spec.rb create mode 100644 spec/rubyspec/core/time/localtime_spec.rb create mode 100644 spec/rubyspec/core/time/mday_spec.rb create mode 100644 spec/rubyspec/core/time/min_spec.rb create mode 100644 spec/rubyspec/core/time/minus_spec.rb create mode 100644 spec/rubyspec/core/time/mktime_spec.rb create mode 100644 spec/rubyspec/core/time/mon_spec.rb create mode 100644 spec/rubyspec/core/time/monday_spec.rb create mode 100644 spec/rubyspec/core/time/month_spec.rb create mode 100644 spec/rubyspec/core/time/new_spec.rb create mode 100644 spec/rubyspec/core/time/now_spec.rb create mode 100644 spec/rubyspec/core/time/nsec_spec.rb create mode 100644 spec/rubyspec/core/time/plus_spec.rb create mode 100644 spec/rubyspec/core/time/round_spec.rb create mode 100644 spec/rubyspec/core/time/saturday_spec.rb create mode 100644 spec/rubyspec/core/time/sec_spec.rb create mode 100644 spec/rubyspec/core/time/shared/asctime.rb create mode 100644 spec/rubyspec/core/time/shared/day.rb create mode 100644 spec/rubyspec/core/time/shared/getgm.rb create mode 100644 spec/rubyspec/core/time/shared/gm.rb create mode 100644 spec/rubyspec/core/time/shared/gmt_offset.rb create mode 100644 spec/rubyspec/core/time/shared/gmtime.rb create mode 100644 spec/rubyspec/core/time/shared/inspect.rb create mode 100644 spec/rubyspec/core/time/shared/isdst.rb create mode 100644 spec/rubyspec/core/time/shared/local.rb create mode 100644 spec/rubyspec/core/time/shared/month.rb create mode 100644 spec/rubyspec/core/time/shared/now.rb create mode 100644 spec/rubyspec/core/time/shared/time_params.rb create mode 100644 spec/rubyspec/core/time/shared/to_i.rb create mode 100644 spec/rubyspec/core/time/strftime_spec.rb create mode 100644 spec/rubyspec/core/time/subsec_spec.rb create mode 100644 spec/rubyspec/core/time/succ_spec.rb create mode 100644 spec/rubyspec/core/time/sunday_spec.rb create mode 100644 spec/rubyspec/core/time/thursday_spec.rb create mode 100644 spec/rubyspec/core/time/time_spec.rb create mode 100644 spec/rubyspec/core/time/to_a_spec.rb create mode 100644 spec/rubyspec/core/time/to_f_spec.rb create mode 100644 spec/rubyspec/core/time/to_i_spec.rb create mode 100644 spec/rubyspec/core/time/to_r_spec.rb create mode 100644 spec/rubyspec/core/time/to_s_spec.rb create mode 100644 spec/rubyspec/core/time/tuesday_spec.rb create mode 100644 spec/rubyspec/core/time/tv_nsec_spec.rb create mode 100644 spec/rubyspec/core/time/tv_sec_spec.rb create mode 100644 spec/rubyspec/core/time/tv_usec_spec.rb create mode 100644 spec/rubyspec/core/time/usec_spec.rb create mode 100644 spec/rubyspec/core/time/utc_offset_spec.rb create mode 100644 spec/rubyspec/core/time/utc_spec.rb create mode 100644 spec/rubyspec/core/time/wday_spec.rb create mode 100644 spec/rubyspec/core/time/wednesday_spec.rb create mode 100644 spec/rubyspec/core/time/yday_spec.rb create mode 100644 spec/rubyspec/core/time/year_spec.rb create mode 100644 spec/rubyspec/core/time/zone_spec.rb create mode 100644 spec/rubyspec/core/true/and_spec.rb create mode 100644 spec/rubyspec/core/true/inspect_spec.rb create mode 100644 spec/rubyspec/core/true/or_spec.rb create mode 100644 spec/rubyspec/core/true/to_s_spec.rb create mode 100644 spec/rubyspec/core/true/xor_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/arity_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/bind_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/clone_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/eql_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/equal_value_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/fixtures/classes.rb create mode 100644 spec/rubyspec/core/unboundmethod/hash_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/inspect_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/name_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/owner_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/parameters_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/shared/to_s.rb create mode 100644 spec/rubyspec/core/unboundmethod/source_location_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/super_method_spec.rb create mode 100644 spec/rubyspec/core/unboundmethod/to_s_spec.rb create mode 100644 spec/rubyspec/default.mspec create mode 100644 spec/rubyspec/fixtures/basicobject/method_missing.rb create mode 100644 spec/rubyspec/fixtures/class.rb create mode 100644 spec/rubyspec/fixtures/class_variables.rb create mode 100644 spec/rubyspec/fixtures/code/a/load_fixture.bundle create mode 100644 spec/rubyspec/fixtures/code/a/load_fixture.dll create mode 100644 spec/rubyspec/fixtures/code/a/load_fixture.so create mode 100644 spec/rubyspec/fixtures/code/b/load_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/concurrent.rb create mode 100644 spec/rubyspec/fixtures/code/concurrent2.rb create mode 100644 spec/rubyspec/fixtures/code/concurrent3.rb create mode 100644 spec/rubyspec/fixtures/code/file_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/gem/load_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/line_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/load_ext_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/load_fixture create mode 100644 spec/rubyspec/fixtures/code/load_fixture.bundle create mode 100644 spec/rubyspec/fixtures/code/load_fixture.dll create mode 100644 spec/rubyspec/fixtures/code/load_fixture.ext create mode 100644 spec/rubyspec/fixtures/code/load_fixture.ext.bundle create mode 100644 spec/rubyspec/fixtures/code/load_fixture.ext.dll create mode 100644 spec/rubyspec/fixtures/code/load_fixture.ext.rb create mode 100644 spec/rubyspec/fixtures/code/load_fixture.ext.so create mode 100644 spec/rubyspec/fixtures/code/load_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/load_fixture.so create mode 100644 spec/rubyspec/fixtures/code/load_wrap_method_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/methods_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/raise_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/recursive_load_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/recursive_require_fixture.rb create mode 100644 spec/rubyspec/fixtures/code/symlink/symlink1.rb create mode 100644 spec/rubyspec/fixtures/code/symlink/symlink2/symlink2.rb create mode 100644 spec/rubyspec/fixtures/code/wrap_fixture.rb create mode 100644 spec/rubyspec/fixtures/code_loading.rb create mode 100644 spec/rubyspec/fixtures/constants.rb create mode 100644 spec/rubyspec/fixtures/enumerator/classes.rb create mode 100644 spec/rubyspec/fixtures/math/common.rb create mode 100644 spec/rubyspec/fixtures/rational.rb create mode 100644 spec/rubyspec/fixtures/reflection.rb create mode 100644 spec/rubyspec/language/BEGIN_spec.rb create mode 100644 spec/rubyspec/language/README create mode 100644 spec/rubyspec/language/alias_spec.rb create mode 100644 spec/rubyspec/language/and_spec.rb create mode 100644 spec/rubyspec/language/array_spec.rb create mode 100644 spec/rubyspec/language/block_spec.rb create mode 100644 spec/rubyspec/language/break_spec.rb create mode 100644 spec/rubyspec/language/case_spec.rb create mode 100644 spec/rubyspec/language/class_spec.rb create mode 100644 spec/rubyspec/language/class_variable_spec.rb create mode 100644 spec/rubyspec/language/constants_spec.rb create mode 100644 spec/rubyspec/language/def_spec.rb create mode 100644 spec/rubyspec/language/defined_spec.rb create mode 100644 spec/rubyspec/language/encoding_spec.rb create mode 100644 spec/rubyspec/language/ensure_spec.rb create mode 100644 spec/rubyspec/language/execution_spec.rb create mode 100644 spec/rubyspec/language/file_spec.rb create mode 100644 spec/rubyspec/language/fixtures/argv_encoding.rb create mode 100644 spec/rubyspec/language/fixtures/array.rb create mode 100644 spec/rubyspec/language/fixtures/block.rb create mode 100644 spec/rubyspec/language/fixtures/break.rb create mode 100644 spec/rubyspec/language/fixtures/break_lambda_toplevel.rb create mode 100644 spec/rubyspec/language/fixtures/break_lambda_toplevel_block.rb create mode 100644 spec/rubyspec/language/fixtures/break_lambda_toplevel_method.rb create mode 100644 spec/rubyspec/language/fixtures/classes.rb create mode 100644 spec/rubyspec/language/fixtures/coding_us_ascii.rb create mode 100644 spec/rubyspec/language/fixtures/coding_utf_8.rb create mode 100644 spec/rubyspec/language/fixtures/constant_visibility.rb create mode 100644 spec/rubyspec/language/fixtures/constants_sclass.rb create mode 100644 spec/rubyspec/language/fixtures/def.rb create mode 100644 spec/rubyspec/language/fixtures/defined.rb create mode 100644 spec/rubyspec/language/fixtures/dollar_zero.rb create mode 100644 spec/rubyspec/language/fixtures/ensure.rb create mode 100644 spec/rubyspec/language/fixtures/file.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_across_files.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_no_comment.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_one_literal.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_required.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_required_diff_enc.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_required_no_comment.rb create mode 100644 spec/rubyspec/language/fixtures/freeze_magic_comment_two_literals.rb create mode 100644 spec/rubyspec/language/fixtures/hash_strings_ascii8bit.rb create mode 100644 spec/rubyspec/language/fixtures/hash_strings_usascii.rb create mode 100644 spec/rubyspec/language/fixtures/hash_strings_utf8.rb create mode 100644 spec/rubyspec/language/fixtures/match_operators.rb create mode 100644 spec/rubyspec/language/fixtures/metaclass.rb create mode 100644 spec/rubyspec/language/fixtures/module.rb create mode 100644 spec/rubyspec/language/fixtures/next.rb create mode 100644 spec/rubyspec/language/fixtures/precedence.rb create mode 100644 spec/rubyspec/language/fixtures/private.rb create mode 100644 spec/rubyspec/language/fixtures/rescue.rb create mode 100644 spec/rubyspec/language/fixtures/return.rb create mode 100644 spec/rubyspec/language/fixtures/send.rb create mode 100644 spec/rubyspec/language/fixtures/squiggly_heredoc.rb create mode 100644 spec/rubyspec/language/fixtures/super.rb create mode 100644 spec/rubyspec/language/fixtures/variables.rb create mode 100644 spec/rubyspec/language/fixtures/yield.rb create mode 100644 spec/rubyspec/language/for_spec.rb create mode 100644 spec/rubyspec/language/hash_spec.rb create mode 100644 spec/rubyspec/language/heredoc_spec.rb create mode 100644 spec/rubyspec/language/if_spec.rb create mode 100644 spec/rubyspec/language/lambda_spec.rb create mode 100644 spec/rubyspec/language/line_spec.rb create mode 100644 spec/rubyspec/language/loop_spec.rb create mode 100644 spec/rubyspec/language/magic_comment_spec.rb create mode 100644 spec/rubyspec/language/match_spec.rb create mode 100644 spec/rubyspec/language/metaclass_spec.rb create mode 100644 spec/rubyspec/language/method_spec.rb create mode 100644 spec/rubyspec/language/module_spec.rb create mode 100644 spec/rubyspec/language/next_spec.rb create mode 100644 spec/rubyspec/language/not_spec.rb create mode 100644 spec/rubyspec/language/numbers_spec.rb create mode 100644 spec/rubyspec/language/optional_assignments_spec.rb create mode 100644 spec/rubyspec/language/or_spec.rb create mode 100644 spec/rubyspec/language/order_spec.rb create mode 100644 spec/rubyspec/language/precedence_spec.rb create mode 100644 spec/rubyspec/language/predefined/data_spec.rb create mode 100644 spec/rubyspec/language/predefined/fixtures/data1.rb create mode 100644 spec/rubyspec/language/predefined/fixtures/data2.rb create mode 100644 spec/rubyspec/language/predefined/fixtures/data3.rb create mode 100644 spec/rubyspec/language/predefined/fixtures/data4.rb create mode 100644 spec/rubyspec/language/predefined/fixtures/data5.rb create mode 100644 spec/rubyspec/language/predefined/fixtures/data_only.rb create mode 100644 spec/rubyspec/language/predefined/fixtures/print_data.rb create mode 100644 spec/rubyspec/language/predefined_spec.rb create mode 100644 spec/rubyspec/language/private_spec.rb create mode 100644 spec/rubyspec/language/proc_spec.rb create mode 100644 spec/rubyspec/language/redo_spec.rb create mode 100644 spec/rubyspec/language/regexp/anchors_spec.rb create mode 100644 spec/rubyspec/language/regexp/back-references_spec.rb create mode 100644 spec/rubyspec/language/regexp/character_classes_spec.rb create mode 100644 spec/rubyspec/language/regexp/encoding_spec.rb create mode 100644 spec/rubyspec/language/regexp/escapes_spec.rb create mode 100644 spec/rubyspec/language/regexp/grouping_spec.rb create mode 100644 spec/rubyspec/language/regexp/interpolation_spec.rb create mode 100644 spec/rubyspec/language/regexp/modifiers_spec.rb create mode 100644 spec/rubyspec/language/regexp/repetition_spec.rb create mode 100644 spec/rubyspec/language/regexp_spec.rb create mode 100644 spec/rubyspec/language/rescue_spec.rb create mode 100644 spec/rubyspec/language/retry_spec.rb create mode 100644 spec/rubyspec/language/return_spec.rb create mode 100644 spec/rubyspec/language/safe_navigator_spec.rb create mode 100644 spec/rubyspec/language/send_spec.rb create mode 100644 spec/rubyspec/language/shared/__FILE__.rb create mode 100644 spec/rubyspec/language/shared/__LINE__.rb create mode 100644 spec/rubyspec/language/singleton_class_spec.rb create mode 100644 spec/rubyspec/language/string_spec.rb create mode 100644 spec/rubyspec/language/super_spec.rb create mode 100644 spec/rubyspec/language/symbol_spec.rb create mode 100644 spec/rubyspec/language/throw_spec.rb create mode 100644 spec/rubyspec/language/undef_spec.rb create mode 100644 spec/rubyspec/language/unless_spec.rb create mode 100644 spec/rubyspec/language/until_spec.rb create mode 100644 spec/rubyspec/language/variables_spec.rb create mode 100644 spec/rubyspec/language/while_spec.rb create mode 100644 spec/rubyspec/language/yield_spec.rb create mode 100644 spec/rubyspec/library/English/English_spec.rb create mode 100644 spec/rubyspec/library/abbrev/abbrev_spec.rb create mode 100644 spec/rubyspec/library/base64/decode64_spec.rb create mode 100644 spec/rubyspec/library/base64/encode64_spec.rb create mode 100644 spec/rubyspec/library/base64/urlsafe_decode64_spec.rb create mode 100644 spec/rubyspec/library/base64/urlsafe_encode64_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/abs_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/add_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/case_compare_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/ceil_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/coerce_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/comparison_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/div_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/divide_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/divmod_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/double_fig_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/eql_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/equal_value_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/exponent_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/finite_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/fix_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/fixtures/classes.rb create mode 100644 spec/rubyspec/library/bigdecimal/floor_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/frac_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/gt_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/gte_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/infinite_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/inspect_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/limit_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/lt_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/lte_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/minus_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/mode_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/modulo_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/mult_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/multiply_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/nan_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/new_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/nonzero_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/plus_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/power_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/precs_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/quo_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/remainder_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/round_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/shared/eql.rb create mode 100644 spec/rubyspec/library/bigdecimal/shared/modulo.rb create mode 100644 spec/rubyspec/library/bigdecimal/shared/mult.rb create mode 100644 spec/rubyspec/library/bigdecimal/shared/power.rb create mode 100644 spec/rubyspec/library/bigdecimal/shared/quo.rb create mode 100644 spec/rubyspec/library/bigdecimal/shared/to_int.rb create mode 100644 spec/rubyspec/library/bigdecimal/sign_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/split_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/sqrt_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/sub_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/to_f_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/to_i_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/to_int_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/to_s_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/truncate_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/uminus_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/uplus_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/ver_spec.rb create mode 100644 spec/rubyspec/library/bigdecimal/zero_spec.rb create mode 100644 spec/rubyspec/library/bigmath/log_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/domain_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/expires_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/initialize_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/name_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/parse_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/path_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/secure_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/to_s_spec.rb create mode 100644 spec/rubyspec/library/cgi/cookie/value_spec.rb create mode 100644 spec/rubyspec/library/cgi/escapeElement_spec.rb create mode 100644 spec/rubyspec/library/cgi/escapeHTML_spec.rb create mode 100644 spec/rubyspec/library/cgi/escape_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/a_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/base_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/blockquote_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/br_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/caption_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/checkbox_group_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/checkbox_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/doctype_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/file_field_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/fixtures/common.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/form_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/frame_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/frameset_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/hidden_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/html_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/image_button_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/img_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/multipart_form_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/password_field_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/popup_menu_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/radio_button_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/radio_group_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/reset_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/scrolling_list_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/shared/popup_menu.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/submit_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/text_field_spec.rb create mode 100644 spec/rubyspec/library/cgi/htmlextension/textarea_spec.rb create mode 100644 spec/rubyspec/library/cgi/http_header_spec.rb create mode 100644 spec/rubyspec/library/cgi/initialize_spec.rb create mode 100644 spec/rubyspec/library/cgi/out_spec.rb create mode 100644 spec/rubyspec/library/cgi/parse_spec.rb create mode 100644 spec/rubyspec/library/cgi/pretty_spec.rb create mode 100644 spec/rubyspec/library/cgi/print_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/accept_charset_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/accept_encoding_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/accept_language_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/accept_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/auth_type_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/cache_control_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/content_length_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/content_type_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/cookies_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/element_reference_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/from_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/gateway_interface_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/has_key_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/host_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/include_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/key_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/keys_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/multipart_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/negotiate_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/params_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/path_info_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/path_translated_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/pragma_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/query_string_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/raw_cookie2_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/raw_cookie_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/referer_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/remote_addr_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/remote_host_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/remote_ident_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/remote_user_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/request_method_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/script_name_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/server_name_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/server_port_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/server_protocol_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/server_software_spec.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/shared/has_key.rb create mode 100644 spec/rubyspec/library/cgi/queryextension/user_agent_spec.rb create mode 100644 spec/rubyspec/library/cgi/rfc1123_date_spec.rb create mode 100644 spec/rubyspec/library/cgi/shared/http_header.rb create mode 100644 spec/rubyspec/library/cgi/unescapeElement_spec.rb create mode 100644 spec/rubyspec/library/cgi/unescapeHTML_spec.rb create mode 100644 spec/rubyspec/library/cgi/unescape_spec.rb create mode 100644 spec/rubyspec/library/complex/math/acos_spec.rb create mode 100644 spec/rubyspec/library/complex/math/acosh_spec.rb create mode 100644 spec/rubyspec/library/complex/math/asin_spec.rb create mode 100644 spec/rubyspec/library/complex/math/asinh_spec.rb create mode 100644 spec/rubyspec/library/complex/math/atan2_spec.rb create mode 100644 spec/rubyspec/library/complex/math/atan_spec.rb create mode 100644 spec/rubyspec/library/complex/math/atanh_spec.rb create mode 100644 spec/rubyspec/library/complex/math/cos_spec.rb create mode 100644 spec/rubyspec/library/complex/math/cosh_spec.rb create mode 100644 spec/rubyspec/library/complex/math/exp_spec.rb create mode 100644 spec/rubyspec/library/complex/math/fixtures/classes.rb create mode 100644 spec/rubyspec/library/complex/math/log10_spec.rb create mode 100644 spec/rubyspec/library/complex/math/log_spec.rb create mode 100644 spec/rubyspec/library/complex/math/shared/acos.rb create mode 100644 spec/rubyspec/library/complex/math/shared/acosh.rb create mode 100644 spec/rubyspec/library/complex/math/shared/asin.rb create mode 100644 spec/rubyspec/library/complex/math/shared/asinh.rb create mode 100644 spec/rubyspec/library/complex/math/shared/atan.rb create mode 100644 spec/rubyspec/library/complex/math/shared/atan2.rb create mode 100644 spec/rubyspec/library/complex/math/shared/atanh.rb create mode 100644 spec/rubyspec/library/complex/math/shared/cos.rb create mode 100644 spec/rubyspec/library/complex/math/shared/cosh.rb create mode 100644 spec/rubyspec/library/complex/math/shared/exp.rb create mode 100644 spec/rubyspec/library/complex/math/shared/log.rb create mode 100644 spec/rubyspec/library/complex/math/shared/log10.rb create mode 100644 spec/rubyspec/library/complex/math/shared/sin.rb create mode 100644 spec/rubyspec/library/complex/math/shared/sinh.rb create mode 100644 spec/rubyspec/library/complex/math/shared/sqrt.rb create mode 100644 spec/rubyspec/library/complex/math/shared/tan.rb create mode 100644 spec/rubyspec/library/complex/math/shared/tanh.rb create mode 100644 spec/rubyspec/library/complex/math/sin_spec.rb create mode 100644 spec/rubyspec/library/complex/math/sinh_spec.rb create mode 100644 spec/rubyspec/library/complex/math/sqrt_spec.rb create mode 100644 spec/rubyspec/library/complex/math/tan_spec.rb create mode 100644 spec/rubyspec/library/complex/math/tanh_spec.rb create mode 100644 spec/rubyspec/library/complex/numeric/im_spec.rb create mode 100644 spec/rubyspec/library/conditionvariable/broadcast_spec.rb create mode 100644 spec/rubyspec/library/conditionvariable/signal_spec.rb create mode 100644 spec/rubyspec/library/conditionvariable/wait_spec.rb create mode 100644 spec/rubyspec/library/coverage/fixtures/second_class.rb create mode 100644 spec/rubyspec/library/coverage/fixtures/some_class.rb create mode 100644 spec/rubyspec/library/coverage/fixtures/spec_helper.rb create mode 100644 spec/rubyspec/library/coverage/fixtures/start_coverage.rb create mode 100644 spec/rubyspec/library/coverage/peek_result_spec.rb create mode 100644 spec/rubyspec/library/coverage/result_spec.rb create mode 100644 spec/rubyspec/library/coverage/start_spec.rb create mode 100644 spec/rubyspec/library/csv/basicwriter/close_on_terminate_spec.rb create mode 100644 spec/rubyspec/library/csv/basicwriter/initialize_spec.rb create mode 100644 spec/rubyspec/library/csv/basicwriter/terminate_spec.rb create mode 100644 spec/rubyspec/library/csv/cell/data_spec.rb create mode 100644 spec/rubyspec/library/csv/cell/initialize_spec.rb create mode 100644 spec/rubyspec/library/csv/fixtures/one_line.csv create mode 100644 spec/rubyspec/library/csv/foreach_spec.rb create mode 100644 spec/rubyspec/library/csv/generate_line_spec.rb create mode 100644 spec/rubyspec/library/csv/generate_row_spec.rb create mode 100644 spec/rubyspec/library/csv/generate_spec.rb create mode 100644 spec/rubyspec/library/csv/iobuf/close_spec.rb create mode 100644 spec/rubyspec/library/csv/iobuf/initialize_spec.rb create mode 100644 spec/rubyspec/library/csv/iobuf/read_spec.rb create mode 100644 spec/rubyspec/library/csv/iobuf/terminate_spec.rb create mode 100644 spec/rubyspec/library/csv/ioreader/close_on_terminate_spec.rb create mode 100644 spec/rubyspec/library/csv/ioreader/get_row_spec.rb create mode 100644 spec/rubyspec/library/csv/ioreader/initialize_spec.rb create mode 100644 spec/rubyspec/library/csv/ioreader/terminate_spec.rb create mode 100644 spec/rubyspec/library/csv/open_spec.rb create mode 100644 spec/rubyspec/library/csv/parse_spec.rb create mode 100644 spec/rubyspec/library/csv/read_spec.rb create mode 100644 spec/rubyspec/library/csv/readlines_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/add_buf_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/buf_size_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/drop_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/element_reference_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/get_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/idx_is_eos_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/initialize_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/is_eos_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/read_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/rel_buf_spec.rb create mode 100644 spec/rubyspec/library/csv/streambuf/terminate_spec.rb create mode 100644 spec/rubyspec/library/csv/stringreader/get_row_spec.rb create mode 100644 spec/rubyspec/library/csv/stringreader/initialize_spec.rb create mode 100644 spec/rubyspec/library/csv/writer/add_row_spec.rb create mode 100644 spec/rubyspec/library/csv/writer/append_spec.rb create mode 100644 spec/rubyspec/library/csv/writer/close_spec.rb create mode 100644 spec/rubyspec/library/csv/writer/create_spec.rb create mode 100644 spec/rubyspec/library/csv/writer/generate_spec.rb create mode 100644 spec/rubyspec/library/csv/writer/initialize_spec.rb create mode 100644 spec/rubyspec/library/csv/writer/terminate_spec.rb create mode 100644 spec/rubyspec/library/date/accessor_spec.rb create mode 100644 spec/rubyspec/library/date/add_month_spec.rb create mode 100644 spec/rubyspec/library/date/add_spec.rb create mode 100644 spec/rubyspec/library/date/ajd_spec.rb create mode 100644 spec/rubyspec/library/date/ajd_to_amjd_spec.rb create mode 100644 spec/rubyspec/library/date/ajd_to_jd_spec.rb create mode 100644 spec/rubyspec/library/date/amjd_spec.rb create mode 100644 spec/rubyspec/library/date/amjd_to_ajd_spec.rb create mode 100644 spec/rubyspec/library/date/append_spec.rb create mode 100644 spec/rubyspec/library/date/asctime_spec.rb create mode 100644 spec/rubyspec/library/date/boat_spec.rb create mode 100644 spec/rubyspec/library/date/case_compare_spec.rb create mode 100644 spec/rubyspec/library/date/civil_spec.rb create mode 100644 spec/rubyspec/library/date/commercial_spec.rb create mode 100644 spec/rubyspec/library/date/commercial_to_jd_spec.rb create mode 100644 spec/rubyspec/library/date/comparison_spec.rb create mode 100644 spec/rubyspec/library/date/constants_spec.rb create mode 100644 spec/rubyspec/library/date/conversions_spec.rb create mode 100644 spec/rubyspec/library/date/ctime_spec.rb create mode 100644 spec/rubyspec/library/date/cwday_spec.rb create mode 100644 spec/rubyspec/library/date/cweek_spec.rb create mode 100644 spec/rubyspec/library/date/cwyear_spec.rb create mode 100644 spec/rubyspec/library/date/day_fraction_spec.rb create mode 100644 spec/rubyspec/library/date/day_fraction_to_time_spec.rb create mode 100644 spec/rubyspec/library/date/day_spec.rb create mode 100644 spec/rubyspec/library/date/downto_spec.rb create mode 100644 spec/rubyspec/library/date/england_spec.rb create mode 100644 spec/rubyspec/library/date/eql_spec.rb create mode 100644 spec/rubyspec/library/date/format/bag/method_missing_spec.rb create mode 100644 spec/rubyspec/library/date/format/bag/to_hash_spec.rb create mode 100644 spec/rubyspec/library/date/gregorian_leap_spec.rb create mode 100644 spec/rubyspec/library/date/gregorian_spec.rb create mode 100644 spec/rubyspec/library/date/hash_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/abs_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/coerce_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/comparison_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/d_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/finite_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/infinite_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/nan_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/uminus_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/uplus_spec.rb create mode 100644 spec/rubyspec/library/date/infinity/zero_spec.rb create mode 100644 spec/rubyspec/library/date/infinity_spec.rb create mode 100644 spec/rubyspec/library/date/inspect_spec.rb create mode 100644 spec/rubyspec/library/date/italy_spec.rb create mode 100644 spec/rubyspec/library/date/jd_spec.rb create mode 100644 spec/rubyspec/library/date/jd_to_ajd_spec.rb create mode 100644 spec/rubyspec/library/date/jd_to_civil_spec.rb create mode 100644 spec/rubyspec/library/date/jd_to_commercial_spec.rb create mode 100644 spec/rubyspec/library/date/jd_to_ld_spec.rb create mode 100644 spec/rubyspec/library/date/jd_to_mjd_spec.rb create mode 100644 spec/rubyspec/library/date/jd_to_ordinal_spec.rb create mode 100644 spec/rubyspec/library/date/jd_to_wday_spec.rb create mode 100644 spec/rubyspec/library/date/julian_leap_spec.rb create mode 100644 spec/rubyspec/library/date/julian_spec.rb create mode 100644 spec/rubyspec/library/date/ld_spec.rb create mode 100644 spec/rubyspec/library/date/ld_to_jd_spec.rb create mode 100644 spec/rubyspec/library/date/leap_spec.rb create mode 100644 spec/rubyspec/library/date/mday_spec.rb create mode 100644 spec/rubyspec/library/date/minus_month_spec.rb create mode 100644 spec/rubyspec/library/date/minus_spec.rb create mode 100644 spec/rubyspec/library/date/mjd_spec.rb create mode 100644 spec/rubyspec/library/date/mjd_to_jd_spec.rb create mode 100644 spec/rubyspec/library/date/mon_spec.rb create mode 100644 spec/rubyspec/library/date/month_spec.rb create mode 100644 spec/rubyspec/library/date/new_spec.rb create mode 100644 spec/rubyspec/library/date/new_start_spec.rb create mode 100644 spec/rubyspec/library/date/next_spec.rb create mode 100644 spec/rubyspec/library/date/next_year_spec.rb create mode 100644 spec/rubyspec/library/date/ordinal_spec.rb create mode 100644 spec/rubyspec/library/date/ordinal_to_jd_spec.rb create mode 100644 spec/rubyspec/library/date/parse_spec.rb create mode 100644 spec/rubyspec/library/date/plus_spec.rb create mode 100644 spec/rubyspec/library/date/prev_year_spec.rb create mode 100644 spec/rubyspec/library/date/relationship_spec.rb create mode 100644 spec/rubyspec/library/date/right_shift_spec.rb create mode 100644 spec/rubyspec/library/date/shared/civil.rb create mode 100644 spec/rubyspec/library/date/shared/commercial.rb create mode 100644 spec/rubyspec/library/date/shared/jd.rb create mode 100644 spec/rubyspec/library/date/shared/new_bang.rb create mode 100644 spec/rubyspec/library/date/shared/ordinal.rb create mode 100644 spec/rubyspec/library/date/shared/parse.rb create mode 100644 spec/rubyspec/library/date/shared/parse_eu.rb create mode 100644 spec/rubyspec/library/date/shared/parse_us.rb create mode 100644 spec/rubyspec/library/date/shared/valid_civil.rb create mode 100644 spec/rubyspec/library/date/shared/valid_commercial.rb create mode 100644 spec/rubyspec/library/date/shared/valid_jd.rb create mode 100644 spec/rubyspec/library/date/shared/valid_ordinal.rb create mode 100644 spec/rubyspec/library/date/start_spec.rb create mode 100644 spec/rubyspec/library/date/step_spec.rb create mode 100644 spec/rubyspec/library/date/strftime_spec.rb create mode 100644 spec/rubyspec/library/date/strptime_spec.rb create mode 100644 spec/rubyspec/library/date/succ_spec.rb create mode 100644 spec/rubyspec/library/date/time_to_day_fraction_spec.rb create mode 100644 spec/rubyspec/library/date/to_s_spec.rb create mode 100644 spec/rubyspec/library/date/today_spec.rb create mode 100644 spec/rubyspec/library/date/upto_spec.rb create mode 100644 spec/rubyspec/library/date/valid_civil_spec.rb create mode 100644 spec/rubyspec/library/date/valid_commercial_spec.rb create mode 100644 spec/rubyspec/library/date/valid_date_spec.rb create mode 100644 spec/rubyspec/library/date/valid_jd_spec.rb create mode 100644 spec/rubyspec/library/date/valid_ordinal_spec.rb create mode 100644 spec/rubyspec/library/date/valid_time_spec.rb create mode 100644 spec/rubyspec/library/date/wday_spec.rb create mode 100644 spec/rubyspec/library/date/yday_spec.rb create mode 100644 spec/rubyspec/library/date/year_spec.rb create mode 100644 spec/rubyspec/library/date/zone_to_diff_spec.rb create mode 100644 spec/rubyspec/library/datetime/_strptime_spec.rb create mode 100644 spec/rubyspec/library/datetime/civil_spec.rb create mode 100644 spec/rubyspec/library/datetime/commercial_spec.rb create mode 100644 spec/rubyspec/library/datetime/hour_spec.rb create mode 100644 spec/rubyspec/library/datetime/httpdate_spec.rb create mode 100644 spec/rubyspec/library/datetime/iso8601_spec.rb create mode 100644 spec/rubyspec/library/datetime/jd_spec.rb create mode 100644 spec/rubyspec/library/datetime/jisx0301_spec.rb create mode 100644 spec/rubyspec/library/datetime/min_spec.rb create mode 100644 spec/rubyspec/library/datetime/minute_spec.rb create mode 100644 spec/rubyspec/library/datetime/new_offset_spec.rb create mode 100644 spec/rubyspec/library/datetime/new_spec.rb create mode 100644 spec/rubyspec/library/datetime/now_spec.rb create mode 100644 spec/rubyspec/library/datetime/offset_spec.rb create mode 100644 spec/rubyspec/library/datetime/ordinal_spec.rb create mode 100644 spec/rubyspec/library/datetime/parse_spec.rb create mode 100644 spec/rubyspec/library/datetime/rfc2822_spec.rb create mode 100644 spec/rubyspec/library/datetime/rfc3339_spec.rb create mode 100644 spec/rubyspec/library/datetime/rfc822_spec.rb create mode 100644 spec/rubyspec/library/datetime/sec_fraction_spec.rb create mode 100644 spec/rubyspec/library/datetime/sec_spec.rb create mode 100644 spec/rubyspec/library/datetime/second_fraction_spec.rb create mode 100644 spec/rubyspec/library/datetime/second_spec.rb create mode 100644 spec/rubyspec/library/datetime/shared/min.rb create mode 100644 spec/rubyspec/library/datetime/shared/sec.rb create mode 100644 spec/rubyspec/library/datetime/strftime_spec.rb create mode 100644 spec/rubyspec/library/datetime/strptime_spec.rb create mode 100644 spec/rubyspec/library/datetime/to_date_spec.rb create mode 100644 spec/rubyspec/library/datetime/to_datetime_spec.rb create mode 100644 spec/rubyspec/library/datetime/to_s_spec.rb create mode 100644 spec/rubyspec/library/datetime/to_time_spec.rb create mode 100644 spec/rubyspec/library/datetime/xmlschema_spec.rb create mode 100644 spec/rubyspec/library/datetime/zone_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegate_class/instance_method_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegate_class/instance_methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegate_class/private_instance_methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegate_class/protected_instance_methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegate_class/public_instance_methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegate_class/respond_to_missing_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/case_compare_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/compare_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/complement_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/eql_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/equal_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/equal_value_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/frozen_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/hash_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/marshal_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/method_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/not_equal_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/not_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/private_methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/protected_methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/public_methods_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/send_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/taint_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/tap_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/trust_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/untaint_spec.rb create mode 100644 spec/rubyspec/library/delegate/delegator/untrust_spec.rb create mode 100644 spec/rubyspec/library/delegate/fixtures/classes.rb create mode 100644 spec/rubyspec/library/digest/bubblebabble_spec.rb create mode 100644 spec/rubyspec/library/digest/hexencode_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/append_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/block_length_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/digest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/digest_length_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/digest_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/equal_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/file_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/hexdigest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/hexdigest_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/inspect_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/length_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/reset_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/shared/constants.rb create mode 100644 spec/rubyspec/library/digest/md5/shared/length.rb create mode 100644 spec/rubyspec/library/digest/md5/shared/sample.rb create mode 100644 spec/rubyspec/library/digest/md5/shared/update.rb create mode 100644 spec/rubyspec/library/digest/md5/size_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/to_s_spec.rb create mode 100644 spec/rubyspec/library/digest/md5/update_spec.rb create mode 100644 spec/rubyspec/library/digest/sha1/digest_spec.rb create mode 100644 spec/rubyspec/library/digest/sha1/file_spec.rb create mode 100644 spec/rubyspec/library/digest/sha1/shared/constants.rb create mode 100644 spec/rubyspec/library/digest/sha256/append_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/block_length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/digest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/digest_length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/digest_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/equal_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/file_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/hexdigest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/hexdigest_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/inspect_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/reset_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/shared/constants.rb create mode 100644 spec/rubyspec/library/digest/sha256/shared/length.rb create mode 100644 spec/rubyspec/library/digest/sha256/shared/update.rb create mode 100644 spec/rubyspec/library/digest/sha256/size_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/to_s_spec.rb create mode 100644 spec/rubyspec/library/digest/sha256/update_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/append_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/block_length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/digest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/digest_length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/digest_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/equal_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/file_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/hexdigest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/hexdigest_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/inspect_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/reset_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/shared/constants.rb create mode 100644 spec/rubyspec/library/digest/sha384/shared/length.rb create mode 100644 spec/rubyspec/library/digest/sha384/shared/update.rb create mode 100644 spec/rubyspec/library/digest/sha384/size_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/to_s_spec.rb create mode 100644 spec/rubyspec/library/digest/sha384/update_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/append_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/block_length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/digest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/digest_length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/digest_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/equal_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/file_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/hexdigest_bang_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/hexdigest_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/inspect_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/length_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/reset_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/shared/constants.rb create mode 100644 spec/rubyspec/library/digest/sha512/shared/length.rb create mode 100644 spec/rubyspec/library/digest/sha512/shared/update.rb create mode 100644 spec/rubyspec/library/digest/sha512/size_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/to_s_spec.rb create mode 100644 spec/rubyspec/library/digest/sha512/update_spec.rb create mode 100644 spec/rubyspec/library/drb/config_spec.rb create mode 100644 spec/rubyspec/library/drb/current_server_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/__drbref_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/__drburi_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/_dump_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/_load_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/eql_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/equal_value_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/hash_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/method_missing_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/new_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/new_with_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/new_with_uri_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/prepare_backtrace_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/pretty_print_cycle_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/pretty_print_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/respond_to_spec.rb create mode 100644 spec/rubyspec/library/drb/drbobject/with_friend_spec.rb create mode 100644 spec/rubyspec/library/drb/fetch_server_spec.rb create mode 100644 spec/rubyspec/library/drb/fixtures/test_server.rb create mode 100644 spec/rubyspec/library/drb/front_spec.rb create mode 100644 spec/rubyspec/library/drb/here_spec.rb create mode 100644 spec/rubyspec/library/drb/install_acl_spec.rb create mode 100644 spec/rubyspec/library/drb/install_id_conv_spec.rb create mode 100644 spec/rubyspec/library/drb/primary_server_spec.rb create mode 100644 spec/rubyspec/library/drb/regist_server_spec.rb create mode 100644 spec/rubyspec/library/drb/remove_server_spec.rb create mode 100644 spec/rubyspec/library/drb/start_service_spec.rb create mode 100644 spec/rubyspec/library/drb/stop_service_spec.rb create mode 100644 spec/rubyspec/library/drb/thread_spec.rb create mode 100644 spec/rubyspec/library/drb/to_id_spec.rb create mode 100644 spec/rubyspec/library/drb/to_obj_spec.rb create mode 100644 spec/rubyspec/library/drb/uri_spec.rb create mode 100644 spec/rubyspec/library/erb/def_class_spec.rb create mode 100644 spec/rubyspec/library/erb/def_method_spec.rb create mode 100644 spec/rubyspec/library/erb/def_module_spec.rb create mode 100644 spec/rubyspec/library/erb/defmethod/def_erb_method_spec.rb create mode 100644 spec/rubyspec/library/erb/filename_spec.rb create mode 100644 spec/rubyspec/library/erb/new_spec.rb create mode 100644 spec/rubyspec/library/erb/result_spec.rb create mode 100644 spec/rubyspec/library/erb/run_spec.rb create mode 100644 spec/rubyspec/library/erb/src_spec.rb create mode 100644 spec/rubyspec/library/erb/util/h_spec.rb create mode 100644 spec/rubyspec/library/erb/util/html_escape_spec.rb create mode 100644 spec/rubyspec/library/erb/util/shared/html_escape.rb create mode 100644 spec/rubyspec/library/erb/util/shared/url_encode.rb create mode 100644 spec/rubyspec/library/erb/util/u_spec.rb create mode 100644 spec/rubyspec/library/erb/util/url_encode_spec.rb create mode 100644 spec/rubyspec/library/etc/endgrent_spec.rb create mode 100644 spec/rubyspec/library/etc/endpwent_spec.rb create mode 100644 spec/rubyspec/library/etc/getgrent_spec.rb create mode 100644 spec/rubyspec/library/etc/getgrgid_spec.rb create mode 100644 spec/rubyspec/library/etc/getgrnam_spec.rb create mode 100644 spec/rubyspec/library/etc/getlogin_spec.rb create mode 100644 spec/rubyspec/library/etc/getpwent_spec.rb create mode 100644 spec/rubyspec/library/etc/getpwnam_spec.rb create mode 100644 spec/rubyspec/library/etc/getpwuid_spec.rb create mode 100644 spec/rubyspec/library/etc/group_spec.rb create mode 100644 spec/rubyspec/library/etc/nprocessors_spec.rb create mode 100644 spec/rubyspec/library/etc/shared/windows.rb create mode 100644 spec/rubyspec/library/etc/struct_group_spec.rb create mode 100644 spec/rubyspec/library/etc/struct_passwd_spec.rb create mode 100644 spec/rubyspec/library/expect/expect_spec.rb create mode 100644 spec/rubyspec/library/fiber/alive_spec.rb create mode 100644 spec/rubyspec/library/fiber/current_spec.rb create mode 100644 spec/rubyspec/library/fiber/resume_spec.rb create mode 100644 spec/rubyspec/library/fiber/transfer_spec.rb create mode 100644 spec/rubyspec/library/find/find_spec.rb create mode 100644 spec/rubyspec/library/find/fixtures/common.rb create mode 100644 spec/rubyspec/library/find/prune_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/each_option_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/each_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/error_message_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/get_option_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/get_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/initialize_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/ordering_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/set_options_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/shared/each.rb create mode 100644 spec/rubyspec/library/getoptlong/shared/get.rb create mode 100644 spec/rubyspec/library/getoptlong/terminate_spec.rb create mode 100644 spec/rubyspec/library/getoptlong/terminated_spec.rb create mode 100644 spec/rubyspec/library/ipaddr/hton_spec.rb create mode 100644 spec/rubyspec/library/ipaddr/ipv4_conversion_spec.rb create mode 100644 spec/rubyspec/library/ipaddr/new_spec.rb create mode 100644 spec/rubyspec/library/ipaddr/operator_spec.rb create mode 100644 spec/rubyspec/library/ipaddr/reverse_spec.rb create mode 100644 spec/rubyspec/library/ipaddr/to_s_spec.rb create mode 100644 spec/rubyspec/library/logger/device/close_spec.rb create mode 100644 spec/rubyspec/library/logger/device/new_spec.rb create mode 100644 spec/rubyspec/library/logger/device/write_spec.rb create mode 100644 spec/rubyspec/library/logger/fixtures/common.rb create mode 100644 spec/rubyspec/library/logger/logger/add_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/close_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/datetime_format_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/debug_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/error_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/fatal_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/info_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/new_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/unknown_spec.rb create mode 100644 spec/rubyspec/library/logger/logger/warn_spec.rb create mode 100644 spec/rubyspec/library/logger/severity_spec.rb create mode 100644 spec/rubyspec/library/mathn/bignum/exponent_spec.rb create mode 100644 spec/rubyspec/library/mathn/complex/Complex_spec.rb create mode 100644 spec/rubyspec/library/mathn/fixnum/exponent_spec.rb create mode 100644 spec/rubyspec/library/mathn/float/exponent_spec.rb create mode 100644 spec/rubyspec/library/mathn/integer/from_prime_division_spec.rb create mode 100644 spec/rubyspec/library/mathn/integer/prime_division_spec.rb create mode 100644 spec/rubyspec/library/mathn/math/fixtures/classes.rb create mode 100644 spec/rubyspec/library/mathn/math/rsqrt_spec.rb create mode 100644 spec/rubyspec/library/mathn/math/shared/rsqrt.rb create mode 100644 spec/rubyspec/library/mathn/math/shared/sqrt.rb create mode 100644 spec/rubyspec/library/mathn/math/sqrt_spec.rb create mode 100644 spec/rubyspec/library/mathn/rational/Rational_spec.rb create mode 100644 spec/rubyspec/library/mathn/rational/inspect_spec.rb create mode 100644 spec/rubyspec/library/matrix/I_spec.rb create mode 100644 spec/rubyspec/library/matrix/build_spec.rb create mode 100644 spec/rubyspec/library/matrix/clone_spec.rb create mode 100644 spec/rubyspec/library/matrix/coerce_spec.rb create mode 100644 spec/rubyspec/library/matrix/collect_spec.rb create mode 100644 spec/rubyspec/library/matrix/column_size_spec.rb create mode 100644 spec/rubyspec/library/matrix/column_spec.rb create mode 100644 spec/rubyspec/library/matrix/column_vector_spec.rb create mode 100644 spec/rubyspec/library/matrix/column_vectors_spec.rb create mode 100644 spec/rubyspec/library/matrix/columns_spec.rb create mode 100644 spec/rubyspec/library/matrix/conj_spec.rb create mode 100644 spec/rubyspec/library/matrix/conjugate_spec.rb create mode 100644 spec/rubyspec/library/matrix/constructor_spec.rb create mode 100644 spec/rubyspec/library/matrix/det_spec.rb create mode 100644 spec/rubyspec/library/matrix/determinant_spec.rb create mode 100644 spec/rubyspec/library/matrix/diagonal_spec.rb create mode 100644 spec/rubyspec/library/matrix/divide_spec.rb create mode 100644 spec/rubyspec/library/matrix/each_spec.rb create mode 100644 spec/rubyspec/library/matrix/each_with_index_spec.rb create mode 100644 spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb create mode 100644 spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb create mode 100644 spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb create mode 100644 spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb create mode 100644 spec/rubyspec/library/matrix/eigenvalue_decomposition/initialize_spec.rb create mode 100644 spec/rubyspec/library/matrix/eigenvalue_decomposition/to_a_spec.rb create mode 100644 spec/rubyspec/library/matrix/element_reference_spec.rb create mode 100644 spec/rubyspec/library/matrix/empty_spec.rb create mode 100644 spec/rubyspec/library/matrix/eql_spec.rb create mode 100644 spec/rubyspec/library/matrix/equal_value_spec.rb create mode 100644 spec/rubyspec/library/matrix/exponent_spec.rb create mode 100644 spec/rubyspec/library/matrix/find_index_spec.rb create mode 100644 spec/rubyspec/library/matrix/fixtures/classes.rb create mode 100644 spec/rubyspec/library/matrix/hash_spec.rb create mode 100644 spec/rubyspec/library/matrix/hermitian_spec.rb create mode 100644 spec/rubyspec/library/matrix/identity_spec.rb create mode 100644 spec/rubyspec/library/matrix/imag_spec.rb create mode 100644 spec/rubyspec/library/matrix/imaginary_spec.rb create mode 100644 spec/rubyspec/library/matrix/inspect_spec.rb create mode 100644 spec/rubyspec/library/matrix/inv_spec.rb create mode 100644 spec/rubyspec/library/matrix/inverse_from_spec.rb create mode 100644 spec/rubyspec/library/matrix/inverse_spec.rb create mode 100644 spec/rubyspec/library/matrix/lower_triangular_spec.rb create mode 100644 spec/rubyspec/library/matrix/lup_decomposition/determinant_spec.rb create mode 100644 spec/rubyspec/library/matrix/lup_decomposition/initialize_spec.rb create mode 100644 spec/rubyspec/library/matrix/lup_decomposition/l_spec.rb create mode 100644 spec/rubyspec/library/matrix/lup_decomposition/p_spec.rb create mode 100644 spec/rubyspec/library/matrix/lup_decomposition/solve_spec.rb create mode 100644 spec/rubyspec/library/matrix/lup_decomposition/to_a_spec.rb create mode 100644 spec/rubyspec/library/matrix/lup_decomposition/u_spec.rb create mode 100644 spec/rubyspec/library/matrix/map_spec.rb create mode 100644 spec/rubyspec/library/matrix/minor_spec.rb create mode 100644 spec/rubyspec/library/matrix/minus_spec.rb create mode 100644 spec/rubyspec/library/matrix/multiply_spec.rb create mode 100644 spec/rubyspec/library/matrix/new_spec.rb create mode 100644 spec/rubyspec/library/matrix/normal_spec.rb create mode 100644 spec/rubyspec/library/matrix/orthogonal_spec.rb create mode 100644 spec/rubyspec/library/matrix/permutation_spec.rb create mode 100644 spec/rubyspec/library/matrix/plus_spec.rb create mode 100644 spec/rubyspec/library/matrix/rank_spec.rb create mode 100644 spec/rubyspec/library/matrix/real_spec.rb create mode 100644 spec/rubyspec/library/matrix/rect_spec.rb create mode 100644 spec/rubyspec/library/matrix/rectangular_spec.rb create mode 100644 spec/rubyspec/library/matrix/regular_spec.rb create mode 100644 spec/rubyspec/library/matrix/round_spec.rb create mode 100644 spec/rubyspec/library/matrix/row_size_spec.rb create mode 100644 spec/rubyspec/library/matrix/row_spec.rb create mode 100644 spec/rubyspec/library/matrix/row_vector_spec.rb create mode 100644 spec/rubyspec/library/matrix/row_vectors_spec.rb create mode 100644 spec/rubyspec/library/matrix/rows_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/Fail_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/Raise_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/divide_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/exponent_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/included_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/initialize_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/minus_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/multiply_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar/plus_spec.rb create mode 100644 spec/rubyspec/library/matrix/scalar_spec.rb create mode 100644 spec/rubyspec/library/matrix/shared/collect.rb create mode 100644 spec/rubyspec/library/matrix/shared/conjugate.rb create mode 100644 spec/rubyspec/library/matrix/shared/determinant.rb create mode 100644 spec/rubyspec/library/matrix/shared/equal_value.rb create mode 100644 spec/rubyspec/library/matrix/shared/identity.rb create mode 100644 spec/rubyspec/library/matrix/shared/imaginary.rb create mode 100644 spec/rubyspec/library/matrix/shared/inverse.rb create mode 100644 spec/rubyspec/library/matrix/shared/rectangular.rb create mode 100644 spec/rubyspec/library/matrix/shared/trace.rb create mode 100644 spec/rubyspec/library/matrix/shared/transpose.rb create mode 100644 spec/rubyspec/library/matrix/singular_spec.rb create mode 100644 spec/rubyspec/library/matrix/spec_helper.rb create mode 100644 spec/rubyspec/library/matrix/square_spec.rb create mode 100644 spec/rubyspec/library/matrix/symmetric_spec.rb create mode 100644 spec/rubyspec/library/matrix/t_spec.rb create mode 100644 spec/rubyspec/library/matrix/to_a_spec.rb create mode 100644 spec/rubyspec/library/matrix/to_s_spec.rb create mode 100644 spec/rubyspec/library/matrix/tr_spec.rb create mode 100644 spec/rubyspec/library/matrix/trace_spec.rb create mode 100644 spec/rubyspec/library/matrix/transpose_spec.rb create mode 100644 spec/rubyspec/library/matrix/unit_spec.rb create mode 100644 spec/rubyspec/library/matrix/unitary_spec.rb create mode 100644 spec/rubyspec/library/matrix/upper_triangular_spec.rb create mode 100644 spec/rubyspec/library/matrix/vector/cross_product_spec.rb create mode 100644 spec/rubyspec/library/matrix/vector/each2_spec.rb create mode 100644 spec/rubyspec/library/matrix/vector/eql_spec.rb create mode 100644 spec/rubyspec/library/matrix/vector/inner_product_spec.rb create mode 100644 spec/rubyspec/library/matrix/vector/normalize_spec.rb create mode 100644 spec/rubyspec/library/matrix/zero_spec.rb create mode 100644 spec/rubyspec/library/net/FTPError_spec.rb create mode 100644 spec/rubyspec/library/net/FTPPermError_spec.rb create mode 100644 spec/rubyspec/library/net/FTPProtoError_spec.rb create mode 100644 spec/rubyspec/library/net/FTPReplyError_spec.rb create mode 100644 spec/rubyspec/library/net/FTPTempError_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/abort_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/acct_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/binary_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/chdir_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/close_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/closed_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/connect_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/debug_mode_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/default_passive_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/delete_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/dir_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/fixtures/default_passive.rb create mode 100644 spec/rubyspec/library/net/ftp/fixtures/passive.rb create mode 100644 spec/rubyspec/library/net/ftp/fixtures/putbinaryfile create mode 100644 spec/rubyspec/library/net/ftp/fixtures/puttextfile create mode 100644 spec/rubyspec/library/net/ftp/fixtures/server.rb create mode 100644 spec/rubyspec/library/net/ftp/get_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/getbinaryfile_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/getdir_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/gettextfile_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/help_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/initialize_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/last_response_code_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/last_response_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/lastresp_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/list_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/login_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/ls_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/mdtm_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/mkdir_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/mtime_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/nlst_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/noop_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/open_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/passive_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/put_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/putbinaryfile_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/puttextfile_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/pwd_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/quit_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/rename_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/resume_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/retrbinary_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/retrlines_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/return_code_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/rmdir_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/sendcmd_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/set_socket_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/shared/getbinaryfile.rb create mode 100644 spec/rubyspec/library/net/ftp/shared/gettextfile.rb create mode 100644 spec/rubyspec/library/net/ftp/shared/last_response_code.rb create mode 100644 spec/rubyspec/library/net/ftp/shared/list.rb create mode 100644 spec/rubyspec/library/net/ftp/shared/putbinaryfile.rb create mode 100644 spec/rubyspec/library/net/ftp/shared/puttextfile.rb create mode 100644 spec/rubyspec/library/net/ftp/shared/pwd.rb create mode 100644 spec/rubyspec/library/net/ftp/site_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/size_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/spec_helper.rb create mode 100644 spec/rubyspec/library/net/ftp/status_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/storbinary_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/storlines_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/system_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/voidcmd_spec.rb create mode 100644 spec/rubyspec/library/net/ftp/welcome_spec.rb create mode 100644 spec/rubyspec/library/net/http/HTTPBadResponse_spec.rb create mode 100644 spec/rubyspec/library/net/http/HTTPError_spec.rb create mode 100644 spec/rubyspec/library/net/http/HTTPFatalError_spec.rb create mode 100644 spec/rubyspec/library/net/http/HTTPHeaderSyntaxError_spec.rb create mode 100644 spec/rubyspec/library/net/http/HTTPRetriableError_spec.rb create mode 100644 spec/rubyspec/library/net/http/HTTPServerException_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/Proxy_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/active_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/address_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/close_on_empty_response_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/copy_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/default_port_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/delete_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/finish_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/fixtures/http_server.rb create mode 100644 spec/rubyspec/library/net/http/http/get2_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/get_print_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/get_response_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/get_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/head2_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/head_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/http_default_port_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/https_default_port_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/initialize_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/inspect_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/is_version_1_1_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/is_version_1_2_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/lock_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/mkcol_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/move_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/new_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/newobj_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/open_timeout_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/options_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/port_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/post2_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/post_form_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/post_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/propfind_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/proppatch_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/proxy_address_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/proxy_class_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/proxy_pass_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/proxy_port_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/proxy_user_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/put2_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/put_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/read_timeout_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/request_get_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/request_head_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/request_post_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/request_put_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/request_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/request_types_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/send_request_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/set_debug_output_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/shared/request_get.rb create mode 100644 spec/rubyspec/library/net/http/http/shared/request_head.rb create mode 100644 spec/rubyspec/library/net/http/http/shared/request_post.rb create mode 100644 spec/rubyspec/library/net/http/http/shared/request_put.rb create mode 100644 spec/rubyspec/library/net/http/http/shared/started.rb create mode 100644 spec/rubyspec/library/net/http/http/shared/version_1_1.rb create mode 100644 spec/rubyspec/library/net/http/http/shared/version_1_2.rb create mode 100644 spec/rubyspec/library/net/http/http/socket_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/start_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/started_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/trace_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/unlock_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/use_ssl_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/version_1_1_spec.rb create mode 100644 spec/rubyspec/library/net/http/http/version_1_2_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpexceptions/fixtures/classes.rb create mode 100644 spec/rubyspec/library/net/http/httpexceptions/initialize_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpexceptions/response_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/body_exist_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/body_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/body_stream_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/exec_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/inspect_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/method_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/path_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/request_body_permitted_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/response_body_permitted_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpgenericrequest/set_body_internal_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/add_field_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/basic_auth_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/canonical_each_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/chunked_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/content_length_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/content_range_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/content_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/delete_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/each_capitalized_name_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/each_capitalized_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/each_header_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/each_key_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/each_name_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/each_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/each_value_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/element_reference_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/element_set_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/fetch_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/fixtures/classes.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/form_data_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/get_fields_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/initialize_http_header_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/key_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/length_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/main_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/proxy_basic_auth_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/range_length_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/range_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/set_content_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/set_form_data_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/set_range_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/shared/each_capitalized.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/shared/each_header.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/shared/each_name.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/shared/set_content_type.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/shared/set_form_data.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/shared/set_range.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/shared/size.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/size_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/sub_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/to_hash_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpheader/type_params_spec.rb create mode 100644 spec/rubyspec/library/net/http/httprequest/initialize_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/body_permitted_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/body_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/code_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/code_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/entity_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/error_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/error_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/exception_type_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/header_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/http_version_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/initialize_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/inspect_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/message_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/msg_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/read_body_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/read_header_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/read_new_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/reading_body_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/response_spec.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/shared/body.rb create mode 100644 spec/rubyspec/library/net/http/httpresponse/value_spec.rb create mode 100644 spec/rubyspec/library/observer/add_observer_spec.rb create mode 100644 spec/rubyspec/library/observer/count_observers_spec.rb create mode 100644 spec/rubyspec/library/observer/delete_observer_spec.rb create mode 100644 spec/rubyspec/library/observer/delete_observers_spec.rb create mode 100644 spec/rubyspec/library/observer/fixtures/classes.rb create mode 100644 spec/rubyspec/library/observer/notify_observers_spec.rb create mode 100644 spec/rubyspec/library/open3/capture2_spec.rb create mode 100644 spec/rubyspec/library/open3/capture2e_spec.rb create mode 100644 spec/rubyspec/library/open3/capture3_spec.rb create mode 100644 spec/rubyspec/library/open3/pipeline_r_spec.rb create mode 100644 spec/rubyspec/library/open3/pipeline_rw_spec.rb create mode 100644 spec/rubyspec/library/open3/pipeline_spec.rb create mode 100644 spec/rubyspec/library/open3/pipeline_start_spec.rb create mode 100644 spec/rubyspec/library/open3/pipeline_w_spec.rb create mode 100644 spec/rubyspec/library/open3/popen2_spec.rb create mode 100644 spec/rubyspec/library/open3/popen2e_spec.rb create mode 100644 spec/rubyspec/library/open3/popen3_spec.rb create mode 100644 spec/rubyspec/library/openssl/cipher_spec.rb create mode 100644 spec/rubyspec/library/openssl/config/freeze_spec.rb create mode 100644 spec/rubyspec/library/openssl/hmac/digest_spec.rb create mode 100644 spec/rubyspec/library/openssl/hmac/hexdigest_spec.rb create mode 100644 spec/rubyspec/library/openssl/random/pseudo_bytes_spec.rb create mode 100644 spec/rubyspec/library/openssl/random/random_bytes_spec.rb create mode 100644 spec/rubyspec/library/openssl/random/shared/random_bytes.rb create mode 100644 spec/rubyspec/library/openssl/shared/constants.rb create mode 100644 spec/rubyspec/library/openssl/x509/name/parse_spec.rb create mode 100644 spec/rubyspec/library/openstruct/delete_field_spec.rb create mode 100644 spec/rubyspec/library/openstruct/element_reference_spec.rb create mode 100644 spec/rubyspec/library/openstruct/element_set_spec.rb create mode 100644 spec/rubyspec/library/openstruct/equal_value_spec.rb create mode 100644 spec/rubyspec/library/openstruct/fixtures/classes.rb create mode 100644 spec/rubyspec/library/openstruct/frozen_spec.rb create mode 100644 spec/rubyspec/library/openstruct/initialize_spec.rb create mode 100644 spec/rubyspec/library/openstruct/inspect_spec.rb create mode 100644 spec/rubyspec/library/openstruct/marshal_dump_spec.rb create mode 100644 spec/rubyspec/library/openstruct/marshal_load_spec.rb create mode 100644 spec/rubyspec/library/openstruct/method_missing_spec.rb create mode 100644 spec/rubyspec/library/openstruct/new_spec.rb create mode 100644 spec/rubyspec/library/openstruct/shared/inspect.rb create mode 100644 spec/rubyspec/library/openstruct/to_h_spec.rb create mode 100644 spec/rubyspec/library/openstruct/to_s_spec.rb create mode 100644 spec/rubyspec/library/pathname/absolute_spec.rb create mode 100644 spec/rubyspec/library/pathname/equal_value_spec.rb create mode 100644 spec/rubyspec/library/pathname/hash_spec.rb create mode 100644 spec/rubyspec/library/pathname/join_spec.rb create mode 100644 spec/rubyspec/library/pathname/new_spec.rb create mode 100644 spec/rubyspec/library/pathname/parent_spec.rb create mode 100644 spec/rubyspec/library/pathname/realdirpath_spec.rb create mode 100644 spec/rubyspec/library/pathname/realpath_spec.rb create mode 100644 spec/rubyspec/library/pathname/relative_path_from_spec.rb create mode 100644 spec/rubyspec/library/pathname/relative_spec.rb create mode 100644 spec/rubyspec/library/pathname/root_spec.rb create mode 100644 spec/rubyspec/library/pathname/sub_spec.rb create mode 100644 spec/rubyspec/library/pp/pp_spec.rb create mode 100644 spec/rubyspec/library/prime/each_spec.rb create mode 100644 spec/rubyspec/library/prime/instance_spec.rb create mode 100644 spec/rubyspec/library/prime/int_from_prime_division_spec.rb create mode 100644 spec/rubyspec/library/prime/integer/each_prime_spec.rb create mode 100644 spec/rubyspec/library/prime/integer/from_prime_division_spec.rb create mode 100644 spec/rubyspec/library/prime/integer/prime_division_spec.rb create mode 100644 spec/rubyspec/library/prime/integer/prime_spec.rb create mode 100644 spec/rubyspec/library/prime/next_spec.rb create mode 100644 spec/rubyspec/library/prime/prime_division_spec.rb create mode 100644 spec/rubyspec/library/prime/prime_spec.rb create mode 100644 spec/rubyspec/library/prime/shared/next.rb create mode 100644 spec/rubyspec/library/prime/succ_spec.rb create mode 100644 spec/rubyspec/library/readline/basic_quote_characters_spec.rb create mode 100644 spec/rubyspec/library/readline/basic_word_break_characters_spec.rb create mode 100644 spec/rubyspec/library/readline/completer_quote_characters_spec.rb create mode 100644 spec/rubyspec/library/readline/completer_word_break_characters_spec.rb create mode 100644 spec/rubyspec/library/readline/completion_append_character_spec.rb create mode 100644 spec/rubyspec/library/readline/completion_case_fold_spec.rb create mode 100644 spec/rubyspec/library/readline/completion_proc_spec.rb create mode 100644 spec/rubyspec/library/readline/constants_spec.rb create mode 100644 spec/rubyspec/library/readline/emacs_editing_mode_spec.rb create mode 100644 spec/rubyspec/library/readline/filename_quote_characters_spec.rb create mode 100644 spec/rubyspec/library/readline/history/append_spec.rb create mode 100644 spec/rubyspec/library/readline/history/delete_at_spec.rb create mode 100644 spec/rubyspec/library/readline/history/each_spec.rb create mode 100644 spec/rubyspec/library/readline/history/element_reference_spec.rb create mode 100644 spec/rubyspec/library/readline/history/element_set_spec.rb create mode 100644 spec/rubyspec/library/readline/history/empty_spec.rb create mode 100644 spec/rubyspec/library/readline/history/history_spec.rb create mode 100644 spec/rubyspec/library/readline/history/length_spec.rb create mode 100644 spec/rubyspec/library/readline/history/pop_spec.rb create mode 100644 spec/rubyspec/library/readline/history/push_spec.rb create mode 100644 spec/rubyspec/library/readline/history/shared/size.rb create mode 100644 spec/rubyspec/library/readline/history/shift_spec.rb create mode 100644 spec/rubyspec/library/readline/history/size_spec.rb create mode 100644 spec/rubyspec/library/readline/history/to_s_spec.rb create mode 100644 spec/rubyspec/library/readline/readline_spec.rb create mode 100644 spec/rubyspec/library/readline/spec_helper.rb create mode 100644 spec/rubyspec/library/readline/vi_editing_mode_spec.rb create mode 100644 spec/rubyspec/library/resolv/get_address_spec.rb create mode 100644 spec/rubyspec/library/resolv/get_addresses_spec.rb create mode 100644 spec/rubyspec/library/resolv/get_name_spec.rb create mode 100644 spec/rubyspec/library/resolv/get_names_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/clone_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/element_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/equal_value_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/hash_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/initialize_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/inspect_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/namespace_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/node_type_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/prefix_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/remove_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/to_s_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/to_string_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/value_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/write_spec.rb create mode 100644 spec/rubyspec/library/rexml/attribute/xpath_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/add_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/append_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/delete_all_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/delete_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/each_attribute_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/each_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/element_reference_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/element_set_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/get_attribute_ns_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/get_attribute_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/initialize_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/length_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/namespaces_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/prefixes_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/shared/add.rb create mode 100644 spec/rubyspec/library/rexml/attributes/shared/length.rb create mode 100644 spec/rubyspec/library/rexml/attributes/size_spec.rb create mode 100644 spec/rubyspec/library/rexml/attributes/to_a_spec.rb create mode 100644 spec/rubyspec/library/rexml/cdata/clone_spec.rb create mode 100644 spec/rubyspec/library/rexml/cdata/initialize_spec.rb create mode 100644 spec/rubyspec/library/rexml/cdata/shared/to_s.rb create mode 100644 spec/rubyspec/library/rexml/cdata/to_s_spec.rb create mode 100644 spec/rubyspec/library/rexml/cdata/value_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/add_element_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/add_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/clone_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/doctype_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/encoding_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/expanded_name_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/new_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/node_type_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/root_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/stand_alone_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/version_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/write_spec.rb create mode 100644 spec/rubyspec/library/rexml/document/xml_decl_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/add_attribute_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/add_attributes_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/add_element_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/add_namespace_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/add_text_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/attribute_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/attributes_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/cdatas_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/clone_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/comments_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/delete_attribute_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/delete_element_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/delete_namespace_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/document_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/each_element_with_attribute_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/each_element_with_text_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/get_text_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/has_attributes_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/has_elements_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/has_text_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/inspect_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/instructions_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/namespace_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/namespaces_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/new_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/next_element_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/node_type_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/prefixes_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/previous_element_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/raw_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/root_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/text_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/texts_spec.rb create mode 100644 spec/rubyspec/library/rexml/element/whitespace_spec.rb create mode 100644 spec/rubyspec/library/rexml/node/each_recursive_spec.rb create mode 100644 spec/rubyspec/library/rexml/node/find_first_recursive_spec.rb create mode 100644 spec/rubyspec/library/rexml/node/index_in_parent_spec.rb create mode 100644 spec/rubyspec/library/rexml/node/next_sibling_node_spec.rb create mode 100644 spec/rubyspec/library/rexml/node/parent_spec.rb create mode 100644 spec/rubyspec/library/rexml/node/previous_sibling_node_spec.rb create mode 100644 spec/rubyspec/library/rexml/shared/each_element.rb create mode 100644 spec/rubyspec/library/rexml/shared/elements_to_a.rb create mode 100644 spec/rubyspec/library/rexml/text/append_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/clone_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/comparison_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/empty_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/indent_text_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/inspect_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/new_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/node_type_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/normalize_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/read_with_substitution_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/to_s_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/unnormalize_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/value_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/wrap_spec.rb create mode 100644 spec/rubyspec/library/rexml/text/write_with_substitution_spec.rb create mode 100644 spec/rubyspec/library/scanf/io/block_scanf_spec.rb create mode 100644 spec/rubyspec/library/scanf/io/fixtures/date.txt create mode 100644 spec/rubyspec/library/scanf/io/fixtures/helloworld.txt create mode 100644 spec/rubyspec/library/scanf/io/scanf_spec.rb create mode 100644 spec/rubyspec/library/scanf/io/shared/block_scanf.rb create mode 100644 spec/rubyspec/library/scanf/string/block_scanf_spec.rb create mode 100644 spec/rubyspec/library/scanf/string/scanf_spec.rb create mode 100644 spec/rubyspec/library/scanf/string/shared/block_scanf.rb create mode 100644 spec/rubyspec/library/securerandom/base64_spec.rb create mode 100644 spec/rubyspec/library/securerandom/hex_spec.rb create mode 100644 spec/rubyspec/library/securerandom/random_bytes_spec.rb create mode 100644 spec/rubyspec/library/securerandom/random_number_spec.rb create mode 100644 spec/rubyspec/library/set/add_spec.rb create mode 100644 spec/rubyspec/library/set/append_spec.rb create mode 100644 spec/rubyspec/library/set/classify_spec.rb create mode 100644 spec/rubyspec/library/set/clear_spec.rb create mode 100644 spec/rubyspec/library/set/collect_spec.rb create mode 100644 spec/rubyspec/library/set/constructor_spec.rb create mode 100644 spec/rubyspec/library/set/delete_if_spec.rb create mode 100644 spec/rubyspec/library/set/delete_spec.rb create mode 100644 spec/rubyspec/library/set/difference_spec.rb create mode 100644 spec/rubyspec/library/set/divide_spec.rb create mode 100644 spec/rubyspec/library/set/each_spec.rb create mode 100644 spec/rubyspec/library/set/empty_spec.rb create mode 100644 spec/rubyspec/library/set/enumerable/to_set_spec.rb create mode 100644 spec/rubyspec/library/set/eql_spec.rb create mode 100644 spec/rubyspec/library/set/equal_value_spec.rb create mode 100644 spec/rubyspec/library/set/exclusion_spec.rb create mode 100644 spec/rubyspec/library/set/flatten_merge_spec.rb create mode 100644 spec/rubyspec/library/set/flatten_spec.rb create mode 100644 spec/rubyspec/library/set/hash_spec.rb create mode 100644 spec/rubyspec/library/set/include_spec.rb create mode 100644 spec/rubyspec/library/set/initialize_spec.rb create mode 100644 spec/rubyspec/library/set/inspect_spec.rb create mode 100644 spec/rubyspec/library/set/intersection_spec.rb create mode 100644 spec/rubyspec/library/set/keep_if_spec.rb create mode 100644 spec/rubyspec/library/set/length_spec.rb create mode 100644 spec/rubyspec/library/set/map_spec.rb create mode 100644 spec/rubyspec/library/set/member_spec.rb create mode 100644 spec/rubyspec/library/set/merge_spec.rb create mode 100644 spec/rubyspec/library/set/minus_spec.rb create mode 100644 spec/rubyspec/library/set/plus_spec.rb create mode 100644 spec/rubyspec/library/set/pretty_print_cycle_spec.rb create mode 100644 spec/rubyspec/library/set/pretty_print_spec.rb create mode 100644 spec/rubyspec/library/set/proper_subset_spec.rb create mode 100644 spec/rubyspec/library/set/proper_superset_spec.rb create mode 100644 spec/rubyspec/library/set/reject_spec.rb create mode 100644 spec/rubyspec/library/set/replace_spec.rb create mode 100644 spec/rubyspec/library/set/select_spec.rb create mode 100644 spec/rubyspec/library/set/shared/add.rb create mode 100644 spec/rubyspec/library/set/shared/collect.rb create mode 100644 spec/rubyspec/library/set/shared/difference.rb create mode 100644 spec/rubyspec/library/set/shared/include.rb create mode 100644 spec/rubyspec/library/set/shared/intersection.rb create mode 100644 spec/rubyspec/library/set/shared/length.rb create mode 100644 spec/rubyspec/library/set/shared/union.rb create mode 100644 spec/rubyspec/library/set/size_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/add_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/append_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/classify_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/clear_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/collect_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/constructor_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/delete_if_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/delete_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/difference_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/divide_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/each_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/empty_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/eql_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/equal_value_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/exclusion_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/flatten_merge_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/flatten_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/hash_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/include_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/initialize_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/inspect_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/intersection_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/keep_if_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/length_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/map_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/member_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/merge_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/minus_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/plus_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/pretty_print_cycle_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/pretty_print_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/proper_subset_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/proper_superset_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/reject_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/replace_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/select_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/shared/add.rb create mode 100644 spec/rubyspec/library/set/sortedset/shared/collect.rb create mode 100644 spec/rubyspec/library/set/sortedset/shared/difference.rb create mode 100644 spec/rubyspec/library/set/sortedset/shared/include.rb create mode 100644 spec/rubyspec/library/set/sortedset/shared/intersection.rb create mode 100644 spec/rubyspec/library/set/sortedset/shared/length.rb create mode 100644 spec/rubyspec/library/set/sortedset/shared/union.rb create mode 100644 spec/rubyspec/library/set/sortedset/size_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/subset_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/subtract_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/superset_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/to_a_spec.rb create mode 100644 spec/rubyspec/library/set/sortedset/union_spec.rb create mode 100644 spec/rubyspec/library/set/subset_spec.rb create mode 100644 spec/rubyspec/library/set/subtract_spec.rb create mode 100644 spec/rubyspec/library/set/superset_spec.rb create mode 100644 spec/rubyspec/library/set/to_a_spec.rb create mode 100644 spec/rubyspec/library/set/union_spec.rb create mode 100644 spec/rubyspec/library/shellwords/shellwords_spec.rb create mode 100644 spec/rubyspec/library/singleton/allocate_spec.rb create mode 100644 spec/rubyspec/library/singleton/clone_spec.rb create mode 100644 spec/rubyspec/library/singleton/dump_spec.rb create mode 100644 spec/rubyspec/library/singleton/dup_spec.rb create mode 100644 spec/rubyspec/library/singleton/fixtures/classes.rb create mode 100644 spec/rubyspec/library/singleton/instance_spec.rb create mode 100644 spec/rubyspec/library/singleton/load_spec.rb create mode 100644 spec/rubyspec/library/singleton/new_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/afamily_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/bind_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/canonname_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/initialize_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/inspect_sockaddr_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ip_address_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ip_port_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ip_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ip_unpack_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ipv4_loopback_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ipv4_multicast_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ipv4_private_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ipv6_loopback_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ipv6_multicast_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/pfamily_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/protocol_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/shared/to_sockaddr.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/socktype_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/tcp_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/to_s_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/to_sockaddr_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/udp_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/unix_path_spec.rb create mode 100644 spec/rubyspec/library/socket/addrinfo/unix_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/close_read_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/close_write_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/do_not_reverse_lookup_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/for_fd_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/getpeername_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/getsockname_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/getsockopt_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/ioctl_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/recv_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/recv_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/send_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/setsockopt_spec.rb create mode 100644 spec/rubyspec/library/socket/basicsocket/shutdown_spec.rb create mode 100644 spec/rubyspec/library/socket/constants/constants_spec.rb create mode 100644 spec/rubyspec/library/socket/fixtures/classes.rb create mode 100644 spec/rubyspec/library/socket/fixtures/send_io.txt create mode 100644 spec/rubyspec/library/socket/ipsocket/addr_spec.rb create mode 100644 spec/rubyspec/library/socket/ipsocket/getaddress_spec.rb create mode 100644 spec/rubyspec/library/socket/ipsocket/peeraddr_spec.rb create mode 100644 spec/rubyspec/library/socket/ipsocket/recvfrom_spec.rb create mode 100644 spec/rubyspec/library/socket/option/bool_spec.rb create mode 100644 spec/rubyspec/library/socket/option/inspect_spec.rb create mode 100644 spec/rubyspec/library/socket/option/int_spec.rb create mode 100644 spec/rubyspec/library/socket/option/linger_spec.rb create mode 100644 spec/rubyspec/library/socket/option/new_spec.rb create mode 100644 spec/rubyspec/library/socket/shared/pack_sockaddr.rb create mode 100644 spec/rubyspec/library/socket/shared/partially_closable_sockets.rb create mode 100644 spec/rubyspec/library/socket/shared/recv_nonblock.rb create mode 100644 spec/rubyspec/library/socket/shared/socketpair.rb create mode 100644 spec/rubyspec/library/socket/socket/accept_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/accept_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/bind_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/connect_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/connect_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/for_fd_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/getaddrinfo_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/gethostbyaddr_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/gethostbyname_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/gethostname_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/getnameinfo_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/getservbyname_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/listen_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/new_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/pack_sockaddr_in_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/pack_sockaddr_un_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/pair_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/recvfrom_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/recvfrom_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/sockaddr_in_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/sockaddr_un_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/socket_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/socketpair_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/sysaccept_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/unpack_sockaddr_in_spec.rb create mode 100644 spec/rubyspec/library/socket/socket/unpack_sockaddr_un_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/accept_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/accept_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/gets_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/listen_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/new_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/output_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/readpartial_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpserver/sysaccept_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpsocket/gethostbyname_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpsocket/new_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpsocket/open_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpsocket/partially_closable_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpsocket/recv_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpsocket/setsockopt_spec.rb create mode 100644 spec/rubyspec/library/socket/tcpsocket/shared/new.rb create mode 100644 spec/rubyspec/library/socket/udpsocket/bind_spec.rb create mode 100644 spec/rubyspec/library/socket/udpsocket/connect_spec.rb create mode 100644 spec/rubyspec/library/socket/udpsocket/new_spec.rb create mode 100644 spec/rubyspec/library/socket/udpsocket/open_spec.rb create mode 100644 spec/rubyspec/library/socket/udpsocket/recvfrom_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/udpsocket/send_spec.rb create mode 100644 spec/rubyspec/library/socket/unixserver/accept_nonblock_spec.rb create mode 100644 spec/rubyspec/library/socket/unixserver/accept_spec.rb create mode 100644 spec/rubyspec/library/socket/unixserver/for_fd_spec.rb create mode 100644 spec/rubyspec/library/socket/unixserver/new_spec.rb create mode 100644 spec/rubyspec/library/socket/unixserver/open_spec.rb create mode 100644 spec/rubyspec/library/socket/unixserver/shared/new.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/addr_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/inspect_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/new_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/open_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/pair_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/partially_closable_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/path_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/peeraddr_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/recv_io_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/recvfrom_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/send_io_spec.rb create mode 100644 spec/rubyspec/library/socket/unixsocket/shared/new.rb create mode 100644 spec/rubyspec/library/stringio/append_spec.rb create mode 100644 spec/rubyspec/library/stringio/binmode_spec.rb create mode 100644 spec/rubyspec/library/stringio/bytes_spec.rb create mode 100644 spec/rubyspec/library/stringio/chars_spec.rb create mode 100644 spec/rubyspec/library/stringio/close_read_spec.rb create mode 100644 spec/rubyspec/library/stringio/close_spec.rb create mode 100644 spec/rubyspec/library/stringio/close_write_spec.rb create mode 100644 spec/rubyspec/library/stringio/closed_read_spec.rb create mode 100644 spec/rubyspec/library/stringio/closed_spec.rb create mode 100644 spec/rubyspec/library/stringio/closed_write_spec.rb create mode 100644 spec/rubyspec/library/stringio/codepoints_spec.rb create mode 100644 spec/rubyspec/library/stringio/each_byte_spec.rb create mode 100644 spec/rubyspec/library/stringio/each_char_spec.rb create mode 100644 spec/rubyspec/library/stringio/each_codepoint_spec.rb create mode 100644 spec/rubyspec/library/stringio/each_line_spec.rb create mode 100644 spec/rubyspec/library/stringio/each_spec.rb create mode 100644 spec/rubyspec/library/stringio/eof_spec.rb create mode 100644 spec/rubyspec/library/stringio/external_encoding_spec.rb create mode 100644 spec/rubyspec/library/stringio/fcntl_spec.rb create mode 100644 spec/rubyspec/library/stringio/fileno_spec.rb create mode 100644 spec/rubyspec/library/stringio/fixtures/classes.rb create mode 100644 spec/rubyspec/library/stringio/flush_spec.rb create mode 100644 spec/rubyspec/library/stringio/fsync_spec.rb create mode 100644 spec/rubyspec/library/stringio/getbyte_spec.rb create mode 100644 spec/rubyspec/library/stringio/getc_spec.rb create mode 100644 spec/rubyspec/library/stringio/getch_spec.rb create mode 100644 spec/rubyspec/library/stringio/gets_spec.rb create mode 100644 spec/rubyspec/library/stringio/initialize_spec.rb create mode 100644 spec/rubyspec/library/stringio/internal_encoding_spec.rb create mode 100644 spec/rubyspec/library/stringio/isatty_spec.rb create mode 100644 spec/rubyspec/library/stringio/length_spec.rb create mode 100644 spec/rubyspec/library/stringio/lineno_spec.rb create mode 100644 spec/rubyspec/library/stringio/lines_spec.rb create mode 100644 spec/rubyspec/library/stringio/open_spec.rb create mode 100644 spec/rubyspec/library/stringio/path_spec.rb create mode 100644 spec/rubyspec/library/stringio/pid_spec.rb create mode 100644 spec/rubyspec/library/stringio/pos_spec.rb create mode 100644 spec/rubyspec/library/stringio/print_spec.rb create mode 100644 spec/rubyspec/library/stringio/printf_spec.rb create mode 100644 spec/rubyspec/library/stringio/putc_spec.rb create mode 100644 spec/rubyspec/library/stringio/puts_spec.rb create mode 100644 spec/rubyspec/library/stringio/read_nonblock_spec.rb create mode 100644 spec/rubyspec/library/stringio/read_spec.rb create mode 100644 spec/rubyspec/library/stringio/readbyte_spec.rb create mode 100644 spec/rubyspec/library/stringio/readchar_spec.rb create mode 100644 spec/rubyspec/library/stringio/readline_spec.rb create mode 100644 spec/rubyspec/library/stringio/readlines_spec.rb create mode 100644 spec/rubyspec/library/stringio/readpartial_spec.rb create mode 100644 spec/rubyspec/library/stringio/reopen_spec.rb create mode 100644 spec/rubyspec/library/stringio/rewind_spec.rb create mode 100644 spec/rubyspec/library/stringio/seek_spec.rb create mode 100644 spec/rubyspec/library/stringio/set_encoding_spec.rb create mode 100644 spec/rubyspec/library/stringio/shared/codepoints.rb create mode 100644 spec/rubyspec/library/stringio/shared/each.rb create mode 100644 spec/rubyspec/library/stringio/shared/each_byte.rb create mode 100644 spec/rubyspec/library/stringio/shared/each_char.rb create mode 100644 spec/rubyspec/library/stringio/shared/eof.rb create mode 100644 spec/rubyspec/library/stringio/shared/getc.rb create mode 100644 spec/rubyspec/library/stringio/shared/isatty.rb create mode 100644 spec/rubyspec/library/stringio/shared/length.rb create mode 100644 spec/rubyspec/library/stringio/shared/read.rb create mode 100644 spec/rubyspec/library/stringio/shared/readchar.rb create mode 100644 spec/rubyspec/library/stringio/shared/sysread.rb create mode 100644 spec/rubyspec/library/stringio/shared/tell.rb create mode 100644 spec/rubyspec/library/stringio/shared/write.rb create mode 100644 spec/rubyspec/library/stringio/size_spec.rb create mode 100644 spec/rubyspec/library/stringio/string_spec.rb create mode 100644 spec/rubyspec/library/stringio/stringio_spec.rb create mode 100644 spec/rubyspec/library/stringio/sync_spec.rb create mode 100644 spec/rubyspec/library/stringio/sysread_spec.rb create mode 100644 spec/rubyspec/library/stringio/syswrite_spec.rb create mode 100644 spec/rubyspec/library/stringio/tell_spec.rb create mode 100644 spec/rubyspec/library/stringio/truncate_spec.rb create mode 100644 spec/rubyspec/library/stringio/tty_spec.rb create mode 100644 spec/rubyspec/library/stringio/ungetbyte_spec.rb create mode 100644 spec/rubyspec/library/stringio/ungetc_spec.rb create mode 100644 spec/rubyspec/library/stringio/write_nonblock_spec.rb create mode 100644 spec/rubyspec/library/stringio/write_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/append_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/beginning_of_line_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/bol_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/check_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/check_until_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/clear_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/concat_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/dup_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/element_reference_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/empty_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/eos_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/exist_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/get_byte_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/getbyte_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/getch_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/initialize_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/inspect_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/match_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/matched_size_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/matched_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/must_C_version_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/peek_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/peep_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/pointer_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/pos_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/post_match_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/pre_match_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/reset_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/rest_size_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/rest_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/restsize_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/scan_full_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/scan_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/scan_until_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/search_full_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/bol.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/concat.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/eos.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/extract_range.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/extract_range_matched.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/get_byte.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/matched_size.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/peek.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/pos.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/rest_size.rb create mode 100644 spec/rubyspec/library/stringscanner/shared/terminate.rb create mode 100644 spec/rubyspec/library/stringscanner/skip_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/skip_until_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/string_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/terminate_spec.rb create mode 100644 spec/rubyspec/library/stringscanner/unscan_spec.rb create mode 100644 spec/rubyspec/library/syslog/alert_spec.rb create mode 100644 spec/rubyspec/library/syslog/close_spec.rb create mode 100644 spec/rubyspec/library/syslog/constants_spec.rb create mode 100644 spec/rubyspec/library/syslog/crit_spec.rb create mode 100644 spec/rubyspec/library/syslog/debug_spec.rb create mode 100644 spec/rubyspec/library/syslog/emerg_spec.rb create mode 100644 spec/rubyspec/library/syslog/err_spec.rb create mode 100644 spec/rubyspec/library/syslog/facility_spec.rb create mode 100644 spec/rubyspec/library/syslog/ident_spec.rb create mode 100644 spec/rubyspec/library/syslog/info_spec.rb create mode 100644 spec/rubyspec/library/syslog/inspect_spec.rb create mode 100644 spec/rubyspec/library/syslog/instance_spec.rb create mode 100644 spec/rubyspec/library/syslog/log_spec.rb create mode 100644 spec/rubyspec/library/syslog/mask_spec.rb create mode 100644 spec/rubyspec/library/syslog/notice_spec.rb create mode 100644 spec/rubyspec/library/syslog/open_spec.rb create mode 100644 spec/rubyspec/library/syslog/opened_spec.rb create mode 100644 spec/rubyspec/library/syslog/options_spec.rb create mode 100644 spec/rubyspec/library/syslog/reopen_spec.rb create mode 100644 spec/rubyspec/library/syslog/shared/log.rb create mode 100644 spec/rubyspec/library/syslog/shared/reopen.rb create mode 100644 spec/rubyspec/library/syslog/warning_spec.rb create mode 100644 spec/rubyspec/library/tempfile/_close_spec.rb create mode 100644 spec/rubyspec/library/tempfile/callback_spec.rb create mode 100644 spec/rubyspec/library/tempfile/close_spec.rb create mode 100644 spec/rubyspec/library/tempfile/delete_spec.rb create mode 100644 spec/rubyspec/library/tempfile/initialize_spec.rb create mode 100644 spec/rubyspec/library/tempfile/length_spec.rb create mode 100644 spec/rubyspec/library/tempfile/open_spec.rb create mode 100644 spec/rubyspec/library/tempfile/path_spec.rb create mode 100644 spec/rubyspec/library/tempfile/shared/length.rb create mode 100644 spec/rubyspec/library/tempfile/shared/unlink.rb create mode 100644 spec/rubyspec/library/tempfile/size_spec.rb create mode 100644 spec/rubyspec/library/tempfile/unlink_spec.rb create mode 100644 spec/rubyspec/library/thread/exclusive_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/append_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/clear_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/close_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/closed_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/deq_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/empty_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/enq_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/length_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/num_waiting_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/pop_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/push_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/shift_spec.rb create mode 100644 spec/rubyspec/library/thread/queue/size_spec.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/clear.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/close.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/closed.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/deque.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/empty.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/enque.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/length.rb create mode 100644 spec/rubyspec/library/thread/shared/queue/num_waiting.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/append_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/clear_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/close_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/closed_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/deq_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/empty_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/enq_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/length_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/max_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/new_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/num_waiting_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/pop_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/push_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/shared/enque.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/shift_spec.rb create mode 100644 spec/rubyspec/library/thread/sizedqueue/size_spec.rb create mode 100644 spec/rubyspec/library/time/httpdate_spec.rb create mode 100644 spec/rubyspec/library/time/iso8601_spec.rb create mode 100644 spec/rubyspec/library/time/rfc2822_spec.rb create mode 100644 spec/rubyspec/library/time/rfc822_spec.rb create mode 100644 spec/rubyspec/library/time/shared/rfc2822.rb create mode 100644 spec/rubyspec/library/time/shared/xmlschema.rb create mode 100644 spec/rubyspec/library/time/to_date_spec.rb create mode 100644 spec/rubyspec/library/time/xmlschema_spec.rb create mode 100644 spec/rubyspec/library/timeout/error_spec.rb create mode 100644 spec/rubyspec/library/timeout/timeout_spec.rb create mode 100644 spec/rubyspec/library/tmpdir/dir/mktmpdir_spec.rb create mode 100644 spec/rubyspec/library/tmpdir/dir/tmpdir_spec.rb create mode 100644 spec/rubyspec/library/uri/decode_www_form_component_spec.rb create mode 100644 spec/rubyspec/library/uri/decode_www_form_spec.rb create mode 100644 spec/rubyspec/library/uri/encode_www_form_component_spec.rb create mode 100644 spec/rubyspec/library/uri/encode_www_form_spec.rb create mode 100644 spec/rubyspec/library/uri/eql_spec.rb create mode 100644 spec/rubyspec/library/uri/equality_spec.rb create mode 100644 spec/rubyspec/library/uri/escape/decode_spec.rb create mode 100644 spec/rubyspec/library/uri/escape/encode_spec.rb create mode 100644 spec/rubyspec/library/uri/escape/escape_spec.rb create mode 100644 spec/rubyspec/library/uri/escape/unescape_spec.rb create mode 100644 spec/rubyspec/library/uri/extract_spec.rb create mode 100644 spec/rubyspec/library/uri/fixtures/classes.rb create mode 100644 spec/rubyspec/library/uri/fixtures/normalization.rb create mode 100644 spec/rubyspec/library/uri/ftp/build_spec.rb create mode 100644 spec/rubyspec/library/uri/ftp/merge_spec.rb create mode 100644 spec/rubyspec/library/uri/ftp/new2_spec.rb create mode 100644 spec/rubyspec/library/uri/ftp/path_spec.rb create mode 100644 spec/rubyspec/library/uri/ftp/set_typecode_spec.rb create mode 100644 spec/rubyspec/library/uri/ftp/to_s_spec.rb create mode 100644 spec/rubyspec/library/uri/ftp/typecode_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/absolute_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/build2_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/build_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/coerce_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/component_ary_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/component_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/default_port_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/eql_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/equal_value_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/fragment_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/hash_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/hierarchical_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/host_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/inspect_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/merge_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/minus_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/normalize_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/opaque_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/password_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/path_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/plus_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/port_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/query_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/registry_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/relative_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/route_from_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/route_to_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/scheme_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/select_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_fragment_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_host_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_opaque_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_password_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_path_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_port_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_query_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_registry_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_scheme_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_user_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/set_userinfo_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/to_s_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/use_registry_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/user_spec.rb create mode 100644 spec/rubyspec/library/uri/generic/userinfo_spec.rb create mode 100644 spec/rubyspec/library/uri/http/build_spec.rb create mode 100644 spec/rubyspec/library/uri/http/request_uri_spec.rb create mode 100644 spec/rubyspec/library/uri/join_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/attributes_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/build_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/dn_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/extensions_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/filter_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/hierarchical_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/scope_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/set_attributes_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/set_dn_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/set_extensions_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/set_filter_spec.rb create mode 100644 spec/rubyspec/library/uri/ldap/set_scope_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/build_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/headers_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/set_headers_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/set_to_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/to_mailtext_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/to_rfc822text_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/to_s_spec.rb create mode 100644 spec/rubyspec/library/uri/mailto/to_spec.rb create mode 100644 spec/rubyspec/library/uri/merge_spec.rb create mode 100644 spec/rubyspec/library/uri/normalize_spec.rb create mode 100644 spec/rubyspec/library/uri/parse_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/escape_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/extract_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/inspect_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/join_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/make_regexp_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/parse_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/split_spec.rb create mode 100644 spec/rubyspec/library/uri/parser/unescape_spec.rb create mode 100644 spec/rubyspec/library/uri/plus_spec.rb create mode 100644 spec/rubyspec/library/uri/regexp_spec.rb create mode 100644 spec/rubyspec/library/uri/route_from_spec.rb create mode 100644 spec/rubyspec/library/uri/route_to_spec.rb create mode 100644 spec/rubyspec/library/uri/select_spec.rb create mode 100644 spec/rubyspec/library/uri/set_component_spec.rb create mode 100644 spec/rubyspec/library/uri/shared/eql.rb create mode 100644 spec/rubyspec/library/uri/shared/extract.rb create mode 100644 spec/rubyspec/library/uri/shared/join.rb create mode 100644 spec/rubyspec/library/uri/shared/parse.rb create mode 100644 spec/rubyspec/library/uri/split_spec.rb create mode 100644 spec/rubyspec/library/uri/uri_spec.rb create mode 100644 spec/rubyspec/library/uri/util/make_components_hash_spec.rb create mode 100644 spec/rubyspec/library/weakref/__getobj___spec.rb create mode 100644 spec/rubyspec/library/weakref/fixtures/classes.rb create mode 100644 spec/rubyspec/library/weakref/send_spec.rb create mode 100644 spec/rubyspec/library/weakref/weakref_alive_spec.rb create mode 100644 spec/rubyspec/library/win32ole/fixtures/classes.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/_getproperty_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/_invoke_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/codepage_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/connect_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/const_load_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/constants_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/create_guid_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/invoke_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/locale_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/new_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/ole_func_methods_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/ole_get_methods_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/ole_method_help_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/ole_method_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/ole_methods_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/ole_obj_help_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/ole_put_methods_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/setproperty_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/shared/ole_method.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole/shared/setproperty.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_event/new_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_event/on_event_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/dispid_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/event_interface_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/event_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/helpcontext_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/helpfile_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/helpstring_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/invkind_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/invoke_kind_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/name_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/new_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/offset_vtbl_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/params_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/return_type_detail_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/return_type_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/return_vtype_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/shared/name.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/size_opt_params_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/size_params_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/to_s_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_method/visible_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/default_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/input_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/name_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/ole_type_detail_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/ole_type_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/optional_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/retval_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/shared/name.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_param/to_s_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/guid_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/helpcontext_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/helpfile_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/helpstring_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/major_version_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/minor_version_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/name_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/new_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/ole_classes_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/ole_methods_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/ole_type_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/progid_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/progids_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/shared/name.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/src_type_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/to_s_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/typekind_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/typelibs_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/variables_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_type/visible_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/name_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/ole_type_detail_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/ole_type_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/shared/name.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/to_s_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/value_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/variable_kind_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/varkind_spec.rb create mode 100644 spec/rubyspec/library/win32ole/win32ole_variable/visible_spec.rb create mode 100644 spec/rubyspec/library/yaml/add_builtin_type_spec.rb create mode 100644 spec/rubyspec/library/yaml/add_domain_type_spec.rb create mode 100644 spec/rubyspec/library/yaml/add_private_type_spec.rb create mode 100644 spec/rubyspec/library/yaml/add_ruby_type_spec.rb create mode 100644 spec/rubyspec/library/yaml/detect_implicit_spec.rb create mode 100644 spec/rubyspec/library/yaml/dump_spec.rb create mode 100644 spec/rubyspec/library/yaml/dump_stream_spec.rb create mode 100644 spec/rubyspec/library/yaml/each_node_spec.rb create mode 100644 spec/rubyspec/library/yaml/emitter_spec.rb create mode 100644 spec/rubyspec/library/yaml/fixtures/common.rb create mode 100644 spec/rubyspec/library/yaml/fixtures/example_class.rb create mode 100644 spec/rubyspec/library/yaml/fixtures/strings.rb create mode 100644 spec/rubyspec/library/yaml/fixtures/test_yaml.yml create mode 100644 spec/rubyspec/library/yaml/generic_parser_spec.rb create mode 100644 spec/rubyspec/library/yaml/load_documents_spec.rb create mode 100644 spec/rubyspec/library/yaml/load_file_spec.rb create mode 100644 spec/rubyspec/library/yaml/load_spec.rb create mode 100644 spec/rubyspec/library/yaml/load_stream_spec.rb create mode 100644 spec/rubyspec/library/yaml/object_maker_spec.rb create mode 100644 spec/rubyspec/library/yaml/parse_documents_spec.rb create mode 100644 spec/rubyspec/library/yaml/parse_file_spec.rb create mode 100644 spec/rubyspec/library/yaml/parse_spec.rb create mode 100644 spec/rubyspec/library/yaml/parser_spec.rb create mode 100644 spec/rubyspec/library/yaml/quick_emit_spec.rb create mode 100644 spec/rubyspec/library/yaml/read_type_class_spec.rb create mode 100644 spec/rubyspec/library/yaml/shared/each_document.rb create mode 100644 spec/rubyspec/library/yaml/tagurize_spec.rb create mode 100644 spec/rubyspec/library/yaml/to_yaml_spec.rb create mode 100644 spec/rubyspec/library/yaml/transfer_spec.rb create mode 100644 spec/rubyspec/library/yaml/try_implicit_spec.rb create mode 100644 spec/rubyspec/library/zlib/adler32_spec.rb create mode 100644 spec/rubyspec/library/zlib/crc32_spec.rb create mode 100644 spec/rubyspec/library/zlib/crc_table_spec.rb create mode 100644 spec/rubyspec/library/zlib/deflate/append_spec.rb create mode 100644 spec/rubyspec/library/zlib/deflate/deflate_spec.rb create mode 100644 spec/rubyspec/library/zlib/deflate/flush_spec.rb create mode 100644 spec/rubyspec/library/zlib/deflate/new_spec.rb create mode 100644 spec/rubyspec/library/zlib/deflate/params_spec.rb create mode 100644 spec/rubyspec/library/zlib/deflate/set_dictionary_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/close_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/closed_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/comment_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/crc_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/finish_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/level_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/mtime_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/orig_name_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/os_code_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/sync_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/to_io_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipfile/wrap_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/each_byte_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/each_line_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/each_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/eof_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/getc_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/gets_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/lineno_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/new_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/open_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/pos_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/read_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/readchar_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/readline_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/readlines_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/rewind_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/shared/each.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/tell_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/ungetc_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipreader/unused_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/append_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/comment_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/flush_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/mtime_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/new_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/open_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/orig_name_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/pos_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/print_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/printf_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/putc_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/puts_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/tell_spec.rb create mode 100644 spec/rubyspec/library/zlib/gzipwriter/write_spec.rb create mode 100644 spec/rubyspec/library/zlib/inflate/append_spec.rb create mode 100644 spec/rubyspec/library/zlib/inflate/finish_spec.rb create mode 100644 spec/rubyspec/library/zlib/inflate/inflate_spec.rb create mode 100644 spec/rubyspec/library/zlib/inflate/new_spec.rb create mode 100644 spec/rubyspec/library/zlib/inflate/set_dictionary_spec.rb create mode 100644 spec/rubyspec/library/zlib/inflate/sync_point_spec.rb create mode 100644 spec/rubyspec/library/zlib/inflate/sync_spec.rb create mode 100644 spec/rubyspec/library/zlib/zlib_version_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/adler_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/avail_in_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/avail_out_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/close_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/closed_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/data_type_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/end_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/ended_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/finish_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/finished_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/flush_next_in_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/flush_next_out_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/reset_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/stream_end_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/total_in_spec.rb create mode 100644 spec/rubyspec/library/zlib/zstream/total_out_spec.rb create mode 100644 spec/rubyspec/optional/capi/README create mode 100644 spec/rubyspec/optional/capi/array_spec.rb create mode 100644 spec/rubyspec/optional/capi/bignum_spec.rb create mode 100644 spec/rubyspec/optional/capi/class_spec.rb create mode 100644 spec/rubyspec/optional/capi/complex_spec.rb create mode 100644 spec/rubyspec/optional/capi/constants_spec.rb create mode 100644 spec/rubyspec/optional/capi/data_spec.rb create mode 100644 spec/rubyspec/optional/capi/encoding_spec.rb create mode 100644 spec/rubyspec/optional/capi/enumerator_spec.rb create mode 100644 spec/rubyspec/optional/capi/exception_spec.rb create mode 100644 spec/rubyspec/optional/capi/ext/.gitignore create mode 100644 spec/rubyspec/optional/capi/ext/array_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/bignum_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/boolean_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/class_id_under_autoload_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/class_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/class_under_autoload_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/complex_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/constants_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/data_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/encoding_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/enumerator_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/exception_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/file_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/fixnum_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/float_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/gc_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/globals_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/hash_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/integer_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/io_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/jruby.h create mode 100644 spec/rubyspec/optional/capi/ext/kernel_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/marshal_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/module_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/module_under_autoload_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/mutex_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/numeric_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/object_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/proc_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/range_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/rational_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/regexp_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/rubinius.h create mode 100644 spec/rubyspec/optional/capi/ext/rubyspec.h create mode 100644 spec/rubyspec/optional/capi/ext/string_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/struct_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/symbol_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/thread_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/time_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/truffleruby.h create mode 100644 spec/rubyspec/optional/capi/ext/typed_data_spec.c create mode 100644 spec/rubyspec/optional/capi/ext/util_spec.c create mode 100644 spec/rubyspec/optional/capi/false_spec.rb create mode 100644 spec/rubyspec/optional/capi/file_spec.rb create mode 100644 spec/rubyspec/optional/capi/fixnum_spec.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/class.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/const_get.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/const_get_at.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/const_get_from.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/const_get_object.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/encoding.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/foo.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/module.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/module_autoload.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/path_to_class.rb create mode 100644 spec/rubyspec/optional/capi/fixtures/proc.rb create mode 100644 spec/rubyspec/optional/capi/float_spec.rb create mode 100644 spec/rubyspec/optional/capi/gc_spec.rb create mode 100644 spec/rubyspec/optional/capi/globals_spec.rb create mode 100644 spec/rubyspec/optional/capi/hash_spec.rb create mode 100644 spec/rubyspec/optional/capi/integer_spec.rb create mode 100644 spec/rubyspec/optional/capi/io_spec.rb create mode 100644 spec/rubyspec/optional/capi/kernel_spec.rb create mode 100644 spec/rubyspec/optional/capi/marshal_spec.rb create mode 100644 spec/rubyspec/optional/capi/module_spec.rb create mode 100644 spec/rubyspec/optional/capi/mutex_spec.rb create mode 100644 spec/rubyspec/optional/capi/numeric_spec.rb create mode 100644 spec/rubyspec/optional/capi/object_spec.rb create mode 100644 spec/rubyspec/optional/capi/proc_spec.rb create mode 100644 spec/rubyspec/optional/capi/rake_helper.rb create mode 100644 spec/rubyspec/optional/capi/range_spec.rb create mode 100644 spec/rubyspec/optional/capi/rational_spec.rb create mode 100644 spec/rubyspec/optional/capi/regexp_spec.rb create mode 100644 spec/rubyspec/optional/capi/spec_helper.rb create mode 100644 spec/rubyspec/optional/capi/string_spec.rb create mode 100644 spec/rubyspec/optional/capi/struct_spec.rb create mode 100644 spec/rubyspec/optional/capi/symbol_spec.rb create mode 100644 spec/rubyspec/optional/capi/thread_spec.rb create mode 100644 spec/rubyspec/optional/capi/time_spec.rb create mode 100644 spec/rubyspec/optional/capi/true_spec.rb create mode 100644 spec/rubyspec/optional/capi/typed_data_spec.rb create mode 100644 spec/rubyspec/optional/capi/util_spec.rb create mode 100644 spec/rubyspec/security/cve_2011_4815_spec.rb create mode 100644 spec/rubyspec/security/cve_2013_4164_spec.rb create mode 100644 spec/rubyspec/security/cve_2014_8080_spec.rb create mode 100644 spec/rubyspec/shared/basicobject/method_missing.rb create mode 100644 spec/rubyspec/shared/basicobject/send.rb create mode 100644 spec/rubyspec/shared/complex/Complex.rb create mode 100644 spec/rubyspec/shared/complex/abs.rb create mode 100644 spec/rubyspec/shared/complex/abs2.rb create mode 100644 spec/rubyspec/shared/complex/arg.rb create mode 100644 spec/rubyspec/shared/complex/coerce.rb create mode 100644 spec/rubyspec/shared/complex/conjugate.rb create mode 100644 spec/rubyspec/shared/complex/constants.rb create mode 100644 spec/rubyspec/shared/complex/denominator.rb create mode 100644 spec/rubyspec/shared/complex/divide.rb create mode 100644 spec/rubyspec/shared/complex/equal_value.rb create mode 100644 spec/rubyspec/shared/complex/exponent.rb create mode 100644 spec/rubyspec/shared/complex/float/arg.rb create mode 100644 spec/rubyspec/shared/complex/hash.rb create mode 100644 spec/rubyspec/shared/complex/image.rb create mode 100644 spec/rubyspec/shared/complex/inspect.rb create mode 100644 spec/rubyspec/shared/complex/minus.rb create mode 100644 spec/rubyspec/shared/complex/multiply.rb create mode 100644 spec/rubyspec/shared/complex/numerator.rb create mode 100644 spec/rubyspec/shared/complex/numeric/arg.rb create mode 100644 spec/rubyspec/shared/complex/numeric/conj.rb create mode 100644 spec/rubyspec/shared/complex/numeric/imag.rb create mode 100644 spec/rubyspec/shared/complex/numeric/polar.rb create mode 100644 spec/rubyspec/shared/complex/numeric/real.rb create mode 100644 spec/rubyspec/shared/complex/plus.rb create mode 100644 spec/rubyspec/shared/complex/polar.rb create mode 100644 spec/rubyspec/shared/complex/real.rb create mode 100644 spec/rubyspec/shared/complex/rect.rb create mode 100644 spec/rubyspec/shared/complex/to_s.rb create mode 100644 spec/rubyspec/shared/enumerator/each.rb create mode 100644 spec/rubyspec/shared/enumerator/enum_cons.rb create mode 100644 spec/rubyspec/shared/enumerator/enum_for.rb create mode 100644 spec/rubyspec/shared/enumerator/new.rb create mode 100644 spec/rubyspec/shared/enumerator/next.rb create mode 100644 spec/rubyspec/shared/enumerator/rewind.rb create mode 100644 spec/rubyspec/shared/enumerator/with_index.rb create mode 100644 spec/rubyspec/shared/enumerator/with_object.rb create mode 100644 spec/rubyspec/shared/fiber/resume.rb create mode 100644 spec/rubyspec/shared/file/blockdev.rb create mode 100644 spec/rubyspec/shared/file/chardev.rb create mode 100644 spec/rubyspec/shared/file/directory.rb create mode 100644 spec/rubyspec/shared/file/executable.rb create mode 100644 spec/rubyspec/shared/file/executable_real.rb create mode 100644 spec/rubyspec/shared/file/exist.rb create mode 100644 spec/rubyspec/shared/file/file.rb create mode 100644 spec/rubyspec/shared/file/grpowned.rb create mode 100644 spec/rubyspec/shared/file/identical.rb create mode 100644 spec/rubyspec/shared/file/owned.rb create mode 100644 spec/rubyspec/shared/file/pipe.rb create mode 100644 spec/rubyspec/shared/file/readable.rb create mode 100644 spec/rubyspec/shared/file/readable_real.rb create mode 100644 spec/rubyspec/shared/file/setgid.rb create mode 100644 spec/rubyspec/shared/file/setuid.rb create mode 100644 spec/rubyspec/shared/file/size.rb create mode 100644 spec/rubyspec/shared/file/socket.rb create mode 100644 spec/rubyspec/shared/file/sticky.rb create mode 100644 spec/rubyspec/shared/file/symlink.rb create mode 100644 spec/rubyspec/shared/file/world_readable.rb create mode 100644 spec/rubyspec/shared/file/world_writable.rb create mode 100644 spec/rubyspec/shared/file/writable.rb create mode 100644 spec/rubyspec/shared/file/writable_real.rb create mode 100644 spec/rubyspec/shared/file/zero.rb create mode 100644 spec/rubyspec/shared/io/putc.rb create mode 100644 spec/rubyspec/shared/kernel/equal.rb create mode 100644 spec/rubyspec/shared/kernel/object_id.rb create mode 100644 spec/rubyspec/shared/kernel/raise.rb create mode 100644 spec/rubyspec/shared/math/atanh.rb create mode 100644 spec/rubyspec/shared/process/abort.rb create mode 100644 spec/rubyspec/shared/process/exit.rb create mode 100644 spec/rubyspec/shared/process/fork.rb create mode 100644 spec/rubyspec/shared/rational/Rational.rb create mode 100644 spec/rubyspec/shared/rational/abs.rb create mode 100644 spec/rubyspec/shared/rational/ceil.rb create mode 100644 spec/rubyspec/shared/rational/coerce.rb create mode 100644 spec/rubyspec/shared/rational/comparison.rb create mode 100644 spec/rubyspec/shared/rational/denominator.rb create mode 100644 spec/rubyspec/shared/rational/div.rb create mode 100644 spec/rubyspec/shared/rational/divide.rb create mode 100644 spec/rubyspec/shared/rational/divmod.rb create mode 100644 spec/rubyspec/shared/rational/equal_value.rb create mode 100644 spec/rubyspec/shared/rational/exponent.rb create mode 100644 spec/rubyspec/shared/rational/fdiv.rb create mode 100644 spec/rubyspec/shared/rational/floor.rb create mode 100644 spec/rubyspec/shared/rational/hash.rb create mode 100644 spec/rubyspec/shared/rational/inspect.rb create mode 100644 spec/rubyspec/shared/rational/marshal_dump.rb create mode 100644 spec/rubyspec/shared/rational/marshal_load.rb create mode 100644 spec/rubyspec/shared/rational/minus.rb create mode 100644 spec/rubyspec/shared/rational/modulo.rb create mode 100644 spec/rubyspec/shared/rational/multiply.rb create mode 100644 spec/rubyspec/shared/rational/numerator.rb create mode 100644 spec/rubyspec/shared/rational/plus.rb create mode 100644 spec/rubyspec/shared/rational/quo.rb create mode 100644 spec/rubyspec/shared/rational/remainder.rb create mode 100644 spec/rubyspec/shared/rational/round.rb create mode 100644 spec/rubyspec/shared/rational/to_f.rb create mode 100644 spec/rubyspec/shared/rational/to_i.rb create mode 100644 spec/rubyspec/shared/rational/to_r.rb create mode 100644 spec/rubyspec/shared/rational/to_s.rb create mode 100644 spec/rubyspec/shared/rational/truncate.rb create mode 100644 spec/rubyspec/shared/string/times.rb create mode 100644 spec/rubyspec/shared/time/strftime_for_date.rb create mode 100644 spec/rubyspec/shared/time/strftime_for_time.rb create mode 100644 spec/rubyspec/spec_helper.rb diff --git a/.gitignore b/.gitignore index eeab066c3f..c68d0cc169 100644 --- a/.gitignore +++ b/.gitignore @@ -172,10 +172,6 @@ y.tab.c /gems/*.gem /gems/*-* -# /spec/ -/spec/mspec -/spec/rubyspec - # /tool/ /tool/config.guess /tool/config.sub diff --git a/spec/mspec/.gitignore b/spec/mspec/.gitignore new file mode 100644 index 0000000000..5c5ecd9731 --- /dev/null +++ b/spec/mspec/.gitignore @@ -0,0 +1,26 @@ +pkg +*.rbc +*.iml +*.iws +*.ipr +*.sw? + +.rbx + +# ctags dir +/tags + +*.gem +.bundle +.config +.yardoc +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +rdoc +spec/reports +test/tmp +test/version_tmp +tmp diff --git a/spec/mspec/.travis.yml b/spec/mspec/.travis.yml new file mode 100644 index 0000000000..26a21e3aeb --- /dev/null +++ b/spec/mspec/.travis.yml @@ -0,0 +1,9 @@ +sudo: false +language: ruby +script: + - bundle exec rspec +rvm: + - 2.2.7 + - 2.3.4 + - 2.4.1 + - ruby-head diff --git a/spec/mspec/Gemfile b/spec/mspec/Gemfile new file mode 100644 index 0000000000..82a7d1678b --- /dev/null +++ b/spec/mspec/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in mspec.gemspec +gemspec diff --git a/spec/mspec/Gemfile.lock b/spec/mspec/Gemfile.lock new file mode 100644 index 0000000000..ddd7fde498 --- /dev/null +++ b/spec/mspec/Gemfile.lock @@ -0,0 +1,30 @@ +PATH + remote: . + specs: + mspec (1.8.0) + +GEM + remote: https://rubygems.org/ + specs: + diff-lcs (1.2.5) + rake (10.4.2) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.8) + rspec-expectations (2.14.5) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.6) + +PLATFORMS + java + ruby + +DEPENDENCIES + mspec! + rake (~> 10.0) + rspec (~> 2.14.1) + +BUNDLED WITH + 1.10.2 diff --git a/spec/mspec/LICENSE b/spec/mspec/LICENSE new file mode 100644 index 0000000000..d581dd1c9f --- /dev/null +++ b/spec/mspec/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2008 Engine Yard, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/spec/mspec/README.md b/spec/mspec/README.md new file mode 100644 index 0000000000..8420fc1fce --- /dev/null +++ b/spec/mspec/README.md @@ -0,0 +1,88 @@ +[![Build Status](https://travis-ci.org/ruby/mspec.svg?branch=master)](https://travis-ci.org/ruby/mspec) + +## Overview + +MSpec is a specialized framework that is syntax-compatible with RSpec for +basic things like 'describe', 'it' blocks and 'before', 'after' actions. MSpec +contains additional features that assist in writing the RubySpecs used by +multiple Ruby implementations. + +MSpec attempts to use the simplest Ruby language features so that beginning +Ruby implementations can run the Ruby specs. + +MSpec is not intended as a replacement for RSpec. MSpec attempts to provide a +subset of RSpec's features in some cases and a superset in others. It does not +provide all the matchers, for instance. + +However, MSpec provides several extensions to facilitate writing the Ruby +specs in a manner compatible with multiple Ruby implementations. + + 1. MSpec offers a set of guards to control execution of the specs. These + guards not only enable or disable execution but also annotate the specs + with additional information about why they are run or not run. + + 2. MSpec provides a different shared spec implementation specifically + designed to ease writing specs for the numerous aliased methods in Ruby. + The MSpec shared spec implementation should not conflict with RSpec's own + shared behavior facility. + + 3. MSpec provides various helper methods to simplify some specs, for + example, creating temporary file names. + + 4. MSpec has several specialized runner scripts that includes a + configuration facility with a default project file and user-specific + overrides. + + +## Bundler + +A Gemfile is provided. Use Bundler to install gem dependencies. To install +Bundler, run the following: + +```bash +gem install bundler +``` + +To install the gem dependencies with Bundler, run the following: + +```bash +ruby -S bundle install +``` + +## Running Specs + +Use RSpec to run the MSpec specs. There are no plans currently to make the +MSpec specs runnable by MSpec. + +After installing the gem dependencies, the specs can be run as follows: + +```bash +ruby -S bundle exec rspec +``` + +Or + +```bash +ruby -S rake +``` + +To run an individual spec file, use the following example: + +```bash +ruby -S bundle exec rspec spec/helpers/ruby_exe_spec.rb +``` + + +## Documentation + +See http://ruby.github.io/rubyspec.github.io/ + + +## Source Code + +See https://github.com/ruby/mspec + + +## License + +See the LICENSE in the source code. diff --git a/spec/mspec/Rakefile b/spec/mspec/Rakefile new file mode 100644 index 0000000000..0e294cde8e --- /dev/null +++ b/spec/mspec/Rakefile @@ -0,0 +1,7 @@ +require 'bundler/gem_tasks' +require 'bundler/setup' +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/spec/mspec/bin/mkspec b/spec/mspec/bin/mkspec new file mode 100755 index 0000000000..00f1fdff47 --- /dev/null +++ b/spec/mspec/bin/mkspec @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path('../../lib', __FILE__) + +require 'mspec/commands/mkspec' + +MkSpec.main diff --git a/spec/mspec/bin/mkspec.bat b/spec/mspec/bin/mkspec.bat new file mode 100755 index 0000000000..1073d20a9b --- /dev/null +++ b/spec/mspec/bin/mkspec.bat @@ -0,0 +1 @@ +@"ruby.exe" "%~dpn0" %* diff --git a/spec/mspec/bin/mspec b/spec/mspec/bin/mspec new file mode 100755 index 0000000000..f833257bb0 --- /dev/null +++ b/spec/mspec/bin/mspec @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path('../../lib', __FILE__) + +require 'mspec/commands/mspec' + +MSpecMain.main diff --git a/spec/mspec/bin/mspec-ci b/spec/mspec/bin/mspec-ci new file mode 100755 index 0000000000..d7cd50a827 --- /dev/null +++ b/spec/mspec/bin/mspec-ci @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path('../../lib', __FILE__) + +require 'mspec/commands/mspec-ci' + +MSpecCI.main diff --git a/spec/mspec/bin/mspec-ci.bat b/spec/mspec/bin/mspec-ci.bat new file mode 100755 index 0000000000..1073d20a9b --- /dev/null +++ b/spec/mspec/bin/mspec-ci.bat @@ -0,0 +1 @@ +@"ruby.exe" "%~dpn0" %* diff --git a/spec/mspec/bin/mspec-run b/spec/mspec/bin/mspec-run new file mode 100755 index 0000000000..010ecefe35 --- /dev/null +++ b/spec/mspec/bin/mspec-run @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path('../../lib', __FILE__) + +require 'mspec/commands/mspec-run' + +MSpecRun.main diff --git a/spec/mspec/bin/mspec-run.bat b/spec/mspec/bin/mspec-run.bat new file mode 100755 index 0000000000..1073d20a9b --- /dev/null +++ b/spec/mspec/bin/mspec-run.bat @@ -0,0 +1 @@ +@"ruby.exe" "%~dpn0" %* diff --git a/spec/mspec/bin/mspec-tag b/spec/mspec/bin/mspec-tag new file mode 100755 index 0000000000..a5f9fffaaa --- /dev/null +++ b/spec/mspec/bin/mspec-tag @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path('../../lib', __FILE__) + +require 'mspec/commands/mspec-tag' + +MSpecTag.main diff --git a/spec/mspec/bin/mspec-tag.bat b/spec/mspec/bin/mspec-tag.bat new file mode 100755 index 0000000000..1073d20a9b --- /dev/null +++ b/spec/mspec/bin/mspec-tag.bat @@ -0,0 +1 @@ +@"ruby.exe" "%~dpn0" %* diff --git a/spec/mspec/bin/mspec.bat b/spec/mspec/bin/mspec.bat new file mode 100755 index 0000000000..1073d20a9b --- /dev/null +++ b/spec/mspec/bin/mspec.bat @@ -0,0 +1 @@ +@"ruby.exe" "%~dpn0" %* diff --git a/spec/mspec/lib/mspec.rb b/spec/mspec/lib/mspec.rb new file mode 100644 index 0000000000..42d590c99a --- /dev/null +++ b/spec/mspec/lib/mspec.rb @@ -0,0 +1,20 @@ +require 'mspec/matchers' +require 'mspec/expectations' +require 'mspec/mocks' +require 'mspec/runner' +require 'mspec/guards' +require 'mspec/helpers' +require 'mspec/version' + +# If the implementation on which the specs are run cannot +# load pp from the standard library, add a pp.rb file that +# defines the #pretty_inspect method on Object or Kernel. +begin + require 'pp' +rescue LoadError + module Kernel + def pretty_inspect + inspect + end + end +end diff --git a/spec/mspec/lib/mspec/commands/mkspec.rb b/spec/mspec/lib/mspec/commands/mkspec.rb new file mode 100755 index 0000000000..7a943aa1fe --- /dev/null +++ b/spec/mspec/lib/mspec/commands/mkspec.rb @@ -0,0 +1,155 @@ +#!/usr/bin/env ruby + +require 'rbconfig' +require 'mspec/version' +require 'mspec/utils/options' +require 'mspec/utils/name_map' +require 'mspec/helpers/fs' + +class MkSpec + attr_reader :config + + def initialize + @config = { + :constants => [], + :requires => [], + :base => "core", + :version => nil + } + @map = NameMap.new true + end + + def options(argv=ARGV) + options = MSpecOptions.new "mkspec [options]", 32 + + options.on("-c", "--constant", "CONSTANT", + "Class or Module to generate spec stubs for") do |name| + config[:constants] << name + end + options.on("-b", "--base", "DIR", + "Directory to generate specs into") do |directory| + config[:base] = File.expand_path directory + end + options.on("-r", "--require", "LIBRARY", + "A library to require") do |file| + config[:requires] << file + end + options.on("-V", "--version-guard", "VERSION", + "Specify version for ruby_version_is guards") do |version| + config[:version] = version + end + options.version MSpec::VERSION + options.help + + options.doc "\n How might this work in the real world?\n" + options.doc " 1. To create spec stubs for every class or module in Object\n" + options.doc " $ mkspec\n" + options.doc " 2. To create spec stubs for Fixnum\n" + options.doc " $ mkspec -c Fixnum\n" + options.doc " 3. To create spec stubs for Complex in 'superspec/complex'\n" + options.doc " $ mkspec -c Complex -r complex -b superspec" + options.doc "" + + options.parse argv + end + + def create_directory(mod) + subdir = @map.dir_name mod, config[:base] + + if File.exist? subdir + unless File.directory? subdir + puts "#{subdir} already exists and is not a directory." + return nil + end + else + mkdir_p subdir + end + + subdir + end + + def write_requires(dir, file) + prefix = config[:base] + '/' + raise dir unless dir.start_with? prefix + sub = dir[prefix.size..-1] + parents = '../' * (sub.split('/').length + 1) + + File.open(file, 'w') do |f| + f.puts "require File.expand_path('../#{parents}spec_helper', __FILE__)" + config[:requires].each do |lib| + f.puts "require '#{lib}'" + end + end + end + + def write_version(f) + f.puts "" + if version = config[:version] + f.puts "ruby_version_is #{version} do" + yield " " + f.puts "end" + else + yield "" + end + end + + def write_spec(file, meth, exists) + if exists + out = `#{ruby} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e '#{meth}' #{file}` + return if out.include?(meth) + end + + File.open file, 'a' do |f| + write_version(f) do |indent| + f.puts <<-EOS +#{indent}describe "#{meth}" do +#{indent} it "needs to be reviewed for spec completeness" +#{indent}end +EOS + end + end + + puts file + end + + def create_file(dir, mod, meth, name) + file = File.join dir, @map.file_name(meth, mod) + exists = File.exist? file + + write_requires dir, file unless exists + write_spec file, name, exists + end + + def run + config[:requires].each { |lib| require lib } + constants = config[:constants] + constants = Object.constants if constants.empty? + + @map.map({}, constants).each do |mod, methods| + name = mod.chop + next unless dir = create_directory(name) + + methods.each { |method| create_file dir, name, method, mod + method } + end + end + + ## + # Determine and return the path of the ruby executable. + + def ruby + ruby = File.join(RbConfig::CONFIG['bindir'], + RbConfig::CONFIG['ruby_install_name']) + + ruby.gsub! File::SEPARATOR, File::ALT_SEPARATOR if File::ALT_SEPARATOR + + return ruby + end + + def self.main + ENV['MSPEC_RUNNER'] = '1' + + script = new + script.options + script.run + end +end diff --git a/spec/mspec/lib/mspec/commands/mspec-ci.rb b/spec/mspec/lib/mspec/commands/mspec-ci.rb new file mode 100644 index 0000000000..225d2bb96d --- /dev/null +++ b/spec/mspec/lib/mspec/commands/mspec-ci.rb @@ -0,0 +1,79 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') + +require 'mspec/version' +require 'mspec/utils/options' +require 'mspec/utils/script' + + +class MSpecCI < MSpecScript + def options(argv=ARGV) + options = MSpecOptions.new "mspec ci [options] (FILE|DIRECTORY|GLOB)+", 30, config + + options.doc " Ask yourself:" + options.doc " 1. How to run the specs?" + options.doc " 2. How to modify the guard behavior?" + options.doc " 2. How to display the output?" + options.doc " 3. What action to perform?" + options.doc " 4. When to perform it?" + + options.doc "\n How to run the specs" + options.chdir + options.prefix + options.configure { |f| load f } + options.name + options.pretend + options.interrupt + + options.doc "\n How to modify the guard behavior" + options.unguarded + options.verify + + options.doc "\n How to display their output" + options.formatters + options.verbose + + options.doc "\n What action to perform" + options.actions + + options.doc "\n When to perform it" + options.action_filters + + options.doc "\n Help!" + options.debug + options.version MSpec::VERSION + options.help + + options.doc "\n Custom options" + custom_options options + + options.doc "\n How might this work in the real world?" + options.doc "\n 1. To simply run the known good specs" + options.doc "\n $ mspec ci" + options.doc "\n 2. To run a subset of the known good specs" + options.doc "\n $ mspec ci path/to/specs" + options.doc "\n 3. To start the debugger before the spec matching 'this crashes'" + options.doc "\n $ mspec ci --spec-debug -S 'this crashes'" + options.doc "" + + patterns = options.parse argv + patterns = config[:ci_files] if patterns.empty? + @files = files patterns + end + + def run + MSpec.register_tags_patterns config[:tags_patterns] + MSpec.register_files @files + + tags = ["fails", "critical", "unstable", "incomplete", "unsupported"] + tags += Array(config[:ci_xtags]) + + require 'mspec/runner/filters/tag' + filter = TagFilter.new(:exclude, *tags) + filter.register + + MSpec.process + exit MSpec.exit_code + end +end diff --git a/spec/mspec/lib/mspec/commands/mspec-run.rb b/spec/mspec/lib/mspec/commands/mspec-run.rb new file mode 100644 index 0000000000..45b26e88ad --- /dev/null +++ b/spec/mspec/lib/mspec/commands/mspec-run.rb @@ -0,0 +1,87 @@ +#!/usr/bin/env ruby + +$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') + +require 'mspec/version' +require 'mspec/utils/options' +require 'mspec/utils/script' + + +class MSpecRun < MSpecScript + def initialize + super + + config[:files] = [] + end + + def options(argv=ARGV) + options = MSpecOptions.new "mspec run [options] (FILE|DIRECTORY|GLOB)+", 30, config + + options.doc " Ask yourself:" + options.doc " 1. What specs to run?" + options.doc " 2. How to modify the execution?" + options.doc " 3. How to modify the guard behavior?" + options.doc " 4. How to display the output?" + options.doc " 5. What action to perform?" + options.doc " 6. When to perform it?" + + options.doc "\n What specs to run" + options.filters + + options.doc "\n How to modify the execution" + options.chdir + options.prefix + options.configure { |f| load f } + options.name + options.randomize + options.repeat + options.pretend + options.interrupt + + options.doc "\n How to modify the guard behavior" + options.unguarded + options.verify + + options.doc "\n How to display their output" + options.formatters + options.verbose + + options.doc "\n What action to perform" + options.actions + + options.doc "\n When to perform it" + options.action_filters + + options.doc "\n Help!" + options.debug + options.version MSpec::VERSION + options.help + + options.doc "\n Custom options" + custom_options options + + options.doc "\n How might this work in the real world?" + options.doc "\n 1. To simply run some specs" + options.doc "\n $ mspec path/to/the/specs" + options.doc " mspec path/to/the_file_spec.rb" + options.doc "\n 2. To run specs tagged with 'fails'" + options.doc "\n $ mspec -g fails path/to/the_file_spec.rb" + options.doc "\n 3. To start the debugger before the spec matching 'this crashes'" + options.doc "\n $ mspec --spec-debug -S 'this crashes' path/to/the_file_spec.rb" + options.doc "\n 4. To run some specs matching 'this crashes'" + options.doc "\n $ mspec -e 'this crashes' path/to/the_file_spec.rb" + + options.doc "" + + patterns = options.parse argv + @files = files_from_patterns(patterns) + end + + def run + MSpec.register_tags_patterns config[:tags_patterns] + MSpec.register_files @files + + MSpec.process + exit MSpec.exit_code + end +end diff --git a/spec/mspec/lib/mspec/commands/mspec-tag.rb b/spec/mspec/lib/mspec/commands/mspec-tag.rb new file mode 100644 index 0000000000..7582015916 --- /dev/null +++ b/spec/mspec/lib/mspec/commands/mspec-tag.rb @@ -0,0 +1,133 @@ +#!/usr/bin/env ruby + +require 'mspec/version' +require 'mspec/utils/options' +require 'mspec/utils/script' + + +class MSpecTag < MSpecScript + def initialize + super + + config[:tagger] = :add + config[:tag] = 'fails:' + config[:outcome] = :fail + config[:ltags] = [] + end + + def options(argv=ARGV) + options = MSpecOptions.new "mspec tag [options] (FILE|DIRECTORY|GLOB)+", 30, config + + options.doc " Ask yourself:" + options.doc " 1. What specs to run?" + options.doc " 2. How to modify the execution?" + options.doc " 3. How to display the output?" + options.doc " 4. What tag action to perform?" + options.doc " 5. When to perform it?" + + options.doc "\n What specs to run" + options.filters + + options.doc "\n How to modify the execution" + options.configure { |f| load f } + options.name + options.pretend + options.unguarded + options.interrupt + + options.doc "\n How to display their output" + options.formatters + options.verbose + + options.doc "\n What action to perform and when to perform it" + options.on("-N", "--add", "TAG", + "Add TAG with format 'tag' or 'tag(comment)' (see -Q, -F, -L)") do |o| + config[:tagger] = :add + config[:tag] = "#{o}:" + end + options.on("-R", "--del", "TAG", + "Delete TAG (see -Q, -F, -L)") do |o| + config[:tagger] = :del + config[:tag] = "#{o}:" + config[:outcome] = :pass + end + options.on("-Q", "--pass", "Apply action to specs that pass (default for --del)") do + config[:outcome] = :pass + end + options.on("-F", "--fail", "Apply action to specs that fail (default for --add)") do + config[:outcome] = :fail + end + options.on("-L", "--all", "Apply action to all specs") do + config[:outcome] = :all + end + options.on("--list", "TAG", "Display descriptions of any specs tagged with TAG") do |t| + config[:tagger] = :list + config[:ltags] << t + end + options.on("--list-all", "Display descriptions of any tagged specs") do + config[:tagger] = :list_all + end + options.on("--purge", "Remove all tags not matching any specs") do + config[:tagger] = :purge + end + + options.doc "\n Help!" + options.debug + options.version MSpec::VERSION + options.help + + options.doc "\n Custom options" + custom_options options + + options.doc "\n How might this work in the real world?" + options.doc "\n 1. To add the 'fails' tag to failing specs" + options.doc "\n $ mspec tag path/to/the_file_spec.rb" + options.doc "\n 2. To remove the 'fails' tag from passing specs" + options.doc "\n $ mspec tag --del fails path/to/the_file_spec.rb" + options.doc "\n 3. To display the descriptions for all specs tagged with 'fails'" + options.doc "\n $ mspec tag --list fails path/to/the/specs" + options.doc "" + + patterns = options.parse argv + if patterns.empty? + puts options + puts "No files specified." + exit 1 + end + @files = files patterns + end + + def register + require 'mspec/runner/actions' + + case config[:tagger] + when :add, :del + tag = SpecTag.new config[:tag] + tagger = TagAction.new(config[:tagger], config[:outcome], tag.tag, tag.comment, + config[:atags], config[:astrings]) + when :list, :list_all + tagger = TagListAction.new config[:tagger] == :list_all ? nil : config[:ltags] + MSpec.register_mode :pretend + config[:formatter] = false + when :purge + tagger = TagPurgeAction.new + MSpec.register_mode :pretend + MSpec.register_mode :unguarded + config[:formatter] = false + else + raise ArgumentError, "No recognized action given" + end + tagger.register + + super + end + + def run + MSpec.register_tags_patterns config[:tags_patterns] + MSpec.register_files @files + + MSpec.process + exit MSpec.exit_code + end +end + diff --git a/spec/mspec/lib/mspec/commands/mspec.rb b/spec/mspec/lib/mspec/commands/mspec.rb new file mode 100755 index 0000000000..6f1ae8cb6e --- /dev/null +++ b/spec/mspec/lib/mspec/commands/mspec.rb @@ -0,0 +1,163 @@ +#!/usr/bin/env ruby + +require 'mspec/version' +require 'mspec/utils/options' +require 'mspec/utils/script' +require 'mspec/helpers/tmp' +require 'mspec/runner/actions/filter' +require 'mspec/runner/actions/timer' + + +class MSpecMain < MSpecScript + def initialize + super + + config[:loadpath] = [] + config[:requires] = [] + config[:target] = ENV['RUBY'] || 'ruby' + config[:flags] = [] + config[:command] = nil + config[:options] = [] + config[:launch] = [] + end + + def options(argv=ARGV) + config[:command] = argv.shift if ["ci", "run", "tag"].include?(argv[0]) + + options = MSpecOptions.new "mspec [COMMAND] [options] (FILE|DIRECTORY|GLOB)+", 30, config + + options.doc " The mspec command sets up and invokes the sub-commands" + options.doc " (see below) to enable, for instance, running the specs" + options.doc " with different implementations like ruby, jruby, rbx, etc.\n" + + options.configure do |f| + load f + config[:options] << '-B' << f + end + + options.targets + + options.on("--warnings", "Don't supress warnings") do + config[:flags] << '-w' + ENV['OUTPUT_WARNINGS'] = '1' + end + + options.on("-j", "--multi", "Run multiple (possibly parallel) subprocesses") do + config[:multi] = true + config[:options] << "-fy" + end + + options.version MSpec::VERSION do + if config[:command] + config[:options] << "-v" + else + puts "#{File.basename $0} #{MSpec::VERSION}" + exit + end + end + + options.help do + if config[:command] + config[:options] << "-h" + else + puts options + exit 1 + end + end + + options.doc "\n Custom options" + custom_options options + + # The rest of the help output + options.doc "\n where COMMAND is one of:\n" + options.doc " run - Run the specified specs (default)" + options.doc " ci - Run the known good specs" + options.doc " tag - Add or remove tags\n" + options.doc " mspec COMMAND -h for more options\n" + options.doc " example: $ mspec run -h\n" + + options.on_extra { |o| config[:options] << o } + options.parse(argv) + + if config[:multi] + options = MSpecOptions.new "mspec", 30, config + options.all + patterns = options.parse(config[:options]) + @files = files_from_patterns(patterns) + end + end + + def register; end + + def multi_exec(argv) + MSpec.register_files @files + + require 'mspec/runner/formatters/multi' + formatter = MultiFormatter.new + + output_files = [] + processes = [cores, @files.size].min + children = processes.times.map { |i| + name = tmp "mspec-multi-#{i}" + output_files << name + + env = { + "SPEC_TEMP_DIR" => "rubyspec_temp_#{i}", + "MSPEC_MULTI" => i.to_s + } + command = argv + ["-o", name] + $stderr.puts "$ #{command.join(' ')}" if $MSPEC_DEBUG + IO.popen([env, *command], "rb+") + } + + puts children.map { |child| child.gets }.uniq + formatter.start + + until @files.empty? + IO.select(children)[0].each { |io| + reply = io.read(1) + case reply + when '.' + formatter.unload + when nil + raise "Worker died!" + else + while chunk = (io.read_nonblock(4096) rescue nil) + reply += chunk + end + raise reply + end + io.puts @files.shift unless @files.empty? + } + end + + ok = true + children.each { |child| + child.puts "QUIT" + Process.wait(child.pid) + ok &&= $?.success? + } + + formatter.aggregate_results(output_files) + formatter.finish + ok + end + + def run + argv = config[:target].split(/\s+/) + + argv.concat config[:launch] + argv.concat config[:flags] + argv.concat config[:loadpath] + argv.concat config[:requires] + argv << "#{MSPEC_HOME}/bin/mspec-#{ config[:command] || "run" }" + argv.concat config[:options] + + if config[:multi] + exit multi_exec(argv) + else + $stderr.puts "$ #{argv.join(' ')}" + exec(*argv) + end + end +end diff --git a/spec/mspec/lib/mspec/expectations.rb b/spec/mspec/lib/mspec/expectations.rb new file mode 100644 index 0000000000..d07f959b27 --- /dev/null +++ b/spec/mspec/lib/mspec/expectations.rb @@ -0,0 +1,2 @@ +require 'mspec/expectations/expectations' +require 'mspec/expectations/should' diff --git a/spec/mspec/lib/mspec/expectations/expectations.rb b/spec/mspec/lib/mspec/expectations/expectations.rb new file mode 100644 index 0000000000..cfdc2b63a3 --- /dev/null +++ b/spec/mspec/lib/mspec/expectations/expectations.rb @@ -0,0 +1,21 @@ +class SpecExpectationNotMetError < StandardError +end + +class SpecExpectationNotFoundError < StandardError + def message + "No behavior expectation was found in the example" + end +end + +class SpecExpectation + def self.fail_with(expected, actual) + expected_to_s = expected.to_s + actual_to_s = actual.to_s + if expected_to_s.size + actual_to_s.size > 80 + message = "#{expected_to_s.chomp}\n#{actual_to_s}" + else + message = "#{expected_to_s} #{actual_to_s}" + end + Kernel.raise SpecExpectationNotMetError, message + end +end diff --git a/spec/mspec/lib/mspec/expectations/should.rb b/spec/mspec/lib/mspec/expectations/should.rb new file mode 100644 index 0000000000..f6d83053f5 --- /dev/null +++ b/spec/mspec/lib/mspec/expectations/should.rb @@ -0,0 +1,29 @@ +class Object + NO_MATCHER_GIVEN = Object.new + + def should(matcher = NO_MATCHER_GIVEN) + MSpec.expectation + MSpec.actions :expectation, MSpec.current.state + unless matcher.equal? NO_MATCHER_GIVEN + unless matcher.matches? self + expected, actual = matcher.failure_message + SpecExpectation.fail_with(expected, actual) + end + else + SpecPositiveOperatorMatcher.new(self) + end + end + + def should_not(matcher = NO_MATCHER_GIVEN) + MSpec.expectation + MSpec.actions :expectation, MSpec.current.state + unless matcher.equal? NO_MATCHER_GIVEN + if matcher.matches? self + expected, actual = matcher.negative_failure_message + SpecExpectation.fail_with(expected, actual) + end + else + SpecNegativeOperatorMatcher.new(self) + end + end +end diff --git a/spec/mspec/lib/mspec/guards.rb b/spec/mspec/lib/mspec/guards.rb new file mode 100644 index 0000000000..0d7d300c64 --- /dev/null +++ b/spec/mspec/lib/mspec/guards.rb @@ -0,0 +1,12 @@ +require 'mspec/utils/ruby_name' +require 'mspec/guards/block_device' +require 'mspec/guards/bug' +require 'mspec/guards/conflict' +require 'mspec/guards/endian' +require 'mspec/guards/feature' +require 'mspec/guards/guard' +require 'mspec/guards/platform' +require 'mspec/guards/quarantine' +require 'mspec/guards/support' +require 'mspec/guards/superuser' +require 'mspec/guards/version' diff --git a/spec/mspec/lib/mspec/guards/block_device.rb b/spec/mspec/lib/mspec/guards/block_device.rb new file mode 100644 index 0000000000..327f6e564e --- /dev/null +++ b/spec/mspec/lib/mspec/guards/block_device.rb @@ -0,0 +1,18 @@ +require 'mspec/guards/guard' + +class BlockDeviceGuard < SpecGuard + def match? + platform_is_not :freebsd, :windows, :opal do + block = `find /dev /devices -type b 2> /dev/null` + return !(block.nil? || block.empty?) + end + + false + end +end + +class Object + def with_block_device(&block) + BlockDeviceGuard.new.run_if(:with_block_device, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/bug.rb b/spec/mspec/lib/mspec/guards/bug.rb new file mode 100644 index 0000000000..31de6e080d --- /dev/null +++ b/spec/mspec/lib/mspec/guards/bug.rb @@ -0,0 +1,30 @@ +require 'mspec/guards/version' + +class BugGuard < VersionGuard + def initialize(bug, version) + @bug = bug + if String === version + MSpec.deprecate "ruby_bug with a single version", 'an exclusive range ("2.1"..."2.3")' + @version = SpecVersion.new version, true + else + super(version) + end + @parameters = [@bug, @version] + end + + def match? + return false if MSpec.mode? :no_ruby_bug + return false unless PlatformGuard.standard? + if Range === @version + super + else + FULL_RUBY_VERSION <= @version + end + end +end + +class Object + def ruby_bug(bug, version, &block) + BugGuard.new(bug, version).run_unless(:ruby_bug, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/conflict.rb b/spec/mspec/lib/mspec/guards/conflict.rb new file mode 100644 index 0000000000..c1d33e3512 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/conflict.rb @@ -0,0 +1,19 @@ +require 'mspec/guards/guard' + +class ConflictsGuard < SpecGuard + def match? + # Always convert constants to symbols regardless of version. + constants = Object.constants.map { |x| x.to_sym } + @parameters.any? { |mod| constants.include? mod } + end +end + +class Object + # In some cases, libraries will modify another Ruby method's + # behavior. The specs for the method's behavior will then fail + # if that library is loaded. This guard will not run if any of + # the specified constants exist in Object.constants. + def conflicts_with(*modules, &block) + ConflictsGuard.new(*modules).run_unless(:conflicts_with, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/endian.rb b/spec/mspec/lib/mspec/guards/endian.rb new file mode 100644 index 0000000000..6bb01263c7 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/endian.rb @@ -0,0 +1,27 @@ +require 'mspec/guards/guard' + +# Despite that these are inverses, the two classes are +# used to simplify MSpec guard reporting modes + +class EndianGuard < SpecGuard + def pattern + @pattern ||= [1].pack('L') + end + private :pattern +end + +class BigEndianGuard < EndianGuard + def match? + pattern[-1] == ?\001 + end +end + +class Object + def big_endian(&block) + BigEndianGuard.new.run_if(:big_endian, &block) + end + + def little_endian(&block) + BigEndianGuard.new.run_unless(:little_endian, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/feature.rb b/spec/mspec/lib/mspec/guards/feature.rb new file mode 100644 index 0000000000..346212bda0 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/feature.rb @@ -0,0 +1,43 @@ +require 'mspec/guards/guard' + +class FeatureGuard < SpecGuard + def self.enabled?(*features) + new(*features).match? + end + + def match? + @parameters.all? { |f| MSpec.feature_enabled? f } + end +end + +class Object + # Provides better documentation in the specs by + # naming sets of features that work together as + # a whole. Examples include :encoding, :fiber, + # :continuation, :fork. + # + # Usage example: + # + # with_feature :encoding do + # # specs for a method that provides aspects + # # of the encoding feature + # end + # + # Multiple features must all be enabled for the + # guard to run: + # + # with_feature :one, :two do + # # these specs will run if features :one AND + # # :two are enabled. + # end + # + # The implementation must explicitly enable a feature + # by adding code like the following to the .mspec + # configuration file: + # + # MSpec.enable_feature :encoding + # + def with_feature(*features, &block) + FeatureGuard.new(*features).run_if(:with_feature, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/guard.rb b/spec/mspec/lib/mspec/guards/guard.rb new file mode 100644 index 0000000000..c95d8f7923 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/guard.rb @@ -0,0 +1,118 @@ +require 'mspec/runner/mspec' +require 'mspec/runner/actions/tally' +require 'mspec/utils/ruby_name' + +class SpecGuard + def self.report + @report ||= Hash.new { |h,k| h[k] = [] } + end + + def self.clear + @report = nil + end + + def self.finish + report.keys.sort.each do |key| + desc = report[key] + size = desc.size + spec = size == 1 ? "spec" : "specs" + print "\n\n#{size} #{spec} omitted by guard: #{key}:\n" + desc.each { |description| print "\n", description; } + end + + print "\n\n" + end + + def self.guards + @guards ||= [] + end + + def self.clear_guards + @guards = [] + end + + # Returns a partial Ruby version string based on +which+. + # For example, if RUBY_VERSION = 8.2.3: + # + # :major => "8" + # :minor => "8.2" + # :tiny => "8.2.3" + # :teeny => "8.2.3" + # :full => "8.2.3" + def self.ruby_version(which = :minor) + case which + when :major + n = 1 + when :minor + n = 2 + when :tiny, :teeny, :full + n = 3 + end + + RUBY_VERSION.split('.')[0,n].join('.') + end + + attr_accessor :name + + def initialize(*args) + @parameters = args + end + + def yield?(invert = false) + return true if MSpec.mode? :unguarded + + allow = match? ^ invert + + if !allow and reporting? + MSpec.guard + MSpec.register :finish, SpecGuard + MSpec.register :add, self + return true + elsif MSpec.mode? :verify + return true + end + + allow + end + + def run_if(name, &block) + @name = name + yield if yield?(false) + ensure + unregister + end + + def run_unless(name, &block) + @name = name + yield if yield?(true) + ensure + unregister + end + + def reporting? + MSpec.mode?(:report) or + (MSpec.mode?(:report_on) and SpecGuard.guards.include?(name)) + end + + def report_key + "#{name} #{@parameters.join(", ")}" + end + + def record(description) + SpecGuard.report[report_key] << description + end + + def add(example) + record example.description + MSpec.retrieve(:formatter).tally.counter.guards! + end + + def unregister + MSpec.unguard + MSpec.unregister :add, self + end + + def match? + raise "must be implemented by the subclass" + end +end diff --git a/spec/mspec/lib/mspec/guards/platform.rb b/spec/mspec/lib/mspec/guards/platform.rb new file mode 100644 index 0000000000..875aef6c9c --- /dev/null +++ b/spec/mspec/lib/mspec/guards/platform.rb @@ -0,0 +1,78 @@ +require 'mspec/guards/guard' + +class PlatformGuard < SpecGuard + def self.implementation?(*args) + args.any? do |name| + case name + when :rubinius + RUBY_NAME.start_with?('rbx') + when :ruby, :jruby, :truffleruby, :ironruby, :macruby, :maglev, :topaz, :opal + RUBY_NAME.start_with?(name.to_s) + else + raise "unknown implementation #{name}" + end + end + end + + def self.standard? + implementation? :ruby + end + + HOST_OS = begin + require 'rbconfig' + RbConfig::CONFIG['host_os'] || RUBY_PLATFORM + rescue LoadError + RUBY_PLATFORM + end.downcase + + def self.os?(*oses) + oses.any? do |os| + raise ":java is not a valid OS" if os == :java + if os == :windows + HOST_OS =~ /(mswin|mingw)/ + else + HOST_OS.include?(os.to_s) + end + end + end + + def self.windows? + os?(:windows) + end + + def self.wordsize?(size) + size == 8 * 1.size + end + + def initialize(*args) + if args.last.is_a?(Hash) + @options, @platforms = args.last, args[0..-2] + else + @options, @platforms = {}, args + end + @parameters = args + end + + def match? + match = @platforms.empty? ? true : PlatformGuard.os?(*@platforms) + @options.each do |key, value| + case key + when :os + match &&= PlatformGuard.os?(*value) + when :wordsize + match &&= PlatformGuard.wordsize? value + end + end + match + end +end + +class Object + def platform_is(*args, &block) + PlatformGuard.new(*args).run_if(:platform_is, &block) + end + + def platform_is_not(*args, &block) + PlatformGuard.new(*args).run_unless(:platform_is_not, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/quarantine.rb b/spec/mspec/lib/mspec/guards/quarantine.rb new file mode 100644 index 0000000000..4724613a0f --- /dev/null +++ b/spec/mspec/lib/mspec/guards/quarantine.rb @@ -0,0 +1,13 @@ +require 'mspec/guards/guard' + +class QuarantineGuard < SpecGuard + def match? + true + end +end + +class Object + def quarantine!(&block) + QuarantineGuard.new.run_unless(:quarantine!, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/superuser.rb b/spec/mspec/lib/mspec/guards/superuser.rb new file mode 100644 index 0000000000..6e447198a7 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/superuser.rb @@ -0,0 +1,17 @@ +require 'mspec/guards/guard' + +class SuperUserGuard < SpecGuard + def match? + Process.euid == 0 + end +end + +class Object + def as_superuser(&block) + SuperUserGuard.new.run_if(:as_superuser, &block) + end + + def as_user(&block) + SuperUserGuard.new.run_unless(:as_user, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/support.rb b/spec/mspec/lib/mspec/guards/support.rb new file mode 100644 index 0000000000..f1760ece2e --- /dev/null +++ b/spec/mspec/lib/mspec/guards/support.rb @@ -0,0 +1,16 @@ +require 'mspec/guards/platform' + +class SupportedGuard < SpecGuard + def match? + if @parameters.include? :ruby + raise Exception, "improper use of not_supported_on guard" + end + !PlatformGuard.standard? and PlatformGuard.implementation?(*@parameters) + end +end + +class Object + def not_supported_on(*args, &block) + SupportedGuard.new(*args).run_unless(:not_supported_on, &block) + end +end diff --git a/spec/mspec/lib/mspec/guards/version.rb b/spec/mspec/lib/mspec/guards/version.rb new file mode 100644 index 0000000000..110853e082 --- /dev/null +++ b/spec/mspec/lib/mspec/guards/version.rb @@ -0,0 +1,39 @@ +require 'mspec/utils/deprecate' +require 'mspec/utils/version' +require 'mspec/guards/guard' + +class VersionGuard < SpecGuard + FULL_RUBY_VERSION = SpecVersion.new SpecGuard.ruby_version(:full) + + def initialize(version) + case version + when String + @version = SpecVersion.new version + when Range + MSpec.deprecate "an empty version range end", 'a specific version' if version.end.empty? + a = SpecVersion.new version.begin + b = SpecVersion.new version.end + unless version.exclude_end? + MSpec.deprecate "ruby_version_is with an inclusive range", 'an exclusive range ("2.1"..."2.3")' + end + @version = version.exclude_end? ? a...b : a..b + else + raise "version must be a String or Range but was a #{version.class}" + end + @parameters = [version] + end + + def match? + if Range === @version + @version.include? FULL_RUBY_VERSION + else + FULL_RUBY_VERSION >= @version + end + end +end + +class Object + def ruby_version_is(*args, &block) + VersionGuard.new(*args).run_if(:ruby_version_is, &block) + end +end diff --git a/spec/mspec/lib/mspec/helpers.rb b/spec/mspec/lib/mspec/helpers.rb new file mode 100644 index 0000000000..f2d1c9fb21 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers.rb @@ -0,0 +1,12 @@ +require 'mspec/helpers/argf' +require 'mspec/helpers/argv' +require 'mspec/helpers/datetime' +require 'mspec/helpers/fixture' +require 'mspec/helpers/flunk' +require 'mspec/helpers/fs' +require 'mspec/helpers/io' +require 'mspec/helpers/mock_to_path' +require 'mspec/helpers/numeric' +require 'mspec/helpers/ruby_exe' +require 'mspec/helpers/scratch' +require 'mspec/helpers/tmp' diff --git a/spec/mspec/lib/mspec/helpers/argf.rb b/spec/mspec/lib/mspec/helpers/argf.rb new file mode 100644 index 0000000000..1ba48b9378 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/argf.rb @@ -0,0 +1,37 @@ +class Object + # Convenience helper for specs using ARGF. + # Set @argf to an instance of ARGF.class with the given +argv+. + # That instance must be used instead of ARGF as ARGF is global + # and it is not always possible to reset its state correctly. + # + # The helper yields to the block and then close + # the files open by the instance. Example: + # + # describe "That" do + # it "does something" do + # argf ['a', 'b'] do + # # do something + # end + # end + # end + def argf(argv) + if argv.empty? or argv.length > 2 + raise "Only 1 or 2 filenames are allowed for the argf helper so files can be properly closed: #{argv.inspect}" + end + @argf ||= nil + raise "Cannot nest calls to the argf helper" if @argf + + @argf = ARGF.class.new(*argv) + @__mspec_saved_argf_file__ = @argf.file + begin + yield + ensure + file1 = @__mspec_saved_argf_file__ + file2 = @argf.file # Either the first file or the second + file1.close if !file1.closed? and file1 != STDIN + file2.close if !file2.closed? and file2 != STDIN + @argf = nil + @__mspec_saved_argf_file__ = nil + end + end +end diff --git a/spec/mspec/lib/mspec/helpers/argv.rb b/spec/mspec/lib/mspec/helpers/argv.rb new file mode 100644 index 0000000000..c8cbbf2ac3 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/argv.rb @@ -0,0 +1,46 @@ +class Object + # Convenience helper for altering ARGV. Saves the + # value of ARGV and sets it to +args+. If a block + # is given, yields to the block and then restores + # the value of ARGV. The previously saved value of + # ARGV can be restored by passing +:restore+. The + # former is useful in a single spec. The latter is + # useful in before/after actions. For example: + # + # describe "This" do + # before do + # argv ['a', 'b'] + # end + # + # after do + # argv :restore + # end + # + # it "does something" do + # # do something + # end + # end + # + # describe "That" do + # it "does something" do + # argv ['a', 'b'] do + # # do something + # end + # end + # end + def argv(args) + if args == :restore + ARGV.replace(@__mspec_saved_argv__ || []) + else + @__mspec_saved_argv__ = ARGV.dup + ARGV.replace args + if block_given? + begin + yield + ensure + argv :restore + end + end + end + end +end diff --git a/spec/mspec/lib/mspec/helpers/datetime.rb b/spec/mspec/lib/mspec/helpers/datetime.rb new file mode 100644 index 0000000000..4cb57bdaa1 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/datetime.rb @@ -0,0 +1,51 @@ +class Object + # The new_datetime helper makes writing DateTime specs more simple by + # providing default constructor values and accepting a Hash of only the + # constructor values needed for the particular spec. For example: + # + # new_datetime :hour => 1, :minute => 20 + # + # Possible keys are: + # :year, :month, :day, :hour, :minute, :second, :offset and :sg. + + def new_datetime(opts={}) + require 'date' + + value = { + :year => -4712, + :month => 1, + :day => 1, + :hour => 0, + :minute => 0, + :second => 0, + :offset => 0, + :sg => Date::ITALY + }.merge opts + + DateTime.new value[:year], value[:month], value[:day], value[:hour], + value[:minute], value[:second], value[:offset], value[:sg] + end + + def with_timezone(name, offset = nil, daylight_saving_zone = "") + zone = name.dup + + if offset + # TZ convention is backwards + offset = -offset + + zone += offset.to_s + zone += ":00:00" + end + zone += daylight_saving_zone + + old = ENV["TZ"] + ENV["TZ"] = zone + + begin + yield + ensure + ENV["TZ"] = old + end + end + +end diff --git a/spec/mspec/lib/mspec/helpers/fixture.rb b/spec/mspec/lib/mspec/helpers/fixture.rb new file mode 100644 index 0000000000..718c1b7a94 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/fixture.rb @@ -0,0 +1,26 @@ +class Object + # Returns the name of a fixture file by adjoining the directory + # of the +file+ argument with "fixtures" and the contents of the + # +args+ array. For example, + # + # +file+ == "some/example_spec.rb" + # + # and + # + # +args+ == ["subdir", "file.txt"] + # + # then the result is the expanded path of + # + # "some/fixtures/subdir/file.txt". + def fixture(file, *args) + path = File.dirname(file) + path = path[0..-7] if path[-7..-1] == "/shared" + fixtures = path[-9..-1] == "/fixtures" ? "" : "fixtures" + if File.respond_to?(:realpath) + path = File.realpath(path) + else + path = File.expand_path(path) + end + File.join(path, fixtures, args) + end +end diff --git a/spec/mspec/lib/mspec/helpers/flunk.rb b/spec/mspec/lib/mspec/helpers/flunk.rb new file mode 100644 index 0000000000..35bd939b85 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/flunk.rb @@ -0,0 +1,5 @@ +class Object + def flunk(msg="This example is a failure") + SpecExpectation.fail_with "Failed:", msg + end +end diff --git a/spec/mspec/lib/mspec/helpers/fs.rb b/spec/mspec/lib/mspec/helpers/fs.rb new file mode 100644 index 0000000000..ee33f5fec0 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/fs.rb @@ -0,0 +1,62 @@ +class Object + # Copies a file + def cp(source, dest) + File.open(dest, "w") do |d| + File.open(source, "r") do |s| + while data = s.read(1024) + d.write data + end + end + end + end + + # Creates each directory in path that does not exist. + def mkdir_p(path) + parts = File.expand_path(path).split %r[/|\\] + name = parts.shift + parts.each do |part| + name = File.join name, part + + if File.file? name + raise ArgumentError, "path component of #{path} is a file" + end + + Dir.mkdir name unless File.directory? name + end + end + + # Recursively removes all files and directories in +path+ + # if +path+ is a directory. Removes the file if +path+ is + # a file. + def rm_r(*paths) + paths.each do |path| + path = File.expand_path path + + prefix = SPEC_TEMP_DIR + unless path[0, prefix.size] == prefix + raise ArgumentError, "#{path} is not prefixed by #{prefix}" + end + + # File.symlink? needs to be checked first as + # File.exist? returns false for dangling symlinks + if File.symlink? path + File.unlink path + elsif File.directory? path + Dir.entries(path).each { |x| rm_r "#{path}/#{x}" unless x =~ /^\.\.?$/ } + Dir.rmdir path + elsif File.exist? path + File.delete path + end + end + end + + # Creates a file +name+. Creates the directory for +name+ + # if it does not exist. + def touch(name, mode="w") + mkdir_p File.dirname(name) + + File.open(name, mode) do |f| + yield f if block_given? + end + end +end diff --git a/spec/mspec/lib/mspec/helpers/io.rb b/spec/mspec/lib/mspec/helpers/io.rb new file mode 100644 index 0000000000..83d14441a7 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/io.rb @@ -0,0 +1,113 @@ +require 'mspec/guards/feature' + +class IOStub + def initialize + @buffer = [] + @output = '' + end + + def write(*str) + self << str.join + end + + def << str + @buffer << str + self + end + + def print(*str) + write(str.join + $\.to_s) + end + + def method_missing(name, *args, &block) + to_s.send(name, *args, &block) + end + + def == other + to_s == other + end + + def =~ other + to_s =~ other + end + + def puts(*str) + if str.empty? + write "\n" + else + write(str.collect { |s| s.to_s.chomp }.concat([nil]).join("\n")) + end + end + + def printf(format, *args) + self << sprintf(format, *args) + end + + def flush + @output += @buffer.join('') + @buffer.clear + self + end + + def to_s + flush + @output + end + + alias_method :to_str, :to_s + + def inspect + to_s.inspect + end +end + +class Object + # Creates a "bare" file descriptor (i.e. one that is not associated + # with any Ruby object). The file descriptor can safely be passed + # to IO.new without creating a Ruby object alias to the fd. + def new_fd(name, mode="w:utf-8") + mode = options_or_mode(mode) + + if mode.kind_of? Hash + if mode.key? :mode + mode = mode[:mode] + else + raise ArgumentError, "new_fd options Hash must include :mode" + end + end + + IO.sysopen name, fmode(mode) + end + + # Creates an IO instance for a temporary file name. The file + # must be deleted. + def new_io(name, mode="w:utf-8") + IO.new new_fd(name, options_or_mode(mode)), options_or_mode(mode) + end + + # This helper simplifies passing file access modes regardless of + # whether the :encoding feature is enabled. Only the access specifier + # itself will be returned if :encoding is not enabled. Otherwise, + # the full mode string will be returned (i.e. the helper is a no-op). + def fmode(mode) + if FeatureGuard.enabled? :encoding + mode + else + mode.split(':').first + end + end + + # This helper simplifies passing file access modes or options regardless of + # whether the :encoding feature is enabled. Only the access specifier itself + # will be returned if :encoding is not enabled. Otherwise, the full mode + # string or option will be returned (i.e. the helper is a no-op). + def options_or_mode(oom) + return fmode(oom) if oom.kind_of? String + + if FeatureGuard.enabled? :encoding + oom + else + fmode(oom[:mode] || "r:utf-8") + end + end +end diff --git a/spec/mspec/lib/mspec/helpers/mock_to_path.rb b/spec/mspec/lib/mspec/helpers/mock_to_path.rb new file mode 100644 index 0000000000..683bb1d9d6 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/mock_to_path.rb @@ -0,0 +1,8 @@ +class Object + def mock_to_path(path) + # Cannot use our Object#mock here since it conflicts with RSpec + obj = MockObject.new('path') + obj.should_receive(:to_path).and_return(path) + obj + end +end diff --git a/spec/mspec/lib/mspec/helpers/numeric.rb b/spec/mspec/lib/mspec/helpers/numeric.rb new file mode 100644 index 0000000000..ff30cf2b83 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/numeric.rb @@ -0,0 +1,72 @@ +require 'mspec/guards/platform' + +class Object + def nan_value + 0/0.0 + end + + def infinity_value + 1/0.0 + end + + def bignum_value(plus=0) + 0x8000_0000_0000_0000 + plus + end + + # This is a bit hairy, but we need to be able to write specs that cover the + # boundary between Fixnum and Bignum for operations like Fixnum#<<. Since + # this boundary is implementation-dependent, we use these helpers to write + # specs based on the relationship between values rather than specific + # values. + if PlatformGuard.standard? or PlatformGuard.implementation? :topaz + if PlatformGuard.wordsize? 32 + def fixnum_max + (2**30) - 1 + end + + def fixnum_min + -(2**30) + end + elsif PlatformGuard.wordsize? 64 + def fixnum_max + (2**62) - 1 + end + + def fixnum_min + -(2**62) + end + end + elsif PlatformGuard.implementation? :opal + def fixnum_max + Integer::MAX + end + + def fixnum_min + Integer::MIN + end + elsif PlatformGuard.implementation? :rubinius + def fixnum_max + Fixnum::MAX + end + + def fixnum_min + Fixnum::MIN + end + elsif PlatformGuard.implementation?(:jruby) || PlatformGuard.implementation?(:truffleruby) + def fixnum_max + 9223372036854775807 + end + + def fixnum_min + -9223372036854775808 + end + else + def fixnum_max + raise "unknown implementation for fixnum_max() helper" + end + + def fixnum_min + raise "unknown implementation for fixnum_min() helper" + end + end +end diff --git a/spec/mspec/lib/mspec/helpers/ruby_exe.rb b/spec/mspec/lib/mspec/helpers/ruby_exe.rb new file mode 100644 index 0000000000..a025be6c81 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/ruby_exe.rb @@ -0,0 +1,178 @@ +require 'mspec/utils/ruby_name' +require 'mspec/guards/platform' +require 'mspec/helpers/tmp' + +# The ruby_exe helper provides a wrapper for invoking the +# same Ruby interpreter with the same falgs as the one running +# the specs and getting the output from running the code. +# If +code+ is a file that exists, it will be run. +# Otherwise, +code+ should be Ruby code that will be run with +# the -e command line option. For example: +# +# ruby_exe('path/to/some/file.rb') +# +# will be executed as +# +# `#{RUBY_EXE} 'path/to/some/file.rb'` +# +# while +# +# ruby_exe('puts "hello, world."') +# +# will be executed as +# +# `#{RUBY_EXE} -e 'puts "hello, world."'` +# +# The ruby_exe helper also accepts an options hash with three +# keys: :options, :args and :env. For example: +# +# ruby_exe('file.rb', :options => "-w", +# :args => "> file.txt", +# :env => { :FOO => "bar" }) +# +# will be executed as +# +# `#{RUBY_EXE} -w #{'file.rb'} > file.txt` +# +# with access to ENV["FOO"] with value "bar". +# +# If +nil+ is passed for the first argument, the command line +# will be built only from the options hash. +# +# The RUBY_EXE constant is setup by mspec automatically +# and is used by ruby_exe and ruby_cmd. The mspec runner script +# will set ENV['RUBY_EXE'] to the name of the executable used +# to invoke the mspec-run script. The value of RUBY_EXE will be +# constructed as follows: +# +# 1. the value of ENV['RUBY_EXE'] +# 2. an explicit value based on RUBY_NAME +# 3. cwd/(RUBY_NAME + $(EXEEXT) || $(exeext) || '') +# 4. $(bindir)/$(RUBY_INSTALL_NAME) +# +# The value will only be used if the file exists and is executable. +# The flags will then be appended to the resulting value. +# +# These 4 ways correspond to the following scenarios: +# +# 1. Using the MSpec runner scripts, the name of the +# executable is explicitly passed by ENV['RUBY_EXE'] +# so there is no ambiguity. +# +# Otherwise, if using RSpec (or something else) +# +# 2. Running the specs while developing an alternative +# Ruby implementation. This explicitly names the +# executable in the development directory based on +# the value of RUBY_NAME, which is probably initialized +# from the value of RUBY_ENGINE. +# 3. Running the specs within the source directory for +# some implementation. (E.g. a local build directory.) +# 4. Running the specs against some installed Ruby +# implementation. +# +# Additionally, the flags passed to mspec +# (with -T on the command line or in the config with set :flags) +# will be appended to RUBY_EXE so that the interpreter +# is always called with those flags. + +class Object + def ruby_exe_options(option) + case option + when :env + ENV['RUBY_EXE'] + when :engine + case RUBY_NAME + when 'rbx' + "bin/rbx" + when 'jruby' + "bin/jruby" + when 'maglev' + "maglev-ruby" + when 'topaz' + "topaz" + when 'ironruby' + "ir" + end + when :name + require 'rbconfig' + bin = RUBY_NAME + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '') + File.join(".", bin) + when :install_name + require 'rbconfig' + bin = RbConfig::CONFIG["RUBY_INSTALL_NAME"] || RbConfig::CONFIG["ruby_install_name"] + bin << (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '') + File.join(RbConfig::CONFIG['bindir'], bin) + end + end + + def resolve_ruby_exe + [:env, :engine, :name, :install_name].each do |option| + next unless exe = ruby_exe_options(option) + + if File.file?(exe) and File.executable?(exe) + exe = File.expand_path(exe) + exe = exe.tr('/', '\\') if PlatformGuard.windows? + flags = ENV['RUBY_FLAGS'] + if flags and !flags.empty? + return exe + ' ' + flags + else + return exe + end + end + end + raise Exception, "Unable to find a suitable ruby executable." + end + + def ruby_exe(code, opts = {}) + if opts[:dir] + raise "ruby_exe(..., dir: dir) is no longer supported, use Dir.chdir" + end + + env = opts[:env] || {} + saved_env = {} + env.each do |key, value| + key = key.to_s + saved_env[key] = ENV[key] if ENV.key? key + ENV[key] = value + end + + escape = opts.delete(:escape) + if code and !File.exist?(code) and escape != false + tmpfile = tmp("rubyexe.rb") + File.open(tmpfile, "w") { |f| f.write(code) } + code = tmpfile + end + + begin + platform_is_not :opal do + `#{ruby_cmd(code, opts)}` + end + ensure + saved_env.each { |key, value| ENV[key] = value } + env.keys.each do |key| + key = key.to_s + ENV.delete key unless saved_env.key? key + end + File.delete tmpfile if tmpfile + end + end + + def ruby_cmd(code, opts = {}) + body = code + + if opts[:escape] + raise "escape: true is no longer supported in ruby_cmd, use ruby_exe or a fixture" + end + + if code and !File.exist?(code) + body = "-e #{code.inspect}" + end + + [RUBY_EXE, opts[:options], body, opts[:args]].compact.join(' ') + end + + unless Object.const_defined?(:RUBY_EXE) and RUBY_EXE + RUBY_EXE = resolve_ruby_exe + end +end diff --git a/spec/mspec/lib/mspec/helpers/scratch.rb b/spec/mspec/lib/mspec/helpers/scratch.rb new file mode 100644 index 0000000000..a6b0c02748 --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/scratch.rb @@ -0,0 +1,17 @@ +module ScratchPad + def self.clear + @record = nil + end + + def self.record(arg) + @record = arg + end + + def self.<<(arg) + @record << arg + end + + def self.recorded + @record + end +end diff --git a/spec/mspec/lib/mspec/helpers/tmp.rb b/spec/mspec/lib/mspec/helpers/tmp.rb new file mode 100644 index 0000000000..742eb57fdc --- /dev/null +++ b/spec/mspec/lib/mspec/helpers/tmp.rb @@ -0,0 +1,45 @@ +# Creates a temporary directory in the current working directory +# for temporary files created while running the specs. All specs +# should clean up any temporary files created so that the temp +# directory is empty when the process exits. + +SPEC_TEMP_DIR = File.expand_path(ENV["SPEC_TEMP_DIR"] || "rubyspec_temp") + +SPEC_TEMP_UNIQUIFIER = "0" + +SPEC_TEMP_DIR_PID = Process.pid + +at_exit do + begin + if SPEC_TEMP_DIR_PID == Process.pid + Dir.delete SPEC_TEMP_DIR if File.directory? SPEC_TEMP_DIR + end + rescue SystemCallError + STDERR.puts <<-EOM + +----------------------------------------------------- +The rubyspec temp directory is not empty. Ensure that +all specs are cleaning up temporary files: + #{SPEC_TEMP_DIR} +----------------------------------------------------- + + EOM + rescue Object => e + STDERR.puts "failed to remove spec temp directory" + STDERR.puts e.message + end +end + +class Object + def tmp(name, uniquify=true) + Dir.mkdir SPEC_TEMP_DIR unless Dir.exist? SPEC_TEMP_DIR + + if uniquify and !name.empty? + slash = name.rindex "/" + index = slash ? slash + 1 : 0 + name.insert index, "#{SPEC_TEMP_UNIQUIFIER.succ!}-" + end + + File.join SPEC_TEMP_DIR, name + end +end diff --git a/spec/mspec/lib/mspec/matchers.rb b/spec/mspec/lib/mspec/matchers.rb new file mode 100644 index 0000000000..8eab73198a --- /dev/null +++ b/spec/mspec/lib/mspec/matchers.rb @@ -0,0 +1,35 @@ +require 'mspec/matchers/base' +require 'mspec/matchers/be_an_instance_of' +require 'mspec/matchers/be_ancestor_of' +require 'mspec/matchers/be_close' +require 'mspec/matchers/be_computed_by' +require 'mspec/matchers/be_empty' +require 'mspec/matchers/be_false' +require 'mspec/matchers/be_kind_of' +require 'mspec/matchers/be_nan' +require 'mspec/matchers/be_nil' +require 'mspec/matchers/be_true' +require 'mspec/matchers/be_true_or_false' +require 'mspec/matchers/complain' +require 'mspec/matchers/eql' +require 'mspec/matchers/equal' +require 'mspec/matchers/equal_element' +require 'mspec/matchers/have_constant' +require 'mspec/matchers/have_class_variable' +require 'mspec/matchers/have_instance_method' +require 'mspec/matchers/have_instance_variable' +require 'mspec/matchers/have_method' +require 'mspec/matchers/have_private_instance_method' +require 'mspec/matchers/have_private_method' +require 'mspec/matchers/have_protected_instance_method' +require 'mspec/matchers/have_public_instance_method' +require 'mspec/matchers/have_singleton_method' +require 'mspec/matchers/include' +require 'mspec/matchers/infinity' +require 'mspec/matchers/match_yaml' +require 'mspec/matchers/raise_error' +require 'mspec/matchers/output' +require 'mspec/matchers/output_to_fd' +require 'mspec/matchers/respond_to' +require 'mspec/matchers/signed_zero' +require 'mspec/matchers/block_caller' diff --git a/spec/mspec/lib/mspec/matchers/base.rb b/spec/mspec/lib/mspec/matchers/base.rb new file mode 100644 index 0000000000..30fb1f93dc --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/base.rb @@ -0,0 +1,95 @@ +class SpecPositiveOperatorMatcher + def initialize(actual) + @actual = actual + end + + def ==(expected) + unless @actual == expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "to equal #{expected.pretty_inspect}") + end + end + + def <(expected) + unless @actual < expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "to be less than #{expected.pretty_inspect}") + end + end + + def <=(expected) + unless @actual <= expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "to be less than or equal to #{expected.pretty_inspect}") + end + end + + def >(expected) + unless @actual > expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "to be greater than #{expected.pretty_inspect}") + end + end + + def >=(expected) + unless @actual >= expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "to be greater than or equal to #{expected.pretty_inspect}") + end + end + + def =~(expected) + unless @actual =~ expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "to match #{expected.pretty_inspect}") + end + end +end + +class SpecNegativeOperatorMatcher + def initialize(actual) + @actual = actual + end + + def ==(expected) + if @actual == expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "not to equal #{expected.pretty_inspect}") + end + end + + def <(expected) + if @actual < expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "not to be less than #{expected.pretty_inspect}") + end + end + + def <=(expected) + if @actual <= expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "not to be less than or equal to #{expected.pretty_inspect}") + end + end + + def >(expected) + if @actual > expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "not to be greater than #{expected.pretty_inspect}") + end + end + + def >=(expected) + if @actual >= expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "not to be greater than or equal to #{expected.pretty_inspect}") + end + end + + def =~(expected) + if @actual =~ expected + SpecExpectation.fail_with("Expected #{@actual.pretty_inspect}", + "not to match #{expected.pretty_inspect}") + end + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb b/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb new file mode 100644 index 0000000000..6e31afcddd --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_an_instance_of.rb @@ -0,0 +1,26 @@ +class BeAnInstanceOfMatcher + def initialize(expected) + @expected = expected + end + + def matches?(actual) + @actual = actual + @actual.instance_of?(@expected) + end + + def failure_message + ["Expected #{@actual.inspect} (#{@actual.class})", + "to be an instance of #{@expected}"] + end + + def negative_failure_message + ["Expected #{@actual.inspect} (#{@actual.class})", + "not to be an instance of #{@expected}"] + end +end + +class Object + def be_an_instance_of(expected) + BeAnInstanceOfMatcher.new(expected) + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb b/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb new file mode 100644 index 0000000000..792c64089a --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_ancestor_of.rb @@ -0,0 +1,24 @@ +class BeAncestorOfMatcher + def initialize(expected) + @expected = expected + end + + def matches?(actual) + @actual = actual + @expected.ancestors.include? @actual + end + + def failure_message + ["Expected #{@actual}", "to be an ancestor of #{@expected}"] + end + + def negative_failure_message + ["Expected #{@actual}", "not to be an ancestor of #{@expected}"] + end +end + +class Object + def be_ancestor_of(expected) + BeAncestorOfMatcher.new(expected) + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_close.rb b/spec/mspec/lib/mspec/matchers/be_close.rb new file mode 100644 index 0000000000..5d79654099 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_close.rb @@ -0,0 +1,27 @@ +TOLERANCE = 0.00003 unless Object.const_defined?(:TOLERANCE) + +class BeCloseMatcher + def initialize(expected, tolerance) + @expected = expected + @tolerance = tolerance + end + + def matches?(actual) + @actual = actual + (@actual - @expected).abs < @tolerance + end + + def failure_message + ["Expected #{@expected}", "to be within +/- #{@tolerance} of #{@actual}"] + end + + def negative_failure_message + ["Expected #{@expected}", "not to be within +/- #{@tolerance} of #{@actual}"] + end +end + +class Object + def be_close(expected, tolerance) + BeCloseMatcher.new(expected, tolerance) + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_computed_by.rb b/spec/mspec/lib/mspec/matchers/be_computed_by.rb new file mode 100644 index 0000000000..c927eb7697 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_computed_by.rb @@ -0,0 +1,37 @@ +class BeComputedByMatcher + def initialize(sym, *args) + @method = sym + @args = args + end + + def matches?(array) + array.each do |line| + @receiver = line.shift + @value = line.pop + @arguments = line + @arguments += @args + @actual = @receiver.send(@method, *@arguments) + return false unless @actual == @value + end + + return true + end + + def method_call + method_call = "#{@receiver.inspect}.#{@method}" + unless @arguments.empty? + method_call = "#{method_call} from #{@arguments.map { |x| x.inspect }.join(", ")}" + end + method_call + end + + def failure_message + ["Expected #{@value.inspect}", "to be computed by #{method_call} (computed #{@actual.inspect} instead)"] + end +end + +class Object + def be_computed_by(sym, *args) + BeComputedByMatcher.new(sym, *args) + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_empty.rb b/spec/mspec/lib/mspec/matchers/be_empty.rb new file mode 100644 index 0000000000..8a401b63fd --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_empty.rb @@ -0,0 +1,20 @@ +class BeEmptyMatcher + def matches?(actual) + @actual = actual + @actual.empty? + end + + def failure_message + ["Expected #{@actual.inspect}", "to be empty"] + end + + def negative_failure_message + ["Expected #{@actual.inspect}", "not to be empty"] + end +end + +class Object + def be_empty + BeEmptyMatcher.new + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_false.rb b/spec/mspec/lib/mspec/matchers/be_false.rb new file mode 100644 index 0000000000..0a6e8cfd63 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_false.rb @@ -0,0 +1,20 @@ +class BeFalseMatcher + def matches?(actual) + @actual = actual + @actual == false + end + + def failure_message + ["Expected #{@actual.inspect}", "to be false"] + end + + def negative_failure_message + ["Expected #{@actual.inspect}", "not to be false"] + end +end + +class Object + def be_false + BeFalseMatcher.new + end +end \ No newline at end of file diff --git a/spec/mspec/lib/mspec/matchers/be_kind_of.rb b/spec/mspec/lib/mspec/matchers/be_kind_of.rb new file mode 100644 index 0000000000..a734f6159c --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_kind_of.rb @@ -0,0 +1,24 @@ +class BeKindOfMatcher + def initialize(expected) + @expected = expected + end + + def matches?(actual) + @actual = actual + @actual.is_a?(@expected) + end + + def failure_message + ["Expected #{@actual.inspect} (#{@actual.class})", "to be kind of #{@expected}"] + end + + def negative_failure_message + ["Expected #{@actual.inspect} (#{@actual.class})", "not to be kind of #{@expected}"] + end +end + +class Object + def be_kind_of(expected) + BeKindOfMatcher.new(expected) + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_nan.rb b/spec/mspec/lib/mspec/matchers/be_nan.rb new file mode 100644 index 0000000000..aa19391211 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_nan.rb @@ -0,0 +1,20 @@ +class BeNaNMatcher + def matches?(actual) + @actual = actual + @actual.kind_of?(Float) && @actual.nan? + end + + def failure_message + ["Expected #{@actual}", "to be NaN"] + end + + def negative_failure_message + ["Expected #{@actual}", "not to be NaN"] + end +end + +class Object + def be_nan + BeNaNMatcher.new + end +end diff --git a/spec/mspec/lib/mspec/matchers/be_nil.rb b/spec/mspec/lib/mspec/matchers/be_nil.rb new file mode 100644 index 0000000000..ecea6feffa --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_nil.rb @@ -0,0 +1,20 @@ +class BeNilMatcher + def matches?(actual) + @actual = actual + @actual.nil? + end + + def failure_message + ["Expected #{@actual.inspect}", "to be nil"] + end + + def negative_failure_message + ["Expected #{@actual.inspect}", "not to be nil"] + end +end + +class Object + def be_nil + BeNilMatcher.new + end +end \ No newline at end of file diff --git a/spec/mspec/lib/mspec/matchers/be_true.rb b/spec/mspec/lib/mspec/matchers/be_true.rb new file mode 100644 index 0000000000..de8e237d35 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_true.rb @@ -0,0 +1,20 @@ +class BeTrueMatcher + def matches?(actual) + @actual = actual + @actual == true + end + + def failure_message + ["Expected #{@actual.inspect}", "to be true"] + end + + def negative_failure_message + ["Expected #{@actual.inspect}", "not to be true"] + end +end + +class Object + def be_true + BeTrueMatcher.new + end +end \ No newline at end of file diff --git a/spec/mspec/lib/mspec/matchers/be_true_or_false.rb b/spec/mspec/lib/mspec/matchers/be_true_or_false.rb new file mode 100644 index 0000000000..b2262779ed --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/be_true_or_false.rb @@ -0,0 +1,20 @@ +class BeTrueOrFalseMatcher + def matches?(actual) + @actual = actual + @actual == true || @actual == false + end + + def failure_message + ["Expected #{@actual.inspect}", "to be true or false"] + end + + def negative_failure_message + ["Expected #{@actual.inspect}", "not to be true or false"] + end +end + +class Object + def be_true_or_false + BeTrueOrFalseMatcher.new + end +end diff --git a/spec/mspec/lib/mspec/matchers/block_caller.rb b/spec/mspec/lib/mspec/matchers/block_caller.rb new file mode 100644 index 0000000000..5451950712 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/block_caller.rb @@ -0,0 +1,35 @@ +class BlockingMatcher + def matches?(block) + started = false + blocking = true + + thread = Thread.new do + started = true + block.call + + blocking = false + end + + while !started and status = thread.status and status != "sleep" + Thread.pass + end + thread.kill + thread.join + + blocking + end + + def failure_message + ['Expected the given Proc', 'to block the caller'] + end + + def negative_failure_message + ['Expected the given Proc', 'to not block the caller'] + end +end + +class Object + def block_caller(timeout = 0.1) + BlockingMatcher.new + end +end diff --git a/spec/mspec/lib/mspec/matchers/complain.rb b/spec/mspec/lib/mspec/matchers/complain.rb new file mode 100644 index 0000000000..1313215156 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/complain.rb @@ -0,0 +1,56 @@ +require 'mspec/helpers/io' + +class ComplainMatcher + def initialize(complaint) + @complaint = complaint + end + + def matches?(proc) + @saved_err = $stderr + @stderr = $stderr = IOStub.new + @verbose = $VERBOSE + $VERBOSE = false + + proc.call + + unless @complaint.nil? + case @complaint + when Regexp + return false unless $stderr =~ @complaint + else + return false unless $stderr == @complaint + end + end + + return $stderr.empty? ? false : true + ensure + $VERBOSE = @verbose + $stderr = @saved_err + end + + def failure_message + if @complaint.nil? + ["Expected a warning", "but received none"] + elsif @complaint.kind_of? Regexp + ["Expected warning to match: #{@complaint.inspect}", "but got: #{@stderr.chomp.inspect}"] + else + ["Expected warning: #{@complaint.inspect}", "but got: #{@stderr.chomp.inspect}"] + end + end + + def negative_failure_message + if @complaint.nil? + ["Unexpected warning: ", @stderr.chomp.inspect] + elsif @complaint.kind_of? Regexp + ["Expected warning not to match: #{@complaint.inspect}", "but got: #{@stderr.chomp.inspect}"] + else + ["Expected warning: #{@complaint.inspect}", "but got: #{@stderr.chomp.inspect}"] + end + end +end + +class Object + def complain(complaint=nil) + ComplainMatcher.new(complaint) + end +end diff --git a/spec/mspec/lib/mspec/matchers/eql.rb b/spec/mspec/lib/mspec/matchers/eql.rb new file mode 100644 index 0000000000..82117d862c --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/eql.rb @@ -0,0 +1,26 @@ +class EqlMatcher + def initialize(expected) + @expected = expected + end + + def matches?(actual) + @actual = actual + @actual.eql?(@expected) + end + + def failure_message + ["Expected #{@actual.pretty_inspect}", + "to have same value and type as #{@expected.pretty_inspect}"] + end + + def negative_failure_message + ["Expected #{@actual.pretty_inspect}", + "not to have same value or type as #{@expected.pretty_inspect}"] + end +end + +class Object + def eql(expected) + EqlMatcher.new(expected) + end +end diff --git a/spec/mspec/lib/mspec/matchers/equal.rb b/spec/mspec/lib/mspec/matchers/equal.rb new file mode 100644 index 0000000000..ee6431fd4f --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/equal.rb @@ -0,0 +1,26 @@ +class EqualMatcher + def initialize(expected) + @expected = expected + end + + def matches?(actual) + @actual = actual + @actual.equal?(@expected) + end + + def failure_message + ["Expected #{@actual.pretty_inspect}", + "to be identical to #{@expected.pretty_inspect}"] + end + + def negative_failure_message + ["Expected #{@actual.pretty_inspect}", + "not to be identical to #{@expected.pretty_inspect}"] + end +end + +class Object + def equal(expected) + EqualMatcher.new(expected) + end +end diff --git a/spec/mspec/lib/mspec/matchers/equal_element.rb b/spec/mspec/lib/mspec/matchers/equal_element.rb new file mode 100644 index 0000000000..8d032fd088 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/equal_element.rb @@ -0,0 +1,78 @@ +class EqualElementMatcher + def initialize(element, attributes = nil, content = nil, options = {}) + @element = element + @attributes = attributes + @content = content + @options = options + end + + def matches?(actual) + @actual = actual + + matched = true + + if @options[:not_closed] + matched &&= actual =~ /^#{Regexp.quote("<" + @element)}.*#{Regexp.quote(">" + (@content || ''))}$/ + else + matched &&= actual =~ /^#{Regexp.quote("<" + @element)}/ + matched &&= actual =~ /#{Regexp.quote("")}$/ + matched &&= actual =~ /#{Regexp.quote(">" + @content + ")/).size == 1) + else + matched &&= (actual.scan(%Q{ #{key}="#{value}"}).size == 1) + end + end + end + end + + !!matched + end + + def failure_message + ["Expected #{@actual.pretty_inspect}", + "to be a '#{@element}' element with #{attributes_for_failure_message} and #{content_for_failure_message}"] + end + + def negative_failure_message + ["Expected #{@actual.pretty_inspect}", + "not to be a '#{@element}' element with #{attributes_for_failure_message} and #{content_for_failure_message}"] + end + + def attributes_for_failure_message + if @attributes + if @attributes.empty? + "no attributes" + else + @attributes.inject([]) { |memo, n| memo << %Q{#{n[0]}="#{n[1]}"} }.join(" ") + end + else + "any attributes" + end + end + + def content_for_failure_message + if @content + if @content.empty? + "no content" + else + "#{@content.inspect} as content" + end + else + "any content" + end + end +end + +class Object + def equal_element(*args) + EqualElementMatcher.new(*args) + end +end \ No newline at end of file diff --git a/spec/mspec/lib/mspec/matchers/have_class_variable.rb b/spec/mspec/lib/mspec/matchers/have_class_variable.rb new file mode 100644 index 0000000000..45cd0b5ae1 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_class_variable.rb @@ -0,0 +1,12 @@ +require 'mspec/matchers/variable' + +class HaveClassVariableMatcher < VariableMatcher + self.variables_method = :class_variables + self.description = 'class variable' +end + +class Object + def have_class_variable(variable) + HaveClassVariableMatcher.new(variable) + end +end \ No newline at end of file diff --git a/spec/mspec/lib/mspec/matchers/have_constant.rb b/spec/mspec/lib/mspec/matchers/have_constant.rb new file mode 100644 index 0000000000..df95219e53 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_constant.rb @@ -0,0 +1,12 @@ +require 'mspec/matchers/variable' + +class HaveConstantMatcher < VariableMatcher + self.variables_method = :constants + self.description = 'constant' +end + +class Object + def have_constant(variable) + HaveConstantMatcher.new(variable) + end +end diff --git a/spec/mspec/lib/mspec/matchers/have_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_instance_method.rb new file mode 100644 index 0000000000..00dcbd39eb --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_instance_method.rb @@ -0,0 +1,24 @@ +require 'mspec/matchers/method' + +class HaveInstanceMethodMatcher < MethodMatcher + def matches?(mod) + @mod = mod + mod.instance_methods(@include_super).include? @method + end + + def failure_message + ["Expected #{@mod} to have instance method '#{@method.to_s}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@mod} NOT to have instance method '#{@method.to_s}'", + "but it does"] + end +end + +class Object + def have_instance_method(method, include_super=true) + HaveInstanceMethodMatcher.new method, include_super + end +end diff --git a/spec/mspec/lib/mspec/matchers/have_instance_variable.rb b/spec/mspec/lib/mspec/matchers/have_instance_variable.rb new file mode 100644 index 0000000000..e83eb9408c --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_instance_variable.rb @@ -0,0 +1,12 @@ +require 'mspec/matchers/variable' + +class HaveInstanceVariableMatcher < VariableMatcher + self.variables_method = :instance_variables + self.description = 'instance variable' +end + +class Object + def have_instance_variable(variable) + HaveInstanceVariableMatcher.new(variable) + end +end \ No newline at end of file diff --git a/spec/mspec/lib/mspec/matchers/have_method.rb b/spec/mspec/lib/mspec/matchers/have_method.rb new file mode 100644 index 0000000000..2fc3e66f69 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_method.rb @@ -0,0 +1,24 @@ +require 'mspec/matchers/method' + +class HaveMethodMatcher < MethodMatcher + def matches?(mod) + @mod = mod + @mod.methods(@include_super).include? @method + end + + def failure_message + ["Expected #{@mod} to have method '#{@method.to_s}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@mod} NOT to have method '#{@method.to_s}'", + "but it does"] + end +end + +class Object + def have_method(method, include_super=true) + HaveMethodMatcher.new method, include_super + end +end diff --git a/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb new file mode 100644 index 0000000000..87d9767a69 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_private_instance_method.rb @@ -0,0 +1,24 @@ +require 'mspec/matchers/method' + +class HavePrivateInstanceMethodMatcher < MethodMatcher + def matches?(mod) + @mod = mod + mod.private_instance_methods(@include_super).include? @method + end + + def failure_message + ["Expected #{@mod} to have private instance method '#{@method.to_s}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@mod} NOT to have private instance method '#{@method.to_s}'", + "but it does"] + end +end + +class Object + def have_private_instance_method(method, include_super=true) + HavePrivateInstanceMethodMatcher.new method, include_super + end +end diff --git a/spec/mspec/lib/mspec/matchers/have_private_method.rb b/spec/mspec/lib/mspec/matchers/have_private_method.rb new file mode 100644 index 0000000000..d99d4ccb7f --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_private_method.rb @@ -0,0 +1,24 @@ +require 'mspec/matchers/method' + +class HavePrivateMethodMatcher < MethodMatcher + def matches?(mod) + @mod = mod + mod.private_methods(@include_super).include? @method + end + + def failure_message + ["Expected #{@mod} to have private method '#{@method.to_s}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@mod} NOT to have private method '#{@method.to_s}'", + "but it does"] + end +end + +class Object + def have_private_method(method, include_super=true) + HavePrivateMethodMatcher.new method, include_super + end +end diff --git a/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb new file mode 100644 index 0000000000..92f38e9acb --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_protected_instance_method.rb @@ -0,0 +1,24 @@ +require 'mspec/matchers/method' + +class HaveProtectedInstanceMethodMatcher < MethodMatcher + def matches?(mod) + @mod = mod + mod.protected_instance_methods(@include_super).include? @method + end + + def failure_message + ["Expected #{@mod} to have protected instance method '#{@method.to_s}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@mod} NOT to have protected instance method '#{@method.to_s}'", + "but it does"] + end +end + +class Object + def have_protected_instance_method(method, include_super=true) + HaveProtectedInstanceMethodMatcher.new method, include_super + end +end diff --git a/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb b/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb new file mode 100644 index 0000000000..035547d28f --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_public_instance_method.rb @@ -0,0 +1,24 @@ +require 'mspec/matchers/method' + +class HavePublicInstanceMethodMatcher < MethodMatcher + def matches?(mod) + @mod = mod + mod.public_instance_methods(@include_super).include? @method + end + + def failure_message + ["Expected #{@mod} to have public instance method '#{@method.to_s}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@mod} NOT to have public instance method '#{@method.to_s}'", + "but it does"] + end +end + +class Object + def have_public_instance_method(method, include_super=true) + HavePublicInstanceMethodMatcher.new method, include_super + end +end diff --git a/spec/mspec/lib/mspec/matchers/have_singleton_method.rb b/spec/mspec/lib/mspec/matchers/have_singleton_method.rb new file mode 100644 index 0000000000..5f3acb84e2 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/have_singleton_method.rb @@ -0,0 +1,24 @@ +require 'mspec/matchers/method' + +class HaveSingletonMethodMatcher < MethodMatcher + def matches?(obj) + @obj = obj + obj.singleton_methods(@include_super).include? @method + end + + def failure_message + ["Expected #{@obj} to have singleton method '#{@method.to_s}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@obj} NOT to have singleton method '#{@method.to_s}'", + "but it does"] + end +end + +class Object + def have_singleton_method(method, include_super=true) + HaveSingletonMethodMatcher.new method, include_super + end +end diff --git a/spec/mspec/lib/mspec/matchers/include.rb b/spec/mspec/lib/mspec/matchers/include.rb new file mode 100644 index 0000000000..b4e54158d1 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/include.rb @@ -0,0 +1,32 @@ +class IncludeMatcher + def initialize(*expected) + @expected = expected + end + + def matches?(actual) + @actual = actual + @expected.each do |e| + @element = e + unless @actual.include?(e) + return false + end + end + return true + end + + def failure_message + ["Expected #{@actual.inspect}", "to include #{@element.inspect}"] + end + + def negative_failure_message + ["Expected #{@actual.inspect}", "not to include #{@element.inspect}"] + end +end + +# Cannot override #include at the toplevel in MRI +module MSpec + def include(*expected) + IncludeMatcher.new(*expected) + end + module_function :include +end diff --git a/spec/mspec/lib/mspec/matchers/infinity.rb b/spec/mspec/lib/mspec/matchers/infinity.rb new file mode 100644 index 0000000000..0949fd47eb --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/infinity.rb @@ -0,0 +1,28 @@ +class InfinityMatcher + def initialize(expected_sign) + @expected_sign = expected_sign + end + + def matches?(actual) + @actual = actual + @actual.kind_of?(Float) && @actual.infinite? == @expected_sign + end + + def failure_message + ["Expected #{@actual}", "to be #{"-" if @expected_sign == -1}Infinity"] + end + + def negative_failure_message + ["Expected #{@actual}", "not to be #{"-" if @expected_sign == -1}Infinity"] + end +end + +class Object + def be_positive_infinity + InfinityMatcher.new(1) + end + + def be_negative_infinity + InfinityMatcher.new(-1) + end +end diff --git a/spec/mspec/lib/mspec/matchers/match_yaml.rb b/spec/mspec/lib/mspec/matchers/match_yaml.rb new file mode 100644 index 0000000000..542dece2b4 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/match_yaml.rb @@ -0,0 +1,47 @@ +class MatchYAMLMatcher + + def initialize(expected) + if valid_yaml?(expected) + @expected = expected + else + @expected = expected.to_yaml + end + end + + def matches?(actual) + @actual = actual + clean_yaml(@actual) == clean_yaml(@expected) + end + + def failure_message + ["Expected #{@actual.inspect}", " to match #{@expected.inspect}"] + end + + def negative_failure_message + ["Expected #{@actual.inspect}", " to match #{@expected.inspect}"] + end + + protected + + def clean_yaml(yaml) + yaml.gsub(/([^-]|^---)\s+\n/, "\\1\n").sub(/\n\.\.\.\n$/, "\n") + end + + def valid_yaml?(obj) + require 'yaml' + begin + YAML.load(obj) + rescue + false + else + true + end + end +end + +class Object + def match_yaml(expected) + MatchYAMLMatcher.new(expected) + end +end + diff --git a/spec/mspec/lib/mspec/matchers/method.rb b/spec/mspec/lib/mspec/matchers/method.rb new file mode 100644 index 0000000000..e8cdfa62ff --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/method.rb @@ -0,0 +1,10 @@ +class MethodMatcher + def initialize(method, include_super=true) + @include_super = include_super + @method = method.to_sym + end + + def matches?(mod) + raise Exception, "define #matches? in the subclass" + end +end diff --git a/spec/mspec/lib/mspec/matchers/output.rb b/spec/mspec/lib/mspec/matchers/output.rb new file mode 100644 index 0000000000..551e7506cf --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/output.rb @@ -0,0 +1,67 @@ +require 'mspec/helpers/io' + +class OutputMatcher + def initialize(stdout, stderr) + @out = stdout + @err = stderr + end + + def matches?(proc) + @saved_out = $stdout + @saved_err = $stderr + @stdout = $stdout = IOStub.new + @stderr = $stderr = IOStub.new + + proc.call + + unless @out.nil? + case @out + when Regexp + return false unless $stdout =~ @out + else + return false unless $stdout == @out + end + end + + unless @err.nil? + case @err + when Regexp + return false unless $stderr =~ @err + else + return false unless $stderr == @err + end + end + + return true + ensure + $stdout = @saved_out + $stderr = @saved_err + end + + def failure_message + expected_out = "\n" + actual_out = "\n" + unless @out.nil? + expected_out += " $stdout: #{@out.inspect}\n" + actual_out += " $stdout: #{@stdout.inspect}\n" + end + unless @err.nil? + expected_out += " $stderr: #{@err.inspect}\n" + actual_out += " $stderr: #{@stderr.inspect}\n" + end + ["Expected:#{expected_out}", " got:#{actual_out}"] + end + + def negative_failure_message + out = "" + out += " $stdout: #{@stdout.chomp.dump}\n" unless @out.nil? + out += " $stderr: #{@stderr.chomp.dump}\n" unless @err.nil? + ["Expected output not to be:\n", out] + end +end + +class Object + def output(stdout=nil, stderr=nil) + OutputMatcher.new(stdout, stderr) + end +end diff --git a/spec/mspec/lib/mspec/matchers/output_to_fd.rb b/spec/mspec/lib/mspec/matchers/output_to_fd.rb new file mode 100644 index 0000000000..5daaf5545c --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/output_to_fd.rb @@ -0,0 +1,71 @@ +require 'mspec/helpers/tmp' + +# Lower-level output speccing mechanism for a single +# output stream. Unlike OutputMatcher which provides +# methods to capture the output, we actually replace +# the FD itself so that there is no reliance on a +# certain method being used. +class OutputToFDMatcher + def initialize(expected, to) + @to, @expected = to, expected + + case @to + when STDOUT + @to_name = "STDOUT" + when STDERR + @to_name = "STDERR" + when IO + @to_name = @to.object_id.to_s + else + raise ArgumentError, "#{@to.inspect} is not a supported output target" + end + end + + def with_tmp + path = tmp("mspec_output_to_#{$$}_#{Time.now.to_i}") + File.open(path, 'w+') { |io| + yield(io) + } + ensure + File.delete path if path + end + + def matches?(block) + old_to = @to.dup + with_tmp do |out| + # Replacing with a file handle so that Readline etc. work + @to.reopen out + begin + block.call + ensure + @to.reopen old_to + old_to.close + end + + out.rewind + @actual = out.read + + case @expected + when Regexp + !(@actual =~ @expected).nil? + else + @actual == @expected + end + end + end + + def failure_message() + ["Expected (#{@to_name}): #{@expected.inspect}\n", + "#{'but got'.rjust(@to_name.length + 10)}: #{@actual.inspect}\nBacktrace"] + end + + def negative_failure_message() + ["Expected output (#{@to_name}) to NOT be:\n", @actual.inspect] + end +end + +class Object + def output_to_fd(what, where = STDOUT) + OutputToFDMatcher.new what, where + end +end diff --git a/spec/mspec/lib/mspec/matchers/raise_error.rb b/spec/mspec/lib/mspec/matchers/raise_error.rb new file mode 100644 index 0000000000..a5d6e01ec9 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/raise_error.rb @@ -0,0 +1,79 @@ +require 'mspec/utils/deprecate' + +class RaiseErrorMatcher + def initialize(exception, message, &block) + @exception = exception + @message = message + @block = block + end + + def matches?(proc) + @result = proc.call + return false + rescue Exception => @actual + if matching_exception?(@actual) + return true + else + raise @actual + end + end + + def matching_exception?(exc) + return false unless @exception === exc + if @message then + case @message + when String + return false if @message != exc.message + when Regexp + return false if @message !~ exc.message + end + end + + # The block has its own expectations and will throw an exception if it fails + @block[exc] if @block + + return true + end + + def exception_class_and_message(exception_class, message) + if message + "#{exception_class} (#{message})" + else + "#{exception_class}" + end + end + + def format_expected_exception + exception_class_and_message(@exception, @message) + end + + def format_exception(exception) + exception_class_and_message(exception.class, exception.message) + end + + def failure_message + message = ["Expected #{format_expected_exception}"] + + if @actual then + message << "but got #{format_exception(@actual)}" + else + message << "but no exception was raised (#{@result.pretty_inspect.chomp} was returned)" + end + + message + end + + def negative_failure_message + message = ["Expected to not get #{format_expected_exception}", ""] + unless @actual.class == @exception + message[1] = "but got #{format_exception(@actual)}" + end + message + end +end + +class Object + def raise_error(exception=Exception, message=nil, &block) + RaiseErrorMatcher.new(exception, message, &block) + end +end diff --git a/spec/mspec/lib/mspec/matchers/respond_to.rb b/spec/mspec/lib/mspec/matchers/respond_to.rb new file mode 100644 index 0000000000..2aa3ab14d1 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/respond_to.rb @@ -0,0 +1,24 @@ +class RespondToMatcher + def initialize(expected) + @expected = expected + end + + def matches?(actual) + @actual = actual + @actual.respond_to?(@expected) + end + + def failure_message + ["Expected #{@actual.inspect} (#{@actual.class})", "to respond to #{@expected}"] + end + + def negative_failure_message + ["Expected #{@actual.inspect} (#{@actual.class})", "not to respond to #{@expected}"] + end +end + +class Object + def respond_to(expected) + RespondToMatcher.new(expected) + end +end diff --git a/spec/mspec/lib/mspec/matchers/signed_zero.rb b/spec/mspec/lib/mspec/matchers/signed_zero.rb new file mode 100644 index 0000000000..3fd1472fc8 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/signed_zero.rb @@ -0,0 +1,28 @@ +class SignedZeroMatcher + def initialize(expected_sign) + @expected_sign = expected_sign + end + + def matches?(actual) + @actual = actual + (1.0/actual).infinite? == @expected_sign + end + + def failure_message + ["Expected #{@actual}", "to be #{"-" if @expected_sign == -1}0.0"] + end + + def negative_failure_message + ["Expected #{@actual}", "not to be #{"-" if @expected_sign == -1}0.0"] + end +end + +class Object + def be_positive_zero + SignedZeroMatcher.new(1) + end + + def be_negative_zero + SignedZeroMatcher.new(-1) + end +end diff --git a/spec/mspec/lib/mspec/matchers/variable.rb b/spec/mspec/lib/mspec/matchers/variable.rb new file mode 100644 index 0000000000..4d801ea337 --- /dev/null +++ b/spec/mspec/lib/mspec/matchers/variable.rb @@ -0,0 +1,24 @@ +class VariableMatcher + class << self + attr_accessor :variables_method, :description + end + + def initialize(variable) + @variable = variable.to_sym + end + + def matches?(object) + @object = object + @object.send(self.class.variables_method).include? @variable + end + + def failure_message + ["Expected #{@object} to have #{self.class.description} '#{@variable}'", + "but it does not"] + end + + def negative_failure_message + ["Expected #{@object} NOT to have #{self.class.description} '#{@variable}'", + "but it does"] + end +end diff --git a/spec/mspec/lib/mspec/mocks.rb b/spec/mspec/lib/mspec/mocks.rb new file mode 100644 index 0000000000..6a029c7b53 --- /dev/null +++ b/spec/mspec/lib/mspec/mocks.rb @@ -0,0 +1,3 @@ +require 'mspec/mocks/mock' +require 'mspec/mocks/proxy' +require 'mspec/mocks/object' diff --git a/spec/mspec/lib/mspec/mocks/mock.rb b/spec/mspec/lib/mspec/mocks/mock.rb new file mode 100644 index 0000000000..1557f2008e --- /dev/null +++ b/spec/mspec/lib/mspec/mocks/mock.rb @@ -0,0 +1,197 @@ +require 'mspec/expectations/expectations' + +class Object + alias_method :__mspec_object_id__, :object_id +end + +module Mock + def self.reset + @mocks = @stubs = @objects = nil + end + + def self.objects + @objects ||= {} + end + + def self.mocks + @mocks ||= Hash.new { |h,k| h[k] = [] } + end + + def self.stubs + @stubs ||= Hash.new { |h,k| h[k] = [] } + end + + def self.replaced_name(obj, sym) + :"__mspec_#{obj.__mspec_object_id__}_#{sym}__" + end + + def self.replaced_key(obj, sym) + [replaced_name(obj, sym), sym] + end + + def self.has_key?(keys, sym) + !!keys.find { |k| k.first == sym } + end + + def self.replaced?(sym) + has_key?(mocks.keys, sym) or has_key?(stubs.keys, sym) + end + + def self.clear_replaced(key) + mocks.delete key + stubs.delete key + end + + def self.mock_respond_to?(obj, sym, include_private = false) + name = replaced_name(obj, :respond_to?) + if replaced? name + obj.__send__ name, sym, include_private + else + obj.respond_to? sym, include_private + end + end + + def self.install_method(obj, sym, type=nil) + meta = obj.singleton_class + + key = replaced_key obj, sym + sym = sym.to_sym + + if (sym == :respond_to? or mock_respond_to?(obj, sym, true)) and !replaced?(key.first) + meta.__send__ :alias_method, key.first, sym + end + + meta.class_eval { + define_method(sym) do |*args, &block| + Mock.verify_call self, sym, *args, &block + end + } + + proxy = MockProxy.new type + + if proxy.mock? + MSpec.expectation + MSpec.actions :expectation, MSpec.current.state + end + + if proxy.stub? + stubs[key].unshift proxy + else + mocks[key] << proxy + end + objects[key] = obj + + proxy + end + + def self.name_or_inspect(obj) + obj.instance_variable_get(:@name) || obj.inspect + end + + def self.verify_count + mocks.each do |key, proxies| + obj = objects[key] + proxies.each do |proxy| + qualifier, count = proxy.count + pass = case qualifier + when :at_least + proxy.calls >= count + when :at_most + proxy.calls <= count + when :exactly + proxy.calls == count + when :any_number_of_times + true + else + false + end + unless pass + SpecExpectation.fail_with( + "Mock '#{name_or_inspect obj}' expected to receive '#{key.last}' " + \ + "#{qualifier.to_s.sub('_', ' ')} #{count} times", + "but received it #{proxy.calls} times") + end + end + end + end + + def self.verify_call(obj, sym, *args, &block) + compare = *args + compare = compare.first if compare.length <= 1 + + key = replaced_key obj, sym + [mocks, stubs].each do |proxies| + proxies[key].each do |proxy| + pass = case proxy.arguments + when :any_args + true + when :no_args + compare.nil? + else + proxy.arguments == compare + end + + if proxy.yielding? + if block + proxy.yielding.each do |args_to_yield| + if block.arity == -1 || block.arity == args_to_yield.size + block.call(*args_to_yield) + else + SpecExpectation.fail_with( + "Mock '#{name_or_inspect obj}' asked to yield " + \ + "|#{proxy.yielding.join(', ')}| on #{sym}\n", + "but a block with arity #{block.arity} was passed") + end + end + else + SpecExpectation.fail_with( + "Mock '#{name_or_inspect obj}' asked to yield " + \ + "|[#{proxy.yielding.join('], [')}]| on #{sym}\n", + "but no block was passed") + end + end + + if pass + proxy.called + + if proxy.raising? + raise proxy.raising + else + return proxy.returning + end + end + end + end + + if sym.to_sym == :respond_to? + mock_respond_to? obj, compare + else + SpecExpectation.fail_with("Mock '#{name_or_inspect obj}': method #{sym}\n", + "called with unexpected arguments (#{Array(compare).join(' ')})") + end + end + + def self.cleanup + objects.each do |key, obj| + if obj.kind_of? MockIntObject + clear_replaced key + next + end + + replaced = key.first + sym = key.last + meta = obj.singleton_class + + if mock_respond_to? obj, replaced, true + meta.__send__ :alias_method, sym, replaced + meta.__send__ :remove_method, replaced + else + meta.__send__ :remove_method, sym + end + + clear_replaced key + end + ensure + reset + end +end diff --git a/spec/mspec/lib/mspec/mocks/object.rb b/spec/mspec/lib/mspec/mocks/object.rb new file mode 100644 index 0000000000..f4652a4671 --- /dev/null +++ b/spec/mspec/lib/mspec/mocks/object.rb @@ -0,0 +1,28 @@ +require 'mspec/mocks/proxy' + +class Object + def stub!(sym) + Mock.install_method self, sym, :stub + end + + def should_receive(sym) + Mock.install_method self, sym + end + + def should_not_receive(sym) + proxy = Mock.install_method self, sym + proxy.exactly(0).times + end + + def mock(name, options={}) + MockObject.new name, options + end + + def mock_int(val) + MockIntObject.new(val) + end + + def mock_numeric(name, options={}) + NumericMockObject.new name, options + end +end diff --git a/spec/mspec/lib/mspec/mocks/proxy.rb b/spec/mspec/lib/mspec/mocks/proxy.rb new file mode 100644 index 0000000000..f5acc89d62 --- /dev/null +++ b/spec/mspec/lib/mspec/mocks/proxy.rb @@ -0,0 +1,186 @@ +class MockObject + def initialize(name, options={}) + @name = name + @null = options[:null_object] + end + + def method_missing(sym, *args, &block) + @null ? self : super + end + private :method_missing +end + +class NumericMockObject < Numeric + def initialize(name, options={}) + @name = name + @null = options[:null_object] + end + + def method_missing(sym, *args, &block) + @null ? self : super + end + + def singleton_method_added(val) + end +end + +class MockIntObject + def initialize(val) + @value = val + @calls = 0 + + key = [self, :to_int] + + Mock.objects[key] = self + Mock.mocks[key] << self + end + + attr_reader :calls + + def to_int + @calls += 1 + @value.to_int + end + + def count + [:at_least, 1] + end +end + +class MockProxy + attr_reader :raising, :yielding + + def initialize(type=nil) + @multiple_returns = nil + @returning = nil + @raising = nil + @yielding = [] + @arguments = :any_args + @type = type || :mock + end + + def mock? + @type == :mock + end + + def stub? + @type == :stub + end + + def count + @count ||= mock? ? [:exactly, 1] : [:any_number_of_times, 0] + end + + def arguments + @arguments + end + + def returning + if @multiple_returns + if @returning.size == 1 + @multiple_returns = false + return @returning = @returning.shift + end + return @returning.shift + end + @returning + end + + def times + self + end + + def calls + @calls ||= 0 + end + + def called + @calls = calls + 1 + end + + def exactly(n) + @count = [:exactly, n_times(n)] + self + end + + def at_least(n) + @count = [:at_least, n_times(n)] + self + end + + def at_most(n) + @count = [:at_most, n_times(n)] + self + end + + def once + exactly 1 + end + + def twice + exactly 2 + end + + def any_number_of_times + @count = [:any_number_of_times, 0] + self + end + + def with(*args) + raise ArgumentError, "you must specify the expected arguments" if args.empty? + if args.length == 1 + @arguments = args.first + else + @arguments = args + end + self + end + + def and_return(*args) + case args.size + when 0 + @returning = nil + when 1 + @returning = args[0] + else + @multiple_returns = true + @returning = args + count[1] = args.size if count[1] < args.size + end + self + end + + def and_raise(exception) + if exception.kind_of? String + @raising = RuntimeError.new exception + else + @raising = exception + end + end + + def raising? + @raising != nil + end + + def and_yield(*args) + @yielding << args + self + end + + def yielding? + !@yielding.empty? + end + + private + + def n_times(n) + case n + when :once + 1 + when :twice + 2 + else + Integer n + end + end +end diff --git a/spec/mspec/lib/mspec/runner.rb b/spec/mspec/lib/mspec/runner.rb new file mode 100644 index 0000000000..df57b9f69b --- /dev/null +++ b/spec/mspec/lib/mspec/runner.rb @@ -0,0 +1,12 @@ +require 'mspec/mocks' +require 'mspec/runner/mspec' +require 'mspec/runner/context' +require 'mspec/runner/evaluate' +require 'mspec/runner/example' +require 'mspec/runner/exception' +require 'mspec/runner/object' +require 'mspec/runner/formatters' +require 'mspec/runner/actions' +require 'mspec/runner/filters' +require 'mspec/runner/shared' +require 'mspec/runner/tag' diff --git a/spec/mspec/lib/mspec/runner/actions.rb b/spec/mspec/lib/mspec/runner/actions.rb new file mode 100644 index 0000000000..0a5a05fbd1 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions.rb @@ -0,0 +1,6 @@ +require 'mspec/runner/actions/tally' +require 'mspec/runner/actions/timer' +require 'mspec/runner/actions/filter' +require 'mspec/runner/actions/tag' +require 'mspec/runner/actions/taglist' +require 'mspec/runner/actions/tagpurge' diff --git a/spec/mspec/lib/mspec/runner/actions/filter.rb b/spec/mspec/lib/mspec/runner/actions/filter.rb new file mode 100644 index 0000000000..35899c8dc8 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions/filter.rb @@ -0,0 +1,40 @@ +require 'mspec/runner/filters/match' + +# ActionFilter is a base class for actions that are triggered by +# specs that match the filter. The filter may be specified by +# strings that match spec descriptions or by tags for strings +# that match spec descriptions. +# +# Unlike TagFilter and RegexpFilter, ActionFilter instances do +# not affect the specs that are run. The filter is only used to +# trigger the action. + +class ActionFilter + def initialize(tags=nil, descs=nil) + @tags = Array(tags) + descs = Array(descs) + @sfilter = descs.empty? ? nil : MatchFilter.new(nil, *descs) + @tfilter = nil + end + + def ===(string) + @sfilter === string or @tfilter === string + end + + def load + return if @tags.empty? + + desc = MSpec.read_tags(@tags).map { |t| t.description } + return if desc.empty? + + @tfilter = MatchFilter.new(nil, *desc) + end + + def register + MSpec.register :load, self + end + + def unregister + MSpec.unregister :load, self + end +end diff --git a/spec/mspec/lib/mspec/runner/actions/leakchecker.rb b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb new file mode 100644 index 0000000000..e947cda9ff --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions/leakchecker.rb @@ -0,0 +1,301 @@ +# Adapted from ruby's test/lib/leakchecker.rb. +# Ruby's 2-clause BSDL follows. + +# Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +class LeakChecker + def initialize + @fd_info = find_fds + @tempfile_info = find_tempfiles + @thread_info = find_threads + @env_info = find_env + @argv_info = find_argv + @encoding_info = find_encodings + end + + def check(test_name) + @no_leaks = true + leaks = [ + check_fd_leak(test_name), + check_tempfile_leak(test_name), + check_thread_leak(test_name), + check_process_leak(test_name), + check_env(test_name), + check_argv(test_name), + check_encodings(test_name) + ] + GC.start if leaks.any? + return leaks.none? + end + + private + def find_fds + fd_dir = "/proc/self/fd" + if File.directory?(fd_dir) + fds = Dir.open(fd_dir) {|d| + a = d.grep(/\A\d+\z/, &:to_i) + if d.respond_to? :fileno + a -= [d.fileno] + end + a + } + fds.sort + else + [] + end + end + + def check_fd_leak(test_name) + leaked = false + live1 = @fd_info + if IO.respond_to?(:console) and (m = IO.method(:console)).arity.nonzero? + m[:close] + end + live2 = find_fds + fd_closed = live1 - live2 + if !fd_closed.empty? + fd_closed.each {|fd| + puts "Closed file descriptor: #{test_name}: #{fd}" + } + end + fd_leaked = live2 - live1 + if !fd_leaked.empty? + leaked = true + h = {} + ObjectSpace.each_object(IO) {|io| + inspect = io.inspect + begin + autoclose = io.autoclose? + fd = io.fileno + rescue IOError # closed IO object + next + end + (h[fd] ||= []) << [io, autoclose, inspect] + } + fd_leaked.each {|fd| + str = '' + if h[fd] + str << ' :' + h[fd].map {|io, autoclose, inspect| + s = ' ' + inspect + s << "(not-autoclose)" if !autoclose + s + }.sort.each {|s| + str << s + } + end + puts "Leaked file descriptor: #{test_name}: #{fd}#{str}" + } + #system("lsof -p #$$") if !fd_leaked.empty? + h.each {|fd, list| + next if list.length <= 1 + if 1 < list.count {|io, autoclose, inspect| autoclose } + str = list.map {|io, autoclose, inspect| " #{inspect}" + (autoclose ? "(autoclose)" : "") }.sort.join + puts "Multiple autoclose IO object for a file descriptor:#{str}" + end + } + end + @fd_info = live2 + return leaked + end + + def extend_tempfile_counter + return if defined? LeakChecker::TempfileCounter + m = Module.new { + @count = 0 + class << self + attr_accessor :count + end + + def new(data) + LeakChecker::TempfileCounter.count += 1 + super(data) + end + } + LeakChecker.const_set(:TempfileCounter, m) + + class << Tempfile::Remover + prepend LeakChecker::TempfileCounter + end + end + + def find_tempfiles(prev_count=-1) + return [prev_count, []] unless defined? Tempfile + extend_tempfile_counter + count = TempfileCounter.count + if prev_count == count + [prev_count, []] + else + tempfiles = ObjectSpace.each_object(Tempfile).find_all {|t| t.path } + [count, tempfiles] + end + end + + def check_tempfile_leak(test_name) + return false unless defined? Tempfile + count1, initial_tempfiles = @tempfile_info + count2, current_tempfiles = find_tempfiles(count1) + leaked = false + tempfiles_leaked = current_tempfiles - initial_tempfiles + if !tempfiles_leaked.empty? + leaked = true + list = tempfiles_leaked.map {|t| t.inspect }.sort + list.each {|str| + puts "Leaked tempfile: #{test_name}: #{str}" + } + tempfiles_leaked.each {|t| t.close! } + end + @tempfile_info = [count2, initial_tempfiles] + return leaked + end + + def find_threads + Thread.list.find_all {|t| + t != Thread.current && t.alive? + } + end + + def check_thread_leak(test_name) + live1 = @thread_info + live2 = find_threads + thread_finished = live1 - live2 + leaked = false + if !thread_finished.empty? + list = thread_finished.map {|t| t.inspect }.sort + list.each {|str| + puts "Finished thread: #{test_name}: #{str}" + } + end + thread_leaked = live2 - live1 + if !thread_leaked.empty? + leaked = true + list = thread_leaked.map {|t| t.inspect }.sort + list.each {|str| + puts "Leaked thread: #{test_name}: #{str}" + } + end + @thread_info = live2 + return leaked + end + + def check_process_leak(test_name) + subprocesses_leaked = Process.waitall + subprocesses_leaked.each { |pid, status| + puts "Leaked subprocess: #{pid}: #{status}" + } + return !subprocesses_leaked.empty? + end + + def find_env + ENV.to_h + end + + def check_env(test_name) + old_env = @env_info + new_env = find_env + return false if old_env == new_env + (old_env.keys | new_env.keys).sort.each {|k| + if old_env.has_key?(k) + if new_env.has_key?(k) + if old_env[k] != new_env[k] + puts "Environment variable changed: #{test_name} : #{k.inspect} changed : #{old_env[k].inspect} -> #{new_env[k].inspect}" + end + else + puts "Environment variable changed: #{test_name} : #{k.inspect} deleted" + end + else + if new_env.has_key?(k) + puts "Environment variable changed: #{test_name} : #{k.inspect} added" + else + flunk "unreachable" + end + end + } + @env_info = new_env + return true + end + + def find_argv + ARGV.map { |e| e.dup } + end + + def check_argv(test_name) + old_argv = @argv_info + new_argv = find_argv + leaked = false + if new_argv != old_argv + puts "ARGV changed: #{test_name} : #{old_argv.inspect} to #{new_argv.inspect}" + @argv_info = new_argv + leaked = true + end + return leaked + end + + def find_encodings + [Encoding.default_internal, Encoding.default_external] + end + + def check_encodings(test_name) + old_internal, old_external = @encoding_info + new_internal, new_external = find_encodings + leaked = false + if new_internal != old_internal + leaked = true + puts "Encoding.default_internal changed: #{test_name} : #{old_internal} to #{new_internal}" + end + if new_external != old_external + leaked = true + puts "Encoding.default_external changed: #{test_name} : #{old_external} to #{new_external}" + end + @encoding_info = [new_internal, new_external] + return leaked + end + + def puts(*args) + if @no_leaks + @no_leaks = false + print "\n" + end + super(*args) + end +end + +class LeakCheckerAction + def register + MSpec.register :start, self + MSpec.register :after, self + end + + def start + @checker = LeakChecker.new + end + + def after(state) + unless @checker.check(state.description) + if state.example + puts state.example.source_location.join(':') + end + end + end +end diff --git a/spec/mspec/lib/mspec/runner/actions/tag.rb b/spec/mspec/lib/mspec/runner/actions/tag.rb new file mode 100644 index 0000000000..760152b2a3 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions/tag.rb @@ -0,0 +1,133 @@ +require 'mspec/runner/actions/filter' + +# TagAction - Write tagged spec description string to a +# tag file associated with each spec file. +# +# The action is triggered by specs whose descriptions +# match the filter created with 'tags' and/or 'desc' +# +# The action fires in the :after event, after the spec +# had been run. The action fires if the outcome of +# running the spec matches 'outcome'. +# +# The arguments are: +# +# action: :add, :del +# outcome: :pass, :fail, :all +# tag: the tag to create/delete +# comment: the comment to create +# tags: zero or more tags to get matching +# spec description strings from +# desc: zero or more strings to match the +# spec description strings + +class TagAction < ActionFilter + def initialize(action, outcome, tag, comment, tags=nil, descs=nil) + super tags, descs + @action = action + @outcome = outcome + @tag = tag + @comment = comment + @report = [] + @exception = false + end + + # Returns true if there are no _tag_ or _description_ filters. This + # means that a TagAction matches any example by default. Otherwise, + # returns true if either the _tag_ or the _description_ filter + # matches +string+. + def ===(string) + return true unless @sfilter or @tfilter + @sfilter === string or @tfilter === string + end + + # Callback for the MSpec :before event. Resets the +#exception?+ + # flag to false. + def before(state) + @exception = false + end + + # Callback for the MSpec :exception event. Sets the +#exception?+ + # flag to true. + def exception(exception) + @exception = true + end + + # Callback for the MSpec :after event. Performs the tag action + # depending on the type of action and the outcome of evaluating + # the example. See +TagAction+ for a description of the actions. + def after(state) + if self === state.description and outcome? + tag = SpecTag.new + tag.tag = @tag + tag.comment = @comment + tag.description = state.description + + case @action + when :add + changed = MSpec.write_tag tag + when :del + changed = MSpec.delete_tag tag + end + + @report << state.description if changed + end + end + + # Returns true if the result of evaluating the example matches + # the _outcome_ registered for this tag action. See +TagAction+ + # for a description of the _outcome_ types. + def outcome? + @outcome == :all or + (@outcome == :pass and not exception?) or + (@outcome == :fail and exception?) + end + + # Returns true if an exception was raised while evaluating the + # current example. + def exception? + @exception + end + + def report + @report.join("\n") + "\n" + end + private :report + + # Callback for the MSpec :finish event. Prints the actions + # performed while evaluating the examples. + def finish + case @action + when :add + if @report.empty? + print "\nTagAction: no specs were tagged with '#{@tag}'\n" + else + print "\nTagAction: specs tagged with '#{@tag}':\n\n" + print report + end + when :del + if @report.empty? + print "\nTagAction: no tags '#{@tag}' were deleted\n" + else + print "\nTagAction: tag '#{@tag}' deleted for specs:\n\n" + print report + end + end + end + + def register + super + MSpec.register :before, self + MSpec.register :exception, self + MSpec.register :after, self + MSpec.register :finish, self + end + + def unregister + super + MSpec.unregister :before, self + MSpec.unregister :exception, self + MSpec.unregister :after, self + MSpec.unregister :finish, self + end +end diff --git a/spec/mspec/lib/mspec/runner/actions/taglist.rb b/spec/mspec/lib/mspec/runner/actions/taglist.rb new file mode 100644 index 0000000000..c1aba53794 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions/taglist.rb @@ -0,0 +1,56 @@ +require 'mspec/runner/actions/filter' + +# TagListAction - prints out the descriptions for any specs +# tagged with +tags+. If +tags+ is an empty list, prints out +# descriptions for any specs that are tagged. +class TagListAction + def initialize(tags=nil) + @tags = tags.nil? || tags.empty? ? nil : Array(tags) + @filter = nil + end + + # Returns true. This enables us to match any tag when loading + # tags from the file. + def include?(arg) + true + end + + # Returns true if any tagged descriptions matches +string+. + def ===(string) + @filter === string + end + + # Prints a banner about matching tagged specs. + def start + if @tags + print "\nListing specs tagged with #{@tags.map { |t| "'#{t}'" }.join(", ") }\n\n" + else + print "\nListing all tagged specs\n\n" + end + end + + # Creates a MatchFilter for specific tags or for all tags. + def load + @filter = nil + desc = MSpec.read_tags(@tags || self).map { |t| t.description } + @filter = MatchFilter.new(nil, *desc) unless desc.empty? + end + + # Prints the spec description if it matches the filter. + def after(state) + return unless self === state.description + print state.description, "\n" + end + + def register + MSpec.register :start, self + MSpec.register :load, self + MSpec.register :after, self + end + + def unregister + MSpec.unregister :start, self + MSpec.unregister :load, self + MSpec.unregister :after, self + end +end diff --git a/spec/mspec/lib/mspec/runner/actions/tagpurge.rb b/spec/mspec/lib/mspec/runner/actions/tagpurge.rb new file mode 100644 index 0000000000..f4587de6bc --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions/tagpurge.rb @@ -0,0 +1,56 @@ +require 'mspec/runner/actions/filter' +require 'mspec/runner/actions/taglist' + +# TagPurgeAction - removes all tags not matching any spec +# descriptions. +class TagPurgeAction < TagListAction + attr_reader :matching + + def initialize + @matching = [] + @filter = nil + @tags = nil + end + + # Prints a banner about purging tags. + def start + print "\nRemoving tags not matching any specs\n\n" + end + + # Creates a MatchFilter for all tags. + def load + @filter = nil + @tags = MSpec.read_tags self + desc = @tags.map { |t| t.description } + @filter = MatchFilter.new(nil, *desc) unless desc.empty? + end + + # Saves any matching tags + def after(state) + @matching << state.description if self === state.description + end + + # Rewrites any matching tags. Prints non-matching tags. + # Deletes the tag file if there were no tags (this cleans + # up empty or malformed tag files). + def unload + if @filter + matched = @tags.select { |t| @matching.any? { |s| s == t.description } } + MSpec.write_tags matched + + (@tags - matched).each { |t| print t.description, "\n" } + else + MSpec.delete_tags + end + end + + def register + super + MSpec.register :unload, self + end + + def unregister + super + MSpec.unregister :unload, self + end +end diff --git a/spec/mspec/lib/mspec/runner/actions/tally.rb b/spec/mspec/lib/mspec/runner/actions/tally.rb new file mode 100644 index 0000000000..33f937293c --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions/tally.rb @@ -0,0 +1,133 @@ +class Tally + attr_accessor :files, :examples, :expectations, :failures, :errors, :guards, :tagged + + def initialize + @files = @examples = @expectations = @failures = @errors = @guards = @tagged = 0 + end + + def files!(add=1) + @files += add + end + + def examples!(add=1) + @examples += add + end + + def expectations!(add=1) + @expectations += add + end + + def failures!(add=1) + @failures += add + end + + def errors!(add=1) + @errors += add + end + + def guards!(add=1) + @guards += add + end + + def tagged!(add=1) + @tagged += add + end + + def file + pluralize files, "file" + end + + def example + pluralize examples, "example" + end + + def expectation + pluralize expectations, "expectation" + end + + def failure + pluralize failures, "failure" + end + + def error + pluralize errors, "error" + end + + def guard + pluralize guards, "guard" + end + + def tag + "#{tagged} tagged" + end + + def format + results = [ file, example, expectation, failure, error, tag ] + if [:report, :report_on, :verify].any? { |m| MSpec.mode? m } + results << guard + end + results.join(", ") + end + + alias_method :to_s, :format + + def pluralize(count, singular) + "#{count} #{singular}#{'s' unless count == 1}" + end + private :pluralize +end + +class TallyAction + attr_reader :counter + + def initialize + @counter = Tally.new + end + + def register + MSpec.register :load, self + MSpec.register :exception, self + MSpec.register :example, self + MSpec.register :tagged, self + MSpec.register :expectation, self + end + + def unregister + MSpec.unregister :load, self + MSpec.unregister :exception, self + MSpec.unregister :example, self + MSpec.unregister :tagged, self + MSpec.unregister :expectation, self + end + + def load + @counter.files! + end + + # Callback for the MSpec :expectation event. Increments the + # tally of expectations (e.g. #should, #should_receive, etc.). + def expectation(state) + @counter.expectations! + end + + # Callback for the MSpec :exception event. Increments the + # tally of errors and failures. + def exception(exception) + exception.failure? ? @counter.failures! : @counter.errors! + end + + # Callback for the MSpec :example event. Increments the tally + # of examples. + def example(state, block) + @counter.examples! + end + + def tagged(state) + @counter.examples! + @counter.tagged! + end + + def format + @counter.format + end +end diff --git a/spec/mspec/lib/mspec/runner/actions/timer.rb b/spec/mspec/lib/mspec/runner/actions/timer.rb new file mode 100644 index 0000000000..e7ebfebe0d --- /dev/null +++ b/spec/mspec/lib/mspec/runner/actions/timer.rb @@ -0,0 +1,22 @@ +class TimerAction + def register + MSpec.register :start, self + MSpec.register :finish, self + end + + def start + @start = Time.now + end + + def finish + @stop = Time.now + end + + def elapsed + @stop - @start + end + + def format + "Finished in %f seconds" % elapsed + end +end diff --git a/spec/mspec/lib/mspec/runner/context.rb b/spec/mspec/lib/mspec/runner/context.rb new file mode 100644 index 0000000000..2b470f226a --- /dev/null +++ b/spec/mspec/lib/mspec/runner/context.rb @@ -0,0 +1,239 @@ +# Holds the state of the +describe+ block that is being +# evaluated. Every example (i.e. +it+ block) is evaluated +# in a context, which may include state set up in before +# :each or before :all blocks. +# +#-- +# A note on naming: this is named _ContextState_ rather +# than _DescribeState_ because +describe+ is the keyword +# in the DSL for refering to the context in which an example +# is evaluated, just as +it+ refers to the example itself. +#++ +class ContextState + attr_reader :state, :parent, :parents, :children, :examples, :to_s + + def initialize(mod, options=nil) + @to_s = mod.to_s + if options.is_a? Hash + @options = options + else + @to_s += "#{".:#".include?(options[0,1]) ? "" : " "}#{options}" if options + @options = { } + end + @options[:shared] ||= false + + @parsed = false + @before = { :all => [], :each => [] } + @after = { :all => [], :each => [] } + @pre = {} + @post = {} + @examples = [] + @parent = nil + @parents = [self] + @children = [] + + @mock_verify = Proc.new { Mock.verify_count } + @mock_cleanup = Proc.new { Mock.cleanup } + @expectation_missing = Proc.new { raise SpecExpectationNotFoundError } + end + + # Remove caching when a ContextState is dup'd for shared specs. + def initialize_copy(other) + @pre = {} + @post = {} + end + + # Returns true if this is a shared +ContextState+. Essentially, when + # created with: describe "Something", :shared => true { ... } + def shared? + return @options[:shared] + end + + # Set the parent (enclosing) +ContextState+ for this state. Creates + # the +parents+ list. + def parent=(parent) + @description = nil + + if shared? + @parent = nil + else + @parent = parent + parent.child self if parent + + @parents = [self] + state = parent + while state + @parents.unshift state + state = state.parent + end + end + end + + # Add the ContextState instance +child+ to the list of nested + # describe blocks. + def child(child) + @children << child + end + + # Adds a nested ContextState in a shared ContextState to a containing + # ContextState. + # + # Normal adoption is from the parent's perspective. But adopt is a good + # verb and it's reasonable for the child to adopt the parent as well. In + # this case, manipulating state from inside the child avoids needlessly + # exposing the state to manipulate it externally in the dup. (See + # #it_should_behave_like) + def adopt(parent) + self.parent = parent + + @examples = @examples.map do |example| + example = example.dup + example.context = self + example + end + + children = @children + @children = [] + + children.each { |child| child.dup.adopt self } + end + + # Returns a list of all before(+what+) blocks from self and any parents. + def pre(what) + @pre[what] ||= parents.inject([]) { |l, s| l.push(*s.before(what)) } + end + + # Returns a list of all after(+what+) blocks from self and any parents. + # The list is in reverse order. In other words, the blocks defined in + # inner describes are in the list before those defined in outer describes, + # and in a particular describe block those defined later are in the list + # before those defined earlier. + def post(what) + @post[what] ||= parents.inject([]) { |l, s| l.unshift(*s.after(what)) } + end + + # Records before(:each) and before(:all) blocks. + def before(what, &block) + return if MSpec.guarded? + block ? @before[what].push(block) : @before[what] + end + + # Records after(:each) and after(:all) blocks. + def after(what, &block) + return if MSpec.guarded? + block ? @after[what].unshift(block) : @after[what] + end + + # Creates an ExampleState instance for the block and stores it + # in a list of examples to evaluate unless the example is filtered. + def it(desc, &block) + example = ExampleState.new(self, desc, block) + MSpec.actions :add, example + return if MSpec.guarded? + @examples << example + end + + # Evaluates the block and resets the toplevel +ContextState+ to #parent. + def describe(&block) + @parsed = protect @to_s, block, false + MSpec.register_current parent + MSpec.register_shared self if shared? + end + + # Returns a description string generated from self and all parents + def description + @description ||= parents.map { |p| p.to_s }.compact.join(" ") + end + + # Injects the before/after blocks and examples from the shared + # describe block into this +ContextState+ instance. + def it_should_behave_like(desc) + return if MSpec.guarded? + + unless state = MSpec.retrieve_shared(desc) + raise Exception, "Unable to find shared 'describe' for #{desc}" + end + + state.before(:all).each { |b| before :all, &b } + state.before(:each).each { |b| before :each, &b } + state.after(:each).each { |b| after :each, &b } + state.after(:all).each { |b| after :all, &b } + + state.examples.each do |example| + example = example.dup + example.context = self + @examples << example + end + + state.children.each do |child| + child.dup.adopt self + end + end + + # Evaluates each block in +blocks+ using the +MSpec.protect+ method + # so that exceptions are handled and tallied. Returns true and does + # NOT evaluate any blocks if +check+ is true and + # MSpec.mode?(:pretend) is true. + def protect(what, blocks, check=true) + return true if check and MSpec.mode? :pretend + Array(blocks).all? { |block| MSpec.protect what, &block } + end + + # Removes filtered examples. Returns true if there are examples + # left to evaluate. + def filter_examples + filtered, @examples = @examples.partition do |ex| + ex.filtered? + end + + filtered.each do |ex| + MSpec.actions :tagged, ex + end + + !@examples.empty? + end + + # Evaluates the examples in a +ContextState+. Invokes the MSpec events + # for :enter, :before, :after, :leave. + def process + MSpec.register_current self + + if @parsed and filter_examples + MSpec.shuffle @examples if MSpec.randomize? + MSpec.actions :enter, description + + if protect "before :all", pre(:all) + @examples.each do |state| + MSpec.repeat do + @state = state + example = state.example + MSpec.actions :before, state + + if protect "before :each", pre(:each) + MSpec.clear_expectations + if example + passed = protect nil, example + MSpec.actions :example, state, example + protect nil, @expectation_missing unless MSpec.expectation? or !passed + end + end + protect "after :each", post(:each) + protect "Mock.verify_count", @mock_verify + + protect "Mock.cleanup", @mock_cleanup + MSpec.actions :after, state + @state = nil + end + end + protect "after :all", post(:all) + else + protect "Mock.cleanup", @mock_cleanup + end + + MSpec.actions :leave + end + + MSpec.register_current nil + children.each { |child| child.process } + end +end diff --git a/spec/mspec/lib/mspec/runner/evaluate.rb b/spec/mspec/lib/mspec/runner/evaluate.rb new file mode 100644 index 0000000000..fded84421f --- /dev/null +++ b/spec/mspec/lib/mspec/runner/evaluate.rb @@ -0,0 +1,54 @@ +class SpecEvaluate + def self.desc=(desc) + @desc = desc + end + + def self.desc + @desc ||= "evaluates " + end + + def initialize(ruby, desc) + @ruby = ruby.rstrip + @desc = desc || self.class.desc + end + + # Formats the Ruby source code for reabable output in the -fs formatter + # option. If the source contains no newline characters, wraps the source in + # single quotes to set if off from the rest of the description string. If + # the source does contain newline characters, sets the indent level to four + # characters. + def format(ruby, newline=true) + if ruby.include?("\n") + lines = ruby.each_line.to_a + if /( *)/ =~ lines.first + if $1.size > 4 + dedent = $1.size - 4 + ruby = lines.map { |l| l[dedent..-1] }.join + else + indent = " " * (4 - $1.size) + ruby = lines.map { |l| "#{indent}#{l}" }.join + end + end + "\n#{ruby}" + else + "'#{ruby.lstrip}'" + end + end + + def define(&block) + ruby = @ruby + desc = @desc + evaluator = self + + specify "#{desc} #{format ruby}" do + evaluator.instance_eval(ruby) + evaluator.instance_eval(&block) + end + end +end + +class Object + def evaluate(str, desc=nil, &block) + SpecEvaluate.new(str, desc).define(&block) + end +end diff --git a/spec/mspec/lib/mspec/runner/example.rb b/spec/mspec/lib/mspec/runner/example.rb new file mode 100644 index 0000000000..19eb29b079 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/example.rb @@ -0,0 +1,34 @@ +require 'mspec/runner/mspec' + +# Holds some of the state of the example (i.e. +it+ block) that is +# being evaluated. See also +ContextState+. +class ExampleState + attr_reader :context, :it, :example + + def initialize(context, it, example=nil) + @context = context + @it = it + @example = example + end + + def context=(context) + @description = nil + @context = context + end + + def describe + @context.description + end + + def description + @description ||= "#{describe} #{@it}" + end + + def filtered? + incl = MSpec.retrieve(:include) || [] + excl = MSpec.retrieve(:exclude) || [] + included = incl.empty? || incl.any? { |f| f === description } + included &&= excl.empty? || !excl.any? { |f| f === description } + !included + end +end diff --git a/spec/mspec/lib/mspec/runner/exception.rb b/spec/mspec/lib/mspec/runner/exception.rb new file mode 100644 index 0000000000..0d9bb43105 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/exception.rb @@ -0,0 +1,43 @@ +# Initialize $MSPEC_DEBUG +$MSPEC_DEBUG ||= false + +class ExceptionState + attr_reader :description, :describe, :it, :exception + + def initialize(state, location, exception) + @exception = exception + + @description = location ? "An exception occurred during: #{location}" : "" + if state + @description += "\n" unless @description.empty? + @description += state.description + @describe = state.describe + @it = state.it + else + @describe = @it = "" + end + end + + def failure? + [SpecExpectationNotMetError, SpecExpectationNotFoundError].any? { |e| @exception.is_a? e } + end + + def message + if @exception.message.empty? + "" + elsif @exception.class == SpecExpectationNotMetError || + @exception.class == SpecExpectationNotFoundError + @exception.message + else + "#{@exception.class}: #{@exception.message}" + end + end + + def backtrace + @backtrace_filter ||= MSpecScript.config[:backtrace_filter] + + bt = @exception.backtrace || [] + + bt.select { |line| $MSPEC_DEBUG or @backtrace_filter !~ line }.join("\n") + end +end diff --git a/spec/mspec/lib/mspec/runner/filters.rb b/spec/mspec/lib/mspec/runner/filters.rb new file mode 100644 index 0000000000..d0420faca6 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/filters.rb @@ -0,0 +1,4 @@ +require 'mspec/runner/filters/match' +require 'mspec/runner/filters/regexp' +require 'mspec/runner/filters/tag' +require 'mspec/runner/filters/profile' diff --git a/spec/mspec/lib/mspec/runner/filters/match.rb b/spec/mspec/lib/mspec/runner/filters/match.rb new file mode 100644 index 0000000000..539fd02d01 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/filters/match.rb @@ -0,0 +1,18 @@ +class MatchFilter + def initialize(what, *strings) + @what = what + @strings = strings + end + + def ===(string) + @strings.any? { |s| string.include?(s) } + end + + def register + MSpec.register @what, self + end + + def unregister + MSpec.unregister @what, self + end +end diff --git a/spec/mspec/lib/mspec/runner/filters/profile.rb b/spec/mspec/lib/mspec/runner/filters/profile.rb new file mode 100644 index 0000000000..a59722c451 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/filters/profile.rb @@ -0,0 +1,54 @@ +class ProfileFilter + def initialize(what, *files) + @what = what + @methods = load(*files) + @pattern = /([^ .#]+[.#])([^ ]+)/ + end + + def find(name) + return name if File.exist?(File.expand_path(name)) + + ["spec/profiles", "spec", "profiles", "."].each do |dir| + file = File.join dir, name + return file if File.exist? file + end + end + + def parse(file) + pattern = /(\S+):\s*/ + key = "" + file.inject(Hash.new { |h,k| h[k] = [] }) do |hash, line| + line.chomp! + if line[0,2] == "- " + hash[key] << line[2..-1].gsub(/[ '"]/, "") + elsif m = pattern.match(line) + key = m[1] + end + hash + end + end + + def load(*files) + files.inject({}) do |hash, file| + next hash unless name = find(file) + + File.open name, "r" do |f| + hash.merge parse(f) + end + end + end + + def ===(string) + return false unless m = @pattern.match(string) + return false unless l = @methods[m[1]] + l.include? m[2] + end + + def register + MSpec.register @what, self + end + + def unregister + MSpec.unregister @what, self + end +end diff --git a/spec/mspec/lib/mspec/runner/filters/regexp.rb b/spec/mspec/lib/mspec/runner/filters/regexp.rb new file mode 100644 index 0000000000..2bd1448d3f --- /dev/null +++ b/spec/mspec/lib/mspec/runner/filters/regexp.rb @@ -0,0 +1,7 @@ +require 'mspec/runner/filters/match' + +class RegexpFilter < MatchFilter + def to_regexp(*strings) + strings.map { |str| Regexp.new str } + end +end diff --git a/spec/mspec/lib/mspec/runner/filters/tag.rb b/spec/mspec/lib/mspec/runner/filters/tag.rb new file mode 100644 index 0000000000..c641c01606 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/filters/tag.rb @@ -0,0 +1,29 @@ +class TagFilter + def initialize(what, *tags) + @what = what + @tags = tags + end + + def load + @descriptions = MSpec.read_tags(@tags).map { |t| t.description } + MSpec.register @what, self + end + + def unload + MSpec.unregister @what, self + end + + def ===(string) + @descriptions.include?(string) + end + + def register + MSpec.register :load, self + MSpec.register :unload, self + end + + def unregister + MSpec.unregister :load, self + MSpec.unregister :unload, self + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters.rb b/spec/mspec/lib/mspec/runner/formatters.rb new file mode 100644 index 0000000000..d085031a12 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters.rb @@ -0,0 +1,12 @@ +require 'mspec/runner/formatters/describe' +require 'mspec/runner/formatters/dotted' +require 'mspec/runner/formatters/file' +require 'mspec/runner/formatters/specdoc' +require 'mspec/runner/formatters/html' +require 'mspec/runner/formatters/summary' +require 'mspec/runner/formatters/unit' +require 'mspec/runner/formatters/spinner' +require 'mspec/runner/formatters/method' +require 'mspec/runner/formatters/yaml' +require 'mspec/runner/formatters/profile' +require 'mspec/runner/formatters/junit' diff --git a/spec/mspec/lib/mspec/runner/formatters/describe.rb b/spec/mspec/lib/mspec/runner/formatters/describe.rb new file mode 100644 index 0000000000..176bd79279 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/describe.rb @@ -0,0 +1,24 @@ +require 'mspec/runner/formatters/dotted' +require 'mspec/runner/actions/tally' + +class DescribeFormatter < DottedFormatter + # Callback for the MSpec :finish event. Prints a summary of + # the number of errors and failures for each +describe+ block. + def finish + describes = Hash.new { |h,k| h[k] = Tally.new } + + @exceptions.each do |exc| + desc = describes[exc.describe] + exc.failure? ? desc.failures! : desc.errors! + end + + print "\n" + describes.each do |d, t| + text = d.size > 40 ? "#{d[0,37]}..." : d.ljust(40) + print "\n#{text} #{t.failure}, #{t.error}" + end + print "\n" unless describes.empty? + + print "\n#{@timer.format}\n\n#{@tally.format}\n" + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/dotted.rb b/spec/mspec/lib/mspec/runner/formatters/dotted.rb new file mode 100644 index 0000000000..61c8e4c27c --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/dotted.rb @@ -0,0 +1,117 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/actions/timer' +require 'mspec/runner/actions/tally' +require 'mspec/runner/actions/leakchecker' if ENV['CHECK_LEAKS'] + +class DottedFormatter + attr_reader :exceptions, :timer, :tally + + def initialize(out=nil) + @exception = @failure = false + @exceptions = [] + @count = 0 # For subclasses + if out.nil? + @out = $stdout + else + @out = File.open out, "w" + end + + @current_state = nil + end + + # Creates the +TimerAction+ and +TallyAction+ instances and + # registers them. Registers +self+ for the +:exception+, + # +:before+, +:after+, and +:finish+ actions. + def register + (@timer = TimerAction.new).register + (@tally = TallyAction.new).register + LeakCheckerAction.new.register if ENV['CHECK_LEAKS'] + @counter = @tally.counter + + MSpec.register :exception, self + MSpec.register :before, self + MSpec.register :after, self + MSpec.register :finish, self + MSpec.register :abort, self + end + + def abort + if @current_state + puts "\naborting example: #{@current_state.description}" + end + end + + # Returns true if any exception is raised while running + # an example. This flag is reset before each example + # is evaluated. + def exception? + @exception + end + + # Returns true if all exceptions during the evaluation + # of an example are failures rather than errors. See + # ExceptionState#failure. This flag is reset + # before each example is evaluated. + def failure? + @failure + end + + # Callback for the MSpec :before event. Resets the + # +#exception?+ and +#failure+ flags. + def before(state=nil) + @current_state = state + @failure = @exception = false + end + + # Callback for the MSpec :exception event. Stores the + # +ExceptionState+ object to generate the list of backtraces + # after all the specs are run. Also updates the internal + # +#exception?+ and +#failure?+ flags. + def exception(exception) + @count += 1 + @failure = @exception ? @failure && exception.failure? : exception.failure? + @exception = true + @exceptions << exception + end + + # Callback for the MSpec :after event. Prints an indicator + # for the result of evaluating this example as follows: + # . = No failure or error + # F = An SpecExpectationNotMetError was raised + # E = Any exception other than SpecExpectationNotMetError + def after(state = nil) + @current_state = nil + + unless exception? + print "." + else + print failure? ? "F" : "E" + end + end + + # Callback for the MSpec :finish event. Prints a description + # and backtrace for every exception that occurred while + # evaluating the examples. + def finish + print "\n" + count = 0 + @exceptions.each do |exc| + count += 1 + print_exception(exc, count) + end + print "\n#{@timer.format}\n\n#{@tally.format}\n" + end + + def print_exception(exc, count) + outcome = exc.failure? ? "FAILED" : "ERROR" + print "\n#{count})\n#{exc.description} #{outcome}\n" + print exc.message, "\n" + print exc.backtrace, "\n" + end + + # A convenience method to allow printing to different outputs. + def print(*args) + @out.print(*args) + @out.flush + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/file.rb b/spec/mspec/lib/mspec/runner/formatters/file.rb new file mode 100644 index 0000000000..6db72af4ff --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/file.rb @@ -0,0 +1,19 @@ +require 'mspec/runner/formatters/dotted' + +class FileFormatter < DottedFormatter + # Unregisters DottedFormatter#before, #after methods and + # registers #load, #unload, which perform the same duties + # as #before, #after in DottedFormatter. + def register + super + + MSpec.unregister :before, self + MSpec.unregister :after, self + + MSpec.register :load, self + MSpec.register :unload, self + end + + alias_method :load, :before + alias_method :unload, :after +end diff --git a/spec/mspec/lib/mspec/runner/formatters/html.rb b/spec/mspec/lib/mspec/runner/formatters/html.rb new file mode 100644 index 0000000000..060d2732f0 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/html.rb @@ -0,0 +1,81 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/formatters/dotted' + +class HtmlFormatter < DottedFormatter + def register + super + MSpec.register :start, self + MSpec.register :enter, self + MSpec.register :leave, self + end + + def start + print <<-EOH + + + +Spec Output For #{RUBY_NAME} (#{RUBY_VERSION}) + + + +EOH + end + + def enter(describe) + print "

#{describe}

\n
    \n" + end + + def leave + print "
\n
\n" + end + + def exception(exception) + super + outcome = exception.failure? ? "FAILED" : "ERROR" + print %[
  • - #{exception.it} (] + print %[#{outcome} - #{@count})
  • \n] + end + + def after(state) + print %[
  • - #{state.it}
  • \n] unless exception? + end + + def finish + success = @exceptions.empty? + unless success + print "
    \n" + print %[
      ] + count = 0 + @exceptions.each do |exc| + outcome = exc.failure? ? "FAILED" : "ERROR" + print %[\n
    1. #{escape(exc.description)} #{outcome}

      \n

      ] + print escape(exc.message) + print "

      \n
      \n"
      +        print escape(exc.backtrace)
      +        print "
      \n
    2. \n" + end + print "
    \n" + end + print %[

    #{@timer.format}

    \n] + print %[

    #{@tally.format}

    \n] + print "\n\n" + end + + def escape(string) + string.gsub("&", " ").gsub("<", "<").gsub(">", ">") + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/junit.rb b/spec/mspec/lib/mspec/runner/formatters/junit.rb new file mode 100644 index 0000000000..647deee7e1 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/junit.rb @@ -0,0 +1,89 @@ +require 'mspec/expectations/expectations' +require 'mspec/utils/ruby_name' +require 'mspec/runner/formatters/yaml' + +class JUnitFormatter < YamlFormatter + def initialize(out=nil) + super + @tests = [] + end + + def after(state = nil) + super + @tests << {:test => state, :exception => false} unless exception? + end + + def exception(exception) + super + @tests << {:test => exception, :exception => true} + end + + def finish + switch + + time = @timer.elapsed + tests = @tally.counter.examples + errors = @tally.counter.errors + failures = @tally.counter.failures + + printf <<-XML + + + + + XML + @tests.each do |h| + description = encode_for_xml h[:test].description + + printf <<-XML, "Spec", description, 0.0 + + XML + if h[:exception] + outcome = h[:test].failure? ? "failure" : "error" + message = encode_for_xml h[:test].message + backtrace = encode_for_xml h[:test].backtrace + print <<-XML + <#{outcome} message="error in #{description}" type="#{outcome}"> + #{message} + #{backtrace} + + XML + end + print <<-XML + + XML + end + + print <<-XML + + + XML + end + + private + LT = "<" + GT = ">" + QU = """ + AP = "'" + AM = "&" + TARGET_ENCODING = "ISO-8859-1" + + def encode_for_xml(str) + encode_as_latin1(str).gsub("<", LT).gsub(">", GT). + gsub('"', QU).gsub("'", AP).gsub("&", AM). + tr("\x00-\x08", "?") + end + + def encode_as_latin1(str) + str.encode(TARGET_ENCODING, :undef => :replace, :invalid => :replace) + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/method.rb b/spec/mspec/lib/mspec/runner/formatters/method.rb new file mode 100644 index 0000000000..ff115193fd --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/method.rb @@ -0,0 +1,93 @@ +require 'mspec/runner/formatters/dotted' + +class MethodFormatter < DottedFormatter + attr_accessor :methods + + def initialize(out=nil) + super + @methods = Hash.new do |h, k| + hash = {} + hash[:examples] = 0 + hash[:expectations] = 0 + hash[:failures] = 0 + hash[:errors] = 0 + hash[:exceptions] = [] + h[k] = hash + end + end + + # Returns the type of method as a "class", "instance", + # or "unknown". + def method_type(sep) + case sep + when '.', '::' + "class" + when '#' + "instance" + else + "unknown" + end + end + + # Callback for the MSpec :before event. Parses the + # describe string into class and method if possible. + # Resets the tallies so the counts are only for this + # example. + def before(state) + super + + # The pattern for a method name is not correctly + # restrictive but it is simplistic and useful + # for our purpose. + /^([A-Za-z_]+\w*)(\.|#|::)([^ ]+)/ =~ state.describe + @key = $1 && $2 && $3 ? "#{$1}#{$2}#{$3}" : state.describe + + unless methods.key? @key + h = methods[@key] + h[:class] = "#{$1}" + h[:method] = "#{$3}" + h[:type] = method_type $2 + h[:description] = state.description + end + + tally.counter.examples = 0 + tally.counter.expectations = 0 + tally.counter.failures = 0 + tally.counter.errors = 0 + + @exceptions = [] + end + + # Callback for the MSpec :after event. Sets or adds to + # tallies for the example block. + def after(state) + h = methods[@key] + h[:examples] += tally.counter.examples + h[:expectations] += tally.counter.expectations + h[:failures] += tally.counter.failures + h[:errors] += tally.counter.errors + @exceptions.each do |exc| + h[:exceptions] << "#{exc.message}\n#{exc.backtrace}\n" + end + end + + # Callback for the MSpec :finish event. Prints out the + # summary information in YAML format for all the methods. + def finish + print "---\n" + + methods.each do |key, hash| + print key.inspect, ":\n" + print " class: ", hash[:class].inspect, "\n" + print " method: ", hash[:method].inspect, "\n" + print " type: ", hash[:type], "\n" + print " description: ", hash[:description].inspect, "\n" + print " examples: ", hash[:examples], "\n" + print " expectations: ", hash[:expectations], "\n" + print " failures: ", hash[:failures], "\n" + print " errors: ", hash[:errors], "\n" + print " exceptions:\n" + hash[:exceptions].each { |exc| print " - ", exc.inspect, "\n" } + end + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/multi.rb b/spec/mspec/lib/mspec/runner/formatters/multi.rb new file mode 100644 index 0000000000..bcc5411e6f --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/multi.rb @@ -0,0 +1,36 @@ +require 'mspec/runner/formatters/spinner' +require 'yaml' + +class MultiFormatter < SpinnerFormatter + def initialize(out=nil) + super(out) + @counter = @tally = Tally.new + @timer = TimerAction.new + @timer.start + end + + def aggregate_results(files) + @timer.finish + @exceptions = [] + + files.each do |file| + d = File.open(file, "r") { |f| YAML.load f } + File.delete file + + @exceptions += Array(d['exceptions']) + @tally.files! d['files'] + @tally.examples! d['examples'] + @tally.expectations! d['expectations'] + @tally.errors! d['errors'] + @tally.failures! d['failures'] + end + end + + def print_exception(exc, count) + print "\n#{count})\n#{exc}\n" + end + + def finish + super(false) + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/profile.rb b/spec/mspec/lib/mspec/runner/formatters/profile.rb new file mode 100644 index 0000000000..498cd4a3b7 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/profile.rb @@ -0,0 +1,70 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/formatters/dotted' + +class ProfileFormatter < DottedFormatter + def initialize(out=nil) + super + + @describe_name = nil + @describe_time = nil + @describes = [] + @its = [] + end + + def register + super + MSpec.register :enter, self + end + + # Callback for the MSpec :enter event. Prints the + # +describe+ block string. + def enter(describe) + if @describe_time + @describes << [@describe_name, Time.now.to_f - @describe_time] + end + + @describe_name = describe + @describe_time = Time.now.to_f + end + + # Callback for the MSpec :before event. Prints the + # +it+ block string. + def before(state) + super + + @it_name = state.it + @it_time = Time.now.to_f + end + + # Callback for the MSpec :after event. Prints a + # newline to finish the description string output. + def after(state) + @its << [@describe_name, @it_name, Time.now.to_f - @it_time] + super + end + + def finish + puts "\nProfiling info:" + + desc = @describes.sort { |a,b| b.last <=> a.last } + desc.delete_if { |a| a.last <= 0.001 } + show = desc[0, 100] + + puts "Top #{show.size} describes:" + + show.each do |des, time| + printf "%3.3f - %s\n", time, des + end + + its = @its.sort { |a,b| b.last <=> a.last } + its.delete_if { |a| a.last <= 0.001 } + show = its[0, 100] + + puts "\nTop #{show.size} its:" + show.each do |des, it, time| + printf "%3.3f - %s %s\n", time, des, it + end + + super + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/specdoc.rb b/spec/mspec/lib/mspec/runner/formatters/specdoc.rb new file mode 100644 index 0000000000..29adde3c5c --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/specdoc.rb @@ -0,0 +1,41 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/formatters/dotted' + +class SpecdocFormatter < DottedFormatter + def register + super + MSpec.register :enter, self + end + + # Callback for the MSpec :enter event. Prints the + # +describe+ block string. + def enter(describe) + print "\n#{describe}\n" + end + + # Callback for the MSpec :before event. Prints the + # +it+ block string. + def before(state) + super + print "- #{state.it}" + end + + # Callback for the MSpec :exception event. Prints + # either 'ERROR - X' or 'FAILED - X' where _X_ is + # the sequential number of the exception raised. If + # there has already been an exception raised while + # evaluating this example, it prints another +it+ + # block description string so that each discription + # string has an associated 'ERROR' or 'FAILED' + def exception(exception) + print "\n- #{exception.it}" if exception? + super + print " (#{exception.failure? ? 'FAILED' : 'ERROR'} - #{@count})" + end + + # Callback for the MSpec :after event. Prints a + # newline to finish the description string output. + def after(state) + print "\n" + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/spinner.rb b/spec/mspec/lib/mspec/runner/formatters/spinner.rb new file mode 100644 index 0000000000..f6f35cc476 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/spinner.rb @@ -0,0 +1,117 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/formatters/dotted' + +class SpinnerFormatter < DottedFormatter + attr_reader :length + + Spins = %w!| / - \\! + HOUR = 3600 + MIN = 60 + + def initialize(out=nil) + super(nil) + + @which = 0 + @loaded = 0 + self.length = 40 + @percent = 0 + @start = Time.now + + term = ENV['TERM'] + @color = (term != "dumb") + @fail_color = "32" + @error_color = "32" + end + + def register + super + + MSpec.register :start, self + MSpec.register :unload, self + MSpec.unregister :before, self + end + + def length=(length) + @length = length + @ratio = 100.0 / length + @position = length / 2 - 2 + end + + def compute_etr + return @etr = "00:00:00" if @percent == 0 + elapsed = Time.now - @start + remain = (100 * elapsed / @percent) - elapsed + + hour = remain >= HOUR ? (remain / HOUR).to_i : 0 + remain -= hour * HOUR + min = remain >= MIN ? (remain / MIN).to_i : 0 + sec = remain - min * MIN + + @etr = "%02d:%02d:%02d" % [hour, min, sec] + end + + def compute_percentage + @percent = @loaded * 100 / @total + bar = ("=" * (@percent / @ratio)).ljust @length + label = "%d%%" % @percent + bar[@position, label.size] = label + @bar = bar + end + + def compute_progress + compute_percentage + compute_etr + end + + def progress_line + @which = (@which + 1) % Spins.size + data = [Spins[@which], @bar, @etr, @counter.failures, @counter.errors] + if @color + "\r[%s | %s | %s] \e[0;#{@fail_color}m%6dF \e[0;#{@error_color}m%6dE\e[0m " % data + else + "\r[%s | %s | %s] %6dF %6dE " % data + end + end + + def clear_progress_line + print "\r#{' '*progress_line.length}" + end + + # Callback for the MSpec :start event. Stores the total + # number of files that will be processed. + def start + @total = MSpec.retrieve(:files).size + compute_progress + print progress_line + end + + # Callback for the MSpec :unload event. Increments the number + # of files that have been run. + def unload + @loaded += 1 + compute_progress + print progress_line + end + + # Callback for the MSpec :exception event. Changes the color + # used to display the tally of errors and failures + def exception(exception) + super + @fail_color = "31" if exception.failure? + @error_color = "33" unless exception.failure? + + clear_progress_line + print_exception(exception, @count) + end + + # Callback for the MSpec :after event. Updates the spinner. + def after(state) + print progress_line + end + + def finish(printed_exceptions = true) + # We already printed the exceptions + @exceptions = [] if printed_exceptions + super() + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/summary.rb b/spec/mspec/lib/mspec/runner/formatters/summary.rb new file mode 100644 index 0000000000..0c9207194c --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/summary.rb @@ -0,0 +1,11 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/formatters/dotted' + +class SummaryFormatter < DottedFormatter + # Callback for the MSpec :after event. Overrides the + # callback provided by +DottedFormatter+ and does not + # print any output for each example evaluated. + def after(state) + # do nothing + end +end diff --git a/spec/mspec/lib/mspec/runner/formatters/unit.rb b/spec/mspec/lib/mspec/runner/formatters/unit.rb new file mode 100644 index 0000000000..69b68dc0d5 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/unit.rb @@ -0,0 +1,21 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/formatters/dotted' + +class UnitdiffFormatter < DottedFormatter + def finish + print "\n\n#{@timer.format}\n" + count = 0 + @exceptions.each do |exc| + outcome = exc.failure? ? "FAILED" : "ERROR" + print "\n#{count += 1})\n#{exc.description} #{outcome}\n" + print exc.message, ": \n" + print exc.backtrace, "\n" + end + print "\n#{@tally.format}\n" + end + + def backtrace(exc) + exc.backtrace && exc.backtrace.join("\n") + end + private :backtrace +end diff --git a/spec/mspec/lib/mspec/runner/formatters/yaml.rb b/spec/mspec/lib/mspec/runner/formatters/yaml.rb new file mode 100644 index 0000000000..090a9b1b9d --- /dev/null +++ b/spec/mspec/lib/mspec/runner/formatters/yaml.rb @@ -0,0 +1,42 @@ +require 'mspec/expectations/expectations' +require 'mspec/runner/formatters/dotted' + +class YamlFormatter < DottedFormatter + def initialize(out=nil) + super(nil) + + if out.nil? + @finish = $stdout + else + @finish = File.open out, "w" + end + end + + def switch + @out = @finish + end + + def after(state) + end + + def finish + switch + + print "---\n" + print "exceptions:\n" + @exceptions.each do |exc| + outcome = exc.failure? ? "FAILED" : "ERROR" + str = "#{exc.description} #{outcome}\n" + str << exc.message << "\n" << exc.backtrace + print "- ", str.inspect, "\n" + end + + print "time: ", @timer.elapsed, "\n" + print "files: ", @tally.counter.files, "\n" + print "examples: ", @tally.counter.examples, "\n" + print "expectations: ", @tally.counter.expectations, "\n" + print "failures: ", @tally.counter.failures, "\n" + print "errors: ", @tally.counter.errors, "\n" + print "tagged: ", @tally.counter.tagged, "\n" + end +end diff --git a/spec/mspec/lib/mspec/runner/mspec.rb b/spec/mspec/lib/mspec/runner/mspec.rb new file mode 100644 index 0000000000..0ff0de36ca --- /dev/null +++ b/spec/mspec/lib/mspec/runner/mspec.rb @@ -0,0 +1,391 @@ +require 'mspec/runner/context' +require 'mspec/runner/exception' +require 'mspec/runner/tag' + +module MSpec + + @exit = nil + @abort = nil + @start = nil + @enter = nil + @before = nil + @add = nil + @after = nil + @leave = nil + @finish = nil + @exclude = nil + @include = nil + @leave = nil + @load = nil + @unload = nil + @tagged = nil + @current = nil + @example = nil + @modes = [] + @shared = {} + @guarded = [] + @features = {} + @exception = nil + @randomize = nil + @repeat = nil + @expectation = nil + @expectations = false + + def self.describe(mod, options=nil, &block) + state = ContextState.new mod, options + state.parent = current + + MSpec.register_current state + state.describe(&block) + + state.process unless state.shared? or current + end + + def self.process + STDOUT.puts RUBY_DESCRIPTION + + actions :start + files + actions :finish + end + + def self.each_file(&block) + if ENV["MSPEC_MULTI"] + STDOUT.print "." + STDOUT.flush + while (file = STDIN.gets.chomp) != "QUIT" + yield file + STDOUT.print "." + STDOUT.flush + end + else + return unless files = retrieve(:files) + shuffle files if randomize? + files.each(&block) + end + end + + def self.files + each_file do |file| + setup_env + store :file, file + actions :load + protect("loading #{file}") { Kernel.load file } + actions :unload + end + end + + def self.setup_env + @env = Object.new + @env.extend MSpec + end + + def self.actions(action, *args) + actions = retrieve(action) + actions.each { |obj| obj.send action, *args } if actions + end + + def self.protect(location, &block) + begin + @env.instance_eval(&block) + return true + rescue SystemExit => e + raise e + rescue Exception => exc + register_exit 1 + actions :exception, ExceptionState.new(current && current.state, location, exc) + return false + end + end + + # Guards can be nested, so a stack is necessary to know when we have + # exited the toplevel guard. + def self.guard + @guarded << true + end + + def self.unguard + @guarded.pop + end + + def self.guarded? + !@guarded.empty? + end + + # Sets the toplevel ContextState to +state+. + def self.register_current(state) + store :current, state + end + + # Sets the toplevel ContextState to +nil+. + def self.clear_current + store :current, nil + end + + # Returns the toplevel ContextState. + def self.current + retrieve :current + end + + # Stores the shared ContextState keyed by description. + def self.register_shared(state) + @shared[state.to_s] = state + end + + # Returns the shared ContextState matching description. + def self.retrieve_shared(desc) + @shared[desc.to_s] + end + + # Stores the exit code used by the runner scripts. + def self.register_exit(code) + store :exit, code + end + + # Retrieves the stored exit code. + def self.exit_code + retrieve(:exit).to_i + end + + # Stores the list of files to be evaluated. + def self.register_files(files) + store :files, files + end + + # Stores one or more substitution patterns for transforming + # a spec filename into a tags filename, where each pattern + # has the form: + # + # [Regexp, String] + # + # See also +tags_file+. + def self.register_tags_patterns(patterns) + store :tags_patterns, patterns + end + + # Registers an operating mode. Modes recognized by MSpec: + # + # :pretend - actions execute but specs are not run + # :verify - specs are run despite guards and the result is + # verified to match the expectation of the guard + # :report - specs that are guarded are reported + # :unguarded - all guards are forced off + def self.register_mode(mode) + modes = retrieve :modes + modes << mode unless modes.include? mode + end + + # Clears all registered modes. + def self.clear_modes + store :modes, [] + end + + # Returns +true+ if +mode+ is registered. + def self.mode?(mode) + retrieve(:modes).include? mode + end + + def self.enable_feature(feature) + retrieve(:features)[feature] = true + end + + def self.disable_feature(feature) + retrieve(:features)[feature] = false + end + + def self.feature_enabled?(feature) + retrieve(:features)[feature] || false + end + + def self.retrieve(symbol) + instance_variable_get :"@#{symbol}" + end + + def self.store(symbol, value) + instance_variable_set :"@#{symbol}", value + end + + # This method is used for registering actions that are + # run at particular points in the spec cycle: + # :start before any specs are run + # :load before a spec file is loaded + # :enter before a describe block is run + # :before before a single spec is run + # :add while a describe block is adding examples to run later + # :expectation before a 'should', 'should_receive', etc. + # :example after an example block is run, passed the block + # :exception after an exception is rescued + # :after after a single spec is run + # :leave after a describe block is run + # :unload after a spec file is run + # :finish after all specs are run + # + # Objects registered as actions above should respond to + # a method of the same name. For example, if an object + # is registered as a :start action, it should respond to + # a #start method call. + # + # Additionally, there are two "action" lists for + # filtering specs: + # :include return true if the spec should be run + # :exclude return true if the spec should NOT be run + # + def self.register(symbol, action) + unless value = retrieve(symbol) + value = store symbol, [] + end + value << action unless value.include? action + end + + def self.unregister(symbol, action) + if value = retrieve(symbol) + value.delete action + end + end + + def self.randomize(flag=true) + @randomize = flag + end + + def self.randomize? + @randomize == true + end + + def self.repeat=(times) + @repeat = times + end + + def self.repeat + (@repeat || 1).times do + yield + end + end + + def self.shuffle(ary) + return if ary.empty? + + size = ary.size + size.times do |i| + r = rand(size - i - 1) + ary[i], ary[r] = ary[r], ary[i] + end + end + + # Records that an expectation has been encountered in an example. + def self.expectation + store :expectations, true + end + + # Returns true if an expectation has been encountered + def self.expectation? + retrieve :expectations + end + + # Resets the flag that an expectation has been encountered in an example. + def self.clear_expectations + store :expectations, false + end + + # Transforms a spec filename into a tags filename by applying each + # substitution pattern in :tags_pattern. The default patterns are: + # + # [%r(/spec/), '/spec/tags/'], [/_spec.rb$/, '_tags.txt'] + # + # which will perform the following transformation: + # + # path/to/spec/class/method_spec.rb => path/to/spec/tags/class/method_tags.txt + # + # See also +register_tags_patterns+. + def self.tags_file + patterns = retrieve(:tags_patterns) || + [[%r(spec/), 'spec/tags/'], [/_spec.rb$/, '_tags.txt']] + patterns.inject(retrieve(:file).dup) do |file, pattern| + file.gsub(*pattern) + end + end + + # Returns a list of tags matching any tag string in +keys+ based + # on the return value of keys.include?("tag_name") + def self.read_tags(keys) + tags = [] + file = tags_file + if File.exist? file + File.open(file, "r:utf-8") do |f| + f.each_line do |line| + line.chomp! + next if line.empty? + tag = SpecTag.new line + tags << tag if keys.include? tag.tag + end + end + end + tags + end + + def self.make_tag_dir(path) + parent = File.dirname(path) + return if File.exist? parent + begin + Dir.mkdir(parent) + rescue SystemCallError + make_tag_dir(parent) + Dir.mkdir(parent) + end + end + + # Writes each tag in +tags+ to the tag file. Overwrites the + # tag file if it exists. + def self.write_tags(tags) + file = tags_file + make_tag_dir(file) + File.open(file, "w:utf-8") do |f| + tags.each { |t| f.puts t } + end + end + + # Writes +tag+ to the tag file if it does not already exist. + # Returns +true+ if the tag is written, +false+ otherwise. + def self.write_tag(tag) + tags = read_tags([tag.tag]) + tags.each do |t| + if t.tag == tag.tag and t.description == tag.description + return false + end + end + + file = tags_file + make_tag_dir(file) + File.open(file, "a:utf-8") { |f| f.puts tag.to_s } + return true + end + + # Deletes +tag+ from the tag file if it exists. Returns +true+ + # if the tag is deleted, +false+ otherwise. Deletes the tag + # file if it is empty. + def self.delete_tag(tag) + deleted = false + desc = tag.escape(tag.description) + file = tags_file + if File.exist? file + lines = IO.readlines(file) + File.open(file, "w:utf-8") do |f| + lines.each do |line| + line = line.chomp + if line.start_with?(tag.tag) and line.end_with?(desc) + deleted = true + else + f.puts line unless line.empty? + end + end + end + File.delete file unless File.size? file + end + return deleted + end + + # Removes the tag file associated with a spec file. + def self.delete_tags + file = tags_file + File.delete file if File.exist? file + end +end diff --git a/spec/mspec/lib/mspec/runner/object.rb b/spec/mspec/lib/mspec/runner/object.rb new file mode 100644 index 0000000000..018e356149 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/object.rb @@ -0,0 +1,28 @@ +class Object + def before(at=:each, &block) + MSpec.current.before at, &block + end + + def after(at=:each, &block) + MSpec.current.after at, &block + end + + def describe(mod, msg=nil, options=nil, &block) + MSpec.describe mod, msg, &block + end + + def it(msg, &block) + MSpec.current.it msg, &block + end + + def it_should_behave_like(desc) + MSpec.current.it_should_behave_like desc + end + + # For ReadRuby compatiability + def doc(*a) + end + + alias_method :context, :describe + alias_method :specify, :it +end diff --git a/spec/mspec/lib/mspec/runner/shared.rb b/spec/mspec/lib/mspec/runner/shared.rb new file mode 100644 index 0000000000..336e35f6ac --- /dev/null +++ b/spec/mspec/lib/mspec/runner/shared.rb @@ -0,0 +1,12 @@ +require 'mspec/runner/mspec' + +class Object + def it_behaves_like(desc, meth, obj=nil) + send :before, :all do + @method = meth + @object = obj + end + + send :it_should_behave_like, desc.to_s + end +end diff --git a/spec/mspec/lib/mspec/runner/tag.rb b/spec/mspec/lib/mspec/runner/tag.rb new file mode 100644 index 0000000000..e2275ad3a6 --- /dev/null +++ b/spec/mspec/lib/mspec/runner/tag.rb @@ -0,0 +1,38 @@ +class SpecTag + attr_accessor :tag, :comment, :description + + def initialize(string=nil) + parse(string) if string + end + + def parse(string) + m = /^([^()#:]+)(\(([^)]+)?\))?:(.*)$/.match string + @tag, @comment, description = m.values_at(1, 3, 4) if m + @description = unescape description + end + + def unescape(str) + return unless str + if str[0] == ?" and str[-1] == ?" + str[1..-2].gsub('\n', "\n") + else + str + end + end + + def escape(str) + if str.include? "\n" + %["#{str.gsub("\n", '\n')}"] + else + str + end + end + + def to_s + "#{@tag}#{ "(#{@comment})" if @comment }:#{escape @description}" + end + + def ==(o) + @tag == o.tag and @comment == o.comment and @description == o.description + end +end diff --git a/spec/mspec/lib/mspec/utils/deprecate.rb b/spec/mspec/lib/mspec/utils/deprecate.rb new file mode 100644 index 0000000000..1db843b329 --- /dev/null +++ b/spec/mspec/lib/mspec/utils/deprecate.rb @@ -0,0 +1,6 @@ +module MSpec + def self.deprecate(what, replacement) + user_caller = caller.find { |line| !line.include?('lib/mspec') } + $stderr.puts "\n#{what} is deprecated, use #{replacement} instead.\nfrom #{user_caller}" + end +end diff --git a/spec/mspec/lib/mspec/utils/name_map.rb b/spec/mspec/lib/mspec/utils/name_map.rb new file mode 100644 index 0000000000..c1de081af0 --- /dev/null +++ b/spec/mspec/lib/mspec/utils/name_map.rb @@ -0,0 +1,128 @@ +class NameMap + MAP = { + '`' => 'backtick', + '+' => 'plus', + '-' => 'minus', + '+@' => 'uplus', + '-@' => 'uminus', + '*' => 'multiply', + '/' => 'divide', + '%' => 'modulo', + '<<' => {'Bignum' => 'left_shift', + 'Fixnum' => 'left_shift', + 'IO' => 'output', + :default => 'append' }, + '>>' => 'right_shift', + '<' => 'lt', + '<=' => 'lte', + '>' => 'gt', + '>=' => 'gte', + '=' => 'assignment', + '==' => 'equal_value', + '===' => 'case_compare', + '<=>' => 'comparison', + '[]' => 'element_reference', + '[]=' => 'element_set', + '**' => 'exponent', + '!' => 'not', + '~' => {'Bignum' => 'complement', + 'Fixnum' => 'complement', + 'Regexp' => 'match', + 'String' => 'match' }, + '!=' => 'not_equal', + '!~' => 'not_match', + '=~' => 'match', + '&' => {'Bignum' => 'bit_and', + 'Fixnum' => 'bit_and', + 'Array' => 'intersection', + 'TrueClass' => 'and', + 'FalseClass' => 'and', + 'NilClass' => 'and', + 'Set' => 'intersection' }, + '|' => {'Bignum' => 'bit_or', + 'Fixnum' => 'bit_or', + 'Array' => 'union', + 'TrueClass' => 'or', + 'FalseClass' => 'or', + 'NilClass' => 'or', + 'Set' => 'union' }, + '^' => {'Bignum' => 'bit_xor', + 'Fixnum' => 'bit_xor', + 'TrueClass' => 'xor', + 'FalseClass' => 'xor', + 'NilClass' => 'xor', + 'Set' => 'exclusion'}, + } + + EXCLUDED = %w[ + MSpecScript + MkSpec + MSpecOption + MSpecOptions + NameMap + SpecVersion + ] + + def initialize(filter=false) + @seen = {} + @filter = filter + end + + def exception?(name) + return false unless c = class_or_module(name) + c == Errno or c.ancestors.include? Exception + end + + def class_or_module(c) + const = Object.const_get(c, false) + filtered = @filter && EXCLUDED.include?(const.name) + return const if Module === const and !filtered + rescue NameError + end + + def namespace(mod, const) + return const.to_s if mod.nil? or %w[Object Class Module].include? mod + "#{mod}::#{const}" + end + + def map(hash, constants, mod=nil) + @seen = {} unless mod + + constants.each do |const| + name = namespace mod, const + m = class_or_module name + next unless m and !@seen[m] + @seen[m] = true + + ms = m.methods(false).map { |x| x.to_s } + hash["#{name}."] = ms.sort unless ms.empty? + + ms = m.public_instance_methods(false) + + m.protected_instance_methods(false) + ms.map! { |x| x.to_s } + hash["#{name}#"] = ms.sort unless ms.empty? + + map hash, m.constants(false), name + end + + hash + end + + def dir_name(c, base) + return File.join(base, 'exception') if exception? c + + c.split('::').inject(base) do |dir, name| + name.gsub!(/Class/, '') unless name == 'Class' + File.join dir, name.downcase + end + end + + def file_name(m, c) + if MAP.key?(m) + name = MAP[m].is_a?(Hash) ? MAP[m][c.split('::').last] || MAP[m][:default] : MAP[m] + else + name = m.gsub(/[?!=]/, '') + end + "#{name}_spec.rb" + end +end diff --git a/spec/mspec/lib/mspec/utils/options.rb b/spec/mspec/lib/mspec/utils/options.rb new file mode 100644 index 0000000000..122ef6e135 --- /dev/null +++ b/spec/mspec/lib/mspec/utils/options.rb @@ -0,0 +1,489 @@ +require 'mspec/version' + +MSPEC_HOME = File.expand_path('../../../..', __FILE__) + +class MSpecOption + attr_reader :short, :long, :arg, :description, :block + + def initialize(short, long, arg, description, block) + @short = short + @long = long + @arg = arg + @description = description + @block = block + end + + def arg? + @arg != nil + end + + def match?(opt) + opt == @short or opt == @long + end +end + +# MSpecOptions provides a parser for command line options. It also +# provides a composable set of options from which the runner scripts +# can select for their particular functionality. +class MSpecOptions + # Raised if incorrect or incomplete formats are passed to #on. + class OptionError < Exception; end + + # Raised if an unrecognized option is encountered. + class ParseError < Exception; end + + attr_accessor :config, :banner, :width, :options + + def initialize(banner="", width=30, config=nil) + @banner = banner + @config = config + @width = width + @options = [] + @doc = [] + @extra = [] + @on_extra = lambda { |x| + raise ParseError, "Unrecognized option: #{x}" if x[0] == ?- + @extra << x + } + + yield self if block_given? + end + + # Registers an option. Acceptable formats for arguments are: + # + # on "-a", "description" + # on "-a", "--abdc", "description" + # on "-a", "ARG", "description" + # on "--abdc", "ARG", "description" + # on "-a", "--abdc", "ARG", "description" + # + # If an block is passed, it will be invoked when the option is + # matched. Not passing a block is permitted, but nonsensical. + def on(*args, &block) + raise OptionError, "option and description are required" if args.size < 2 + + description = args.pop + short, long, argument = nil + args.each do |arg| + if arg[0] == ?- + if arg[1] == ?- + long = arg + else + short = arg + end + else + argument = arg + end + end + + add short, long, argument, description, block + end + + # Adds documentation text for an option and adds an +MSpecOption+ + # instance to the list of registered options. + def add(short, long, arg, description, block) + s = short ? short.dup : " " + s += (short ? ", " : " ") if long + doc " #{s}#{long} #{arg}".ljust(@width-1) + " #{description}" + @options << MSpecOption.new(short, long, arg, description, block) + end + + # Searches all registered options to find a match for +opt+. Returns + # +nil+ if no registered options match. + def match?(opt) + @options.find { |o| o.match? opt } + end + + # Processes an option. Calles the #on_extra block (or default) for + # unrecognized options. For registered options, possibly fetches an + # argument and invokes the option's block if it is not nil. + def process(argv, entry, opt, arg) + unless option = match?(opt) + @on_extra[entry] + else + if option.arg? + arg = argv.shift if arg.nil? + raise ParseError, "No argument provided for #{opt}" unless arg + option.block[arg] if option.block + else + option.block[] if option.block + end + end + option + end + + # Splits a string at +n+ characters into the +opt+ and the +rest+. + # The +arg+ is set to +nil+ if +rest+ is an empty string. + def split(str, n) + opt = str[0, n] + rest = str[n, str.size] + arg = rest == "" ? nil : rest + return opt, arg, rest + end + + # Parses an array of command line entries, calling blocks for + # registered options. + def parse(argv=ARGV) + argv = Array(argv).dup + + while entry = argv.shift + # collect everything that is not an option + if entry[0] != ?- or entry.size < 2 + @on_extra[entry] + next + end + + # this is a long option + if entry[1] == ?- + opt, arg = entry.split "=" + process argv, entry, opt, arg + next + end + + # disambiguate short option group from short option with argument + opt, arg, rest = split entry, 2 + + # process first option + option = process argv, entry, opt, arg + next unless option and !option.arg? + + # process the rest of the options + while rest.size > 0 + opt, arg, rest = split rest, 1 + opt = "-" + opt + option = process argv, opt, opt, arg + break if !option or option.arg? + end + end + + @extra + rescue ParseError => e + puts self + puts e + exit 1 + end + + # Adds a string of documentation text inline in the text generated + # from the options. See #on and #add. + def doc(str) + @doc << str + end + + # Convenience method for providing -v, --version options. + def version(version, &block) + show = block || lambda { puts "#{File.basename $0} #{version}"; exit } + on "-v", "--version", "Show version", &show + end + + # Convenience method for providing -h, --help options. + def help(&block) + help = block || lambda { puts self; exit 1 } + on "-h", "--help", "Show this message", &help + end + + # Stores a block that will be called with unrecognized options + def on_extra(&block) + @on_extra = block + end + + # Returns a string representation of the options and doc strings. + def to_s + @banner + "\n\n" + @doc.join("\n") + "\n" + end + + # The methods below provide groups of options that + # are composed by the particular runners to provide + # their functionality + + def configure(&block) + on("-B", "--config", "FILE", + "Load FILE containing configuration options", &block) + end + + def name + on("-n", "--name", "RUBY_NAME", + "Set the value of RUBY_NAME (used to determine the implementation)") do |n| + Object.const_set :RUBY_NAME, n + end + end + + def targets + on("-t", "--target", "TARGET", + "Implementation to run the specs, where TARGET is:") do |t| + case t + when 'r', 'ruby' + config[:target] = 'ruby' + when 'x', 'rubinius' + config[:target] = './bin/rbx' + when 'X', 'rbx' + config[:target] = 'rbx' + when 'j', 'jruby' + config[:target] = 'jruby' + when 'i','ironruby' + config[:target] = 'ir' + when 'm','maglev' + config[:target] = 'maglev-ruby' + when 't','topaz' + config[:target] = 'topaz' + when 'o','opal' + mspec_lib = File.expand_path('../../../', __FILE__) + config[:target] = "./bin/opal -syaml -sfileutils -rnodejs -rnodejs/require -rnodejs/yaml -rprocess -Derror -I#{mspec_lib} -I./lib/ -I. " + else + config[:target] = t + end + end + + doc "" + doc " r or ruby invokes ruby in PATH" + doc " x or rubinius invokes ./bin/rbx" + doc " X or rbx invokes rbx in PATH" + doc " j or jruby invokes jruby in PATH" + doc " i or ironruby invokes ir in PATH" + doc " m or maglev invokes maglev-ruby in PATH" + doc " t or topaz invokes topaz in PATH" + doc " o or opal invokes ./bin/opal with options" + doc " full path to EXE invokes EXE directly\n" + + on("-T", "--target-opt", "OPT", + "Pass OPT as a flag to the target implementation") do |t| + config[:flags] << t + end + on("-I", "--include", "DIR", + "Pass DIR through as the -I option to the target") do |d| + config[:loadpath] << "-I#{d}" + end + on("-r", "--require", "LIBRARY", + "Pass LIBRARY through as the -r option to the target") do |f| + config[:requires] << "-r#{f}" + end + end + + def formatters + on("-f", "--format", "FORMAT", + "Formatter for reporting, where FORMAT is one of:") do |o| + require 'mspec/runner/formatters' + case o + when 's', 'spec', 'specdoc' + config[:formatter] = SpecdocFormatter + when 'h', 'html' + config[:formatter] = HtmlFormatter + when 'd', 'dot', 'dotted' + config[:formatter] = DottedFormatter + when 'b', 'describe' + config[:formatter] = DescribeFormatter + when 'f', 'file' + config[:formatter] = FileFormatter + when 'u', 'unit', 'unitdiff' + config[:formatter] = UnitdiffFormatter + when 'm', 'summary' + config[:formatter] = SummaryFormatter + when 'a', '*', 'spin' + config[:formatter] = SpinnerFormatter + when 't', 'method' + config[:formatter] = MethodFormatter + when 'y', 'yaml' + config[:formatter] = YamlFormatter + when 'p', 'profile' + config[:formatter] = ProfileFormatter + when 'j', 'junit' + config[:formatter] = JUnitFormatter + else + abort "Unknown format: #{o}\n#{@parser}" unless File.exist?(o) + require File.expand_path(o) + if Object.const_defined?(:CUSTOM_MSPEC_FORMATTER) + config[:formatter] = CUSTOM_MSPEC_FORMATTER + else + abort "You must define CUSTOM_MSPEC_FORMATTER in your custom formatter file" + end + end + end + + doc "" + doc " s, spec, specdoc SpecdocFormatter" + doc " h, html, HtmlFormatter" + doc " d, dot, dotted DottedFormatter" + doc " f, file FileFormatter" + doc " u, unit, unitdiff UnitdiffFormatter" + doc " m, summary SummaryFormatter" + doc " a, *, spin SpinnerFormatter" + doc " t, method MethodFormatter" + doc " y, yaml YamlFormatter" + doc " p, profile ProfileFormatter" + doc " j, junit JUnitFormatter\n" + + on("-o", "--output", "FILE", + "Write formatter output to FILE") do |f| + config[:output] = f + end + end + + def filters + on("-e", "--example", "STR", + "Run examples with descriptions matching STR") do |o| + config[:includes] << o + end + on("-E", "--exclude", "STR", + "Exclude examples with descriptions matching STR") do |o| + config[:excludes] << o + end + on("-p", "--pattern", "PATTERN", + "Run examples with descriptions matching PATTERN") do |o| + config[:patterns] << Regexp.new(o) + end + on("-P", "--excl-pattern", "PATTERN", + "Exclude examples with descriptions matching PATTERN") do |o| + config[:xpatterns] << Regexp.new(o) + end + on("-g", "--tag", "TAG", + "Run examples with descriptions matching ones tagged with TAG") do |o| + config[:tags] << o + end + on("-G", "--excl-tag", "TAG", + "Exclude examples with descriptions matching ones tagged with TAG") do |o| + config[:xtags] << o + end + on("-w", "--profile", "FILE", + "Run examples for methods listed in the profile FILE") do |f| + config[:profiles] << f + end + on("-W", "--excl-profile", "FILE", + "Exclude examples for methods listed in the profile FILE") do |f| + config[:xprofiles] << f + end + end + + def chdir + on("-C", "--chdir", "DIR", + "Change the working directory to DIR before running specs") do |d| + Dir.chdir d + end + end + + def prefix + on("--prefix", "STR", "Prepend STR when resolving spec file names") do |p| + config[:prefix] = p + end + end + + def pretend + on("-Z", "--dry-run", + "Invoke formatters and other actions, but don't execute the specs") do + MSpec.register_mode :pretend + end + end + + def unguarded + on("--unguarded", "Turn off all guards") do + MSpec.register_mode :unguarded + end + on("--no-ruby_bug", "Turn off the ruby_bug guard") do + MSpec.register_mode :no_ruby_bug + end + end + + def randomize + on("-H", "--random", + "Randomize the list of spec files") do + MSpec.randomize + end + end + + def repeat + on("-R", "--repeat", "NUMBER", + "Repeatedly run an example NUMBER times") do |o| + MSpec.repeat = o.to_i + end + end + + def verbose + on("-V", "--verbose", "Output the name of each file processed") do + obj = Object.new + def obj.start + @width = MSpec.retrieve(:files).inject(0) { |max, f| f.size > max ? f.size : max } + end + def obj.load + file = MSpec.retrieve :file + print "\n#{file.ljust(@width)}" + end + MSpec.register :start, obj + MSpec.register :load, obj + end + + on("-m", "--marker", "MARKER", + "Output MARKER for each file processed") do |o| + obj = Object.new + obj.instance_variable_set :@marker, o + def obj.load + print @marker + end + MSpec.register :load, obj + end + end + + def interrupt + on("--int-spec", "Control-C interupts the current spec only") do + config[:abort] = false + end + end + + def verify + on("--report-on", "GUARD", "Report specs guarded by GUARD") do |g| + MSpec.register_mode :report_on + SpecGuard.guards << g.to_sym + end + on("-O", "--report", "Report guarded specs") do + MSpec.register_mode :report + end + on("-Y", "--verify", + "Verify that guarded specs pass and fail as expected") do + MSpec.register_mode :verify + end + end + + def action_filters + on("-K", "--action-tag", "TAG", + "Spec descriptions marked with TAG will trigger the specified action") do |o| + config[:atags] << o + end + on("-S", "--action-string", "STR", + "Spec descriptions matching STR will trigger the specified action") do |o| + config[:astrings] << o + end + end + + def actions + on("--spec-debug", + "Invoke the debugger when a spec description matches (see -K, -S)") do + config[:debugger] = true + end + end + + def debug + on("-d", "--debug", + "Set MSpec debugging flag for more verbose output") do + $MSPEC_DEBUG = true + end + end + + def all + # Generated with: + # puts File.read(__FILE__).scan(/def (\w+).*\n\s*on\(/) + configure {} + name + targets + formatters + filters + chdir + prefix + pretend + unguarded + randomize + repeat + verbose + interrupt + verify + action_filters + actions + debug + end +end diff --git a/spec/mspec/lib/mspec/utils/ruby_name.rb b/spec/mspec/lib/mspec/utils/ruby_name.rb new file mode 100644 index 0000000000..e381e387f6 --- /dev/null +++ b/spec/mspec/lib/mspec/utils/ruby_name.rb @@ -0,0 +1,8 @@ +unless Object.const_defined?(:RUBY_NAME) and RUBY_NAME + if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE + RUBY_NAME = RUBY_ENGINE + else + require 'rbconfig' + RUBY_NAME = RbConfig::CONFIG["RUBY_INSTALL_NAME"] || RbConfig::CONFIG["ruby_install_name"] + end +end diff --git a/spec/mspec/lib/mspec/utils/script.rb b/spec/mspec/lib/mspec/utils/script.rb new file mode 100644 index 0000000000..28be854a85 --- /dev/null +++ b/spec/mspec/lib/mspec/utils/script.rb @@ -0,0 +1,267 @@ +require 'mspec/guards/guard' +require 'mspec/utils/warnings' + +# MSpecScript provides a skeleton for all the MSpec runner scripts. + +class MSpecScript + # Returns the config object. Maintained at the class + # level to easily enable simple config files. See the + # class method +set+. + def self.config + @config ||= { + :path => ['.', 'spec'], + :config_ext => '.mspec' + } + end + + # Associates +value+ with +key+ in the config object. Enables + # simple config files of the form: + # + # class MSpecScript + # set :target, "ruby" + # set :files, ["one_spec.rb", "two_spec.rb"] + # end + def self.set(key, value) + config[key] = value + end + + # Gets the value of +key+ from the config object. Simplifies + # getting values in a config file: + # + # class MSpecScript + # set :a, 1 + # set :b, 2 + # set :c, get(:a) + get(:b) + # end + def self.get(key) + config[key] + end + + def initialize + config[:formatter] = nil + config[:includes] = [] + config[:excludes] = [] + config[:patterns] = [] + config[:xpatterns] = [] + config[:tags] = [] + config[:xtags] = [] + config[:profiles] = [] + config[:xprofiles] = [] + config[:atags] = [] + config[:astrings] = [] + config[:ltags] = [] + config[:abort] = true + @loaded = [] + end + + # Returns the config object maintained by the instance's class. + # See the class methods +set+ and +config+. + def config + MSpecScript.config + end + + # Returns +true+ if the file was located in +config[:path]+, + # possibly appending +config[:config_ext]. Returns +false+ + # otherwise. + def try_load(target) + names = [target] + unless target[-6..-1] == config[:config_ext] + names << target + config[:config_ext] + end + + names.each do |name| + config[:path].each do |dir| + file = File.expand_path name, dir + if @loaded.include?(file) + return true + elsif File.exist? file + value = Kernel.load(file) + @loaded << file + return value + end + end + end + + false + end + + def load(target) + try_load(target) or abort "Could not load config file #{target}" + end + + # Attempts to load a default config file. First tries to load + # 'default.mspec'. If that fails, attempts to load a config + # file name constructed from the value of RUBY_ENGINE and the + # first two numbers in RUBY_VERSION. For example, on MRI 1.8.6, + # the file name would be 'ruby.1.8.mspec'. + def load_default + try_load 'default.mspec' + + if Object.const_defined?(:RUBY_ENGINE) + engine = RUBY_ENGINE + else + engine = 'ruby' + end + try_load "#{engine}.#{SpecGuard.ruby_version}.mspec" + try_load "#{engine}.mspec" + end + + # Callback for enabling custom options. This version is a no-op. + # Provide an implementation specific version in a config file. + # Called by #options after the MSpec-provided options are added. + def custom_options(options) + options.doc " No custom options registered" + end + + # Registers all filters and actions. + def register + require 'mspec/runner/formatters/dotted' + require 'mspec/runner/formatters/spinner' + require 'mspec/runner/formatters/file' + require 'mspec/runner/filters' + + if config[:formatter].nil? + config[:formatter] = STDOUT.tty? ? SpinnerFormatter : @files.size < 50 ? DottedFormatter : FileFormatter + end + + if config[:formatter] + formatter = config[:formatter].new(config[:output]) + formatter.register + MSpec.store :formatter, formatter + end + + MatchFilter.new(:include, *config[:includes]).register unless config[:includes].empty? + MatchFilter.new(:exclude, *config[:excludes]).register unless config[:excludes].empty? + RegexpFilter.new(:include, *config[:patterns]).register unless config[:patterns].empty? + RegexpFilter.new(:exclude, *config[:xpatterns]).register unless config[:xpatterns].empty? + TagFilter.new(:include, *config[:tags]).register unless config[:tags].empty? + TagFilter.new(:exclude, *config[:xtags]).register unless config[:xtags].empty? + ProfileFilter.new(:include, *config[:profiles]).register unless config[:profiles].empty? + ProfileFilter.new(:exclude, *config[:xprofiles]).register unless config[:xprofiles].empty? + + DebugAction.new(config[:atags], config[:astrings]).register if config[:debugger] + + custom_register + end + + # Callback for enabling custom actions, etc. This version is a + # no-op. Provide an implementation specific version in a config + # file. Called by #register. + def custom_register + end + + # Sets up signal handlers. Only a handler for SIGINT is + # registered currently. + def signals + if config[:abort] + Signal.trap "INT" do + MSpec.actions :abort + puts "\nProcess aborted!" + exit! 1 + end + end + end + + # Attempts to resolve +partial+ as a file or directory name in the + # following order: + # + # 1. +partial+ + # 2. +partial+ + "_spec.rb" + # 3. File.join(config[:prefix], partial) + # 4. File.join(config[:prefix], partial + "_spec.rb") + # + # If it is a file name, returns the name as an entry in an array. + # If it is a directory, returns all *_spec.rb files in the + # directory and subdirectories. + # + # If unable to resolve +partial+, +Kernel.abort+ is called. + def entries(partial) + file = partial + "_spec.rb" + patterns = [partial, file] + if config[:prefix] + patterns << File.join(config[:prefix], partial) + patterns << File.join(config[:prefix], file) + end + + patterns.each do |pattern| + expanded = File.expand_path(pattern) + if File.file?(expanded) + return [expanded] + elsif File.directory?(expanded) + return Dir["#{expanded}/**/*_spec.rb"].sort + end + end + + abort "Could not find spec file #{partial}" + end + + # Resolves each entry in +list+ to a set of files. + # + # If the entry has a leading '^' character, the list of files + # is subtracted from the list of files accumulated to that point. + # + # If the entry has a leading ':' character, the corresponding + # key is looked up in the config object and the entries in the + # value retrieved are processed through #entries. + def files(list) + list.inject([]) do |files, item| + case item[0] + when ?^ + files -= entries(item[1..-1]) + when ?: + key = item[1..-1].to_sym + files += files(Array(config[key])) + else + files += entries(item) + end + files + end + end + + def files_from_patterns(patterns) + unless $0.end_with?("_spec.rb") + if patterns.empty? + patterns = config[:files] + end + if patterns.empty? and File.directory? "./spec" + patterns = ["spec/"] + end + if patterns.empty? + puts "No files specified." + exit 1 + end + end + files patterns + end + + def cores + require 'etc' + Etc.nprocessors + end + + def setup_env + ENV['MSPEC_RUNNER'] = '1' + + unless ENV['RUBY_EXE'] + ENV['RUBY_EXE'] = config[:target] if config[:target] + end + + unless ENV['RUBY_FLAGS'] + ENV['RUBY_FLAGS'] = config[:flags].join(" ") if config[:flags] + end + end + + # Instantiates an instance and calls the series of methods to + # invoke the script. + def self.main + script = new + script.load_default + script.try_load '~/.mspecrc' + script.options + script.signals + script.register + script.setup_env + require 'mspec' + script.run + end +end diff --git a/spec/mspec/lib/mspec/utils/version.rb b/spec/mspec/lib/mspec/utils/version.rb new file mode 100644 index 0000000000..787a76b053 --- /dev/null +++ b/spec/mspec/lib/mspec/utils/version.rb @@ -0,0 +1,52 @@ +class SpecVersion + # If beginning implementations have a problem with this include, we can + # manually implement the relational operators that are needed. + include Comparable + + # SpecVersion handles comparison correctly for the context by filling in + # missing version parts according to the value of +ceil+. If +ceil+ is + # +false+, 0 digits fill in missing version parts. If +ceil+ is +true+, 9 + # digits fill in missing parts. (See e.g. VersionGuard and BugGuard.) + def initialize(version, ceil = false) + @version = version + @ceil = ceil + @integer = nil + end + + def to_s + @version + end + + def to_str + to_s + end + + # Converts a string representation of a version major.minor.tiny + # to an integer representation so that comparisons can be made. For example, + # "2.2.10" < "2.2.2" would be false if compared as strings. + def to_i + unless @integer + major, minor, tiny = @version.split "." + if @ceil + tiny = 99 unless tiny + end + parts = [major, minor, tiny].map { |x| x.to_i } + @integer = ("1%02d%02d%02d" % parts).to_i + end + @integer + end + + def to_int + to_i + end + + def <=>(other) + if other.respond_to? :to_int + other = Integer other + else + other = SpecVersion.new(String(other)).to_i + end + + self.to_i <=> other + end +end diff --git a/spec/mspec/lib/mspec/utils/warnings.rb b/spec/mspec/lib/mspec/utils/warnings.rb new file mode 100644 index 0000000000..74c7f88a52 --- /dev/null +++ b/spec/mspec/lib/mspec/utils/warnings.rb @@ -0,0 +1,32 @@ +require 'mspec/guards/version' + +if RUBY_ENGINE == "ruby" and RUBY_VERSION >= "2.4.0" + ruby_version_is "2.4"..."2.5" do + # Kernel#warn does not delegate to Warning.warn in 2.4 + module Kernel + def warn(*messages) + return if $VERBOSE == nil or messages.empty? + msg = messages.join("\n") + msg += "\n" unless msg.end_with?("\n") + Warning.warn(msg) + end + private :warn + end + end + + def Warning.warn(message) + case message + when /constant ::(Fixnum|Bignum) is deprecated/ + when /\/(argf|io|stringio)\/.+(ARGF|IO)#(lines|chars|bytes|codepoints) is deprecated/ + when /Thread\.exclusive is deprecated.+\n.+thread\/exclusive_spec\.rb/ + when /hash\/shared\/index\.rb:\d+: warning: Hash#index is deprecated; use Hash#key/ + when /env\/shared\/key\.rb:\d+: warning: ENV\.index is deprecated; use ENV\.key/ + when /exponent(_spec)?\.rb:\d+: warning: in a\*\*b, b may be too big/ + when /enumerator\/(new|initialize_spec)\.rb:\d+: warning: Enumerator\.new without a block is deprecated/ + else + $stderr.write message + end + end +else + $VERBOSE = nil unless ENV['OUTPUT_WARNINGS'] +end diff --git a/spec/mspec/lib/mspec/version.rb b/spec/mspec/lib/mspec/version.rb new file mode 100644 index 0000000000..9126f5366e --- /dev/null +++ b/spec/mspec/lib/mspec/version.rb @@ -0,0 +1,5 @@ +require 'mspec/utils/version' + +module MSpec + VERSION = SpecVersion.new "1.8.0" +end diff --git a/spec/mspec/mspec.gemspec b/spec/mspec/mspec.gemspec new file mode 100644 index 0000000000..428067dfd3 --- /dev/null +++ b/spec/mspec/mspec.gemspec @@ -0,0 +1,40 @@ +# -*- encoding: utf-8 -*- +$:.unshift File.expand_path('../lib', __FILE__) +require 'mspec/version' + +Gem::Specification.new do |gem| + gem.name = "mspec" + gem.version = MSpec::VERSION.to_s + gem.authors = ["Brian Shirai"] + gem.email = ["bshirai@engineyard.com"] + gem.homepage = "http://rubyspec.org" + + gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) unless File.extname(f) == ".bat" }.compact + gem.files = `git ls-files`.split("\n") + gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + gem.require_paths = ["lib"] + gem.description = <<-EOD +MSpec is a specialized framework for RubySpec. + EOD + gem.summary = <<-EOS +MSpec is a specialized framework that is syntax-compatible +with RSpec for basic things like describe, it blocks and +before, after actions. + +MSpec contains additional features that assist in writing +the RubySpecs used by multiple Ruby implementations. Also, +MSpec attempts to use the simplest Ruby language features +so that beginning Ruby implementations can run it. + EOS + gem.has_rdoc = true + gem.extra_rdoc_files = %w[ README.md LICENSE ] + gem.rubygems_version = %q{1.3.5} + gem.rubyforge_project = 'http://rubyforge.org/projects/mspec' + + gem.rdoc_options << '--title' << 'MSpec Gem' << + '--main' << 'README.md' << + '--line-numbers' + + gem.add_development_dependency "rake", "~> 10.0" + gem.add_development_dependency "rspec", "~> 2.14.1" +end diff --git a/spec/mspec/spec/commands/fixtures/four.txt b/spec/mspec/spec/commands/fixtures/four.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/mspec/spec/commands/fixtures/level2/three_spec.rb b/spec/mspec/spec/commands/fixtures/level2/three_spec.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/mspec/spec/commands/fixtures/one_spec.rb b/spec/mspec/spec/commands/fixtures/one_spec.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/mspec/spec/commands/fixtures/three.rb b/spec/mspec/spec/commands/fixtures/three.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/mspec/spec/commands/fixtures/two_spec.rb b/spec/mspec/spec/commands/fixtures/two_spec.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/mspec/spec/commands/mkspec_spec.rb b/spec/mspec/spec/commands/mkspec_spec.rb new file mode 100644 index 0000000000..ab3410af50 --- /dev/null +++ b/spec/mspec/spec/commands/mkspec_spec.rb @@ -0,0 +1,363 @@ +require 'spec_helper' +require 'mspec/commands/mkspec' + + +describe "The -c, --constant CONSTANT option" do + before :each do + @options = MSpecOptions.new + MSpecOptions.stub(:new).and_return(@options) + @script = MkSpec.new + @config = @script.config + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("-c", "--constant", "CONSTANT", + an_instance_of(String)) + @script.options [] + end + + it "adds CONSTANT to the list of constants" do + ["-c", "--constant"].each do |opt| + @config[:constants] = [] + @script.options [opt, "Object"] + @config[:constants].should include("Object") + end + end +end + +describe "The -b, --base DIR option" do + before :each do + @options = MSpecOptions.new + MSpecOptions.stub(:new).and_return(@options) + @script = MkSpec.new + @config = @script.config + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("-b", "--base", "DIR", + an_instance_of(String)) + @script.options + end + + it "sets the base directory relative to which the spec directories are created" do + ["-b", "--base"].each do |opt| + @config[:base] = nil + @script.options [opt, "superspec"] + @config[:base].should == File.expand_path("superspec") + end + end +end + +describe "The -r, --require LIBRARY option" do + before :each do + @options = MSpecOptions.new + MSpecOptions.stub(:new).and_return(@options) + @script = MkSpec.new + @config = @script.config + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("-r", "--require", "LIBRARY", + an_instance_of(String)) + @script.options + end + + it "adds CONSTANT to the list of constants" do + ["-r", "--require"].each do |opt| + @config[:requires] = [] + @script.options [opt, "libspec"] + @config[:requires].should include("libspec") + end + end +end + +describe "The -V, --version-guard VERSION option" do + before :each do + @options = MSpecOptions.new + MSpecOptions.stub(:new).and_return(@options) + @script = MkSpec.new + @config = @script.config + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("-V", "--version-guard", "VERSION", + an_instance_of(String)) + @script.options + end + + it "sets the version for the ruby_version_is guards to VERSION" do + ["-r", "--require"].each do |opt| + @config[:requires] = [] + @script.options [opt, "libspec"] + @config[:requires].should include("libspec") + end + end +end + +describe MkSpec, "#options" do + before :each do + @options = MSpecOptions.new + MSpecOptions.stub(:new).and_return(@options) + @script = MkSpec.new + end + + it "parses the command line options" do + @options.should_receive(:parse).with(["--this", "and", "--that"]) + @script.options ["--this", "and", "--that"] + end + + it "parses ARGV unless passed other options" do + @options.should_receive(:parse).with(ARGV) + @script.options + end + + it "prints help and exits if passed an unrecognized option" do + @options.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String)) + @options.stub(:puts) + @options.stub(:exit) + @script.options "--iunknown" + end +end + +describe MkSpec, "#create_directory" do + before :each do + @script = MkSpec.new + @script.config[:base] = "spec" + end + + it "prints a warning if a file with the directory name exists" do + File.should_receive(:exist?).and_return(true) + File.should_receive(:directory?).and_return(false) + FileUtils.should_not_receive(:mkdir_p) + @script.should_receive(:puts).with("spec/class already exists and is not a directory.") + @script.create_directory("Class").should == nil + end + + it "does nothing if the directory already exists" do + File.should_receive(:exist?).and_return(true) + File.should_receive(:directory?).and_return(true) + FileUtils.should_not_receive(:mkdir_p) + @script.create_directory("Class").should == "spec/class" + end + + it "creates the directory if it does not exist" do + File.should_receive(:exist?).and_return(false) + @script.should_receive(:mkdir_p).with("spec/class") + @script.create_directory("Class").should == "spec/class" + end + + it "creates the directory for a namespaced module if it does not exist" do + File.should_receive(:exist?).and_return(false) + @script.should_receive(:mkdir_p).with("spec/struct/tms") + @script.create_directory("Struct::Tms").should == "spec/struct/tms" + end +end + +describe MkSpec, "#write_requires" do + before :each do + @script = MkSpec.new + @script.config[:base] = "spec" + + @file = double("file") + File.stub(:open).and_yield(@file) + end + + it "writes the spec_helper require line" do + @file.should_receive(:puts).with("require File.expand_path('../../../../spec_helper', __FILE__)") + @script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb") + end + + it "writes require lines for each library specified on the command line" do + @file.stub(:puts) + @file.should_receive(:puts).with("require File.expand_path('../../../../spec_helper', __FILE__)") + @file.should_receive(:puts).with("require 'complex'") + @script.config[:requires] << 'complex' + @script.write_requires("spec/core/tcejbo", "spec/core/tcejbo/inspect_spec.rb") + end +end + +describe MkSpec, "#write_spec" do + before :each do + @file = IOStub.new + File.stub(:open).and_yield(@file) + + @script = MkSpec.new + @script.stub(:puts) + + @response = double("system command response") + @response.stub(:include?).and_return(false) + @script.stub(:`).and_return(@response) + end + + it "checks if specs exist for the method if the spec file exists" do + name = Regexp.escape(@script.ruby) + @script.should_receive(:`).with( + %r"#{name} #{MSPEC_HOME}/bin/mspec-run --dry-run --unguarded -fs -e 'Object#inspect' spec/core/tcejbo/inspect_spec.rb") + @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true) + end + + it "checks for the method name in the spec file output" do + @response.should_receive(:include?).with("Array#[]=") + @script.write_spec("spec/core/yarra/element_set_spec.rb", "Array#[]=", true) + end + + it "returns nil if the spec file exists and contains a spec for the method" do + @response.stub(:include?).and_return(true) + @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true).should == nil + end + + it "does not print the spec file name if it exists and contains a spec for the method" do + @response.stub(:include?).and_return(true) + @script.should_not_receive(:puts) + @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true) + end + + it "prints the spec file name if a template spec is written" do + @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb") + @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true) + end + + it "writes a template spec to the file if the spec file does not exist" do + @file.should_receive(:puts).twice + @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb") + @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", false) + end + + it "writes a template spec to the file if it exists but contains no spec for the method" do + @response.should_receive(:include?).and_return(false) + @file.should_receive(:puts).twice + @script.should_receive(:puts).with("spec/core/tcejbo/inspect_spec.rb") + @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true) + end + + it "writes a template spec" do + @script.write_spec("spec/core/tcejbo/inspect_spec.rb", "Object#inspect", true) + @file.should == < ["run"]}) + @script.should_receive(:create_file).with("spec/mkspec", "MkSpec", "run", "MkSpec#run") + @script.run + end +end + +describe MkSpec, ".main" do + before :each do + @script = double("MkSpec").as_null_object + MkSpec.stub(:new).and_return(@script) + end + + it "sets MSPEC_RUNNER = '1' in the environment" do + ENV["MSPEC_RUNNER"] = "0" + MkSpec.main + ENV["MSPEC_RUNNER"].should == "1" + end + + it "creates an instance of MSpecScript" do + MkSpec.should_receive(:new).and_return(@script) + MkSpec.main + end + + it "calls the #options method on the script" do + @script.should_receive(:options) + MkSpec.main + end + + it "calls the #run method on the script" do + @script.should_receive(:run) + MkSpec.main + end +end diff --git a/spec/mspec/spec/commands/mspec_ci_spec.rb b/spec/mspec/spec/commands/mspec_ci_spec.rb new file mode 100644 index 0000000000..1e8949b0d3 --- /dev/null +++ b/spec/mspec/spec/commands/mspec_ci_spec.rb @@ -0,0 +1,155 @@ +require 'spec_helper' +require 'mspec/runner/mspec' +require 'mspec/runner/filters/tag' +require 'mspec/commands/mspec-ci' + +describe MSpecCI, "#options" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + + @script = MSpecCI.new + @script.stub(:config).and_return(@config) + @script.stub(:files).and_return([]) + end + + it "enables the chdir option" do + @options.should_receive(:chdir) + @script.options + end + + it "enables the prefix option" do + @options.should_receive(:prefix) + @script.options + end + + it "enables the config option" do + @options.should_receive(:configure) + @script.options + end + + it "provides a custom action (block) to the config option" do + @script.should_receive(:load).with("cfg.mspec") + @script.options ["-B", "cfg.mspec"] + end + + it "enables the name option" do + @options.should_receive(:name) + @script.options + end + + it "enables the dry run option" do + @options.should_receive(:pretend) + @script.options + end + + it "enables the unguarded option" do + @options.should_receive(:unguarded) + @script.options + end + + it "enables the interrupt single specs option" do + @options.should_receive(:interrupt) + @script.options + end + + it "enables the formatter options" do + @options.should_receive(:formatters) + @script.options + end + + it "enables the verbose option" do + @options.should_receive(:verbose) + @script.options + end + + it "enables the action options" do + @options.should_receive(:actions) + @script.options + end + + it "enables the action filter options" do + @options.should_receive(:action_filters) + @script.options + end + + it "enables the version option" do + @options.should_receive(:version) + @script.options + end + + it "enables the help option" do + @options.should_receive(:help) + @script.options + end + + it "calls #custom_options" do + @script.should_receive(:custom_options).with(@options) + @script.options + end +end + +describe MSpecCI, "#run" do + before :each do + MSpec.stub(:process) + + @filter = double("TagFilter") + TagFilter.stub(:new).and_return(@filter) + @filter.stub(:register) + + @tags = ["fails", "critical", "unstable", "incomplete", "unsupported"] + + @config = { :ci_files => ["one", "two"] } + @script = MSpecCI.new + @script.stub(:exit) + @script.stub(:config).and_return(@config) + @script.stub(:files).and_return(["one", "two"]) + @script.options + end + + it "registers the tags patterns" do + @config[:tags_patterns] = [/spec/, "tags"] + MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"]) + @script.run + end + + it "registers the files to process" do + MSpec.should_receive(:register_files).with(["one", "two"]) + @script.run + end + + it "registers a tag filter for 'fails', 'unstable', 'incomplete', 'critical', 'unsupported'" do + filter = double("fails filter") + TagFilter.should_receive(:new).with(:exclude, *@tags).and_return(filter) + filter.should_receive(:register) + @script.run + end + + it "registers an additional exclude tag specified by :ci_xtags" do + @config[:ci_xtags] = "windows" + filter = double("fails filter") + TagFilter.should_receive(:new).with(:exclude, *(@tags + ["windows"])).and_return(filter) + filter.should_receive(:register) + @script.run + end + + it "registers additional exclude tags specified by a :ci_xtags array" do + @config[:ci_xtags] = ["windows", "windoze"] + filter = double("fails filter") + TagFilter.should_receive(:new).with(:exclude, + *(@tags + ["windows", "windoze"])).and_return(filter) + filter.should_receive(:register) + @script.run + end + + it "processes the files" do + MSpec.should_receive(:process) + @script.run + end + + it "exits with the exit code registered with MSpec" do + MSpec.stub(:exit_code).and_return(7) + @script.should_receive(:exit).with(7) + @script.run + end +end diff --git a/spec/mspec/spec/commands/mspec_run_spec.rb b/spec/mspec/spec/commands/mspec_run_spec.rb new file mode 100644 index 0000000000..4d350cdc02 --- /dev/null +++ b/spec/mspec/spec/commands/mspec_run_spec.rb @@ -0,0 +1,185 @@ +require 'spec_helper' +require 'mspec/runner/mspec' +require 'mspec/commands/mspec-run' + +one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb' +two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb' + +describe MSpecRun, ".new" do + before :each do + @script = MSpecRun.new + end + + it "sets config[:files] to an empty list" do + @script.config[:files].should == [] + end +end + +describe MSpecRun, "#options" do + before :each do + @stdout, $stdout = $stdout, IOStub.new + + @argv = [one_spec, two_spec] + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + + @script = MSpecRun.new + @script.stub(:config).and_return(@config) + end + + after :each do + $stdout = @stdout + end + + it "enables the filter options" do + @options.should_receive(:filters) + @script.options @argv + end + + it "enables the chdir option" do + @options.should_receive(:chdir) + @script.options @argv + end + + it "enables the prefix option" do + @options.should_receive(:prefix) + @script.options @argv + end + + it "enables the configure option" do + @options.should_receive(:configure) + @script.options @argv + end + + it "provides a custom action (block) to the config option" do + @script.should_receive(:load).with("cfg.mspec") + @script.options ["-B", "cfg.mspec", one_spec] + end + + it "enables the name option" do + @options.should_receive(:name) + @script.options @argv + end + + it "enables the randomize option to runs specs in random order" do + @options.should_receive(:randomize) + @script.options @argv + end + + it "enables the dry run option" do + @options.should_receive(:pretend) + @script.options @argv + end + + it "enables the unguarded option" do + @options.should_receive(:unguarded) + @script.options @argv + end + + it "enables the interrupt single specs option" do + @options.should_receive(:interrupt) + @script.options @argv + end + + it "enables the formatter options" do + @options.should_receive(:formatters) + @script.options @argv + end + + it "enables the verbose option" do + @options.should_receive(:verbose) + @script.options @argv + end + + it "enables the verify options" do + @options.should_receive(:verify) + @script.options @argv + end + + it "enables the action options" do + @options.should_receive(:actions) + @script.options @argv + end + + it "enables the action filter options" do + @options.should_receive(:action_filters) + @script.options @argv + end + + it "enables the version option" do + @options.should_receive(:version) + @script.options @argv + end + + it "enables the help option" do + @options.should_receive(:help) + @script.options @argv + end + + it "exits if there are no files to process and './spec' is not a directory" do + File.should_receive(:directory?).with("./spec").and_return(false) + @options.should_receive(:parse).and_return([]) + @script.should_receive(:exit) + @script.options + $stdout.should include "No files specified" + end + + it "process 'spec/' if it is a directory and no files were specified" do + File.should_receive(:directory?).with("./spec").and_return(true) + @options.should_receive(:parse).and_return([]) + @script.should_receive(:files).with(["spec/"]) + @script.options + end + + it "calls #custom_options" do + @script.should_receive(:custom_options).with(@options) + @script.options @argv + end +end + +describe MSpecRun, "#run" do + before :each do + @script = MSpecRun.new + @script.stub(:exit) + @spec_dir = File.expand_path(File.dirname(__FILE__)+"/fixtures") + @file_patterns = [ + @spec_dir+"/level2", + @spec_dir+"/one_spec.rb", + @spec_dir+"/two_spec.rb"] + @files = [ + @spec_dir+"/level2/three_spec.rb", + @spec_dir+"/one_spec.rb", + @spec_dir+"/two_spec.rb"] + @script.options @file_patterns + MSpec.stub :process + end + + it "registers the tags patterns" do + @script.config[:tags_patterns] = [/spec/, "tags"] + MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"]) + @script.run + end + + it "registers the files to process" do + MSpec.should_receive(:register_files).with(@files) + @script.run + end + + it "uses config[:files] if no files are given on the command line" do + @script.config[:files] = @file_patterns + MSpec.should_receive(:register_files).with(@files) + @script.options [] + @script.run + end + + it "processes the files" do + MSpec.should_receive(:process) + @script.run + end + + it "exits with the exit code registered with MSpec" do + MSpec.stub(:exit_code).and_return(7) + @script.should_receive(:exit).with(7) + @script.run + end +end diff --git a/spec/mspec/spec/commands/mspec_spec.rb b/spec/mspec/spec/commands/mspec_spec.rb new file mode 100644 index 0000000000..8b8b8fcc41 --- /dev/null +++ b/spec/mspec/spec/commands/mspec_spec.rb @@ -0,0 +1,215 @@ +require 'spec_helper' +require 'yaml' +require 'mspec/commands/mspec' + +describe MSpecMain, "#options" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + + @script = MSpecMain.new + @script.stub(:config).and_return(@config) + @script.stub(:load) + end + + it "enables the configure option" do + @options.should_receive(:configure) + @script.options + end + + it "provides a custom action (block) to the config option" do + @script.options ["-B", "config"] + @config[:options].should include("-B", "config") + end + + it "loads the file specified by the config option" do + @script.should_receive(:load).with("config") + @script.options ["-B", "config"] + end + + it "enables the target options" do + @options.should_receive(:targets) + @script.options + end + + it "sets config[:options] to all argv entries that are not registered options" do + @options.on "-X", "--exclude", "ARG", "description" + @script.options [".", "-G", "fail", "-X", "ARG", "--list", "unstable", "some/file.rb"] + @config[:options].should == [".", "-G", "fail", "--list", "unstable", "some/file.rb"] + end + + it "calls #custom_options" do + @script.should_receive(:custom_options).with(@options) + @script.options + end +end + +describe MSpecMain, "#run" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + @script = MSpecMain.new + @script.stub(:config).and_return(@config) + @script.stub(:exec) + @err = $stderr + $stderr = IOStub.new + end + + after :each do + $stderr = @err + end + + it "uses exec to invoke the runner script" do + @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run") + @script.options [] + @script.run + end + + it "shows the command line on stderr" do + @script.should_receive(:exec).with("ruby", "#{MSPEC_HOME}/bin/mspec-run") + @script.options [] + @script.run + $stderr.to_s.should == "$ ruby #{Dir.pwd}/bin/mspec-run\n" + end + + it "adds config[:launch] to the exec options" do + @script.should_receive(:exec).with("ruby", + "-Xlaunch.option", "#{MSPEC_HOME}/bin/mspec-run") + @config[:launch] << "-Xlaunch.option" + @script.options [] + @script.run + $stderr.to_s.should == "$ ruby -Xlaunch.option #{Dir.pwd}/bin/mspec-run\n" + end + + it "calls #multi_exec if the command is 'ci' and the multi option is passed" do + @script.should_receive(:multi_exec).and_return do |argv| + argv.should == ["ruby", "#{MSPEC_HOME}/bin/mspec-ci", "-fy"] + end + @script.options ["ci", "-j"] + lambda do + @script.run + end.should raise_error(SystemExit) + end +end + +describe "The --warnings option" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + @script = MSpecMain.new + @script.stub(:config).and_return(@config) + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("--warnings", an_instance_of(String)) + @script.options + end + + it "sets flags to -w" do + @config[:flags] = [] + @script.options ["--warnings"] + @config[:flags].should include("-w") + end + + it "set OUTPUT_WARNINGS = '1' in the environment" do + ENV['OUTPUT_WARNINGS'] = '0' + @script.options ["--warnings"] + ENV['OUTPUT_WARNINGS'].should == '1' + end +end + +describe "The -j, --multi option" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + @script = MSpecMain.new + @script.stub(:config).and_return(@config) + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("-j", "--multi", an_instance_of(String)) + @script.options + end + + it "sets the multiple process option" do + ["-j", "--multi"].each do |opt| + @config[:multi] = nil + @script.options [opt] + @config[:multi].should == true + end + end + + it "sets the formatter to YamlFormatter" do + ["-j", "--multi"].each do |opt| + @config[:options] = [] + @script.options [opt] + @config[:options].should include("-fy") + end + end +end + +describe "The -h, --help option" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + @script = MSpecMain.new + @script.stub(:config).and_return(@config) + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("-h", "--help", an_instance_of(String)) + @script.options + end + + it "passes the option to the subscript" do + ["-h", "--help"].each do |opt| + @config[:options] = [] + @script.options ["ci", opt] + @config[:options].sort.should == ["-h"] + end + end + + it "prints help and exits" do + @script.should_receive(:puts).twice + @script.should_receive(:exit).twice + ["-h", "--help"].each do |opt| + @script.options [opt] + end + end +end + +describe "The -v, --version option" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + @script = MSpecMain.new + @script.stub(:config).and_return(@config) + end + + it "is enabled by #options" do + @options.stub(:on) + @options.should_receive(:on).with("-v", "--version", an_instance_of(String)) + @script.options + end + + it "passes the option to the subscripts" do + ["-v", "--version"].each do |opt| + @config[:options] = [] + @script.options ["ci", opt] + @config[:options].sort.should == ["-v"] + end + end + + it "prints the version and exits if no subscript is invoked" do + @config[:command] = nil + File.stub(:basename).and_return("mspec") + @script.should_receive(:puts).twice.with("mspec #{MSpec::VERSION}") + @script.should_receive(:exit).twice + ["-v", "--version"].each do |opt| + @script.options [opt] + end + end +end diff --git a/spec/mspec/spec/commands/mspec_tag_spec.rb b/spec/mspec/spec/commands/mspec_tag_spec.rb new file mode 100644 index 0000000000..3c2e94db52 --- /dev/null +++ b/spec/mspec/spec/commands/mspec_tag_spec.rb @@ -0,0 +1,419 @@ +require 'spec_helper' +require 'mspec/runner/mspec' +require 'mspec/commands/mspec-tag' +require 'mspec/runner/actions/tag' +require 'mspec/runner/actions/taglist' +require 'mspec/runner/actions/tagpurge' + +one_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/one_spec.rb' +two_spec = File.expand_path(File.dirname(__FILE__)) + '/fixtures/two_spec.rb' + +describe MSpecTag, ".new" do + before :each do + @script = MSpecTag.new + end + + it "sets config[:ltags] to an empty list" do + @script.config[:ltags].should == [] + end + + it "sets config[:tagger] to :add" do + @script.config[:tagger] = :add + end + + it "sets config[:tag] to 'fails:'" do + @script.config[:tag] = 'fails:' + end + + it "sets config[:outcome] to :fail" do + @script.config[:outcome] = :fail + end +end + +describe MSpecTag, "#options" do + before :each do + @stdout, $stdout = $stdout, IOStub.new + + @argv = [one_spec, two_spec] + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + + @script = MSpecTag.new + @script.stub(:config).and_return(@config) + end + + after :each do + $stdout = @stdout + end + + it "enables the filter options" do + @options.should_receive(:filters) + @script.options @argv + end + + it "enables the configure option" do + @options.should_receive(:configure) + @script.options @argv + end + + it "provides a custom action (block) to the config option" do + @script.should_receive(:load).with("cfg.mspec") + @script.options ["-B", "cfg.mspec", one_spec] + end + + it "enables the name option" do + @options.should_receive(:name) + @script.options @argv + end + + it "enables the dry run option" do + @options.should_receive(:pretend) + @script.options @argv + end + + it "enables the unguarded option" do + @options.should_receive(:unguarded) + @script.options @argv + end + + it "enables the interrupt single specs option" do + @options.should_receive(:interrupt) + @script.options @argv + end + + it "enables the formatter options" do + @options.should_receive(:formatters) + @script.options @argv + end + + it "enables the verbose option" do + @options.should_receive(:verbose) + @script.options @argv + end + + it "enables the version option" do + @options.should_receive(:version) + @script.options @argv + end + + it "enables the help option" do + @options.should_receive(:help) + @script.options @argv + end + + it "calls #custom_options" do + @script.should_receive(:custom_options).with(@options) + @script.options @argv + end + + it "exits if there are no files to process" do + @options.should_receive(:parse).and_return([]) + @script.should_receive(:exit) + @script.options + $stdout.should include "No files specified" + end +end + +describe MSpecTag, "options" do + before :each do + @options, @config = new_option + MSpecOptions.stub(:new).and_return(@options) + @script = MSpecTag.new + @script.stub(:config).and_return(@config) + end + + describe "-N, --add TAG" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("-N", "--add", "TAG", an_instance_of(String)) + @script.options [one_spec] + end + + it "sets the mode to :add and sets the tag to TAG" do + ["-N", "--add"].each do |opt| + @config[:tagger] = nil + @config[:tag] = nil + @script.options [opt, "taggit", one_spec] + @config[:tagger].should == :add + @config[:tag].should == "taggit:" + end + end + end + + describe "-R, --del TAG" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("-R", "--del", "TAG", + an_instance_of(String)) + @script.options [one_spec] + end + + it "it sets the mode to :del, the tag to TAG, and the outcome to :pass" do + ["-R", "--del"].each do |opt| + @config[:tagger] = nil + @config[:tag] = nil + @config[:outcome] = nil + @script.options [opt, "taggit", one_spec] + @config[:tagger].should == :del + @config[:tag].should == "taggit:" + @config[:outcome].should == :pass + end + end + end + + describe "-Q, --pass" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("-Q", "--pass", an_instance_of(String)) + @script.options [one_spec] + end + + it "sets the outcome to :pass" do + ["-Q", "--pass"].each do |opt| + @config[:outcome] = nil + @script.options [opt, one_spec] + @config[:outcome].should == :pass + end + end + end + + describe "-F, --fail" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("-F", "--fail", an_instance_of(String)) + @script.options [one_spec] + end + + it "sets the outcome to :fail" do + ["-F", "--fail"].each do |opt| + @config[:outcome] = nil + @script.options [opt, one_spec] + @config[:outcome].should == :fail + end + end + end + + describe "-L, --all" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("-L", "--all", an_instance_of(String)) + @script.options [one_spec] + end + + it "sets the outcome to :all" do + ["-L", "--all"].each do |opt| + @config[:outcome] = nil + @script.options [opt, one_spec] + @config[:outcome].should == :all + end + end + end + + describe "--list TAG" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("--list", "TAG", an_instance_of(String)) + @script.options [one_spec] + end + + it "sets the mode to :list" do + @config[:tagger] = nil + @script.options ["--list", "TAG", one_spec] + @config[:tagger].should == :list + end + + it "sets ltags to include TAG" do + @config[:tag] = nil + @script.options ["--list", "TAG", one_spec] + @config[:ltags].should == ["TAG"] + end + end + + describe "--list-all" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("--list-all", an_instance_of(String)) + @script.options [one_spec] + end + + it "sets the mode to :list_all" do + @config[:tagger] = nil + @script.options ["--list-all", one_spec] + @config[:tagger].should == :list_all + end + end + + describe "--purge" do + it "is enabled with #options" do + @options.stub(:on) + @options.should_receive(:on).with("--purge", an_instance_of(String)) + @script.options [one_spec] + end + + it "sets the mode to :purge" do + @config[:tagger] = nil + @script.options ["--purge", one_spec] + @config[:tagger].should == :purge + end + end +end + +describe MSpecTag, "#run" do + before :each do + MSpec.stub(:process) + + options = double("MSpecOptions").as_null_object + options.stub(:parse).and_return(["one", "two"]) + MSpecOptions.stub(:new).and_return(options) + + @config = { } + @script = MSpecTag.new + @script.stub(:exit) + @script.stub(:config).and_return(@config) + @script.stub(:files).and_return(["one", "two"]) + @script.options + end + + it "registers the tags patterns" do + @config[:tags_patterns] = [/spec/, "tags"] + MSpec.should_receive(:register_tags_patterns).with([/spec/, "tags"]) + @script.run + end + + it "registers the files to process" do + MSpec.should_receive(:register_files).with(["one", "two"]) + @script.run + end + + it "processes the files" do + MSpec.should_receive(:process) + @script.run + end + + it "exits with the exit code registered with MSpec" do + MSpec.stub(:exit_code).and_return(7) + @script.should_receive(:exit).with(7) + @script.run + end +end + +describe MSpecTag, "#register" do + before :each do + @script = MSpecTag.new + @config = @script.config + @config[:tag] = "fake:" + @config[:atags] = [] + @config[:astrings] = [] + @config[:ltags] = ["fails", "unstable"] + + @script.stub(:files).and_return([]) + @script.options "fake" + + @t = double("TagAction") + @t.stub(:register) + + @tl = double("TagListAction") + @tl.stub(:register) + end + + it "raises an ArgumentError if no recognized action is given" do + @config[:tagger] = :totally_whack + lambda { @script.register }.should raise_error(ArgumentError) + end + + describe "when config[:tagger] is the default (:add)" do + before :each do + @config[:formatter] = false + end + + it "creates a TagAction" do + TagAction.should_receive(:new).and_return(@t) + @script.register + end + + it "creates a TagAction if config[:tagger] is :del" do + @config[:tagger] = :del + @config[:outcome] = :pass + TagAction.should_receive(:new).with(:del, :pass, "fake", nil, [], []).and_return(@t) + @script.register + end + + it "calls #register on the TagAction instance" do + TagAction.should_receive(:new).and_return(@t) + @t.should_receive(:register) + @script.register + end + end + + describe "when config[:tagger] is :list" do + before :each do + TagListAction.should_receive(:new).with(@config[:ltags]).and_return(@tl) + @config[:tagger] = :list + end + + it "creates a TagListAction" do + @tl.should_receive(:register) + @script.register + end + + it "registers MSpec pretend mode" do + MSpec.should_receive(:register_mode).with(:pretend) + @script.register + end + + it "sets config[:formatter] to false" do + @script.register + @config[:formatter].should be_false + end + end + + describe "when config[:tagger] is :list_all" do + before :each do + TagListAction.should_receive(:new).with(nil).and_return(@tl) + @config[:tagger] = :list_all + end + + it "creates a TagListAction" do + @tl.should_receive(:register) + @script.register + end + + it "registers MSpec pretend mode" do + MSpec.should_receive(:register_mode).with(:pretend) + @script.register + end + + it "sets config[:formatter] to false" do + @script.register + @config[:formatter].should be_false + end + end + + describe "when config[:tagger] is :purge" do + before :each do + TagPurgeAction.should_receive(:new).and_return(@tl) + MSpec.stub(:register_mode) + @config[:tagger] = :purge + end + + it "creates a TagPurgeAction" do + @tl.should_receive(:register) + @script.register + end + + it "registers MSpec in pretend mode" do + MSpec.should_receive(:register_mode).with(:pretend) + @script.register + end + + it "registers MSpec in unguarded mode" do + MSpec.should_receive(:register_mode).with(:unguarded) + @script.register + end + + it "sets config[:formatter] to false" do + @script.register + @config[:formatter].should be_false + end + end +end diff --git a/spec/mspec/spec/expectations/expectations_spec.rb b/spec/mspec/spec/expectations/expectations_spec.rb new file mode 100644 index 0000000000..fea692f3e3 --- /dev/null +++ b/spec/mspec/spec/expectations/expectations_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' + +describe SpecExpectationNotMetError do + it "is a subclass of StandardError" do + SpecExpectationNotMetError.ancestors.should include(StandardError) + end +end + +describe SpecExpectationNotFoundError do + it "is a subclass of StandardError" do + SpecExpectationNotFoundError.ancestors.should include(StandardError) + end +end + +describe SpecExpectationNotFoundError, "#message" do + it "returns 'No behavior expectation was found in the example'" do + m = SpecExpectationNotFoundError.new.message + m.should == "No behavior expectation was found in the example" + end +end + +describe SpecExpectation, "#fail_with" do + it "raises an SpecExpectationNotMetError" do + lambda { + SpecExpectation.fail_with "expected this", "to equal that" + }.should raise_error(SpecExpectationNotMetError, "expected this to equal that") + end +end diff --git a/spec/mspec/spec/expectations/should.rb b/spec/mspec/spec/expectations/should.rb new file mode 100644 index 0000000000..8404ff044e --- /dev/null +++ b/spec/mspec/spec/expectations/should.rb @@ -0,0 +1,72 @@ +$: << File.dirname(__FILE__) + '/../../lib' +require 'mspec' +require 'mspec/utils/script' + +# The purpose of these specs is to confirm that the #should +# and #should_not methods are functioning appropriately. We +# use a separate spec file that is invoked from the MSpec +# specs but is run by MSpec. This avoids conflicting with +# RSpec's #should and #should_not methods. + +class ShouldSpecsMonitor + def initialize + @called = 0 + end + + def expectation(state) + @called += 1 + end + + def finish + puts "I was called #{@called} times" + end +end + +# Simplistic runner +formatter = DottedFormatter.new +formatter.register + +monitor = ShouldSpecsMonitor.new +MSpec.register :expectation, monitor +MSpec.register :finish, monitor + +at_exit { MSpec.actions :finish } + +MSpec.actions :start + +# Specs +describe "MSpec expectation method #should" do + it "accepts a matcher" do + :sym.should be_kind_of(Symbol) + end + + it "causes a failue to be recorded" do + 1.should == 2 + end + + it "registers that an expectation has been encountered" do + # an empty example block causes an exception because + # no expectation was encountered + end + + it "invokes the MSpec :expectation actions" do + 1.should == 1 + end +end + +describe "MSpec expectation method #should_not" do + it "accepts a matcher" do + "sym".should_not be_kind_of(Symbol) + end + + it "causes a failure to be recorded" do + 1.should_not == 1 + end + + it "registers that an expectation has been encountered" do + end + + it "invokes the MSpec :expectation actions" do + 1.should_not == 2 + end +end diff --git a/spec/mspec/spec/expectations/should_spec.rb b/spec/mspec/spec/expectations/should_spec.rb new file mode 100644 index 0000000000..3258caf13c --- /dev/null +++ b/spec/mspec/spec/expectations/should_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' +require 'rbconfig' + +describe "MSpec" do + before :all do + path = RbConfig::CONFIG['bindir'] + exe = RbConfig::CONFIG['ruby_install_name'] + file = File.dirname(__FILE__) + '/should.rb' + @out = `#{path}/#{exe} #{file}` + end + + describe "#should" do + it "records failures" do + @out.should include <<-EOS +1) +MSpec expectation method #should causes a failue to be recorded FAILED +Expected 1 + to equal 2 +EOS + end + + it "raises exceptions for examples with no expectations" do + @out.should include <<-EOS +2) +MSpec expectation method #should registers that an expectation has been encountered FAILED +No behavior expectation was found in the example +EOS + end + end + + describe "#should_not" do + it "records failures" do + @out.should include <<-EOS +3) +MSpec expectation method #should_not causes a failure to be recorded FAILED +Expected 1 + not to equal 1 +EOS + end + + it "raises exceptions for examples with no expectations" do + @out.should include <<-EOS +4) +MSpec expectation method #should_not registers that an expectation has been encountered FAILED +No behavior expectation was found in the example +EOS + end + end + + it "prints status information" do + @out.should include ".FF..FF." + end + + it "prints out a summary" do + @out.should include "0 files, 8 examples, 6 expectations, 4 failures, 0 errors" + end + + it "records expectations" do + @out.should include "I was called 6 times" + end +end diff --git a/spec/mspec/spec/fixtures/a_spec.rb b/spec/mspec/spec/fixtures/a_spec.rb new file mode 100644 index 0000000000..17a7e8b664 --- /dev/null +++ b/spec/mspec/spec/fixtures/a_spec.rb @@ -0,0 +1,15 @@ +unless defined?(RSpec) + describe "Foo#bar" do + it "passes" do + 1.should == 1 + end + + it "errors" do + 1.should == 2 + end + + it "fails" do + raise "failure" + end + end +end diff --git a/spec/mspec/spec/fixtures/b_spec.rb b/spec/mspec/spec/fixtures/b_spec.rb new file mode 100644 index 0000000000..f1f63317cb --- /dev/null +++ b/spec/mspec/spec/fixtures/b_spec.rb @@ -0,0 +1,7 @@ +unless defined?(RSpec) + describe "Bar#baz" do + it "works" do + 1.should == 1 + end + end +end diff --git a/spec/mspec/spec/fixtures/config.mspec b/spec/mspec/spec/fixtures/config.mspec new file mode 100644 index 0000000000..4a069e2eb0 --- /dev/null +++ b/spec/mspec/spec/fixtures/config.mspec @@ -0,0 +1,10 @@ +class MSpecScript + set :target, 'ruby' + + set :backtrace_filter, /lib\/mspec\// + + set :tags_patterns, [ + [%r(spec/fixtures/), 'spec/fixtures/tags/'], + [/_spec.rb$/, '_tags.txt'] + ] +end diff --git a/spec/mspec/spec/fixtures/my_ruby b/spec/mspec/spec/fixtures/my_ruby new file mode 100755 index 0000000000..4d552f27fb --- /dev/null +++ b/spec/mspec/spec/fixtures/my_ruby @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +echo $RUBY_EXE +ruby "$@" diff --git a/spec/mspec/spec/fixtures/print_interpreter_spec.rb b/spec/mspec/spec/fixtures/print_interpreter_spec.rb new file mode 100644 index 0000000000..e1c514dc87 --- /dev/null +++ b/spec/mspec/spec/fixtures/print_interpreter_spec.rb @@ -0,0 +1,4 @@ +unless defined?(RSpec) + puts ENV["RUBY_EXE"] + puts ruby_cmd("nil").split.first +end diff --git a/spec/mspec/spec/fixtures/tagging_spec.rb b/spec/mspec/spec/fixtures/tagging_spec.rb new file mode 100644 index 0000000000..0097fd1808 --- /dev/null +++ b/spec/mspec/spec/fixtures/tagging_spec.rb @@ -0,0 +1,16 @@ +# encoding: utf-8 +unless defined?(RSpec) + describe "Tag#me" do + it "passes" do + 1.should == 1 + end + + it "errors" do + 1.should == 2 + end + + it "érròrs in unicode" do + 1.should == 2 + end + end +end diff --git a/spec/mspec/spec/guards/block_device_spec.rb b/spec/mspec/spec/guards/block_device_spec.rb new file mode 100644 index 0000000000..3b437b6d74 --- /dev/null +++ b/spec/mspec/spec/guards/block_device_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' +require 'mspec/guards' + +describe Object, "#with_block_device" do + before :each do + ScratchPad.clear + + @guard = BlockDeviceGuard.new + BlockDeviceGuard.stub(:new).and_return(@guard) + end + + platform_is_not :freebsd, :windows do + it "yields if block device is available" do + @guard.should_receive(:`).and_return("block devices") + with_block_device { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "does not yield if block device is not available" do + @guard.should_receive(:`).and_return(nil) + with_block_device { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + end + + platform_is :freebsd, :windows do + it "does not yield, since platform does not support block devices" do + @guard.should_not_receive(:`) + with_block_device { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + end + + it "sets the name of the guard to :with_block_device" do + with_block_device { } + @guard.name.should == :with_block_device + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(true) + @guard.should_receive(:unregister) + lambda do + with_block_device { raise Exception } + end.should raise_error(Exception) + end +end diff --git a/spec/mspec/spec/guards/bug_spec.rb b/spec/mspec/spec/guards/bug_spec.rb new file mode 100644 index 0000000000..93c549041a --- /dev/null +++ b/spec/mspec/spec/guards/bug_spec.rb @@ -0,0 +1,151 @@ +require 'spec_helper' +require 'mspec/guards' + +describe BugGuard, "#match? when #implementation? is 'ruby'" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + end + + after :all do + $VERBOSE = @verbose + end + + before :each do + hide_deprecation_warnings + stub_const "VersionGuard::FULL_RUBY_VERSION", SpecVersion.new('1.8.6') + @ruby_name = Object.const_get :RUBY_NAME + Object.const_set :RUBY_NAME, 'ruby' + end + + after :each do + Object.const_set :RUBY_NAME, @ruby_name + end + + it "returns false when version argument is less than RUBY_VERSION" do + BugGuard.new("#1", "1.8.5").match?.should == false + end + + it "returns true when version argument is equal to RUBY_VERSION" do + BugGuard.new("#1", "1.8.6").match?.should == true + end + + it "returns true when version argument is greater than RUBY_VERSION" do + BugGuard.new("#1", "1.8.7").match?.should == true + end + + it "returns true when version argument implicitly includes RUBY_VERSION" do + BugGuard.new("#1", "1.8").match?.should == true + BugGuard.new("#1", "1.8.6").match?.should == true + end + + it "returns true when the argument range includes RUBY_VERSION" do + BugGuard.new("#1", '1.8.5'..'1.8.7').match?.should == true + BugGuard.new("#1", '1.8'..'1.9').match?.should == true + BugGuard.new("#1", '1.8'...'1.9').match?.should == true + BugGuard.new("#1", '1.8'..'1.8.6').match?.should == true + BugGuard.new("#1", '1.8.5'..'1.8.6').match?.should == true + BugGuard.new("#1", ''...'1.8.7').match?.should == true + end + + it "returns false when the argument range does not include RUBY_VERSION" do + BugGuard.new("#1", '1.8.7'..'1.8.9').match?.should == false + BugGuard.new("#1", '1.8.4'..'1.8.5').match?.should == false + BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false + BugGuard.new("#1", '1.8.5'...'1.8.6').match?.should == false + BugGuard.new("#1", ''...'1.8.6').match?.should == false + end + + it "returns false when MSpec.mode?(:no_ruby_bug) is true" do + MSpec.should_receive(:mode?).with(:no_ruby_bug).twice.and_return(:true) + BugGuard.new("#1", "1.8.5").match?.should == false + BugGuard.new("#1", "1.8").match?.should == false + end +end + +describe BugGuard, "#match? when #implementation? is not 'ruby'" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + end + + after :all do + $VERBOSE = @verbose + end + + before :each do + hide_deprecation_warnings + @ruby_version = Object.const_get :RUBY_VERSION + @ruby_name = Object.const_get :RUBY_NAME + + Object.const_set :RUBY_VERSION, '1.8.6' + Object.const_set :RUBY_NAME, 'jruby' + end + + after :each do + Object.const_set :RUBY_VERSION, @ruby_version + Object.const_set :RUBY_NAME, @ruby_name + end + + it "returns false when version argument is less than RUBY_VERSION" do + BugGuard.new("#1", "1.8").match?.should == false + BugGuard.new("#1", "1.8.6").match?.should == false + end + + it "returns false when version argument is equal to RUBY_VERSION" do + BugGuard.new("#1", "1.8.6").match?.should == false + end + + it "returns false when version argument is greater than RUBY_VERSION" do + BugGuard.new("#1", "1.8.7").match?.should == false + end + + it "returns false no matter if the argument range includes RUBY_VERSION" do + BugGuard.new("#1", '1.8'...'1.9').match?.should == false + BugGuard.new("#1", '1.8.5'...'1.8.7').match?.should == false + BugGuard.new("#1", '1.8.4'...'1.8.6').match?.should == false + end + + it "returns false when MSpec.mode?(:no_ruby_bug) is true" do + MSpec.stub(:mode?).and_return(:true) + BugGuard.new("#1", "1.8.6").match?.should == false + end +end + +describe Object, "#ruby_bug" do + before :each do + hide_deprecation_warnings + @guard = BugGuard.new "#1234", "x.x.x" + BugGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "yields when #match? returns false" do + @guard.stub(:match?).and_return(false) + ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "does not yield when #match? returns true" do + @guard.stub(:match?).and_return(true) + ruby_bug("#1234", "1.8.6") { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "requires a bug tracker number and a version number" do + lambda { ruby_bug { } }.should raise_error(ArgumentError) + lambda { ruby_bug("#1234") { } }.should raise_error(ArgumentError) + end + + it "sets the name of the guard to :ruby_bug" do + ruby_bug("#1234", "1.8.6") { } + @guard.name.should == :ruby_bug + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:unregister) + lambda do + ruby_bug("", "") { raise Exception } + end.should raise_error(Exception) + end +end diff --git a/spec/mspec/spec/guards/conflict_spec.rb b/spec/mspec/spec/guards/conflict_spec.rb new file mode 100644 index 0000000000..e06a2809ee --- /dev/null +++ b/spec/mspec/spec/guards/conflict_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' +require 'mspec/guards' + +describe Object, "#conflicts_with" do + before :each do + ScratchPad.clear + end + + it "does not yield if Object.constants includes any of the arguments" do + Object.stub(:constants).and_return(["SomeClass", "OtherClass"]) + conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "does not yield if Object.constants (as Symbols) includes any of the arguments" do + Object.stub(:constants).and_return([:SomeClass, :OtherClass]) + conflicts_with(:SomeClass, :AClass, :BClass) { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "yields if Object.constants does not include any of the arguments" do + Object.stub(:constants).and_return(["SomeClass", "OtherClass"]) + conflicts_with(:AClass, :BClass) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "yields if Object.constants (as Symbols) does not include any of the arguments" do + Object.stub(:constants).and_return([:SomeClass, :OtherClass]) + conflicts_with(:AClass, :BClass) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end +end + +describe Object, "#conflicts_with" do + before :each do + @guard = ConflictsGuard.new + ConflictsGuard.stub(:new).and_return(@guard) + end + + it "sets the name of the guard to :conflicts_with" do + conflicts_with(:AClass, :BClass) { } + @guard.name.should == :conflicts_with + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:unregister) + lambda do + conflicts_with(:AClass, :BClass) { raise Exception } + end.should raise_error(Exception) + end +end diff --git a/spec/mspec/spec/guards/endian_spec.rb b/spec/mspec/spec/guards/endian_spec.rb new file mode 100644 index 0000000000..5b40c203ab --- /dev/null +++ b/spec/mspec/spec/guards/endian_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' +require 'mspec/guards' + +describe Object, "#big_endian" do + before :each do + @guard = BigEndianGuard.new + BigEndianGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "yields on big-endian platforms" do + @guard.stub(:pattern).and_return([?\001]) + big_endian { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "does not yield on little-endian platforms" do + @guard.stub(:pattern).and_return([?\000]) + big_endian { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "sets the name of the guard to :big_endian" do + big_endian { } + @guard.name.should == :big_endian + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.stub(:pattern).and_return([?\001]) + @guard.should_receive(:unregister) + lambda do + big_endian { raise Exception } + end.should raise_error(Exception) + end +end + +describe Object, "#little_endian" do + before :each do + @guard = BigEndianGuard.new + BigEndianGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "yields on little-endian platforms" do + @guard.stub(:pattern).and_return([?\000]) + little_endian { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "does not yield on big-endian platforms" do + @guard.stub(:pattern).and_return([?\001]) + little_endian { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end +end diff --git a/spec/mspec/spec/guards/feature_spec.rb b/spec/mspec/spec/guards/feature_spec.rb new file mode 100644 index 0000000000..d14e5f8e67 --- /dev/null +++ b/spec/mspec/spec/guards/feature_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' +require 'mspec/guards' + +describe FeatureGuard, ".enabled?" do + it "returns true if the feature is enabled" do + MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true) + FeatureGuard.enabled?(:encoding).should be_true + end + + it "returns false if the feature is not enabled" do + MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false) + FeatureGuard.enabled?(:encoding).should be_false + end + + it "returns true if all the features are enabled" do + MSpec.should_receive(:feature_enabled?).with(:one).and_return(true) + MSpec.should_receive(:feature_enabled?).with(:two).and_return(true) + FeatureGuard.enabled?(:one, :two).should be_true + end + + it "returns false if any of the features are not enabled" do + MSpec.should_receive(:feature_enabled?).with(:one).and_return(true) + MSpec.should_receive(:feature_enabled?).with(:two).and_return(false) + FeatureGuard.enabled?(:one, :two).should be_false + end +end + +describe Object, "#with_feature" do + before :each do + ScratchPad.clear + + @guard = FeatureGuard.new :encoding + FeatureGuard.stub(:new).and_return(@guard) + end + + it "sets the name of the guard to :with_feature" do + with_feature(:encoding) { } + @guard.name.should == :with_feature + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(true) + @guard.should_receive(:unregister) + lambda do + with_feature { raise Exception } + end.should raise_error(Exception) + end +end + +describe Object, "#with_feature" do + before :each do + ScratchPad.clear + end + + it "yields if the feature is enabled" do + MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(true) + with_feature(:encoding) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "yields if all the features are enabled" do + MSpec.should_receive(:feature_enabled?).with(:one).and_return(true) + MSpec.should_receive(:feature_enabled?).with(:two).and_return(true) + with_feature(:one, :two) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "does not yield if the feature is not enabled" do + MSpec.should_receive(:feature_enabled?).with(:encoding).and_return(false) + with_feature(:encoding) { ScratchPad.record :yield } + ScratchPad.recorded.should be_nil + end + + it "does not yield if any of the features are not enabled" do + MSpec.should_receive(:feature_enabled?).with(:one).and_return(true) + MSpec.should_receive(:feature_enabled?).with(:two).and_return(false) + with_feature(:one, :two) { ScratchPad.record :yield } + ScratchPad.recorded.should be_nil + end +end diff --git a/spec/mspec/spec/guards/guard_spec.rb b/spec/mspec/spec/guards/guard_spec.rb new file mode 100644 index 0000000000..ca7dba6455 --- /dev/null +++ b/spec/mspec/spec/guards/guard_spec.rb @@ -0,0 +1,180 @@ +require 'spec_helper' +require 'mspec/guards' +require 'rbconfig' + +describe SpecGuard, ".ruby_version" do + before :each do + @ruby_version = Object.const_get :RUBY_VERSION + Object.const_set :RUBY_VERSION, "8.2.3" + end + + after :each do + Object.const_set :RUBY_VERSION, @ruby_version + end + + it "returns the full version for :full" do + SpecGuard.ruby_version(:full).should == "8.2.3" + end + + it "returns major.minor.tiny for :tiny" do + SpecGuard.ruby_version(:tiny).should == "8.2.3" + end + + it "returns major.minor.tiny for :teeny" do + SpecGuard.ruby_version(:tiny).should == "8.2.3" + end + + it "returns major.minor for :minor" do + SpecGuard.ruby_version(:minor).should == "8.2" + end + + it "defaults to :minor" do + SpecGuard.ruby_version.should == "8.2" + end + + it "returns major for :major" do + SpecGuard.ruby_version(:major).should == "8" + end +end + +describe SpecGuard, "#yield?" do + before :each do + MSpec.clear_modes + @guard = SpecGuard.new + @guard.stub(:match?).and_return(false) + end + + after :each do + MSpec.unregister :add, @guard + MSpec.clear_modes + SpecGuard.clear_guards + end + + it "returns true if MSpec.mode?(:unguarded) is true" do + MSpec.register_mode :unguarded + @guard.yield?.should == true + end + + it "returns true if MSpec.mode?(:verify) is true" do + MSpec.register_mode :verify + @guard.yield?.should == true + end + + it "returns true if MSpec.mode?(:verify) is true regardless of invert being true" do + MSpec.register_mode :verify + @guard.yield?(true).should == true + end + + it "returns true if MSpec.mode?(:report) is true" do + MSpec.register_mode :report + @guard.yield?.should == true + end + + it "returns true if MSpec.mode?(:report) is true regardless of invert being true" do + MSpec.register_mode :report + @guard.yield?(true).should == true + end + + it "returns true if MSpec.mode?(:report_on) is true and SpecGuards.guards contains the named guard" do + MSpec.register_mode :report_on + SpecGuard.guards << :guard_name + @guard.yield?.should == false + @guard.name = :guard_name + @guard.yield?.should == true + end + + it "returns #match? if neither report nor verify mode are true" do + @guard.stub(:match?).and_return(false) + @guard.yield?.should == false + @guard.stub(:match?).and_return(true) + @guard.yield?.should == true + end + + it "returns #match? if invert is true and neither report nor verify mode are true" do + @guard.stub(:match?).and_return(false) + @guard.yield?(true).should == true + @guard.stub(:match?).and_return(true) + @guard.yield?(true).should == false + end +end + +describe SpecGuard, "#match?" do + before :each do + @guard = SpecGuard.new + end + + it "must be implemented in subclasses" do + lambda { + @guard.match? + }.should raise_error("must be implemented by the subclass") + end +end + +describe SpecGuard, "#unregister" do + before :each do + MSpec.stub(:unregister) + @guard = SpecGuard.new + end + + it "unregisters from MSpec :add actions" do + MSpec.should_receive(:unregister).with(:add, @guard) + @guard.unregister + end +end + +describe SpecGuard, "#record" do + after :each do + SpecGuard.clear + end + + it "saves the name of the guarded spec under the name of the guard" do + guard = SpecGuard.new "a", "1.8"..."1.9" + guard.name = :named_guard + guard.record "SomeClass#action returns true" + SpecGuard.report.should == { + 'named_guard a, 1.8...1.9' => ["SomeClass#action returns true"] + } + end +end + +describe SpecGuard, ".guards" do + it "returns an Array" do + SpecGuard.guards.should be_kind_of(Array) + end +end + +describe SpecGuard, ".clear_guards" do + it "resets the array to empty" do + SpecGuard.guards << :guard + SpecGuard.guards.should == [:guard] + SpecGuard.clear_guards + SpecGuard.guards.should == [] + end +end + +describe SpecGuard, ".finish" do + before :each do + $stdout = @out = IOStub.new + end + + after :each do + $stdout = STDOUT + SpecGuard.clear + end + + it "prints the descriptions of the guarded specs" do + guard = SpecGuard.new "a", "1.8"..."1.9" + guard.name = :named_guard + guard.record "SomeClass#action returns true" + guard.record "SomeClass#reverse returns false" + SpecGuard.finish + $stdout.should == %[ + +2 specs omitted by guard: named_guard a, 1.8...1.9: + +SomeClass#action returns true +SomeClass#reverse returns false + +] + end +end diff --git a/spec/mspec/spec/guards/platform_spec.rb b/spec/mspec/spec/guards/platform_spec.rb new file mode 100644 index 0000000000..578773e476 --- /dev/null +++ b/spec/mspec/spec/guards/platform_spec.rb @@ -0,0 +1,331 @@ +require 'spec_helper' +require 'mspec/guards' + +describe Object, "#platform_is" do + before :each do + @guard = PlatformGuard.new :dummy + PlatformGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "does not yield when #os? returns false" do + PlatformGuard.stub(:os?).and_return(false) + platform_is(:ruby) { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "yields when #os? returns true" do + PlatformGuard.stub(:os?).and_return(true) + platform_is(:solarce) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "sets the name of the guard to :platform_is" do + platform_is(:solarce) { } + @guard.name.should == :platform_is + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(true) + @guard.should_receive(:unregister) + lambda do + platform_is(:solarce) { raise Exception } + end.should raise_error(Exception) + end +end + +describe Object, "#platform_is_not" do + before :each do + @guard = PlatformGuard.new :dummy + PlatformGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "does not yield when #os? returns true" do + PlatformGuard.stub(:os?).and_return(true) + platform_is_not(:ruby) { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "yields when #os? returns false" do + PlatformGuard.stub(:os?).and_return(false) + platform_is_not(:solarce) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "sets the name of the guard to :platform_is_not" do + platform_is_not(:solarce) { } + @guard.name.should == :platform_is_not + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(false) + @guard.should_receive(:unregister) + lambda do + platform_is_not(:solarce) { raise Exception } + end.should raise_error(Exception) + end +end + +describe Object, "#platform_is :wordsize => SIZE_SPEC" do + before :each do + @guard = PlatformGuard.new :darwin, :wordsize => 32 + PlatformGuard.stub(:os?).and_return(true) + PlatformGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "yields when #wordsize? returns true" do + PlatformGuard.stub(:wordsize?).and_return(true) + platform_is(:wordsize => 32) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "doesn not yield when #wordsize? returns false" do + PlatformGuard.stub(:wordsize?).and_return(false) + platform_is(:wordsize => 32) { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end +end + +describe Object, "#platform_is_not :wordsize => SIZE_SPEC" do + before :each do + @guard = PlatformGuard.new :darwin, :wordsize => 32 + PlatformGuard.stub(:os?).and_return(true) + PlatformGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "yields when #wordsize? returns false" do + PlatformGuard.stub(:wordsize?).and_return(false) + platform_is_not(:wordsize => 32) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "doesn not yield when #wordsize? returns true" do + PlatformGuard.stub(:wordsize?).and_return(true) + platform_is_not(:wordsize => 32) { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end +end + +describe PlatformGuard, ".implementation?" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + end + + after :all do + $VERBOSE = @verbose + end + + before :each do + @ruby_name = Object.const_get :RUBY_NAME + end + + after :each do + Object.const_set :RUBY_NAME, @ruby_name + end + + it "returns true if passed :ruby and RUBY_NAME == 'ruby'" do + Object.const_set :RUBY_NAME, 'ruby' + PlatformGuard.implementation?(:ruby).should == true + end + + it "returns true if passed :rubinius and RUBY_NAME == 'rbx'" do + Object.const_set :RUBY_NAME, 'rbx' + PlatformGuard.implementation?(:rubinius).should == true + end + + it "returns true if passed :jruby and RUBY_NAME == 'jruby'" do + Object.const_set :RUBY_NAME, 'jruby' + PlatformGuard.implementation?(:jruby).should == true + end + + it "returns true if passed :ironruby and RUBY_NAME == 'ironruby'" do + Object.const_set :RUBY_NAME, 'ironruby' + PlatformGuard.implementation?(:ironruby).should == true + end + + it "returns true if passed :maglev and RUBY_NAME == 'maglev'" do + Object.const_set :RUBY_NAME, 'maglev' + PlatformGuard.implementation?(:maglev).should == true + end + + it "returns true if passed :topaz and RUBY_NAME == 'topaz'" do + Object.const_set :RUBY_NAME, 'topaz' + PlatformGuard.implementation?(:topaz).should == true + end + + it "returns true if passed :ruby and RUBY_NAME matches /^ruby/" do + Object.const_set :RUBY_NAME, 'ruby' + PlatformGuard.implementation?(:ruby).should == true + + Object.const_set :RUBY_NAME, 'ruby1.8' + PlatformGuard.implementation?(:ruby).should == true + + Object.const_set :RUBY_NAME, 'ruby1.9' + PlatformGuard.implementation?(:ruby).should == true + end + + it "raises an error when passed an unrecognized name" do + Object.const_set :RUBY_NAME, 'ruby' + lambda { + PlatformGuard.implementation?(:python) + }.should raise_error(/unknown implementation/) + end +end + +describe PlatformGuard, ".standard?" do + it "returns true if implementation? returns true" do + PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(true) + PlatformGuard.standard?.should be_true + end + + it "returns false if implementation? returns false" do + PlatformGuard.should_receive(:implementation?).with(:ruby).and_return(false) + PlatformGuard.standard?.should be_false + end +end + +describe PlatformGuard, ".wordsize?" do + it "returns true when arg is 32 and 1.size is 4" do + PlatformGuard.wordsize?(32).should == (1.size == 4) + end + + it "returns true when arg is 64 and 1.size is 8" do + PlatformGuard.wordsize?(64).should == (1.size == 8) + end +end + +describe PlatformGuard, ".os?" do + before :each do + stub_const 'PlatformGuard::HOST_OS', 'solarce' + end + + it "returns false when arg does not match the platform" do + PlatformGuard.os?(:ruby).should == false + end + + it "returns false when no arg matches the platform" do + PlatformGuard.os?(:ruby, :jruby, :rubinius, :maglev).should == false + end + + it "returns true when arg matches the platform" do + PlatformGuard.os?(:solarce).should == true + end + + it "returns true when any arg matches the platform" do + PlatformGuard.os?(:ruby, :jruby, :solarce, :rubinius, :maglev).should == true + end + + it "returns true when arg is :windows and the platform contains 'mswin'" do + stub_const 'PlatformGuard::HOST_OS', 'mswin32' + PlatformGuard.os?(:windows).should == true + end + + it "returns true when arg is :windows and the platform contains 'mingw'" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32' + PlatformGuard.os?(:windows).should == true + end + + it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mswin32' + PlatformGuard.os?(:linux).should == false + end + + it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32' + PlatformGuard.os?(:linux).should == false + end +end + +describe PlatformGuard, ".os? on JRuby" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + end + + after :all do + $VERBOSE = @verbose + end + + before :each do + @ruby_platform = Object.const_get :RUBY_PLATFORM + Object.const_set :RUBY_PLATFORM, 'java' + end + + after :each do + Object.const_set :RUBY_PLATFORM, @ruby_platform + end + + it "raises an error when testing for a :java platform" do + lambda { + PlatformGuard.os?(:java) + }.should raise_error(":java is not a valid OS") + end + + it "returns true when arg is :windows and RUBY_PLATFORM contains 'java' and os?(:windows) is true" do + stub_const 'PlatformGuard::HOST_OS', 'mswin32' + PlatformGuard.os?(:windows).should == true + end + + it "returns true when RUBY_PLATFORM contains 'java' and os?(argument) is true" do + stub_const 'PlatformGuard::HOST_OS', 'amiga' + PlatformGuard.os?(:amiga).should == true + end +end + +describe PlatformGuard, ".os?" do + before :each do + stub_const 'PlatformGuard::HOST_OS', 'unreal' + end + + it "returns true if argument matches RbConfig::CONFIG['host_os']" do + PlatformGuard.os?(:unreal).should == true + end + + it "returns true if any argument matches RbConfig::CONFIG['host_os']" do + PlatformGuard.os?(:bsd, :unreal, :amiga).should == true + end + + it "returns false if no argument matches RbConfig::CONFIG['host_os']" do + PlatformGuard.os?(:bsd, :netbsd, :amiga, :msdos).should == false + end + + it "returns false if argument does not match RbConfig::CONFIG['host_os']" do + PlatformGuard.os?(:amiga).should == false + end + + it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mswin32' + PlatformGuard.os?(:windows).should == true + end + + it "returns true when arg is :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32' + PlatformGuard.os?(:windows).should == true + end + + it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mswin'" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32' + PlatformGuard.os?(:linux).should == false + end + + it "returns false when arg is not :windows and RbConfig::CONFIG['host_os'] contains 'mingw'" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32' + PlatformGuard.os?(:linux).should == false + end +end + +describe PlatformGuard, ".windows?" do + it "returns true on windows" do + stub_const 'PlatformGuard::HOST_OS', 'i386-mingw32' + PlatformGuard.windows?.should == true + end + + it "returns false on non-windows" do + stub_const 'PlatformGuard::HOST_OS', 'i586-linux' + PlatformGuard.windows?.should == false + end +end diff --git a/spec/mspec/spec/guards/quarantine_spec.rb b/spec/mspec/spec/guards/quarantine_spec.rb new file mode 100644 index 0000000000..e5c7da7939 --- /dev/null +++ b/spec/mspec/spec/guards/quarantine_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' +require 'mspec/guards' + +describe QuarantineGuard, "#match?" do + it "returns true" do + QuarantineGuard.new.match?.should == true + end +end + +describe Object, "#quarantine!" do + before :each do + ScratchPad.clear + + @guard = QuarantineGuard.new + QuarantineGuard.stub(:new).and_return(@guard) + end + + it "does not yield" do + quarantine! { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "sets the name of the guard to :quarantine!" do + quarantine! { } + @guard.name.should == :quarantine! + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(false) + @guard.should_receive(:unregister) + lambda do + quarantine! { raise Exception } + end.should raise_error(Exception) + end +end diff --git a/spec/mspec/spec/guards/superuser_spec.rb b/spec/mspec/spec/guards/superuser_spec.rb new file mode 100644 index 0000000000..f8815057e1 --- /dev/null +++ b/spec/mspec/spec/guards/superuser_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' +require 'mspec/guards' + +describe Object, "#as_superuser" do + before :each do + @guard = SuperUserGuard.new + SuperUserGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "does not yield when Process.euid is not 0" do + Process.stub(:euid).and_return(501) + as_superuser { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "yields when Process.euid is 0" do + Process.stub(:euid).and_return(0) + as_superuser { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "sets the name of the guard to :as_superuser" do + as_superuser { } + @guard.name.should == :as_superuser + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(true) + @guard.should_receive(:unregister) + lambda do + as_superuser { raise Exception } + end.should raise_error(Exception) + end +end diff --git a/spec/mspec/spec/guards/support_spec.rb b/spec/mspec/spec/guards/support_spec.rb new file mode 100644 index 0000000000..43a7e76f06 --- /dev/null +++ b/spec/mspec/spec/guards/support_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' +require 'mspec/guards' + +describe Object, "#not_supported_on" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + @ruby_name = Object.const_get :RUBY_NAME if Object.const_defined? :RUBY_NAME + end + + after :all do + $VERBOSE = @verbose + if @ruby_name + Object.const_set :RUBY_NAME, @ruby_name + else + Object.send :remove_const, :RUBY_NAME + end + end + + before :each do + ScratchPad.clear + end + + it "raises an Exception when passed :ruby" do + Object.const_set :RUBY_NAME, "jruby" + lambda { + not_supported_on(:ruby) { ScratchPad.record :yield } + }.should raise_error(Exception) + ScratchPad.recorded.should_not == :yield + end + + it "does not yield when #implementation? returns true" do + Object.const_set :RUBY_NAME, "jruby" + not_supported_on(:jruby) { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "yields when #standard? returns true" do + Object.const_set :RUBY_NAME, "ruby" + not_supported_on(:rubinius) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "yields when #implementation? returns false" do + Object.const_set :RUBY_NAME, "jruby" + not_supported_on(:rubinius) { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end +end + +describe Object, "#not_supported_on" do + before :each do + @guard = SupportedGuard.new + SupportedGuard.stub(:new).and_return(@guard) + end + + it "sets the name of the guard to :not_supported_on" do + not_supported_on(:rubinius) { } + @guard.name.should == :not_supported_on + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(false) + @guard.should_receive(:unregister) + lambda do + not_supported_on(:rubinius) { raise Exception } + end.should raise_error(Exception) + end +end diff --git a/spec/mspec/spec/guards/user_spec.rb b/spec/mspec/spec/guards/user_spec.rb new file mode 100644 index 0000000000..2de4db7390 --- /dev/null +++ b/spec/mspec/spec/guards/user_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' +require 'mspec/guards' + +describe Object, "#as_user" do + before :each do + ScratchPad.clear + end + + it "yields when the Process.euid is not 0" do + Process.stub(:euid).and_return(501) + as_user { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "does not yield when the Process.euid is 0" do + Process.stub(:euid).and_return(0) + as_user { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end +end diff --git a/spec/mspec/spec/guards/version_spec.rb b/spec/mspec/spec/guards/version_spec.rb new file mode 100644 index 0000000000..f11e3dbd94 --- /dev/null +++ b/spec/mspec/spec/guards/version_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' +require 'mspec/guards' + +# The VersionGuard specifies a version of Ruby with a String of +# the form: v = 'major.minor.tiny'. +# +# A VersionGuard instance can be created with a single String, +# which means any version >= each component of v. +# Or, the guard can be created with a Range, a..b, or a...b, +# where a, b are of the same form as v. The meaning of the Range +# is as typically understood: a..b means v >= a and v <= b; +# a...b means v >= a and v < b. + +describe VersionGuard, "#match?" do + before :each do + hide_deprecation_warnings + stub_const "VersionGuard::FULL_RUBY_VERSION", SpecVersion.new('1.8.6') + end + + it "returns true when the argument is equal to RUBY_VERSION" do + VersionGuard.new('1.8.6').match?.should == true + end + + it "returns true when the argument is less than RUBY_VERSION" do + VersionGuard.new('1.8').match?.should == true + VersionGuard.new('1.8.5').match?.should == true + end + + it "returns false when the argument is greater than RUBY_VERSION" do + VersionGuard.new('1.8.7').match?.should == false + VersionGuard.new('1.9.2').match?.should == false + end + + it "returns true when the argument range includes RUBY_VERSION" do + VersionGuard.new('1.8.5'..'1.8.7').match?.should == true + VersionGuard.new('1.8'..'1.9').match?.should == true + VersionGuard.new('1.8'...'1.9').match?.should == true + VersionGuard.new('1.8'..'1.8.6').match?.should == true + VersionGuard.new('1.8.5'..'1.8.6').match?.should == true + VersionGuard.new(''...'1.8.7').match?.should == true + end + + it "returns false when the argument range does not include RUBY_VERSION" do + VersionGuard.new('1.8.7'..'1.8.9').match?.should == false + VersionGuard.new('1.8.4'..'1.8.5').match?.should == false + VersionGuard.new('1.8.4'...'1.8.6').match?.should == false + VersionGuard.new('1.8.5'...'1.8.6').match?.should == false + VersionGuard.new(''...'1.8.6').match?.should == false + end +end + +describe Object, "#ruby_version_is" do + before :each do + @guard = VersionGuard.new 'x.x.x' + VersionGuard.stub(:new).and_return(@guard) + ScratchPad.clear + end + + it "yields when #match? returns true" do + @guard.stub(:match?).and_return(true) + ruby_version_is('x.x.x') { ScratchPad.record :yield } + ScratchPad.recorded.should == :yield + end + + it "does not yield when #match? returns false" do + @guard.stub(:match?).and_return(false) + ruby_version_is('x.x.x') { ScratchPad.record :yield } + ScratchPad.recorded.should_not == :yield + end + + it "sets the name of the guard to :ruby_version_is" do + ruby_version_is("") { } + @guard.name.should == :ruby_version_is + end + + it "calls #unregister even when an exception is raised in the guard block" do + @guard.should_receive(:match?).and_return(true) + @guard.should_receive(:unregister) + lambda do + ruby_version_is("") { raise Exception } + end.should raise_error(Exception) + end +end diff --git a/spec/mspec/spec/helpers/argf_spec.rb b/spec/mspec/spec/helpers/argf_spec.rb new file mode 100644 index 0000000000..cf5eb0fe88 --- /dev/null +++ b/spec/mspec/spec/helpers/argf_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#argf" do + before :each do + @saved_argv = ARGV.dup + @argv = [__FILE__] + end + + it "sets @argf to an instance of ARGF.class with the given argv" do + argf @argv do + @argf.should be_an_instance_of ARGF.class + @argf.filename.should == @argv.first + end + @argf.should be_nil + end + + it "does not alter ARGV nor ARGF" do + argf @argv do + end + ARGV.should == @saved_argv + ARGF.argv.should == @saved_argv + end + + it "does not close STDIN" do + argf ['-'] do + end + STDIN.should_not be_closed + end + + it "disallows nested calls" do + argf @argv do + lambda { argf @argv }.should raise_error + end + end +end diff --git a/spec/mspec/spec/helpers/argv_spec.rb b/spec/mspec/spec/helpers/argv_spec.rb new file mode 100644 index 0000000000..c3b21c7639 --- /dev/null +++ b/spec/mspec/spec/helpers/argv_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#argv" do + before :each do + ScratchPad.clear + + @saved_argv = ARGV.dup + @argv = ["a", "b"] + end + + it "replaces and restores the value of ARGV" do + argv @argv + ARGV.should == @argv + argv :restore + ARGV.should == @saved_argv + end + + it "yields to the block after setting ARGV" do + argv @argv do + ScratchPad.record ARGV.dup + end + ScratchPad.recorded.should == @argv + ARGV.should == @saved_argv + end +end diff --git a/spec/mspec/spec/helpers/datetime_spec.rb b/spec/mspec/spec/helpers/datetime_spec.rb new file mode 100644 index 0000000000..8696c0c9c7 --- /dev/null +++ b/spec/mspec/spec/helpers/datetime_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#new_datetime" do + it "returns a default DateTime instance" do + new_datetime.should == DateTime.new + end + + it "returns a DateTime instance with the specified year value" do + d = new_datetime :year => 1970 + d.year.should == 1970 + end + + it "returns a DateTime instance with the specified month value" do + d = new_datetime :month => 11 + d.mon.should == 11 + end + + it "returns a DateTime instance with the specified day value" do + d = new_datetime :day => 23 + d.day.should == 23 + end + + it "returns a DateTime instance with the specified hour value" do + d = new_datetime :hour => 10 + d.hour.should == 10 + end + + it "returns a DateTime instance with the specified minute value" do + d = new_datetime :minute => 10 + d.min.should == 10 + end + + it "returns a DateTime instance with the specified second value" do + d = new_datetime :second => 2 + d.sec.should == 2 + end + + it "returns a DateTime instance with the specified offset value" do + d = new_datetime :offset => Rational(3,24) + d.offset.should == Rational(3,24) + end +end diff --git a/spec/mspec/spec/helpers/fixture_spec.rb b/spec/mspec/spec/helpers/fixture_spec.rb new file mode 100644 index 0000000000..4dbdd092f1 --- /dev/null +++ b/spec/mspec/spec/helpers/fixture_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#fixture" do + before :each do + @dir = File.realpath("..", __FILE__) + end + + it "returns the expanded path to a fixture file" do + name = fixture(__FILE__, "subdir", "file.txt") + name.should == "#{@dir}/fixtures/subdir/file.txt" + end + + it "omits '/shared' if it is the suffix of the directory string" do + name = fixture("#{@dir}/shared/file.rb", "subdir", "file.txt") + name.should == "#{@dir}/fixtures/subdir/file.txt" + end + + it "does not append '/fixtures' if it is the suffix of the directory string" do + commands_dir = "#{File.dirname(@dir)}/commands" + name = fixture("#{commands_dir}/fixtures/file.rb", "subdir", "file.txt") + name.should == "#{commands_dir}/fixtures/subdir/file.txt" + end +end diff --git a/spec/mspec/spec/helpers/flunk_spec.rb b/spec/mspec/spec/helpers/flunk_spec.rb new file mode 100644 index 0000000000..7b1216d3f7 --- /dev/null +++ b/spec/mspec/spec/helpers/flunk_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/runner/mspec' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#flunk" do + before :each do + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + end + + it "raises an SpecExpectationNotMetError unconditionally" do + lambda { flunk }.should raise_error(SpecExpectationNotMetError) + end + + it "accepts on argument for an optional message" do + lambda {flunk "test"}.should raise_error(SpecExpectationNotMetError) + end +end diff --git a/spec/mspec/spec/helpers/fs_spec.rb b/spec/mspec/spec/helpers/fs_spec.rb new file mode 100644 index 0000000000..5afa91ff58 --- /dev/null +++ b/spec/mspec/spec/helpers/fs_spec.rb @@ -0,0 +1,182 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#cp" do + before :each do + @source = tmp("source.txt") + @copy = tmp("copied.txt") + + @contents = "This is a copy." + File.open(@source, "w") { |f| f.write @contents } + end + + after :each do + File.delete @source if File.exist? @source + File.delete @copy if File.exist? @copy + end + + it "copies a file" do + cp @source, @copy + data = IO.read(@copy) + data.should == @contents + data.should == IO.read(@source) + end +end + +describe Object, "#touch" do + before :all do + @name = tmp("touched.txt") + end + + after :each do + File.delete @name if File.exist? @name + end + + it "creates a file" do + touch @name + File.exist?(@name).should be_true + end + + it "accepts an optional mode argument" do + touch @name, "wb" + File.exist?(@name).should be_true + end + + it "overwrites an existing file" do + File.open(@name, "w") { |f| f.puts "used" } + File.size(@name).should > 0 + + touch @name + File.size(@name).should == 0 + end + + it "yields the open file if passed a block" do + touch(@name) { |f| f.write "touching" } + IO.read(@name).should == "touching" + end +end + +describe Object, "#touch" do + before :all do + @name = tmp("subdir/touched.txt") + end + + after :each do + rm_r File.dirname(@name) + end + + it "creates all the directories in the path to the file" do + touch @name + File.exist?(@name).should be_true + end +end + +describe Object, "#mkdir_p" do + before :all do + @dir1 = tmp("/nested") + @dir2 = @dir1 + "/directory" + @paths = [ @dir2, @dir1 ] + end + + after :each do + File.delete @dir1 if File.file? @dir1 + @paths.each { |path| Dir.rmdir path if File.directory? path } + end + + it "creates all the directories in a path" do + mkdir_p @dir2 + File.directory?(@dir2).should be_true + end + + it "raises an ArgumentError if a path component is a file" do + File.open(@dir1, "w") { |f| } + lambda { mkdir_p @dir2 }.should raise_error(ArgumentError) + end +end + +describe Object, "#rm_r" do + before :all do + @topdir = tmp("rm_r_tree") + @topfile = @topdir + "/file.txt" + @link = @topdir + "/file.lnk" + @socket = @topdir + "/socket.sck" + @subdir1 = @topdir + "/subdir1" + @subdir2 = @subdir1 + "/subdir2" + @subfile = @subdir1 + "/subfile.txt" + end + + before :each do + mkdir_p @subdir2 + touch @topfile + touch @subfile + end + + after :each do + File.delete @link if File.exist? @link or File.symlink? @link + File.delete @socket if File.exist? @socket + File.delete @subfile if File.exist? @subfile + File.delete @topfile if File.exist? @topfile + + Dir.rmdir @subdir2 if File.directory? @subdir2 + Dir.rmdir @subdir1 if File.directory? @subdir1 + Dir.rmdir @topdir if File.directory? @topdir + end + + it "raises an ArgumentError if the path is not prefixed by MSPEC_RM_PREFIX" do + lambda { rm_r "some_file.txt" }.should raise_error(ArgumentError) + end + + it "removes a single file" do + rm_r @subfile + File.exist?(@subfile).should be_false + end + + it "removes multiple files" do + rm_r @topfile, @subfile + File.exist?(@topfile).should be_false + File.exist?(@subfile).should be_false + end + + platform_is_not :windows do + it "removes a symlink to a file" do + File.symlink @topfile, @link + rm_r @link + File.exist?(@link).should be_false + end + + it "removes a symlink to a directory" do + File.symlink @subdir1, @link + rm_r @link + lambda do + File.lstat(@link) + end.should raise_error(Errno::ENOENT) + File.exist?(@subdir1).should be_true + end + + it "removes a dangling symlink" do + File.symlink "non_existent_file", @link + rm_r @link + lambda do + File.lstat(@link) + end.should raise_error(Errno::ENOENT) + end + + it "removes a socket" do + require 'socket' + UNIXServer.new(@socket).close + rm_r @socket + File.exist?(@socket).should be_false + end + end + + it "removes a single directory" do + rm_r @subdir2 + File.directory?(@subdir2).should be_false + end + + it "recursively removes a directory tree" do + rm_r @topdir + File.directory?(@topdir).should be_false + end +end diff --git a/spec/mspec/spec/helpers/io_spec.rb b/spec/mspec/spec/helpers/io_spec.rb new file mode 100644 index 0000000000..6dfd81ee56 --- /dev/null +++ b/spec/mspec/spec/helpers/io_spec.rb @@ -0,0 +1,174 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe IOStub do + before :each do + @out = IOStub.new + @sep = $\ + end + + after :each do + $\ = @sep + end + + it "provides a write method" do + @out.write "this" + @out.should == "this" + end + + it "concatenates the arguments sent to write" do + @out.write "flim ", "flam" + @out.should == "flim flam" + end + + it "provides a print method that appends the default separator" do + $\ = " [newline] " + @out.print "hello" + @out.print "world" + @out.should == "hello [newline] world [newline] " + end + + it "provides a puts method that appends the default separator" do + @out.puts "hello", 1, 2, 3 + @out.should == "hello\n1\n2\n3\n" + end + + it "provides a puts method that appends separator if argument not given" do + @out.puts + @out.should == "\n" + end + + it "provides a printf method" do + @out.printf "%-10s, %03d, %2.1f", "test", 42, 4.2 + @out.should == "test , 042, 4.2" + end + + it "provides a flush method that does nothing and returns self" do + @out.flush.should == @out + end +end + +describe Object, "#new_fd" do + before :each do + @name = tmp("io_specs") + @io = nil + end + + after :each do + @io.close if @io and not @io.closed? + rm_r @name + end + + it "returns a Fixnum that can be used to create an IO instance" do + fd = new_fd @name + fd.should be_an_instance_of(Fixnum) + + @io = IO.new fd, fmode('w:utf-8') + @io.sync = true + @io.print "io data" + + IO.read(@name).should == "io data" + end + + it "accepts an options Hash" do + FeatureGuard.stub(:enabled?).and_return(true) + fd = new_fd @name, { :mode => 'w:utf-8' } + fd.should be_an_instance_of(Fixnum) + + @io = IO.new fd, fmode('w:utf-8') + @io.sync = true + @io.print "io data" + + IO.read(@name).should == "io data" + end + + it "raises an ArgumentError if the options Hash does not include :mode" do + FeatureGuard.stub(:enabled?).and_return(true) + lambda { new_fd @name, { :encoding => "utf-8" } }.should raise_error(ArgumentError) + end +end + +describe Object, "#new_io" do + before :each do + @name = tmp("io_specs.txt") + end + + after :each do + @io.close if @io and !@io.closed? + rm_r @name + end + + it "returns an IO instance" do + @io = new_io @name + @io.should be_an_instance_of(IO) + end + + it "opens the IO for reading if passed 'r'" do + touch(@name) { |f| f.print "io data" } + @io = new_io @name, "r" + @io.read.should == "io data" + lambda { @io.puts "more data" }.should raise_error(IOError) + end + + it "opens the IO for writing if passed 'w'" do + @io = new_io @name, "w" + @io.sync = true + + @io.print "io data" + IO.read(@name).should == "io data" + end + + it "opens the IO for reading if passed { :mode => 'r' }" do + touch(@name) { |f| f.print "io data" } + @io = new_io @name, { :mode => "r" } + @io.read.should == "io data" + lambda { @io.puts "more data" }.should raise_error(IOError) + end + + it "opens the IO for writing if passed { :mode => 'w' }" do + @io = new_io @name, { :mode => "w" } + @io.sync = true + + @io.print "io data" + IO.read(@name).should == "io data" + end +end + +describe Object, "#fmode" do + it "returns the argument unmodified if :encoding feature is enabled" do + FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true) + fmode("rb:binary:utf-8").should == "rb:binary:utf-8" + end + + it "returns only the file access mode if :encoding feature is not enabled" do + FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(false) + fmode("rb:binary:utf-8").should == "rb" + end +end + +describe Object, "#options_or_mode" do + describe "if passed a Hash" do + it "returns a mode string if :encoding feature is not enabled" do + FeatureGuard.should_receive(:enabled?).with(:encoding).twice.and_return(false) + options_or_mode(:mode => "rb:binary").should == "rb" + end + + it "returns a Hash if :encoding feature is enabled" do + FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true) + options_or_mode(:mode => "rb:utf-8").should == { :mode => "rb:utf-8" } + end + end + + describe "if passed a String" do + it "returns only the file access mode if :encoding feature is not enabled" do + FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(false) + options_or_mode("rb:binary:utf-8").should == "rb" + end + + it "returns the argument unmodified if :encoding feature is enabled" do + FeatureGuard.should_receive(:enabled?).with(:encoding).and_return(true) + options_or_mode("rb:binary:utf-8").should == "rb:binary:utf-8" + end + end +end diff --git a/spec/mspec/spec/helpers/mock_to_path_spec.rb b/spec/mspec/spec/helpers/mock_to_path_spec.rb new file mode 100644 index 0000000000..464e7e5440 --- /dev/null +++ b/spec/mspec/spec/helpers/mock_to_path_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#mock_to_path" do + it "returns an object that responds to #to_path" do + obj = mock_to_path("foo") + obj.should be_a(MockObject) + obj.should respond_to(:to_path) + obj.to_path + end + + it "returns the provided path when #to_path is called" do + obj = mock_to_path("/tmp/foo") + obj.to_path.should == "/tmp/foo" + end +end diff --git a/spec/mspec/spec/helpers/numeric_spec.rb b/spec/mspec/spec/helpers/numeric_spec.rb new file mode 100644 index 0000000000..2ea56e5961 --- /dev/null +++ b/spec/mspec/spec/helpers/numeric_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#bignum_value" do + it "returns a value that is an instance of Bignum on any platform" do + bignum_value.should == 0x8000_0000_0000_0000 + end + + it "returns the default value incremented by the argument" do + bignum_value(42).should == 0x8000_0000_0000_002a + end +end + +describe Object, "#nan_value" do + it "returns NaN" do + nan_value.nan?.should be_true + end +end + +describe Object, "#infinity_value" do + it "returns Infinity" do + infinity_value.infinite?.should == 1 + end +end diff --git a/spec/mspec/spec/helpers/ruby_exe_spec.rb b/spec/mspec/spec/helpers/ruby_exe_spec.rb new file mode 100644 index 0000000000..debfc3b1ca --- /dev/null +++ b/spec/mspec/spec/helpers/ruby_exe_spec.rb @@ -0,0 +1,220 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' +require 'rbconfig' + +class RubyExeSpecs +end + +describe "#ruby_exe_options" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + + @ruby_name = Object.const_get :RUBY_NAME + @ruby_exe_env = ENV['RUBY_EXE'] + + @script = RubyExeSpecs.new + end + + after :all do + Object.const_set :RUBY_NAME, @ruby_name + ENV['RUBY_EXE'] = @ruby_exe_env + $VERBOSE = @verbose + end + + before :each do + @script = RubyExeSpecs.new + end + + it "returns ENV['RUBY_EXE'] when passed :env" do + ENV['RUBY_EXE'] = "kowabunga" + @script.ruby_exe_options(:env).should == "kowabunga" + end + + it "returns 'bin/jruby' when passed :engine and RUBY_NAME is 'jruby'" do + Object.const_set :RUBY_NAME, 'jruby' + @script.ruby_exe_options(:engine).should == 'bin/jruby' + end + + it "returns 'bin/rbx' when passed :engine, RUBY_NAME is 'rbx'" do + Object.const_set :RUBY_NAME, 'rbx' + @script.ruby_exe_options(:engine).should == 'bin/rbx' + end + + it "returns 'ir' when passed :engine and RUBY_NAME is 'ironruby'" do + Object.const_set :RUBY_NAME, 'ironruby' + @script.ruby_exe_options(:engine).should == 'ir' + end + + it "returns 'maglev-ruby' when passed :engine and RUBY_NAME is 'maglev'" do + Object.const_set :RUBY_NAME, 'maglev' + @script.ruby_exe_options(:engine).should == 'maglev-ruby' + end + + it "returns 'topaz' when passed :engine and RUBY_NAME is 'topaz'" do + Object.const_set :RUBY_NAME, 'topaz' + @script.ruby_exe_options(:engine).should == 'topaz' + end + + it "returns RUBY_NAME + $(EXEEXT) when passed :name" do + bin = RUBY_NAME + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '') + name = File.join ".", bin + @script.ruby_exe_options(:name).should == name + end + + it "returns $(bindir)/$(RUBY_INSTALL_NAME) + $(EXEEXT) when passed :install_name" do + bin = RbConfig::CONFIG['RUBY_INSTALL_NAME'] + (RbConfig::CONFIG['EXEEXT'] || RbConfig::CONFIG['exeext'] || '') + name = File.join RbConfig::CONFIG['bindir'], bin + @script.ruby_exe_options(:install_name).should == name + end +end + +describe "#resolve_ruby_exe" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + + @name = "ruby_spec_exe" + end + + before :each do + @script = RubyExeSpecs.new + end + + after :all do + $VERBOSE = @verbose + end + + it "returns the value returned by #ruby_exe_options if it exists and is executable" do + @script.should_receive(:ruby_exe_options).and_return(@name) + File.should_receive(:file?).with(@name).and_return(true) + File.should_receive(:executable?).with(@name).and_return(true) + File.should_receive(:expand_path).with(@name).and_return(@name) + @script.resolve_ruby_exe.should == @name + end + + it "expands the path portion of the result of #ruby_exe_options" do + @script.should_receive(:ruby_exe_options).and_return("#{@name}") + File.should_receive(:file?).with(@name).and_return(true) + File.should_receive(:executable?).with(@name).and_return(true) + File.should_receive(:expand_path).with(@name).and_return("/usr/bin/#{@name}") + @script.resolve_ruby_exe.should == "/usr/bin/#{@name}" + end + + it "adds the flags after the executable" do + @name = 'bin/rbx' + @script.should_receive(:ruby_exe_options).and_return(@name) + File.should_receive(:file?).with(@name).and_return(true) + File.should_receive(:executable?).with(@name).and_return(true) + File.should_receive(:expand_path).with(@name).and_return(@name) + + ENV.should_receive(:[]).with("RUBY_FLAGS").and_return('-X19') + @script.resolve_ruby_exe.should == 'bin/rbx -X19' + end + + it "raises an exception if no exe is found" do + File.should_receive(:file?).at_least(:once).and_return(false) + lambda { + @script.resolve_ruby_exe + }.should raise_error(Exception) + end +end + +describe Object, "#ruby_cmd" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + + @ruby_exe = Object.const_get :RUBY_EXE + Object.const_set :RUBY_EXE, 'ruby_spec_exe -w -Q' + + @file = "some/ruby/file.rb" + @code = %(some "real" 'ruby' code) + + @script = RubyExeSpecs.new + end + + after :all do + Object.const_set :RUBY_EXE, @ruby_exe + $VERBOSE = @verbose + end + + it "returns a command that runs the given file if it is a file that exists" do + File.should_receive(:exist?).with(@file).and_return(true) + @script.ruby_cmd(@file).should == "ruby_spec_exe -w -Q some/ruby/file.rb" + end + + it "includes the given options and arguments with a file" do + File.should_receive(:exist?).with(@file).and_return(true) + @script.ruby_cmd(@file, :options => "-w -Cdir", :args => "< file.txt").should == + "ruby_spec_exe -w -Q -w -Cdir some/ruby/file.rb < file.txt" + end + + it "includes the given options and arguments with -e" do + File.should_receive(:exist?).with(@code).and_return(false) + @script.ruby_cmd(@code, :options => "-W0 -Cdir", :args => "< file.txt").should == + %(ruby_spec_exe -w -Q -W0 -Cdir -e "some \\"real\\" 'ruby' code" < file.txt) + end + + it "returns a command with options and arguments but without code or file" do + @script.ruby_cmd(nil, :options => "-c", :args => "> file.txt").should == + "ruby_spec_exe -w -Q -c > file.txt" + end +end + +describe Object, "#ruby_exe" do + before :all do + @script = RubyExeSpecs.new + end + + before :each do + @script.stub(:`) + end + + it "executes (using `) the result of calling #ruby_cmd with the given arguments" do + code = "code" + options = {} + @script.should_receive(:ruby_cmd).and_return("ruby_cmd") + @script.should_receive(:`).with("ruby_cmd") + @script.ruby_exe(code, options) + end + + describe "with :dir option" do + it "is deprecated" do + lambda { + @script.ruby_exe nil, :dir => "tmp" + }.should raise_error(/no longer supported, use Dir\.chdir/) + end + end + + describe "with :env option" do + it "preserves the values of existing ENV keys" do + ENV["ABC"] = "123" + ENV.stub(:[]) + ENV.should_receive(:[]).with("ABC") + @script.ruby_exe nil, :env => { :ABC => "xyz" } + end + + it "adds the :env entries to ENV" do + ENV.should_receive(:[]=).with("ABC", "xyz") + @script.ruby_exe nil, :env => { :ABC => "xyz" } + end + + it "deletes the :env entries in ENV when an exception is raised" do + ENV.should_receive(:delete).with("XYZ") + @script.ruby_exe nil, :env => { :XYZ => "xyz" } + end + + it "resets the values of existing ENV keys when an exception is raised" do + ENV["ABC"] = "123" + ENV.should_receive(:[]=).with("ABC", "xyz") + ENV.should_receive(:[]=).with("ABC", "123") + + @script.should_receive(:`).and_raise(Exception) + lambda do + @script.ruby_exe nil, :env => { :ABC => "xyz" } + end.should raise_error(Exception) + end + end +end diff --git a/spec/mspec/spec/helpers/scratch_spec.rb b/spec/mspec/spec/helpers/scratch_spec.rb new file mode 100644 index 0000000000..6a9eb2cf73 --- /dev/null +++ b/spec/mspec/spec/helpers/scratch_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe ScratchPad do + it "records an object and returns a previously recorded object" do + ScratchPad.record :this + ScratchPad.recorded.should == :this + end + + it "clears the recorded object" do + ScratchPad.record :that + ScratchPad.recorded.should == :that + ScratchPad.clear + ScratchPad.recorded.should == nil + end + + it "provides a convenience shortcut to append to a previously recorded object" do + ScratchPad.record [] + ScratchPad << :new + ScratchPad << :another + ScratchPad.recorded.should == [:new, :another] + end +end diff --git a/spec/mspec/spec/helpers/tmp_spec.rb b/spec/mspec/spec/helpers/tmp_spec.rb new file mode 100644 index 0000000000..afadc7f51c --- /dev/null +++ b/spec/mspec/spec/helpers/tmp_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' +require 'mspec/guards' +require 'mspec/helpers' + +describe Object, "#tmp" do + before :all do + @dir = "#{File.expand_path(Dir.pwd)}/rubyspec_temp" + end + + it "returns a name relative to the current working directory" do + tmp("test.txt").should == "#{@dir}/#{SPEC_TEMP_UNIQUIFIER}-test.txt" + end + + it "returns a 'unique' name on repeated calls" do + a = tmp("text.txt") + b = tmp("text.txt") + a.should_not == b + end + + it "does not 'uniquify' the name if requested not to" do + tmp("test.txt", false).should == "#{@dir}/test.txt" + end + + it "returns the name of the temporary directory when passed an empty string" do + tmp("").should == "#{@dir}/" + end +end diff --git a/spec/mspec/spec/integration/interpreter_spec.rb b/spec/mspec/spec/integration/interpreter_spec.rb new file mode 100644 index 0000000000..b6fa6859d1 --- /dev/null +++ b/spec/mspec/spec/integration/interpreter_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe "The interpreter passed with -t" do + it "is used in subprocess" do + fixtures = "spec/fixtures" + interpreter = "#{fixtures}/my_ruby" + out, ret = run_mspec("run", "#{fixtures}/print_interpreter_spec.rb -t #{interpreter}") + out = out.lines.map(&:chomp).reject { |line| + line == 'RUBY_DESCRIPTION' + }.take(3) + out.should == [ + interpreter, + interpreter, + "CWD/#{interpreter}" + ] + ret.success?.should == true + end +end diff --git a/spec/mspec/spec/integration/run_spec.rb b/spec/mspec/spec/integration/run_spec.rb new file mode 100644 index 0000000000..93d2ef8b68 --- /dev/null +++ b/spec/mspec/spec/integration/run_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe "Running mspec" do + a_spec_output = <' +CWD/spec/fixtures/a_spec.rb:2:in `' +CWD/bin/mspec-run:7:in `
    ' + +2) +Foo#bar fails ERROR +RuntimeError: failure +CWD/spec/fixtures/a_spec.rb:12:in `block (2 levels) in ' +CWD/spec/fixtures/a_spec.rb:2:in `' +CWD/bin/mspec-run:7:in `
    ' + +Finished in D.DDDDDD seconds +EOS + + a_stats = "1 file, 3 examples, 2 expectations, 1 failure, 1 error, 0 tagged\n" + ab_stats = "2 files, 4 examples, 3 expectations, 1 failure, 1 error, 0 tagged\n" + + it "runs the specs" do + fixtures = "spec/fixtures" + out, ret = run_mspec("run", "#{fixtures}/a_spec.rb") + out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}" + ret.success?.should == false + end + + it "directly with mspec-run runs the specs" do + fixtures = "spec/fixtures" + out, ret = run_mspec("-run", "#{fixtures}/a_spec.rb") + out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}" + ret.success?.should == false + end + + it "runs the specs in parallel with -j" do + fixtures = "spec/fixtures" + out, ret = run_mspec("run", "-j #{fixtures}/a_spec.rb #{fixtures}/b_spec.rb") + progress_bar = + "\r[/ | 0% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " + + "\r[- | ==================50% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " + + "\r[\\ | ==================100%================== | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " + out.should == "RUBY_DESCRIPTION\n#{progress_bar}\n#{a_spec_output}\n#{ab_stats}" + ret.success?.should == false + end +end diff --git a/spec/mspec/spec/integration/tag_spec.rb b/spec/mspec/spec/integration/tag_spec.rb new file mode 100644 index 0000000000..d980769043 --- /dev/null +++ b/spec/mspec/spec/integration/tag_spec.rb @@ -0,0 +1,63 @@ +# encoding: utf-8 +require 'spec_helper' + +describe "Running mspec tag" do + before :all do + FileUtils.rm_rf 'spec/fixtures/tags' + end + + after :all do + FileUtils.rm_rf 'spec/fixtures/tags' + end + + it "tags the failing specs" do + fixtures = "spec/fixtures" + out, ret = run_mspec("tag", "--add fails --fail #{fixtures}/tagging_spec.rb") + out.should == <' +CWD/spec/fixtures/tagging_spec.rb:3:in `' +CWD/bin/mspec-tag:7:in `
    ' + +2) +Tag#me érròrs in unicode FAILED +Expected 1 + to equal 2 + +CWD/spec/fixtures/tagging_spec.rb:13:in `block (2 levels) in ' +CWD/spec/fixtures/tagging_spec.rb:3:in `' +CWD/bin/mspec-tag:7:in `
    ' + +Finished in D.DDDDDD seconds + +1 file, 3 examples, 3 expectations, 2 failures, 0 errors, 0 tagged +EOS + ret.success?.should == false + end + + it "does not run already tagged specs" do + fixtures = "spec/fixtures" + out, ret = run_mspec("run", "--excl-tag fails #{fixtures}/tagging_spec.rb") + out.should == < operator" do + it "raises an SpecExpectationNotMetError when expected > actual returns false" do + lambda { + SpecPositiveOperatorMatcher.new(4) > 5 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x to be greater than y'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected 4\n", "to be greater than 5\n") + SpecPositiveOperatorMatcher.new(4) > 5 + end + + it "does not raise an exception when expected > actual returns true" do + SpecPositiveOperatorMatcher.new(5) > 4 + end +end + +describe SpecPositiveOperatorMatcher, ">= operator" do + it "raises an SpecExpectationNotMetError when expected >= actual returns false" do + lambda { + SpecPositiveOperatorMatcher.new(4) >= 5 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x to be greater than or equal to y'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected 4\n", "to be greater than or equal to 5\n") + SpecPositiveOperatorMatcher.new(4) >= 5 + end + + it "does not raise an exception when expected > actual returns true" do + SpecPositiveOperatorMatcher.new(5) >= 4 + SpecPositiveOperatorMatcher.new(5) >= 5 + end +end + +describe SpecPositiveOperatorMatcher, "< operater" do + it "raises an SpecExpectationNotMetError when expected < actual returns false" do + lambda { + SpecPositiveOperatorMatcher.new(5) < 4 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x to be less than y'" do + SpecExpectation.should_receive(:fail_with).with("Expected 5\n", "to be less than 4\n") + SpecPositiveOperatorMatcher.new(5) < 4 + end + + it "does not raise an exception when expected < actual returns true" do + SpecPositiveOperatorMatcher.new(4) < 5 + end +end + +describe SpecPositiveOperatorMatcher, "<= operater" do + it "raises an SpecExpectationNotMetError when expected < actual returns false" do + lambda { + SpecPositiveOperatorMatcher.new(5) <= 4 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x to be less than or equal to y'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected 5\n", "to be less than or equal to 4\n") + SpecPositiveOperatorMatcher.new(5) <= 4 + end + + it "does not raise an exception when expected < actual returns true" do + SpecPositiveOperatorMatcher.new(4) <= 5 + SpecPositiveOperatorMatcher.new(4) <= 4 + end +end + +describe SpecNegativeOperatorMatcher, "== operator" do + it "raises an SpecExpectationNotMetError when expected == actual returns true" do + lambda { + SpecNegativeOperatorMatcher.new(1) == 1 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x not to equal y'" do + SpecExpectation.should_receive(:fail_with).with("Expected 1\n", "not to equal 1\n") + SpecNegativeOperatorMatcher.new(1) == 1 + end + + it "does not raise an exception when expected == actual returns false" do + SpecNegativeOperatorMatcher.new(1) == 2 + end +end + +describe SpecNegativeOperatorMatcher, "=~ operator" do + it "raises an SpecExpectationNotMetError when expected =~ actual returns true" do + lambda { + SpecNegativeOperatorMatcher.new('real') =~ /real/ + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected \"x\" not to match /y/'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected \"real\"\n", "not to match /real/\n") + SpecNegativeOperatorMatcher.new('real') =~ /real/ + end + + it "does not raise an exception when expected =~ actual returns false" do + SpecNegativeOperatorMatcher.new('real') =~ /fake/ + end +end + +describe SpecNegativeOperatorMatcher, "< operator" do + it "raises an SpecExpectationNotMetError when expected < actual returns true" do + lambda { + SpecNegativeOperatorMatcher.new(4) < 5 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x not to be less than y'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected 4\n", "not to be less than 5\n") + SpecNegativeOperatorMatcher.new(4) < 5 + end + + it "does not raise an exception when expected < actual returns false" do + SpecNegativeOperatorMatcher.new(5) < 4 + end +end + +describe SpecNegativeOperatorMatcher, "<= operator" do + it "raises an SpecExpectationNotMetError when expected <= actual returns true" do + lambda { + SpecNegativeOperatorMatcher.new(4) <= 5 + }.should raise_error(SpecExpectationNotMetError) + lambda { + SpecNegativeOperatorMatcher.new(5) <= 5 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x not to be less than or equal to y'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected 4\n", "not to be less than or equal to 5\n") + SpecNegativeOperatorMatcher.new(4) <= 5 + end + + it "does not raise an exception when expected <= actual returns false" do + SpecNegativeOperatorMatcher.new(5) <= 4 + end +end + +describe SpecNegativeOperatorMatcher, "> operator" do + it "raises an SpecExpectationNotMetError when expected > actual returns true" do + lambda { + SpecNegativeOperatorMatcher.new(5) > 4 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x not to be greater than y'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected 5\n", "not to be greater than 4\n") + SpecNegativeOperatorMatcher.new(5) > 4 + end + + it "does not raise an exception when expected > actual returns false" do + SpecNegativeOperatorMatcher.new(4) > 5 + end +end + +describe SpecNegativeOperatorMatcher, ">= operator" do + it "raises an SpecExpectationNotMetError when expected >= actual returns true" do + lambda { + SpecNegativeOperatorMatcher.new(5) >= 4 + }.should raise_error(SpecExpectationNotMetError) + lambda { + SpecNegativeOperatorMatcher.new(5) >= 5 + }.should raise_error(SpecExpectationNotMetError) + end + + it "provides a failure message that 'Expected x not to be greater than or equal to y'" do + SpecExpectation.should_receive(:fail_with).with( + "Expected 5\n", "not to be greater than or equal to 4\n") + SpecNegativeOperatorMatcher.new(5) >= 4 + end + + it "does not raise an exception when expected >= actual returns false" do + SpecNegativeOperatorMatcher.new(4) >= 5 + end +end diff --git a/spec/mspec/spec/matchers/be_an_instance_of_spec.rb b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb new file mode 100644 index 0000000000..7f2126df7d --- /dev/null +++ b/spec/mspec/spec/matchers/be_an_instance_of_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +module BeAnInOfSpecs + class A + end + + class B < A + end + + class C < B + end +end + +describe BeAnInstanceOfMatcher do + it "matches when actual is an instance_of? expected" do + a = BeAnInOfSpecs::A.new + BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(a).should be_true + + b = BeAnInOfSpecs::B.new + BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(b).should be_true + end + + it "does not match when actual is not an instance_of? expected" do + a = BeAnInOfSpecs::A.new + BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(a).should be_false + + b = BeAnInOfSpecs::B.new + BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(b).should be_false + + c = BeAnInOfSpecs::C.new + BeAnInstanceOfMatcher.new(BeAnInOfSpecs::A).matches?(c).should be_false + BeAnInstanceOfMatcher.new(BeAnInOfSpecs::B).matches?(c).should be_false + end + + it "provides a useful failure message" do + matcher = BeAnInstanceOfMatcher.new(Numeric) + matcher.matches?("string") + matcher.failure_message.should == [ + "Expected \"string\" (String)", "to be an instance of Numeric"] + end + + it "provides a useful negative failure message" do + matcher = BeAnInstanceOfMatcher.new(Numeric) + matcher.matches?(4.0) + matcher.negative_failure_message.should == [ + "Expected 4.0 (Float)", "not to be an instance of Numeric"] + end +end diff --git a/spec/mspec/spec/matchers/be_ancestor_of_spec.rb b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb new file mode 100644 index 0000000000..c6bd1a26c7 --- /dev/null +++ b/spec/mspec/spec/matchers/be_ancestor_of_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class Parent; end +class Child < Parent; end + +describe BeAncestorOfMatcher do + it "matches when actual is an ancestor of expected" do + BeAncestorOfMatcher.new(Child).matches?(Parent).should == true + end + + it "does not match when actual is not an ancestor of expected" do + BeAncestorOfMatcher.new(Parent).matches?(Child).should == false + end + + it "provides a useful failure message" do + matcher = BeAncestorOfMatcher.new(Parent) + matcher.matches?(Child) + matcher.failure_message.should == ["Expected Child", "to be an ancestor of Parent"] + end + + it "provides a useful negative failure message" do + matcher = BeAncestorOfMatcher.new(Child) + matcher.matches?(Parent) + matcher.negative_failure_message.should == ["Expected Parent", "not to be an ancestor of Child"] + end +end diff --git a/spec/mspec/spec/matchers/be_close_spec.rb b/spec/mspec/spec/matchers/be_close_spec.rb new file mode 100644 index 0000000000..3ced61dc7a --- /dev/null +++ b/spec/mspec/spec/matchers/be_close_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +# Adapted from RSpec 1.0.8 +describe BeCloseMatcher do + it "matches when actual == expected" do + BeCloseMatcher.new(5.0, 0.5).matches?(5.0).should == true + end + + it "matches when actual < (expected + tolerance)" do + BeCloseMatcher.new(5.0, 0.5).matches?(5.49).should == true + end + + it "matches when actual > (expected - tolerance)" do + BeCloseMatcher.new(5.0, 0.5).matches?(4.51).should == true + end + + it "does not match when actual == (expected + tolerance)" do + BeCloseMatcher.new(5.0, 0.5).matches?(5.5).should == false + end + + it "does not match when actual == (expected - tolerance)" do + BeCloseMatcher.new(5.0, 0.5).matches?(4.5).should == false + end + + it "does not match when actual < (expected - tolerance)" do + BeCloseMatcher.new(5.0, 0.5).matches?(4.49).should == false + end + + it "does not match when actual > (expected + tolerance)" do + BeCloseMatcher.new(5.0, 0.5).matches?(5.51).should == false + end + + it "provides a useful failure message" do + matcher = BeCloseMatcher.new(5.0, 0.5) + matcher.matches?(5.5) + matcher.failure_message.should == ["Expected 5.0", "to be within +/- 0.5 of 5.5"] + end + + it "provides a useful negative failure message" do + matcher = BeCloseMatcher.new(5.0, 0.5) + matcher.matches?(5.0) + matcher.negative_failure_message.should == ["Expected 5.0", "not to be within +/- 0.5 of 5.0"] + end +end diff --git a/spec/mspec/spec/matchers/be_computed_by_spec.rb b/spec/mspec/spec/matchers/be_computed_by_spec.rb new file mode 100644 index 0000000000..9833e211a4 --- /dev/null +++ b/spec/mspec/spec/matchers/be_computed_by_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' +require 'mspec/matchers' + +describe BeComputedByMatcher do + it "matches when all entries in the Array compute" do + array = [ [65, "A"], + [90, "Z"] ] + BeComputedByMatcher.new(:chr).matches?(array).should be_true + end + + it "matches when all entries in the Array with arguments compute" do + array = [ [1, 2, 3], + [2, 4, 6] ] + BeComputedByMatcher.new(:+).matches?(array).should be_true + end + + it "does not match when any entry in the Array does not compute" do + array = [ [65, "A" ], + [91, "Z" ] ] + BeComputedByMatcher.new(:chr).matches?(array).should be_false + end + + it "accepts an argument list to apply to each method call" do + array = [ [65, "1000001" ], + [90, "1011010" ] ] + BeComputedByMatcher.new(:to_s, 2).matches?(array).should be_true + end + + it "does not match when any entry in the Array with arguments does not compute" do + array = [ [1, 2, 3], + [2, 4, 7] ] + BeComputedByMatcher.new(:+).matches?(array).should be_false + end + + it "provides a useful failure message" do + array = [ [65, "A" ], + [91, "Z" ] ] + matcher = BeComputedByMatcher.new(:chr) + matcher.matches?(array) + matcher.failure_message.should == ["Expected \"Z\"", "to be computed by 91.chr (computed \"[\" instead)"] + end +end diff --git a/spec/mspec/spec/matchers/be_empty_spec.rb b/spec/mspec/spec/matchers/be_empty_spec.rb new file mode 100644 index 0000000000..cb8663f5ee --- /dev/null +++ b/spec/mspec/spec/matchers/be_empty_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe BeEmptyMatcher do + it "matches when actual is empty" do + BeEmptyMatcher.new.matches?("").should == true + end + + it "does not match when actual is not empty" do + BeEmptyMatcher.new.matches?([10]).should == false + end + + it "provides a useful failure message" do + matcher = BeEmptyMatcher.new + matcher.matches?("not empty string") + matcher.failure_message.should == ["Expected \"not empty string\"", "to be empty"] + end + + it "provides a useful negative failure message" do + matcher = BeEmptyMatcher.new + matcher.matches?("") + matcher.negative_failure_message.should == ["Expected \"\"", "not to be empty"] + end +end + diff --git a/spec/mspec/spec/matchers/be_false_spec.rb b/spec/mspec/spec/matchers/be_false_spec.rb new file mode 100644 index 0000000000..31afd24ebc --- /dev/null +++ b/spec/mspec/spec/matchers/be_false_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe BeFalseMatcher do + it "matches when actual is false" do + BeFalseMatcher.new.matches?(false).should == true + end + + it "does not match when actual is not false" do + BeFalseMatcher.new.matches?("").should == false + BeFalseMatcher.new.matches?(true).should == false + BeFalseMatcher.new.matches?(nil).should == false + BeFalseMatcher.new.matches?(0).should == false + end + + it "provides a useful failure message" do + matcher = BeFalseMatcher.new + matcher.matches?("some string") + matcher.failure_message.should == ["Expected \"some string\"", "to be false"] + end + + it "provides a useful negative failure message" do + matcher = BeFalseMatcher.new + matcher.matches?(false) + matcher.negative_failure_message.should == ["Expected false", "not to be false"] + end +end diff --git a/spec/mspec/spec/matchers/be_kind_of_spec.rb b/spec/mspec/spec/matchers/be_kind_of_spec.rb new file mode 100644 index 0000000000..554ae6aa82 --- /dev/null +++ b/spec/mspec/spec/matchers/be_kind_of_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe BeKindOfMatcher do + it "matches when actual is a kind_of? expected" do + BeKindOfMatcher.new(Integer).matches?(1).should == true + BeKindOfMatcher.new(Fixnum).matches?(2).should == true + BeKindOfMatcher.new(Regexp).matches?(/m/).should == true + end + + it "does not match when actual is not a kind_of? expected" do + BeKindOfMatcher.new(Integer).matches?(1.5).should == false + BeKindOfMatcher.new(String).matches?(:a).should == false + BeKindOfMatcher.new(Hash).matches?([]).should == false + end + + it "provides a useful failure message" do + matcher = BeKindOfMatcher.new(Numeric) + matcher.matches?('string') + matcher.failure_message.should == [ + "Expected \"string\" (String)", "to be kind of Numeric"] + end + + it "provides a useful negative failure message" do + matcher = BeKindOfMatcher.new(Numeric) + matcher.matches?(4.0) + matcher.negative_failure_message.should == [ + "Expected 4.0 (Float)", "not to be kind of Numeric"] + end +end diff --git a/spec/mspec/spec/matchers/be_nan_spec.rb b/spec/mspec/spec/matchers/be_nan_spec.rb new file mode 100644 index 0000000000..2062763a92 --- /dev/null +++ b/spec/mspec/spec/matchers/be_nan_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/guards' +require 'mspec/helpers' +require 'mspec/matchers' + +describe BeNaNMatcher do + it "matches when actual is NaN" do + BeNaNMatcher.new.matches?(nan_value).should == true + end + + it "does not match when actual is not NaN" do + BeNaNMatcher.new.matches?(1.0).should == false + BeNaNMatcher.new.matches?(0).should == false + end + + it "provides a useful failure message" do + matcher = BeNaNMatcher.new + matcher.matches?(0) + matcher.failure_message.should == ["Expected 0", "to be NaN"] + end + + it "provides a useful negative failure message" do + matcher = BeNaNMatcher.new + matcher.matches?(nan_value) + matcher.negative_failure_message.should == ["Expected NaN", "not to be NaN"] + end +end diff --git a/spec/mspec/spec/matchers/be_nil_spec.rb b/spec/mspec/spec/matchers/be_nil_spec.rb new file mode 100644 index 0000000000..6551feb5de --- /dev/null +++ b/spec/mspec/spec/matchers/be_nil_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe BeNilMatcher do + it "matches when actual is nil" do + BeNilMatcher.new.matches?(nil).should == true + end + + it "does not match when actual is not nil" do + BeNilMatcher.new.matches?("").should == false + BeNilMatcher.new.matches?(false).should == false + BeNilMatcher.new.matches?(0).should == false + end + + it "provides a useful failure message" do + matcher = BeNilMatcher.new + matcher.matches?("some string") + matcher.failure_message.should == ["Expected \"some string\"", "to be nil"] + end + + it "provides a useful negative failure message" do + matcher = BeNilMatcher.new + matcher.matches?(nil) + matcher.negative_failure_message.should == ["Expected nil", "not to be nil"] + end +end diff --git a/spec/mspec/spec/matchers/be_true_or_false_spec.rb b/spec/mspec/spec/matchers/be_true_or_false_spec.rb new file mode 100644 index 0000000000..3edffcb1b1 --- /dev/null +++ b/spec/mspec/spec/matchers/be_true_or_false_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe BeTrueOrFalseMatcher do + it "matches when actual is true" do + BeTrueOrFalseMatcher.new.matches?(true).should == true + end + + it "matches when actual is false" do + BeTrueOrFalseMatcher.new.matches?(false).should == true + end + + it "provides a useful failure message" do + matcher = BeTrueOrFalseMatcher.new + matcher.matches?("some string") + matcher.failure_message.should == ["Expected \"some string\"", "to be true or false"] + end +end diff --git a/spec/mspec/spec/matchers/be_true_spec.rb b/spec/mspec/spec/matchers/be_true_spec.rb new file mode 100644 index 0000000000..90c89b3911 --- /dev/null +++ b/spec/mspec/spec/matchers/be_true_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe BeTrueMatcher do + it "matches when actual is true" do + BeTrueMatcher.new.matches?(true).should == true + end + + it "does not match when actual is not true" do + BeTrueMatcher.new.matches?("").should == false + BeTrueMatcher.new.matches?(false).should == false + BeTrueMatcher.new.matches?(nil).should == false + BeTrueMatcher.new.matches?(0).should == false + end + + it "provides a useful failure message" do + matcher = BeTrueMatcher.new + matcher.matches?("some string") + matcher.failure_message.should == ["Expected \"some string\"", "to be true"] + end + + it "provides a useful negative failure message" do + matcher = BeTrueMatcher.new + matcher.matches?(true) + matcher.negative_failure_message.should == ["Expected true", "not to be true"] + end +end diff --git a/spec/mspec/spec/matchers/block_caller_spec.rb b/spec/mspec/spec/matchers/block_caller_spec.rb new file mode 100644 index 0000000000..d6793b9779 --- /dev/null +++ b/spec/mspec/spec/matchers/block_caller_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe BlockingMatcher do + it 'matches when a Proc blocks the caller' do + BlockingMatcher.new.matches?(proc { sleep }).should == true + end + + it 'does not match when a Proc does not block the caller' do + BlockingMatcher.new.matches?(proc { 1 }).should == false + end +end diff --git a/spec/mspec/spec/matchers/complain_spec.rb b/spec/mspec/spec/matchers/complain_spec.rb new file mode 100644 index 0000000000..709b57be6c --- /dev/null +++ b/spec/mspec/spec/matchers/complain_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe ComplainMatcher do + it "matches when executing the proc results in output to $stderr" do + proc = lambda { warn "I'm gonna tell yo mama" } + ComplainMatcher.new(nil).matches?(proc).should == true + end + + it "maches when executing the proc results in the expected output to $stderr" do + proc = lambda { warn "Que haces?" } + ComplainMatcher.new("Que haces?\n").matches?(proc).should == true + ComplainMatcher.new("Que pasa?\n").matches?(proc).should == false + ComplainMatcher.new(/Que/).matches?(proc).should == true + ComplainMatcher.new(/Quoi/).matches?(proc).should == false + end + + it "does not match when there is no output to $stderr" do + ComplainMatcher.new(nil).matches?(lambda {}).should == false + end + + it "provides a useful failure message" do + matcher = ComplainMatcher.new(nil) + matcher.matches?(lambda { }) + matcher.failure_message.should == ["Expected a warning", "but received none"] + matcher = ComplainMatcher.new("listen here") + matcher.matches?(lambda { warn "look out" }) + matcher.failure_message.should == + ["Expected warning: \"listen here\"", "but got: \"look out\""] + matcher = ComplainMatcher.new(/talk/) + matcher.matches?(lambda { warn "listen up" }) + matcher.failure_message.should == + ["Expected warning to match: /talk/", "but got: \"listen up\""] + end + + it "provides a useful negative failure message" do + proc = lambda { warn "ouch" } + matcher = ComplainMatcher.new(nil) + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Unexpected warning: ", "\"ouch\""] + matcher = ComplainMatcher.new("ouchy") + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected warning: \"ouchy\"", "but got: \"ouch\""] + matcher = ComplainMatcher.new(/ou/) + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected warning not to match: /ou/", "but got: \"ouch\""] + end +end diff --git a/spec/mspec/spec/matchers/eql_spec.rb b/spec/mspec/spec/matchers/eql_spec.rb new file mode 100644 index 0000000000..711ebdb679 --- /dev/null +++ b/spec/mspec/spec/matchers/eql_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe EqlMatcher do + it "matches when actual is eql? to expected" do + EqlMatcher.new(1).matches?(1).should == true + EqlMatcher.new(1.5).matches?(1.5).should == true + EqlMatcher.new("red").matches?("red").should == true + EqlMatcher.new(:blue).matches?(:blue).should == true + EqlMatcher.new(Object).matches?(Object).should == true + + o = Object.new + EqlMatcher.new(o).matches?(o).should == true + end + + it "does not match when actual is not eql? to expected" do + EqlMatcher.new(1).matches?(1.0).should == false + EqlMatcher.new(Hash).matches?(Object).should == false + end + + it "provides a useful failure message" do + matcher = EqlMatcher.new("red") + matcher.matches?("red") + matcher.failure_message.should == ["Expected \"red\"\n", "to have same value and type as \"red\"\n"] + end + + it "provides a useful negative failure message" do + matcher = EqlMatcher.new(1) + matcher.matches?(1.0) + matcher.negative_failure_message.should == ["Expected 1.0\n", "not to have same value or type as 1\n"] + end +end diff --git a/spec/mspec/spec/matchers/equal_element_spec.rb b/spec/mspec/spec/matchers/equal_element_spec.rb new file mode 100644 index 0000000000..45b8390364 --- /dev/null +++ b/spec/mspec/spec/matchers/equal_element_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe EqualElementMatcher do + it "matches if it finds an element with the passed name, no matter what attributes/content" do + EqualElementMatcher.new("A").matches?('').should be_true + EqualElementMatcher.new("A").matches?('').should be_true + EqualElementMatcher.new("A").matches?('').should be_true + + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + EqualElementMatcher.new("BASE").matches?('').should be_false + end + + it "matches if it finds an element with the passed name and the passed attributes" do + EqualElementMatcher.new("A", {}).matches?('').should be_true + EqualElementMatcher.new("A", nil).matches?('').should be_true + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').should be_true + + EqualElementMatcher.new("A", {}).matches?('').should be_false + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').should be_false + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').should be_false + EqualElementMatcher.new("A", "HREF" => "http://example.com").matches?('').should be_false + end + + it "matches if it finds an element with the passed name, the passed attributes and the passed content" do + EqualElementMatcher.new("A", {}, "").matches?('').should be_true + EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('Example').should be_true + + EqualElementMatcher.new("A", {}, "Test").matches?('').should be_false + EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('').should be_false + EqualElementMatcher.new("A", {"HREF" => "http://example.com"}, "Example").matches?('Test').should be_false + end + + it "can match unclosed elements" do + EqualElementMatcher.new("BASE", nil, nil, :not_closed => true).matches?('').should be_true + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, nil, :not_closed => true).matches?('').should be_true + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Example", :not_closed => true).matches?('Example').should be_true + + EqualElementMatcher.new("BASE", {}, nil, :not_closed => true).matches?('').should be_false + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "", :not_closed => true).matches?('Example').should be_false + EqualElementMatcher.new("BASE", {"HREF" => "http://example.com"}, "Test", :not_closed => true).matches?('Example').should be_false + end + + it "provides a useful failure message" do + equal_element = EqualElementMatcher.new("A", {}, "Test") + equal_element.matches?('').should be_false + equal_element.failure_message.should == [%{Expected ""\n}, %{to be a 'A' element with no attributes and "Test" as content}] + + equal_element = EqualElementMatcher.new("A", {}, "") + equal_element.matches?('Test').should be_false + equal_element.failure_message.should == [%{Expected "Test"\n}, %{to be a 'A' element with no attributes and no content}] + + equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com") + equal_element.matches?('Test').should be_false + equal_element.failure_message.should == [%{Expected "Test"\n}, %{to be a 'A' element with HREF="http://www.example.com" and any content}] + end + + it "provides a useful negative failure message" do + equal_element = EqualElementMatcher.new("A", {}, "Test") + equal_element.matches?('').should be_false + equal_element.negative_failure_message.should == [%{Expected ""\n}, %{not to be a 'A' element with no attributes and "Test" as content}] + + equal_element = EqualElementMatcher.new("A", {}, "") + equal_element.matches?('Test').should be_false + equal_element.negative_failure_message.should == [%{Expected "Test"\n}, %{not to be a 'A' element with no attributes and no content}] + + equal_element = EqualElementMatcher.new("A", "HREF" => "http://www.example.com") + equal_element.matches?('Test').should be_false + equal_element.negative_failure_message.should == [%{Expected "Test"\n}, %{not to be a 'A' element with HREF="http://www.example.com" and any content}] + end +end diff --git a/spec/mspec/spec/matchers/equal_spec.rb b/spec/mspec/spec/matchers/equal_spec.rb new file mode 100644 index 0000000000..ca7bf83fdd --- /dev/null +++ b/spec/mspec/spec/matchers/equal_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe EqualMatcher do + it "matches when actual is equal? to expected" do + EqualMatcher.new(1).matches?(1).should == true + EqualMatcher.new(:blue).matches?(:blue).should == true + EqualMatcher.new(Object).matches?(Object).should == true + + o = Object.new + EqualMatcher.new(o).matches?(o).should == true + end + + it "does not match when actual is not a equal? to expected" do + EqualMatcher.new(1).matches?(1.0).should == false + EqualMatcher.new("blue").matches?("blue").should == false + EqualMatcher.new(Hash).matches?(Object).should == false + end + + it "provides a useful failure message" do + matcher = EqualMatcher.new("red") + matcher.matches?("red") + matcher.failure_message.should == ["Expected \"red\"\n", "to be identical to \"red\"\n"] + end + + it "provides a useful negative failure message" do + matcher = EqualMatcher.new(1) + matcher.matches?(1) + matcher.negative_failure_message.should == ["Expected 1\n", "not to be identical to 1\n"] + end +end diff --git a/spec/mspec/spec/matchers/have_class_variable_spec.rb b/spec/mspec/spec/matchers/have_class_variable_spec.rb new file mode 100644 index 0000000000..e440050056 --- /dev/null +++ b/spec/mspec/spec/matchers/have_class_variable_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class IVarModMock; end + +shared_examples_for "have_class_variable, on all Ruby versions" do + after :all do + Object.const_set :RUBY_VERSION, @ruby_version + end + + it "matches when mod has the class variable, given as string" do + matcher = HaveClassVariableMatcher.new('@foo') + matcher.matches?(IVarModMock).should be_true + end + + it "matches when mod has the class variable, given as symbol" do + matcher = HaveClassVariableMatcher.new(:@foo) + matcher.matches?(IVarModMock).should be_true + end + + it "does not match when mod hasn't got the class variable, given as string" do + matcher = HaveClassVariableMatcher.new('@bar') + matcher.matches?(IVarModMock).should be_false + end + + it "does not match when mod hasn't got the class variable, given as symbol" do + matcher = HaveClassVariableMatcher.new(:@bar) + matcher.matches?(IVarModMock).should be_false + end + + it "provides a failure message for #should" do + matcher = HaveClassVariableMatcher.new(:@bar) + matcher.matches?(IVarModMock) + matcher.failure_message.should == [ + "Expected IVarModMock to have class variable '@bar'", + "but it does not" + ] + end + + it "provides a failure messoge for #should_not" do + matcher = HaveClassVariableMatcher.new(:@bar) + matcher.matches?(IVarModMock) + matcher.negative_failure_message.should == [ + "Expected IVarModMock NOT to have class variable '@bar'", + "but it does" + ] + end +end + +describe HaveClassVariableMatcher, "on RUBY_VERSION >= 1.9" do + before :all do + @ruby_version = Object.const_get :RUBY_VERSION + Object.const_set :RUBY_VERSION, '1.9.0' + + def IVarModMock.class_variables + [:@foo] + end + end + + it_should_behave_like "have_class_variable, on all Ruby versions" +end diff --git a/spec/mspec/spec/matchers/have_constant_spec.rb b/spec/mspec/spec/matchers/have_constant_spec.rb new file mode 100644 index 0000000000..20c5f161d4 --- /dev/null +++ b/spec/mspec/spec/matchers/have_constant_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HCMSpecs + X = :x +end + +describe HaveConstantMatcher do + it "matches when mod has the constant" do + matcher = HaveConstantMatcher.new :X + matcher.matches?(HCMSpecs).should be_true + end + + it "does not match when mod does not have the constant" do + matcher = HaveConstantMatcher.new :A + matcher.matches?(HCMSpecs).should be_false + end + + it "provides a failure message for #should" do + matcher = HaveConstantMatcher.new :A + matcher.matches?(HCMSpecs) + matcher.failure_message.should == [ + "Expected HCMSpecs to have constant 'A'", + "but it does not" + ] + end + + it "provides a failure messoge for #should_not" do + matcher = HaveConstantMatcher.new :X + matcher.matches?(HCMSpecs) + matcher.negative_failure_message.should == [ + "Expected HCMSpecs NOT to have constant 'X'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/have_instance_method_spec.rb b/spec/mspec/spec/matchers/have_instance_method_spec.rb new file mode 100644 index 0000000000..738f5f875d --- /dev/null +++ b/spec/mspec/spec/matchers/have_instance_method_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HIMMSpecs + def instance_method + end + + class Subclass < HIMMSpecs + def instance_sub_method + end + end +end + +describe HaveInstanceMethodMatcher do + it "inherits from MethodMatcher" do + HaveInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher) + end + + it "matches when mod has the instance method" do + matcher = HaveInstanceMethodMatcher.new :instance_method + matcher.matches?(HIMMSpecs).should be_true + matcher.matches?(HIMMSpecs::Subclass).should be_true + end + + it "does not match when mod does not have the instance method" do + matcher = HaveInstanceMethodMatcher.new :another_method + matcher.matches?(HIMMSpecs).should be_false + end + + it "does not match if the method is in a superclass and include_super is false" do + matcher = HaveInstanceMethodMatcher.new :instance_method, false + matcher.matches?(HIMMSpecs::Subclass).should be_false + end + + it "provides a failure message for #should" do + matcher = HaveInstanceMethodMatcher.new :some_method + matcher.matches?(HIMMSpecs) + matcher.failure_message.should == [ + "Expected HIMMSpecs to have instance method 'some_method'", + "but it does not" + ] + end + + it "provides a failure messoge for #should_not" do + matcher = HaveInstanceMethodMatcher.new :some_method + matcher.matches?(HIMMSpecs) + matcher.negative_failure_message.should == [ + "Expected HIMMSpecs NOT to have instance method 'some_method'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/have_instance_variable_spec.rb b/spec/mspec/spec/matchers/have_instance_variable_spec.rb new file mode 100644 index 0000000000..ababb38bc7 --- /dev/null +++ b/spec/mspec/spec/matchers/have_instance_variable_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +shared_examples_for "have_instance_variable, on all Ruby versions" do + after :all do + Object.const_set :RUBY_VERSION, @ruby_version + end + + it "matches when object has the instance variable, given as string" do + matcher = HaveInstanceVariableMatcher.new('@foo') + matcher.matches?(@object).should be_true + end + + it "matches when object has the instance variable, given as symbol" do + matcher = HaveInstanceVariableMatcher.new(:@foo) + matcher.matches?(@object).should be_true + end + + it "does not match when object hasn't got the instance variable, given as string" do + matcher = HaveInstanceVariableMatcher.new('@bar') + matcher.matches?(@object).should be_false + end + + it "does not match when object hasn't got the instance variable, given as symbol" do + matcher = HaveInstanceVariableMatcher.new(:@bar) + matcher.matches?(@object).should be_false + end + + it "provides a failure message for #should" do + matcher = HaveInstanceVariableMatcher.new(:@bar) + matcher.matches?(@object) + matcher.failure_message.should == [ + "Expected #{@object.inspect} to have instance variable '@bar'", + "but it does not" + ] + end + + it "provides a failure messoge for #should_not" do + matcher = HaveInstanceVariableMatcher.new(:@bar) + matcher.matches?(@object) + matcher.negative_failure_message.should == [ + "Expected #{@object.inspect} NOT to have instance variable '@bar'", + "but it does" + ] + end +end + +describe HaveInstanceVariableMatcher, "on RUBY_VERSION >= 1.9" do + before :all do + @ruby_version = Object.const_get :RUBY_VERSION + Object.const_set :RUBY_VERSION, '1.9.0' + + @object = Object.new + def @object.instance_variables + [:@foo] + end + end + + it_should_behave_like "have_instance_variable, on all Ruby versions" +end diff --git a/spec/mspec/spec/matchers/have_method_spec.rb b/spec/mspec/spec/matchers/have_method_spec.rb new file mode 100644 index 0000000000..41bd485119 --- /dev/null +++ b/spec/mspec/spec/matchers/have_method_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HMMSpecs + def instance_method + end + + class Subclass < HMMSpecs + def instance_sub_method + end + end +end + +describe HaveMethodMatcher do + it "inherits from MethodMatcher" do + HaveMethodMatcher.new(:m).should be_kind_of(MethodMatcher) + end + + it "matches when mod has the method" do + matcher = HaveMethodMatcher.new :instance_method + matcher.matches?(HMMSpecs).should be_true + matcher.matches?(HMMSpecs.new).should be_true + matcher.matches?(HMMSpecs::Subclass).should be_true + matcher.matches?(HMMSpecs::Subclass.new).should be_true + end + + it "does not match when mod does not have the method" do + matcher = HaveMethodMatcher.new :another_method + matcher.matches?(HMMSpecs).should be_false + end + + it "does not match if the method is in a superclass and include_super is false" do + matcher = HaveMethodMatcher.new :instance_method, false + matcher.matches?(HMMSpecs::Subclass).should be_false + end + + it "provides a failure message for #should" do + matcher = HaveMethodMatcher.new :some_method + matcher.matches?(HMMSpecs) + matcher.failure_message.should == [ + "Expected HMMSpecs to have method 'some_method'", + "but it does not" + ] + end + + it "provides a failure messoge for #should_not" do + matcher = HaveMethodMatcher.new :some_method + matcher.matches?(HMMSpecs) + matcher.negative_failure_message.should == [ + "Expected HMMSpecs NOT to have method 'some_method'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/have_private_instance_method_spec.rb b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb new file mode 100644 index 0000000000..827c6b6034 --- /dev/null +++ b/spec/mspec/spec/matchers/have_private_instance_method_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HPIMMSpecs + private + + def private_method + end + + class Subclass < HPIMMSpecs + private + + def private_sub_method + end + end +end + +describe HavePrivateInstanceMethodMatcher do + it "inherits from MethodMatcher" do + HavePrivateInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher) + end + + it "matches when mod has the private instance method" do + matcher = HavePrivateInstanceMethodMatcher.new :private_method + matcher.matches?(HPIMMSpecs).should be_true + matcher.matches?(HPIMMSpecs::Subclass).should be_true + end + + it "does not match when mod does not have the private instance method" do + matcher = HavePrivateInstanceMethodMatcher.new :another_method + matcher.matches?(HPIMMSpecs).should be_false + end + + it "does not match if the method is in a superclass and include_super is false" do + matcher = HavePrivateInstanceMethodMatcher.new :private_method, false + matcher.matches?(HPIMMSpecs::Subclass).should be_false + end + + it "provides a failure message for #should" do + matcher = HavePrivateInstanceMethodMatcher.new :some_method + matcher.matches?(HPIMMSpecs) + matcher.failure_message.should == [ + "Expected HPIMMSpecs to have private instance method 'some_method'", + "but it does not" + ] + end + + it "provides a failure message for #should_not" do + matcher = HavePrivateInstanceMethodMatcher.new :some_method + matcher.matches?(HPIMMSpecs) + matcher.negative_failure_message.should == [ + "Expected HPIMMSpecs NOT to have private instance method 'some_method'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/have_private_method_spec.rb b/spec/mspec/spec/matchers/have_private_method_spec.rb new file mode 100644 index 0000000000..e63a9a3c2f --- /dev/null +++ b/spec/mspec/spec/matchers/have_private_method_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HPMMSpecs + def self.private_method + end + + private_class_method :private_method +end + +describe HavePrivateMethodMatcher do + it "inherits from MethodMatcher" do + HavePrivateMethodMatcher.new(:m).should be_kind_of(MethodMatcher) + end + + it "matches when mod has the private method" do + matcher = HavePrivateMethodMatcher.new :private_method + matcher.matches?(HPMMSpecs).should be_true + end + + it "does not match when mod does not have the private method" do + matcher = HavePrivateMethodMatcher.new :another_method + matcher.matches?(HPMMSpecs).should be_false + end + + it "provides a failure message for #should" do + matcher = HavePrivateMethodMatcher.new :some_method + matcher.matches?(HPMMSpecs) + matcher.failure_message.should == [ + "Expected HPMMSpecs to have private method 'some_method'", + "but it does not" + ] + end + + it "provides a failure message for #should_not" do + matcher = HavePrivateMethodMatcher.new :private_method + matcher.matches?(HPMMSpecs) + matcher.negative_failure_message.should == [ + "Expected HPMMSpecs NOT to have private method 'private_method'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb new file mode 100644 index 0000000000..460d0368fb --- /dev/null +++ b/spec/mspec/spec/matchers/have_protected_instance_method_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HPIMMSpecs + protected + + def protected_method + end + + class Subclass < HPIMMSpecs + protected + + def protected_sub_method + end + end +end + +describe HaveProtectedInstanceMethodMatcher do + it "inherits from MethodMatcher" do + HaveProtectedInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher) + end + + it "matches when mod has the protected instance method" do + matcher = HaveProtectedInstanceMethodMatcher.new :protected_method + matcher.matches?(HPIMMSpecs).should be_true + matcher.matches?(HPIMMSpecs::Subclass).should be_true + end + + it "does not match when mod does not have the protected instance method" do + matcher = HaveProtectedInstanceMethodMatcher.new :another_method + matcher.matches?(HPIMMSpecs).should be_false + end + + it "does not match if the method is in a superclass and include_super is false" do + matcher = HaveProtectedInstanceMethodMatcher.new :protected_method, false + matcher.matches?(HPIMMSpecs::Subclass).should be_false + end + + it "provides a failure message for #should" do + matcher = HaveProtectedInstanceMethodMatcher.new :some_method + matcher.matches?(HPIMMSpecs) + matcher.failure_message.should == [ + "Expected HPIMMSpecs to have protected instance method 'some_method'", + "but it does not" + ] + end + + it "provides a failure messoge for #should_not" do + matcher = HaveProtectedInstanceMethodMatcher.new :some_method + matcher.matches?(HPIMMSpecs) + matcher.negative_failure_message.should == [ + "Expected HPIMMSpecs NOT to have protected instance method 'some_method'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/have_public_instance_method_spec.rb b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb new file mode 100644 index 0000000000..bff1046f04 --- /dev/null +++ b/spec/mspec/spec/matchers/have_public_instance_method_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HPIMMSpecs + def public_method + end + + class Subclass < HPIMMSpecs + def public_sub_method + end + end +end + +describe HavePublicInstanceMethodMatcher do + it "inherits from MethodMatcher" do + HavePublicInstanceMethodMatcher.new(:m).should be_kind_of(MethodMatcher) + end + + it "matches when mod has the public instance method" do + matcher = HavePublicInstanceMethodMatcher.new :public_method + matcher.matches?(HPIMMSpecs).should be_true + matcher.matches?(HPIMMSpecs::Subclass).should be_true + end + + it "does not match when mod does not have the public instance method" do + matcher = HavePublicInstanceMethodMatcher.new :another_method + matcher.matches?(HPIMMSpecs).should be_false + end + + it "does not match if the method is in a superclass and include_super is false" do + matcher = HavePublicInstanceMethodMatcher.new :public_method, false + matcher.matches?(HPIMMSpecs::Subclass).should be_false + end + + it "provides a failure message for #should" do + matcher = HavePublicInstanceMethodMatcher.new :some_method + matcher.matches?(HPIMMSpecs) + matcher.failure_message.should == [ + "Expected HPIMMSpecs to have public instance method 'some_method'", + "but it does not" + ] + end + + it "provides a failure messoge for #should_not" do + matcher = HavePublicInstanceMethodMatcher.new :some_method + matcher.matches?(HPIMMSpecs) + matcher.negative_failure_message.should == [ + "Expected HPIMMSpecs NOT to have public instance method 'some_method'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/have_singleton_method_spec.rb b/spec/mspec/spec/matchers/have_singleton_method_spec.rb new file mode 100644 index 0000000000..57c37e01d9 --- /dev/null +++ b/spec/mspec/spec/matchers/have_singleton_method_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class HSMMSpecs + def self.singleton_method + end +end + +describe HaveSingletonMethodMatcher do + it "inherits from MethodMatcher" do + HaveSingletonMethodMatcher.new(:m).should be_kind_of(MethodMatcher) + end + + it "matches when the class has a singleton method" do + matcher = HaveSingletonMethodMatcher.new :singleton_method + matcher.matches?(HSMMSpecs).should be_true + end + + it "matches when the object has a singleton method" do + obj = double("HSMMSpecs") + def obj.singleton_method; end + + matcher = HaveSingletonMethodMatcher.new :singleton_method + matcher.matches?(obj).should be_true + end + + it "provides a failure message for #should" do + matcher = HaveSingletonMethodMatcher.new :some_method + matcher.matches?(HSMMSpecs) + matcher.failure_message.should == [ + "Expected HSMMSpecs to have singleton method 'some_method'", + "but it does not" + ] + end + + it "provides a failure message for #should_not" do + matcher = HaveSingletonMethodMatcher.new :singleton_method + matcher.matches?(HSMMSpecs) + matcher.negative_failure_message.should == [ + "Expected HSMMSpecs NOT to have singleton method 'singleton_method'", + "but it does" + ] + end +end diff --git a/spec/mspec/spec/matchers/include_spec.rb b/spec/mspec/spec/matchers/include_spec.rb new file mode 100644 index 0000000000..f045c5e0cb --- /dev/null +++ b/spec/mspec/spec/matchers/include_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe IncludeMatcher do + it "matches when actual includes expected" do + IncludeMatcher.new(2).matches?([1,2,3]).should == true + IncludeMatcher.new("b").matches?("abc").should == true + end + + it "does not match when actual does not include expected" do + IncludeMatcher.new(4).matches?([1,2,3]).should == false + IncludeMatcher.new("d").matches?("abc").should == false + end + + it "matches when actual includes all expected" do + IncludeMatcher.new(3, 2, 1).matches?([1,2,3]).should == true + IncludeMatcher.new("a", "b", "c").matches?("abc").should == true + end + + it "does not match when actual does not include all expected" do + IncludeMatcher.new(3, 2, 4).matches?([1,2,3]).should == false + IncludeMatcher.new("a", "b", "c", "d").matches?("abc").should == false + end + + it "provides a useful failure message" do + matcher = IncludeMatcher.new(5, 2) + matcher.matches?([1,2,3]) + matcher.failure_message.should == ["Expected [1, 2, 3]", "to include 5"] + end + + it "provides a useful negative failure message" do + matcher = IncludeMatcher.new(1, 2, 3) + matcher.matches?([1,2,3]) + matcher.negative_failure_message.should == ["Expected [1, 2, 3]", "not to include 3"] + end +end diff --git a/spec/mspec/spec/matchers/infinity_spec.rb b/spec/mspec/spec/matchers/infinity_spec.rb new file mode 100644 index 0000000000..6eb8ac2940 --- /dev/null +++ b/spec/mspec/spec/matchers/infinity_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/guards' +require 'mspec/helpers' +require 'mspec/matchers' + +describe InfinityMatcher do + it "matches when actual is infinite and has the correct sign" do + InfinityMatcher.new(1).matches?(infinity_value).should == true + InfinityMatcher.new(-1).matches?(-infinity_value).should == true + end + + it "does not match when actual is not infinite" do + InfinityMatcher.new(1).matches?(1.0).should == false + InfinityMatcher.new(-1).matches?(-1.0).should == false + end + + it "does not match when actual is infinite but has the incorrect sign" do + InfinityMatcher.new(1).matches?(-infinity_value).should == false + InfinityMatcher.new(-1).matches?(infinity_value).should == false + end + + it "provides a useful failure message" do + matcher = InfinityMatcher.new(-1) + matcher.matches?(0) + matcher.failure_message.should == ["Expected 0", "to be -Infinity"] + end + + it "provides a useful negative failure message" do + matcher = InfinityMatcher.new(1) + matcher.matches?(infinity_value) + matcher.negative_failure_message.should == ["Expected Infinity", "not to be Infinity"] + end +end diff --git a/spec/mspec/spec/matchers/match_yaml_spec.rb b/spec/mspec/spec/matchers/match_yaml_spec.rb new file mode 100644 index 0000000000..4f16aee0ec --- /dev/null +++ b/spec/mspec/spec/matchers/match_yaml_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe MatchYAMLMatcher do + before :each do + @matcher = MatchYAMLMatcher.new("--- \nfoo: bar\n") + end + + it "compares YAML documents and matches if they're equivalent" do + @matcher.matches?("--- \nfoo: bar\n").should == true + end + + it "compares YAML documents and does not match if they're not equivalent" do + @matcher.matches?("--- \nbar: foo\n").should == false + @matcher.matches?("--- \nfoo: \nbar\n").should == false + end + + it "also receives objects that respond_to to_yaml" do + matcher = MatchYAMLMatcher.new("some string") + matcher.matches?("some string").should == true + + matcher = MatchYAMLMatcher.new(['a', 'b']) + matcher.matches?("--- \n- a\n- b\n").should == true + + matcher = MatchYAMLMatcher.new("foo" => "bar") + matcher.matches?("--- \nfoo: bar\n").should == true + end + + it "matches documents with trailing whitespace" do + @matcher.matches?("--- \nfoo: bar \n").should == true + @matcher.matches?("--- \nfoo: bar \n").should == true + end + + it "fails with a descriptive error message" do + @matcher.matches?("foo").should == false + @matcher.failure_message.should == ["Expected \"foo\"", " to match \"--- \\nfoo: bar\\n\""] + end +end diff --git a/spec/mspec/spec/matchers/output_spec.rb b/spec/mspec/spec/matchers/output_spec.rb new file mode 100644 index 0000000000..264da3b569 --- /dev/null +++ b/spec/mspec/spec/matchers/output_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe OutputMatcher do + it "matches when executing the proc results in the expected output to $stdout" do + proc = Proc.new { puts "bang!" } + OutputMatcher.new("bang!\n", nil).matches?(proc).should == true + OutputMatcher.new("pop", nil).matches?(proc).should == false + OutputMatcher.new(/bang/, nil).matches?(proc).should == true + OutputMatcher.new(/po/, nil).matches?(proc).should == false + end + + it "matches when executing the proc results in the expected output to $stderr" do + proc = Proc.new { $stderr.write "boom!" } + OutputMatcher.new(nil, "boom!").matches?(proc).should == true + OutputMatcher.new(nil, "fizzle").matches?(proc).should == false + OutputMatcher.new(nil, /boom/).matches?(proc).should == true + OutputMatcher.new(nil, /fizzl/).matches?(proc).should == false + end + + it "provides a useful failure message" do + proc = Proc.new { print "unexpected"; $stderr.print "unerror" } + matcher = OutputMatcher.new("expected", "error") + matcher.matches?(proc) + matcher.failure_message.should == + ["Expected:\n $stdout: \"expected\"\n $stderr: \"error\"\n", + " got:\n $stdout: \"unexpected\"\n $stderr: \"unerror\"\n"] + matcher = OutputMatcher.new("expected", nil) + matcher.matches?(proc) + matcher.failure_message.should == + ["Expected:\n $stdout: \"expected\"\n", + " got:\n $stdout: \"unexpected\"\n"] + matcher = OutputMatcher.new(nil, "error") + matcher.matches?(proc) + matcher.failure_message.should == + ["Expected:\n $stderr: \"error\"\n", + " got:\n $stderr: \"unerror\"\n"] + matcher = OutputMatcher.new(/base/, nil) + matcher.matches?(proc) + matcher.failure_message.should == + ["Expected:\n $stdout: /base/\n", + " got:\n $stdout: \"unexpected\"\n"] + matcher = OutputMatcher.new(nil, /octave/) + matcher.matches?(proc) + matcher.failure_message.should == + ["Expected:\n $stderr: /octave/\n", + " got:\n $stderr: \"unerror\"\n"] + end + + it "provides a useful negative failure message" do + proc = Proc.new { puts "expected"; $stderr.puts "error" } + matcher = OutputMatcher.new("expected", "error") + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected output not to be:\n", " $stdout: \"expected\"\n $stderr: \"error\"\n"] + matcher = OutputMatcher.new("expected", nil) + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected output not to be:\n", " $stdout: \"expected\"\n"] + matcher = OutputMatcher.new(nil, "error") + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected output not to be:\n", " $stderr: \"error\"\n"] + matcher = OutputMatcher.new(/expect/, nil) + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected output not to be:\n", " $stdout: \"expected\"\n"] + matcher = OutputMatcher.new(nil, /err/) + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected output not to be:\n", " $stderr: \"error\"\n"] + end +end diff --git a/spec/mspec/spec/matchers/output_to_fd_spec.rb b/spec/mspec/spec/matchers/output_to_fd_spec.rb new file mode 100644 index 0000000000..d35da58829 --- /dev/null +++ b/spec/mspec/spec/matchers/output_to_fd_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe OutputToFDMatcher do + # Figure out how in the hell to achieve this + it "matches when running the block produces the expected output to the given FD" do + output_to_fd("Hi\n", STDERR).matches?(lambda { $stderr.print "Hi\n" }).should == true + end + + it "does not match if running the block does not produce the expected output to the FD" do + output_to_fd("Hi\n", STDERR).matches?(lambda { $stderr.puts("Hello\n") }).should == false + end + + it "propagate the exception if one is thrown while matching" do + exc = RuntimeError.new("propagates") + lambda { + output_to_fd("Hi\n", STDERR).matches?(lambda { + raise exc + }).should == false + }.should raise_error(exc) + end + + it "defaults to matching against STDOUT" do + output_to_fd("Hi\n").matches?(lambda { $stdout.print "Hi\n" }).should == true + end + + it "accepts any IO instance" do + io = IO.new STDOUT.fileno + output_to_fd("Hi\n", io).matches?(lambda { io.print "Hi\n" }).should == true + end + + it "allows matching with a Regexp" do + s = "Hi there\n" + output_to_fd(/Hi/, STDERR).matches?(lambda { $stderr.print s }).should == true + output_to_fd(/Hi?/, STDERR).matches?(lambda { $stderr.print s }).should == true + output_to_fd(/[hH]i?/, STDERR).matches?(lambda { $stderr.print s }).should == true + output_to_fd(/.*/, STDERR).matches?(lambda { $stderr.print s }).should == true + output_to_fd(/H.*?here/, STDERR).matches?(lambda { $stderr.print s }).should == true + output_to_fd(/Ahoy/, STDERR).matches?(lambda { $stderr.print s }).should == false + end +end diff --git a/spec/mspec/spec/matchers/raise_error_spec.rb b/spec/mspec/spec/matchers/raise_error_spec.rb new file mode 100644 index 0000000000..88aab34d53 --- /dev/null +++ b/spec/mspec/spec/matchers/raise_error_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +class ExpectedException < Exception; end +class UnexpectedException < Exception; end + +describe RaiseErrorMatcher do + it "matches when the proc raises the expected exception" do + proc = Proc.new { raise ExpectedException } + matcher = RaiseErrorMatcher.new(ExpectedException, nil) + matcher.matches?(proc).should == true + end + + it "executes it's optional block if matched" do + run = false + proc = Proc.new { raise ExpectedException } + matcher = RaiseErrorMatcher.new(ExpectedException, nil) { |error| + run = true + error.class.should == ExpectedException + } + + matcher.matches?(proc).should == true + run.should == true + end + + it "matches when the proc raises the expected exception with the expected message" do + proc = Proc.new { raise ExpectedException, "message" } + matcher = RaiseErrorMatcher.new(ExpectedException, "message") + matcher.matches?(proc).should == true + end + + it "matches when the proc raises the expected exception with a matching message" do + proc = Proc.new { raise ExpectedException, "some message" } + matcher = RaiseErrorMatcher.new(ExpectedException, /some/) + matcher.matches?(proc).should == true + end + + it "does not match when the proc does not raise the expected exception" do + exc = UnexpectedException.new + matcher = RaiseErrorMatcher.new(ExpectedException, nil) + + matcher.matching_exception?(exc).should == false + lambda { + matcher.matches?(Proc.new { raise exc }) + }.should raise_error(UnexpectedException) + end + + it "does not match when the proc raises the expected exception with an unexpected message" do + exc = ExpectedException.new("unexpected") + matcher = RaiseErrorMatcher.new(ExpectedException, "expected") + + matcher.matching_exception?(exc).should == false + lambda { + matcher.matches?(Proc.new { raise exc }) + }.should raise_error(ExpectedException) + end + + it "does not match when the proc does not raise an exception" do + proc = Proc.new {} + matcher = RaiseErrorMatcher.new(ExpectedException, "expected") + matcher.matches?(proc).should == false + end + + it "provides a useful failure message" do + exc = UnexpectedException.new("unexpected") + matcher = RaiseErrorMatcher.new(ExpectedException, "expected") + + matcher.matching_exception?(exc).should == false + lambda { + matcher.matches?(Proc.new { raise exc }) + }.should raise_error(UnexpectedException) + matcher.failure_message.should == + ["Expected ExpectedException (expected)", "but got UnexpectedException (unexpected)"] + end + + it "provides a useful failure message when no exception is raised" do + proc = Proc.new { 120 } + matcher = RaiseErrorMatcher.new(ExpectedException, "expected") + matcher.matches?(proc) + matcher.failure_message.should == + ["Expected ExpectedException (expected)", "but no exception was raised (120 was returned)"] + end + + it "provides a useful failure message when no exception is raised and nil is returned" do + proc = Proc.new { nil } + matcher = RaiseErrorMatcher.new(ExpectedException, "expected") + matcher.matches?(proc) + matcher.failure_message.should == + ["Expected ExpectedException (expected)", "but no exception was raised (nil was returned)"] + end + + it "provides a useful negative failure message" do + proc = Proc.new { raise ExpectedException, "expected" } + matcher = RaiseErrorMatcher.new(ExpectedException, "expected") + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected to not get ExpectedException (expected)", ""] + end + + it "provides a useful negative failure message for strict subclasses of the matched exception class" do + proc = Proc.new { raise UnexpectedException, "unexpected" } + matcher = RaiseErrorMatcher.new(Exception, nil) + matcher.matches?(proc) + matcher.negative_failure_message.should == + ["Expected to not get Exception", "but got UnexpectedException (unexpected)"] + end +end diff --git a/spec/mspec/spec/matchers/respond_to_spec.rb b/spec/mspec/spec/matchers/respond_to_spec.rb new file mode 100644 index 0000000000..988caf4dff --- /dev/null +++ b/spec/mspec/spec/matchers/respond_to_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe RespondToMatcher do + it "matches when actual does respond_to? expected" do + RespondToMatcher.new(:to_s).matches?(Object.new).should == true + RespondToMatcher.new(:inject).matches?([]).should == true + RespondToMatcher.new(:[]).matches?(1).should == true + RespondToMatcher.new(:[]=).matches?("string").should == true + end + + it "does not match when actual does not respond_to? expected" do + RespondToMatcher.new(:to_i).matches?(Object.new).should == false + RespondToMatcher.new(:inject).matches?(1).should == false + RespondToMatcher.new(:non_existent_method).matches?([]).should == false + RespondToMatcher.new(:[]=).matches?(1).should == false + end + + it "provides a useful failure message" do + matcher = RespondToMatcher.new(:non_existent_method) + matcher.matches?('string') + matcher.failure_message.should == [ + "Expected \"string\" (String)", "to respond to non_existent_method"] + end + + it "provides a useful negative failure message" do + matcher = RespondToMatcher.new(:to_i) + matcher.matches?(4.0) + matcher.negative_failure_message.should == [ + "Expected 4.0 (Float)", "not to respond to to_i"] + end +end diff --git a/spec/mspec/spec/matchers/signed_zero_spec.rb b/spec/mspec/spec/matchers/signed_zero_spec.rb new file mode 100644 index 0000000000..9c5c50c602 --- /dev/null +++ b/spec/mspec/spec/matchers/signed_zero_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers' + +describe SignedZeroMatcher do + it "matches when actual is zero and has the correct sign" do + SignedZeroMatcher.new(1).matches?(0.0).should == true + SignedZeroMatcher.new(-1).matches?(-0.0).should == true + end + + it "does not match when actual is non-zero" do + SignedZeroMatcher.new(1).matches?(1.0).should == false + SignedZeroMatcher.new(-1).matches?(-1.0).should == false + end + + it "does not match when actual is zero but has the incorrect sign" do + SignedZeroMatcher.new(1).matches?(-0.0).should == false + SignedZeroMatcher.new(-1).matches?(0.0).should == false + end + + it "provides a useful failure message" do + matcher = SignedZeroMatcher.new(-1) + matcher.matches?(0.0) + matcher.failure_message.should == ["Expected 0.0", "to be -0.0"] + end + + it "provides a useful negative failure message" do + matcher = SignedZeroMatcher.new(-1) + matcher.matches?(-0.0) + matcher.negative_failure_message.should == ["Expected -0.0", "not to be -0.0"] + end +end diff --git a/spec/mspec/spec/mocks/mock_spec.rb b/spec/mspec/spec/mocks/mock_spec.rb new file mode 100644 index 0000000000..1a7fb2d567 --- /dev/null +++ b/spec/mspec/spec/mocks/mock_spec.rb @@ -0,0 +1,469 @@ +# This is a bit awkward. Currently the way to verify that the +# opposites are true (for example a failure when the specified +# arguments are NOT provided) is to simply alter the particular +# spec to a failure condition. +require 'spec_helper' +require 'mspec/runner/mspec' +require 'mspec/mocks/mock' +require 'mspec/mocks/proxy' + +describe Mock, ".mocks" do + it "returns a Hash" do + Mock.mocks.should be_kind_of(Hash) + end +end + +describe Mock, ".stubs" do + it "returns a Hash" do + Mock.stubs.should be_kind_of(Hash) + end +end + +describe Mock, ".replaced_name" do + it "returns the name for a method that is being replaced by a mock method" do + m = double('a fake id') + m.stub(:__mspec_object_id__).and_return(42) + Mock.replaced_name(m, :method_call).should == :__mspec_42_method_call__ + end +end + +describe Mock, ".replaced_key" do + it "returns a key used internally by Mock" do + m = double('a fake id') + m.stub(:__mspec_object_id__).and_return(42) + Mock.replaced_key(m, :method_call).should == [:__mspec_42_method_call__, :method_call] + end +end + +describe Mock, ".replaced?" do + before :each do + @mock = double('install_method') + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + end + + it "returns true if a method has been stubbed on an object" do + Mock.install_method @mock, :method_call + Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true + end + + it "returns true if a method has been mocked on an object" do + Mock.install_method @mock, :method_call, :stub + Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_true + end + + it "returns false if a method has not been stubbed or mocked" do + Mock.replaced?(Mock.replaced_name(@mock, :method_call)).should be_false + end +end + +describe Mock, ".name_or_inspect" do + before :each do + @mock = double("I have a #name") + end + + it "returns the value of @name if set" do + @mock.instance_variable_set(:@name, "Myself") + Mock.name_or_inspect(@mock).should == "Myself" + end +end + +describe Mock, ".install_method for mocks" do + before :each do + @mock = double('install_method') + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + end + + after :each do + Mock.cleanup + end + + it "returns a MockProxy instance" do + Mock.install_method(@mock, :method_call).should be_an_instance_of(MockProxy) + end + + it "does not override a previously mocked method with the same name" do + Mock.install_method(@mock, :method_call).with(:a, :b).and_return(1) + Mock.install_method(@mock, :method_call).with(:c).and_return(2) + @mock.method_call(:a, :b) + @mock.method_call(:c) + lambda { @mock.method_call(:d) }.should raise_error(SpecExpectationNotMetError) + end + + # This illustrates RSpec's behavior. This spec fails in mock call count verification + # on RSpec (i.e. Mock 'foo' expected :foo with (any args) once, but received it 0 times) + # and we mimic the behavior of RSpec. + # + # describe "A mock receiving multiple calls to #should_receive" do + # it "returns the first value mocked" do + # m = mock 'multiple #should_receive' + # m.should_receive(:foo).and_return(true) + # m.foo.should == true + # m.should_receive(:foo).and_return(false) + # m.foo.should == true + # end + # end + # + it "does not override a previously mocked method having the same arguments" do + Mock.install_method(@mock, :method_call).with(:a).and_return(true) + @mock.method_call(:a).should == true + Mock.install_method(@mock, :method_call).with(:a).and_return(false) + @mock.method_call(:a).should == true + lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError) + end + + it "properly sends #respond_to? calls to the aliased respond_to? method when not matching mock expectations" do + Mock.install_method(@mock, :respond_to?).with(:to_str).and_return('mock to_str') + Mock.install_method(@mock, :respond_to?).with(:to_int).and_return('mock to_int') + @mock.respond_to?(:to_str).should == 'mock to_str' + @mock.respond_to?(:to_int).should == 'mock to_int' + @mock.respond_to?(:to_s).should == true + @mock.respond_to?(:not_really_a_real_method_seriously).should == false + end + + it "adds to the expectation tally" do + state = double("run state").as_null_object + state.stub(:state).and_return(double("spec state")) + MSpec.should_receive(:current).and_return(state) + MSpec.should_receive(:actions).with(:expectation, state.state) + Mock.install_method(@mock, :method_call).and_return(1) + @mock.method_call.should == 1 + end + + it "registers that an expectation has been encountered" do + state = double("run state").as_null_object + state.stub(:state).and_return(double("spec state")) + MSpec.should_receive(:expectation) + Mock.install_method(@mock, :method_call).and_return(1) + @mock.method_call.should == 1 + end +end + +describe Mock, ".install_method for stubs" do + before :each do + @mock = double('install_method') + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + end + + after :each do + Mock.cleanup + end + + it "returns a MockProxy instance" do + Mock.install_method(@mock, :method_call, :stub).should be_an_instance_of(MockProxy) + end + + # This illustrates RSpec's behavior. This spec passes on RSpec and we mimic it + # + # describe "A mock receiving multiple calls to #stub" do + # it "returns the last value stubbed" do + # m = mock 'multiple #stub' + # m.stub(:foo).and_return(true) + # m.foo.should == true + # m.stub(:foo).and_return(false) + # m.foo.should == false + # end + # end + it "inserts new stubs before old stubs" do + Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(true) + @mock.method_call(:a).should == true + Mock.install_method(@mock, :method_call, :stub).with(:a).and_return(false) + @mock.method_call(:a).should == false + Mock.verify_count + end + + it "does not add to the expectation tally" do + state = double("run state").as_null_object + state.stub(:state).and_return(double("spec state")) + MSpec.should_not_receive(:actions) + Mock.install_method(@mock, :method_call, :stub).and_return(1) + @mock.method_call.should == 1 + end +end + +describe Mock, ".install_method" do + before :each do + @mock = double('install_method') + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + end + + after :each do + Mock.cleanup + end + + it "does not alias a mocked or stubbed method when installing a new mock or stub" do + @mock.should_not respond_to(:method_call) + + Mock.install_method @mock, :method_call + @mock.should respond_to(:method_call) + @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call)) + + Mock.install_method @mock, :method_call, :stub + @mock.should respond_to(:method_call) + @mock.should_not respond_to(Mock.replaced_name(@mock, :method_call)) + end +end + +class MockAndRaiseError < Exception; end + +describe Mock, ".verify_call" do + before :each do + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + + @mock = double('verify_call') + @proxy = Mock.install_method @mock, :method_call + end + + after :each do + ScratchPad.clear + Mock.cleanup + end + + it "does not raise an exception when the mock method receives the expected arguments" do + @proxy.with(1, 'two', :three) + Mock.verify_call @mock, :method_call, 1, 'two', :three + end + + it "raises an SpecExpectationNotMetError when the mock method does not receive the expected arguments" do + @proxy.with(4, 2) + lambda { + Mock.verify_call @mock, :method_call, 42 + }.should raise_error(SpecExpectationNotMetError) + end + + it "raises an SpecExpectationNotMetError when the mock method is called with arguments but expects none" do + lambda { + @proxy.with(:no_args) + Mock.verify_call @mock, :method_call, "hello" + }.should raise_error(SpecExpectationNotMetError) + end + + it "raises an SpecExpectationNotMetError when the mock method is called with no arguments but expects some" do + @proxy.with("hello", "beautiful", "world") + lambda { + Mock.verify_call @mock, :method_call + }.should raise_error(SpecExpectationNotMetError) + end + + it "does not raise an exception when the mock method is called with arguments and is expecting :any_args" do + @proxy.with(:any_args) + Mock.verify_call @mock, :method_call, 1, 2, 3 + end + + it "yields a passed block when it is expected to" do + @proxy.and_yield() + Mock.verify_call @mock, :method_call do + ScratchPad.record true + end + ScratchPad.recorded.should == true + end + + it "does not yield a passed block when it is not expected to" do + Mock.verify_call @mock, :method_call do + ScratchPad.record true + end + ScratchPad.recorded.should == nil + end + + it "can yield subsequently" do + @proxy.and_yield(1).and_yield(2).and_yield(3) + + ScratchPad.record [] + Mock.verify_call @mock, :method_call do |arg| + ScratchPad << arg + end + ScratchPad.recorded.should == [1, 2, 3] + end + + it "can yield and return an expected value" do + @proxy.and_yield(1).and_return(3) + + Mock.verify_call(@mock, :method_call) { |arg| ScratchPad.record arg }.should == 3 + ScratchPad.recorded.should == 1 + end + + it "raises an expection when it is expected to yield but no block is given" do + @proxy.and_yield(1, 2, 3) + lambda { + Mock.verify_call(@mock, :method_call) + }.should raise_error(SpecExpectationNotMetError) + end + + it "raises an expection when it is expected to yield more arguments than the block can take" do + @proxy.and_yield(1, 2, 3) + lambda { + Mock.verify_call(@mock, :method_call) {|a, b|} + }.should raise_error(SpecExpectationNotMetError) + end + + it "does not raise an expection when it is expected to yield to a block that can take any number of arguments" do + @proxy.and_yield(1, 2, 3) + expect { + Mock.verify_call(@mock, :method_call) {|*a|} + }.not_to raise_error + end + + it "raises an exception when expected to" do + @proxy.and_raise(MockAndRaiseError) + lambda { + Mock.verify_call @mock, :method_call + }.should raise_error(MockAndRaiseError) + end +end + +describe Mock, ".verify_count" do + before :each do + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + + @mock = double('verify_count') + @proxy = Mock.install_method @mock, :method_call + end + + after :each do + Mock.cleanup + end + + it "does not raise an exception when the mock receives at least the expected number of calls" do + @proxy.at_least(2) + @mock.method_call + @mock.method_call + Mock.verify_count + end + + it "raises an SpecExpectationNotMetError when the mock receives less than at least the expected number of calls" do + @proxy.at_least(2) + @mock.method_call + lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError) + end + + it "does not raise an exception when the mock receives at most the expected number of calls" do + @proxy.at_most(2) + @mock.method_call + @mock.method_call + Mock.verify_count + end + + it "raises an SpecExpectationNotMetError when the mock receives more than at most the expected number of calls" do + @proxy.at_most(2) + @mock.method_call + @mock.method_call + @mock.method_call + lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError) + end + + it "does not raise an exception when the mock receives exactly the expected number of calls" do + @proxy.exactly(2) + @mock.method_call + @mock.method_call + Mock.verify_count + end + + it "raises an SpecExpectationNotMetError when the mock receives less than exactly the expected number of calls" do + @proxy.exactly(2) + @mock.method_call + lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError) + end + + it "raises an SpecExpectationNotMetError when the mock receives more than exactly the expected number of calls" do + @proxy.exactly(2) + @mock.method_call + @mock.method_call + @mock.method_call + lambda { Mock.verify_count }.should raise_error(SpecExpectationNotMetError) + end +end + +describe Mock, ".verify_count mixing mocks and stubs" do + before :each do + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + + @mock = double('verify_count') + end + + after :each do + Mock.cleanup + end + + it "does not raise an exception for a stubbed method that is never called" do + Mock.install_method @mock, :method_call, :stub + Mock.verify_count + end + + it "verifies the calls to the mocked method when a mock is defined after a stub" do + Mock.install_method @mock, :method_call, :stub + Mock.install_method @mock, :method_call, :mock + @mock.method_call + Mock.verify_count + end + + it "verifies the calls to the mocked method when a mock is defined before a stub" do + Mock.install_method @mock, :method_call, :mock + Mock.install_method @mock, :method_call, :stub + @mock.method_call + Mock.verify_count + end +end + +describe Mock, ".cleanup" do + before :each do + MSpec.stub(:actions) + MSpec.stub(:current).and_return(double("spec state").as_null_object) + + @mock = double('cleanup') + @proxy = Mock.install_method @mock, :method_call + @stub = Mock.install_method @mock, :method_call, :stub + end + + after :each do + Mock.cleanup + end + + it "removes the mock method call if it did not override an existing method" do + @mock.should respond_to(:method_call) + + Mock.cleanup + @mock.should_not respond_to(:method_call) + end + + it "removes the replaced method if the mock method overrides an existing method" do + def @mock.already_here() :hey end + @mock.should respond_to(:already_here) + replaced_name = Mock.replaced_name(@mock, :already_here) + Mock.install_method @mock, :already_here + @mock.should respond_to(replaced_name) + + Mock.cleanup + @mock.should_not respond_to(replaced_name) + @mock.should respond_to(:already_here) + @mock.already_here.should == :hey + end + + it "removes all mock expectations" do + Mock.mocks.should == { Mock.replaced_key(@mock, :method_call) => [@proxy] } + Mock.cleanup + Mock.mocks.should == {} + end + + it "removes all stubs" do + Mock.stubs.should == { Mock.replaced_key(@mock, :method_call) => [@stub] } + Mock.cleanup + Mock.stubs.should == {} + end + + it "removes the replaced name for mocks" do + replaced_key = Mock.replaced_key(@mock, :method_call) + Mock.should_receive(:clear_replaced).with(replaced_key) + + replaced_name = Mock.replaced_name(@mock, :method_call) + Mock.replaced?(replaced_name).should be_true + + Mock.cleanup + Mock.replaced?(replaced_name).should be_false + end +end diff --git a/spec/mspec/spec/mocks/proxy_spec.rb b/spec/mspec/spec/mocks/proxy_spec.rb new file mode 100644 index 0000000000..d9e754b972 --- /dev/null +++ b/spec/mspec/spec/mocks/proxy_spec.rb @@ -0,0 +1,405 @@ +require 'spec_helper' +require 'mspec/mocks/proxy' + +describe MockObject, ".new" do + it "creates a new mock object" do + m = MockObject.new('not a null object') + lambda { m.not_a_method }.should raise_error(NoMethodError) + end + + it "creates a new mock object that follows the NullObject pattern" do + m = MockObject.new('null object', :null_object => true) + m.not_really_a_method.should equal(m) + end +end + +describe MockProxy, ".new" do + it "creates a mock proxy by default" do + MockProxy.new.mock?.should be_true + end + + it "creates a stub proxy by request" do + MockProxy.new(:stub).stub?.should be_true + end + + it "sets the call expectation to 1 call for a mock" do + MockProxy.new.count.should == [:exactly, 1] + end + + it "sets the call expectation to any number of times for a stub" do + MockProxy.new(:stub).count.should == [:any_number_of_times, 0] + end +end + +describe MockProxy, "#count" do + before :each do + @proxy = MockProxy.new + end + + it "returns the expected number of calls the mock should receive" do + @proxy.count.should == [:exactly, 1] + @proxy.at_least(3).count.should == [:at_least, 3] + end +end + +describe MockProxy, "#arguments" do + before :each do + @proxy = MockProxy.new + end + + it "returns the expected arguments" do + @proxy.arguments.should == :any_args + end +end + +describe MockProxy, "#with" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.with(:a).should be_equal(@proxy) + end + + it "raises an ArgumentError if no arguments are given" do + lambda { @proxy.with }.should raise_error(ArgumentError) + end + + it "accepts any number of arguments" do + @proxy.with(1, 2, 3).should be_an_instance_of(MockProxy) + @proxy.arguments.should == [1,2,3] + end +end + +describe MockProxy, "#once" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.once.should be_equal(@proxy) + end + + it "sets the expected calls to 1" do + @proxy.once + @proxy.count.should == [:exactly, 1] + end + + it "accepts no arguments" do + lambda { @proxy.once(:a) }.should raise_error + end +end + +describe MockProxy, "#twice" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.twice.should be_equal(@proxy) + end + + it "sets the expected calls to 2" do + @proxy.twice + @proxy.count.should == [:exactly, 2] + end + + it "accepts no arguments" do + lambda { @proxy.twice(:b) }.should raise_error + end +end + +describe MockProxy, "#exactly" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.exactly(2).should be_equal(@proxy) + end + + it "sets the expected calls to exactly n" do + @proxy.exactly(5) + @proxy.count.should == [:exactly, 5] + end + + it "does not accept an argument that Integer() cannot convert" do + lambda { @proxy.exactly('x') }.should raise_error + end +end + +describe MockProxy, "#at_least" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.at_least(3).should be_equal(@proxy) + end + + it "sets the expected calls to at least n" do + @proxy.at_least(3) + @proxy.count.should == [:at_least, 3] + end + + it "accepts :once :twice" do + @proxy.at_least(:once) + @proxy.count.should == [:at_least, 1] + @proxy.at_least(:twice) + @proxy.count.should == [:at_least, 2] + end + + it "does not accept an argument that Integer() cannot convert" do + lambda { @proxy.at_least('x') }.should raise_error + end +end + +describe MockProxy, "#at_most" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.at_most(2).should be_equal(@proxy) + end + + it "sets the expected calls to at most n" do + @proxy.at_most(2) + @proxy.count.should == [:at_most, 2] + end + + it "accepts :once, :twice" do + @proxy.at_most(:once) + @proxy.count.should == [:at_most, 1] + @proxy.at_most(:twice) + @proxy.count.should == [:at_most, 2] + end + + it "does not accept an argument that Integer() cannot convert" do + lambda { @proxy.at_most('x') }.should raise_error + end +end + +describe MockProxy, "#any_number_of_times" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.any_number_of_times.should be_equal(@proxy) + end + + it "sets the expected calls to any number of times" do + @proxy.any_number_of_times + @proxy.count.should == [:any_number_of_times, 0] + end + + it "does not accept an argument" do + lambda { @proxy.any_number_of_times(2) }.should raise_error + end +end + +describe MockProxy, "#and_return" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.and_return(false).should equal(@proxy) + end + + it "sets the expected return value" do + @proxy.and_return(false) + @proxy.returning.should == false + end + + it "accepts any number of return values" do + @proxy.and_return(1, 2, 3) + @proxy.returning.should == 1 + @proxy.returning.should == 2 + @proxy.returning.should == 3 + end + + it "implicitly sets the expected number of calls" do + @proxy.and_return(1, 2, 3) + @proxy.count.should == [:exactly, 3] + end + + it "only sets the expected number of calls if it is higher than what is already set" do + @proxy.at_least(5).times.and_return(1, 2, 3) + @proxy.count.should == [:at_least, 5] + + @proxy.at_least(2).times.and_return(1, 2, 3) + @proxy.count.should == [:at_least, 3] + end +end + +describe MockProxy, "#returning" do + before :each do + @proxy = MockProxy.new + end + + it "returns nil by default" do + @proxy.returning.should be_nil + end + + it "returns the value set by #and_return" do + @proxy.and_return(2) + @proxy.returning.should == 2 + @proxy.returning.should == 2 + end + + it "returns a sequence of values set by #and_return" do + @proxy.and_return(1,2,3,4) + @proxy.returning.should == 1 + @proxy.returning.should == 2 + @proxy.returning.should == 3 + @proxy.returning.should == 4 + @proxy.returning.should == 4 + @proxy.returning.should == 4 + end +end + +describe MockProxy, "#calls" do + before :each do + @proxy = MockProxy.new + end + + it "returns the number of times the proxy is called" do + @proxy.calls.should == 0 + end +end + +describe MockProxy, "#called" do + before :each do + @proxy = MockProxy.new + end + + it "increments the number of times the proxy is called" do + @proxy.called + @proxy.called + @proxy.calls.should == 2 + end +end + +describe MockProxy, "#times" do + before :each do + @proxy = MockProxy.new + end + + it "is a no-op" do + @proxy.times.should == @proxy + end +end + +describe MockProxy, "#stub?" do + it "returns true if the proxy is created as a stub" do + MockProxy.new(:stub).stub?.should be_true + end + + it "returns false if the proxy is created as a mock" do + MockProxy.new(:mock).stub?.should be_false + end +end + +describe MockProxy, "#mock?" do + it "returns true if the proxy is created as a mock" do + MockProxy.new(:mock).mock?.should be_true + end + + it "returns false if the proxy is created as a stub" do + MockProxy.new(:stub).mock?.should be_false + end +end + +describe MockProxy, "#and_yield" do + before :each do + @proxy = MockProxy.new + end + + it "returns self" do + @proxy.and_yield(false).should equal(@proxy) + end + + it "sets the expected values to yield" do + @proxy.and_yield(1).yielding.should == [[1]] + end + + it "accepts multiple values to yield" do + @proxy.and_yield(1, 2, 3).yielding.should == [[1, 2, 3]] + end +end + +describe MockProxy, "#raising" do + before :each do + @proxy = MockProxy.new + end + + it "returns nil by default" do + @proxy.raising.should be_nil + end + + it "returns the exception object passed to #and_raise" do + exc = double("exception") + @proxy.and_raise(exc) + @proxy.raising.should equal(exc) + end + + it "returns an instance of RuntimeError when a String is passed to #and_raise" do + @proxy.and_raise("an error") + exc = @proxy.raising + exc.should be_an_instance_of(RuntimeError) + exc.message.should == "an error" + end +end + +describe MockProxy, "#yielding" do + before :each do + @proxy = MockProxy.new + end + + it "returns an empty array by default" do + @proxy.yielding.should == [] + end + + it "returns an array of arrays of values the proxy should yield" do + @proxy.and_yield(3) + @proxy.yielding.should == [[3]] + end + + it "returns an accumulation of arrays of values the proxy should yield" do + @proxy.and_yield(1).and_yield(2, 3) + @proxy.yielding.should == [[1], [2, 3]] + end +end + +describe MockProxy, "#yielding?" do + before :each do + @proxy = MockProxy.new + end + + it "returns false if the proxy is not yielding" do + @proxy.yielding?.should be_false + end + + it "returns true if the proxy is yielding" do + @proxy.and_yield(1) + @proxy.yielding?.should be_true + end +end + +describe MockIntObject, "#to_int" do + before :each do + @int = MockIntObject.new(10) + end + + it "returns the number if to_int is called" do + @int.to_int.should == 10 + @int.count.should == [:at_least, 1] + end + + it "tries to convert the target to int if to_int is called" do + MockIntObject.new(@int).to_int.should == 10 + @int.count.should == [:at_least, 1] + end +end diff --git a/spec/mspec/spec/runner/actions/filter_spec.rb b/spec/mspec/spec/runner/actions/filter_spec.rb new file mode 100644 index 0000000000..d185781757 --- /dev/null +++ b/spec/mspec/spec/runner/actions/filter_spec.rb @@ -0,0 +1,84 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/actions/filter' +require 'mspec/runner/mspec' +require 'mspec/runner/tag' + +describe ActionFilter do + it "creates a filter when not passed a description" do + MatchFilter.should_not_receive(:new) + ActionFilter.new(nil, nil) + end + + it "creates a filter from a single description" do + MatchFilter.should_receive(:new).with(nil, "match me") + ActionFilter.new(nil, "match me") + end + + it "creates a filter from an array of descriptions" do + MatchFilter.should_receive(:new).with(nil, "match me", "again") + ActionFilter.new(nil, ["match me", "again"]) + end +end + +describe ActionFilter, "#===" do + before :each do + MSpec.stub(:read_tags).and_return(["match"]) + @action = ActionFilter.new(nil, ["catch", "if you"]) + end + + it "returns false if there are no filters" do + action = ActionFilter.new + action.===("anything").should == false + end + + it "returns true if the argument matches any of the descriptions" do + @action.===("catch").should == true + @action.===("if you can").should == true + end + + it "returns false if the argument does not match any of the descriptions" do + @action.===("patch me").should == false + @action.===("if I can").should == false + end +end + +describe ActionFilter, "#load" do + before :each do + @tag = SpecTag.new "tag(comment):description" + end + + it "creates a filter from a single tag" do + MSpec.should_receive(:read_tags).with(["tag"]).and_return([@tag]) + MatchFilter.should_receive(:new).with(nil, "description") + ActionFilter.new("tag", nil).load + end + + it "creates a filter from an array of tags" do + MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([@tag]) + MatchFilter.should_receive(:new).with(nil, "description") + ActionFilter.new(["tag", "key"], nil).load + end + + it "creates a filter from both tags and descriptions" do + MSpec.should_receive(:read_tags).and_return([@tag]) + filter = ActionFilter.new("tag", ["match me", "again"]) + MatchFilter.should_receive(:new).with(nil, "description") + filter.load + end +end + +describe ActionFilter, "#register" do + it "registers itself with MSpec for the :load actions" do + filter = ActionFilter.new + MSpec.should_receive(:register).with(:load, filter) + filter.register + end +end + +describe ActionFilter, "#unregister" do + it "unregisters itself with MSpec for the :load actions" do + filter = ActionFilter.new + MSpec.should_receive(:unregister).with(:load, filter) + filter.unregister + end +end diff --git a/spec/mspec/spec/runner/actions/tag_spec.rb b/spec/mspec/spec/runner/actions/tag_spec.rb new file mode 100644 index 0000000000..92df362d02 --- /dev/null +++ b/spec/mspec/spec/runner/actions/tag_spec.rb @@ -0,0 +1,315 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/actions/tag' +require 'mspec/runner/mspec' +require 'mspec/runner/example' +require 'mspec/runner/tag' + +describe TagAction, ".new" do + it "creates an MatchFilter with its tag and desc arguments" do + filter = double('action filter').as_null_object + MatchFilter.should_receive(:new).with(nil, "some", "thing").and_return(filter) + TagAction.new :add, :all, nil, nil, ["tag", "key"], ["some", "thing"] + end +end + +describe TagAction, "#===" do + before :each do + MSpec.stub(:read_tags).and_return(["match"]) + @action = TagAction.new :add, :fail, nil, nil, nil, ["catch", "if you"] + end + + it "returns true if there are no filters" do + action = TagAction.new :add, :all, nil, nil + action.===("anything").should == true + end + + it "returns true if the argument matches any of the descriptions" do + @action.===("catch").should == true + @action.===("if you can").should == true + end + + it "returns false if the argument does not match any of the descriptions" do + @action.===("patch me").should == false + @action.===("if I can").should == false + end +end + +describe TagAction, "#exception?" do + before :each do + @action = TagAction.new :add, :fail, nil, nil, nil, nil + end + + it "returns false if no exception has been raised while evaluating an example" do + @action.exception?.should be_false + end + + it "returns true if an exception was raised while evaluating an example" do + @action.exception ExceptionState.new nil, nil, Exception.new("failed") + @action.exception?.should be_true + end +end + +describe TagAction, "#outcome?" do + before :each do + MSpec.stub(:read_tags).and_return([]) + @exception = ExceptionState.new nil, nil, Exception.new("failed") + end + + it "returns true if outcome is :fail and the spec fails" do + action = TagAction.new :add, :fail, nil, nil, nil, nil + action.exception @exception + action.outcome?.should == true + end + + it "returns false if the outcome is :fail and the spec passes" do + action = TagAction.new :add, :fail, nil, nil, nil, nil + action.outcome?.should == false + end + + it "returns true if the outcome is :pass and the spec passes" do + action = TagAction.new :del, :pass, nil, nil, nil, nil + action.outcome?.should == true + end + + it "returns false if the outcome is :pass and the spec fails" do + action = TagAction.new :del, :pass, nil, nil, nil, nil + action.exception @exception + action.outcome?.should == false + end + + it "returns true if the outcome is :all" do + action = TagAction.new :add, :all, nil, nil, nil, nil + action.exception @exception + action.outcome?.should == true + end +end + +describe TagAction, "#before" do + it "resets the #exception? flag to false" do + action = TagAction.new :add, :fail, nil, nil, nil, nil + action.exception?.should be_false + action.exception ExceptionState.new(nil, nil, Exception.new("Fail!")) + action.exception?.should be_true + action.before(ExampleState.new(ContextState.new("describe"), "it")) + action.exception?.should be_false + end +end + +describe TagAction, "#exception" do + it "sets the #exception? flag" do + action = TagAction.new :add, :fail, nil, nil, nil, nil + action.exception?.should be_false + action.exception ExceptionState.new(nil, nil, Exception.new("Fail!")) + action.exception?.should be_true + end +end + +describe TagAction, "#after when action is :add" do + before :each do + MSpec.stub(:read_tags).and_return([]) + context = ContextState.new "Catch#me" + @state = ExampleState.new context, "if you can" + @tag = SpecTag.new "tag(comment):Catch#me if you can" + SpecTag.stub(:new).and_return(@tag) + @exception = ExceptionState.new nil, nil, Exception.new("failed") + end + + it "does not write a tag if the description does not match" do + MSpec.should_not_receive(:write_tag) + action = TagAction.new :add, :all, "tag", "comment", nil, "match" + action.after @state + end + + it "does not write a tag if outcome is :fail and the spec passed" do + MSpec.should_not_receive(:write_tag) + action = TagAction.new :add, :fail, "tag", "comment", nil, "can" + action.after @state + end + + it "writes a tag if the outcome is :fail and the spec failed" do + MSpec.should_receive(:write_tag).with(@tag) + action = TagAction.new :add, :fail, "tag", "comment", nil, "can" + action.exception @exception + action.after @state + end + + it "does not write a tag if outcome is :pass and the spec failed" do + MSpec.should_not_receive(:write_tag) + action = TagAction.new :add, :pass, "tag", "comment", nil, "can" + action.exception @exception + action.after @state + end + + it "writes a tag if the outcome is :pass and the spec passed" do + MSpec.should_receive(:write_tag).with(@tag) + action = TagAction.new :add, :pass, "tag", "comment", nil, "can" + action.after @state + end + + it "writes a tag if the outcome is :all" do + MSpec.should_receive(:write_tag).with(@tag) + action = TagAction.new :add, :all, "tag", "comment", nil, "can" + action.after @state + end +end + +describe TagAction, "#after when action is :del" do + before :each do + MSpec.stub(:read_tags).and_return([]) + context = ContextState.new "Catch#me" + @state = ExampleState.new context, "if you can" + @tag = SpecTag.new "tag(comment):Catch#me if you can" + SpecTag.stub(:new).and_return(@tag) + @exception = ExceptionState.new nil, nil, Exception.new("failed") + end + + it "does not delete a tag if the description does not match" do + MSpec.should_not_receive(:delete_tag) + action = TagAction.new :del, :all, "tag", "comment", nil, "match" + action.after @state + end + + it "does not delete a tag if outcome is :fail and the spec passed" do + MSpec.should_not_receive(:delete_tag) + action = TagAction.new :del, :fail, "tag", "comment", nil, "can" + action.after @state + end + + it "deletes a tag if the outcome is :fail and the spec failed" do + MSpec.should_receive(:delete_tag).with(@tag) + action = TagAction.new :del, :fail, "tag", "comment", nil, "can" + action.exception @exception + action.after @state + end + + it "does not delete a tag if outcome is :pass and the spec failed" do + MSpec.should_not_receive(:delete_tag) + action = TagAction.new :del, :pass, "tag", "comment", nil, "can" + action.exception @exception + action.after @state + end + + it "deletes a tag if the outcome is :pass and the spec passed" do + MSpec.should_receive(:delete_tag).with(@tag) + action = TagAction.new :del, :pass, "tag", "comment", nil, "can" + action.after @state + end + + it "deletes a tag if the outcome is :all" do + MSpec.should_receive(:delete_tag).with(@tag) + action = TagAction.new :del, :all, "tag", "comment", nil, "can" + action.after @state + end +end + +describe TagAction, "#finish" do + before :each do + $stdout = @out = IOStub.new + context = ContextState.new "Catch#me" + @state = ExampleState.new context, "if you can" + MSpec.stub(:write_tag).and_return(true) + MSpec.stub(:delete_tag).and_return(true) + end + + after :each do + $stdout = STDOUT + end + + it "reports no specs tagged if none where tagged" do + action = TagAction.new :add, :fail, "tag", "comment", nil, "can" + action.stub(:outcome?).and_return(false) + action.after @state + action.finish + @out.should == "\nTagAction: no specs were tagged with 'tag'\n" + end + + it "reports no specs tagged if none where tagged" do + action = TagAction.new :del, :fail, "tag", "comment", nil, "can" + action.stub(:outcome?).and_return(false) + action.after @state + action.finish + @out.should == "\nTagAction: no tags 'tag' were deleted\n" + end + + it "reports the spec descriptions that were tagged" do + action = TagAction.new :add, :fail, "tag", "comment", nil, "can" + action.stub(:outcome?).and_return(true) + action.after @state + action.finish + @out.should == +%[ +TagAction: specs tagged with 'tag': + +Catch#me if you can +] + end + + it "reports the spec descriptions for the tags that were deleted" do + action = TagAction.new :del, :fail, "tag", "comment", nil, "can" + action.stub(:outcome?).and_return(true) + action.after @state + action.finish + @out.should == +%[ +TagAction: tag 'tag' deleted for specs: + +Catch#me if you can +] + end +end + +describe TagAction, "#register" do + before :each do + MSpec.stub(:register) + MSpec.stub(:read_tags).and_return([]) + @action = TagAction.new :add, :all, nil, nil, nil, nil + end + + it "registers itself with MSpec for the :before event" do + MSpec.should_receive(:register).with(:before, @action) + @action.register + end + + it "registers itself with MSpec for the :after event" do + MSpec.should_receive(:register).with(:after, @action) + @action.register + end + + it "registers itself with MSpec for the :exception event" do + MSpec.should_receive(:register).with(:exception, @action) + @action.register + end + + it "registers itself with MSpec for the :finish event" do + MSpec.should_receive(:register).with(:finish, @action) + @action.register + end +end + +describe TagAction, "#unregister" do + before :each do + MSpec.stub(:unregister) + MSpec.stub(:read_tags).and_return([]) + @action = TagAction.new :add, :all, nil, nil, nil, nil + end + + it "unregisters itself with MSpec for the :before event" do + MSpec.should_receive(:unregister).with(:before, @action) + @action.unregister + end + + it "unregisters itself with MSpec for the :after event" do + MSpec.should_receive(:unregister).with(:after, @action) + @action.unregister + end + + it "unregisters itself with MSpec for the :exception event" do + MSpec.should_receive(:unregister).with(:exception, @action) + @action.unregister + end + + it "unregisters itself with MSpec for the :finish event" do + MSpec.should_receive(:unregister).with(:finish, @action) + @action.unregister + end +end diff --git a/spec/mspec/spec/runner/actions/taglist_spec.rb b/spec/mspec/spec/runner/actions/taglist_spec.rb new file mode 100644 index 0000000000..418c761c2d --- /dev/null +++ b/spec/mspec/spec/runner/actions/taglist_spec.rb @@ -0,0 +1,152 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/actions/taglist' +require 'mspec/runner/mspec' +require 'mspec/runner/example' +require 'mspec/runner/tag' + +describe TagListAction, "#include?" do + it "returns true" do + TagListAction.new.include?(:anything).should be_true + end +end + +describe TagListAction, "#===" do + before :each do + tag = SpecTag.new "fails:description" + MSpec.stub(:read_tags).and_return([tag]) + @filter = double("MatchFilter").as_null_object + MatchFilter.stub(:new).and_return(@filter) + @action = TagListAction.new + @action.load + end + + it "returns true if filter === string returns true" do + @filter.should_receive(:===).with("str").and_return(true) + @action.===("str").should be_true + end + + it "returns false if filter === string returns false" do + @filter.should_receive(:===).with("str").and_return(false) + @action.===("str").should be_false + end +end + +describe TagListAction, "#start" do + before :each do + @stdout = $stdout + $stdout = IOStub.new + end + + after :each do + $stdout = @stdout + end + + it "prints a banner for specific tags" do + action = TagListAction.new ["fails", "unstable"] + action.start + $stdout.should == "\nListing specs tagged with 'fails', 'unstable'\n\n" + end + + it "prints a banner for all tags" do + action = TagListAction.new + action.start + $stdout.should == "\nListing all tagged specs\n\n" + end +end + +describe TagListAction, "#load" do + before :each do + @t1 = SpecTag.new "fails:I fail" + @t2 = SpecTag.new "unstable:I'm unstable" + end + + it "creates a MatchFilter for matching tags" do + MSpec.should_receive(:read_tags).with(["fails"]).and_return([@t1]) + MatchFilter.should_receive(:new).with(nil, "I fail") + TagListAction.new(["fails"]).load + end + + it "creates a MatchFilter for all tags" do + MSpec.should_receive(:read_tags).and_return([@t1, @t2]) + MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable") + TagListAction.new.load + end + + it "does not create a MatchFilter if there are no matching tags" do + MSpec.stub(:read_tags).and_return([]) + MatchFilter.should_not_receive(:new) + TagListAction.new(["fails"]).load + end +end + +describe TagListAction, "#after" do + before :each do + @stdout = $stdout + $stdout = IOStub.new + + @state = double("ExampleState") + @state.stub(:description).and_return("str") + + @action = TagListAction.new + end + + after :each do + $stdout = @stdout + end + + it "prints nothing if the filter does not match" do + @action.should_receive(:===).with("str").and_return(false) + @action.after(@state) + $stdout.should == "" + end + + it "prints the example description if the filter matches" do + @action.should_receive(:===).with("str").and_return(true) + @action.after(@state) + $stdout.should == "str\n" + end +end + +describe TagListAction, "#register" do + before :each do + MSpec.stub(:register) + @action = TagListAction.new + end + + it "registers itself with MSpec for the :start event" do + MSpec.should_receive(:register).with(:start, @action) + @action.register + end + + it "registers itself with MSpec for the :load event" do + MSpec.should_receive(:register).with(:load, @action) + @action.register + end + + it "registers itself with MSpec for the :after event" do + MSpec.should_receive(:register).with(:after, @action) + @action.register + end +end + +describe TagListAction, "#unregister" do + before :each do + MSpec.stub(:unregister) + @action = TagListAction.new + end + + it "unregisters itself with MSpec for the :start event" do + MSpec.should_receive(:unregister).with(:start, @action) + @action.unregister + end + + it "unregisters itself with MSpec for the :load event" do + MSpec.should_receive(:unregister).with(:load, @action) + @action.unregister + end + + it "unregisters itself with MSpec for the :after event" do + MSpec.should_receive(:unregister).with(:after, @action) + @action.unregister + end +end diff --git a/spec/mspec/spec/runner/actions/tagpurge_spec.rb b/spec/mspec/spec/runner/actions/tagpurge_spec.rb new file mode 100644 index 0000000000..27ad2a1470 --- /dev/null +++ b/spec/mspec/spec/runner/actions/tagpurge_spec.rb @@ -0,0 +1,154 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/actions/tagpurge' +require 'mspec/runner/mspec' +require 'mspec/runner/example' +require 'mspec/runner/tag' + +describe TagPurgeAction, "#start" do + before :each do + @stdout = $stdout + $stdout = IOStub.new + end + + after :each do + $stdout = @stdout + end + + it "prints a banner" do + action = TagPurgeAction.new + action.start + $stdout.should == "\nRemoving tags not matching any specs\n\n" + end +end + +describe TagPurgeAction, "#load" do + before :each do + @t1 = SpecTag.new "fails:I fail" + @t2 = SpecTag.new "unstable:I'm unstable" + end + + it "creates a MatchFilter for all tags" do + MSpec.should_receive(:read_tags).and_return([@t1, @t2]) + MatchFilter.should_receive(:new).with(nil, "I fail", "I'm unstable") + TagPurgeAction.new.load + end +end + +describe TagPurgeAction, "#after" do + before :each do + @state = double("ExampleState") + @state.stub(:description).and_return("str") + + @action = TagPurgeAction.new + end + + it "does not save the description if the filter does not match" do + @action.should_receive(:===).with("str").and_return(false) + @action.after @state + @action.matching.should == [] + end + + it "saves the description if the filter matches" do + @action.should_receive(:===).with("str").and_return(true) + @action.after @state + @action.matching.should == ["str"] + end +end + +describe TagPurgeAction, "#unload" do + before :each do + @stdout = $stdout + $stdout = IOStub.new + + @t1 = SpecTag.new "fails:I fail" + @t2 = SpecTag.new "unstable:I'm unstable" + @t3 = SpecTag.new "fails:I'm unstable" + + MSpec.stub(:read_tags).and_return([@t1, @t2, @t3]) + MSpec.stub(:write_tags) + + @state = double("ExampleState") + @state.stub(:description).and_return("I'm unstable") + + @action = TagPurgeAction.new + @action.load + @action.after @state + end + + after :each do + $stdout = @stdout + end + + it "does not rewrite any tags if there were no tags for the specs" do + MSpec.should_receive(:read_tags).and_return([]) + MSpec.should_receive(:delete_tags) + MSpec.should_not_receive(:write_tags) + + @action.load + @action.after @state + @action.unload + + $stdout.should == "" + end + + it "rewrites tags that were matched" do + MSpec.should_receive(:write_tags).with([@t2, @t3]) + @action.unload + end + + it "prints tags that were not matched" do + @action.unload + $stdout.should == "I fail\n" + end +end + +describe TagPurgeAction, "#unload" do + before :each do + @stdout = $stdout + $stdout = IOStub.new + + MSpec.stub(:read_tags).and_return([]) + + @state = double("ExampleState") + @state.stub(:description).and_return("I'm unstable") + + @action = TagPurgeAction.new + @action.load + @action.after @state + end + + after :each do + $stdout = @stdout + end + + it "deletes the tag file if no tags were found" do + MSpec.should_not_receive(:write_tags) + MSpec.should_receive(:delete_tags) + @action.unload + $stdout.should == "" + end +end + +describe TagPurgeAction, "#register" do + before :each do + MSpec.stub(:register) + @action = TagPurgeAction.new + end + + it "registers itself with MSpec for the :unload event" do + MSpec.should_receive(:register).with(:unload, @action) + @action.register + end +end + +describe TagPurgeAction, "#unregister" do + before :each do + MSpec.stub(:unregister) + @action = TagPurgeAction.new + end + + it "unregisters itself with MSpec for the :unload event" do + MSpec.should_receive(:unregister).with(:unload, @action) + @action.unregister + end +end diff --git a/spec/mspec/spec/runner/actions/tally_spec.rb b/spec/mspec/spec/runner/actions/tally_spec.rb new file mode 100644 index 0000000000..be4635ffeb --- /dev/null +++ b/spec/mspec/spec/runner/actions/tally_spec.rb @@ -0,0 +1,352 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/runner/actions/tally' +require 'mspec/runner/mspec' +require 'mspec/runner/example' + +describe Tally, "#files!" do + before :each do + @tally = Tally.new + end + + it "increments the count returned by #files" do + @tally.files! 3 + @tally.files.should == 3 + @tally.files! + @tally.files.should == 4 + end +end + +describe Tally, "#examples!" do + before :each do + @tally = Tally.new + end + + it "increments the count returned by #examples" do + @tally.examples! 2 + @tally.examples.should == 2 + @tally.examples! 2 + @tally.examples.should == 4 + end +end + +describe Tally, "#expectations!" do + before :each do + @tally = Tally.new + end + + it "increments the count returned by #expectations" do + @tally.expectations! + @tally.expectations.should == 1 + @tally.expectations! 3 + @tally.expectations.should == 4 + end +end + +describe Tally, "#failures!" do + before :each do + @tally = Tally.new + end + + it "increments the count returned by #failures" do + @tally.failures! 1 + @tally.failures.should == 1 + @tally.failures! + @tally.failures.should == 2 + end +end + +describe Tally, "#errors!" do + before :each do + @tally = Tally.new + end + + it "increments the count returned by #errors" do + @tally.errors! + @tally.errors.should == 1 + @tally.errors! 2 + @tally.errors.should == 3 + end +end + +describe Tally, "#guards!" do + before :each do + @tally = Tally.new + end + + it "increments the count returned by #guards" do + @tally.guards! + @tally.guards.should == 1 + @tally.guards! 2 + @tally.guards.should == 3 + end +end + +describe Tally, "#file" do + before :each do + @tally = Tally.new + end + + it "returns a formatted string of the number of #files" do + @tally.file.should == "0 files" + @tally.files! + @tally.file.should == "1 file" + @tally.files! + @tally.file.should == "2 files" + end +end + +describe Tally, "#example" do + before :each do + @tally = Tally.new + end + + it "returns a formatted string of the number of #examples" do + @tally.example.should == "0 examples" + @tally.examples! + @tally.example.should == "1 example" + @tally.examples! + @tally.example.should == "2 examples" + end +end + +describe Tally, "#expectation" do + before :each do + @tally = Tally.new + end + + it "returns a formatted string of the number of #expectations" do + @tally.expectation.should == "0 expectations" + @tally.expectations! + @tally.expectation.should == "1 expectation" + @tally.expectations! + @tally.expectation.should == "2 expectations" + end +end + +describe Tally, "#failure" do + before :each do + @tally = Tally.new + end + + it "returns a formatted string of the number of #failures" do + @tally.failure.should == "0 failures" + @tally.failures! + @tally.failure.should == "1 failure" + @tally.failures! + @tally.failure.should == "2 failures" + end +end + +describe Tally, "#error" do + before :each do + @tally = Tally.new + end + + it "returns a formatted string of the number of #errors" do + @tally.error.should == "0 errors" + @tally.errors! + @tally.error.should == "1 error" + @tally.errors! + @tally.error.should == "2 errors" + end +end + +describe Tally, "#guard" do + before :each do + @tally = Tally.new + end + + it "returns a formatted string of the number of #guards" do + @tally.guard.should == "0 guards" + @tally.guards! + @tally.guard.should == "1 guard" + @tally.guards! + @tally.guard.should == "2 guards" + end +end + +describe Tally, "#format" do + before :each do + @tally = Tally.new + end + + after :each do + MSpec.clear_modes + end + + it "returns a formatted string of counts" do + @tally.files! + @tally.examples! 2 + @tally.expectations! 4 + @tally.errors! + @tally.tagged! + @tally.format.should == "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged" + end + + it "includes guards if MSpec is in verify mode" do + MSpec.register_mode :verify + @tally.files! + @tally.examples! 2 + @tally.expectations! 4 + @tally.errors! + @tally.tagged! + @tally.guards! + @tally.format.should == + "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 1 guard" + end + + it "includes guards if MSpec is in report mode" do + MSpec.register_mode :report + @tally.files! + @tally.examples! 2 + @tally.expectations! 4 + @tally.errors! + @tally.tagged! + @tally.guards! 2 + @tally.format.should == + "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 1 tagged, 2 guards" + end + + it "includes guards if MSpec is in report_on mode" do + MSpec.register_mode :report_on + @tally.files! + @tally.examples! 2 + @tally.expectations! 4 + @tally.errors! + @tally.guards! 2 + @tally.format.should == + "1 file, 2 examples, 4 expectations, 0 failures, 1 error, 0 tagged, 2 guards" + end +end + +describe TallyAction, "#counter" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "returns the Tally object" do + @tally.counter.should be_kind_of(Tally) + end +end + +describe TallyAction, "#load" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "increments the count returned by Tally#files" do + @tally.load + @tally.counter.files.should == 1 + end +end + +describe TallyAction, "#expectation" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "increments the count returned by Tally#expectations" do + @tally.expectation @state + @tally.counter.expectations.should == 1 + end +end + +describe TallyAction, "#example" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "increments counts returned by Tally#examples" do + @tally.example @state, nil + @tally.counter.examples.should == 1 + @tally.counter.expectations.should == 0 + @tally.counter.failures.should == 0 + @tally.counter.errors.should == 0 + end +end + +describe TallyAction, "#exception" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "increments counts returned by Tally#failures" do + exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!") + @tally.exception exc + @tally.counter.examples.should == 0 + @tally.counter.expectations.should == 0 + @tally.counter.failures.should == 1 + @tally.counter.errors.should == 0 + end +end + +describe TallyAction, "#exception" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "increments counts returned by Tally#errors" do + exc = ExceptionState.new nil, nil, Exception.new("Error!") + @tally.exception exc + @tally.counter.examples.should == 0 + @tally.counter.expectations.should == 0 + @tally.counter.failures.should == 0 + @tally.counter.errors.should == 1 + end +end + +describe TallyAction, "#format" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "returns a readable string of counts" do + @tally.load + @tally.example @state, nil + @tally.expectation @state + @tally.expectation @state + exc = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("Failed!") + @tally.exception exc + @tally.format.should == "1 file, 1 example, 2 expectations, 1 failure, 0 errors, 0 tagged" + end +end + +describe TallyAction, "#register" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "registers itself with MSpec for appropriate actions" do + MSpec.should_receive(:register).with(:load, @tally) + MSpec.should_receive(:register).with(:exception, @tally) + MSpec.should_receive(:register).with(:example, @tally) + MSpec.should_receive(:register).with(:tagged, @tally) + MSpec.should_receive(:register).with(:expectation, @tally) + @tally.register + end +end + +describe TallyAction, "#unregister" do + before :each do + @tally = TallyAction.new + @state = ExampleState.new("describe", "it") + end + + it "unregisters itself with MSpec for appropriate actions" do + MSpec.should_receive(:unregister).with(:load, @tally) + MSpec.should_receive(:unregister).with(:exception, @tally) + MSpec.should_receive(:unregister).with(:example, @tally) + MSpec.should_receive(:unregister).with(:tagged, @tally) + MSpec.should_receive(:unregister).with(:expectation, @tally) + @tally.unregister + end +end diff --git a/spec/mspec/spec/runner/actions/timer_spec.rb b/spec/mspec/spec/runner/actions/timer_spec.rb new file mode 100644 index 0000000000..417367d5a2 --- /dev/null +++ b/spec/mspec/spec/runner/actions/timer_spec.rb @@ -0,0 +1,44 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/actions/timer' +require 'mspec/runner/mspec' +require 'time' + +describe TimerAction do + before :each do + @timer = TimerAction.new + @start_time = Time.utc(2009, 3, 30, 14, 5, 19) + @stop_time = Time.utc(2009, 3, 30, 14, 5, 52) + end + + it "responds to #start by recording the current time" do + Time.should_receive(:now) + @timer.start + end + + it "responds to #finish by recording the current time" do + Time.should_receive(:now) + @timer.finish + end + + it "responds to #elapsed by returning the difference between stop and start" do + Time.stub(:now).and_return(@start_time) + @timer.start + Time.stub(:now).and_return(@stop_time) + @timer.finish + @timer.elapsed.should == 33 + end + + it "responds to #format by returning a readable string of elapsed time" do + Time.stub(:now).and_return(@start_time) + @timer.start + Time.stub(:now).and_return(@stop_time) + @timer.finish + @timer.format.should == "Finished in 33.000000 seconds" + end + + it "responds to #register by registering itself with MSpec for appropriate actions" do + MSpec.should_receive(:register).with(:start, @timer) + MSpec.should_receive(:register).with(:finish, @timer) + @timer.register + end +end diff --git a/spec/mspec/spec/runner/context_spec.rb b/spec/mspec/spec/runner/context_spec.rb new file mode 100644 index 0000000000..f8759b639d --- /dev/null +++ b/spec/mspec/spec/runner/context_spec.rb @@ -0,0 +1,1041 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/matchers/base' +require 'mspec/runner/mspec' +require 'mspec/mocks/mock' +require 'mspec/runner/context' +require 'mspec/runner/example' + +describe ContextState, "#describe" do + before :each do + @state = ContextState.new "C#m" + @proc = lambda {|*| ScratchPad.record :a } + ScratchPad.clear + end + + it "evaluates the passed block" do + @state.describe(&@proc) + ScratchPad.recorded.should == :a + end + + it "evaluates the passed block via #protect" do + @state.should_receive(:protect).with("C#m", @proc, false) + @state.describe(&@proc) + end + + it "registers #parent as the current MSpec ContextState" do + parent = ContextState.new "" + @state.parent = parent + MSpec.should_receive(:register_current).with(parent) + @state.describe { } + end + + it "registers self with MSpec when #shared? is true" do + state = ContextState.new "something shared", :shared => true + MSpec.should_receive(:register_shared).with(state) + state.describe { } + end +end + +describe ContextState, "#shared?" do + it "returns false when the ContextState is not shared" do + ContextState.new("").shared?.should be_false + end + + it "returns true when the ContextState is shared" do + ContextState.new("", {:shared => true}).shared?.should be_true + end +end + +describe ContextState, "#to_s" do + it "returns a description string for self when passed a Module" do + ContextState.new(Object).to_s.should == "Object" + end + + it "returns a description string for self when passed a String" do + ContextState.new("SomeClass").to_s.should == "SomeClass" + end + + it "returns a description string for self when passed a Module, String" do + ContextState.new(Object, "when empty").to_s.should == "Object when empty" + end + + it "returns a description string for self when passed a Module and String beginning with '#'" do + ContextState.new(Object, "#to_s").to_s.should == "Object#to_s" + end + + it "returns a description string for self when passed a Module and String beginning with '.'" do + ContextState.new(Object, ".to_s").to_s.should == "Object.to_s" + end + + it "returns a description string for self when passed a Module and String beginning with '::'" do + ContextState.new(Object, "::to_s").to_s.should == "Object::to_s" + end +end + +describe ContextState, "#description" do + before :each do + @state = ContextState.new "when empty" + @parent = ContextState.new "Toplevel" + end + + it "returns a composite description string from self and all parents" do + @parent.description.should == "Toplevel" + @state.description.should == "when empty" + @state.parent = @parent + @state.description.should == "Toplevel when empty" + end +end + +describe ContextState, "#it" do + before :each do + @state = ContextState.new "" + @proc = lambda {|*| } + + @ex = ExampleState.new("", "", &@proc) + end + + it "creates an ExampleState instance for the block" do + ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex) + @state.describe(&@proc) + @state.it("it", &@proc) + end + + it "calls registered :add actions" do + ExampleState.should_receive(:new).with(@state, "it", @proc).and_return(@ex) + + add_action = double("add") + add_action.should_receive(:add).with(@ex).and_return { ScratchPad.record :add } + MSpec.register :add, add_action + + @state.it("it", &@proc) + ScratchPad.recorded.should == :add + MSpec.unregister :add, add_action + end +end + +describe ContextState, "#examples" do + before :each do + @state = ContextState.new "" + end + + it "returns a list of all examples in this ContextState" do + @state.it("first") { } + @state.it("second") { } + @state.examples.size.should == 2 + end +end + +describe ContextState, "#before" do + before :each do + @state = ContextState.new "" + @proc = lambda {|*| } + end + + it "records the block for :each" do + @state.before(:each, &@proc) + @state.before(:each).should == [@proc] + end + + it "records the block for :all" do + @state.before(:all, &@proc) + @state.before(:all).should == [@proc] + end +end + +describe ContextState, "#after" do + before :each do + @state = ContextState.new "" + @proc = lambda {|*| } + end + + it "records the block for :each" do + @state.after(:each, &@proc) + @state.after(:each).should == [@proc] + end + + it "records the block for :all" do + @state.after(:all, &@proc) + @state.after(:all).should == [@proc] + end +end + +describe ContextState, "#pre" do + before :each do + @a = lambda {|*| } + @b = lambda {|*| } + @c = lambda {|*| } + + parent = ContextState.new "" + parent.before(:each, &@c) + parent.before(:all, &@c) + + @state = ContextState.new "" + @state.parent = parent + end + + it "returns before(:each) actions in the order they were defined" do + @state.before(:each, &@a) + @state.before(:each, &@b) + @state.pre(:each).should == [@c, @a, @b] + end + + it "returns before(:all) actions in the order they were defined" do + @state.before(:all, &@a) + @state.before(:all, &@b) + @state.pre(:all).should == [@c, @a, @b] + end +end + +describe ContextState, "#post" do + before :each do + @a = lambda {|*| } + @b = lambda {|*| } + @c = lambda {|*| } + + parent = ContextState.new "" + parent.after(:each, &@c) + parent.after(:all, &@c) + + @state = ContextState.new "" + @state.parent = parent + end + + it "returns after(:each) actions in the reverse order they were defined" do + @state.after(:each, &@a) + @state.after(:each, &@b) + @state.post(:each).should == [@b, @a, @c] + end + + it "returns after(:all) actions in the reverse order they were defined" do + @state.after(:all, &@a) + @state.after(:all, &@b) + @state.post(:all).should == [@b, @a, @c] + end +end + +describe ContextState, "#protect" do + before :each do + ScratchPad.record [] + @a = lambda {|*| ScratchPad << :a } + @b = lambda {|*| ScratchPad << :b } + @c = lambda {|*| raise Exception, "Fail!" } + end + + it "returns true and does execute any blocks if check and MSpec.mode?(:pretend) are true" do + MSpec.should_receive(:mode?).with(:pretend).and_return(true) + ContextState.new("").protect("message", [@a, @b]).should be_true + ScratchPad.recorded.should == [] + end + + it "executes the blocks if MSpec.mode?(:pretend) is false" do + MSpec.should_receive(:mode?).with(:pretend).and_return(false) + ContextState.new("").protect("message", [@a, @b]) + ScratchPad.recorded.should == [:a, :b] + end + + it "executes the blocks if check is false" do + ContextState.new("").protect("message", [@a, @b], false) + ScratchPad.recorded.should == [:a, :b] + end + + it "returns true if none of the blocks raise an exception" do + ContextState.new("").protect("message", [@a, @b]).should be_true + end + + it "returns false if any of the blocks raise an exception" do + ContextState.new("").protect("message", [@a, @c, @b]).should be_false + end +end + +describe ContextState, "#parent=" do + before :each do + @state = ContextState.new "" + @parent = double("describe") + @parent.stub(:parent).and_return(nil) + @parent.stub(:child) + end + + it "does not set self as a child of parent if shared" do + @parent.should_not_receive(:child) + state = ContextState.new "", :shared => true + state.parent = @parent + end + + it "does not set parents if shared" do + state = ContextState.new "", :shared => true + state.parent = @parent + state.parents.should == [state] + end + + it "sets self as a child of parent" do + @parent.should_receive(:child).with(@state) + @state.parent = @parent + end + + it "creates the list of parents" do + @state.parent = @parent + @state.parents.should == [@parent, @state] + end +end + +describe ContextState, "#parent" do + before :each do + @state = ContextState.new "" + @parent = double("describe") + @parent.stub(:parent).and_return(nil) + @parent.stub(:child) + end + + it "returns nil if parent has not been set" do + @state.parent.should be_nil + end + + it "returns the parent" do + @state.parent = @parent + @state.parent.should == @parent + end +end + +describe ContextState, "#parents" do + before :each do + @first = ContextState.new "" + @second = ContextState.new "" + @parent = double("describe") + @parent.stub(:parent).and_return(nil) + @parent.stub(:child) + end + + it "returns a list of all enclosing ContextState instances" do + @first.parent = @parent + @second.parent = @first + @second.parents.should == [@parent, @first, @second] + end +end + +describe ContextState, "#child" do + before :each do + @first = ContextState.new "" + @second = ContextState.new "" + @parent = double("describe") + @parent.stub(:parent).and_return(nil) + @parent.stub(:child) + end + + it "adds the ContextState to the list of contained ContextStates" do + @first.child @second + @first.children.should == [@second] + end +end + +describe ContextState, "#children" do + before :each do + @parent = ContextState.new "" + @first = ContextState.new "" + @second = ContextState.new "" + end + + it "returns the list of directly contained ContextStates" do + @first.parent = @parent + @second.parent = @first + @parent.children.should == [@first] + @first.children.should == [@second] + end +end + +describe ContextState, "#state" do + before :each do + MSpec.store :before, [] + MSpec.store :after, [] + + @state = ContextState.new "" + end + + it "returns nil if no spec is being executed" do + @state.state.should == nil + end + + it "returns a ExampleState instance if an example is being executed" do + ScratchPad.record @state + @state.describe { } + @state.it("") { ScratchPad.record ScratchPad.recorded.state } + @state.process + @state.state.should == nil + ScratchPad.recorded.should be_kind_of(ExampleState) + end +end + +describe ContextState, "#process" do + before :each do + MSpec.store :before, [] + MSpec.store :after, [] + MSpec.stub(:register_current) + + @state = ContextState.new "" + @state.describe { } + + @a = lambda {|*| ScratchPad << :a } + @b = lambda {|*| ScratchPad << :b } + ScratchPad.record [] + end + + it "calls each before(:all) block" do + @state.before(:all, &@a) + @state.before(:all, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [:a, :b] + end + + it "calls each after(:all) block" do + @state.after(:all, &@a) + @state.after(:all, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [:b, :a] + end + + it "calls each it block" do + @state.it("one", &@a) + @state.it("two", &@b) + @state.process + ScratchPad.recorded.should == [:a, :b] + end + + it "does not call the #it block if #filtered? returns true" do + @state.it("one", &@a) + @state.it("two", &@b) + @state.examples.first.stub(:filtered?).and_return(true) + @state.process + ScratchPad.recorded.should == [:b] + end + + it "calls each before(:each) block" do + @state.before(:each, &@a) + @state.before(:each, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [:a, :b] + end + + it "calls each after(:each) block" do + @state.after(:each, &@a) + @state.after(:each, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [:b, :a] + end + + it "calls Mock.cleanup for each it block" do + @state.it("") { } + @state.it("") { } + Mock.should_receive(:cleanup).twice + @state.process + end + + it "calls Mock.verify_count for each it block" do + @state.it("") { } + @state.it("") { } + Mock.should_receive(:verify_count).twice + @state.process + end + + it "calls the describe block" do + ScratchPad.record [] + @state.describe { ScratchPad << :a } + @state.process + ScratchPad.recorded.should == [:a] + end + + it "creates a new ExampleState instance for each example" do + ScratchPad.record @state + @state.describe { } + @state.it("it") { ScratchPad.record ScratchPad.recorded.state } + @state.process + ScratchPad.recorded.should be_kind_of(ExampleState) + end + + it "clears the expectations flag before evaluating the #it block" do + MSpec.clear_expectations + MSpec.should_receive(:clear_expectations) + @state.it("it") { ScratchPad.record MSpec.expectation? } + @state.process + ScratchPad.recorded.should be_false + end + + it "shuffles the spec list if MSpec.randomize? is true" do + MSpec.randomize + MSpec.should_receive(:shuffle) + @state.it("") { } + @state.process + MSpec.randomize false + end + + it "sets the current MSpec ContextState" do + MSpec.should_receive(:register_current).with(@state) + @state.process + end + + it "resets the current MSpec ContextState to nil when there are examples" do + MSpec.should_receive(:register_current).with(nil) + @state.it("") { } + @state.process + end + + it "resets the current MSpec ContextState to nil when there are no examples" do + MSpec.should_receive(:register_current).with(nil) + @state.process + end + + it "call #process on children when there are examples" do + child = ContextState.new "" + child.should_receive(:process) + @state.child child + @state.it("") { } + @state.process + end + + it "call #process on children when there are no examples" do + child = ContextState.new "" + child.should_receive(:process) + @state.child child + @state.process + end +end + +describe ContextState, "#process" do + before :each do + MSpec.store :exception, [] + + @state = ContextState.new "" + @state.describe { } + + action = double("action") + def action.exception(exc) + ScratchPad.record :exception if exc.exception.is_a? SpecExpectationNotFoundError + end + MSpec.register :exception, action + + MSpec.clear_expectations + ScratchPad.clear + end + + after :each do + MSpec.store :exception, nil + end + + it "raises an SpecExpectationNotFoundError if an #it block does not contain an expectation" do + @state.it("it") { } + @state.process + ScratchPad.recorded.should == :exception + end + + it "does not raise an SpecExpectationNotFoundError if an #it block does contain an expectation" do + @state.it("it") { MSpec.expectation } + @state.process + ScratchPad.recorded.should be_nil + end + + it "does not raise an SpecExpectationNotFoundError if the #it block causes a failure" do + @state.it("it") { raise Exception, "Failed!" } + @state.process + ScratchPad.recorded.should be_nil + end +end + +describe ContextState, "#process" do + before :each do + MSpec.store :example, [] + + @state = ContextState.new "" + @state.describe { } + + example = double("example") + def example.example(state, spec) + ScratchPad << state << spec + end + MSpec.register :example, example + + ScratchPad.record [] + end + + after :each do + MSpec.store :example, nil + end + + it "calls registered :example actions with the current ExampleState and block" do + @state.it("") { MSpec.expectation } + @state.process + + ScratchPad.recorded.first.should be_kind_of(ExampleState) + ScratchPad.recorded.last.should be_kind_of(Proc) + end + + it "does not call registered example actions if the example has no block" do + @state.it("empty example") + @state.process + ScratchPad.recorded.should == [] + end +end + +describe ContextState, "#process" do + before :each do + MSpec.store :before, [] + MSpec.store :after, [] + + @state = ContextState.new "" + @state.describe { } + @state.it("") { MSpec.expectation } + end + + after :each do + MSpec.store :before, nil + MSpec.store :after, nil + end + + it "calls registered :before actions with the current ExampleState instance" do + before = double("before") + before.should_receive(:before).and_return { + ScratchPad.record :before + @spec_state = @state.state + } + MSpec.register :before, before + @state.process + ScratchPad.recorded.should == :before + @spec_state.should be_kind_of(ExampleState) + end + + it "calls registered :after actions with the current ExampleState instance" do + after = double("after") + after.should_receive(:after).and_return { + ScratchPad.record :after + @spec_state = @state.state + } + MSpec.register :after, after + @state.process + ScratchPad.recorded.should == :after + @spec_state.should be_kind_of(ExampleState) + end +end + +describe ContextState, "#process" do + before :each do + MSpec.store :enter, [] + MSpec.store :leave, [] + + @state = ContextState.new "C#m" + @state.describe { } + @state.it("") { MSpec.expectation } + end + + after :each do + MSpec.store :enter, nil + MSpec.store :leave, nil + end + + it "calls registered :enter actions with the current #describe string" do + enter = double("enter") + enter.should_receive(:enter).with("C#m").and_return { ScratchPad.record :enter } + MSpec.register :enter, enter + @state.process + ScratchPad.recorded.should == :enter + end + + it "calls registered :leave actions" do + leave = double("leave") + leave.should_receive(:leave).and_return { ScratchPad.record :leave } + MSpec.register :leave, leave + @state.process + ScratchPad.recorded.should == :leave + end +end + +describe ContextState, "#process when an exception is raised in before(:all)" do + before :each do + MSpec.store :before, [] + MSpec.store :after, [] + + @state = ContextState.new "" + @state.describe { } + + @a = lambda {|*| ScratchPad << :a } + @b = lambda {|*| ScratchPad << :b } + ScratchPad.record [] + + @state.before(:all) { raise Exception, "Fail!" } + end + + after :each do + MSpec.store :before, nil + MSpec.store :after, nil + end + + it "does not call before(:each)" do + @state.before(:each, &@a) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call the it block" do + @state.it("one", &@a) + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call after(:each)" do + @state.after(:each, &@a) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call after(:each)" do + @state.after(:all, &@a) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call Mock.verify_count" do + @state.it("") { } + Mock.should_not_receive(:verify_count) + @state.process + end + + it "calls Mock.cleanup" do + @state.it("") { } + Mock.should_receive(:cleanup) + @state.process + end +end + +describe ContextState, "#process when an exception is raised in before(:each)" do + before :each do + MSpec.store :before, [] + MSpec.store :after, [] + + @state = ContextState.new "" + @state.describe { } + + @a = lambda {|*| ScratchPad << :a } + @b = lambda {|*| ScratchPad << :b } + ScratchPad.record [] + + @state.before(:each) { raise Exception, "Fail!" } + end + + after :each do + MSpec.store :before, nil + MSpec.store :after, nil + end + + it "does not call the it block" do + @state.it("one", &@a) + @state.process + ScratchPad.recorded.should == [] + end + + it "does call after(:each)" do + @state.after(:each, &@a) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [:a] + end + + it "does not call Mock.verify_count" do + @state.it("") { } + Mock.should_not_receive(:verify_count) + @state.process + end +end + +describe ContextState, "#process in pretend mode" do + before :all do + MSpec.register_mode :pretend + end + + after :all do + MSpec.clear_modes + end + + before :each do + ScratchPad.clear + MSpec.store :before, [] + MSpec.store :after, [] + + @state = ContextState.new "" + @state.describe { } + @state.it("") { } + end + + after :each do + MSpec.store :before, nil + MSpec.store :after, nil + end + + it "calls registered :before actions with the current ExampleState instance" do + before = double("before") + before.should_receive(:before).and_return { + ScratchPad.record :before + @spec_state = @state.state + } + MSpec.register :before, before + @state.process + ScratchPad.recorded.should == :before + @spec_state.should be_kind_of(ExampleState) + end + + it "calls registered :after actions with the current ExampleState instance" do + after = double("after") + after.should_receive(:after).and_return { + ScratchPad.record :after + @spec_state = @state.state + } + MSpec.register :after, after + @state.process + ScratchPad.recorded.should == :after + @spec_state.should be_kind_of(ExampleState) + end +end + +describe ContextState, "#process in pretend mode" do + before :all do + MSpec.register_mode :pretend + end + + after :all do + MSpec.clear_modes + end + + before :each do + MSpec.store :before, [] + MSpec.store :after, [] + + @state = ContextState.new "" + @state.describe { } + + @a = lambda {|*| ScratchPad << :a } + @b = lambda {|*| ScratchPad << :b } + ScratchPad.record [] + end + + it "calls the describe block" do + ScratchPad.record [] + @state.describe { ScratchPad << :a } + @state.process + ScratchPad.recorded.should == [:a] + end + + it "does not call any before(:all) block" do + @state.before(:all, &@a) + @state.before(:all, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call any after(:all) block" do + @state.after(:all, &@a) + @state.after(:all, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call any it block" do + @state.it("one", &@a) + @state.it("two", &@b) + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call any before(:each) block" do + @state.before(:each, &@a) + @state.before(:each, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call any after(:each) block" do + @state.after(:each, &@a) + @state.after(:each, &@b) + @state.it("") { } + @state.process + ScratchPad.recorded.should == [] + end + + it "does not call Mock.cleanup" do + @state.it("") { } + @state.it("") { } + Mock.should_not_receive(:cleanup) + @state.process + end +end + +describe ContextState, "#process in pretend mode" do + before :all do + MSpec.register_mode :pretend + end + + after :all do + MSpec.clear_modes + end + + before :each do + MSpec.store :enter, [] + MSpec.store :leave, [] + + @state = ContextState.new "" + @state.describe { } + @state.it("") { } + end + + after :each do + MSpec.store :enter, nil + MSpec.store :leave, nil + end + + it "calls registered :enter actions with the current #describe string" do + enter = double("enter") + enter.should_receive(:enter).and_return { ScratchPad.record :enter } + MSpec.register :enter, enter + @state.process + ScratchPad.recorded.should == :enter + end + + it "calls registered :leave actions" do + leave = double("leave") + leave.should_receive(:leave).and_return { ScratchPad.record :leave } + MSpec.register :leave, leave + @state.process + ScratchPad.recorded.should == :leave + end +end + +describe ContextState, "#it_should_behave_like" do + before :each do + @shared_desc = :shared_context + @shared = ContextState.new(@shared_desc, :shared => true) + MSpec.stub(:retrieve_shared).and_return(@shared) + + @state = ContextState.new "Top level" + @a = lambda {|*| } + @b = lambda {|*| } + end + + it "raises an Exception if unable to find the shared ContextState" do + MSpec.should_receive(:retrieve_shared).and_return(nil) + lambda { @state.it_should_behave_like "this" }.should raise_error(Exception) + end + + describe "for nested ContextState instances" do + before :each do + @nested = ContextState.new "nested context" + @nested.parents.unshift @shared + + @shared.children << @nested + + @nested_dup = @nested.dup + @nested.stub(:dup).and_return(@nested_dup) + end + + it "duplicates the nested ContextState" do + @state.it_should_behave_like @shared_desc + @state.children.first.should equal(@nested_dup) + end + + it "sets the parent of the nested ContextState to the containing ContextState" do + @state.it_should_behave_like @shared_desc + @nested_dup.parent.should equal(@state) + end + + it "sets the context for nested examples to the nested ContextState's dup" do + @shared.it "an example", &@a + @shared.it "another example", &@b + @state.it_should_behave_like @shared_desc + @nested_dup.examples.each { |x| x.context.should equal(@nested_dup) } + end + + it "omits the shored ContextState's description" do + @nested.it "an example", &@a + @nested.it "another example", &@b + @state.it_should_behave_like @shared_desc + + @nested_dup.description.should == "Top level nested context" + @nested_dup.examples.first.description.should == "Top level nested context an example" + @nested_dup.examples.last.description.should == "Top level nested context another example" + end + end + + it "adds duped examples from the shared ContextState" do + @shared.it "some method", &@a + ex_dup = @shared.examples.first.dup + @shared.examples.first.stub(:dup).and_return(ex_dup) + + @state.it_should_behave_like @shared_desc + @state.examples.should == [ex_dup] + end + + it "sets the context for examples to the containing ContextState" do + @shared.it "an example", &@a + @shared.it "another example", &@b + @state.it_should_behave_like @shared_desc + @state.examples.each { |x| x.context.should equal(@state) } + end + + it "adds before(:all) blocks from the shared ContextState" do + @shared.before :all, &@a + @shared.before :all, &@b + @state.it_should_behave_like @shared_desc + @state.before(:all).should include(*@shared.before(:all)) + end + + it "adds before(:each) blocks from the shared ContextState" do + @shared.before :each, &@a + @shared.before :each, &@b + @state.it_should_behave_like @shared_desc + @state.before(:each).should include(*@shared.before(:each)) + end + + it "adds after(:each) blocks from the shared ContextState" do + @shared.after :each, &@a + @shared.after :each, &@b + @state.it_should_behave_like @shared_desc + @state.after(:each).should include(*@shared.after(:each)) + end + + it "adds after(:all) blocks from the shared ContextState" do + @shared.after :all, &@a + @shared.after :all, &@b + @state.it_should_behave_like @shared_desc + @state.after(:all).should include(*@shared.after(:all)) + end +end + +describe ContextState, "#filter_examples" do + before :each do + @state = ContextState.new "" + @state.it("one") { } + @state.it("two") { } + end + + it "removes examples that are filtered" do + @state.examples.first.stub(:filtered?).and_return(true) + @state.examples.size.should == 2 + @state.filter_examples + @state.examples.size.should == 1 + end + + it "returns true if there are remaining examples to evaluate" do + @state.examples.first.stub(:filtered?).and_return(true) + @state.filter_examples.should be_true + end + + it "returns false if there are no remaining examples to evaluate" do + @state.examples.first.stub(:filtered?).and_return(true) + @state.examples.last.stub(:filtered?).and_return(true) + @state.filter_examples.should be_false + end +end diff --git a/spec/mspec/spec/runner/example_spec.rb b/spec/mspec/spec/runner/example_spec.rb new file mode 100644 index 0000000000..b4391f802d --- /dev/null +++ b/spec/mspec/spec/runner/example_spec.rb @@ -0,0 +1,117 @@ +require 'spec_helper' +require 'mspec/matchers/base' +require 'mspec/runner/mspec' +require 'mspec/mocks/mock' +require 'mspec/runner/example' + +describe ExampleState do + it "is initialized with the ContextState, #it string, and #it block" do + prc = lambda { } + context = ContextState.new "" + ExampleState.new(context, "does", prc).should be_kind_of(ExampleState) + end +end + +describe ExampleState, "#describe" do + before :each do + @context = ContextState.new Object, "#to_s" + @state = ExampleState.new @context, "it" + end + + it "returns the ContextState#description" do + @state.describe.should == @context.description + end +end + +describe ExampleState, "#it" do + before :each do + @state = ExampleState.new ContextState.new("describe"), "it" + end + + it "returns the argument to the #it block" do + @state.it.should == "it" + end +end + +describe ExampleState, "#context=" do + before :each do + @state = ExampleState.new ContextState.new("describe"), "it" + @context = ContextState.new "New#context" + end + + it "sets the containing ContextState" do + @state.context = @context + @state.context.should == @context + end + + it "resets the description" do + @state.description.should == "describe it" + @state.context = @context + @state.description.should == "New#context it" + end +end + +describe ExampleState, "#example" do + before :each do + @proc = lambda { } + @state = ExampleState.new ContextState.new("describe"), "it", @proc + end + + it "returns the #it block" do + @state.example.should == @proc + end +end + +describe ExampleState, "#filtered?" do + before :each do + MSpec.store :include, nil + MSpec.store :exclude, nil + + @state = ExampleState.new ContextState.new("describe"), "it" + @filter = double("filter") + end + + after :each do + MSpec.store :include, nil + MSpec.store :exclude, nil + end + + it "returns false if MSpec include filters list is empty" do + @state.filtered?.should == false + end + + it "returns false if MSpec include filters match this spec" do + @filter.should_receive(:===).and_return(true) + MSpec.register :include, @filter + @state.filtered?.should == false + end + + it "returns true if MSpec include filters do not match this spec" do + @filter.should_receive(:===).and_return(false) + MSpec.register :include, @filter + @state.filtered?.should == true + end + + it "returns false if MSpec exclude filters list is empty" do + @state.filtered?.should == false + end + + it "returns false if MSpec exclude filters do not match this spec" do + @filter.should_receive(:===).and_return(false) + MSpec.register :exclude, @filter + @state.filtered?.should == false + end + + it "returns true if MSpec exclude filters match this spec" do + @filter.should_receive(:===).and_return(true) + MSpec.register :exclude, @filter + @state.filtered?.should == true + end + + it "returns true if MSpec include and exclude filters match this spec" do + @filter.should_receive(:===).twice.and_return(true) + MSpec.register :include, @filter + MSpec.register :exclude, @filter + @state.filtered?.should == true + end +end diff --git a/spec/mspec/spec/runner/exception_spec.rb b/spec/mspec/spec/runner/exception_spec.rb new file mode 100644 index 0000000000..309442435c --- /dev/null +++ b/spec/mspec/spec/runner/exception_spec.rb @@ -0,0 +1,146 @@ +require 'spec_helper' +require 'mspec/expectations/expectations' +require 'mspec/runner/example' +require 'mspec/runner/exception' +require 'mspec/utils/script' + +describe ExceptionState, "#initialize" do + it "takes a state, location (e.g. before :each), and exception" do + context = ContextState.new "Class#method" + state = ExampleState.new context, "does something" + exc = Exception.new "Fail!" + ExceptionState.new(state, "location", exc).should be_kind_of(ExceptionState) + end +end + +describe ExceptionState, "#description" do + before :each do + context = ContextState.new "Class#method" + @state = ExampleState.new context, "does something" + end + + it "returns the state description if state was not nil" do + exc = ExceptionState.new(@state, nil, nil) + exc.description.should == "Class#method does something" + end + + it "returns the location if it is not nil and description is nil" do + exc = ExceptionState.new(nil, "location", nil) + exc.description.should == "An exception occurred during: location" + end + + it "returns both description and location if neither are nil" do + exc = ExceptionState.new(@state, "location", nil) + exc.description.should == "An exception occurred during: location\nClass#method does something" + end +end + +describe ExceptionState, "#describe" do + before :each do + context = ContextState.new "Class#method" + @state = ExampleState.new context, "does something" + end + + it "returns the ExampleState#describe string if created with a non-nil state" do + ExceptionState.new(@state, nil, nil).describe.should == @state.describe + end + + it "returns an empty string if created with a nil state" do + ExceptionState.new(nil, nil, nil).describe.should == "" + end +end + +describe ExceptionState, "#it" do + before :each do + context = ContextState.new "Class#method" + @state = ExampleState.new context, "does something" + end + + it "returns the ExampleState#it string if created with a non-nil state" do + ExceptionState.new(@state, nil, nil).it.should == @state.it + end + + it "returns an empty string if created with a nil state" do + ExceptionState.new(nil, nil, nil).it.should == "" + end +end + +describe ExceptionState, "#failure?" do + before :each do + @state = ExampleState.new ContextState.new("C#m"), "works" + end + + it "returns true if the exception is an SpecExpectationNotMetError" do + exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!") + exc.failure?.should be_true + end + + it "returns true if the exception is an SpecExpectationNotFoundError" do + exc = ExceptionState.new @state, "", SpecExpectationNotFoundError.new("Fail!") + exc.failure?.should be_true + end + + it "returns false if the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do + exc = ExceptionState.new @state, "", Exception.new("Fail!") + exc.failure?.should be_false + end +end + +describe ExceptionState, "#message" do + before :each do + @state = ExampleState.new ContextState.new("C#m"), "works" + end + + it "returns if the exception message is empty" do + exc = ExceptionState.new @state, "", Exception.new("") + exc.message.should == "" + end + + it "returns the message without exception class when the exception is an SpecExpectationNotMetError" do + exc = ExceptionState.new @state, "", SpecExpectationNotMetError.new("Fail!") + exc.message.should == "Fail!" + end + + it "returns SpecExpectationNotFoundError#message when the exception is an SpecExpectationNotFoundError" do + e = SpecExpectationNotFoundError.new + exc = ExceptionState.new @state, "", e + exc.message.should == e.message + end + + it "returns the message with exception class when the exception is not an SpecExpectationNotMetError or an SpecExpectationNotFoundError" do + exc = ExceptionState.new @state, "", Exception.new("Fail!") + exc.message.should == "Exception: Fail!" + end +end + +describe ExceptionState, "#backtrace" do + before :each do + @state = ExampleState.new ContextState.new("C#m"), "works" + begin + raise Exception + rescue Exception => @exception + @exc = ExceptionState.new @state, "", @exception + end + end + + after :each do + $MSPEC_DEBUG = nil + end + + it "returns a string representation of the exception backtrace" do + @exc.backtrace.should be_kind_of(String) + end + + it "does not filter files from the backtrace if $MSPEC_DEBUG is true" do + $MSPEC_DEBUG = true + @exc.backtrace.should == @exception.backtrace.join("\n") + end + + it "filters files matching config[:backtrace_filter]" do + MSpecScript.set :backtrace_filter, %r[mspec/lib] + $MSPEC_DEBUG = nil + @exc.backtrace.split("\n").each do |line| + line.should_not =~ %r[mspec/lib] + end + end +end diff --git a/spec/mspec/spec/runner/filters/a.yaml b/spec/mspec/spec/runner/filters/a.yaml new file mode 100644 index 0000000000..1940e3cba6 --- /dev/null +++ b/spec/mspec/spec/runner/filters/a.yaml @@ -0,0 +1,4 @@ +--- +A#: +- a +- aa diff --git a/spec/mspec/spec/runner/filters/b.yaml b/spec/mspec/spec/runner/filters/b.yaml new file mode 100644 index 0000000000..a24bdb2f19 --- /dev/null +++ b/spec/mspec/spec/runner/filters/b.yaml @@ -0,0 +1,11 @@ +--- +B.: +- b +- bb +B::C#: +- b! +- b= +- b? +- "-" +- "[]" +- "[]=" diff --git a/spec/mspec/spec/runner/filters/match_spec.rb b/spec/mspec/spec/runner/filters/match_spec.rb new file mode 100644 index 0000000000..f2c665c495 --- /dev/null +++ b/spec/mspec/spec/runner/filters/match_spec.rb @@ -0,0 +1,34 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/mspec' +require 'mspec/runner/filters/match' + +describe MatchFilter, "#===" do + before :each do + @filter = MatchFilter.new nil, 'a', 'b', 'c' + end + + it "returns true if the argument matches any of the #initialize strings" do + @filter.===('aaa').should == true + @filter.===('bccb').should == true + end + + it "returns false if the argument matches none of the #initialize strings" do + @filter.===('d').should == false + end +end + +describe MatchFilter, "#register" do + it "registers itself with MSpec for the designated action list" do + filter = MatchFilter.new :include + MSpec.should_receive(:register).with(:include, filter) + filter.register + end +end + +describe MatchFilter, "#unregister" do + it "unregisters itself with MSpec for the designated action list" do + filter = MatchFilter.new :exclude + MSpec.should_receive(:unregister).with(:exclude, filter) + filter.unregister + end +end diff --git a/spec/mspec/spec/runner/filters/profile_spec.rb b/spec/mspec/spec/runner/filters/profile_spec.rb new file mode 100644 index 0000000000..78807bca5c --- /dev/null +++ b/spec/mspec/spec/runner/filters/profile_spec.rb @@ -0,0 +1,117 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/mspec' +require 'mspec/runner/filters/profile' + +describe ProfileFilter, "#find" do + before :each do + @filter = ProfileFilter.new nil + File.stub(:exist?).and_return(false) + @file = "rails.yaml" + end + + it "attempts to locate the file through the expanded path name" do + File.should_receive(:expand_path).with(@file).and_return(@file) + File.should_receive(:exist?).with(@file).and_return(true) + @filter.find(@file).should == @file + end + + it "attemps to locate the file in 'spec/profiles'" do + path = File.join "spec/profiles", @file + File.should_receive(:exist?).with(path).and_return(true) + @filter.find(@file).should == path + end + + it "attemps to locate the file in 'spec'" do + path = File.join "spec", @file + File.should_receive(:exist?).with(path).and_return(true) + @filter.find(@file).should == path + end + + it "attemps to locate the file in 'profiles'" do + path = File.join "profiles", @file + File.should_receive(:exist?).with(path).and_return(true) + @filter.find(@file).should == path + end + + it "attemps to locate the file in '.'" do + path = File.join ".", @file + File.should_receive(:exist?).with(path).and_return(true) + @filter.find(@file).should == path + end +end + +describe ProfileFilter, "#parse" do + before :each do + @filter = ProfileFilter.new nil + @file = File.open(File.dirname(__FILE__) + "/b.yaml", "r") + end + + after :each do + @file.close + end + + it "creates a Hash of the contents of the YAML file" do + @filter.parse(@file).should == { + "B." => ["b", "bb"], + "B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="] + } + end +end + +describe ProfileFilter, "#load" do + before :each do + @filter = ProfileFilter.new nil + @files = [ + File.dirname(__FILE__) + "/a.yaml", + File.dirname(__FILE__) + "/b.yaml" + ] + end + + it "generates a composite hash from multiple YAML files" do + @filter.load(*@files).should == { + "A#" => ["a", "aa"], + "B." => ["b", "bb"], + "B::C#" => ["b!", "b=", "b?", "-", "[]", "[]="] + } + end +end + +describe ProfileFilter, "#===" do + before :each do + @filter = ProfileFilter.new nil + @filter.stub(:load).and_return({ "A#" => ["[]=", "a", "a!", "a?", "aa="]}) + @filter.send :initialize, nil + end + + it "returns true if the spec description is for a method in the profile" do + @filter.===("The A#[]= method").should == true + @filter.===("A#a returns").should == true + @filter.===("A#a! replaces").should == true + @filter.===("A#a? returns").should == true + @filter.===("A#aa= raises").should == true + end + + it "returns false if the spec description is for a method not in the profile" do + @filter.===("The A#[] method").should == false + @filter.===("B#a returns").should == false + @filter.===("A.a! replaces").should == false + @filter.===("AA#a? returns").should == false + @filter.===("A#aa raises").should == false + end +end + +describe ProfileFilter, "#register" do + it "registers itself with MSpec for the designated action list" do + filter = ProfileFilter.new :include + MSpec.should_receive(:register).with(:include, filter) + filter.register + end +end + +describe ProfileFilter, "#unregister" do + it "unregisters itself with MSpec for the designated action list" do + filter = ProfileFilter.new :exclude + MSpec.should_receive(:unregister).with(:exclude, filter) + filter.unregister + end +end diff --git a/spec/mspec/spec/runner/filters/regexp_spec.rb b/spec/mspec/spec/runner/filters/regexp_spec.rb new file mode 100644 index 0000000000..6c05b0f42f --- /dev/null +++ b/spec/mspec/spec/runner/filters/regexp_spec.rb @@ -0,0 +1,13 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/mspec' +require 'mspec/runner/filters/regexp' + +describe RegexpFilter, "#to_regexp" do + before :each do + @filter = RegexpFilter.new nil + end + + it "converts its arguments to Regexp instances" do + @filter.to_regexp('a(b|c)', 'b[^ab]', 'cc?').should == [/a(b|c)/, /b[^ab]/, /cc?/] + end +end diff --git a/spec/mspec/spec/runner/filters/tag_spec.rb b/spec/mspec/spec/runner/filters/tag_spec.rb new file mode 100644 index 0000000000..fe1f3df039 --- /dev/null +++ b/spec/mspec/spec/runner/filters/tag_spec.rb @@ -0,0 +1,92 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/mspec' +require 'mspec/runner/filters/match' +require 'mspec/runner/filters/tag' + +describe TagFilter, "#load" do + before :each do + @match = double("match filter").as_null_object + @filter = TagFilter.new :include, "tag", "key" + @tag = SpecTag.new "tag(comment):description" + MSpec.stub(:read_tags).and_return([@tag]) + MSpec.stub(:register) + end + + it "loads tags from the tag file" do + MSpec.should_receive(:read_tags).with(["tag", "key"]).and_return([]) + @filter.load + end + + + it "registers itself with MSpec for the :include action" do + filter = TagFilter.new(:include) + MSpec.should_receive(:register).with(:include, filter) + filter.load + end + + it "registers itself with MSpec for the :exclude action" do + filter = TagFilter.new(:exclude) + MSpec.should_receive(:register).with(:exclude, filter) + filter.load + end +end + +describe TagFilter, "#unload" do + before :each do + @filter = TagFilter.new :include, "tag", "key" + @tag = SpecTag.new "tag(comment):description" + MSpec.stub(:read_tags).and_return([@tag]) + MSpec.stub(:register) + end + + it "unregisters itself" do + @filter.load + MSpec.should_receive(:unregister).with(:include, @filter) + @filter.unload + end +end + +describe TagFilter, "#register" do + before :each do + MSpec.stub(:register) + end + + it "registers itself with MSpec for the :load, :unload actions" do + filter = TagFilter.new(nil) + MSpec.should_receive(:register).with(:load, filter) + MSpec.should_receive(:register).with(:unload, filter) + filter.register + end +end + +describe TagFilter, "#unregister" do + before :each do + MSpec.stub(:unregister) + end + + it "unregisters itself with MSpec for the :load, :unload actions" do + filter = TagFilter.new(nil) + MSpec.should_receive(:unregister).with(:load, filter) + MSpec.should_receive(:unregister).with(:unload, filter) + filter.unregister + end +end + +describe TagFilter, "#===" do + before :each do + @filter = TagFilter.new nil, "tag", "key" + @tag = SpecTag.new "tag(comment):description" + MSpec.stub(:read_tags).and_return([@tag]) + MSpec.stub(:register) + @filter.load + end + + it "returns true if the argument matches any of the descriptions" do + @filter.===('description').should == true + end + + it "returns false if the argument matches none of the descriptions" do + @filter.===('descriptionA').should == false + @filter.===('adescription').should == false + end +end diff --git a/spec/mspec/spec/runner/formatters/describe_spec.rb b/spec/mspec/spec/runner/formatters/describe_spec.rb new file mode 100644 index 0000000000..415ced71fb --- /dev/null +++ b/spec/mspec/spec/runner/formatters/describe_spec.rb @@ -0,0 +1,67 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/describe' +require 'mspec/runner/example' + +describe DescribeFormatter, "#finish" do + before :each do + MSpec.stub(:register) + MSpec.stub(:unregister) + + @timer = double("timer").as_null_object + TimerAction.stub(:new).and_return(@timer) + @timer.stub(:format).and_return("Finished in 2.0 seconds") + + $stdout = @out = IOStub.new + context = ContextState.new "Class#method" + @state = ExampleState.new(context, "runs") + + @formatter = DescribeFormatter.new + @formatter.register + + @tally = @formatter.tally + @counter = @tally.counter + + @counter.files! + @counter.examples! + @counter.expectations! 2 + end + + after :each do + $stdout = STDOUT + end + + it "prints a summary of elapsed time" do + @formatter.finish + @out.should =~ /^Finished in 2.0 seconds$/ + end + + it "prints a tally of counts" do + @formatter.finish + @out.should =~ /^1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged$/ + end + + it "does not print exceptions" do + @formatter.finish + @out.should == %[ + +Finished in 2.0 seconds + +1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged +] + end + + it "prints a summary of failures and errors for each describe block" do + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @formatter.finish + @out.should == %[ + +Class#method 0 failures, 1 error + +Finished in 2.0 seconds + +1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged +] + end +end diff --git a/spec/mspec/spec/runner/formatters/dotted_spec.rb b/spec/mspec/spec/runner/formatters/dotted_spec.rb new file mode 100644 index 0000000000..1e9b06f6e1 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/dotted_spec.rb @@ -0,0 +1,285 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/dotted' +require 'mspec/runner/mspec' +require 'mspec/runner/example' +require 'mspec/utils/script' + +describe DottedFormatter, "#initialize" do + it "permits zero arguments" do + DottedFormatter.new + end + + it "accepts one argument" do + DottedFormatter.new nil + end +end + +describe DottedFormatter, "#register" do + before :each do + @formatter = DottedFormatter.new + MSpec.stub(:register) + end + + it "registers self with MSpec for appropriate actions" do + MSpec.should_receive(:register).with(:exception, @formatter) + MSpec.should_receive(:register).with(:before, @formatter) + MSpec.should_receive(:register).with(:after, @formatter) + MSpec.should_receive(:register).with(:finish, @formatter) + @formatter.register + end + + it "creates TimerAction and TallyAction" do + timer = double("timer") + tally = double("tally") + timer.should_receive(:register) + tally.should_receive(:register) + tally.should_receive(:counter) + TimerAction.should_receive(:new).and_return(timer) + TallyAction.should_receive(:new).and_return(tally) + @formatter.register + end +end + +describe DottedFormatter, "#print" do + before :each do + $stdout = IOStub.new + end + + after :each do + $stdout = STDOUT + end + + it "writes to $stdout by default" do + formatter = DottedFormatter.new + formatter.print "begonias" + $stdout.should == "begonias" + end + + it "writes to the file specified when the formatter was created" do + out = IOStub.new + File.should_receive(:open).with("some/file", "w").and_return(out) + formatter = DottedFormatter.new "some/file" + formatter.print "begonias" + out.should == "begonias" + end + + it "flushes the IO output" do + $stdout.should_receive(:flush) + formatter = DottedFormatter.new + formatter.print "begonias" + end +end + +describe DottedFormatter, "#exception" do + before :each do + @formatter = DottedFormatter.new + @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed") + @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!") + end + + it "sets the #failure? flag" do + @formatter.exception @failure + @formatter.failure?.should be_true + @formatter.exception @error + @formatter.failure?.should be_false + end + + it "sets the #exception? flag" do + @formatter.exception @error + @formatter.exception?.should be_true + @formatter.exception @failure + @formatter.exception?.should be_true + end + + it "addes the exception to the list of exceptions" do + @formatter.exceptions.should == [] + @formatter.exception @error + @formatter.exception @failure + @formatter.exceptions.should == [@error, @failure] + end +end + +describe DottedFormatter, "#exception?" do + before :each do + @formatter = DottedFormatter.new + @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed") + @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!") + end + + it "returns false if there have been no exceptions" do + @formatter.exception?.should be_false + end + + it "returns true if any exceptions are errors" do + @formatter.exception @failure + @formatter.exception @error + @formatter.exception?.should be_true + end + + it "returns true if all exceptions are failures" do + @formatter.exception @failure + @formatter.exception @failure + @formatter.exception?.should be_true + end + + it "returns true if all exceptions are errors" do + @formatter.exception @error + @formatter.exception @error + @formatter.exception?.should be_true + end +end + +describe DottedFormatter, "#failure?" do + before :each do + @formatter = DottedFormatter.new + @failure = ExceptionState.new nil, nil, SpecExpectationNotMetError.new("failed") + @error = ExceptionState.new nil, nil, MSpecExampleError.new("boom!") + end + + it "returns false if there have been no exceptions" do + @formatter.failure?.should be_false + end + + it "returns false if any exceptions are errors" do + @formatter.exception @failure + @formatter.exception @error + @formatter.failure?.should be_false + end + + it "returns true if all exceptions are failures" do + @formatter.exception @failure + @formatter.exception @failure + @formatter.failure?.should be_true + end +end + +describe DottedFormatter, "#before" do + before :each do + @state = ExampleState.new ContextState.new("describe"), "it" + @formatter = DottedFormatter.new + @formatter.exception ExceptionState.new(nil, nil, SpecExpectationNotMetError.new("Failed!")) + end + + it "resets the #failure? flag to false" do + @formatter.failure?.should be_true + @formatter.before @state + @formatter.failure?.should be_false + end + + it "resets the #exception? flag to false" do + @formatter.exception?.should be_true + @formatter.before @state + @formatter.exception?.should be_false + end +end + +describe DottedFormatter, "#after" do + before :each do + $stdout = @out = IOStub.new + @formatter = DottedFormatter.new + @state = ExampleState.new ContextState.new("describe"), "it" + end + + after :each do + $stdout = STDOUT + end + + it "prints a '.' if there was no exception raised" do + @formatter.after(@state) + @out.should == "." + end + + it "prints an 'F' if there was an expectation failure" do + exc = SpecExpectationNotMetError.new "failed" + @formatter.exception ExceptionState.new(@state, nil, exc) + @formatter.after(@state) + @out.should == "F" + end + + it "prints an 'E' if there was an exception other than expectation failure" do + exc = MSpecExampleError.new("boom!") + @formatter.exception ExceptionState.new(@state, nil, exc) + @formatter.after(@state) + @out.should == "E" + end + + it "prints an 'E' if there are mixed exceptions and exepctation failures" do + exc = SpecExpectationNotMetError.new "failed" + @formatter.exception ExceptionState.new(@state, nil, exc) + exc = MSpecExampleError.new("boom!") + @formatter.exception ExceptionState.new(@state, nil, exc) + @formatter.after(@state) + @out.should == "E" + end +end + +describe DottedFormatter, "#finish" do + before :each do + @tally = double("tally").as_null_object + TallyAction.stub(:new).and_return(@tally) + @timer = double("timer").as_null_object + TimerAction.stub(:new).and_return(@timer) + + $stdout = @out = IOStub.new + context = ContextState.new "Class#method" + @state = ExampleState.new(context, "runs") + MSpec.stub(:register) + @formatter = DottedFormatter.new + @formatter.register + end + + after :each do + $stdout = STDOUT + end + + it "prints a failure message for an exception" do + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken") + @formatter.exception exc + @formatter.after @state + @formatter.finish + @out.should =~ /^1\)\nClass#method runs ERROR$/ + end + + it "prints a backtrace for an exception" do + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @formatter.after @state + @formatter.finish + @out.should =~ %r[path/to/some/file.rb:35:in method$] + end + + it "prints a summary of elapsed time" do + @timer.should_receive(:format).and_return("Finished in 2.0 seconds") + @formatter.finish + @out.should =~ /^Finished in 2.0 seconds$/ + end + + it "prints a tally of counts" do + @tally.should_receive(:format).and_return("1 example, 0 failures") + @formatter.finish + @out.should =~ /^1 example, 0 failures$/ + end + + it "prints errors, backtraces, elapsed time, and tallies" do + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @timer.should_receive(:format).and_return("Finished in 2.0 seconds") + @tally.should_receive(:format).and_return("1 example, 1 failure") + @formatter.after @state + @formatter.finish + @out.should == +%[E + +1) +Class#method runs ERROR +MSpecExampleError: broken +path/to/some/file.rb:35:in method + +Finished in 2.0 seconds + +1 example, 1 failure +] + end +end diff --git a/spec/mspec/spec/runner/formatters/file_spec.rb b/spec/mspec/spec/runner/formatters/file_spec.rb new file mode 100644 index 0000000000..946683ad58 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/file_spec.rb @@ -0,0 +1,84 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/file' +require 'mspec/runner/mspec' +require 'mspec/runner/example' + +describe FileFormatter, "#register" do + before :each do + @formatter = FileFormatter.new + MSpec.stub(:register) + MSpec.stub(:unregister) + end + + it "registers self with MSpec for :load, :unload actions" do + MSpec.should_receive(:register).with(:load, @formatter) + MSpec.should_receive(:register).with(:unload, @formatter) + @formatter.register + end + + it "unregisters self with MSpec for :before, :after actions" do + MSpec.should_receive(:unregister).with(:before, @formatter) + MSpec.should_receive(:unregister).with(:after, @formatter) + @formatter.register + end +end + +describe FileFormatter, "#load" do + before :each do + @state = ExampleState.new ContextState.new("describe"), "it" + @formatter = FileFormatter.new + @formatter.exception ExceptionState.new(nil, nil, SpecExpectationNotMetError.new("Failed!")) + end + + it "resets the #failure? flag to false" do + @formatter.failure?.should be_true + @formatter.load @state + @formatter.failure?.should be_false + end + + it "resets the #exception? flag to false" do + @formatter.exception?.should be_true + @formatter.load @state + @formatter.exception?.should be_false + end +end + +describe FileFormatter, "#unload" do + before :each do + $stdout = @out = IOStub.new + @formatter = FileFormatter.new + @state = ExampleState.new ContextState.new("describe"), "it" + end + + after :each do + $stdout = STDOUT + end + + it "prints a '.' if there was no exception raised" do + @formatter.unload(@state) + @out.should == "." + end + + it "prints an 'F' if there was an expectation failure" do + exc = SpecExpectationNotMetError.new "failed" + @formatter.exception ExceptionState.new(@state, nil, exc) + @formatter.unload(@state) + @out.should == "F" + end + + it "prints an 'E' if there was an exception other than expectation failure" do + exc = MSpecExampleError.new("boom!") + @formatter.exception ExceptionState.new(@state, nil, exc) + @formatter.unload(@state) + @out.should == "E" + end + + it "prints an 'E' if there are mixed exceptions and exepctation failures" do + exc = SpecExpectationNotMetError.new "failed" + @formatter.exception ExceptionState.new(@state, nil, exc) + exc = MSpecExampleError.new("boom!") + @formatter.exception ExceptionState.new(@state, nil, exc) + @formatter.unload(@state) + @out.should == "E" + end +end diff --git a/spec/mspec/spec/runner/formatters/html_spec.rb b/spec/mspec/spec/runner/formatters/html_spec.rb new file mode 100644 index 0000000000..d2aff1b2a7 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/html_spec.rb @@ -0,0 +1,217 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/utils/ruby_name' +require 'mspec/guards/guard' +require 'mspec/runner/formatters/html' +require 'mspec/runner/mspec' +require 'mspec/runner/example' +require 'mspec/utils/script' + +describe HtmlFormatter do + before :each do + @formatter = HtmlFormatter.new + end + + it "responds to #register by registering itself with MSpec for appropriate actions" do + MSpec.stub(:register) + MSpec.should_receive(:register).with(:start, @formatter) + MSpec.should_receive(:register).with(:enter, @formatter) + MSpec.should_receive(:register).with(:leave, @formatter) + @formatter.register + end +end + +describe HtmlFormatter, "#start" do + before :each do + $stdout = @out = IOStub.new + @formatter = HtmlFormatter.new + end + + after :each do + $stdout = STDOUT + end + + it "prints the HTML head" do + @formatter.start + ruby_name = RUBY_NAME + ruby_name.should =~ /^#{ruby_name}/ + @out.should == +%[ + + +Spec Output For #{ruby_name} (#{RUBY_VERSION}) + + + +] + end +end + +describe HtmlFormatter, "#enter" do + before :each do + $stdout = @out = IOStub.new + @formatter = HtmlFormatter.new + end + + after :each do + $stdout = STDOUT + end + + it "prints the #describe string" do + @formatter.enter "describe" + @out.should == "

    describe

    \n
      \n" + end +end + +describe HtmlFormatter, "#leave" do + before :each do + $stdout = @out = IOStub.new + @formatter = HtmlFormatter.new + end + + after :each do + $stdout = STDOUT + end + + it "prints the closing tags for the #describe string" do + @formatter.leave + @out.should == "
    \n
    \n" + end +end + +describe HtmlFormatter, "#exception" do + before :each do + $stdout = @out = IOStub.new + @formatter = HtmlFormatter.new + @formatter.register + @state = ExampleState.new ContextState.new("describe"), "it" + end + + after :each do + $stdout = STDOUT + end + + it "prints the #it string once for each exception raised" do + exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing") + @formatter.exception exc + exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful") + @formatter.exception exc + @out.should == +%[
  • - it (FAILED - 1)
  • +
  • - it (ERROR - 2)
  • +] + end +end + +describe HtmlFormatter, "#after" do + before :each do + $stdout = @out = IOStub.new + @formatter = HtmlFormatter.new + @formatter.register + @state = ExampleState.new ContextState.new("describe"), "it" + end + + after :each do + $stdout = STDOUT + end + + it "prints the #it once when there are no exceptions raised" do + @formatter.after @state + @out.should == %[
  • - it
  • \n] + end + + it "does not print any output if an exception is raised" do + exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing") + @formatter.exception exc + out = @out.dup + @formatter.after @state + @out.should == out + end +end + +describe HtmlFormatter, "#finish" do + before :each do + @tally = double("tally").as_null_object + TallyAction.stub(:new).and_return(@tally) + @timer = double("timer").as_null_object + TimerAction.stub(:new).and_return(@timer) + + $stdout = @out = IOStub.new + context = ContextState.new "describe" + @state = ExampleState.new(context, "it") + MSpec.stub(:register) + @formatter = HtmlFormatter.new + @formatter.register + @exception = MSpecExampleError.new("broken") + @exception.stub(:backtrace).and_return(["file.rb:1", "file.rb:2"]) + end + + after :each do + $stdout = STDOUT + end + + it "prints a failure message for an exception" do + exc = ExceptionState.new @state, nil, @exception + @formatter.exception exc + @formatter.finish + @out.should include "

    describe it ERROR

    " + end + + it "prints a backtrace for an exception" do + exc = ExceptionState.new @state, nil, @exception + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @formatter.finish + @out.should =~ %r[
    .*path/to/some/file.rb:35:in method.*
    ]m + end + + it "prints a summary of elapsed time" do + @timer.should_receive(:format).and_return("Finished in 2.0 seconds") + @formatter.finish + @out.should include "

    Finished in 2.0 seconds

    \n" + end + + it "prints a tally of counts" do + @tally.should_receive(:format).and_return("1 example, 0 failures") + @formatter.finish + @out.should include '

    1 example, 0 failures

    ' + end + + it "prints errors, backtraces, elapsed time, and tallies" do + exc = ExceptionState.new @state, nil, @exception + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + + @timer.should_receive(:format).and_return("Finished in 2.0 seconds") + @tally.should_receive(:format).and_return("1 example, 1 failures") + @formatter.finish + @out.should == +%[
  • - it (ERROR - 1)
  • +
    +
      +
    1. describe it ERROR

      +

      MSpecExampleError: broken

      +
      +path/to/some/file.rb:35:in method
      +
    2. +
    +

    Finished in 2.0 seconds

    +

    1 example, 1 failures

    + + +] + end +end diff --git a/spec/mspec/spec/runner/formatters/junit_spec.rb b/spec/mspec/spec/runner/formatters/junit_spec.rb new file mode 100644 index 0000000000..66e7d70e92 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/junit_spec.rb @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/junit' +require 'mspec/runner/example' + +describe JUnitFormatter, "#initialize" do + it "permits zero arguments" do + lambda { JUnitFormatter.new }.should_not raise_error + end + + it "accepts one argument" do + lambda { JUnitFormatter.new nil }.should_not raise_error + end +end + +describe JUnitFormatter, "#print" do + before :each do + $stdout = IOStub.new + @out = IOStub.new + File.stub(:open).and_return(@out) + @formatter = JUnitFormatter.new "some/file" + end + + after :each do + $stdout = STDOUT + end + + it "writes to $stdout if #switch has not been called" do + @formatter.print "begonias" + $stdout.should == "begonias" + @out.should == "" + end + + it "writes to the file passed to #initialize once #switch has been called" do + @formatter.switch + @formatter.print "begonias" + $stdout.should == "" + @out.should == "begonias" + end + + it "writes to $stdout once #switch is called if no file was passed to #initialize" do + formatter = JUnitFormatter.new + formatter.switch + formatter.print "begonias" + $stdout.should == "begonias" + @out.should == "" + end +end + +describe JUnitFormatter, "#finish" do + before :each do + @tally = double("tally").as_null_object + @counter = double("counter").as_null_object + @tally.stub(:counter).and_return(@counter) + TallyAction.stub(:new).and_return(@tally) + + @timer = double("timer").as_null_object + TimerAction.stub(:new).and_return(@timer) + + $stdout = IOStub.new + context = ContextState.new "describe" + @state = ExampleState.new(context, "it") + + @formatter = JUnitFormatter.new + @formatter.stub(:backtrace).and_return("") + MSpec.stub(:register) + @formatter.register + + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @formatter.after @state + end + + after :each do + $stdout = STDOUT + end + + it "calls #switch" do + @formatter.should_receive(:switch) + @formatter.finish + end + + it "outputs a failure message and backtrace" do + @formatter.finish + $stdout.should include 'message="error in describe it" type="error"' + $stdout.should include "MSpecExampleError: broken\n" + $stdout.should include "path/to/some/file.rb:35:in method" + end + + it "encodes message and backtrace in latin1 for jenkins" do + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken…") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in methød") + @formatter.exception exc + @formatter.finish + $stdout.should =~ /MSpecExampleError: broken((\.\.\.)|\?)\n/ + $stdout.should =~ /path\/to\/some\/file\.rb:35:in meth(\?|o)d/ + end + + it "outputs an elapsed time" do + @timer.should_receive(:elapsed).and_return(4.2) + @formatter.finish + $stdout.should include 'time="4.2"' + end + + it "outputs overall elapsed time" do + @timer.should_receive(:elapsed).and_return(4.2) + @formatter.finish + $stdout.should include 'timeCount="4.2"' + end + + it "outputs the number of examples as test count" do + @counter.should_receive(:examples).and_return(9) + @formatter.finish + $stdout.should include 'tests="9"' + end + + it "outputs overall number of examples as test count" do + @counter.should_receive(:examples).and_return(9) + @formatter.finish + $stdout.should include 'testCount="9"' + end + + it "outputs a failure count" do + @counter.should_receive(:failures).and_return(2) + @formatter.finish + $stdout.should include 'failureCount="2"' + end + + it "outputs overall failure count" do + @counter.should_receive(:failures).and_return(2) + @formatter.finish + $stdout.should include 'failures="2"' + end + + it "outputs an error count" do + @counter.should_receive(:errors).and_return(1) + @formatter.finish + $stdout.should include 'errors="1"' + end + + it "outputs overall error count" do + @counter.should_receive(:errors).and_return(1) + @formatter.finish + $stdout.should include 'errorCount="1"' + end +end diff --git a/spec/mspec/spec/runner/formatters/method_spec.rb b/spec/mspec/spec/runner/formatters/method_spec.rb new file mode 100644 index 0000000000..77204f74c5 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/method_spec.rb @@ -0,0 +1,178 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/method' +require 'mspec/runner/mspec' +require 'mspec/runner/example' +require 'mspec/utils/script' + +describe MethodFormatter, "#method_type" do + before :each do + @formatter = MethodFormatter.new + end + + it "returns 'class' if the separator is '.' or '::'" do + @formatter.method_type('.').should == "class" + @formatter.method_type('::').should == "class" + end + + it "returns 'instance' if the separator is '#'" do + @formatter.method_type('#').should == "instance" + end + + it "returns 'unknown' for all other cases" do + @formatter.method_type(nil).should == "unknown" + end +end + +describe MethodFormatter, "#before" do + before :each do + @formatter = MethodFormatter.new + MSpec.stub(:register) + @formatter.register + end + + it "resets the tally counters to 0" do + @formatter.tally.counter.examples = 3 + @formatter.tally.counter.expectations = 4 + @formatter.tally.counter.failures = 2 + @formatter.tally.counter.errors = 1 + + state = ExampleState.new ContextState.new("describe"), "it" + @formatter.before state + @formatter.tally.counter.examples.should == 0 + @formatter.tally.counter.expectations.should == 0 + @formatter.tally.counter.failures.should == 0 + @formatter.tally.counter.errors.should == 0 + end + + it "records the class, method if available" do + state = ExampleState.new ContextState.new("Some#method"), "it" + @formatter.before state + key = "Some#method" + @formatter.methods.keys.should include(key) + h = @formatter.methods[key] + h[:class].should == "Some" + h[:method].should == "method" + h[:description].should == "Some#method it" + end + + it "does not record class, method unless both are available" do + state = ExampleState.new ContextState.new("Some method"), "it" + @formatter.before state + key = "Some method" + @formatter.methods.keys.should include(key) + h = @formatter.methods[key] + h[:class].should == "" + h[:method].should == "" + h[:description].should == "Some method it" + end + + it "sets the method type to unknown if class and method are not available" do + state = ExampleState.new ContextState.new("Some method"), "it" + @formatter.before state + key = "Some method" + h = @formatter.methods[key] + h[:type].should == "unknown" + end + + it "sets the method type based on the class, method separator" do + [["C#m", "instance"], ["C.m", "class"], ["C::m", "class"]].each do |k, t| + state = ExampleState.new ContextState.new(k), "it" + @formatter.before state + h = @formatter.methods[k] + h[:type].should == t + end + end + + it "clears the list of exceptions" do + state = ExampleState.new ContextState.new("describe"), "it" + @formatter.exceptions << "stuff" + @formatter.before state + @formatter.exceptions.should be_empty + end +end + +describe MethodFormatter, "#after" do + before :each do + @formatter = MethodFormatter.new + MSpec.stub(:register) + @formatter.register + end + + it "sets the tally counts" do + state = ExampleState.new ContextState.new("Some#method"), "it" + @formatter.before state + + @formatter.tally.counter.examples = 3 + @formatter.tally.counter.expectations = 4 + @formatter.tally.counter.failures = 2 + @formatter.tally.counter.errors = 1 + + @formatter.after state + h = @formatter.methods["Some#method"] + h[:examples].should == 3 + h[:expectations].should == 4 + h[:failures].should == 2 + h[:errors].should == 1 + end + + it "renders the list of exceptions" do + state = ExampleState.new ContextState.new("Some#method"), "it" + @formatter.before state + + exc = SpecExpectationNotMetError.new "failed" + @formatter.exception ExceptionState.new(state, nil, exc) + @formatter.exception ExceptionState.new(state, nil, exc) + + @formatter.after state + h = @formatter.methods["Some#method"] + h[:exceptions].should == [ + %[failed\n\n], + %[failed\n\n] + ] + end +end + +describe MethodFormatter, "#after" do + before :each do + $stdout = IOStub.new + context = ContextState.new "Class#method" + @state = ExampleState.new(context, "runs") + @formatter = MethodFormatter.new + MSpec.stub(:register) + @formatter.register + end + + after :each do + $stdout = STDOUT + end + + it "prints a summary of the results of an example in YAML format" do + @formatter.before @state + @formatter.tally.counter.examples = 3 + @formatter.tally.counter.expectations = 4 + @formatter.tally.counter.failures = 2 + @formatter.tally.counter.errors = 1 + + exc = SpecExpectationNotMetError.new "failed" + @formatter.exception ExceptionState.new(@state, nil, exc) + @formatter.exception ExceptionState.new(@state, nil, exc) + + @formatter.after @state + @formatter.finish + $stdout.should == +%[--- +"Class#method": + class: "Class" + method: "method" + type: instance + description: "Class#method runs" + examples: 3 + expectations: 4 + failures: 2 + errors: 1 + exceptions: + - "failed\\n\\n" + - "failed\\n\\n" +] + end +end diff --git a/spec/mspec/spec/runner/formatters/multi_spec.rb b/spec/mspec/spec/runner/formatters/multi_spec.rb new file mode 100644 index 0000000000..afcc7e9cea --- /dev/null +++ b/spec/mspec/spec/runner/formatters/multi_spec.rb @@ -0,0 +1,68 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/multi' +require 'mspec/runner/example' + +describe MultiFormatter, "#aggregate_results" do + before :each do + @stdout, $stdout = $stdout, IOStub.new + + @file = double("file").as_null_object + + File.stub(:delete) + YAML.stub(:load) + + @hash = { "files"=>1, "examples"=>1, "expectations"=>2, "failures"=>0, "errors"=>0 } + File.stub(:open).and_yield(@file).and_return(@hash) + + @formatter = MultiFormatter.new + @formatter.timer.stub(:format).and_return("Finished in 42 seconds") + end + + after :each do + $stdout = @stdout + end + + it "outputs a summary without errors" do + @formatter.aggregate_results(["a", "b"]) + @formatter.finish + $stdout.should == +%[ + +Finished in 42 seconds + +2 files, 2 examples, 4 expectations, 0 failures, 0 errors, 0 tagged +] + end + + it "outputs a summary with errors" do + @hash["exceptions"] = [ + "Some#method works real good FAILED\nExpected real good\n to equal fail\n\nfoo.rb:1\nfoo.rb:2", + "Some#method never fails ERROR\nExpected 5\n to equal 3\n\nfoo.rb:1\nfoo.rb:2" + ] + @formatter.aggregate_results(["a"]) + @formatter.finish + $stdout.should == +%[ + +1) +Some#method works real good FAILED +Expected real good + to equal fail + +foo.rb:1 +foo.rb:2 + +2) +Some#method never fails ERROR +Expected 5 + to equal 3 + +foo.rb:1 +foo.rb:2 + +Finished in 42 seconds + +1 file, 1 example, 2 expectations, 0 failures, 0 errors, 0 tagged +] + end +end diff --git a/spec/mspec/spec/runner/formatters/specdoc_spec.rb b/spec/mspec/spec/runner/formatters/specdoc_spec.rb new file mode 100644 index 0000000000..edb439fc11 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/specdoc_spec.rb @@ -0,0 +1,106 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/specdoc' +require 'mspec/runner/example' + +describe SpecdocFormatter do + before :each do + @formatter = SpecdocFormatter.new + end + + it "responds to #register by registering itself with MSpec for appropriate actions" do + MSpec.stub(:register) + MSpec.should_receive(:register).with(:enter, @formatter) + @formatter.register + end +end + +describe SpecdocFormatter, "#enter" do + before :each do + $stdout = @out = IOStub.new + @formatter = SpecdocFormatter.new + end + + after :each do + $stdout = STDOUT + end + + it "prints the #describe string" do + @formatter.enter("describe") + @out.should == "\ndescribe\n" + end +end + +describe SpecdocFormatter, "#before" do + before :each do + $stdout = @out = IOStub.new + @formatter = SpecdocFormatter.new + @state = ExampleState.new ContextState.new("describe"), "it" + end + + after :each do + $stdout = STDOUT + end + + it "prints the #it string" do + @formatter.before @state + @out.should == "- it" + end + + it "resets the #exception? flag" do + exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing") + @formatter.exception exc + @formatter.exception?.should be_true + @formatter.before @state + @formatter.exception?.should be_false + end +end + +describe SpecdocFormatter, "#exception" do + before :each do + $stdout = @out = IOStub.new + @formatter = SpecdocFormatter.new + context = ContextState.new "describe" + @state = ExampleState.new context, "it" + end + + after :each do + $stdout = STDOUT + end + + it "prints 'ERROR' if an exception is not an SpecExpectationNotMetError" do + exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful") + @formatter.exception exc + @out.should == " (ERROR - 1)" + end + + it "prints 'FAILED' if an exception is an SpecExpectationNotMetError" do + exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing") + @formatter.exception exc + @out.should == " (FAILED - 1)" + end + + it "prints the #it string if an exception has already been raised" do + exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing") + @formatter.exception exc + exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful") + @formatter.exception exc + @out.should == " (FAILED - 1)\n- it (ERROR - 2)" + end +end + +describe SpecdocFormatter, "#after" do + before :each do + $stdout = @out = IOStub.new + @formatter = SpecdocFormatter.new + @state = ExampleState.new "describe", "it" + end + + after :each do + $stdout = STDOUT + end + + it "prints a newline character" do + @formatter.after @state + @out.should == "\n" + end +end diff --git a/spec/mspec/spec/runner/formatters/spinner_spec.rb b/spec/mspec/spec/runner/formatters/spinner_spec.rb new file mode 100644 index 0000000000..a122620e39 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/spinner_spec.rb @@ -0,0 +1,83 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/spinner' +require 'mspec/runner/mspec' +require 'mspec/runner/example' + +describe SpinnerFormatter, "#initialize" do + it "permits zero arguments" do + SpinnerFormatter.new + end + + it "accepts one argument" do + SpinnerFormatter.new nil + end +end + +describe SpinnerFormatter, "#register" do + before :each do + @formatter = SpinnerFormatter.new + MSpec.stub(:register) + end + + it "registers self with MSpec for appropriate actions" do + MSpec.should_receive(:register).with(:start, @formatter) + MSpec.should_receive(:register).with(:unload, @formatter) + MSpec.should_receive(:register).with(:after, @formatter) + MSpec.should_receive(:register).with(:finish, @formatter) + @formatter.register + end + + it "creates TimerAction and TallyAction" do + timer = double("timer") + tally = double("tally") + timer.should_receive(:register) + tally.should_receive(:register) + tally.should_receive(:counter) + TimerAction.should_receive(:new).and_return(timer) + TallyAction.should_receive(:new).and_return(tally) + @formatter.register + end +end + +describe SpinnerFormatter, "#print" do + after :each do + $stdout = STDOUT + end + + it "ignores the argument to #initialize and writes to $stdout" do + $stdout = IOStub.new + formatter = SpinnerFormatter.new "some/file" + formatter.print "begonias" + $stdout.should == "begonias" + end +end + +describe SpinnerFormatter, "#after" do + before :each do + $stdout = IOStub.new + MSpec.store(:files, ["a", "b", "c", "d"]) + @formatter = SpinnerFormatter.new + @formatter.register + @state = ExampleState.new("describe", "it") + end + + after :each do + $stdout = STDOUT + end + + it "updates the spinner" do + @formatter.start + @formatter.after @state + @formatter.unload + + if ENV["TERM"] != "dumb" + green = "\e[0;32m" + reset = "\e[0m" + end + + output = "\r[/ | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \ + "\r[- | 0% | 00:00:00] #{green} 0F #{green} 0E#{reset} " \ + "\r[\\ | ========== 25% | 00:00:00] #{green} 0F #{green} 0E#{reset} " + $stdout.should == output + end +end diff --git a/spec/mspec/spec/runner/formatters/summary_spec.rb b/spec/mspec/spec/runner/formatters/summary_spec.rb new file mode 100644 index 0000000000..16a156b695 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/summary_spec.rb @@ -0,0 +1,26 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/summary' +require 'mspec/runner/example' + +describe SummaryFormatter, "#after" do + before :each do + $stdout = @out = IOStub.new + @formatter = SummaryFormatter.new + @formatter.register + context = ContextState.new "describe" + @state = ExampleState.new(context, "it") + end + + after :each do + $stdout = STDOUT + end + + it "does not print anything" do + exc = ExceptionState.new @state, nil, SpecExpectationNotMetError.new("disappointing") + @formatter.exception exc + exc = ExceptionState.new @state, nil, MSpecExampleError.new("painful") + @formatter.exception exc + @formatter.after(@state) + @out.should == "" + end +end diff --git a/spec/mspec/spec/runner/formatters/unit_spec.rb b/spec/mspec/spec/runner/formatters/unit_spec.rb new file mode 100644 index 0000000000..c8ba406f51 --- /dev/null +++ b/spec/mspec/spec/runner/formatters/unit_spec.rb @@ -0,0 +1,74 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/unit' +require 'mspec/runner/example' +require 'mspec/utils/script' + +describe UnitdiffFormatter, "#finish" do + before :each do + @tally = double("tally").as_null_object + TallyAction.stub(:new).and_return(@tally) + @timer = double("timer").as_null_object + TimerAction.stub(:new).and_return(@timer) + + $stdout = @out = IOStub.new + context = ContextState.new "describe" + @state = ExampleState.new(context, "it") + MSpec.stub(:register) + @formatter = UnitdiffFormatter.new + @formatter.register + end + + after :each do + $stdout = STDOUT + end + + it "prints a failure message for an exception" do + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken") + @formatter.exception exc + @formatter.after @state + @formatter.finish + @out.should =~ /^1\)\ndescribe it ERROR$/ + end + + it "prints a backtrace for an exception" do + exc = ExceptionState.new @state, nil, Exception.new("broken") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @formatter.finish + @out.should =~ %r[path/to/some/file.rb:35:in method$] + end + + it "prints a summary of elapsed time" do + @timer.should_receive(:format).and_return("Finished in 2.0 seconds") + @formatter.finish + @out.should =~ /^Finished in 2.0 seconds$/ + end + + it "prints a tally of counts" do + @tally.should_receive(:format).and_return("1 example, 0 failures") + @formatter.finish + @out.should =~ /^1 example, 0 failures$/ + end + + it "prints errors, backtraces, elapsed time, and tallies" do + exc = ExceptionState.new @state, nil, Exception.new("broken") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @formatter.after @state + @timer.should_receive(:format).and_return("Finished in 2.0 seconds") + @tally.should_receive(:format).and_return("1 example, 0 failures") + @formatter.finish + @out.should == +%[E + +Finished in 2.0 seconds + +1) +describe it ERROR +Exception: broken: +path/to/some/file.rb:35:in method + +1 example, 0 failures +] + end +end diff --git a/spec/mspec/spec/runner/formatters/yaml_spec.rb b/spec/mspec/spec/runner/formatters/yaml_spec.rb new file mode 100644 index 0000000000..eb4d99f74c --- /dev/null +++ b/spec/mspec/spec/runner/formatters/yaml_spec.rb @@ -0,0 +1,125 @@ +require File.dirname(__FILE__) + '/../../spec_helper' +require 'mspec/runner/formatters/yaml' +require 'mspec/runner/example' + +describe YamlFormatter, "#initialize" do + it "permits zero arguments" do + YamlFormatter.new + end + + it "accepts one argument" do + YamlFormatter.new nil + end +end + +describe YamlFormatter, "#print" do + before :each do + $stdout = IOStub.new + @out = IOStub.new + File.stub(:open).and_return(@out) + @formatter = YamlFormatter.new "some/file" + end + + after :each do + $stdout = STDOUT + end + + it "writes to $stdout if #switch has not been called" do + @formatter.print "begonias" + $stdout.should == "begonias" + @out.should == "" + end + + it "writes to the file passed to #initialize once #switch has been called" do + @formatter.switch + @formatter.print "begonias" + $stdout.should == "" + @out.should == "begonias" + end + + it "writes to $stdout once #switch is called if no file was passed to #initialize" do + formatter = YamlFormatter.new + formatter.switch + formatter.print "begonias" + $stdout.should == "begonias" + @out.should == "" + end +end + +describe YamlFormatter, "#finish" do + before :each do + @tally = double("tally").as_null_object + @counter = double("counter").as_null_object + @tally.stub(:counter).and_return(@counter) + TallyAction.stub(:new).and_return(@tally) + + @timer = double("timer").as_null_object + TimerAction.stub(:new).and_return(@timer) + + $stdout = IOStub.new + context = ContextState.new "describe" + @state = ExampleState.new(context, "it") + + @formatter = YamlFormatter.new + @formatter.stub(:backtrace).and_return("") + MSpec.stub(:register) + @formatter.register + + exc = ExceptionState.new @state, nil, MSpecExampleError.new("broken") + exc.stub(:backtrace).and_return("path/to/some/file.rb:35:in method") + @formatter.exception exc + @formatter.after @state + end + + after :each do + $stdout = STDOUT + end + + it "calls #switch" do + @formatter.should_receive(:switch) + @formatter.finish + end + + it "outputs a failure message and backtrace" do + @formatter.finish + $stdout.should include "describe it ERROR" + $stdout.should include "MSpecExampleError: broken\\n" + $stdout.should include "path/to/some/file.rb:35:in method" + end + + it "outputs an elapsed time" do + @timer.should_receive(:elapsed).and_return(4.2) + @formatter.finish + $stdout.should include "time: 4.2" + end + + it "outputs a file count" do + @counter.should_receive(:files).and_return(3) + @formatter.finish + $stdout.should include "files: 3" + end + + it "outputs an example count" do + @counter.should_receive(:examples).and_return(3) + @formatter.finish + $stdout.should include "examples: 3" + end + + it "outputs an expectation count" do + @counter.should_receive(:expectations).and_return(9) + @formatter.finish + $stdout.should include "expectations: 9" + end + + it "outputs a failure count" do + @counter.should_receive(:failures).and_return(2) + @formatter.finish + $stdout.should include "failures: 2" + end + + it "outputs an error count" do + @counter.should_receive(:errors).and_return(1) + @formatter.finish + $stdout.should include "errors: 1" + end +end diff --git a/spec/mspec/spec/runner/mspec_spec.rb b/spec/mspec/spec/runner/mspec_spec.rb new file mode 100644 index 0000000000..9b8142414e --- /dev/null +++ b/spec/mspec/spec/runner/mspec_spec.rb @@ -0,0 +1,595 @@ +require 'spec_helper' +require 'mspec/helpers/tmp' +require 'mspec/helpers/fs' +require 'mspec/matchers/base' +require 'mspec/runner/mspec' +require 'mspec/runner/example' + +describe MSpec, ".register_files" do + it "records which spec files to run" do + MSpec.register_files [:one, :two, :three] + MSpec.retrieve(:files).should == [:one, :two, :three] + end +end + +describe MSpec, ".register_mode" do + before :each do + MSpec.clear_modes + end + + it "sets execution mode flags" do + MSpec.register_mode :verify + MSpec.retrieve(:modes).should == [:verify] + end +end + +describe MSpec, ".register_tags_patterns" do + it "records the patterns for generating a tag file from a spec file" do + MSpec.register_tags_patterns [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]] + MSpec.retrieve(:tags_patterns).should == [[/spec\/ruby/, "spec/tags"], [/frozen/, "ruby"]] + end +end + +describe MSpec, ".register_exit" do + before :each do + MSpec.store :exit, 0 + end + + it "records the exit code" do + MSpec.exit_code.should == 0 + MSpec.register_exit 1 + MSpec.exit_code.should == 1 + end +end + +describe MSpec, ".exit_code" do + it "retrieves the code set with .register_exit" do + MSpec.register_exit 99 + MSpec.exit_code.should == 99 + end +end + +describe MSpec, ".store" do + it "records data for MSpec settings" do + MSpec.store :anything, :value + MSpec.retrieve(:anything).should == :value + end +end + +describe MSpec, ".retrieve" do + it "accesses .store'd data" do + MSpec.register :action, :first + MSpec.retrieve(:action).should == [:first] + end +end + +describe MSpec, ".randomize" do + it "sets the flag to randomize spec execution order" do + MSpec.randomize?.should == false + MSpec.randomize + MSpec.randomize?.should == true + MSpec.randomize false + MSpec.randomize?.should == false + end +end + +describe MSpec, ".register" do + it "is the gateway behind the register(symbol, action) facility" do + MSpec.register :bonus, :first + MSpec.register :bonus, :second + MSpec.register :bonus, :second + MSpec.retrieve(:bonus).should == [:first, :second] + end +end + +describe MSpec, ".unregister" do + it "is the gateway behind the unregister(symbol, actions) facility" do + MSpec.register :unregister, :first + MSpec.register :unregister, :second + MSpec.unregister :unregister, :second + MSpec.retrieve(:unregister).should == [:first] + end +end + +describe MSpec, ".protect" do + before :each do + MSpec.clear_current + @cs = ContextState.new "C#m" + @cs.parent = MSpec.current + + @es = ExampleState.new @cs, "runs" + ScratchPad.record Exception.new("Sharp!") + end + + it "returns true if no exception is raised" do + MSpec.protect("passed") { 1 }.should be_true + end + + it "returns false if an exception is raised" do + MSpec.protect("testing") { raise ScratchPad.recorded }.should be_false + end + + it "rescues any exceptions raised when evaluating the block argument" do + MSpec.protect("") { raise Exception, "Now you see me..." } + end + + it "does not rescue SystemExit" do + begin + MSpec.protect("") { exit 1 } + rescue SystemExit + ScratchPad.record :system_exit + end + ScratchPad.recorded.should == :system_exit + end + + it "calls all the exception actions" do + exc = ExceptionState.new @es, "testing", ScratchPad.recorded + ExceptionState.stub(:new).and_return(exc) + action = double("exception") + action.should_receive(:exception).with(exc) + MSpec.register :exception, action + MSpec.protect("testing") { raise ScratchPad.recorded } + MSpec.unregister :exception, action + end + + it "registers a non-zero exit code when an exception is raised" do + MSpec.should_receive(:register_exit).with(1) + MSpec.protect("testing") { raise ScratchPad.recorded } + end +end + +describe MSpec, ".register_current" do + before :each do + MSpec.clear_current + end + + it "sets the value returned by MSpec.current" do + MSpec.current.should be_nil + MSpec.register_current :a + MSpec.current.should == :a + end +end + +describe MSpec, ".clear_current" do + it "sets the value returned by MSpec.current to nil" do + MSpec.register_current :a + MSpec.current.should_not be_nil + MSpec.clear_current + MSpec.current.should be_nil + end +end + +describe MSpec, ".current" do + before :each do + MSpec.clear_current + end + + it "returns nil if no ContextState has been registered" do + MSpec.current.should be_nil + end + + it "returns the most recently registered ContextState" do + first = ContextState.new "" + second = ContextState.new "" + MSpec.register_current first + MSpec.current.should == first + MSpec.register_current second + MSpec.current.should == second + end +end + +describe MSpec, ".actions" do + before :each do + MSpec.store :start, [] + ScratchPad.record [] + start_one = double("one") + start_one.stub(:start).and_return { ScratchPad << :one } + start_two = double("two") + start_two.stub(:start).and_return { ScratchPad << :two } + MSpec.register :start, start_one + MSpec.register :start, start_two + end + + it "does not attempt to run any actions if none have been registered" do + MSpec.store :finish, nil + lambda { MSpec.actions :finish }.should_not raise_error + end + + it "runs each action registered as a start action" do + MSpec.actions :start + ScratchPad.recorded.should == [:one, :two] + end +end + +describe MSpec, ".mode?" do + before :each do + MSpec.clear_modes + end + + it "returns true if the mode has been set" do + MSpec.mode?(:verify).should == false + MSpec.register_mode :verify + MSpec.mode?(:verify).should == true + end +end + +describe MSpec, ".clear_modes" do + it "clears all registered modes" do + MSpec.register_mode(:pretend) + MSpec.register_mode(:verify) + + MSpec.mode?(:pretend).should == true + MSpec.mode?(:verify).should == true + + MSpec.clear_modes + + MSpec.mode?(:pretend).should == false + MSpec.mode?(:verify).should == false + end +end + +describe MSpec, ".guarded?" do + before :each do + MSpec.instance_variable_set :@guarded, [] + end + + it "returns false if no guard has run" do + MSpec.guarded?.should == false + end + + it "returns true if a single guard has run" do + MSpec.guard + MSpec.guarded?.should == true + end + + it "returns true if more than one guard has run" do + MSpec.guard + MSpec.guard + MSpec.guarded?.should == true + end + + it "returns true until all guards have finished" do + MSpec.guard + MSpec.guard + MSpec.guarded?.should == true + MSpec.unguard + MSpec.guarded?.should == true + MSpec.unguard + MSpec.guarded?.should == false + end +end + +describe MSpec, ".describe" do + before :each do + MSpec.clear_current + @cs = ContextState.new "" + ContextState.stub(:new).and_return(@cs) + MSpec.stub(:current).and_return(nil) + MSpec.stub(:register_current) + end + + it "creates a new ContextState for the block" do + ContextState.should_receive(:new).and_return(@cs) + MSpec.describe(Object) { } + end + + it "accepts an optional second argument" do + ContextState.should_receive(:new).and_return(@cs) + MSpec.describe(Object, "msg") { } + end + + it "registers the newly created ContextState" do + MSpec.should_receive(:register_current).with(@cs).twice + MSpec.describe(Object) { } + end + + it "invokes the ContextState#describe method" do + prc = lambda { } + @cs.should_receive(:describe).with(&prc) + MSpec.describe(Object, "msg", &prc) + end +end + +describe MSpec, ".process" do + before :each do + MSpec.stub(:files) + MSpec.store :start, [] + MSpec.store :finish, [] + STDOUT.stub(:puts) + end + + it "prints the RUBY_DESCRIPTION" do + STDOUT.should_receive(:puts).with(RUBY_DESCRIPTION) + MSpec.process + end + + it "calls all start actions" do + start = double("start") + start.stub(:start).and_return { ScratchPad.record :start } + MSpec.register :start, start + MSpec.process + ScratchPad.recorded.should == :start + end + + it "calls all finish actions" do + finish = double("finish") + finish.stub(:finish).and_return { ScratchPad.record :finish } + MSpec.register :finish, finish + MSpec.process + ScratchPad.recorded.should == :finish + end + + it "calls the files method" do + MSpec.should_receive(:files) + MSpec.process + end +end + +describe MSpec, ".files" do + before :each do + MSpec.store :load, [] + MSpec.store :unload, [] + MSpec.register_files [:one, :two, :three] + Kernel.stub(:load) + end + + it "calls load actions before each file" do + load = double("load") + load.stub(:load).and_return { ScratchPad.record :load } + MSpec.register :load, load + MSpec.files + ScratchPad.recorded.should == :load + end + + it "shuffles the file list if .randomize? is true" do + MSpec.randomize + MSpec.should_receive(:shuffle) + MSpec.files + MSpec.randomize false + end + + it "registers the current file" do + MSpec.should_receive(:store).with(:file, :one) + MSpec.should_receive(:store).with(:file, :two) + MSpec.should_receive(:store).with(:file, :three) + MSpec.files + end +end + +describe MSpec, ".shuffle" do + before :each do + @base = (0..100).to_a + @list = @base.clone + MSpec.shuffle @list + end + + it "does not alter the elements in the list" do + @base.each do |elt| + @list.should include(elt) + end + end + + it "changes the order of the list" do + # obviously, this spec has a certain probability + # of failing. If it fails, run it again. + @list.should_not == @base + end +end + +describe MSpec, ".tags_file" do + before :each do + MSpec.store :file, "path/to/spec/something/some_spec.rb" + MSpec.store :tags_patterns, nil + end + + it "returns the default tags file for the current spec file" do + MSpec.tags_file.should == "path/to/spec/tags/something/some_tags.txt" + end + + it "returns the tags file for the current spec file with custom tags_patterns" do + MSpec.register_tags_patterns [[/^(.*)\/spec/, '\1/tags'], [/_spec.rb/, "_tags.txt"]] + MSpec.tags_file.should == "path/to/tags/something/some_tags.txt" + end + + it "performs multiple substitutions" do + MSpec.register_tags_patterns [ + [%r(/spec/something/), "/spec/other/"], + [%r(/spec/), "/spec/tags/"], + [/_spec.rb/, "_tags.txt"] + ] + MSpec.tags_file.should == "path/to/spec/tags/other/some_tags.txt" + end + + it "handles cases where no substitution is performed" do + MSpec.register_tags_patterns [[/nothing/, "something"]] + MSpec.tags_file.should == "path/to/spec/something/some_spec.rb" + end +end + +describe MSpec, ".read_tags" do + before :each do + MSpec.stub(:tags_file).and_return(File.dirname(__FILE__) + '/tags.txt') + end + + it "returns a list of tag instances for matching tag names found" do + one = SpecTag.new "fail(broken):Some#method? works" + MSpec.read_tags(["fail", "pass"]).should == [one] + end + + it "returns [] if no tags names match" do + MSpec.read_tags("super").should == [] + end +end + +describe MSpec, ".read_tags" do + before :each do + @tag = SpecTag.new "fails:Some#method" + File.open(tmp("tags.txt", false), "w") do |f| + f.puts "" + f.puts @tag + f.puts "" + end + MSpec.stub(:tags_file).and_return(tmp("tags.txt", false)) + end + + it "does not return a tag object for empty lines" do + MSpec.read_tags(["fails"]).should == [@tag] + end +end + +describe MSpec, ".write_tags" do + before :each do + FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false) + MSpec.stub(:tags_file).and_return(tmp("tags.txt", false)) + @tag1 = SpecTag.new "check(broken):Tag#rewrite works" + @tag2 = SpecTag.new "broken:Tag#write_tags fails" + end + + after :all do + rm_r tmp("tags.txt", false) + end + + it "overwrites the tags in the tag file" do + IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works +incomplete(20%):The#best method ever +benchmark(0.01825):The#fastest method today +extended():\"Multi-line\\ntext\\ntag\" +] + MSpec.write_tags [@tag1, @tag2] + IO.read(tmp("tags.txt", false)).should == %[check(broken):Tag#rewrite works +broken:Tag#write_tags fails +] + end +end + +describe MSpec, ".write_tag" do + before :each do + FileUtils.stub(:mkdir_p) + MSpec.stub(:tags_file).and_return(tmp("tags.txt", false)) + @tag = SpecTag.new "fail(broken):Some#method works" + end + + after :all do + rm_r tmp("tags.txt", false) + end + + it "writes a tag to the tags file for the current spec file" do + MSpec.write_tag @tag + IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n" + end + + it "does not write a duplicate tag" do + File.open(tmp("tags.txt", false), "w") { |f| f.puts @tag } + MSpec.write_tag @tag + IO.read(tmp("tags.txt", false)).should == "fail(broken):Some#method works\n" + end +end + +describe MSpec, ".delete_tag" do + before :each do + FileUtils.cp File.dirname(__FILE__) + "/tags.txt", tmp("tags.txt", false) + MSpec.stub(:tags_file).and_return(tmp("tags.txt", false)) + @tag = SpecTag.new "fail(Comments don't matter):Some#method? works" + end + + after :each do + rm_r tmp("tags.txt", false) + end + + it "deletes the tag if it exists" do + MSpec.delete_tag(@tag).should == true + IO.read(tmp("tags.txt", false)).should == %[incomplete(20%):The#best method ever +benchmark(0.01825):The#fastest method today +extended():\"Multi-line\\ntext\\ntag\" +] + end + + it "deletes a tag with escaped newlines" do + MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true + IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works +incomplete(20%):The#best method ever +benchmark(0.01825):The#fastest method today +] + end + + it "does not change the tags file contents if the tag doesn't exist" do + @tag.tag = "failed" + MSpec.delete_tag(@tag).should == false + IO.read(tmp("tags.txt", false)).should == %[fail(broken):Some#method? works +incomplete(20%):The#best method ever +benchmark(0.01825):The#fastest method today +extended():\"Multi-line\\ntext\\ntag\" +] + end + + it "deletes the tag file if it is empty" do + MSpec.delete_tag(@tag).should == true + MSpec.delete_tag(SpecTag.new("incomplete:The#best method ever")).should == true + MSpec.delete_tag(SpecTag.new("benchmark:The#fastest method today")).should == true + MSpec.delete_tag(SpecTag.new('extended:"Multi-line\ntext\ntag"')).should == true + File.exist?(tmp("tags.txt", false)).should == false + end +end + +describe MSpec, ".delete_tags" do + before :each do + @tags = tmp("tags.txt", false) + FileUtils.cp File.dirname(__FILE__) + "/tags.txt", @tags + MSpec.stub(:tags_file).and_return(@tags) + end + + it "deletes the tag file" do + MSpec.delete_tags + File.exist?(@tags).should be_false + end +end + +describe MSpec, ".expectation" do + it "sets the flag that an expectation has been reported" do + MSpec.clear_expectations + MSpec.expectation?.should be_false + MSpec.expectation + MSpec.expectation?.should be_true + end +end + +describe MSpec, ".expectation?" do + it "returns true if an expectation has been reported" do + MSpec.expectation + MSpec.expectation?.should be_true + end + + it "returns false if an expectation has not been reported" do + MSpec.clear_expectations + MSpec.expectation?.should be_false + end +end + +describe MSpec, ".clear_expectations" do + it "clears the flag that an expectation has been reported" do + MSpec.expectation + MSpec.expectation?.should be_true + MSpec.clear_expectations + MSpec.expectation?.should be_false + end +end + +describe MSpec, ".register_shared" do + it "stores a shared ContextState by description" do + parent = ContextState.new "container" + state = ContextState.new "shared" + state.parent = parent + prc = lambda { } + state.describe(&prc) + MSpec.register_shared(state) + MSpec.retrieve(:shared)["shared"].should == state + end +end + +describe MSpec, ".retrieve_shared" do + it "retrieves the shared ContextState matching description" do + state = ContextState.new "" + MSpec.retrieve(:shared)["shared"] = state + MSpec.retrieve_shared(:shared).should == state + end +end diff --git a/spec/mspec/spec/runner/shared_spec.rb b/spec/mspec/spec/runner/shared_spec.rb new file mode 100644 index 0000000000..791588fdca --- /dev/null +++ b/spec/mspec/spec/runner/shared_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' +require 'mspec/runner/shared' +require 'mspec/runner/context' +require 'mspec/runner/example' + +describe Object, "#it_behaves_like" do + before :each do + ScratchPad.clear + + MSpec.setup_env + + @state = ContextState.new "Top level" + @state.instance_variable_set :@parsed, true + + @shared = ContextState.new :shared_spec, :shared => true + MSpec.stub(:retrieve_shared).and_return(@shared) + end + + it "creates @method set to the name of the aliased method" do + @shared.it("an example") { ScratchPad.record @method } + @state.it_behaves_like :shared_spec, :some_method + @state.process + ScratchPad.recorded.should == :some_method + end + + it "creates @object if the passed object" do + object = Object.new + @shared.it("an example") { ScratchPad.record @object } + @state.it_behaves_like :shared_spec, :some_method, object + @state.process + ScratchPad.recorded.should == object + end + + it "creates @object if the passed false" do + object = false + @shared.it("an example") { ScratchPad.record @object } + @state.it_behaves_like :shared_spec, :some_method, object + @state.process + ScratchPad.recorded.should == object + end + + it "sends :it_should_behave_like" do + @state.should_receive(:it_should_behave_like) + @state.it_behaves_like :shared_spec, :some_method + end + + describe "with multiple shared contexts" do + before :each do + @obj = Object.new + @obj2 = Object.new + + @state2 = ContextState.new "Second top level" + @state2.instance_variable_set :@parsed, true + end + + it "ensures the shared spec state is distinct" do + @shared.it("an example") { ScratchPad.record [@method, @object] } + + @state.it_behaves_like :shared_spec, :some_method, @obj + + @state.process + ScratchPad.recorded.should == [:some_method, @obj] + + @state2.it_behaves_like :shared_spec, :another_method, @obj2 + + @state2.process + ScratchPad.recorded.should == [:another_method, @obj2] + end + + it "ensures the shared spec state is distinct for nested shared specs" do + nested = ContextState.new "nested context" + nested.instance_variable_set :@parsed, true + nested.parent = @shared + + nested.it("another example") { ScratchPad.record [:shared, @method, @object] } + + @state.it_behaves_like :shared_spec, :some_method, @obj + + @state.process + ScratchPad.recorded.should == [:shared, :some_method, @obj] + + @state2.it_behaves_like :shared_spec, :another_method, @obj2 + + @state2.process + ScratchPad.recorded.should == [:shared, :another_method, @obj2] + end + end +end diff --git a/spec/mspec/spec/runner/tag_spec.rb b/spec/mspec/spec/runner/tag_spec.rb new file mode 100644 index 0000000000..db55a1b186 --- /dev/null +++ b/spec/mspec/spec/runner/tag_spec.rb @@ -0,0 +1,123 @@ +require 'spec_helper' +require 'mspec/runner/tag' + +describe SpecTag do + it "accepts an optional string to parse into fields" do + tag = SpecTag.new "tag(comment):description" + tag.tag.should == "tag" + tag.comment.should == "comment" + tag.description.should == "description" + end +end + +describe SpecTag, "#parse" do + before :each do + @tag = SpecTag.new + end + + it "accepts 'tag(comment):description'" do + @tag.parse "tag(I'm real):Some#method returns a value" + @tag.tag.should == "tag" + @tag.comment.should == "I'm real" + @tag.description.should == "Some#method returns a value" + end + + it "accepts 'tag:description'" do + @tag.parse "tag:Another#method" + @tag.tag.should == "tag" + @tag.comment.should == nil + @tag.description.should == "Another#method" + end + + it "accepts 'tag():description'" do + @tag.parse "tag():Another#method" + @tag.tag.should == "tag" + @tag.comment.should == nil + @tag.description.should == "Another#method" + end + + it "accepts 'tag:'" do + @tag.parse "tag:" + @tag.tag.should == "tag" + @tag.comment.should == nil + @tag.description.should == "" + end + + it "accepts 'tag(bug:555):Another#method'" do + @tag.parse "tag(bug:555):Another#method" + @tag.tag.should == "tag" + @tag.comment.should == "bug:555" + @tag.description.should == "Another#method" + end + + it "accepts 'tag(http://someplace.com/neato):Another#method'" do + @tag.parse "tag(http://someplace.com/neato):Another#method" + @tag.tag.should == "tag" + @tag.comment.should == "http://someplace.com/neato" + @tag.description.should == "Another#method" + end + + it "accepts 'tag(comment):\"Multi-line\\ntext\"'" do + @tag.parse 'tag(comment):"Multi-line\ntext"' + @tag.tag.should == "tag" + @tag.comment.should == "comment" + @tag.description.should == "Multi-line\ntext" + end + + it "ignores '#anything'" do + @tag.parse "# this could be a comment" + @tag.tag.should == nil + @tag.comment.should == nil + @tag.description.should == nil + end +end + +describe SpecTag, "#to_s" do + it "formats itself as 'tag(comment):description'" do + txt = "tag(comment):description" + tag = SpecTag.new txt + tag.tag.should == "tag" + tag.comment.should == "comment" + tag.description.should == "description" + tag.to_s.should == txt + end + + it "formats itself as 'tag:description" do + txt = "tag:description" + tag = SpecTag.new txt + tag.tag.should == "tag" + tag.comment.should == nil + tag.description.should == "description" + tag.to_s.should == txt + end + + it "formats itself as 'tag(comment):\"multi-line\\ntext\\ntag\"'" do + txt = 'tag(comment):"multi-line\ntext\ntag"' + tag = SpecTag.new txt + tag.tag.should == "tag" + tag.comment.should == "comment" + tag.description.should == "multi-line\ntext\ntag" + tag.to_s.should == txt + end +end + +describe SpecTag, "#==" do + it "returns true if the tags have the same fields" do + one = SpecTag.new "tag(this):unicorn" + two = SpecTag.new "tag(this):unicorn" + one.==(two).should == true + [one].==([two]).should == true + end +end + +describe SpecTag, "#unescape" do + it "replaces \\n by LF when the description is quoted" do + tag = SpecTag.new 'tag:"desc with\nnew line"' + tag.description.should == "desc with\nnew line" + end + + it "does not replaces \\n by LF when the description is not quoted " do + tag = SpecTag.new 'tag:desc with\nnew line' + tag.description.should == "desc with\\nnew line" + end +end diff --git a/spec/mspec/spec/runner/tags.txt b/spec/mspec/spec/runner/tags.txt new file mode 100644 index 0000000000..f4eb6ad034 --- /dev/null +++ b/spec/mspec/spec/runner/tags.txt @@ -0,0 +1,4 @@ +fail(broken):Some#method? works +incomplete(20%):The#best method ever +benchmark(0.01825):The#fastest method today +extended():"Multi-line\ntext\ntag" diff --git a/spec/mspec/spec/spec_helper.rb b/spec/mspec/spec/spec_helper.rb new file mode 100644 index 0000000000..4fae9bd7e2 --- /dev/null +++ b/spec/mspec/spec/spec_helper.rb @@ -0,0 +1,54 @@ +require 'pp' +require 'mspec/helpers/io' +require 'mspec/helpers/scratch' + +# Remove this when MRI has intelligent warnings +$VERBOSE = nil unless $VERBOSE + +class MOSConfig < Hash + def initialize + self[:loadpath] = [] + self[:requires] = [] + self[:flags] = [] + self[:options] = [] + self[:includes] = [] + self[:excludes] = [] + self[:patterns] = [] + self[:xpatterns] = [] + self[:tags] = [] + self[:xtags] = [] + self[:atags] = [] + self[:astrings] = [] + self[:target] = 'ruby' + self[:command] = nil + self[:ltags] = [] + self[:files] = [] + self[:launch] = [] + end +end + +def new_option + config = MOSConfig.new + return MSpecOptions.new("spec", 20, config), config +end + +# Just to have an exception name output not be "Exception" +class MSpecExampleError < Exception +end + +def hide_deprecation_warnings + MSpec.stub(:deprecate) +end + +def run_mspec(command, args) + cwd = Dir.pwd + command = " #{command}" unless command.start_with?('-') + cmd = "#{cwd}/bin/mspec#{command} -B spec/fixtures/config.mspec #{args}" + out = `#{cmd} 2>&1` + ret = $? + out = out.sub(/\A\$.+\n/, '') # Remove printed command line + out = out.sub(RUBY_DESCRIPTION, "RUBY_DESCRIPTION") + out = out.gsub(/\d\.\d{6}/, "D.DDDDDD") + out = out.gsub(cwd, "CWD") + return out, ret +end diff --git a/spec/mspec/spec/utils/deprecate_spec.rb b/spec/mspec/spec/utils/deprecate_spec.rb new file mode 100644 index 0000000000..14e05c6f35 --- /dev/null +++ b/spec/mspec/spec/utils/deprecate_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' +require 'mspec/utils/deprecate' + +describe MSpec, "#deprecate" do + it "warns when using a deprecated method" do + warning = nil + $stderr.stub(:puts) { |str| warning = str } + MSpec.deprecate(:some_method, :other_method) + warning.should start_with(<<-EOS.chomp) + +some_method is deprecated, use other_method instead. +from +EOS + warning.should include(__FILE__) + warning.should include('8') + end +end diff --git a/spec/mspec/spec/utils/name_map_spec.rb b/spec/mspec/spec/utils/name_map_spec.rb new file mode 100644 index 0000000000..d38230ce06 --- /dev/null +++ b/spec/mspec/spec/utils/name_map_spec.rb @@ -0,0 +1,175 @@ +require 'spec_helper' +require 'mspec/utils/name_map' + +module NameMapSpecs + class A + A = self + + def self.a; end + def a; end + def c; end + + class B + def b; end + end + end + + class Error + end + + class Fixnum + def f; end + end + + def self.n; end + def n; end +end + +describe NameMap, "#exception?" do + before :each do + @map = NameMap.new + end + + it "returns true if the constant is Errno" do + @map.exception?("Errno").should == true + end + + it "returns true if the constant is a kind of Exception" do + @map.exception?("Errno::EBADF").should == true + @map.exception?("LoadError").should == true + @map.exception?("SystemExit").should == true + end + + it "returns false if the constant is not a kind of Exception" do + @map.exception?("NameMapSpecs::Error").should == false + @map.exception?("NameMapSpecs").should == false + end + + it "returns false if the constant does not exist" do + @map.exception?("Nonexistent").should == false + end +end + +describe NameMap, "#class_or_module" do + before :each do + @map = NameMap.new true + end + + it "returns the constant specified by the string" do + @map.class_or_module("NameMapSpecs").should == NameMapSpecs + end + + it "returns the constant specified by the 'A::B' string" do + @map.class_or_module("NameMapSpecs::A").should == NameMapSpecs::A + end + + it "returns nil if the constant is not a class or module" do + @map.class_or_module("Float::MAX").should == nil + end + + it "returns nil if the constant is in the set of excluded constants" do + excluded = %w[ + MSpecScript + MkSpec + NameMap + ] + + excluded.each do |const| + @map.class_or_module(const).should == nil + end + end + + it "returns nil if the constant does not exist" do + @map.class_or_module("Heaven").should == nil + @map.class_or_module("Hell").should == nil + @map.class_or_module("Bush::Brain").should == nil + end +end + +describe NameMap, "#dir_name" do + before :each do + @map = NameMap.new + end + + it "returns a directory name from the base name and constant" do + @map.dir_name("NameMapSpecs", 'spec/core').should == 'spec/core/namemapspecs' + end + + it "returns a directory name from the components in the constants name" do + @map.dir_name("NameMapSpecs::A", 'spec').should == 'spec/namemapspecs/a' + @map.dir_name("NameMapSpecs::A::B", 'spec').should == 'spec/namemapspecs/a/b' + end + + it "returns a directory name without 'class' for constants like TrueClass" do + @map.dir_name("TrueClass", 'spec').should == 'spec/true' + @map.dir_name("FalseClass", 'spec').should == 'spec/false' + end + + it "returns 'exception' for the directory name of any Exception subclass" do + @map.dir_name("SystemExit", 'spec').should == 'spec/exception' + @map.dir_name("Errno::EBADF", 'spec').should == 'spec/exception' + end + + it "returns 'class' for Class" do + @map.dir_name("Class", 'spec').should == 'spec/class' + end +end + +# These specs do not cover all the mappings, but only describe how the +# name is derived when the hash item maps to a single value, a hash with +# a specific item, or a hash with a :default item. +describe NameMap, "#file_name" do + before :each do + @map = NameMap.new + end + + it "returns the name of the spec file based on the constant and method" do + @map.file_name("[]=", "Array").should == "element_set_spec.rb" + end + + it "returns the name of the spec file based on the special entry for the method" do + @map.file_name("~", "Regexp").should == "match_spec.rb" + @map.file_name("~", "Fixnum").should == "complement_spec.rb" + end + + it "returns the name of the spec file based on the default entry for the method" do + @map.file_name("<<", "NameMapSpecs").should == "append_spec.rb" + end + + it "uses the last component of the constant to look up the method name" do + @map.file_name("^", "NameMapSpecs::Fixnum").should == "bit_xor_spec.rb" + end +end + +describe NameMap, "#namespace" do + before :each do + @map = NameMap.new + end + + it "prepends the module to the constant name" do + @map.namespace("SubModule", Integer).should == "SubModule::Integer" + end + + it "does not prepend Object, Class, or Module to the constant name" do + @map.namespace("Object", String).should == "String" + @map.namespace("Module", Integer).should == "Integer" + @map.namespace("Class", Float).should == "Float" + end +end + +describe NameMap, "#map" do + before :each do + @map = NameMap.new + end + + it "flattens an object hierarchy into a single Hash" do + @map.map({}, [NameMapSpecs]).should == { + "NameMapSpecs." => ["n"], + "NameMapSpecs#" => ["n"], + "NameMapSpecs::A." => ["a"], + "NameMapSpecs::A#" => ["a", "c"], + "NameMapSpecs::A::B#" => ["b"], + "NameMapSpecs::Fixnum#" => ["f"] + } + end +end diff --git a/spec/mspec/spec/utils/options_spec.rb b/spec/mspec/spec/utils/options_spec.rb new file mode 100644 index 0000000000..26c52bdbd0 --- /dev/null +++ b/spec/mspec/spec/utils/options_spec.rb @@ -0,0 +1,1309 @@ +require 'spec_helper' +require 'mspec/utils/options' +require 'mspec/version' +require 'mspec/guards/guard' +require 'mspec/runner/mspec' +require 'mspec/runner/formatters' + +describe MSpecOption, ".new" do + before :each do + @opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block) + end + + it "sets the short attribute" do + @opt.short.should == "-a" + end + + it "sets the long attribute" do + @opt.long.should == "--bdc" + end + + it "sets the arg attribute" do + @opt.arg.should == "ARG" + end + + it "sets the description attribute" do + @opt.description.should == "desc" + end + + it "sets the block attribute" do + @opt.block.should == :block + end +end + +describe MSpecOption, "#arg?" do + it "returns true if arg attribute is not nil" do + MSpecOption.new(nil, nil, "ARG", nil, nil).arg?.should be_true + end + + it "returns false if arg attribute is nil" do + MSpecOption.new(nil, nil, nil, nil, nil).arg?.should be_false + end +end + +describe MSpecOption, "#match?" do + before :each do + @opt = MSpecOption.new("-a", "--bdc", "ARG", "desc", :block) + end + + it "returns true if the argument matches the short option" do + @opt.match?("-a").should be_true + end + + it "returns true if the argument matches the long option" do + @opt.match?("--bdc").should be_true + end + + it "returns false if the argument matches neither the short nor long option" do + @opt.match?("-b").should be_false + @opt.match?("-abdc").should be_false + end +end + +describe MSpecOptions, ".new" do + before :each do + @opt = MSpecOptions.new("cmd", 20, :config) + end + + it "sets the banner attribute" do + @opt.banner.should == "cmd" + end + + it "sets the config attribute" do + @opt.config.should == :config + end + + it "sets the width attribute" do + @opt.width.should == 20 + end + + it "sets the default width attribute" do + MSpecOptions.new.width.should == 30 + end +end + +describe MSpecOptions, "#on" do + before :each do + @opt = MSpecOptions.new + end + + it "adds a short option" do + @opt.should_receive(:add).with("-a", nil, nil, "desc", nil) + @opt.on("-a", "desc") + end + + it "adds a short option taking an argument" do + @opt.should_receive(:add).with("-a", nil, "ARG", "desc", nil) + @opt.on("-a", "ARG", "desc") + end + + it "adds a long option" do + @opt.should_receive(:add).with("-a", nil, nil, "desc", nil) + @opt.on("-a", "desc") + end + + it "adds a long option taking an argument" do + @opt.should_receive(:add).with("-a", nil, nil, "desc", nil) + @opt.on("-a", "desc") + end + + it "adds a short and long option" do + @opt.should_receive(:add).with("-a", nil, nil, "desc", nil) + @opt.on("-a", "desc") + end + + it "adds a short and long option taking an argument" do + @opt.should_receive(:add).with("-a", nil, nil, "desc", nil) + @opt.on("-a", "desc") + end + + it "raises MSpecOptions::OptionError if pass less than 2 arguments" do + lambda { @opt.on }.should raise_error(MSpecOptions::OptionError) + lambda { @opt.on "" }.should raise_error(MSpecOptions::OptionError) + end +end + +describe MSpecOptions, "#add" do + before :each do + @opt = MSpecOptions.new "cmd", 20 + @prc = lambda { } + end + + it "adds documentation for an option" do + @opt.should_receive(:doc).with(" -t, --typo ARG Correct typo ARG") + @opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc) + end + + it "leaves spaces in the documentation for a missing short option" do + @opt.should_receive(:doc).with(" --typo ARG Correct typo ARG") + @opt.add(nil, "--typo", "ARG", "Correct typo ARG", @prc) + end + + it "handles a short option with argument but no long argument" do + @opt.should_receive(:doc).with(" -t ARG Correct typo ARG") + @opt.add("-t", nil, "ARG", "Correct typo ARG", @prc) + end + + it "registers an option" do + option = MSpecOption.new "-t", "--typo", "ARG", "Correct typo ARG", @prc + MSpecOption.should_receive(:new).with( + "-t", "--typo", "ARG", "Correct typo ARG", @prc).and_return(option) + @opt.add("-t", "--typo", "ARG", "Correct typo ARG", @prc) + @opt.options.should == [option] + end +end + +describe MSpecOptions, "#match?" do + before :each do + @opt = MSpecOptions.new + end + + it "returns the MSpecOption instance matching the argument" do + @opt.on "-a", "--abdc", "desc" + option = @opt.match? "-a" + @opt.match?("--abdc").should be(option) + option.should be_kind_of(MSpecOption) + option.short.should == "-a" + option.long.should == "--abdc" + option.description.should == "desc" + end +end + +describe MSpecOptions, "#process" do + before :each do + @opt = MSpecOptions.new + ScratchPad.clear + end + + it "calls the on_extra block if the argument does not match any option" do + @opt.on_extra { ScratchPad.record :extra } + @opt.process ["-a"], "-a", "-a", nil + ScratchPad.recorded.should == :extra + end + + it "returns the matching option" do + @opt.on "-a", "ARG", "desc" + option = @opt.process [], "-a", "-a", "ARG" + option.should be_kind_of(MSpecOption) + option.short.should == "-a" + option.arg.should == "ARG" + option.description.should == "desc" + end + + it "raises an MSpecOptions::ParseError if arg is nil and there are no more entries in argv" do + @opt.on "-a", "ARG", "desc" + lambda { @opt.process [], "-a", "-a", nil }.should raise_error(MSpecOptions::ParseError) + end + + it "fetches the argument for the option from argv if arg is nil" do + @opt.on("-a", "ARG", "desc") { |o| ScratchPad.record o } + @opt.process ["ARG"], "-a", "-a", nil + ScratchPad.recorded.should == "ARG" + end + + it "calls the option's block" do + @opt.on("-a", "ARG", "desc") { ScratchPad.record :option } + @opt.process [], "-a", "-a", "ARG" + ScratchPad.recorded.should == :option + end + + it "does not call the option's block if it is nil" do + @opt.on "-a", "ARG", "desc" + lambda { @opt.process [], "-a", "-a", "ARG" }.should_not raise_error + end +end + +describe MSpecOptions, "#split" do + before :each do + @opt = MSpecOptions.new + end + + it "breaks a string at the nth character" do + opt, arg, rest = @opt.split "-bdc", 2 + opt.should == "-b" + arg.should == "dc" + rest.should == "dc" + end + + it "returns nil for arg if there are no characters left" do + opt, arg, rest = @opt.split "-b", 2 + opt.should == "-b" + arg.should == nil + rest.should == "" + end +end + +describe MSpecOptions, "#parse" do + before :each do + @opt = MSpecOptions.new + @prc = lambda { ScratchPad.record :parsed } + @arg_prc = lambda { |o| ScratchPad.record [:parsed, o] } + ScratchPad.clear + end + + it "parses a short option" do + @opt.on "-a", "desc", &@prc + @opt.parse ["-a"] + ScratchPad.recorded.should == :parsed + end + + it "parse a long option" do + @opt.on "--abdc", "desc", &@prc + @opt.parse ["--abdc"] + ScratchPad.recorded.should == :parsed + end + + it "parses a short option group" do + @opt.on "-a", "ARG", "desc", &@arg_prc + @opt.parse ["-a", "ARG"] + ScratchPad.recorded.should == [:parsed, "ARG"] + end + + it "parses a short option with an argument" do + @opt.on "-a", "ARG", "desc", &@arg_prc + @opt.parse ["-a", "ARG"] + ScratchPad.recorded.should == [:parsed, "ARG"] + end + + it "parses a short option with connected argument" do + @opt.on "-a", "ARG", "desc", &@arg_prc + @opt.parse ["-aARG"] + ScratchPad.recorded.should == [:parsed, "ARG"] + end + + it "parses a long option with an argument" do + @opt.on "--abdc", "ARG", "desc", &@arg_prc + @opt.parse ["--abdc", "ARG"] + ScratchPad.recorded.should == [:parsed, "ARG"] + end + + it "parses a long option with an '=' argument" do + @opt.on "--abdc", "ARG", "desc", &@arg_prc + @opt.parse ["--abdc=ARG"] + ScratchPad.recorded.should == [:parsed, "ARG"] + end + + it "parses a short option group with the final option taking an argument" do + ScratchPad.record [] + @opt.on("-a", "desc") { |o| ScratchPad << :a } + @opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] } + @opt.parse ["-ab", "ARG"] + ScratchPad.recorded.should == [:a, [:b, "ARG"]] + end + + it "parses a short option group with a connected argument" do + ScratchPad.record [] + @opt.on("-a", "desc") { |o| ScratchPad << :a } + @opt.on("-b", "ARG", "desc") { |o| ScratchPad << [:b, o] } + @opt.on("-c", "desc") { |o| ScratchPad << :c } + @opt.parse ["-acbARG"] + ScratchPad.recorded.should == [:a, :c, [:b, "ARG"]] + end + + it "returns the unprocessed entries" do + @opt.on "-a", "ARG", "desc", &@arg_prc + @opt.parse(["abdc", "-a", "ilny"]).should == ["abdc"] + end + + it "calls the on_extra handler with unrecognized options" do + ScratchPad.record [] + @opt.on_extra { |o| ScratchPad << o } + @opt.on "-a", "desc" + @opt.parse ["-a", "-b"] + ScratchPad.recorded.should == ["-b"] + end + + it "does not attempt to call the block if it is nil" do + @opt.on "-a", "ARG", "desc" + @opt.parse(["-a", "ARG"]).should == [] + end + + it "raises MSpecOptions::ParseError if passed an unrecognized option" do + @opt.should_receive(:raise).with(MSpecOptions::ParseError, an_instance_of(String)) + @opt.stub(:puts) + @opt.stub(:exit) + @opt.parse "-u" + end +end + +describe MSpecOptions, "#banner=" do + before :each do + @opt = MSpecOptions.new + end + + it "sets the banner attribute" do + @opt.banner.should == "" + @opt.banner = "banner" + @opt.banner.should == "banner" + end +end + +describe MSpecOptions, "#width=" do + before :each do + @opt = MSpecOptions.new + end + + it "sets the width attribute" do + @opt.width.should == 30 + @opt.width = 20 + @opt.width.should == 20 + end +end + +describe MSpecOptions, "#config=" do + before :each do + @opt = MSpecOptions.new + end + + it "sets the config attribute" do + @opt.config.should be_nil + @opt.config = :config + @opt.config.should == :config + end +end + +describe MSpecOptions, "#doc" do + before :each do + @opt = MSpecOptions.new "command" + end + + it "adds text to be displayed with #to_s" do + @opt.doc "Some message" + @opt.doc "Another message" + @opt.to_s.should == <<-EOD +command + +Some message +Another message +EOD + end +end + +describe MSpecOptions, "#version" do + before :each do + @opt = MSpecOptions.new + ScratchPad.clear + end + + it "installs a basic -v, --version option" do + @opt.should_receive(:puts) + @opt.should_receive(:exit) + @opt.version "1.0.0" + @opt.parse "-v" + end + + it "accepts a block instead of using the default block" do + @opt.version("1.0.0") { |o| ScratchPad.record :version } + @opt.parse "-v" + ScratchPad.recorded.should == :version + end +end + +describe MSpecOptions, "#help" do + before :each do + @opt = MSpecOptions.new + ScratchPad.clear + end + + it "installs a basic -h, --help option" do + @opt.should_receive(:puts) + @opt.should_receive(:exit).with(1) + @opt.help + @opt.parse "-h" + end + + it "accepts a block instead of using the default block" do + @opt.help { |o| ScratchPad.record :help } + @opt.parse "-h" + ScratchPad.recorded.should == :help + end +end + +describe MSpecOptions, "#on_extra" do + before :each do + @opt = MSpecOptions.new + ScratchPad.clear + end + + it "registers a block to be called when an option is not recognized" do + @opt.on_extra { ScratchPad.record :extra } + @opt.parse "-g" + ScratchPad.recorded.should == :extra + end +end + +describe MSpecOptions, "#to_s" do + before :each do + @opt = MSpecOptions.new "command" + end + + it "returns the banner and descriptive strings for all registered options" do + @opt.on "-t", "--this ARG", "Adds this ARG to the list" + @opt.to_s.should == <<-EOD +command + + -t, --this ARG Adds this ARG to the list +EOD + end +end + +describe "The -B, --config FILE option" do + before :each do + @options, @config = new_option + end + + it "is enabled with #configure { }" do + @options.should_receive(:on).with("-B", "--config", "FILE", + an_instance_of(String)) + @options.configure {} + end + + it "calls the passed block" do + ["-B", "--config"].each do |opt| + ScratchPad.clear + + @options.configure { |x| ScratchPad.record x } + @options.parse [opt, "file"] + ScratchPad.recorded.should == "file" + end + end +end + +describe "The -C, --chdir DIR option" do + before :each do + @options, @config = new_option + @options.chdir + end + + it "is enabled with #chdir" do + @options.should_receive(:on).with("-C", "--chdir", "DIR", + an_instance_of(String)) + @options.chdir + end + + it "changes the working directory to DIR" do + Dir.should_receive(:chdir).with("dir").twice + ["-C", "--chdir"].each do |opt| + @options.parse [opt, "dir"] + end + end +end + +describe "The --prefix STR option" do + before :each do + @options, @config = new_option + end + + it "is enabled with #prefix" do + @options.should_receive(:on).with("--prefix", "STR", + an_instance_of(String)) + @options.prefix + end + + it "sets the prefix config value" do + @options.prefix + @options.parse ["--prefix", "some/dir"] + @config[:prefix].should == "some/dir" + end +end + +describe "The -n, --name RUBY_NAME option" do + before :each do + @verbose, $VERBOSE = $VERBOSE, nil + @options, @config = new_option + end + + after :each do + $VERBOSE = @verbose + end + + it "is enabled with #name" do + @options.should_receive(:on).with("-n", "--name", "RUBY_NAME", + an_instance_of(String)) + @options.name + end + + it "sets RUBY_NAME when invoked" do + Object.should_receive(:const_set).with(:RUBY_NAME, "name").twice + @options.name + @options.parse ["-n", "name"] + @options.parse ["--name", "name"] + end +end + +describe "The -t, --target TARGET option" do + before :each do + @options, @config = new_option + @options.targets + end + + it "is enabled with #targets" do + @options.stub(:on) + @options.should_receive(:on).with("-t", "--target", "TARGET", + an_instance_of(String)) + @options.targets + end + + it "sets the target to 'ruby' and flags to verbose with TARGET 'r' or 'ruby'" do + ["-t", "--target"].each do |opt| + ["r", "ruby"].each do |t| + @config[:target] = nil + @options.parse [opt, t] + @config[:target].should == "ruby" + end + end + end + + it "sets the target to 'jruby' with TARGET 'j' or 'jruby'" do + ["-t", "--target"].each do |opt| + ["j", "jruby"].each do |t| + @config[:target] = nil + @options.parse [opt, t] + @config[:target].should == "jruby" + end + end + end + + it "sets the target to 'shotgun/rubinius' with TARGET 'x' or 'rubinius'" do + ["-t", "--target"].each do |opt| + ["x", "rubinius"].each do |t| + @config[:target] = nil + @options.parse [opt, t] + @config[:target].should == "./bin/rbx" + end + end + end + + it "set the target to 'rbx' with TARGET 'rbx'" do + ["-t", "--target"].each do |opt| + ["X", "rbx"].each do |t| + @config[:target] = nil + @options.parse [opt, t] + @config[:target].should == "rbx" + end + end + end + + it "sets the target to 'maglev' with TARGET 'm' or 'maglev'" do + ["-t", "--target"].each do |opt| + ["m", "maglev"].each do |t| + @config[:target] = nil + @options.parse [opt, t] + @config[:target].should == "maglev-ruby" + end + end + end + + it "sets the target to 'topaz' with TARGET 't' or 'topaz'" do + ["-t", "--target"].each do |opt| + ["t", "topaz"].each do |t| + @config[:target] = nil + @options.parse [opt, t] + @config[:target].should == "topaz" + end + end + end + + it "sets the target to TARGET" do + ["-t", "--target"].each do |opt| + @config[:target] = nil + @options.parse [opt, "whateva"] + @config[:target].should == "whateva" + end + end +end + +describe "The -T, --target-opt OPT option" do + before :each do + @options, @config = new_option + @options.targets + end + + it "is enabled with #targets" do + @options.stub(:on) + @options.should_receive(:on).with("-T", "--target-opt", "OPT", + an_instance_of(String)) + @options.targets + end + + it "adds OPT to flags" do + ["-T", "--target-opt"].each do |opt| + @config[:flags].delete "--whateva" + @options.parse [opt, "--whateva"] + @config[:flags].should include("--whateva") + end + end +end + +describe "The -I, --include DIR option" do + before :each do + @options, @config = new_option + @options.targets + end + + it "is enabled with #targets" do + @options.stub(:on) + @options.should_receive(:on).with("-I", "--include", "DIR", + an_instance_of(String)) + @options.targets + end + + it "add DIR to the load path" do + ["-I", "--include"].each do |opt| + @config[:loadpath].delete "-Ipackage" + @options.parse [opt, "package"] + @config[:loadpath].should include("-Ipackage") + end + end +end + +describe "The -r, --require LIBRARY option" do + before :each do + @options, @config = new_option + @options.targets + end + + it "is enabled with #targets" do + @options.stub(:on) + @options.should_receive(:on).with("-r", "--require", "LIBRARY", + an_instance_of(String)) + @options.targets + end + + it "adds LIBRARY to the requires list" do + ["-r", "--require"].each do |opt| + @config[:requires].delete "-rlibrick" + @options.parse [opt, "librick"] + @config[:requires].should include("-rlibrick") + end + end +end + +describe "The -f, --format FORMAT option" do + before :each do + @options, @config = new_option + @options.formatters + end + + it "is enabled with #formatters" do + @options.stub(:on) + @options.should_receive(:on).with("-f", "--format", "FORMAT", + an_instance_of(String)) + @options.formatters + end + + it "sets the SpecdocFormatter with FORMAT 's' or 'specdoc'" do + ["-f", "--format"].each do |opt| + ["s", "specdoc"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == SpecdocFormatter + end + end + end + + it "sets the HtmlFormatter with FORMAT 'h' or 'html'" do + ["-f", "--format"].each do |opt| + ["h", "html"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == HtmlFormatter + end + end + end + + it "sets the DottedFormatter with FORMAT 'd', 'dot' or 'dotted'" do + ["-f", "--format"].each do |opt| + ["d", "dot", "dotted"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == DottedFormatter + end + end + end + + it "sets the DescribeFormatter with FORMAT 'b' or 'describe'" do + ["-f", "--format"].each do |opt| + ["b", "describe"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == DescribeFormatter + end + end + end + + it "sets the FileFormatter with FORMAT 'f', 'file'" do + ["-f", "--format"].each do |opt| + ["f", "file"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == FileFormatter + end + end + end + + it "sets the UnitdiffFormatter with FORMAT 'u', 'unit', or 'unitdiff'" do + ["-f", "--format"].each do |opt| + ["u", "unit", "unitdiff"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == UnitdiffFormatter + end + end + end + + it "sets the SummaryFormatter with FORMAT 'm' or 'summary'" do + ["-f", "--format"].each do |opt| + ["m", "summary"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == SummaryFormatter + end + end + end + + it "sets the SpinnerFormatter with FORMAT 'a', '*', or 'spin'" do + ["-f", "--format"].each do |opt| + ["a", "*", "spin"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == SpinnerFormatter + end + end + end + + it "sets the MethodFormatter with FORMAT 't' or 'method'" do + ["-f", "--format"].each do |opt| + ["t", "method"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == MethodFormatter + end + end + end + + it "sets the YamlFormatter with FORMAT 'y' or 'yaml'" do + ["-f", "--format"].each do |opt| + ["y", "yaml"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == YamlFormatter + end + end + end + + it "sets the JUnitFormatter with FORMAT 'j' or 'junit'" do + ["-f", "--format"].each do |opt| + ["j", "junit"].each do |f| + @config[:formatter] = nil + @options.parse [opt, f] + @config[:formatter].should == JUnitFormatter + end + end + end +end + +describe "The -o, --output FILE option" do + before :each do + @options, @config = new_option + @options.formatters + end + + it "is enabled with #formatters" do + @options.stub(:on) + @options.should_receive(:on).with("-o", "--output", "FILE", + an_instance_of(String)) + @options.formatters + end + + it "sets the output to FILE" do + ["-o", "--output"].each do |opt| + @config[:output] = nil + @options.parse [opt, "some/file"] + @config[:output].should == "some/file" + end + end +end + +describe "The -e, --example STR" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-e", "--example", "STR", + an_instance_of(String)) + @options.filters + end + + it "adds STR to the includes list" do + ["-e", "--example"].each do |opt| + @config[:includes] = [] + @options.parse [opt, "this spec"] + @config[:includes].should include("this spec") + end + end +end + +describe "The -E, --exclude STR" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-E", "--exclude", "STR", + an_instance_of(String)) + @options.filters + end + + it "adds STR to the excludes list" do + ["-E", "--exclude"].each do |opt| + @config[:excludes] = [] + @options.parse [opt, "this spec"] + @config[:excludes].should include("this spec") + end + end +end + +describe "The -p, --pattern PATTERN" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-p", "--pattern", "PATTERN", + an_instance_of(String)) + @options.filters + end + + it "adds PATTERN to the included patterns list" do + ["-p", "--pattern"].each do |opt| + @config[:patterns] = [] + @options.parse [opt, "this spec"] + @config[:patterns].should include(/this spec/) + end + end +end + +describe "The -P, --excl-pattern PATTERN" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-P", "--excl-pattern", "PATTERN", + an_instance_of(String)) + @options.filters + end + + it "adds PATTERN to the excluded patterns list" do + ["-P", "--excl-pattern"].each do |opt| + @config[:xpatterns] = [] + @options.parse [opt, "this spec"] + @config[:xpatterns].should include(/this spec/) + end + end +end + +describe "The -g, --tag TAG" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-g", "--tag", "TAG", + an_instance_of(String)) + @options.filters + end + + it "adds TAG to the included tags list" do + ["-g", "--tag"].each do |opt| + @config[:tags] = [] + @options.parse [opt, "this spec"] + @config[:tags].should include("this spec") + end + end +end + +describe "The -G, --excl-tag TAG" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-G", "--excl-tag", "TAG", + an_instance_of(String)) + @options.filters + end + + it "adds TAG to the excluded tags list" do + ["-G", "--excl-tag"].each do |opt| + @config[:xtags] = [] + @options.parse [opt, "this spec"] + @config[:xtags].should include("this spec") + end + end +end + +describe "The -w, --profile FILE option" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-w", "--profile", "FILE", + an_instance_of(String)) + @options.filters + end + + it "adds FILE to the included profiles list" do + ["-w", "--profile"].each do |opt| + @config[:profiles] = [] + @options.parse [opt, "spec/profiles/rails.yaml"] + @config[:profiles].should include("spec/profiles/rails.yaml") + end + end +end + +describe "The -W, --excl-profile FILE option" do + before :each do + @options, @config = new_option + @options.filters + end + + it "is enabled with #filters" do + @options.stub(:on) + @options.should_receive(:on).with("-W", "--excl-profile", "FILE", + an_instance_of(String)) + @options.filters + end + + it "adds FILE to the excluded profiles list" do + ["-W", "--excl-profile"].each do |opt| + @config[:xprofiles] = [] + @options.parse [opt, "spec/profiles/rails.yaml"] + @config[:xprofiles].should include("spec/profiles/rails.yaml") + end + end +end + +describe "The -Z, --dry-run option" do + before :each do + @options, @config = new_option + @options.pretend + end + + it "is enabled with #pretend" do + @options.should_receive(:on).with("-Z", "--dry-run", an_instance_of(String)) + @options.pretend + end + + it "registers the MSpec pretend mode" do + MSpec.should_receive(:register_mode).with(:pretend).twice + ["-Z", "--dry-run"].each do |opt| + @options.parse opt + end + end +end + +describe "The --unguarded option" do + before :each do + @options, @config = new_option + @options.unguarded + end + + it "is enabled with #unguarded" do + @options.stub(:on) + @options.should_receive(:on).with("--unguarded", an_instance_of(String)) + @options.unguarded + end + + it "registers the MSpec unguarded mode" do + MSpec.should_receive(:register_mode).with(:unguarded) + @options.parse "--unguarded" + end +end + +describe "The --no-ruby_guard option" do + before :each do + @options, @config = new_option + @options.unguarded + end + + it "is enabled with #unguarded" do + @options.stub(:on) + @options.should_receive(:on).with("--no-ruby_bug", an_instance_of(String)) + @options.unguarded + end + + it "registers the MSpec no_ruby_bug mode" do + MSpec.should_receive(:register_mode).with(:no_ruby_bug) + @options.parse "--no-ruby_bug" + end +end + +describe "The -H, --random option" do + before :each do + @options, @config = new_option + @options.randomize + end + + it "is enabled with #randomize" do + @options.should_receive(:on).with("-H", "--random", an_instance_of(String)) + @options.randomize + end + + it "registers the MSpec randomize mode" do + MSpec.should_receive(:randomize).twice + ["-H", "--random"].each do |opt| + @options.parse opt + end + end +end + +describe "The -R, --repeat option" do + before :each do + @options, @config = new_option + @options.repeat + end + + it "is enabled with #repeat" do + @options.should_receive(:on).with("-R", "--repeat", "NUMBER", an_instance_of(String)) + @options.repeat + end + + it "registers the MSpec repeat mode" do + ["-R", "--repeat"].each do |opt| + MSpec.repeat = 1 + @options.parse [opt, "10"] + repeat_count = 0 + MSpec.repeat do + repeat_count += 1 + end + repeat_count.should == 10 + end + end +end + +describe "The -V, --verbose option" do + before :each do + @options, @config = new_option + @options.verbose + end + + it "is enabled with #verbose" do + @options.stub(:on) + @options.should_receive(:on).with("-V", "--verbose", an_instance_of(String)) + @options.verbose + end + + it "registers a verbose output object with MSpec" do + MSpec.should_receive(:register).with(:start, anything()).twice + MSpec.should_receive(:register).with(:load, anything()).twice + ["-V", "--verbose"].each do |opt| + @options.parse opt + end + end +end + +describe "The -m, --marker MARKER option" do + before :each do + @options, @config = new_option + @options.verbose + end + + it "is enabled with #verbose" do + @options.stub(:on) + @options.should_receive(:on).with("-m", "--marker", "MARKER", + an_instance_of(String)) + @options.verbose + end + + it "registers a marker output object with MSpec" do + MSpec.should_receive(:register).with(:load, anything()).twice + ["-m", "--marker"].each do |opt| + @options.parse [opt, ","] + end + end +end + +describe "The --int-spec option" do + before :each do + @options, @config = new_option + @options.interrupt + end + + it "is enabled with #interrupt" do + @options.should_receive(:on).with("--int-spec", an_instance_of(String)) + @options.interrupt + end + + it "sets the abort config option to false to only abort the running spec with ^C" do + @config[:abort] = true + @options.parse "--int-spec" + @config[:abort].should == false + end +end + +describe "The -Y, --verify option" do + before :each do + @options, @config = new_option + @options.verify + end + + it "is enabled with #interrupt" do + @options.stub(:on) + @options.should_receive(:on).with("-Y", "--verify", an_instance_of(String)) + @options.verify + end + + it "sets the MSpec mode to :verify" do + MSpec.should_receive(:register_mode).with(:verify).twice + ["-Y", "--verify"].each do |m| + @options.parse m + end + end +end + +describe "The -O, --report option" do + before :each do + @options, @config = new_option + @options.verify + end + + it "is enabled with #interrupt" do + @options.stub(:on) + @options.should_receive(:on).with("-O", "--report", an_instance_of(String)) + @options.verify + end + + it "sets the MSpec mode to :report" do + MSpec.should_receive(:register_mode).with(:report).twice + ["-O", "--report"].each do |m| + @options.parse m + end + end +end + +describe "The --report-on GUARD option" do + before :all do + MSpec.stub(:register_mode) + end + + before :each do + @options, @config = new_option + @options.verify + + SpecGuard.clear_guards + end + + after :each do + SpecGuard.clear_guards + end + + it "is enabled with #interrupt" do + @options.stub(:on) + @options.should_receive(:on).with("--report-on", "GUARD", an_instance_of(String)) + @options.verify + end + + it "sets the MSpec mode to :report_on" do + MSpec.should_receive(:register_mode).with(:report_on) + @options.parse ["--report-on", "ruby_bug"] + end + + it "converts the guard name to a symbol" do + name = double("ruby_bug") + name.should_receive(:to_sym) + @options.parse ["--report-on", name] + end + + it "saves the name of the guard" do + @options.parse ["--report-on", "ruby_bug"] + SpecGuard.guards.should == [:ruby_bug] + end +end + +describe "The -K, --action-tag TAG option" do + before :each do + @options, @config = new_option + @options.action_filters + end + + it "is enabled with #action_filters" do + @options.stub(:on) + @options.should_receive(:on).with("-K", "--action-tag", "TAG", + an_instance_of(String)) + @options.action_filters + end + + it "adds TAG to the list of tags that trigger actions" do + ["-K", "--action-tag"].each do |opt| + @config[:atags] = [] + @options.parse [opt, "action-tag"] + @config[:atags].should include("action-tag") + end + end +end + +describe "The -S, --action-string STR option" do + before :each do + @options, @config = new_option + @options.action_filters + end + + it "is enabled with #action_filters" do + @options.stub(:on) + @options.should_receive(:on).with("-S", "--action-string", "STR", + an_instance_of(String)) + @options.action_filters + end + + it "adds STR to the list of spec descriptions that trigger actions" do + ["-S", "--action-string"].each do |opt| + @config[:astrings] = [] + @options.parse [opt, "action-str"] + @config[:astrings].should include("action-str") + end + end +end + +describe "The -d, --debug option" do + before :each do + @options, @config = new_option + @options.debug + end + + after :each do + $MSPEC_DEBUG = nil + end + + it "is enabled with #debug" do + @options.stub(:on) + @options.should_receive(:on).with("-d", "--debug", an_instance_of(String)) + @options.debug + end + + it "sets $MSPEC_DEBUG to true" do + ["-d", "--debug"].each do |opt| + $MSPEC_DEBUG.should_not be_true + @options.parse opt + $MSPEC_DEBUG.should be_true + $MSPEC_DEBUG = nil + end + end +end diff --git a/spec/mspec/spec/utils/script_spec.rb b/spec/mspec/spec/utils/script_spec.rb new file mode 100644 index 0000000000..62f6787acd --- /dev/null +++ b/spec/mspec/spec/utils/script_spec.rb @@ -0,0 +1,473 @@ +require 'spec_helper' +require 'mspec/utils/script' +require 'mspec/runner/mspec' +require 'mspec/runner/filters' +require 'mspec/runner/actions/filter' + +describe MSpecScript, ".config" do + it "returns a Hash" do + MSpecScript.config.should be_kind_of(Hash) + end +end + +describe MSpecScript, ".set" do + it "sets the config hash key, value" do + MSpecScript.set :a, 10 + MSpecScript.config[:a].should == 10 + end +end + +describe MSpecScript, ".get" do + it "gets the config hash value for a key" do + MSpecScript.set :a, 10 + MSpecScript.get(:a).should == 10 + end +end + +describe MSpecScript, "#config" do + it "returns the MSpecScript config hash" do + MSpecScript.set :b, 5 + MSpecScript.new.config[:b].should == 5 + end + + it "returns the MSpecScript config hash from subclasses" do + class MSSClass < MSpecScript; end + MSpecScript.set :b, 5 + MSSClass.new.config[:b].should == 5 + end +end + +describe MSpecScript, "#load_default" do + before :all do + @verbose = $VERBOSE + $VERBOSE = nil + end + + after :all do + $VERBOSE = @verbose + end + + before :each do + @version = RUBY_VERSION + if Object.const_defined? :RUBY_ENGINE + @engine = Object.const_get :RUBY_ENGINE + end + @script = MSpecScript.new + MSpecScript.stub(:new).and_return(@script) + end + + after :each do + Object.const_set :RUBY_VERSION, @version + Object.const_set :RUBY_ENGINE, @engine if @engine + end + + it "attempts to load 'default.mspec'" do + @script.stub(:try_load) + @script.should_receive(:try_load).with('default.mspec').and_return(true) + @script.load_default + end + + it "attempts to load a config file based on RUBY_ENGINE and RUBY_VERSION" do + Object.const_set :RUBY_ENGINE, "ybur" + Object.const_set :RUBY_VERSION, "1.8.9" + default = "ybur.1.8.mspec" + @script.should_receive(:try_load).with('default.mspec').and_return(false) + @script.should_receive(:try_load).with(default) + @script.should_receive(:try_load).with('ybur.mspec') + @script.load_default + end +end + +describe MSpecScript, ".main" do + before :each do + @script = double("MSpecScript").as_null_object + MSpecScript.stub(:new).and_return(@script) + # Do not require full mspec as it would conflict with RSpec + MSpecScript.should_receive(:require).with('mspec') + end + + it "creates an instance of MSpecScript" do + MSpecScript.should_receive(:new).and_return(@script) + MSpecScript.main + end + + it "attempts to load the default config" do + @script.should_receive(:load_default) + MSpecScript.main + end + + it "attempts to load the '~/.mspecrc' script" do + @script.should_receive(:try_load).with('~/.mspecrc') + MSpecScript.main + end + + it "calls the #options method on the script" do + @script.should_receive(:options) + MSpecScript.main + end + + it "calls the #signals method on the script" do + @script.should_receive(:signals) + MSpecScript.main + end + + it "calls the #register method on the script" do + @script.should_receive(:register) + MSpecScript.main + end + + it "calls the #setup_env method on the script" do + @script.should_receive(:setup_env) + MSpecScript.main + end + + it "calls the #run method on the script" do + @script.should_receive(:run) + MSpecScript.main + end +end + +describe MSpecScript, "#initialize" do + before :each do + @config = MSpecScript.new.config + end + + it "sets the default config values" do + @config[:formatter].should == nil + @config[:includes].should == [] + @config[:excludes].should == [] + @config[:patterns].should == [] + @config[:xpatterns].should == [] + @config[:tags].should == [] + @config[:xtags].should == [] + @config[:atags].should == [] + @config[:astrings].should == [] + @config[:abort].should == true + @config[:config_ext].should == '.mspec' + end +end + +describe MSpecScript, "#load" do + before :each do + File.stub(:exist?).and_return(false) + @script = MSpecScript.new + @file = "default.mspec" + @base = "default" + end + + it "attempts to locate the file through the expanded path name" do + File.should_receive(:expand_path).with(@file, ".").and_return(@file) + File.should_receive(:exist?).with(@file).and_return(true) + Kernel.should_receive(:load).with(@file).and_return(:loaded) + @script.load(@file).should == :loaded + end + + it "appends config[:config_ext] to the name and attempts to locate the file through the expanded path name" do + File.should_receive(:expand_path).with(@base, ".").and_return(@base) + File.should_receive(:expand_path).with(@base, "spec").and_return(@base) + File.should_receive(:expand_path).with(@file, ".").and_return(@file) + File.should_receive(:exist?).with(@base).and_return(false) + File.should_receive(:exist?).with(@file).and_return(true) + Kernel.should_receive(:load).with(@file).and_return(:loaded) + @script.load(@base).should == :loaded + end + + it "attemps to locate the file in '.'" do + path = File.expand_path @file, "." + File.should_receive(:exist?).with(path).and_return(true) + Kernel.should_receive(:load).with(path).and_return(:loaded) + @script.load(@file).should == :loaded + end + + it "appends config[:config_ext] to the name and attempts to locate the file in '.'" do + path = File.expand_path @file, "." + File.should_receive(:exist?).with(path).and_return(true) + Kernel.should_receive(:load).with(path).and_return(:loaded) + @script.load(@base).should == :loaded + end + + it "attemps to locate the file in 'spec'" do + path = File.expand_path @file, "spec" + File.should_receive(:exist?).with(path).and_return(true) + Kernel.should_receive(:load).with(path).and_return(:loaded) + @script.load(@file).should == :loaded + end + + it "appends config[:config_ext] to the name and attempts to locate the file in 'spec'" do + path = File.expand_path @file, "spec" + File.should_receive(:exist?).with(path).and_return(true) + Kernel.should_receive(:load).with(path).and_return(:loaded) + @script.load(@base).should == :loaded + end + + it "loads a given file only once" do + path = File.expand_path @file, "spec" + File.should_receive(:exist?).with(path).and_return(true) + Kernel.should_receive(:load).once.with(path).and_return(:loaded) + @script.load(@base).should == :loaded + @script.load(@base).should == true + end +end + +describe MSpecScript, "#custom_options" do + before :each do + @script = MSpecScript.new + end + + after :each do + end + + it "prints 'None'" do + options = double("options") + options.should_receive(:doc).with(" No custom options registered") + @script.custom_options options + end +end + +describe MSpecScript, "#register" do + before :each do + @script = MSpecScript.new + + @formatter = double("formatter").as_null_object + @script.config[:formatter] = @formatter + end + + it "creates and registers the formatter" do + @formatter.should_receive(:new).and_return(@formatter) + @formatter.should_receive(:register) + @script.register + end + + it "does not register the formatter if config[:formatter] is false" do + @script.config[:formatter] = false + @script.register + end + + it "calls #custom_register" do + @script.should_receive(:custom_register) + @script.register + end + + it "registers :formatter with the formatter instance" do + @formatter.stub(:new).and_return(@formatter) + MSpec.should_receive(:store).with(:formatter, @formatter) + @script.register + end + + it "does not register :formatter if config[:formatter] is false" do + @script.config[:formatter] = false + MSpec.should_not_receive(:store) + @script.register + end +end + +describe MSpecScript, "#register" do + before :each do + @script = MSpecScript.new + + @formatter = double("formatter").as_null_object + @script.config[:formatter] = @formatter + + @filter = double("filter") + @filter.should_receive(:register) + + @ary = ["some", "spec"] + end + + it "creates and registers a MatchFilter for include specs" do + MatchFilter.should_receive(:new).with(:include, *@ary).and_return(@filter) + @script.config[:includes] = @ary + @script.register + end + + it "creates and registers a MatchFilter for excluded specs" do + MatchFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter) + @script.config[:excludes] = @ary + @script.register + end + + it "creates and registers a RegexpFilter for include specs" do + RegexpFilter.should_receive(:new).with(:include, *@ary).and_return(@filter) + @script.config[:patterns] = @ary + @script.register + end + + it "creates and registers a RegexpFilter for excluded specs" do + RegexpFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter) + @script.config[:xpatterns] = @ary + @script.register + end + + it "creates and registers a TagFilter for include specs" do + TagFilter.should_receive(:new).with(:include, *@ary).and_return(@filter) + @script.config[:tags] = @ary + @script.register + end + + it "creates and registers a TagFilter for excluded specs" do + TagFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter) + @script.config[:xtags] = @ary + @script.register + end + + it "creates and registers a ProfileFilter for include specs" do + ProfileFilter.should_receive(:new).with(:include, *@ary).and_return(@filter) + @script.config[:profiles] = @ary + @script.register + end + + it "creates and registers a ProfileFilter for excluded specs" do + ProfileFilter.should_receive(:new).with(:exclude, *@ary).and_return(@filter) + @script.config[:xprofiles] = @ary + @script.register + end +end + +describe MSpecScript, "#signals" do + before :each do + @script = MSpecScript.new + @abort = @script.config[:abort] + end + + after :each do + @script.config[:abort] = @abort + end + + it "traps the INT signal if config[:abort] is true" do + Signal.should_receive(:trap).with("INT") + @script.config[:abort] = true + @script.signals + end + + it "does not trap the INT signal if config[:abort] is not true" do + Signal.should_not_receive(:trap).with("INT") + @script.config[:abort] = false + @script.signals + end +end + +describe MSpecScript, "#entries" do + before :each do + @script = MSpecScript.new + + File.stub(:expand_path).and_return("name") + File.stub(:file?).and_return(false) + File.stub(:directory?).and_return(false) + end + + it "returns the pattern in an array if it is a file" do + File.should_receive(:expand_path).with("file").and_return("file/expanded") + File.should_receive(:file?).with("file/expanded").and_return(true) + @script.entries("file").should == ["file/expanded"] + end + + it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do + File.should_receive(:directory?).with("name").and_return(true) + File.stub(:expand_path).and_return("name","name/**/*_spec.rb") + Dir.should_receive(:[]).with("name/**/*_spec.rb").and_return(["dir1", "dir2"]) + @script.entries("name").should == ["dir1", "dir2"] + end + + it "aborts if pattern cannot be resolved to a file nor a directory" do + @script.should_receive(:abort) + @script.entries("pattern") + end + + describe "with config[:prefix] set" do + before :each do + prefix = "prefix/dir" + @script.config[:prefix] = prefix + @name = prefix + "/name" + end + + it "returns the pattern in an array if it is a file" do + File.should_receive(:expand_path).with(@name).and_return(@name) + File.should_receive(:file?).with(@name).and_return(true) + @script.entries("name").should == [@name] + end + + it "returns Dir['pattern/**/*_spec.rb'] if pattern is a directory" do + File.stub(:expand_path).and_return(@name, @name+"/**/*_spec.rb") + File.should_receive(:directory?).with(@name).and_return(true) + Dir.should_receive(:[]).with(@name + "/**/*_spec.rb").and_return(["dir1", "dir2"]) + @script.entries("name").should == ["dir1", "dir2"] + end + + it "aborts if pattern cannot be resolved to a file nor a directory" do + @script.should_receive(:abort) + @script.entries("pattern") + end + end +end + +describe MSpecScript, "#files" do + before :each do + @script = MSpecScript.new + end + + it "accumlates the values returned by #entries" do + @script.should_receive(:entries).and_return(["file1"], ["file2"]) + @script.files(["a", "b"]).should == ["file1", "file2"] + end + + it "strips a leading '^' and removes the values returned by #entries" do + @script.should_receive(:entries).and_return(["file1"], ["file2"], ["file1"]) + @script.files(["a", "b", "^a"]).should == ["file2"] + end + + it "processes the array elements in order" do + @script.should_receive(:entries).and_return(["file1"], ["file1"], ["file2"]) + @script.files(["^a", "a", "b"]).should == ["file1", "file2"] + end +end + +describe MSpecScript, "#files" do + before :each do + MSpecScript.set :files, ["file1", "file2"] + + @script = MSpecScript.new + end + + after :each do + MSpecScript.config.delete :files + end + + it "looks up items with leading ':' in the config object" do + @script.should_receive(:entries).and_return(["file1"], ["file2"]) + @script.files([":files"]).should == ["file1", "file2"] + end + + it "returns an empty list if the config key is not set" do + @script.files([":all_files"]).should == [] + end +end + +describe MSpecScript, "#setup_env" do + before :each do + @script = MSpecScript.new + @options, @config = new_option + @script.stub(:config).and_return(@config) + end + + after :each do + end + + it "sets MSPEC_RUNNER = '1' in the environment" do + ENV["MSPEC_RUNNER"] = "0" + @script.setup_env + ENV["MSPEC_RUNNER"].should == "1" + end + + it "sets RUBY_EXE = config[:target] in the environment" do + ENV["RUBY_EXE"] = nil + @script.setup_env + ENV["RUBY_EXE"].should == @config[:target] + end + + it "sets RUBY_FLAGS = config[:flags] in the environment" do + ENV["RUBY_FLAGS"] = nil + @config[:flags] = ["-w", "-Q"] + @script.setup_env + ENV["RUBY_FLAGS"].should == "-w -Q" + end +end diff --git a/spec/mspec/spec/utils/version_spec.rb b/spec/mspec/spec/utils/version_spec.rb new file mode 100644 index 0000000000..0b2d383c6d --- /dev/null +++ b/spec/mspec/spec/utils/version_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' +require 'mspec/utils/version' + +describe SpecVersion, "#to_s" do + it "returns the string with which it was initialized" do + SpecVersion.new("1.8").to_s.should == "1.8" + SpecVersion.new("2.118.9").to_s.should == "2.118.9" + end +end + +describe SpecVersion, "#to_str" do + it "returns the same string as #to_s" do + version = SpecVersion.new("2.118.9") + version.to_str.should == version.to_s + end +end + +describe SpecVersion, "#to_i with ceil = false" do + it "returns an integer representation of the version string" do + SpecVersion.new("2.23.10").to_i.should == 1022310 + end + + it "replaces missing version parts with zeros" do + SpecVersion.new("1.8").to_i.should == 1010800 + SpecVersion.new("1.8.6").to_i.should == 1010806 + end +end + +describe SpecVersion, "#to_i with ceil = true" do + it "returns an integer representation of the version string" do + SpecVersion.new("1.8.6", true).to_i.should == 1010806 + end + + it "fills in 9s for missing tiny values" do + SpecVersion.new("1.8", true).to_i.should == 1010899 + SpecVersion.new("1.8.6", true).to_i.should == 1010806 + end +end + +describe SpecVersion, "#to_int" do + it "returns the same value as #to_i" do + version = SpecVersion.new("4.16.87") + version.to_int.should == version.to_i + end +end diff --git a/spec/mspec/tool/remove_old_guards.rb b/spec/mspec/tool/remove_old_guards.rb new file mode 100644 index 0000000000..d0920344eb --- /dev/null +++ b/spec/mspec/tool/remove_old_guards.rb @@ -0,0 +1,41 @@ +# Remove old version guards in ruby/spec + +def dedent(line) + if line.start_with?(" ") + line[2..-1] + else + line + end +end + +def remove_guards(guard, keep) + Dir["*/**/*.rb"].each do |file| + contents = File.read(file) + if contents =~ guard + puts file + lines = contents.lines.to_a + while first = lines.find_index { |line| line =~ guard } + indent = lines[first][/^(\s*)/, 1].length + last = (first+1...lines.size).find { |i| + space = lines[i][/^(\s*)end$/, 1] and space.length == indent + } + raise file unless last + if keep + lines[first..last] = lines[first+1..last-1].map { |l| dedent(l) } + else + if first > 0 and lines[first-1] == "\n" + first -= 1 + elsif lines[last+1] == "\n" + last += 1 + end + lines[first..last] = [] + end + end + File.write file, lines.join + end + end +end + +version = "2.2" +remove_guards(/ruby_version_is ["']#{version}["'] do/, true) +remove_guards(/ruby_version_is ["'][0-9.]*["']...["']#{version}["'] do/, false) diff --git a/spec/rubyspec/.gitignore b/spec/rubyspec/.gitignore new file mode 100644 index 0000000000..3f1206a16e --- /dev/null +++ b/spec/rubyspec/.gitignore @@ -0,0 +1,5 @@ +/Gemfile.lock +/rubyspec_temp +/ext +/.ruby-version +/.ruby-gemset diff --git a/spec/rubyspec/.travis.yml b/spec/rubyspec/.travis.yml new file mode 100644 index 0000000000..04da12a130 --- /dev/null +++ b/spec/rubyspec/.travis.yml @@ -0,0 +1,30 @@ +sudo: false +language: ruby +install: + - git clone https://github.com/ruby/mspec.git ../mspec +script: + - ../mspec/bin/mspec $MSPEC_OPTS +matrix: + include: + - os: osx + rvm: 2.4.0 + - os: linux + rvm: 2.4.1 + env: MSPEC_OPTS="-R2 -ff" + - os: linux + rvm: 2.2.7 + - os: linux + rvm: 2.3.4 + - os: linux + rvm: 2.4.1 + env: CHECK_LEAKS=true + - os: linux + rvm: ruby-head +branches: + only: + - master + - /^try/ +notifications: + email: + on_success: change + on_failure: change diff --git a/spec/rubyspec/CHANGES.before-2008-05-10 b/spec/rubyspec/CHANGES.before-2008-05-10 new file mode 100644 index 0000000000..18778bc146 --- /dev/null +++ b/spec/rubyspec/CHANGES.before-2008-05-10 @@ -0,0 +1,17796 @@ + Changelog +=========== + +This file contains the entire revision history of the specs from +December 2006 onwards, when the spec project got started more or +less officially by converting the remaining Test::Unit style tests +in Rubinius to the spec style. The history is not preserved in the +git repository history itself, so this data is here for reference. +All the commit hashes are from the Rubinius repository. + +It still misses quite a few of the earlier, disparate specs and +tests because up to that point the organisation was much looser +and gathering an exhaustive accounting of the entire history of +TDD/BDD would be time-consuming, particularly with the few full +directory moves in there and such. All of the data is preserved +in the Rubinius repository if someone is interested in that bit +of history. + +Be aware that the history contains some Rubinius-specific specs +by necessity. If you find any commits listed that were _solely_ +for Rubinius, feel free to strip them out. + +Thanks to everyone committing up to this point--over 2600 commits +in just this incomplete version. Keep it up. + + + + Revision History +------------------ + + +commit 2b24a1e84c350810817885eeb6532f43c698a95c +Author: Ryan Davis +Date: Fri May 9 16:45:07 2008 -0700 + + Fixed up pack for base64 and uuencode to be MUCH MUCH cleaner and 2x faster + +commit 022bc5dbfafcf1f9fd5e25820104718bd4d45661 +Author: Vladimir Sizikov +Date: Fri May 9 23:51:47 2008 +0200 + + Share common specs for BigDecimal's #mult and #*. + +commit 414e7eedce9d0cea982e24f1031c407daccc648b +Author: Vladimir Sizikov +Date: Fri May 9 23:19:38 2008 +0200 + + New rubyspecs for BigDecimal#mult + + * Verifies that proper signs are calculated when + zero is involved. + +commit 6883d7d0c67f7be84e7ea1703912452eaecaac6c +Author: Vladimir Sizikov +Date: Fri May 9 22:54:49 2008 +0200 + + New rubyspec for Module#new with block. + +commit f8bd3e34014a7351470685676b6b168abd787794 +Author: Phil Hagelberg +Date: Fri May 9 12:53:00 2008 -0700 + + Added specs for OpenSSL::HMAC.hexdigest and .digest + +commit 686c28493d42b9c798aa791823395d1000423225 +Author: Vladimir Sizikov +Date: Fri May 9 20:20:13 2008 +0200 + + Some more rubyspecs for BigDecimal's #floor and #ceil. + +commit aba022a6620ec8d3a09067e9677f0f9c5d8078ee +Author: Vladimir Sizikov +Date: Fri May 9 17:51:35 2008 +0200 + + New rubyspecs for BigDecimal's #floor and #ceil. + +commit e4d844ba5851a798b7acb684cf68fdcef353d13c +Author: Brian Ford +Date: Thu May 8 22:13:58 2008 -0700 + + Excluded stdlib specs from default CI run. Added spec/full.mspec. + +commit 6a133574617cb435ad1684f208430112ff6839f6 +Author: Ryan Davis +Date: Thu May 8 16:19:50 2008 -0700 + + String#unpack overhaul. NO extra methods littered through Fixnum/Integer/String. NO procs. More readable, but still messy. + +commit 11dd3ae2c4e0dd81304e85ba662db41196f1ce4c +Author: Brian Ford +Date: Wed May 7 23:59:31 2008 -0700 + + Fixed constant type clash for ModuleSpecs modules. + +commit 4e702d10b32fdba62cdeae476b8217019839c3b0 +Author: Brian Ford +Date: Wed May 7 21:42:11 2008 -0700 + + Some specs for Kernel#__add_method__ and Module.__add_method__. + +commit 819649f24f59819be185b0562b94f9089f8c000c +Author: Brian Ford +Date: Wed May 7 14:48:01 2008 -0700 + + Added spec for Kernel#eval with binding from method defined by #eval. + +commit d73b17b88b6084fdf7cab764b0fbdd3b3882dd81 +Author: Brian Ford +Date: Wed May 7 10:06:26 2008 -0700 + + Use literals in Bignum#to_f specs (alternate fix for #535). + +commit ee211770eb8792b3f58f78ff60eec6d5289caa20 +Author: Ryan Davis +Date: Wed May 7 02:38:00 2008 -0700 + + Added specs for big uncovered areas, still not 100% + +commit 7ce9bc2d7edc64f6886c3d34836bc0394414ed66 +Author: Ryan Davis +Date: Tue May 6 03:56:19 2008 -0700 + + Fixed typo + +commit af3407251ee0f287ec80232c354153af169636e4 +Author: Adam Gardiner +Date: Tue May 6 22:01:23 2008 +1000 + + Fix bug in Debugger::Output.wrap + +commit d9322306ea70f2b847b0f806bdb13ea02f2d6b4d +Author: Marnen Laibow-Koser +Date: Mon May 5 13:09:32 2008 -0400 + + Fix some bugs in BigDecimal#/. More may yet lurk. + +commit 2f3a4cc14433858b13caa932c8a50c31e024c7e8 +Author: Federico Builes +Date: Mon May 5 12:04:26 2008 -0500 + + Adding more specs for REXML::Element + + * Covers REXML::Element#{add_attribute, add_attributes, add_namespace, add_text, clone, comments} + +commit 7db8c2b563ea474cf2db5fa14bb2a6345c8c469f +Author: Federico Builes +Date: Mon May 5 10:54:00 2008 -0500 + + One more case for YAML.load specs + +commit 098decdf510b05f82ff9a6cc6769cf478a3236ab +Author: Marnen Laibow-Koser +Date: Sun May 4 22:29:35 2008 -0400 + + Define BigDecimal#ver. + +commit f6f1fe6a667570e4c1521649b964dca1352d1c32 +Author: Marnen Laibow-Koser +Date: Sun May 4 22:12:08 2008 -0400 + + BigDecimal#new: Make space between '-' and 'Infinity' unparsable, as per spec. + +commit 503aae7cdbb208da8f25080762e17f0866845c4d +Author: Eero Saynatkari +Date: Mon May 5 06:19:40 2008 -0400 + + Method call parsing spec from Jim Kingdon with minor addition. + + * Moved the SyntaxError producing code into an #eval because the file + cannot be compiled to run otherwise. + +commit 398d5de0a0ffaf746e39e5f6a6ded02483fd1842 +Author: Eero Saynatkari +Date: Mon May 5 02:26:39 2008 -0400 + + Spec for :match node, implicit Regexp matches against $_. + + * Compiler and Language specs. + +commit 206cea31c6a93fe434948dcb79321e2c119edf21 +Author: Marnen Laibow-Koser +Date: Sat May 3 11:40:17 2008 -0400 + + Implement BigDecimal#power and #**, fix some bugs in #mult. + +commit a197099d9be6e48ad32480ae323302c83146147b +Author: Eero Saynatkari +Date: Sat May 3 02:18:35 2008 -0400 + + Fixed a logic and syntax error in BigDecimal#mult specs. + + * Removed some parentheses too. + +commit 081afd58a29ccd5025b806f53e9d7679b9296a7f +Author: Wilson Bilkovich +Date: Sat May 3 02:25:45 2008 -0400 + + Make sure subclasses that implement their own Hash#default work (Merb) + +commit 203ca288175416fadb110b2aa9cdf8cfbf13215d +Author: Wilson Bilkovich +Date: Sat May 3 01:49:35 2008 -0400 + + Specs and implementation for module include order (fixes abstract.rb) + +commit c788a9f2d9c4561a2837bbf78f68a6885d626917 +Author: Marnen Laibow-Koser +Date: Fri May 2 18:14:32 2008 -0400 + + Implement BigDecimal#*, as well as #mult without precision support. + +commit 57d78528ff4cf249d906785ffbfdde1fda4aa3cc +Author: Marnen Laibow-Koser +Date: Fri May 2 17:36:15 2008 -0400 + + Implement BigDecimal#/ and #quo. Not perfect; still relies on #/. + +commit c42cc2cacc347d8284650c7046d4dadf94d7d4a5 +Author: Marnen Laibow-Koser +Date: Fri May 2 16:13:07 2008 -0400 + + Fix a typo in specs. + +commit ae179b410665da18628f249e6796f1e07ab83763 +Author: Marnen Laibow-Koser +Date: Fri May 2 16:10:43 2008 -0400 + + Get BigDecimal#floor basically working. + * The failing specs depend on #/, which isn't implemented yet. + +commit f8221117d174b91affe406c8089ed25e887232b3 +Author: Marnen Laibow-Koser +Date: Fri May 2 16:06:32 2008 -0400 + + Fix bugs in BigDecimal#add and #+. This also affects #sub, #-, and #ceil. + +commit cdd196daf7643e846b7f3582b1e441b883e02aba +Author: Marnen Laibow-Koser +Date: Fri May 2 15:41:29 2008 -0400 + + More specs to fix bugs in BigDecimal#add and #+. + +commit c1c52a2a531b570fa1025d99e464d93c570cf59e +Author: Marnen Laibow-Koser +Date: Fri May 2 15:22:33 2008 -0400 + + Write another spec for BigDecimal#ceil. + +commit 71b65cdbfa5aae461fc52c997df9fca3bee9c8d5 +Author: Marnen Laibow-Koser +Date: Fri May 2 14:47:34 2008 -0400 + + Write tests for a bug in BigDecimal#add and #+ where 0 + 1 = 0.1. + +commit 55988ef53879c1c489c570b3f37717365c7f8e2b +Author: Wilson Bilkovich +Date: Sat May 3 01:04:11 2008 -0400 + + Fix use of alias keyword inside instance_eval + +commit d4011595a0077e91665f85410d458c57367cf50b +Author: Vladimir Sizikov +Date: Fri May 2 20:38:15 2008 +0200 + + Added news specs for BigDecimal#mult. + +commit b6771644d35b6b8f3c87f7f4461bcaba99cd976f +Author: Vladimir Sizikov +Date: Fri May 2 19:41:09 2008 +0200 + + More BigDecimal#divmod rubyspecs. + + MRI-specific bug is hidden behind ruby-bug guard. + +commit 854a011324ce717cfd47ddec6389a9e9abb0db18 +Author: Vladimir Sizikov +Date: Fri May 2 18:45:29 2008 +0200 + + New BigDecimal#divmod specs. + +commit b9806e0efb2a8e51d70f6d51733df7bed88152d9 +Author: Vladimir Sizikov +Date: Fri May 2 16:45:10 2008 +0200 + + A couple of test cases for BigDecimal's #quo, #div, #/. + +commit 3cf6c1e03001ba1dda966e3392b665f5b08a1b9d +Author: Vladimir Sizikov +Date: Fri May 2 15:03:37 2008 +0200 + + More tests for BigDecimal#floor. + +commit b70023978562af89cf4349e14e9443adb37ecbbe +Author: Eero Saynatkari +Date: Thu May 1 21:15:29 2008 -0400 + + Improved a spec description for String#index. + + * The description looks exactly like we had the wrong implementation + relying on % 256 and someone wrote a spec to make sure that did not + happen. However, the description was more or less meaningless to + what was actually being specced. + +commit 6e6aa411ff4c7a837d5d4adb9ab893719cf9e122 +Author: Evan Phoenix +Date: Thu May 1 10:48:20 2008 -0700 + + Fix a number of things to pass all def specs + + This is the result of ping-pong between Evan and Wilson. It refactors + out enclosing_class from being used, and instead information is always + pulled directly from the StaticScope object. This lets us inject proper + scoping changes in ruby. + +commit 2db27aef88e2ca7752beba846d172ede276275e0 +Author: Dirkjan Bussink +Date: Thu May 1 19:03:50 2008 +0200 + + Implemented Socket.unpack_sockaddr_un + +commit d515221698e02b52ed4661113d659744fbfae36f +Author: Dirkjan Bussink +Date: Thu May 1 18:18:32 2008 +0200 + + Forgot to update spec tags for TCPSocket.gethostbyname + +commit bf839a99c3a5b773b6b96c6d5a1fcc5056511e7a +Author: Dirkjan Bussink +Date: Thu May 1 14:03:30 2008 +0200 + + Implement File#mtime specs + +commit b8c713e6b972b464788c740b4283a5b4226c123c +Author: Dirkjan Bussink +Date: Thu May 1 13:55:36 2008 +0200 + + Implemented File.lchmod and initial specs + +commit 059c926d7280c2e7c9f8bf710c5aef70cde3e777 +Author: Adam Wiggins +Date: Sun Apr 27 15:03:31 2008 -0700 + + IO.popen read/write pipes + + Signed-off-by: Dirkjan Bussink + +commit d9a050aa45efd00a40395b7ac7ac069f4be1fd1c +Author: Adam Gardiner +Date: Thu May 1 16:25:18 2008 +1000 + + Spec fixes for Tuple#to_a + +commit 0b610359fbfe8137fdba95d90b659238168d6788 +Author: Marnen Laibow-Koser +Date: Wed Apr 30 17:45:51 2008 -0400 + + Update spectags. + +commit 024ebfdf3fa9c54b8a81134edb52fe10b09e4b91 +Author: Vladimir Sizikov +Date: Wed Apr 30 22:56:44 2008 +0200 + + Added BigDecimal#divmod excludes. + +commit e12d21a90760df723c0f48265cb49a9c4463db7c +Author: Vladimir Sizikov +Date: Wed Apr 30 20:51:06 2008 +0200 + + More tests for BigDecimal#divmod. + +commit 68cfef604f9b5411ca9e0349883bac4f59541f0d +Author: Marnen Laibow-Koser +Date: Wed Apr 30 16:47:31 2008 -0400 + + Make BigDecimal#finite? handle NaN correctly, and refactor accordingly. + +commit 5066bcb8881241caf6d13be625b32633bda6567e +Author: Marnen Laibow-Koser +Date: Wed Apr 30 15:01:12 2008 -0400 + + Make BigDecimal#<= and #>= pass Vladimir's new specs. + +commit 49601aff01c394fe2168f5f221a987be63a9ebc7 +Author: Vladimir Sizikov +Date: Wed Apr 30 20:20:18 2008 +0200 + + Various improvements to BigDecimal rubyspecs. + + * Corrected comparison specs (properly add arrays there) + * New reminder specs + * New modulo and % specs + * Tagged rbx failures + +commit dd1700b747ba26b27eff0b249623aca559db06e1 +Author: Vladimir Sizikov +Date: Wed Apr 30 17:48:20 2008 +0200 + + More test cases for BigDecimal#modulo and #%. + +commit 8eb9dc1b0aee3587f4da8b9cbe306fd431159d79 +Author: Vladimir Sizikov +Date: Wed Apr 30 16:57:16 2008 +0200 + + New specs for BigDecimal#modulo and #%. + +commit 4a846f807fe2c4c12d8719bc5c9ccb4ab696aff9 +Author: Federico Builes +Date: Tue Apr 29 15:29:34 2008 -0500 + + Fixes REXML::Element#namespaces specs + + * Use sort on the arrays to make sure the specs pass on JRuby too. + +commit 823683a864072ef6a81e808dbf792dee45d29c52 +Author: Federico Builes +Date: Tue Apr 29 14:54:08 2008 -0500 + + Adds more specs for REXML. + + * Specs for REXML#{inspect, namespace, namespaces, prefixes, text and text=}. + +commit a11a10760ce92ee373e04a5445234521a27874cc +Author: Marnen Laibow-Koser +Date: Mon Apr 28 17:55:55 2008 -0400 + + Committing so we can bisect. + +commit df94214b1d132b02e3dd5b166d1c7c5cd5d50a21 +Author: Drew Olson +Date: Mon Apr 28 19:21:07 2008 -0700 + + Added spec for Array#remove_outer_arrays + +commit ec4ece9c06b42c257b4ffce2cf319f0ad23f65e8 +Author: Drew Olson +Date: Sun Apr 27 20:15:47 2008 -0500 + + Added more edge cases for recursive arrays to spec for File#join + * an empty array containing an empty array which contains a recursive array should return + '[...]' when File#join is called on it. + +commit 698a5d291cf63e56e9a3508a8850c77fa2c23430 +Author: Marnen Laibow-Koser +Date: Mon Apr 28 16:17:05 2008 -0400 + + Implement BigDecimal#=== as alias of #eql?. + +commit 18f515e735eecc519be55a6e3253db7135a137ad +Author: Marnen Laibow-Koser +Date: Mon Apr 28 16:09:43 2008 -0400 + + Implement BigDecimal#sub. + +commit b331faa567dc1d98163c6447897221877cf756eb +Author: Marnen Laibow-Koser +Date: Mon Apr 28 16:04:06 2008 -0400 + + Implement BigDecimal#add. + +commit f3f94c9b53045ddde335981897e2f6087dab7ef2 +Author: Charles Comstock +Date: Mon Apr 28 12:01:41 2008 -0500 + + hack to fix DRb.start_service spec to at least test start_service + +commit 4c8d6d90c69615386e26c71633e242f4e1f19342 +Author: Charles Comstock +Date: Mon Apr 28 11:56:47 2008 -0500 + + spec for DRb.stop_service to see if it clears the socket correctly + +commit 03cb539f42f0b558fa29911c1dfc71ec5f2b183f +Author: Charles Comstock +Date: Mon Apr 28 11:20:17 2008 -0500 + + Revert "Revert "Made DRb spec depend partially on PID so multiple runs don't clash."" + + Apparently this is a supposed fix for concurrent spec runs, not for the spec failure + + This reverts commit 08695d9a6940ab74f6eb8965e449a417002a42a6. + +commit 2172e2ac20b69a97c2ad66551b3620a43bfda700 +Author: Marnen Laibow-Koser +Date: Mon Apr 28 02:14:18 2008 -0400 + + Make BigDecimal#exponent return Bignums as necessary, not just Fixnums. + +commit dc93d06163e80cdf89a67532654a850828119287 +Author: Marnen Laibow-Koser +Date: Mon Apr 28 01:23:19 2008 -0400 + + Correct implementation of BigDecimal#+ and #-. There's still a lot of repetition to be factored out, but this algorithm is more correct than the last try. + +commit 1da58bb7f0afbba4f8412e06983304dc7d887ac9 +Author: Luis Lavena +Date: Thu Apr 24 16:37:59 2008 -0300 + + Corrected small typo on File#join specs under Windows. + +commit b287619579ad11535722a2374b6f849d88fe9931 +Author: Drew Olson +Date: Thu Apr 24 14:24:10 2008 -0700 + + Spec for File#join now describes correct behavior for arrays with recursive sub-arrays. + +commit 5830380895c0bec16c6af39d0f29d8d70268028d +Author: Charles Comstock +Date: Sun Apr 27 14:53:47 2008 -0500 + + DRb.start_service spec fails because of a timing bug in DRb + + See http://jira.codehaus.org/browse/JRUBY-2347 + +commit 08695d9a6940ab74f6eb8965e449a417002a42a6 +Author: Charles Comstock +Date: Sun Apr 27 14:51:16 2008 -0500 + + Revert "Made DRb spec depend partially on PID so multiple runs don't clash." + + The spec is designed for sane behavior, if Rubinius or the + implementation of DRb is causing problems then they should be + fixed, not the spec in this case. Fixing the spec will only + hide the bug. + + See http://jira.codehaus.org/browse/JRUBY-2347 for more commentary on the problem. + + This reverts commit f89bd8c6c425c9d9bcc3e589b8d3b05ce3ccbced. + +commit 94ba0884c8e7f398b6fe8d6736834f62f6a49815 +Author: Vladimir Sizikov +Date: Sun Apr 27 21:23:47 2008 +0200 + + More checks for BigDecimal#abs specs. + +commit 80932d25ca95e2e8c803d244a7636e3004525ade +Author: Vladimir Sizikov +Date: Sun Apr 27 21:10:26 2008 +0200 + + More test cases for BigDecimal#finite? specs. + +commit 4b541ed23ccac65f6f4b2ef8aad56e9aa7a69e12 +Author: Vladimir Sizikov +Date: Sun Apr 27 21:04:08 2008 +0200 + + Added testcase for BigDecimal#infinite? for NaN. + +commit 4a1f39426fc60ae7c2ed0470259fa0752a46d030 +Author: Adam Wiggins +Date: Sat Apr 26 22:57:09 2008 -0700 + + IO#write returns 0 when writing a blank string, to match behavior of MRI + + Signed-off-by: Dirkjan Bussink + +commit 56c0088f9b075769933c8c87e3c2d256cff3a3e8 +Author: Marnen Laibow-Koser +Date: Sun Apr 27 00:28:47 2008 -0400 + + Typo. + +commit c11410654b9046cdb58dba1d116f58ce74f4c263 +Author: Marnen Laibow-Koser +Date: Sun Apr 27 00:24:32 2008 -0400 + + Finish implementing #@- and #infinite?. Update spectags, of course + +commit dc9f427ecb9d55559d800af70f9c1a3f2f2123b5 +Author: Marnen Laibow-Koser +Date: Sun Apr 27 00:07:31 2008 -0400 + + Amplify a comment. + +commit b9776b953ae67f2088e44b640145af464a1cf942 +Author: Marnen Laibow-Koser +Date: Sun Apr 27 00:02:48 2008 -0400 + + Get BigDecimal#+ working. I hate this algorithm, but it works without running out of memory. + * Update spec tags. + +commit b87ff5c22891f19ad0b956e7e02cc3a3d1adcc93 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 14:27:00 2008 -0400 + + Implement BigDecimal#coerce. + * Update spectags. + * Rewrite one spec so it doesn't depend on BigDecimal#-, which is not yet implemented. + +commit f89bd8c6c425c9d9bcc3e589b8d3b05ce3ccbced +Author: Brian Ford +Date: Sat Apr 26 15:52:49 2008 -0700 + + Made DRb spec depend partially on PID so multiple runs don't clash. + +commit 3c49a1d16f20726c4ee2d7eb5f5c671537aa59d5 +Author: Brian Ford +Date: Sat Apr 26 15:13:47 2008 -0700 + + Added wordsize guard for BigDecimal#exponent spec. + +commit 3aac5f6d64f4cbbca70ecf01b7ed9be596fa5b76 +Author: Brian Ford +Date: Sat Apr 26 15:09:40 2008 -0700 + + Updated spec_helper and renamed CaptureOutput to IOStub. + +commit 94322a6a95770a030d28925cc7213a38c5687ea1 +Author: Vladimir Sizikov +Date: Sat Apr 26 23:16:59 2008 +0200 + + A bit more test cases for BigDecimal#-@. + +commit 9919c5e3be59562532c967b479c959cf6270046e +Author: Vladimir Sizikov +Date: Sat Apr 26 21:01:44 2008 +0200 + + New specs for BigDecimal#uminus. + +commit c3e74531f1ca1e70671f529671c0fa474968dc87 +Author: Marius Nuennerich +Date: Sat Apr 26 13:08:04 2008 +0200 + + FreeBSD seems to work like the rest, not darwin + + Tested on FreeBSD/i386 7-STABLE + +commit c06a091b285f388f09b11037975921662759eea2 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 12:14:33 2008 -0400 + + Implement BigDecimal#exponent, update spectags. Looks like parts of #** have accidentally stopped failing too. :) + +commit e5b753b7e659b29f5ed4aa57018f922111b238f5 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 11:53:55 2008 -0400 + + Specify return type of BigDecimal#ceil as BigDecimal, as per library documentation. + +commit 0ca3b9ceb6ef5ca1898250b89f75c0194b5da481 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 11:50:24 2008 -0400 + + Fix BigDecimal#inspect output, update spectags. + +commit ca99aa062afe9106ec614e2d8969d3491803c9a2 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 11:48:00 2008 -0400 + + Specify return type of BigDecimal#ceil as BigDecimal, as per library documentation. + +commit 587a5cdbbfa4cccdbfe98339ca999f1d63bd66cf +Author: Vladimir Sizikov +Date: Sat Apr 26 15:02:33 2008 +0200 + + Corrected one Array#hash test case. + + Now Array#hash pass MRI 1.8.6, 1.8.7, 1.9 and JRuby. + +commit f86bdb98b8b9f5ea878c5d142f3a694e5278db77 +Author: Vladimir Sizikov +Date: Sat Apr 26 14:19:14 2008 +0200 + + Quarantined couple of specs that fail on *ALL* implmenetations. + + Probably, we need a better way to do that, but quarantine + is a quick and simple way, easily detectable later on. + +commit 7ca928211180c66b9879afbc382c376a7649e1b0 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 00:51:39 2008 -0400 + + Implement BigDecimal#to_f, update tags. Will this need more work? + +commit 69dec41f6b5b532c5de7f46e97f97c9e102305c7 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 00:44:40 2008 -0400 + + Implement BigDecimal#truncate. + * Update spec tags. + * Reorganize variables slightly to remove duplication. + +commit c823e62c3a6776b62f65c34b16bdca5748d1add9 +Author: Marnen Laibow-Koser +Date: Sat Apr 26 00:36:43 2008 -0400 + + Implement BigDecimal#truncate. + * Update spec tags. + * Reorganize variables slightly to remove duplication. + +commit 3f4e5dc78de5bf3e81ae1ce7a0d14852a32aeade +Author: Marnen Laibow-Koser +Date: Sat Apr 26 00:16:38 2008 -0400 + + Rewrite spec description to bring it in line with what the spec actually does. :) + +commit 15d87e8a983d08d99fc3ec6bfbb7f36ed0cd4c4e +Author: Marnen Laibow-Koser +Date: Fri Apr 25 18:09:33 2008 -0400 + + Implement BigDecimal#to_i, update spec tags. + +commit 59873b144ea836e2f9bbef7d5186a1287155e76a +Author: Wilson Bilkovich +Date: Fri Apr 25 20:46:17 2008 -0400 + + Specs for autoload path normalization + +commit 71fe2d45d147fe2c41937ae5ef6dbb8814f491c4 +Author: Wilson Bilkovich +Date: Fri Apr 25 17:05:17 2008 -0400 + + Use a separate class in Singleton 'new' specs to avoid contamination + +commit 2dc8f9eb9c6db014bd6cc132d987fdb4612816f8 +Author: Wilson Bilkovich +Date: Fri Apr 25 16:45:28 2008 -0400 + + Handle more nightmare Autoload edge cases by hooking into Kernel#require + +commit 5c1a375a15adbe20a9bf3d1b95e1f2d30feaa90e +Author: Michael Fellinger +Date: Sat Apr 26 04:26:52 2008 +0900 + + Spec for Module#autoload when the load path has already been required + + Signed-off-by: Wilson Bilkovich + +commit ee47a0cc0da787599479fc8dd085b7481b591176 +Author: Charles Oliver Nutter +Date: Fri Apr 25 15:41:19 2008 -0500 + + Enabled another $_ spec and added a proc dispatch scoping test to $~ and $_ + +commit eabc4609758dc99727c77493c58f187782ea957f +Author: Charles Oliver Nutter +Date: Fri Apr 25 15:32:03 2008 -0500 + + Added some basic specs for $_: implicit assign, explicit assign, scoping + +commit 61194dec429a9f288791156639f058e45a4e72e9 +Author: Charles Oliver Nutter +Date: Fri Apr 25 14:49:53 2008 -0500 + + Add some specs for $~ scoping and assignment. + +commit 4c5cec4f6e10864c68b140e71cc2559e7a7d636b +Author: Brian Ford +Date: Thu Apr 24 18:28:08 2008 -0700 + + Added incomplete tags for CSV spec stubs. + +commit 6e231caef62e678413e86317881aaab200d0802e +Author: Brian Ford +Date: Thu Apr 24 18:15:06 2008 -0700 + + Reprocessed library CSV specs with new mkspec. + +commit 4cdc61a76cce73b52f05f53f820838cc7e3c2823 +Author: Michael Fellinger +Date: Fri Apr 25 08:46:32 2008 +0900 + + Updating specs for the module #included calling #extend issue. + + Signed-off-by: Brian Ford + +commit 40e775bf036aa59e69268708f8c78b8a56e0f9ce +Author: Brian Ford +Date: Thu Apr 24 17:10:55 2008 -0700 + + Moved #bignum_value helper to MSpec. + +commit 9b52edbb14ff2fc18faa429daf4ceaff5b87db11 +Author: Marnen Laibow-Koser +Date: Thu Apr 24 14:37:53 2008 -0400 + + Implement BigDecimal#fix, make #frac trap for a few common cases without running out of memory for big numbers. + + * Update spec tags. + +commit 6a604c0a9863073cfd7540ff755e7ca035a7dff5 +Author: Marius Nuennerich +Date: Thu Apr 24 19:32:14 2008 +0200 + + Don't run Process.setpriority spec on FreeBSD + +commit 0ab639af500d947c5b5feb1d8f00f5fbc97a0edc +Author: Vladimir Sizikov +Date: Thu Apr 24 13:37:23 2008 +0200 + + Adjusted IO specs to supply blocks for each-like methods. + + See [ruby-core:16557] for more details. + +commit a7b603a9ce6bfb570785e803bdb89ae36bb6253d +Author: Vladimir Sizikov +Date: Thu Apr 24 13:14:40 2008 +0200 + + Fixed IO, Process, Regexp specs ('should' was missing). + +commit 26de6c05c050d0dbcb073c407abda47f964bfd29 +Author: Vladimir Sizikov +Date: Thu Apr 24 12:50:46 2008 +0200 + + Fixed File specs ('should' is misspelled) + +commit 99a2b23d8fb42cb377cb3fb9ab2569c555aec8bf +Author: Vladimir Sizikov +Date: Thu Apr 24 12:47:57 2008 +0200 + + Fixed Array specs ('should' was missing). + +commit 67b301a03fd6f7f0fa38ce106ab05825f2cbb15c +Author: Vladimir Sizikov +Date: Thu Apr 24 12:43:39 2008 +0200 + + Fixed specs ('should' was missing), some new test cases for BigDecimal. + +commit aecbea57de7ee1b50bd4b06871dd08e762a6ccb8 +Author: Vladimir Sizikov +Date: Thu Apr 24 12:35:54 2008 +0200 + + More test cases for BigDecimal#nan? + +commit 7aaf8fa137b8961ca122eb92e7447936ad7a44cc +Author: Vladimir Sizikov +Date: Thu Apr 24 12:26:20 2008 +0200 + + More test cases for BigDecimal#zero? + +commit 58ecee694f191aa05e7867544cf8d63129558447 +Author: Vladimir Sizikov +Date: Thu Apr 24 12:19:11 2008 +0200 + + A bit more test cases for BigDecimas#-. + +commit e946dd03d590e29a1d344e7579d5ff047df4a76b +Author: Vladimir Sizikov +Date: Thu Apr 24 12:17:16 2008 +0200 + + New and updated specs for BigDecimal#-. + +commit 01d82db424b4e447b98e5f2eb3e162b991dece8a +Author: Brian Ford +Date: Thu Apr 24 01:25:20 2008 -0700 + + Tag for new private setter method spec. + +commit c0ee2e133a4e5fc179b96329ffd3934dd9263c2b +Merge: 374ab81... e9826b9... +Author: Tony Arcieri +Date: Thu Apr 24 02:19:00 2008 -0600 + + Merge branch 'master' of git@git.rubini.us:code + +commit 374ab81e2c01ea5ac48cda2004ae92a989d7f3d7 +Author: Tony Arcieri +Date: Thu Apr 24 02:18:26 2008 -0600 + + Specs for calling a private setter method on self + + * Not presently working under rbx, works under MRI + * I don't entirely know the process for this, but this is expected to break + +commit 39505393f330b5f622788f1d98ea8ff3781499c7 +Author: Luis Lavena +Date: Thu Apr 24 04:04:32 2008 -0300 + + Fixes Dir fixtures and specs for Windows. + + Usage of special characters *, ?, | and : is not allowed under Windows + * and ? represent wildcards, | is pipe tunelling and : is drive letter + separator. + + Files or Directories cannot contain slashes (\/), wildcards, double- + quotes, pipe tunelling or stream redirectors (<>). + +commit 2ecc076e488ed1a519fc5b6876c68a3d91d55c87 +Author: Brian Ford +Date: Wed Apr 23 22:55:44 2008 -0700 + + Update tags for newly passing File.join specs. + +commit ff3756e179920b84d5a55fc7bbc2688706df044f +Author: Wilson Bilkovich +Date: Thu Apr 24 01:09:17 2008 -0400 + + Add specs for nested method definitions and other complex scenarios + +commit 24785f7c28cde09ce0400e5d80f832ae11cddefa +Author: Wilson Bilkovich +Date: Wed Apr 23 20:56:55 2008 -0400 + + Spec for using ||= to initialize a class variable + +commit 98b0c44057cb827107cae0f0174b5e81ac2064fd +Author: Wilson Bilkovich +Date: Wed Apr 23 19:49:21 2008 -0400 + + Rewrite descriptions of language/def specs + +commit 598c287cc36179644a1bbf2a303a56fc85bb1b12 +Author: Brian Ford +Date: Wed Apr 23 12:39:04 2008 -0700 + + Replaced use of :mswin with :windows in platform_is[_not] guards. + +commit 01fe417f27ad43495327a522ece2f02769064df7 +Author: Vladimir Sizikov +Date: Wed Apr 23 21:30:34 2008 +0200 + + Added excludes for BigDecimal#div specs. + +commit 46f022d49c394b027491295e7fd5cb305af33404 +Author: Vladimir Sizikov +Date: Wed Apr 23 21:27:42 2008 +0200 + + More specs for BigDecimal#div + +commit 72433091c6a845c5f550b27111748e29fb5eac09 +Author: Brian Ford +Date: Wed Apr 23 10:56:57 2008 -0700 + + Added #tmp helper to MSpec for returning a temp file name. + +commit f4e975e5255fb36bb8e9be7d310850135ce3515f +Author: Marnen Laibow-Koser +Date: Wed Apr 23 10:54:42 2008 -0400 + + Implement BigDecimal#frac, update spec tags. + +commit b60deba2368a1212d6acd3e49481ba9495de7f2f +Author: Marnen Laibow-Koser +Date: Wed Apr 23 10:49:39 2008 -0400 + + Correct a spec error. + +commit e19cf9401c029f90e117b1c17083c928b0d1c9ca +Author: Marnen Laibow-Koser +Date: Wed Apr 23 10:24:50 2008 -0400 + + Implement BigDecimal#-@, update spec tags. + +commit c3fc05389c75aca3150038814b324266501fdb8f +Author: Vladimir Sizikov +Date: Wed Apr 23 16:12:24 2008 +0200 + + A bit more test cases for BigDecimal#sqrt. + +commit b2a220f86887bfe6030a34bc8cd1b748c88cc2b8 +Author: Marnen Laibow-Koser +Date: Wed Apr 23 01:43:16 2008 -0400 + + Get BigDecimal#to_s working according to spec. + + * Implement #to_s. + * Update spec tags. + +commit 82638601be12e410413047779f01840d6d0db3d8 +Author: Adam Gardiner +Date: Wed Apr 23 09:56:48 2008 +1000 + + Refactor Debugger to remove dependencies on Debugger::Interface + + Also: + - Add List#inspect to show number of items in list + - Fix decode output to show original instructions in place of + yield_debugger + - Improve regex used to match method names to handle more + operators + +commit 41c64f2825d347fbe2ef9edc33dd8f1e84773251 +Author: Wilson Bilkovich +Date: Tue Apr 22 18:12:06 2008 -0400 + + Spec and implementation for NilClass#dup + +commit d3e313ed38a847e29225ba814a956d0929ea6460 +Author: Vladimir Sizikov +Date: Tue Apr 22 22:03:47 2008 +0200 + + New and updated specs for Bigdecimal's #abs and #sqrt. + +commit 2013e106181879b886f2e1cb78e81f52cd284666 +Author: Wilson Bilkovich +Date: Tue Apr 22 15:52:10 2008 -0400 + + Re-implement Module#autoload and autoload?. Now passing all autoload specs. + +commit 9156271e2b12138e2b2b712a76f0110f20a757b7 +Author: Wilson Bilkovich +Date: Tue Apr 22 15:34:43 2008 -0400 + + Add (failing) spec for toplevel autoloaded constant access + +commit 8eb5451f88a37dc247e42913c1d72d072a9b02ef +Author: Vladimir Sizikov +Date: Tue Apr 22 19:06:00 2008 +0200 + + One more test case, for BigDecimal#sqrt with nil. + +commit e7894fb78cf92b53e9bdc6dcf023d8dd2d66b2ed +Author: Vladimir Sizikov +Date: Tue Apr 22 18:52:24 2008 +0200 + + More detailed specs for BigDecimal#sqrt and fixes for old ones. + +commit 527a4b663c487cd9222ee2e6917e330ff9a130a1 +Author: Wilson Bilkovich +Date: Tue Apr 22 12:38:53 2008 -0400 + + Rename ambiguously-worded autoload spec + +commit 3e6f16c41569dbba291bc3cececf137fc8952ee2 +Author: Marnen Laibow-Koser +Date: Tue Apr 22 12:31:17 2008 -0400 + + Change to a significand-and-exponent implementation. + + * Update spec tags. + +commit f1b2bf51042ca563ca74a9cf83db0e46a1bfabce +Author: Marnen Laibow-Koser +Date: Tue Apr 22 09:54:53 2008 -0400 + + Fix BigDecimal#zero, update spec tags. Also make #precs deal correctly with lowercase exponents. + +commit d0171de114e777f07a3e62972663475dd7747b05 +Author: Marnen Laibow-Koser +Date: Tue Apr 22 09:24:06 2008 -0400 + + Implement BigDecimal#precs. + + * Get #precs working. This will be less tortured once I implement a significand-and-exponent format. + * Update spec tags. + +commit e1fc7c6dc4c02c1763947c34d05f894661a84525 +Author: Marnen Laibow-Koser +Date: Tue Apr 22 01:30:10 2008 -0400 + + Continue implementing bits of #inspect and updating spec tags. + +commit e4371f120c9c5c3c88a26d5f24f0d3ab888c954f +Author: Marnen Laibow-Koser +Date: Tue Apr 22 01:24:14 2008 -0400 + + Implement BigDecimal#==/eql? and the beginnings of #inspect. + + * Find a way of implementing the equality test that satisfies the specs. + * Don't be so baroque in parsing strings in constructor. + * Update spec tags. + * Fix regression in abs_spec. + * Start implementing #inspect. Not really ready for prime time yet. + +commit 0494c1c35582381345194c76f7384eb9044797fc +Author: Marnen Laibow-Koser +Date: Tue Apr 22 00:41:03 2008 -0400 + + Start implementing BigDecimal#sign and #zero?. + + * Clean up specs for #sign. + * Write some initial code to get these working. Not all there yet. + +commit 3c071b5f921898d87437803a500535b639d465ef +Author: Marnen Laibow-Koser +Date: Mon Apr 21 23:43:10 2008 -0400 + + Get BigDecimal#abs working. + + * Implement the function. + * Improve the spec. + +commit 054582f3b89d757f033cd5f09cbf90fa08ad81d6 +Author: MenTaLguY +Date: Mon Apr 21 22:32:42 2008 -0400 + + fix linked actors spec (sort of) + +commit d7a7d0c4d0d83d7e69216c96a249c4091fe75323 +Author: MenTaLguY +Date: Mon Apr 21 22:28:29 2008 -0400 + + fix up registration spec + +commit 645784c3d39f776f583874e7c9244ff3de64cfe7 +Author: Marnen Laibow-Koser +Date: Mon Apr 21 19:56:51 2008 -0400 + + Update tags on failing specs. + +commit 960faf5382d90db376ff14bb836463f1860a4b62 +Merge: 2e2150f... 046ba62... +Author: Marnen Laibow-Koser +Date: Mon Apr 21 18:46:45 2008 -0400 + + Merge branch 'master' of git@git.rubini.us:code + +commit 046ba622836321f487f241c145a3bdf0968f0a67 +Author: Wilson Bilkovich +Date: Mon Apr 21 18:44:50 2008 -0400 + + Specs for failing Module#autoload case (replicates a scenario from Merb) + +commit 18a2a26fa511d4943a724e27ce09e5855a257e90 +Merge: 1f5f4b5... 991c6e6... +Author: Marnen Laibow-Koser +Date: Mon Apr 21 14:15:07 2008 -0400 + + Merge branch 'master' of git://git.rubini.us/code + + Conflicts: + + lib/bigdecimal.rb + spec/ruby/1.8/library/matrix/diagonal_spec.rb + spec/ruby/1.8/library/matrix/element_reference_spec.rb + spec/ruby/1.8/library/matrix/shared/identity.rb + spec/ruby/1.8/library/matrix/shared/transpose.rb + +commit edd397c82a924e406eabbcd7e84243d94f8e8067 +Author: Federico Builes +Date: Sun Apr 20 18:11:23 2008 -0500 + + Adds MinGW to the IO#popen spec guard + +commit 10df9f89189637b2c5a54b01a88eca6c9fbb4601 +Author: Adam Wiggins +Date: Sun Apr 20 14:53:41 2008 -0700 + + IO.popen specs for reading and writing to pipes + +commit 3f70eceb3b9415a14f602c5b96121a459dca1e67 +Author: Brian Ford +Date: Sat Apr 19 22:32:28 2008 -0700 + + Fix silly typo in Numeric#quo specs. + +commit 6101a4992ddc15c0140f4d7702cf88d2d3a2ac53 +Author: Brian Ford +Date: Sat Apr 19 22:23:14 2008 -0700 + + Guard affected specs with conflicts_with :Rational. + +commit 354445f4d20ec66f207d65d1ccceb681bba7fff0 +Author: Federico Builes +Date: Sat Apr 19 14:23:14 2008 -0500 + + Clarifying some of the Matrix specs + + * Fixes two errors introduced by 28700c5cf7 + +commit 2f5ca541fc08f0c033bc6541c72962228ea607de +Author: Eero Saynatkari +Date: Sat Apr 19 15:07:49 2008 -0400 + + Compiler specs' TestGenerator relies on broken #=== semantics, comply. + +commit 37cc9d4d6eb3442814ecc51845f025f464da64f7 +Author: Eero Saynatkari +Date: Sat Apr 19 15:05:30 2008 -0400 + + Specs for default #=== and its relationship with #== and #equal? + + * Rubinius deviates to not check object id directly. + +commit 28700c5cf7630be59877122e6470c42622b7365a +Author: Federico Builes +Date: Sat Apr 19 13:53:33 2008 -0500 + + Additional specs for Matrix + + * Some of the constructors in Matrix keep referencing the original arguments after creation, these specs cover those cases. + +commit 12b0bc93e5a6b328ad0968c03c47af71f671aae2 +Author: Federico Builes +Date: Sat Apr 19 13:06:04 2008 -0500 + + Replace object_id for equal? in Matrix specs + +commit ae377f0e56b8f31356935b3ac0800f561b2d1b2c +Author: Dirkjan Bussink +Date: Sat Apr 19 14:49:34 2008 +0200 + + Fix File::Stat#uid specs + +commit 2e01a86a3977fe87f4f0734e50598b41f66f29d7 +Author: MenTaLguY +Date: Sat Apr 19 03:24:32 2008 -0400 + + Gutted and reworked Actor, following Erlang more closely. + +commit de40303e17e2de1e7980564b43ee162c5080afa6 +Author: Federico Builes +Date: Sat Apr 19 01:05:43 2008 -0500 + + Fixes Matrix#clone specs for MRI + + * Makes sure the values (not the references) of the original rows are copied. + +commit 2b3a44158ae93ab5883da22e5f36df92485f3ad4 +Author: Federico Builes +Date: Fri Apr 18 22:47:15 2008 -0500 + + Fixes a few things inside the Matrix specs. + + * Removes some of the "needs to be reviewed for completeness" messages. + * Changes some of the descriptions + +commit 3be265a93a75b6a0267b1770f8cad671c4244671 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:26:29 2008 -0400 + + Matrix.unit, one more alias for .identity. + + Signed-off-by: Federico Builes + +commit 57aa8ba9a1dbdf62e9cf644bbde4603b841ffc76 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:25:55 2008 -0400 + + Name spec correctly. + + Signed-off-by: Federico Builes + +commit 3390dc4c6725d996eeb0c2e4ec73949bc0be2290 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:23:54 2008 -0400 + + Specs for Matrix.scalar and .identity/I. + + Signed-off-by: Federico Builes + +commit 958ca1faa1dc60ce591b4b2f768f22ac7f6cb56f +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:22:09 2008 -0400 + + Move the "needs to be reviewed" indicator to the right place. + + Signed-off-by: Federico Builes + +commit bf3eab630654eaaca9256850d258343e3024989e +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:03:45 2008 -0400 + + Use size functions instead of constants. + + Signed-off-by: Federico Builes + +commit 3981c931e7f4fde730d51614d40e44b9209347f9 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 11:58:22 2008 -0400 + + Specs for Matrix#clone and #transpose (alias #t). + + Signed-off-by: Federico Builes + +commit 72e1ea8900a638c796de9e715c5dffcf4ac90546 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:27:06 2008 -0400 + + Basic specs for Matrix.zero. + + Signed-off-by: Federico Builes + +commit f5d294ad941c477060e9b5d2329790db7e1e5700 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:20:27 2008 -0400 + + Move before block to a clearer place. + + Signed-off-by: Federico Builes + +commit b6bc5b224ade56ab96f3585b6b1c25e6dd5e1ad5 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:19:27 2008 -0400 + + Write specs for Matrix.diagonal. + + Signed-off-by: Federico Builes + +commit b4d056baa33a2181ab64c065ad1eb4adebcfaddf +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:11:11 2008 -0400 + + Some initial specs for Matrix#[] and Matrix.[]. + + These are in the same file because of coding conventions, but they probably should not be since .[] is a constructor and has very little in common conceptually with #[], which is a subscript operator. + + Signed-off-by: Federico Builes + +commit 5476d836577c0fbdbda097762862cf153ffb5e07 +Author: Brian Ford +Date: Fri Apr 18 18:35:34 2008 -0700 + + Some method profiles of data provided by John Lam. + + Run these as follows: + + bin/mspec -w rails.yaml spec/ruby + + We'll be adding our own trace script, but for now, these + are snapshots of methods used by Rails loading a simple + "hello world" controller. The rails.yaml file is core + methods. The core.yaml file is generated by NameMap from + mspec/bin/name_map.rb. + +commit 24c71675cc63c86832ef8bc55d2f0167dff53073 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 17:24:08 2008 -0400 + + First stab at BigDecimal. + + * A skeleton of a somewhat naïve implementation of BigDecimal. + * Updated spec tags. + +commit 1f5f4b59400b8b11df83b274efc8ce98186220ef +Merge: 9d21b0e... 968a0ec... +Author: Marnen Laibow-Koser +Date: Fri Apr 18 17:28:02 2008 -0400 + + Merge branch 'bigdecimal' + +commit 9d21b0e890a9394658689af2bdee7e449cd2200b +Merge: c3f3507... 1a08506... +Author: Marnen Laibow-Koser +Date: Fri Apr 18 17:25:09 2008 -0400 + + Merge branch 'master' of git://git.rubini.us/code + +commit 968a0ecda8477b33ceab2e7d0c7e7d084a105bdb +Author: Marnen Laibow-Koser +Date: Fri Apr 18 17:24:08 2008 -0400 + + First stab at BigDecimal. + + * A skeleton of a somewhat naïve implementation of BigDecimal. + * Updated spec tags. + +commit 1f410d918a59b9b49e87a407cc8fba4bbf342a79 +Author: Dirkjan Bussink +Date: Fri Apr 18 22:34:53 2008 +0200 + + Fix a bunch of specs and minor issues in File::Stat + + Specs for File::Stat#<=>, File::Stat#ino, File::Stat#inspect, + File::Stat#mode, File#Stat.initialize and some minor bugfixes + such as the fact that File::Stat needs to include Comparable + (like MRI). + +commit d6f2c6995941762878f4b777a39b0c23ea654605 +Author: Dirkjan Bussink +Date: Thu Apr 17 22:30:36 2008 +0200 + + Remove specs for non-existent File::Stat#initialize_copy + +commit c3f350716a35cb869b3ea0289c0e404d07b8819f +Merge: 810afff... b861102... +Author: Marnen Laibow-Koser +Date: Fri Apr 18 16:18:34 2008 -0400 + + Merge branch 'master' of git://git.rubini.us/code + +commit 72101783ec6e66a4f9ac3f9c90f7e8f5b67058ec +Author: Ryan Davis +Date: Tue Apr 8 17:05:26 2008 -0700 + + Reworked masgn specs to evaluate L2R and assign L2R. excluded. + +commit 4e4bec628b21938617bdfa5a2ef17aedf02c112c +Author: Ryan Davis +Date: Tue Apr 8 15:17:00 2008 -0700 + + trailing whitespace is killing me... evan\! fix your editor\! + +commit 810afffa2e549048947c07b30d77be255db42d73 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:26:29 2008 -0400 + + Matrix.unit, one more alias for .identity. + +commit 2c84f77535d677a42bee93759c77f79c2cdd4d93 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:25:55 2008 -0400 + + Name spec correctly. + +commit 762f5ee0f7ba4234847c695c92e3ed27dd05e134 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:23:54 2008 -0400 + + Specs for Matrix.scalar and .identity/I. + +commit b68295e0046a2eb1fb911ea891d6e0a29174ea30 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:22:09 2008 -0400 + + Move the "needs to be reviewed" indicator to the right place. + +commit 4b6e1097feafe2247e59d6004a36bb0987734138 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 12:03:45 2008 -0400 + + Use size functions instead of constants. + +commit 2086f0c1f1f899f2e41307a5434a5bb6446e20a2 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 11:58:22 2008 -0400 + + Specs for Matrix#clone and #transpose (alias #t). + +commit 2939c55b2e9f38b5115b98429de97bc4fff6f165 +Merge: a47f2b8... 42d3212... +Author: Marnen Laibow-Koser +Date: Fri Apr 18 02:09:07 2008 -0400 + + Merge branch 'master' of git://github.com/evanphx/rubinius + +commit a47f2b852ca309a68b687157a6cd973716328887 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:27:06 2008 -0400 + + Basic specs for Matrix.zero. + +commit aa3b2eeef70cb8967ef6c92ee24a226c2d1202c1 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:20:27 2008 -0400 + + Move before block to a clearer place. + +commit ca6ac1e59ddb268b388975a2fb5b11e6026e65c8 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:19:27 2008 -0400 + + Write specs for Matrix.diagonal. + +commit b24216d8b0ecfba6888f909415e2523eaed2aeb2 +Author: Marnen Laibow-Koser +Date: Fri Apr 18 01:11:11 2008 -0400 + + Some initial specs for Matrix#[] and Matrix.[]. + + These are in the same file because of coding conventions, but they probably should not be since .[] is a constructor and has very little in common conceptually with #[], which is a subscript operator. + +commit 9313f29ed952f604e0d124ced38ee930b5780b27 +Author: Marnen Laibow-Koser +Date: Thu Apr 17 22:35:43 2008 -0400 + + New spec tags for Complex#%. + +commit 08f316de96c94b7d4865d77873327deddeabb664 +Author: Marnen Laibow-Koser +Date: Thu Apr 17 22:32:08 2008 -0400 + + More specs for Complex. + + * Specs for <=>, conj/conjugate, to_s. + +commit 973c304cc16fa6b78dba31de11b151da2daae762 +Author: Marnen Laibow-Koser +Date: Thu Apr 17 22:09:20 2008 -0400 + + More specs for Complex + * Complex#abs, abs2, angle, arg, and %. Not sure that % is correctly specified. + +commit e32b26694277065fe28f138dca837b8c0509c735 +Author: Marnen Laibow-Koser +Date: Thu Apr 17 21:17:26 2008 -0400 + + More specs for Complex. + + * Write specs for * and /. + * Rewrite + and - to use alternate constructor syntax. + + Signed-off-by: Marnen Laibow-Koser + +commit 0cbf88a6c61e477f4b9a7758a9fab1258efbf30f +Author: Marnen Laibow-Koser +Date: Thu Apr 17 20:46:04 2008 -0400 + + Write some specs for Complex. + + * Basic specs for Complex.new, Complex.new!, Complex#+, and Complex#-. + + Signed-off-by: Marnen Laibow-Koser + +commit 71909e78b8d77f7e48d306e30f51fbc21b5fbefb +Author: Brian Ford +Date: Thu Apr 17 15:26:53 2008 -0700 + + Reorganize and fix Matrix specs. + +commit 5a9325457696dfba3c410c0adcbdec706ecda3bf +Author: Brian Ford +Date: Thu Apr 17 12:49:45 2008 -0700 + + Added spec templates and incomplete tags for CGI. + +commit d62de6b4096a9b3bd3fda197b70d6e603596e865 +Author: Vladimir Sizikov +Date: Thu Apr 17 21:12:08 2008 +0200 + + More detailed speecs for BigDecimal#new + +commit ebd6fb8f879f94ff51b74cb4e76080fad7b66cb5 +Author: Vladimir Sizikov +Date: Thu Apr 17 19:55:31 2008 +0200 + + More detailed specs for BigDecimal's <, <=, >, >=, <=>. + +commit 8caef40cbe873dc2825bc0ba1e66f983b8219cca +Author: MenTaLguY +Date: Thu Apr 17 01:16:25 2008 -0400 + + add tag object argument to send_in_* + +commit 0596b1aca45a85de5f3d727632585da924fd3eb0 +Author: Matthias Reitinger +Date: Sat Apr 12 10:11:13 2008 +0200 + + New specs for BigDecimal.new. + + Signed-off-by: Marius Nuennerich + +commit 5c176e50fe962de1095a75221b4d63e75acc505f +Author: Benjamin Stiglitz +Date: Wed Apr 16 11:32:18 2008 -0700 + + Cleaned up Numeric#div spec + + The spec names are no longer quite as atrocious; the spec output is now fairly + readable. The different Integer-Float quotient permutations are now correctly + specified as well. + + Signed-off-by: Brian Ford + +commit e1406b19c51bfca5f6936d143087043316c68c13 +Author: Eero Saynatkari +Date: Tue Apr 15 20:07:48 2008 -0400 + + Specs for Kernel#p behaviour. + + * Args vs. no args. + * Record separator is not taken into account. + +commit 30c717e1736b65a852df501f71e320599fc17786 +Author: Dirkjan Bussink +Date: Tue Apr 15 21:32:13 2008 +0200 + + Fix typo in File.grpowned? spec + +commit 1bc17a0b4c8f19b84ffdd0b17ec24243a1df6092 +Author: Dirkjan Bussink +Date: Tue Apr 15 21:31:02 2008 +0200 + + Fix File.grpowned? and it's spec + +commit f49cf4d0319b5772ede7bcddd763c691d5253b18 +Author: Dirkjan Bussink +Date: Tue Apr 15 20:44:18 2008 +0200 + + Update tags for implemented File.grpowned? specs + +commit ea19fb07cb7b789165aec5da0f571345b96f1f0f +Author: Dirkjan Bussink +Date: Tue Apr 15 20:41:02 2008 +0200 + + Spec File::Stat#grpowned? and implement File.grpowned? + +commit c411b15b9f94fec21b02a9208cbae4b42452431d +Author: Dirkjan Bussink +Date: Tue Apr 15 20:19:24 2008 +0200 + + Properly rename File::Stat#dev_major and File::Stat#dev_minor specs + +commit 26ba3ad30cd726b058cd76f23dc7a79555be724e +Author: Charles Oliver Nutter +Date: Tue Apr 15 10:25:44 2008 -0700 + + Quarantine the cvar-related instance_eval spec for now; it's not clean. + +commit d72c609ce4567d7a7fdfd2ee4713ac07033c81db +Author: Marius Nuennerich +Date: Mon Apr 14 20:13:38 2008 +0200 + + Use EnvSpecs where possible + +commit 8ccdf2d612f15515837095e2e4a570861024294c +Author: Marius Nuennerich +Date: Mon Apr 14 17:42:36 2008 +0200 + + Use EnvSpecs module for platform dependent stuff + +commit 130e4bdb1d9fa9512dfe45d4ff4d718096683cdb +Author: Dirkjan Bussink +Date: Mon Apr 14 19:45:24 2008 +0200 + + Fix specs for a bunch of File::Stat methods and implement File::Stat#<=> + + Created specs for atime, blksize, blocks, ctime, mtime and <=> + +commit e5aa89ff13128afb9b43ad77678792aeae4d48ea +Author: Dirkjan Bussink +Date: Sat Apr 12 17:54:57 2008 +0200 + + Remove tag for fixed File#lstat + +commit 21cd4a10833ef3bdda1593423faccb334de16536 +Author: Dirkjan Bussink +Date: Sat Apr 12 17:52:33 2008 +0200 + + Remove unneccary spec placeholders for File#stat / File#lstat + + The shared spec already tests this behavior. With the new added spec + for the difference between the two, File.stat / File.lstat is pretty + well covered for now. + +commit eacb4f8a4d0ba606458a5756ddd6f2ce723a3dfa +Author: Dirkjan Bussink +Date: Sat Apr 12 17:45:55 2008 +0200 + + Specced different behavior between File.stat and File.lstat + +commit 4ae163810074effc068babf538f004e9ff117156 +Author: Adam Gardiner +Date: Mon Apr 14 16:49:04 2008 +1000 + + Refactor Debugger interface into a CmdLineInterface class + +commit e61241498f6ca63b7d5e50e94a70456bc40e929b +Author: Adam Gardiner +Date: Mon Apr 14 14:17:03 2008 +1000 + + Breakpoint clean-up + +commit ae738f21979edf727437438b992629dd0b59a42e +Author: MenTaLguY +Date: Sun Apr 13 16:14:34 2008 -0400 + + elminate Mailbox#clear; difficult to implement with sane semanitics + +commit 76385484049e47f53b840ddf3c0dfe9e365ca8cf +Author: Federico Builes +Date: Sat Apr 12 16:39:19 2008 -0500 + + More specs for REXML::Element + +commit 00547bc562c359ddac13d04a5c955ee25171bcb4 +Author: Matthias Reitinger +Date: Sat Apr 12 11:15:12 2008 +0200 + + Fixed incorrect check for object equality in BigDecimal#nonzero? spec + + One should not use == to check if the method returns self, but equal? + + Signed-off-by: Federico Builes + +commit e8403792167c86f120ce7bdcd1e2c7ce1bc31fea +Author: Matthias Reitinger +Date: Sat Apr 12 11:31:25 2008 +0200 + + Eliminated use of to_s to check for NaN in BigDecimal specs + + Changed "to_s.should == 'NaN'" to "nan?.should == true" + + Signed-off-by: Federico Builes + +commit 5883dd78ad92031c920bb9ee2b703702969a5854 +Author: Charles Oliver Nutter +Date: Sat Apr 12 09:42:11 2008 -0500 + + A few more instance_eval specs, for non-immediate numerics and cvars. + +commit e8fd8e696d5487fa698a9a8b1bab2fb54b420133 +Author: Charles Oliver Nutter +Date: Sat Apr 12 08:28:41 2008 -0500 + + Added instance_eval spec for defining methods under immediates. + +commit c23b365a95862cd438e6228929a3a4e935d60de9 +Author: Vladimir Sizikov +Date: Fri Apr 11 22:09:06 2008 +0200 + + New rubypsecs for BigDecimal#fix and #frac. + +commit 6b6b63ebedb61466b4f04f510bf859574efec7d9 +Author: Vladimir Sizikov +Date: Fri Apr 11 20:07:07 2008 +0200 + + New rubyspecs for Bigdecimal#floor and #ceil. + +commit 75e9118aea32baaeec82efedb5106c63bb0eef44 +Author: Vladimir Sizikov +Date: Fri Apr 11 17:50:36 2008 +0200 + + Corrected Bigdecimal specs since they were missing "should" statements. :) + + Also, added some more cases. + +commit 18fafb2e1f653887fdd3cdef693448d9b2bea29e +Author: Vladimir Sizikov +Date: Fri Apr 11 17:21:02 2008 +0200 + + New rubyspecs for BigDecimal's #power, #** and #exponent. + +commit 35e32daa38c7df385aac99f7b709a4038141faaa +Author: Vladimir Sizikov +Date: Fri Apr 11 13:41:13 2008 +0200 + + New and updated rubyspecs for BigDecimal#precs. + +commit e0172d4eee7a775ab53562477997855ed66615a7 +Author: Vladimir Sizikov +Date: Fri Apr 11 12:31:22 2008 +0200 + + More rubyspecs for BigDecimal#split and some corrections for older ones. + +commit 37d312770700da5eb124fdce7a7b1687c2d9b839 +Author: Adam Gardiner +Date: Fri Apr 11 13:55:00 2008 +1000 + + Get breakpoint handling working properly + +commit 498b95a720e98b70b56af9dfd2c1ba20c0bf89c3 +Author: Adam Gardiner +Date: Wed Apr 9 17:23:56 2008 +1000 + + Make ISeq#decode return symbols rather than objects by default + +commit b8bda0546cdb9ac04ae629f13ccfce5f474e6f2c +Author: Adam Gardiner +Date: Mon Mar 17 14:33:45 2008 +1100 + + Ensure breakpoint original instruction is correct + + When multiple breakpoints are set at the same location, + only the first breakpoint sees the original instruction. + This commit ensures the BreakpointTracker detects such + situations, and updates the breakpoint to set the correct + oringinal instruction to use. + +commit 2700924f23e0283a059583f9e92188b1c3c4f220 +Author: Adam Gardiner +Date: Thu Mar 13 17:20:49 2008 +1100 + + Reorganize Breakpoint class hierarchy + + Refactor Breakpoint class hierarchy in preparation for + adding PersistentBreakpoint and BreakpointRestorer classes. + +commit bfa69d930c38897df18b656d7b86f0b549bed57f +Author: Vladimir Sizikov +Date: Fri Apr 11 03:01:37 2008 +0200 + + Some more test cases for BigDecimas#finite? and #nonzero?. + +commit 71a4b0a51ea4da0c41d7b096aa7b88deb8d0d049 +Author: Vladimir Sizikov +Date: Fri Apr 11 02:42:33 2008 +0200 + + A bit more rubyspecs for BigDecimal#sub and #to_s. + +commit 8ff9ae455c6c7f4b38f3b4dcbdc6c677759f13e2 +Author: Vladimir Sizikov +Date: Fri Apr 11 01:35:16 2008 +0200 + + New rubyspecs for BigDecimal#truncate. + +commit f0a5c13f218d1e2187dfff09bd27cbd6dde544ca +Author: Brian Ford +Date: Thu Apr 10 15:09:55 2008 -0700 + + Converted VMActor specs to dir/files. Added incomplete tags. + +commit df74b0fd98597b51d4c1d51ae09706d51e1a5d3c +Author: Brian Ford +Date: Thu Apr 10 15:01:08 2008 -0700 + + Converted Mailbox specs to dir/files. Added incomplete tags. + +commit 29d223d8bfcc36edc16db58d50f8186905df773a +Author: Brian Ford +Date: Thu Apr 10 14:49:52 2008 -0700 + + Converted Actor specs to dir/files. Added incomplete tags. + +commit 08ab8db440cfdaa7e06b19a0d88750678d4fccbf +Author: Vladimir Sizikov +Date: Thu Apr 10 22:09:39 2008 +0200 + + New rubyspecs for bigdecimal, and excludes. + +commit b76a9e964899348d667181d288c5d4ec0e422c9f +Author: Vladimir Sizikov +Date: Thu Apr 10 19:33:03 2008 +0200 + + One rubyspec for the class definition: def nil:Foo; end + +commit c526f5744ece40e312340556991ee54e4504ebcd +Author: Brian Ford +Date: Thu Apr 10 00:53:06 2008 -0700 + + Processed Rational, Complex, Matrix with mkspec. + +commit 3de6f530c42bdca8c9b1202e60d0d14850024d15 +Author: Brian Ford +Date: Thu Apr 10 00:27:40 2008 -0700 + + Processed IO with mkspec. Added incomplete tags. + +commit faaf8bdb8893f71234d7e2fab07aa11d6c556384 +Author: Brian Ford +Date: Thu Apr 10 00:17:35 2008 -0700 + + Clean up especially bad whitespace in File specs. + +commit fedda8f6865c6cdb07c7599606204f0700042574 +Author: Brian Ford +Date: Thu Apr 10 00:09:56 2008 -0700 + + Processed File specs with mkspec. Added incomplete tags. + +commit 09f6f1b5138b7ca1d276a8c68ee6bf1cba7691b7 +Author: Brian Ford +Date: Wed Apr 9 23:42:40 2008 -0700 + + Processed Kernel specs with mkspec. Added incomplete tags. + +commit e3ca2e3e077c0e026b96e1e68808b95d44233cf5 +Merge: cf0c855... 4d0d1f6... +Author: Thomas Lachmann +Date: Thu Apr 10 02:48:31 2008 +0200 + + Merge branch 'master' into bigdecimal_specs + +commit cf0c8552f31cfd856822c8aa43a5d9d265481ac0 +Author: Thomas Lachmann +Date: Thu Apr 10 02:40:22 2008 +0200 + + Next bunch of specs for Bigdecimal. + +commit 4d0d1f6b98ac2dafa487ece31512443a07bbc928 +Author: Marius Nuennerich +Date: Wed Apr 9 23:42:10 2008 +0200 + + Fix ENV specs + + * Try to avoid `env` + +commit dfcc69ea8bd78e9e463defdef3b4529a5af40bb5 +Merge: 75e6ccd... 6a50f0d... +Author: Marius Nuennerich +Date: Wed Apr 9 22:26:26 2008 +0200 + + Merge branch 'master' of git://git.rubini.us/code + +commit 75e6ccd48bce9e0e939a0ff1d484f14a029969f9 +Author: Marius Nuennerich +Date: Wed Apr 9 22:26:11 2008 +0200 + + Fixes for ENV + + * Add specs + * Add some missing methods to ENV + +commit 6a50f0d2f5146901fe96fe86802df155c9266a21 +Author: Thomas Lachmann +Date: Wed Apr 9 21:11:05 2008 +0200 + + Fixed failures for BigDecimal#specs. + +commit 09bc62e39a8b92c25aeb6287f9fbf4e9cd2b9a6f +Author: Thomas Lachmann +Date: Wed Apr 9 19:55:45 2008 +0200 + + Bunch of specs for BigDecimal. + +commit c281add79d621f6327740109895c624dd25a2e1b +Author: Federico Builes +Date: Wed Apr 9 09:13:29 2008 -0500 + + Cleaning up UPSocket#send specs + + * Got rid of the weird exception catching + * DRY things up a bit with before :each + +commit 8ebefe3c0a61b7aab8ac3d0ae9768c35b657cdb6 +Author: Federico Builes +Date: Wed Apr 9 09:11:43 2008 -0500 + + Adding spec helpers to REXML specs + +commit e3064084efbbac1147d477435010d933ce101413 +Author: Eero Saynatkari +Date: Tue Apr 8 22:20:43 2008 -0400 + + Amended spec wording for Singleton._load slightly. Updated exclude. + +commit 23e621625b95e0db82bd406a5eb8fa7324e41a6e +Author: Chris Shea +Date: Tue Apr 8 15:49:11 2008 -0600 + + Create spec for Marshal.load of Singleton instance + + Signed-off-by: Eero Saynatkari + +commit eec07baa07d591059c64f32c0ddef169cfcccaef +Author: Charles Comstock +Date: Tue Apr 8 20:20:02 2008 -0500 + + Thread#wakeup deadlock for MRI marked as ruby_bug + +commit dbb744d9692c2432d7aebecac17365125efe9087 +Author: Charles Comstock +Date: Tue Apr 8 20:02:08 2008 -0500 + + spec for wakeup which causes MRI to deadlock when it shouldn't + +commit 208a7df6ec2d3c8f550a7ac24db849e593cdc9f3 +Author: Charles Comstock +Date: Tue Apr 8 17:58:15 2008 -0500 + + specs for Thread::list + +commit f6f307e75e49cdf597b0b3755ab214c6fc1950dd +Author: Thomas Lachmann +Date: Wed Apr 9 00:54:34 2008 +0200 + + specs for BigDecimal.new and BigDecimal#zero? (plus tag files). + + Signed-off-by: Eero Saynatkari + +commit 5b1f2043f70b0088f1c32be79eeaa8179c2210a6 +Author: Jeff Rose +Date: Wed Apr 9 00:44:27 2008 +0200 + + Specs for Actor linking and registration, and Mailbox timeouts. + + Signed-off-by: Eero Saynatkari + +commit 4eea149d3d503c121fb7c65115e374838fff8c8a +Author: Ryan Davis +Date: Tue Apr 8 14:55:18 2008 -0700 + + Added extra Array subclass dup spec + +commit 783a884931b718b8fa65dd9768fbebd8a0d1ac0c +Author: Ryan Davis +Date: Tue Apr 8 14:18:45 2008 -0700 + + minor cleanup + +commit 0e047cc97aa6a5acd7193bdde1139f6a89f108b8 +Author: Ryan Davis +Date: Tue Apr 8 14:18:27 2008 -0700 + + minor cleanup + +commit f4797827393e0d9d0e5df5aa5184ecebb066d766 +Author: Matthias Reitinger +Date: Tue Apr 8 17:08:32 2008 +0200 + + Extended Symbol#inspect spec and reworked Symbol#inspect to fulfill them + + Signed-off-by: Eero Saynatkari + +commit b3c3a5f60177f9c52725b6cacf019412d2c747ea +Author: Eero Saynatkari +Date: Tue Apr 8 17:29:58 2008 -0400 + + Excludes for BigDecimal specs. + +commit b7cd3c38d146a7833ef1d426ea8acd4ee4cb09bf +Author: Eero Saynatkari +Date: Tue Apr 8 17:13:48 2008 -0400 + + Switched #requires around to have access to #pretty_inspect. + +commit aba428095e09ead8ed66895b175e5f3673c4310e +Author: Thomas Lachmann +Date: Tue Apr 8 17:45:13 2008 +0200 + + Spec for BigDecimal#to_f. + + Signed-off-by: Eero Saynatkari + +commit 20a5789f9dc4e6d30dffb594476b354e4aeee201 +Author: Thomas Lachmann +Date: Tue Apr 8 17:21:39 2008 +0200 + + Spec for BigDecimal#finite? + + Signed-off-by: Eero Saynatkari + +commit 896609e7ae8ee12c72e4e3ce86897c1f8b98f3fb +Author: Ryan Davis +Date: Tue Apr 8 02:03:22 2008 -0700 + + overlooked 2 specs + +commit 77774ed4300d5245c58dbcc686cd72dc48f08a1f +Author: Ryan Davis +Date: Tue Apr 8 02:00:24 2008 -0700 + + Added a bunch of specs to String#to_f + +commit 1b91113c3e8fb46a0d355cae9000ee4c82f95ac3 +Author: Federico Builes +Date: Mon Apr 7 21:46:17 2008 -0500 + + More specs for REXML + +commit 2460839e3fbe2967b9df70db3de33b2a102b9a44 +Author: Brian Ford +Date: Mon Apr 7 12:19:40 2008 -0700 + + Reworked how MSpec handles config files. Use 'set :sym, value' now. + +commit 67d3869e9b3fef6d47727206d02814da410e02fc +Author: Jeff +Date: Mon Apr 7 15:04:09 2008 +0200 + + Adding specs for Mailbox and Actor, and renaming the VMActor describe to match the standard scheme. + + Signed-off-by: Charles Comstock + +commit 7391c1fbc02966165de03724c42fc1d5243ac99f +Author: Marius Nuennerich +Date: Sun Apr 6 22:29:22 2008 +0200 + + repair UDPSocket spec + + Signed-off-by: Dirkjan Bussink + +commit 5a205207faad0a85271bfcb459390793702c4143 +Author: Eero Saynatkari +Date: Sat Apr 5 04:54:21 2008 -0400 + + Partially revert "Add spec files for cgi.rb." + + This partially reverts commit e2714f2fd2d8825ac8af761a5a4545e4d0731735. + + Conflicts, left these files: + + spec/ruby/1.8/library/cgi/escapeHTML_spec.rb + spec/ruby/1.8/library/cgi/escape_spec.rb + spec/ruby/1.8/library/cgi/rfc1123_date_spec.rb + spec/ruby/1.8/library/cgi/unescapeHTML_spec.rb + spec/ruby/1.8/library/cgi/unescape_spec.rb + +commit 22f3042377731cb6ff963b9e322b24014b286895 +Author: Eero Saynatkari +Date: Sat Apr 5 03:18:15 2008 -0400 + + Added excludes for the CGI specs. + +commit 7b9f5a213c971636b663e992fcb8578888d27f52 +Author: makoto kuwata +Date: Sat Apr 5 13:03:59 2008 +0900 + + Add spec file for CGI::rfc1123_date(). + + Signed-off-by: Eero Saynatkari + +commit 31edbd64bba7f352930ac04d51b63e72553796a9 +Author: makoto kuwata +Date: Sat Apr 5 13:03:14 2008 +0900 + + Add spec files for CGI::escapeHTML() and CGI::unescapeHTML(). + + Signed-off-by: Eero Saynatkari + +commit fc321869d73f58dcfbb55ba374646c1568528004 +Author: makoto kuwata +Date: Sat Apr 5 13:01:33 2008 +0900 + + Add spec files for CGI::escape() and CGI::unescape(). + + Signed-off-by: Eero Saynatkari + +commit e2714f2fd2d8825ac8af761a5a4545e4d0731735 +Author: makoto kuwata +Date: Sat Apr 5 12:44:57 2008 +0900 + + Add spec files for cgi.rb. + + Signed-off-by: Eero Saynatkari + +commit 2a1d0ad7e51ba52a918111d53be6a641c41a0445 +Author: Eero Saynatkari +Date: Fri Apr 4 22:21:47 2008 -0400 + + Improved the *rest argument count spec a bit. + +commit e8053e4bb108cf877ac8fdafc104eb34bad671f0 +Author: Eero Saynatkari +Date: Fri Apr 4 20:38:23 2008 -0400 + + Specs for unlimited argument count for *rest defns. + +commit 03e092e45015f8115f806e11460121c560e60b4b +Author: Ryan Davis +Date: Fri Apr 4 17:54:25 2008 -0700 + + Converted symbol spec to be generative, allowing easier pattern detection + +commit bbda617127a8ac319a58fa190d43b3a0d960d309 +Author: Charles Comstock +Date: Fri Apr 4 14:07:50 2008 -0500 + + updated File#inspect tags + +commit 38eb679d6b6c5aef8bccb2139e681c926b3290c7 +Author: Eero Saynatkari +Date: Fri Apr 4 06:00:37 2008 -0400 + + Specs for ~/ expansion in #require, #load. It has broken at some point. + +commit 2d600c01205fbb7ccd98e7f7a88ebcbd0e1d1d43 +Author: Paul Thornthwaite +Date: Fri Apr 4 08:43:42 2008 +0100 + + Updated specs for Set library + + * Added specs for Set#subset and Set#proper_subset + * Added specs covering empty sets and comparisons + * Corrected spec string to include ? on superset method names + + Signed-off-by: Eero Saynatkari + +commit 3a547c2b82434c64b72967ebd917fc063ff1317d +Author: Brian Ford +Date: Thu Apr 3 23:16:18 2008 -0700 + + Fixed GetoptLong specs to not depend on value of ARGV. + +commit 5dd9b0ecdddfd990d6387a0a7c70173ea0cededa +Author: Brian Ford +Date: Wed Apr 2 23:27:04 2008 -0700 + + Add config file for and rework MSpec runners. + +commit 773a13ed9005628e48ed146180041caa035f4072 +Author: David Yip +Date: Thu Apr 3 03:18:48 2008 -0400 + + Added spec: full contents of StringIO stream should be accessible after rewind. + + Spec tested against Ruby 1.8.6p111 and Ruby 1.8.6p114 on OS X 10.4.11. + + Signed-off-by: Eero Saynatkari + +commit ba2ca41cb29ac08c94231a2383940464e6fd1c9d +Author: Federico Builes +Date: Thu Apr 3 08:45:49 2008 -0500 + + Updated tags for REXML specs + + Signed-off-by: Eero Saynatkari + +commit 09c080bf33092b9d147d1b0a5de920fce8527fdc +Author: Federico Builes +Date: Thu Apr 3 08:45:26 2008 -0500 + + Fixes whitespace in REXML::Element specs + + Signed-off-by: Eero Saynatkari + +commit 3a997bc18f589b91b4cd518448644171f3054abf +Author: Federico Builes +Date: Thu Apr 3 08:29:23 2008 -0500 + + More specs for REXML::Element + + Signed-off-by: Eero Saynatkari + +commit d250939060a4a91a6fee59bd4bfa4e86eb271373 +Author: Paul Thornthwaite +Date: Thu Apr 3 14:36:42 2008 +0100 + + Specs for Set#superset and Set#proper_superset added + + Signed-off-by: Eero Saynatkari + +commit 01399738d5ad0136ef205b8501b12012c7e42230 +Author: Eero Saynatkari +Date: Thu Apr 3 18:09:20 2008 -0400 + + Removed excludes for Object#kind_of?, #is_a?. + +commit 0e7d1c6e02e5617bb251366e0d60760edb29377e +Author: Vladimir Sizikov +Date: Thu Apr 3 20:04:34 2008 +0200 + + Fixed copy-paste error in Object#is_a? specs. + + Adjusted the Object#is_a? exclude. + +commit 4a9cb7cc0c734b4280c3a65906c85e1c1e2f4990 +Author: Arthur Schreiber +Date: Thu Apr 3 19:02:05 2008 +0200 + + Add specs for #kind_of? / #is_a? behaviour that are failing in Rubinius. + +commit e88fdb6cbd9fa829a81e6c7664e88f6956ddae64 +Author: Eero Saynatkari +Date: Thu Apr 3 07:18:56 2008 -0400 + + Spec to check `A = 12; class A; end` raises TypeError. Works as is. + +commit 3c0db09626333405bdcb72e62ddb8fb2ea176ff5 +Author: Eero Saynatkari +Date: Thu Apr 3 06:44:32 2008 -0400 + + Spec for const lookup: `A = 12; class A::B; end` should raise TypeError. + + * Currently crashes due to a lookup problem. + * VVSiz discovered and reported. + +commit edda5994c293e4d26b4a741e90e0ab61513e8dec +Author: Adam Gardiner +Date: Wed Apr 2 16:39:09 2008 +1100 + + Do not strip leading spaces in debugger output + +commit eecc2bca5045921368378abfccafcf70339441f9 +Author: Vladimir Sizikov +Date: Wed Apr 2 21:34:52 2008 +0200 + + Enabled File#truncate testcase for JRuby. + +commit 4d555cf50dfe6a8e9cb2f24a6a636a9df3f03768 +Author: Vladimir Sizikov +Date: Wed Apr 2 20:03:02 2008 +0200 + + Added test case to File.open rubyspecs. + + Courtesy of David Yip. + +commit 42f0b52cd9fbac4a39fc1e5c2a241462bee5bf3b +Author: Brian Ford +Date: Wed Apr 2 01:11:38 2008 -0700 + + Use kind_of instruction since #kind_of? is not available at all times. + +commit 9ee52514eee820b9af7c9e6d2eaaca8d2bca363b +Author: Charles Comstock +Date: Tue Apr 1 17:16:47 2008 -0500 + + IO#reopen should return self + +commit f1481283091fcbe662fd01d409f5a2d2d7e3aa59 +Author: Charles Comstock +Date: Tue Apr 1 18:57:06 2008 -0500 + + added primitive io_close_ng and tagged IO#close spec failures + +commit 3861e75e01af9319e2af879e2644fc8509947903 +Author: Charles Comstock +Date: Tue Apr 1 16:07:27 2008 -0500 + + IO#close should return nil and refactored TCPServer.accept specs + +commit d6dfbd3b0bab57453e67991c3320744b08346979 +Author: Charles Comstock +Date: Tue Apr 1 15:04:18 2008 -0500 + + DRb specs now attempt to check if server is up/down prior to each call to start_server + + note that there is something wrong with the way stop_server works in rubinius as it appears that the TCPServer is still binding the port. Spec is tagged to deal with this but technically it's probably a bug in TCPServer + +commit 4119fe8baab45be6b1d1370b8a9537e710b1a60a +Author: Eero Saynatkari +Date: Tue Apr 1 12:40:47 2008 -0400 + + Sanity changes to #load specs to bring them up to date. + + * Please change the specs if you change the implementation, sheesh. + +commit 3b58cb35abeba31f7ac72e3ab37b2630949406a7 +Author: Eero Saynatkari +Date: Tue Apr 1 10:59:58 2008 -0400 + + Spec for forced recompiling through second parameter of Kernel#load. + +commit 5d7a73ae15a4c40e31486a60cbb66f3de1ac4697 +Author: David Whittington +Date: Wed Apr 2 02:57:35 2008 +0000 + + Add tags for failing private keyword specs + +commit 1b2f118be7ff9b6adfea736ecbbb8f3fd8dd0f49 +Author: David Whittington +Date: Wed Apr 2 02:53:43 2008 +0000 + + Added a couple evil private keyword tests + +commit f58c67e33a99f751c3520ab65c96e28a91c45900 +Author: Eero Saynatkari +Date: Tue Apr 1 09:59:22 2008 -0400 + + Conditional compilation. Rubinius.compile_if($DEBUG) { p somevariable }. + + * Hacky and probably fragile but it seems to work. Whenever the gvar + given as condition evaluates to false, the entire block is omitted + from the produced bytecode. If it evaluates to true, then the extra + block itself is stripped and only the block contents remain. + * Do NOT use indiscriminately until we have played around with it for + a bit to avoid problems. + * Manipulates the sexp, not the AST to avoid worrying about locals + and scopes and whatnot. + * Enabled by default; for example -d will work out of the box (you + do need to have the file recompiled obviously.) + +commit 4f78ee2b0bebb9170a483927af9c7520ca67f912 +Author: Eero Saynatkari +Date: Tue Apr 1 09:58:53 2008 -0400 + + Specs to verify conditional compilation in the compiler. + +commit 8dfece35e3bc83e14e92bfee9ea0ebabb795da70 +Author: Brian Ford +Date: Tue Apr 1 01:07:14 2008 -0700 + + Fix up language symbol specs. + +commit 29cc22f2c1f7ce2ce15a7f339d1159cf93510daa +Author: Brian Ford +Date: Tue Apr 1 00:40:34 2008 -0700 + + Constant lookup only searches class or module (#457). + +commit 538611f2aa06a1cf1c3958583bd6a8487deee994 +Author: Eero Saynatkari +Date: Mon Mar 31 18:03:41 2008 -0400 + + Spec for empty loop body. + +commit fd0d1079671d7664de3a6a836c5e5624d487a4e1 +Author: Dirkjan Bussink +Date: Mon Mar 31 23:47:40 2008 +0200 + + Spec for constant lookup on non Module or Class objects + + This exposes the bug also described in ticket \#457 + +commit 3b7cf550c70db2dd53cb58ef3efd2651ee352134 +Author: Vladimir Sizikov +Date: Mon Mar 31 21:53:40 2008 +0200 + + Added a couple of Dir.glob/Dir[] rubyspecs. + (Courtesy of Roland Swingler) + +commit bbfa77a8517390bdc807f41bfe6d101791980d8f +Author: Vladimir Sizikov +Date: Mon Mar 31 19:04:09 2008 +0200 + + Fixed DRb rubyspecs (proper spec name, removed invalid file, better cleanup). + +commit d8a4fb0b16dc4c722cf148ff83bcad05fbb4af1e +Author: Vladimir Sizikov +Date: Mon Mar 31 14:29:54 2008 +0200 + + Make sure Marshall#load rubyspec closes the file. + +commit 4082a7663eaef50000be46d909c22fbb97a1a3e8 +Author: Vladimir Sizikov +Date: Mon Mar 31 13:57:35 2008 +0200 + + Reverted new Range#step rubyspecs, since they fail on MRI and JRuby. + + Partial revert "Fixes for Range#step." + This (PARTIALLY) reverts commit a6b06a67207c40ffa9ccf191c051fdf2fa0f5359. + + The specs are reverted since they fail on: + MRI 1.8.6 pl 36 (Ubuntu default) + MRI 1.8.6 pl 114 (Current compatibility target) + MRI 1.8.6 from 1_8 branch + MRI 1.9 from Ruby trunk + JRuby 1.1 from trunk + + The specs expect that to_f is invoked, but MRI and JRuby don't behave + that way. Furthermore, Float is not a special case. There are other + cases, like Rational. Take a look into MRI code, there is no special + handling for Float. + + Please, test your spec updates at least against the current + compatibility target (MRI 1.8.6 patchlevel 114) to avoid problems. + +commit 6d9680ecaaa2a9aadd35699c8064bf6481acc107 +Author: Vladimir Sizikov +Date: Mon Mar 31 13:23:20 2008 +0200 + + Added new rubyspecs for IndexError out of String#[]= + +commit c8a52bb7cf191bb35efc89c560bdeced4241f015 +Author: Eero Saynatkari +Date: Mon Mar 31 04:38:49 2008 -0400 + + Split Regexp#=~, #match specs; they behave differently on match. + + * #=~ Returns index, #match returns MatchData. + * Grammar fixes. + +commit 6c2727e928991cdf9f809cb5941c3afedb5171ff +Author: Eero Saynatkari +Date: Mon Mar 31 04:07:17 2008 -0400 + + Fix Regexp#match, #=~ spec to actually be shared. Exposes #454. + +commit e258a2bccafffba57ab86d1c1a104839bda424da +Author: Eero Saynatkari +Date: Mon Mar 31 03:30:48 2008 -0400 + + Spec to verify IO behaviour with an altered BufferSize from Le Huy. + + * Moved spec to spec/core/io/ and simply used the first one. + * This problem seems to have been largely corrected. + +commit 7a39be8bea055464838ff24c70e170a91f8df68c +Author: Ben Burkert +Date: Sat Mar 29 19:39:11 2008 -0500 + + Added spec for Module#define_method + + Methods defined by define_method with a proc should have the + same scope for local variables as the proc. + + Signed-off-by: Eero Saynatkari + +commit 12c639d90ff3d14f8010ca7c782612bd7c1777ab +Author: Eero Saynatkari +Date: Sat Mar 29 23:58:13 2008 -0400 + + Tony Arcieri's specs for inter-VM Actors. + + * VMActor implements the Actor interface to work in Rubinius' Multi-VM + context: VMActors can reside on any VM instance. + +commit a0d0884aa3c9e7a6fa949cbde1cdf2392bc4ff23 +Author: Eero Saynatkari +Date: Sat Mar 29 15:59:42 2008 -0400 + + Module#attach_foreign allows using a symbol to give the function name. + + * Specs for the same. + +commit a5f397f38d6c9eafcac163c2cf678d5c55a6b79b +Author: Eero Saynatkari +Date: Fri Mar 28 23:40:04 2008 -0400 + + Specs for FFI in general and Module#attach_foreign in particular. + + * Very basic specs to verify that FFI in fact works correctly. + * We need to define what the behaviour should be in the case of e.g. an + incorrect function signature. Currently it may or may not cause SEGVs + depending on the exact usage. Remainder specs are in but quarantined. + +commit 3dc5c635b56bc599a718a94f990976b67ab52b6c +Author: Eero Saynatkari +Date: Wed Mar 26 02:01:12 2008 -0400 + + Specs for Module#attach_foreign. + + * The method is a replacement for #attach_function but allows + giving the library name as well to access external libs. + * This acts a higher-level interface to FFI.create_function. The + "real" FFI specs will be written for that method instead. + +commit 677412353409ba4e5d67f19a3d095c62d009c88f +Author: Brian Ford +Date: Fri Mar 28 18:04:40 2008 -0700 + + Added CType#isctrl, #toprint. Rework String#inspect, #dump. + +commit 87ba991b9b488b808ebf729b9e41765df76cc602 +Author: Brian Ford +Date: Fri Mar 28 15:09:11 2008 -0700 + + Reworked String#each and #sum. Added String#modified? and specs. + +commit 204d8ce1a792a61882e549953b5b878139ac9cda +Author: Hongli Lai +Date: Fri Mar 28 23:32:18 2008 +0100 + + Spec: Marshal raises EOFError on loading an empty file + + Signed-off-by: Michael S. Klishin + +commit f6e698f96ce9e2a8c8abe856322add02931df8b7 +Author: Michael S. Klishin +Date: Sat Mar 29 02:20:23 2008 +0200 + + Tag new spec for ensure as failing + +commit ef7e4436389a0f4346b3a3bc5c275b653f46d6bb +Author: Hongli Lai +Date: Fri Mar 28 23:22:44 2008 +0100 + + Add spec for exception handling inside ensure block. + + Signed-off-by: Michael S. Klishin + +commit f54c91f6cb7498fe44b1b05a1372d9f6ed3ea1ee +Author: Stuart Halloway +Date: Fri Mar 28 10:11:05 2008 -0400 + + Fixes Pathname#absolute? and #relative?. + + * specs now pass + * underlying cause was corner case in File#basename + * new passing spec for corner case + + Signed-off-by: Charles Comstock + +commit 0d4606d53d8fc0bcb2370bd648546abffd402673 +Author: Charles Comstock +Date: Fri Mar 28 16:45:47 2008 -0500 + + fixed CSV::Reader.parse spec to use local fixtures + +commit 35a15c6c85ebb6eabaec16e03aa88399061844e9 +Author: Alister Lee +Date: Sat Mar 8 18:11:24 2008 +1100 + + Beginning of specs for CVS::Reader.parse + + Signed-off-by: Charles Comstock + +commit d4161a379eab621e338a8c82f088b834756082e9 +Author: Charles Comstock +Date: Fri Mar 28 16:39:50 2008 -0500 + + removed csv/reader/parse_spec to commit alister lee's spec + +commit 534806c10a95435873efcb0d215732d7da4f2fd6 +Author: Charles Comstock +Date: Fri Mar 28 16:38:03 2008 -0500 + + mkspec generated specs for csv.rb + +commit a6b06a67207c40ffa9ccf191c051fdf2fa0f5359 +Author: Stuart Halloway +Date: Fri Mar 28 06:09:34 2008 -0400 + + Fixes for Range#step. + + * previously failing specs pass + * new spec added to cover float/int difference + + Signed-off-by: Michael S. Klishin + +commit 6886ec5851783c5364ff5bc464ee94071fc8535e +Author: Michael S. Klishin +Date: Fri Mar 28 00:06:56 2008 +0200 + + Update stdlib and specs for REXML from 1.8.6 patchlevel 114 (see details!) + + * Update stdlib/rexml to use REXML from Ruby 1.8.6 p114. + * REXML in p114 is screwed up: call sites were not updated + after REXML::Formatters::Transient#initialize arity + change. Ruby 1.8.x branch in SVN though has + completely different REXML layout and organization + (rev. 15833) so there's no way to fix it until we know + where REXML changes are headed in 1.8.x branch. + * Update REXML spec and tags for it. + +commit 3145a74a85d72f6ef8a93384a74d96a589bfb5eb +Author: Brian Ford +Date: Wed Mar 26 22:27:41 2008 -0700 + + Rework and cleanup of various String methods. + + Also, ensure that when Strings are converted through FFI + and passed to C functions, the char array is explicitly + terminated with \0. + +commit 9ba3e515b49729e0cb80181af9e28e3ce4c70e97 +Author: Brian Ford +Date: Wed Mar 26 18:40:57 2008 -0700 + + Shuffle some String methods. Add specs for and rework String#substring. + +commit 990d47b84bc6301be2a8bcbaccbae65ef697c417 +Author: Brian Ford +Date: Tue Mar 25 16:22:45 2008 -0700 + + Added String#compare_substring. Reworked String#chop! and #chomp!. + + Also, to ensure that ByteArray instances that are accessible in + Ruby are properly handled by C functions, changed string_equal_p + to use strncmp instead of strcmp. + +commit f47c446daa136e6f31f5c590dd535ba22e89a0b2 +Author: Brian Ford +Date: Tue Mar 25 11:36:16 2008 -0700 + + Fix errors in String#count_table spec descriptions. + +commit 9425d0de9a7883c14de6ae9ae5db05ab92141ab9 +Author: Dirkjan Bussink +Date: Wed Mar 26 22:38:48 2008 +0100 + + Guarded two failing specs on OpenBSD that also fail on MRI + + MRI on OpenBSD also suffers from the 0.0 / -0.0 issue (the + GCC version on that platform too). The child reaping spec + also fails on both MRI and Rubinius + +commit 288a6e2ca3675a1e60bfd6b8b328c2a4d513c12f +Author: Dirkjan Bussink +Date: Wed Mar 26 22:15:16 2008 +0100 + + Fix Socket specs for more strict BSD behavior + +commit 63513d23f16ca7919b8605e016a3a941b79c0834 +Author: Ryan Davis +Date: Tue Mar 25 17:20:53 2008 -0700 + + oops! extra exclude + +commit a36a4bf8cde95c99282e07f46438430588288736 +Author: Ryan Davis +Date: Tue Mar 25 17:20:19 2008 -0700 + + really minor changes + +commit e9b759812deaf97e7fe5846c116d53f69b63e244 +Author: Ryan Davis +Date: Tue Mar 25 17:19:41 2008 -0700 + + Added the sucky parser spec--not passed yet + +commit 90eb74998e132373e6b96e3c66bfa909854e3ef0 +Author: Ryan Davis +Date: Tue Mar 18 17:41:50 2008 -0700 + + Added spec for 'a [ 42 ]' + +commit 2d34643c75b53b832e89d2473d501ab1c8a5df02 +Author: David Whittington +Date: Wed Mar 26 08:01:20 2008 +0000 + + Tagged Generator specs as unstable due to memory consumption + + Each spec consumes > 60MB of memory. After looking at the specs there is no way + they should be consuming that much memory. + +commit 52d81e0593dbca8abfecefe2e9c3d2ab504cfe0b +Author: Brian Ford +Date: Tue Mar 25 10:43:17 2008 -0700 + + Added String#copy_from primitive. Reworked String justify methods. + +commit 1aabda50ea82974b96a7032a0ea13865b2332b5d +Author: Brian Ford +Date: Mon Mar 24 21:57:02 2008 -0700 + + Added Tuple.template and reworked String#tr and friends. + +commit bc7d9ccb8b8ca77d8479f325ea314fc09bc34907 +Author: Brian Ford +Date: Fri Mar 21 00:51:10 2008 -0700 + + Rework methods that behave like String#count. + +commit 1e5ac9a6818c972882e080aeb723a105108e0c57 +Author: Brian Ford +Date: Wed Mar 19 21:25:07 2008 -0700 + + Rewrite of String#casecmp, approx 2x faster. + +commit c39f2cb708169d35c2fbeb969ee3323c704f0566 +Author: Matt Palmer +Date: Tue Mar 25 21:09:37 2008 +1100 + + Some specs for the timeout library + +commit cb69bdadeb10cf6b4b2c71a095562f8d8371d76d +Author: Federico Builes +Date: Mon Mar 24 17:55:23 2008 -0500 + + Small fix for Socket.getaddrinfo spec + + Signed-off-by: Michael S. Klishin + +commit 5c3a61edef3c456b8296e65f8e06026347339a36 +Author: Federico Builes +Date: Mon Mar 24 17:06:36 2008 -0500 + + Fix for the socket's issue + + Signed-off-by: Michael S. Klishin + +commit f3fd9ac4eebd0bc2a0a06bbe06921463d03177eb +Author: Federico Builes +Date: Mon Mar 24 14:10:46 2008 -0500 + + Fixes specs for Socket and adds a gethostname spec + + * Changes hardcoded "localhost"s to Socket#gethostname calls. + * Adds a simple spec for Socket#gethostname + + Signed-off-by: Michael S. Klishin + +commit 7131328bc02057b16071a933fe98f331b27e00bb +Author: Michael S. Klishin +Date: Tue Mar 25 00:24:01 2008 +0200 + + Applied slightly modified patch by Federico Builes: + + * Add REXML::Document and REXML::Attribute specs + +commit cb464295e5accb00e783f7f9e2a0b10c64ad6579 +Author: Vladimir Sizikov +Date: Sun Mar 23 12:06:07 2008 +0100 + + Added new Range#step rubyspecs. + + Excludes for rbx also updated. + +commit 7d181716ac3b92d8a31a20ec30daee455d36fc58 +Author: Charles Oliver Nutter +Date: Sat Mar 22 14:51:30 2008 -0500 + + Added order-of-evaluation spec and tags for rubinius failures. + +commit 5caf94ce6deb5e28c9a3de02e60a9b86cbdaf7ec +Author: Charles Comstock +Date: Fri Mar 21 12:37:02 2008 -0500 + + tagged new specs for pathname + +commit 62f88983ee3fa1b09d8f7df56e35cbfdac6d2a06 +Author: Martin Stannard +Date: Fri Mar 21 12:10:23 2008 +1100 + + added some specs for pathname library + + there are failures in absolute and relative specs + + Signed-off-by: Charles Comstock + +commit 655f61650bb299f38c9fd978594baa483fc0d0cc +Author: Ryan Davis +Date: Tue Mar 18 16:22:24 2008 -0700 + + Reduced parser todos from 113 to 89 + +commit f97b2fc2ee3310e81871200125bbd7e33c2636bf +Author: Ryan Davis +Date: Tue Mar 18 13:51:31 2008 -0700 + + Moved sexp_expectations.rb to fixtures subdir + +commit 0a185e5ac48954cf4addae0c8f09dcb5be259f8e +Author: Ryan Davis +Date: Mon Mar 17 17:55:50 2008 -0700 + + Added f'd up note about the spec failing + +commit 978f043e1ed3a2b7cb7d4129e0002be485b0a78c +Author: Dirkjan Bussink +Date: Tue Mar 18 21:17:32 2008 +0100 + + Fix Process.groups spec + + Process.groups can return an array with the same gid multiple + times on certain platforms (at least on FreeBSD and OpenBSD). + +commit 8812658dde5e317dfebd0ea3c159ad0a1b98e8e8 +Author: Dirkjan Bussink +Date: Tue Mar 18 21:02:00 2008 +0100 + + Update spec tags for ERB + +commit 47216560d4a980cbaac2855e0c5ee302e0754bf8 +Author: Dirkjan Bussink +Date: Tue Mar 18 20:53:16 2008 +0100 + + Update spec tags for IO + +commit 7d34f4053023d99c3be4964bfebb3a1c74cd40c9 +Author: Dirkjan Bussink +Date: Tue Mar 18 20:32:39 2008 +0100 + + Update spec tags for File + +commit 8a66bc6f5e378f49febb80fba37723a7de0d2475 +Author: Brian Ford +Date: Mon Mar 17 15:46:51 2008 -0700 + + Added specs for File.[l]chown/#chown, code for File.lchown. + +commit 960872ae163a5615f513c58d727a7fd93664673e +Author: Glenn Davy +Date: Mon Mar 10 10:00:40 2008 +1100 + + Make File.fnmatch respect case when using square brackets + +commit 0e32f8e224543a3c152b0351540eaa36fdfcdb06 +Author: Brian Ford +Date: Mon Mar 17 11:04:27 2008 -0700 + + Added exclude for failing spec added in b635fcf0. + +commit 62687753b239984acba4f0e80899ca75a8a08cfe +Author: Brian Ford +Date: Mon Mar 17 10:56:31 2008 -0700 + + Fixes and specs for Module class_variables methods. + +commit b635fcf041707fe55a26b7709aef8dc1b2509161 +Author: Charles Oliver Nutter +Date: Mon Mar 17 12:52:13 2008 -0500 + + Add a simple Module#private spec. + +commit 2aa98e1df50bba768b57018f6e90c56fe39206f4 +Author: Vladimir Sizikov +Date: Mon Mar 17 13:28:17 2008 +0100 + + Make sure no processes left hanging after IO#close specs. + +commit 8f332dde4460c03c378f1d1ecc1fbae54557d8ee +Author: Matt Palmer +Date: Mon Mar 17 16:44:24 2008 +1100 + + Raise an Errno exception if a write fails + +commit 55c830063115e4455eeda3f8de639a7f7e0624f5 +Author: Matt Palmer +Date: Mon Mar 17 16:42:16 2008 +1100 + + Raise IOError if we attempt to write to a readonly file + +commit ad64c0ea7598b8a4c62ba2dd435f70c976186a50 +Author: David Whittington +Date: Sun Mar 16 04:24:54 2008 +0000 + + Modified file type specs to search for sockets in /var/run instead of /var + + Doing a find on /var could take quite a while + might do nasty things like do + finds on backup files etc. Running a find on /var/run should be faster and + safer. + +commit ff5e9d3b9d7f3e484211b66fff96e665ed13614b +Author: Vladimir Sizikov +Date: Wed Mar 12 17:44:55 2008 +0100 + + Revert "Added simple spec for range splatting". + + This reverts commit 9b3988436a21f61c86168a7566d472c4dfa22162. + + The spec uses '=' instead of '==', and it verifies something + that is not true for MRI (1.8, 1.9) or JRuby. + +commit 004662e54477269a98475f84724972b82885d9cb +Author: Brian Ford +Date: Sat Mar 15 01:09:43 2008 -0700 + + Exclude failing UNIXServer.new spec. + +commit 13340924519f607d9c48da04c3f3ab41a1de3e86 +Author: Brian Ford +Date: Fri Mar 14 18:14:06 2008 -0700 + + Tagged unstable Process.kill specs that cause hangup on linux. + +commit c4a4dc19a26db058594c8056933cdab42d4f26fd +Author: Matt Palmer +Date: Fri Mar 14 21:13:31 2008 +1100 + + Fix up IO#write spec so it works cross-platform + + It looks like the Linux implementation of IO#write and IO#read are a bit + different from the OS X version, because the spec worked on OS X. + Presumably this tiny change won't cause any conniptions. + +commit 33890d9a77d5a34c15263f84b9b415ffc084815a +Author: Kamal Fariz Mahyuddin +Date: Fri Mar 14 14:42:11 2008 +0800 + + Remove fail tags from passing ruby/1.8/core specs + +commit 4bdd3df099fe627d158f4c6d35e5a7df0a891e86 +Author: Adam Gardiner +Date: Fri Mar 14 12:58:40 2008 +1100 + + Fix bug where stepping by line would sometimes skip a line + +commit 260190092afbcfadd1a6e1d6db1674ecf021b686 +Author: Matt Palmer +Date: Sat Mar 8 19:26:41 2008 +1100 + + Put in an explicit IO.new test for single-argument + + Assuming that your UDPSocket tests passing will prove that IO.new takes + one argument might have been, in retrospect, a little retarded. + +commit 58216e07f0728415762fe5fbe98e1e984dfea31b +Author: Matt Palmer +Date: Sat Mar 8 18:45:17 2008 +1100 + + Mark changing failures in the CI test suite + + Fix up so that the CI doesn't fail as a result of my previous changes to the + UDPSocket specs. + +commit 36f91c5da132f309fbf6d047fd74ebd8aa7cbf22 +Author: Matt Palmer +Date: Sat Mar 8 17:26:49 2008 +1100 + + Rearrange the UDPSocket test cases for better separation + + * open_specs now only contains a spec that calls UDPSocket.open; + * send_specs now has separate tests for ad-hoc and connection-oriented + sends. + +commit b40c1cf434bd0879f672ec1dc471f1e1dfaccc1c +Author: Wilson Bilkovich +Date: Thu Mar 13 17:07:50 2008 -0700 + + Add (failing) Symbol#to_yaml spec based on ticket 322 + +commit c0bcb0151379fe9858d0fafd2ef56cf1b08daff3 +Author: Wilson Bilkovich +Date: Thu Mar 13 16:23:37 2008 -0700 + + Apply ticket 351 and resolve ticket 350 (RbYAML bugs) + +commit a8d6e8cddfd8bc2dccaa93b25adfb31b39b96dba +Author: Ryan Davis +Date: Thu Mar 13 16:25:55 2008 -0700 + + Removed all should_not raise_error from shared/time_params.rb + +commit 01f09f4e5697c4a775ac321a71d3b777196d9001 +Author: Ryan Davis +Date: Thu Mar 13 15:47:37 2008 -0700 + + cleaned up spec with new raise_error block form + +commit e965fc735311915dd43c47cc4853e163376cc6be +Author: Lachie Cox +Date: Sat Mar 8 17:14:37 2008 +1100 + + enhanced syntax error to give same message as MRI + +commit 868b38152ca99189fce85542a9068c0d01ee4a41 +Author: Ryan Davis +Date: Thu Mar 13 15:07:33 2008 -0700 + + Added exclude for last patch applied + +commit 9b3988436a21f61c86168a7566d472c4dfa22162 +Author: Patrick Hurley +Date: Mon Mar 3 14:04:14 2008 -0500 + + Added simple spec for range splatting + +commit 3c7a017e173945d3f9b18d566bb1c3d6d04e97e4 +Author: Charles Comstock +Date: Thu Mar 13 17:18:39 2008 -0500 + + fixed tags for new constant specs + +commit a966436b7be78bc063e32bc16496f5cabbb0a152 +Author: Matt Palmer +Date: Sat Mar 8 14:56:58 2008 +1100 + + Make sure modules included in Object are found + + Add a spec to make sure that constants from modules included in Object are + found. Evan is committing the fix for this separately. + + Signed-off-by: Charles Comstock + +commit 4e0ddd3e701f68b592cb69972f7d587b90392913 +Author: Wilson Bilkovich +Date: Thu Mar 13 15:05:54 2008 -0700 + + Correct a 'defined?' spec added by ticket 388 + +commit 407095d8ffbf0563fa46e5d4ed6a08423eddb2ad +Author: Martin Stannard +Date: Sat Mar 8 15:47:59 2008 +1100 + + Added tests where defined? method should return string descriptions of objects + +commit f366309a8fff28552d7d27101d8b3d7b4352e235 +Author: Gianluigi Spagnuolo +Date: Fri Feb 29 10:42:42 2008 +0100 + + Fixed Array set element problem + +commit 42c22bf542edc8c8379587507fd9e35ba25b190c +Author: Charles Comstock +Date: Thu Mar 13 17:00:31 2008 -0500 + + updated tags for new read specs + +commit 45c43a7ab3310a41b0b3367f4762a1bb55b02405 +Author: Ben Askins +Date: Sun Mar 9 11:41:49 2008 +1100 + + Fix typo in file/open_spec.rb + + Signed-off-by: Charles Comstock + +commit a221ea56325fe082154a629094abb27d40919a39 +Author: Alister Lee +Date: Sun Mar 9 15:27:34 2008 +1100 + + Specs to expose defect in eof treatment in IO.read + + Signed-off-by: Charles Comstock + +commit 4967adb3d49252aae75b6b57159fb5879ac75db1 +Author: Myles Byrne +Date: Sat Mar 8 12:14:20 2008 +1100 + + Check existence of ArgumentError + +commit 45e46234da288052e639bb5c9c122874fd4d4e1c +Author: Brian Ford +Date: Thu Mar 13 10:28:54 2008 -0700 + + Fix File[Test].size? and specs for it. + +commit d467bf21c4037784a21ba964b24c28fc80b34736 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 17:36:56 2008 +0800 + + Fix IO::foreach when separator is nil + +commit 70615e1c15692b8a8149e1616c802db9eb5bad11 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 17:32:40 2008 +0800 + + Fix IO#flush to raise IOError on closed stream. Remove empty tag files. + +commit 9c9e7f422c98bf6add6c9a426ae25e3a6dbced85 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 17:29:31 2008 +0800 + + Fix IO#fcntl to raise IOError on closed stream + +commit 215d600002948efb949422c0163aa9bbe5790507 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 17:27:43 2008 +0800 + + Fix IO#dup to raise IOError on closed stream + +commit 879ee8124a2ad8ce83bcd9c51b2d6df0baecb40d +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 16:29:03 2008 +0800 + + Fix a bunch more IOError when closed stream + +commit 487d9561992eb03c3d12de5128772cd194b37b8b +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 16:26:33 2008 +0800 + + Implement IO#read_nonblock + +commit 15c58fa2c47d2dc61b3dac436ab3b56a727b7dc5 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 15:46:22 2008 +0800 + + Fix remaining IO.read specs + + * Passing nil to length treats it as no length limit + * Passing nil to offset treats it as 0 + +commit 9daee4f9c3b62db34b07d74171d1017fa823533c +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 15:32:13 2008 +0800 + + Fix IO#sync to raise IOError on closed stream + +commit 2ac848c09e055b3eacc8bb18f713d56715484063 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 15:24:36 2008 +0800 + + Fix IO#sync to raise IOError on closed stream + +commit 36aa8577603f1d8ca76344fc3e889bb7c991bfe9 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 14:51:13 2008 +0800 + + Fix IO#sysseek to raise IOError on closed stream + +commit 3307f5a4db121c2097b450278bc3cf19550f267b +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 14:48:30 2008 +0800 + + Fix IO#pos and #IO#tell to raise IOError, move their specs to shared + +commit 72890065371f3e1d1cde43618a3da04c900749aa +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 14:39:21 2008 +0800 + + Implement IO#to_io + +commit 4977bd1f22278e19ba69203c2545ad97c297ae23 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 14:33:03 2008 +0800 + + Remove IO#isatty tag file also, since they are sharing the same specs + +commit 5dd3115465852ddb03b7100b21739f9d38f0ee58 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 14:29:13 2008 +0800 + + Fix IO#tty? should raise IOError on closed stream + +commit 063f56b4c402180c2c989a15b75fe7a15d4c5c61 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 13:55:55 2008 +0800 + + Make IO#syswrite use the shared IO#write specs + +commit 22de413f6cccb3eb100fd29da90c2ded84ea19f3 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 12:08:50 2008 +0800 + + Update IO#write_nonblock's tag + +commit 25a5ac7e9123512e87e6460f1fa5ecbcfc7349b5 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 12:07:22 2008 +0800 + + Pull out 2 differences between IO#write and IO#write_nonblock specs + +commit a40dbd0f36f0237bc27c905c399aba1e62bbfa70 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 11:39:03 2008 +0800 + + Alias IO#write_nonblock IO#write and make IO#write specs shared + +commit 1c8eb4bc04405753dd607af1f5d231df01fd2536 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 11:06:09 2008 +0800 + + Make the mock return a string to prevent a coercion error + +commit a85b2105c826a7d39dc45c90cad37faf75baac86 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 10:39:00 2008 +0800 + + Fix IO#write should raise IOError on closed stream + +commit e8c8af1aa888dc3e5600cad64f03c09aebaf6d22 +Author: Kamal Fariz Mahyuddin +Date: Thu Mar 13 10:34:08 2008 +0800 + + Fix IO#to_i should raise IOError on closed stream + +commit 49d48c381b7ed0f2576c2c5bff3ac8825a0dd49e +Author: Ryan Davis +Date: Wed Mar 12 17:56:26 2008 -0700 + + Fix the insanity + +commit 646136d0f75b165a3a62266791556d3f4f03c835 +Author: Ryan Davis +Date: Wed Mar 12 17:04:30 2008 -0700 + + Finally got compiler specs passing + +commit 052bbcbe4f51b322ae44dc387320f9b4964d74cd +Author: Wilson Bilkovich +Date: Wed Mar 12 16:23:58 2008 -0700 + + Correctly set Syslog mask in Syslog::open and add crappy spec for it + +commit dbabc5bda94a2bd77b2cb777666d286155c75ee0 +Author: Wilson Bilkovich +Date: Wed Mar 12 14:55:08 2008 -0700 + + Correct Syslog specs and modify syslog.rb to pass them + +commit 5b8bee08f2a19d6f25df98183a24745ed33ed519 +Author: Wilson Bilkovich +Date: Wed Mar 12 14:54:38 2008 -0700 + + Modify Kernel#load specs so that they pass on 1.8.6-p111 + +commit b96974693cee75772b09052f8ec7110a000c2429 +Author: Ryan Davis +Date: Wed Mar 12 14:00:42 2008 -0700 + + Fixed specs for wilson's compiler encloser changes + +commit 2a21597719bea1ea7db27a552ea6dfb6865963d7 +Author: Ryan Davis +Date: Wed Mar 12 14:00:06 2008 -0700 + + half work on pretty_inspect + +commit 6e398ca491b67a6c468798fd92a9764f70bc68a8 +Author: Kamal Fariz Mahyuddin +Date: Wed Mar 12 15:18:02 2008 +0800 + + Share String#to_a a specs with String#entries + +commit 2ff775cbcf2ade4315fbdbb37fa78ee84a1e645a +Author: Kamal Fariz Mahyuddin +Date: Wed Mar 12 11:59:26 2008 +0800 + + Add String#to_a specs + +commit 4f1204bac224ad28375f06e5fb77156367895156 +Author: Wilson Bilkovich +Date: Tue Mar 11 19:53:32 2008 -0700 + + Spec and implement Array#pack 'v' option + +commit 91d51783f44c3a9b1adfe03b7b9fa35476494ce1 +Author: Adam Gardiner +Date: Wed Mar 12 12:45:56 2008 +1100 + + Debugger::Output#wrap should handle width of 0 + +commit 51c316464ad44cadad7ecd997ce45e8392695f4c +Author: Wilson Bilkovich +Date: Tue Mar 11 15:26:52 2008 -0700 + + Implement support for :postexe nodes (END { some_code }) + +commit 569dd9f10d5194c22335ce58a678d1f9c73f91d0 +Author: oleg dashevskii +Date: Wed Mar 12 03:54:39 2008 +0600 + + Import matrix lib with specs (#389 and #400) + + Original patches by Chris Lloyd and matta. + +commit 6beb50b7cc2dd3a0f57f3dee45767bb363082159 +Author: Brian Ford +Date: Tue Mar 11 13:20:07 2008 -0700 + + More Integer#times specs. + +commit 746d89d6d55c82f26be08f182301926efd62d362 +Author: Wilson Bilkovich +Date: Mon Mar 10 15:39:26 2008 -0700 + + Correctly set the enclosing class for evaled code. + +commit 218cc7fbdd1b5d1c52248e65817752b8a50821ad +Author: Vladimir Sizikov +Date: Mon Mar 10 04:46:00 2008 +0100 + + Added JRuby speciifc guard to singleton rubyspecs (JRUBY-2239). + +commit 25e3f23e78f2b17e02d2c0a058925f8a0ec0d790 +Author: Dirkjan Bussink +Date: Mon Mar 10 11:25:38 2008 +0100 + + Be sure not to close the socket before the Errno.handle call + + Also a small fix for a spec that fails on OpenBSD + +commit 9e7fdf3b0040971f7b8402b9cf5422efaedb2f4f +Author: Dirkjan Bussink +Date: Mon Mar 10 09:54:16 2008 +0100 + + Fix TCPSocket#new spec, BSD systems make a distinction between IPv6/IPv4 localhost + +commit e5512b2a7725a67471eba086b107b0f4b1f136b2 +Author: Dirkjan Bussink +Date: Mon Mar 10 08:35:50 2008 +0100 + + Fix for failing unpack_sockaddr_in spec on Linux + +commit b9eab2266e5d1f073b6f876710dc9e848fe25b0c +Author: Dirkjan Bussink +Date: Sun Mar 9 23:37:05 2008 +0100 + + Remove spec tag for now fixed Hash.allocate + +commit b6ba9a757b0531791424df38bce6587a53db6002 +Author: Dirkjan Bussink +Date: Sun Mar 9 23:12:16 2008 +0100 + + Remove tag for now correct Fixnum#[] spec + +commit 6785c2b44da90d95ef77e98cba42a953828b622f +Author: Dirkjan Bussink +Date: Sun Mar 9 23:11:37 2008 +0100 + + Fix Fixnum#[] specs + +commit 0aa09ce9b7269d54cdef583a2eaf0cb57c32f773 +Author: Dirkjan Bussink +Date: Sun Mar 9 23:05:21 2008 +0100 + + Removed tags for working Socket specs + +commit 18b27b0ebdc3713962771ca75c1321cabee08d61 +Author: Dirkjan Bussink +Date: Sun Mar 9 22:48:45 2008 +0100 + + Untag now working IPAddr specs + +commit f4c0d08bec8fb2db7d130363b0609de7b7720d7e +Author: Dirkjan Bussink +Date: Sun Mar 9 19:56:25 2008 +0100 + + Slow IPAddr specs are now fast + +commit ff71385a67b2853130e63f9942bcea6ac69d591f +Author: Eero +Date: Sun Mar 9 10:35:27 2008 -0400 + + Specs for #412. Array#sort and #sort block form calls #<=> on elements. + + * Block form should not expect anything of the elements, all is + done through the return value of the block. + +commit e6edd1bb4bc52053bdb834d52e31fa185f2a2d62 +Author: Dirkjan Bussink +Date: Sun Mar 9 14:51:00 2008 +0100 + + Updated tags for IPAddr because of fixed bit operations + +commit 4f59fa9bd187822cd836aa046bb8fd40e4412c30 +Author: Dirkjan Bussink +Date: Sun Mar 9 14:49:02 2008 +0100 + + Fix Fixnum and Bignum shift operations to match MRI + + Added behavior for the edge cases, but took a different + approach than the LH tickets. I don't think we should + change coercion functions for this. + +commit ad8c630662dcb611cd955db08a6f4d53d1dc0dfd +Author: Dirkjan Bussink +Date: Sun Mar 9 13:43:38 2008 +0100 + + Fix Bignum#& and specs for Fixnum AND, OR and XOR + +commit 2529acd5e1cc8e61bd995e00834ee1f6941b1d9d +Author: Eric Hodel +Date: Sun Mar 9 14:18:04 2008 +1100 + + Fix require_spec and load_spec. + +commit 57c7ded8e4d9567aa3c392e8a8262389387ebbfb +Author: Eric Hodel +Date: Sun Mar 9 12:25:40 2008 +1100 + + Don't spec .rba require behavior in spec/ruby/1.8. + +commit ac630b23da01dcc3a1de1bfa06bac4d301a5031b +Author: Wilson Bilkovich +Date: Sat Mar 8 17:23:34 2008 -0800 + + Better fix for calling to_proc on BlockPass nodes + +commit c17b32d44be8452cd867a8212a0fd8bb49c94821 +Author: Wilson Bilkovich +Date: Sat Mar 8 16:34:02 2008 -0800 + + Tag failing Method spec for CI + +commit c5d4a3b8f84b7558a5dfedb699a1a3ee4d61f118 +Author: Wilson Bilkovich +Date: Sat Mar 8 16:26:58 2008 -0800 + + Call Proc.__from_block__ on block_pass arguments + +commit a63f457821e67d138d9cf1c5ac8b0760cb25bfc2 +Author: Eric Hodel +Date: Sun Mar 9 10:42:51 2008 +1100 + + Remove support for zip rba files, libzip. rake clean required. + +commit 142222e41bddd2138d82f349f73dbc0fe2cf3fc2 +Author: Charles Nutter +Date: Sat Mar 8 16:23:37 2008 -0600 + + Adding a spec for Method#to_proc proc used in define_method. + +commit b748efa9904baf0be26aa5b7297fc8ba76e46a74 +Author: Wilson Bilkovich +Date: Sat Mar 8 13:09:44 2008 -0800 + + Fix Module#method_defined? and friends for accessors + +commit 9b9d8216014c95eb7b4a925e93d0db8e9f5fd308 +Author: Vladimir Sizikov +Date: Sat Mar 8 10:46:18 2008 +0100 + + Adedd a couple of GzipReader#rewind specs. + +commit 4612812bde4a2fccbaa72ea54ef76c7d964d216b +Author: Dirkjan Bussink +Date: Sat Mar 8 15:49:57 2008 +0100 + + Fix the Array#pack specs, network order is the same everywhere + +commit a720bba1619deb4358b453f58913d30a1a311b07 +Author: Dirkjan Bussink +Date: Sat Mar 8 15:27:47 2008 +0100 + + Fix Sprintf for positive non decimal notation + + This fix combined with the pack/unpack implementation for + type n also fix some IPAddr specs. + +commit caef838aca82665d4c2f691e4873e339a9c7238d +Author: Lachie Cox +Date: Sat Mar 8 12:47:19 2008 +1100 + + updated Array#pack specs to work on big endian machines + + Signed-off-by: Dirkjan Bussink + +commit e3763469a224b4b3668bc1ddef2d982245787646 +Author: Lachie Cox +Date: Sat Mar 8 12:42:36 2008 +1100 + + Added implementation of pack schemes for "n" and added handling of multiple items for "i","s" and "l" + + Signed-off-by: Dirkjan Bussink + +commit 41b26c49f5a16377af2c677eb702d665dd062a56 +Author: Eric Hodel +Date: Sat Mar 8 15:35:10 2008 +1100 + + Fix IO#pos EOF spec. Pair: Lincoln, Evan. + +commit 1e039fb5c9bcff987769c8644ec47c30aa250952 +Author: Eric Hodel +Date: Sat Mar 8 14:53:01 2008 +1100 + + Fix Zlib::GzipWriter#finish. Pair: Lincoln. + +commit 8551da47a01ef24eaf31fac55253fb05fe81cfcd +Author: Eric Hodel +Date: Sat Mar 8 14:21:08 2008 +1100 + + Add Zlib::GzipReader #eof?, #pos, #read w/length + +commit a4dba8317311cc3a51231895b2eaea09daaa61be +Author: Eric Hodel +Date: Sat Mar 8 10:41:33 2008 +1100 + + Ensure #pos clears internal eof flag + +commit 407e1a4191da6ecd59c1347198a60be2556e043b +Author: Brian Ford +Date: Fri Mar 7 17:04:09 2008 -0800 + + Tweaks to LookupTable. Converted Errno::Mapping to use LT. + +commit eb937c8f1041884e412e3d074387ca9f14bb03ef +Author: Brian Ford +Date: Fri Mar 7 13:48:45 2008 -0800 + + Fixed LookupTable#delete. Added LookupTable#entries, #dup. + +commit d7d9bfd01180cf2c4fc74d2709f71fc7dd59f2f6 +Author: Brian Ford +Date: Fri Mar 7 15:03:14 2008 -0800 + + Bandaid fix for failing #autoload specs. + + These need to be properly scoped. However, changing + :A to ModuleSpec:A causes a sigbus. + +commit aea5cc446cd2c1b0cbd29e606b21b6d5959eb5ee +Author: Caleb Tennis +Date: Fri Mar 7 16:18:19 2008 -0500 + + Add rb_gv_get and rb_gv_set, plus specs. + + Add rb_set_safe_level, rb_secure, and rb_safe_level methods, and specs. + +commit cd0b8969487af84a4f40466714dab2d5a1efc224 +Author: Ryan Davis +Date: Thu Mar 6 17:11:20 2008 -0800 + + excluded + +commit e40f2bb09d8e3137de2856cb1e9c9438945603dc +Author: Ryan Davis +Date: Thu Mar 6 17:11:00 2008 -0800 + + More specs to test out const scoping with eval + +commit 3926add9039d1af4a60b633ef8805d471f28e02f +Author: Ryan Davis +Date: Thu Mar 6 17:01:21 2008 -0800 + + Further clarified StringIO#getc specs. + They weren't really testing what they were doing. + Fixed StringIO#getc. now properly pushes single chars and sets @pos so it can be mixed with puts/write as needed + +commit d2d3750c4960d4a6f2a5d2b16b8bae3d598fbe36 +Author: Caleb Tennis +Date: Thu Mar 6 19:16:24 2008 -0500 + + Add rb_define_global_function to subtend, with tests + +commit 4ab5cc17b70b6569cf9311142d4b278dedfd0a64 +Author: Brian Ford +Date: Thu Mar 6 09:53:20 2008 -0800 + + Added LookupTable and specs. + +commit 1ca8a272137ed7020cb977bf51dd2b7164ccbd7e +Author: Charles Nutter +Date: Wed Mar 5 17:28:40 2008 -0600 + + TCPServer.new coerces non-integer port to string and uses getservbyname logic. + +commit f0c03880972c19d1a12367dc51ed77f69d9ce8ca +Author: Charles Nutter +Date: Wed Mar 5 16:44:33 2008 -0600 + + Add a couple specs for killing/raising in a thread blocked on accept. + +commit 9f80ef157851671727653f46225b99af5d1a259e +Author: Vladimir Sizikov +Date: Tue Mar 4 21:26:33 2008 +0100 + + Proper spec for %u with negative bignums and comments on MRI behavior. + +commit 3f9c36081c9b62bcde40206e64afdc2ac088bee8 +Author: Dirkjan Bussink +Date: Tue Mar 4 19:09:56 2008 +0100 + + Update tags for fixed File#chmod specs + +commit 735e818c38f8cefe0cd90514dac5282845a67dd4 +Author: Dirkjan Bussink +Date: Tue Mar 4 15:13:23 2008 +0100 + + Improve testing of coercion in File#chmod specs + +commit 77a717f5962b2965ad9146e16cb36bedac891c80 +Author: Vladimir Sizikov +Date: Mon Mar 3 18:16:04 2008 +0100 + + Adjusted syslog specs to better handle impls that don't provide syslog. + + For example, JRuby does not provide syslog (yet). + +commit 605bdc53e9dd4fb95dae6557d9ee6f9e2b8ceb80 +Author: David Whittington +Date: Mon Mar 3 08:44:33 2008 +0000 + + Modified Bignum threshold specs to take into account platform wordsize + +commit 0af27d11d7dd68cfe49985dc4588933cc41f4fc8 +Author: Wilson Bilkovich +Date: Sun Mar 2 16:40:15 2008 -0500 + + Tag headius's new to_proc spec as failing + +commit b1caeeac673451a960917bb699a20e74cf488432 +Author: Vladimir Sizikov +Date: Sun Mar 2 13:30:35 2008 +0100 + + Adjusted Kernel#catch test a bit, to make it more generic. + +commit 60f9544ade9d6e71fe3e423ab82cc87838478032 +Author: Charles Nutter +Date: Sun Mar 2 04:36:53 2008 -0600 + + Add a spec for #363, & not coercing using to_proc. + +commit 70aa320f7f5bc75ed95362b0fb6d724e64224a88 +Author: Wilson Bilkovich +Date: Sat Mar 1 17:17:55 2008 -0500 + + Tweak new Marshal spec to pass on MatzRuby + +commit 35476e1bde23de26c01df409b750e91ef981fefc +Author: Wilson Bilkovich +Date: Sat Mar 1 17:11:53 2008 -0500 + + Tag new failing Marshal spec + +commit d9f83819f1ed2505740ae0737199fecab29809bb +Author: Jared Luxenberg +Date: Sat Mar 1 16:20:18 2008 -0500 + + Added specs for marshalling subclasses of Hash with init parameters + + Test that Marshal.dump gives correct output for such an object (passes) + Test that Marshal.load is able to deal such an object (fails) + +commit 6039a3bd457c5d3dc99f5935999da574d17f1e5d +Author: Wilson Bilkovich +Date: Sat Mar 1 16:20:08 2008 -0500 + + Tweak Process.setrlimit spec for odd Linux platforms + +commit 25cfa6a96315ee203d06381ee3ddb76b60023360 +Author: Chuck Remes +Date: Sat Mar 1 10:24:55 2008 -0600 + + Fixes a race condition on OSX when "find"-ing character devices + + - on OSX the spec fixture would return /dev/fd/0 as a character + device when run from the command line. This always succeeded. + When run as a subprocess (like from cron or rubuildius' + IO#popen) then OSX uses /dev/fd/0 and /dev/fd/2 for capturing + stdin, stdout, stderr and others in that environment. While + the fixture would "find" /dev/fd/2 as a character device, by + the time the assertion tested it the underlying OS would change + it to another device type causing the assertion to fail. This + is just bad luck. We now grab the #last device found rather + than the first. + +commit b6e95321df023ac989c4e5bb926ec55493260bc9 +Author: Vladimir Sizikov +Date: Fri Feb 29 16:04:10 2008 +0100 + + New rubyspecs for IO#ungetc. + +commit 9bd2f0740c71d426cfa3c3636c2451762f640c14 +Author: Brian Ford +Date: Thu Feb 28 21:59:35 2008 -0800 + + Specs for Hash.allocate. Fix awaits replacing Hash with LookupTable in core. + +commit c1d979639bfc19072351211815ffd5c8da772dcd +Author: Brian Ford +Date: Thu Feb 28 21:56:33 2008 -0800 + + Specs and fixes for Module.allocate. + +commit 904fd6136f00bab5fec62e8e702a0508dec44bac +Author: Brian Ford +Date: Thu Feb 28 19:45:39 2008 -0800 + + Specs and fixes for Array.allocate. + +commit 776a24f0d14bbb5127c804cf0579960335c1a049 +Author: Brian Ford +Date: Thu Feb 28 19:35:55 2008 -0800 + + Specs for String.allocate and fixes to make them pass. + +commit fa35211f357ff1b9660a318c12b86ca156c5f26d +Author: Ari Brown +Date: Thu Feb 28 20:27:55 2008 -0500 + + Moved stdlib/syslog.rb to lib/syslog.rb . it works! + + * everything runs! yay! + +commit 8f103a6f9d7a168e37d1063e40bee960d64fc609 +Author: Ari Brown +Date: Thu Feb 28 19:42:11 2008 -0500 + + Added specs and the constant module for stdlib/syslog.rb + + * added some specs for that which is testable + * fixed the constant module so the constants are defined + * fixed 'undefined method' problem in #write (private) + +commit 0c89dc90fdcb7933169e23462197d59f9627f510 +Author: Ryan Davis +Date: Thu Feb 28 14:31:05 2008 -0800 + + Added basic throw/catch specs. Fixed raised NameError to contain the name + +commit c8f4db4270984b60a087dd423c9e0da3e3760622 +Author: Phil Hagelberg +Date: Thu Feb 28 14:11:30 2008 -0800 + + tag failing proc spec + +commit a1591319696385191f3301516d2f8265cd8fedcb +Merge: f167f8f... 3f1acce... +Author: Phil Hagelberg +Date: Thu Feb 28 13:43:52 2008 -0800 + + Merge branch 'master' of git@git.rubini.us:code + +commit f167f8f6f7f3a1b8804a5452643236a23c0ce4c4 +Author: Phil Hagelberg +Date: Thu Feb 28 13:43:40 2008 -0800 + + failing spec for returning from procs + +commit 3f1acce781c0dcf43698441036a085a0cef02d29 +Author: Wilson Bilkovich +Date: Thu Feb 28 16:14:55 2008 -0500 + + Basic support for UNIXSocket and UNIXServer + Fix some 'Errno' typos in socket.rb + +commit afbf38613364436630933753d99ee94c03b85074 +Author: Vladimir Sizikov +Date: Thu Feb 28 21:34:10 2008 +0100 + + Added specs for File.fnmatch with case-sensitive brackets. + +commit 28323bda3d1f3295371b6ea99ed8ba6ee15661bb +Author: Vladimir Sizikov +Date: Thu Feb 28 20:47:51 2008 +0100 + + Added specs for File.fnmatch with '**/' patterns. + +commit 893ff4729d024198d5b423cc4426153f49cb5ebe +Author: Brian Ford +Date: Thu Feb 28 11:30:49 2008 -0800 + + Fixed lookup of class variables defined in metaclasses. + +commit dee531b18d96199d608d8e2e8e27f54ef500a716 +Author: Brian Ford +Date: Wed Feb 27 21:47:13 2008 -0800 + + Additional Symbol#inspect specs. Another try at making them pass. + +commit 3bfb705b709ab35593684a68b35fb0ee8e1e01d7 +Author: Brian Ford +Date: Wed Feb 27 21:46:37 2008 -0800 + + Silence 'woot' echo on ubuntu from #system specs. + +commit 7fb76f2c4a9fb0c5695a38b90150ea6f50097237 +Author: Ryan Davis +Date: Wed Feb 27 18:05:48 2008 -0800 + + Fixed Symbol#inspect from over quoting + +commit 4ac32e4c9d0ff55aad50a00944f1a64931cfd1c6 +Author: Ryan Davis +Date: Wed Feb 27 17:18:09 2008 -0800 + + Added some pretty rude specs for Kernel#system and got them to pass. + Fixed a wierd problem with system/exec not cleaning up the fork process right + +commit 73be3b88af1ac96a6d4afabddd2871cfc4691eec +Author: Brian Ford +Date: Wed Feb 27 15:36:17 2008 -0800 + + Fix String to properly initialize backing store when subclassed. + +commit 5ab2f9e594b7e66a04028e60f3517488e345f508 +Author: Brian Ford +Date: Wed Feb 27 15:04:09 2008 -0800 + + Scope classes used in String specs. + +commit e45d58100850443fedada905f654bae3f4144790 +Author: Charles Nutter +Date: Wed Feb 27 17:04:58 2008 -0600 + + Add /devices to find commands; Solaris uses /devices instead of /dev. + +commit 1403477197873d613cfb93d644f78b4067d180d3 +Author: Vladimir Sizikov +Date: Wed Feb 27 20:21:48 2008 +0100 + + Adjusted Env spec, to be able to run it on Solaris. + + grep is replaced by egrep, since older greps don't + take -e parameter (like on Solaris). + +commit b239a3b615d341f982a7a4a3a1b1200d95f79684 +Author: Adam Shelly +Date: Wed Feb 27 04:09:24 2008 -0500 + + Amending specs for Array#pack('U') + + * rbx is now passing most specs + * failing specs are due to String#unpack. + + Signed-off-by: Brian Ford + +commit 328c40e0f24601e739f404ab252652deca477513 +Author: Brian Ford +Date: Wed Feb 27 02:46:21 2008 -0800 + + Fixed Array instantiation to work with subclasses. + +commit 96c4ea885fbd075765b9d234de2754df3c857b07 +Author: Adam Gardiner +Date: Wed Feb 27 09:26:25 2008 +1100 + + Move Debugger::Output specs to match new location of class + +commit c59f16f34f47860b200c6de4a2c1144c566de3dd +Author: Brian Ford +Date: Tue Feb 26 10:23:14 2008 -0800 + + Exclude new failing Array specs. + +commit 27248a45f079fd5a8cdb9ee71d008d135dcbe63d +Author: Charles Nutter +Date: Tue Feb 26 00:10:29 2008 -0600 + + Add additional Array tests from BFTS. + +commit a0e156f4c5bc12bf39950afeb58a6962b37efaa7 +Author: Adam Gardiner +Date: Tue Feb 26 16:53:01 2008 +1100 + + Fix Debugger help output formatting to use wrapping + +commit fa5304d42c72a07b09cece99cb22c90f6b399a51 +Author: Adam Gardiner +Date: Tue Feb 26 13:22:31 2008 +1100 + + Add wrapping to debugger column output + +commit 1a5d830b41eef37bb78168c959dd5b2f0757fde4 +Author: Brian Ford +Date: Mon Feb 25 18:58:11 2008 -0800 + + Conform Bignum#div, #divmod to weird MRI maths. + +commit eb5c6e367990bfdd193bcdf3055009f3e3e1aeaf +Author: Adam Gardiner +Date: Tue Feb 26 13:23:29 2008 +1100 + + Fix Debugger specs to pass on ci + +commit a2feff6782a052a9b71da90e9d4e1b2d991cc598 +Author: Wilson Bilkovich +Date: Mon Feb 25 18:53:24 2008 -0500 + + Patch by Jos Backus (josb) - Closes ticket 364 (FreeBSD warnings) + +commit cee08883cc3de2e41a88b506f7d7f8d40697eaa2 +Author: Ryan Davis +Date: Mon Feb 25 13:20:21 2008 -0800 + + Fixed autotest churn by removing empty.txt and moving to /tmp + +commit f26bb0c4e3b8435a853a9f4843173748d98075fd +Author: Brian Ford +Date: Mon Feb 25 11:25:10 2008 -0800 + + Add the rest of spec/* directories to CI process. + +commit 29f36833e79de6115c27d744adf158e1b3ba42f0 +Author: Brian Ford +Date: Mon Feb 25 00:29:45 2008 -0800 + + Excludes for spec/kernel, spec/debugger to run with CI. + +commit 12bbdf70af31d5168c2df0a9b53651f94b36899d +Author: Brian Ford +Date: Mon Feb 25 00:22:13 2008 -0800 + + Excludes for subtend specs so they will run with CI. + +commit 0cbc2b1f20d8aee7ea74eb14e1f9cf242f8b47d5 +Author: Brian Ford +Date: Sun Feb 24 23:57:56 2008 -0800 + + Remove specs for removed Compression::ZLib. + +commit 1b4fbc76c2eb84e5cb45562f54ac105784f9e134 +Author: Brian Ford +Date: Sun Feb 24 23:49:57 2008 -0800 + + Conform Ar specs. + +commit f8e62002711c3cfd8024faca497775f7253a326a +Author: Charles Nutter +Date: Mon Feb 25 05:24:54 2008 +0100 + + Add a second case for truncating IO buffers that specifies too-small size. + +commit 9f3e25289cc52cd3f3fb240de1ad82a16a8b135c +Author: Nikolai Lugovoi +Date: Tue Feb 12 23:19:27 2008 +0200 + + Fixes for String#to_sub_replacement: + + * removed String#replace_slashes + * using plain byte-by-byte scan instead of regexps to detect and handle backslash escapes + * better handle unknown escapes and cases like '\\\1' + * updated specs for String#sub + + Signed-off-by: Brian Ford + +commit d87df0b7634ae37f85fc8f2795e4c8c425614b11 +Author: Charles Nutter +Date: Mon Feb 25 02:57:27 2008 +0100 + + Add a spec for Enumerable#inject with a *arg; JRUBY-2162 exposed it. + +commit f04fcabf8c064dfcbf3b118bdc83289da169a30c +Author: Dirkjan Bussink +Date: Sun Feb 24 21:24:51 2008 +0100 + + truncate behaves different on OpenBSD, changed specs according to MRI behavior + +commit b74a2f45b32a02469d61d4ace04912ec25f19543 +Author: Dirkjan Bussink +Date: Sun Feb 24 20:18:02 2008 +0100 + + Looks like Darwin does provide Process::RLIMIT_AS + +commit 7113973abff64eeb1304b15be46f07d301d84f3f +Author: Dirkjan Bussink +Date: Sun Feb 24 18:25:55 2008 +0100 + + OpenBSD doesn't provide Process::RLIMIT_AS, so this spec should be excluded + +commit 49b72719bf5c732f4aa2ad0d70e5a224556fb471 +Author: oleg dashevskii +Date: Sun Feb 24 11:04:07 2008 +0600 + + Spec for method taking lambda and block. + + * should raise SyntaxError + * passes on MRI + * fails on rubinius + + Signed-off-by: oleg dashevskii + +commit 60bbc8506d70571249972dbf124df520f0a4a476 +Author: Chuck Remes +Date: Sat Feb 23 10:23:09 2008 -0600 + + Fix unpack_spec expectation for little-endian byte ordering + + Signed-off-by: Dirkjan Bussink + +commit 17e45cee97057684e6c24608f97de48c28947384 +Author: Chuck Remes +Date: Sat Feb 23 09:44:37 2008 -0600 + + Fix unpack to use native host byte order for formats /ILQS/ + + - unpack_spec had a bad expectation on little-endian platforms + - unpack_spec got some updated description strings to correctly identify + the host byte ordering expected in the spec + - kernel/core/string.rb now unpacks formats /ILQS/ in the platform's native byte + ordering + + Signed-off-by: Dirkjan Bussink + +commit 1540cb7caa0f200ed6d318971fb7302cd089e27d +Author: Chuck Remes +Date: Sat Feb 23 08:10:00 2008 -0600 + + Add some missing endian guards to the unpack_spec + + - in my haste, forgot one set of guards around some specs + + Signed-off-by: Dirkjan Bussink + +commit 8488676fc0dac5db5d01dd92e061476226d58bd1 +Author: Chuck Remes +Date: Sat Feb 23 00:19:33 2008 -0600 + + Fix several Array#pack and String#unpack bugs related to byte ordering (endiannes) + + - added a small utility method endian? to the kernel module; determines host byte + ordering by taking a symbol (:big, :little) and comparing it to Rubinius::ENDIAN + - modified Array#pack to check for the native byte ordering for /ils/i formats + - modified String#unpack to use native byte ordering for /DdFfIiLlQqSs/ formats + - modified String#extract_number to do special processing for big-endian platforms + and for formats using native byte ordering on a big-endian platform + - added little_endian and big_endian guards around several String#unpack specs; + now passes running against MRI and rbx + + Signed-off-by: Dirkjan Bussink + +commit f8146d29bfdf67349f3f9c0c7105ce595981255f +Author: Gianluigi Spagnuolo +Date: Sat Feb 23 12:44:25 2008 +0100 + + Added some test to Regexp.quote to manage tab and white space + + Signed-off-by: Dirkjan Bussink + +commit 714efa8574687e1fd31f904a4f35cce8056719f5 +Author: Brian Ford +Date: Sat Feb 23 00:01:38 2008 -0800 + + Fixed Digest specs to pass with RSpec. + +commit a0fe2f7fa080729b77b32ffe21be5705a162ed71 +Author: Eric Hodel +Date: Fri Feb 22 22:26:52 2008 -0800 + + Remove ffi_decode_sockaddr, replace with existing ruby code. + +commit b2baf0911e4a88ba2f6c4cb8e3e31d2a3aa1c6bf +Author: Eric Hodel +Date: Fri Feb 22 17:06:18 2008 -0800 + + Move Ar to kernel/core. + +commit 01baf002a8c7bd6e249b9477c1f78e6b99a67bf6 +Author: Philipp Bruschweiler +Date: Wed Jan 16 00:11:12 2008 +0100 + + added specs for SHA256/384/512 + + these specs were as well shamelessly copied from the md5 specs. + they work, but every sha* class has a folder for itsself, that's a + lot of duplicatd code. maybe someone with more experience in + writing specs should have a look at this. + + Signed-off-by: Dirkjan Bussink + +commit 924224fcf655da90148ebd8234033a71e1b23090 +Author: Caleb Tennis +Date: Fri Feb 22 17:13:14 2008 -0500 + + Catch no block given in rb_yield, raise LocalJumpError + + As well, define that as an exception for subtend + + Update spec + +commit 3748843421832df5b842a677ddd2e55fbefb0b5f +Author: Caleb Tennis +Date: Fri Feb 22 17:04:33 2008 -0500 + + Update rb_yield spec + +commit f60ca442b1466f29432995700457e8b34f4ff294 +Author: Caleb Tennis +Date: Fri Feb 22 17:00:36 2008 -0500 + + Fix rb_yield call + +commit a75afc4595fd20d7853ff65afe015de88b265b93 +Author: Caleb Tennis +Date: Fri Feb 22 16:48:17 2008 -0500 + + Add blocks to subtend methods, as they should be able to access them like any other method. + + Also, update the spec + +commit d9911f8b00243f3c95759612dde35edf6edaa678 +Author: Caleb Tennis +Date: Thu Jan 31 13:54:24 2008 -0500 + + Add block specs and rb_block_given_p + +commit b6c806f0d8213c4751c69638174f60b80f9ba303 +Author: Wilson Bilkovich +Date: Fri Feb 22 15:31:58 2008 -0500 + + Failing spec and exclude for left-to-right masgn evaluation order + +commit 8f9e3c9e5e7dfc535e8fe6b10b945587586651ec +Author: Dirkjan Bussink +Date: Fri Feb 22 13:59:44 2008 +0100 + + Fix Socket#getservbyname, not every platform defines http/udp + +commit f29ff3bcaf0bf83d2924d08ea5f6c0bbb5df9948 +Author: Eric Hodel +Date: Thu Feb 21 16:47:02 2008 -0800 + + Allow Ar to create archives + +commit e50ec6470dfc905198065a98b65b33a99da60e15 +Author: Wilson Bilkovich +Date: Thu Feb 21 20:20:41 2008 -0500 + + Some compiler specs for 'defined?' handling + +commit ba5a0d87182d83000205e1202f5c473568a50489 +Author: Dirkjan Bussink +Date: Fri Feb 22 01:08:51 2008 +0100 + + Fixed #332 and cleaned up Time a bit. Thanks to gls + +commit edf1e0d530ebb39a1b46d0fa518b9ca85db544da +Author: Evan Phoenix +Date: Thu Feb 21 02:01:21 2008 -0800 + + Fix the last usage of block return (ie, internal long return). + + * LongReturnException is now used whenever a block requests that + it's home context should return. + +commit 83ed7161701202d48490e7f38b568bc504f9690f +Author: Brian Ford +Date: Wed Feb 20 23:59:47 2008 -0800 + + Added little/big_endian guards to Array#pack and String#unpack specs. + +commit 65b4ed86002371f2b56759aadc61e61c1cbbdba4 +Author: Brian Ford +Date: Wed Feb 20 22:49:19 2008 -0800 + + Exclude Socket#unpack_sockaddr_in spec. See tag comment. + +commit 9fbda05c4dffb964a9f10e26d62240fbd52200a0 +Author: Brian Ford +Date: Wed Feb 20 22:48:31 2008 -0800 + + Exclude super slow IPAddr specs. + +commit 3d39fb35dcd3c28fa626aeb96057b927c6bfe7c9 +Author: Brian Ford +Date: Wed Feb 20 18:54:50 2008 -0800 + + Redo expectation in Socket#getaddrinfo spec. + +commit 69576ede38d9bf09d1afd0120726ca756a0aa7cf +Author: Brian Ford +Date: Wed Feb 20 18:31:27 2008 -0800 + + Account for variable length array in Socket#getaddrinfo. + +commit f396bd718572d9402d0d7eeb8da02474914396a8 +Author: Brian Ford +Date: Wed Feb 20 17:59:29 2008 -0800 + + Use File.delete in YAML specs instead of rm. + +commit 7698ec3855ce572f1e10962596804b82f3cd6534 +Author: Adam Gardiner +Date: Thu Feb 14 10:07:48 2008 +1100 + + Hook-up new StepBreakpoint to new debugger step commands + + * The commands step and stepi have now been added to the + debugger, and step into called methods. + * The commands next, nexti and out have been converted + to use the new StepBreakpoint. The legacy versions + remain, but have been renamed as ln, lni, and lo; these + will be removed once the new commands have proven stable. + * Replaced VM method cache command with VM send site command + to show details of SendSites in the current method. + +commit f192d65ec5eb31b4a807b9c3eb7360b84739d9f2 +Author: Adam Gardiner +Date: Thu Jan 31 16:43:19 2008 +1100 + + Initial implementation of StepBreakpoint + + StepBreakpoint class moves step logic out of the Debugger + and into breakpoint, where it more logically belongs. + +commit fd0ff43d2d384e221ff8de611843f3406d192657 +Author: Brian Ford +Date: Wed Feb 20 17:04:46 2008 -0800 + + Fixed YAML spec to pass MRI. Added fails tag for rbx. + +commit d69834a5217ddc6667b495fbe7d4dd8ad413ba88 +Author: Wilson Bilkovich +Date: Wed Feb 20 15:42:25 2008 -0500 + + Fix dead code in TCPSocket.new specs + +commit 4644222e63046783933ca9b2e4514e3ff21fbb57 +Author: Brian Ford +Date: Wed Feb 20 12:31:14 2008 -0800 + + Add missing tag file for method_spec. + +commit 230d5d506f4203bcd3922880fae506fa480e6308 +Author: Brian Ford +Date: Wed Feb 20 12:17:13 2008 -0800 + + Fix typo in socket specs. + +commit a5d49537832a9cc33b07cade265af0834f123533 +Author: Brian Ford +Date: Wed Feb 20 12:09:11 2008 -0800 + + Move specs for calling methods to language/method_spec.rb. + +commit ead32a1f2820a4e2fcc906a8e7f3603490ba901c +Author: Brian Ford +Date: Wed Feb 20 11:53:45 2008 -0800 + + Use bignum_value where a Bignum is intended in the specs. + +commit 1021345337bca1f928879713cb84a76b9c7935a1 +Author: Brian Ford +Date: Wed Feb 20 10:21:42 2008 -0800 + + Removed unused require 'stringio' from io/syswrite specs. + +commit cfd51af482321b4d672d69569de185f582a21831 +Author: Brian Ford +Date: Wed Feb 20 09:00:36 2008 -0800 + + Symbols as Fixnums is long deprecated. We don't spec it. + +commit a8bd2a1aba97653625a9b568d1a7112b5fce45f6 +Author: Brian Ford +Date: Wed Feb 20 00:15:54 2008 -0800 + + RbYAML is not in Ruby standard lib. Move specs for it to spec/library/rbyaml. + +commit 56b454af2ded18d0459bc974efa666ccf3b8de0f +Author: Brian Ford +Date: Wed Feb 20 00:10:24 2008 -0800 + + Restrict specs in spec/ruby/1.8 to current stable 1.8 version. + +commit 22e01d1914db92d159ee15d3cf73c9d6e9d0a24b +Author: Brian Ford +Date: Tue Feb 19 23:20:27 2008 -0800 + + Fix Dir#pos=/#seek specs. We shouldn't spec undefined platform behavior. + +commit d522af83d0cfcdf39932afff7ba7d75d77dd0453 +Author: Vladimir Sizikov +Date: Wed Feb 20 11:51:44 2008 +0100 + + New IO.read specs. + +commit 77fdbe404e31f44e1c302eb99a7ff129523183ce +Author: Eric Hodel +Date: Tue Feb 19 16:14:43 2008 -0800 + + Add library to read/write ar(5) files + +commit d7702f979732de90358dc35d795c6ac621f815bc +Author: Matthijs Langenberg +Date: Mon Feb 18 18:04:27 2008 +0100 + + writen some examples for Base64 module + + Signed-off-by: Dirkjan Bussink + +commit 33b189478c05bd687ac8b062cd5307a3290d8931 +Author: Brian Ford +Date: Tue Feb 19 00:27:51 2008 -0800 + + Convert platform guard :size option to :wordsize. + +commit cbcdb8346a2c75ba65910b486cee718cd3aa5175 +Author: Brian Ford +Date: Mon Feb 18 23:07:41 2008 -0800 + + Exclude TCPSocket.new for now, hangs on ubuntu gutsy. + +commit ec990b6ebcd35cbf9dc192852f37e184c3e4079b +Author: Adam Gardiner +Date: Tue Feb 19 15:55:25 2008 +1100 + + Re-enable debug on context change + + The cpu_yield_debugger_check was not being performed as + a result of changes to method dispatch related to the + implementation of SendSite. + +commit aa585b7e637e2fd873602ee6725256429f413582 +Author: Brian Ford +Date: Mon Feb 18 18:59:18 2008 -0800 + + Removed :version guarded specs that are not current stable. + +commit 431af5920a0a02dfca927961a2d6457ae5f050e2 +Author: Brian Ford +Date: Mon Feb 18 17:40:56 2008 -0800 + + Added new tags files for excludes. + +commit 10dd37903533cac9a6f77ead70f3aa9ee1dc9098 +Author: Brian Ford +Date: Mon Feb 18 16:38:58 2008 -0800 + + Removed deprecated $deferr from getoptlong.rb. Moved to /lib. + + Small fixes to other library specs to get them running under CI. + +commit ee2dabf771a5e6d8d70c47fa49b1298d2d002c8c +Author: Brian Ford +Date: Mon Feb 18 00:42:54 2008 -0800 + + Use the spec guards properly. + +commit 91d6c64be8827768ba2e39b80a4eb81b9affc122 +Author: Brian Ford +Date: Sun Feb 17 22:40:21 2008 -0800 + + Deprecate #setup, #teardown in specs; use #before, #after. + +commit 6ba49012504c08973e1fb2fd1b9fce75c351d148 +Author: Brian Ford +Date: Sun Feb 17 22:00:56 2008 -0800 + + The #fails_on guard has been removed. Use #ruby_bug or tagged excludes. + +commit e24231f5c62c0b73768c7503f50b53e8ffc345d1 +Author: Brian Ford +Date: Sun Feb 17 21:08:01 2008 -0800 + + Renamed *_excludes.txt to *_tags.txt for specs. + +commit a1c707b517e13115692173bc2048309e74c00915 +Author: Brian Ford +Date: Sun Feb 17 20:45:14 2008 -0800 + + Hand merge recent excludes changes to spec/tags directory. + +commit 838bee7e99bb1179c9a3a7782dcab9c2b904e72e +Author: Brian Ford +Date: Sun Feb 17 00:17:51 2008 -0800 + + Moved excludes from spec/data to spec/tags. Added "fails" tags. + +commit 8ad91b03788d89ccd12fbcf19c06c9ef4f0cfee8 +Author: Brian Ford +Date: Mon Feb 4 19:19:00 2008 -0800 + + Misc fixes to get MSpec running specs. + +commit a683dd75786ab6c6a255c9bac399dc6be7aaa4b5 +Author: Tyler McMullen +Date: Sat Feb 16 23:39:38 2008 -0500 + + Add support for H and h to Array#pack. + + * Updated array/pack_spec with specs for H and h, separately + * Updated Array#pack to handle both with a single block of code + + Signed-off-by: Brian Ford + +commit b1d3ba9d10f6a9ea87d8cb9be21d0d432e973117 +Author: oleg dashevskii +Date: Mon Feb 18 01:18:24 2008 +0600 + + Update specs for calling methods. + + Nasty binding stuff (first noted in #293) got specced and put into excludes. + +commit 02225daa5cef4fa3f48cac73d4bf0f9d02f3ebe0 +Author: oleg dashevskii +Date: Sun Feb 17 23:20:08 2008 +0600 + + Cross-breed and update for, while & until language specs. Little fix for hash spec. + + The compiler drops out on "for @@var in 1..3", so this is commented out. + Variable scope stuff arrived into excludes. + +commit f43383a150131278d30535196e8da4e60dff97b1 +Author: Vladimir Sizikov +Date: Sun Feb 17 13:10:55 2008 +0100 + + New specs for RangeExceptions out of Fixnum and Array methods. + +commit 7d1c744d9c1ae50376be406a28e383a04ca6b4fc +Author: Vladimir Sizikov +Date: Sat Feb 16 13:49:11 2008 +0100 + + Corrected copy-paste error in recent fixnum specs. + +commit 08982321472008f7645212289d2624d19053ed7e +Author: Eric Hodel +Date: Fri Feb 15 21:18:53 2008 -0800 + + Fix IO#read for large files and small parts of files. + + Fix IO#read with buffer. + + Fix IO#eof? when buffer reaches eof. + +commit 1d07588d61b3835a6165c5de1f731277812cff79 +Author: Eric Hodel +Date: Fri Feb 15 19:11:42 2008 -0800 + + Add missing spec for IO#eof? and fix. + +commit e0a6c8e179e48b423b6eb142b27460cd86d0223b +Author: Eric Hodel +Date: Fri Feb 15 17:50:47 2008 -0800 + + (Last change was ok). Force check for data so #eof? works + +commit d7e67c257c213f9e25b3123ce85576feb71a0089 +Author: Eric Hodel +Date: Fri Feb 15 17:28:48 2008 -0800 + + Revert "Force a check for more data on the IO for IO#eof?" + + This reverts commit 3d4427e802756678608bf9840ba6f26fc81cf7fe. + +commit 4c1182c184bb6c2c97c5fc8ce83f242fe5f5144b +Author: Eric Hodel +Date: Fri Feb 15 17:26:08 2008 -0800 + + Force a check for more data on the IO for IO#eof? + +commit 94466db3347889850feb25dd7c83883df21bac92 +Author: Brian Ford +Date: Fri Feb 15 14:13:29 2008 -0800 + + Added Float examples to Bignum bitwise operator specs. + +commit 3a668451d3bcc46b162a69ce1f8ec5d6a98b2d22 +Author: Brian Ford +Date: Fri Feb 15 13:44:24 2008 -0800 + + Added bignum_value helper. Added specs for Fixnum bitwise operators. + +commit 217eb67a4c2f0bf1222628abfecfadbede5fb3b8 +Author: Dirkjan Bussink +Date: Fri Feb 15 21:09:43 2008 +0100 + + Fix process specs for FreeBSD + +commit f25e0e130110ebbef0b5bc0c28c9b08db6c73a1f +Author: Dirkjan Bussink +Date: Fri Feb 15 13:18:40 2008 +0100 + + Removed now working exclude for Array#sort + +commit 56af7be26dcc9b7270de6d96e73e09a4f17cc710 +Author: Eero Saynatkari +Date: Thu Feb 14 20:48:37 2008 -0500 + + Improved Array#sort, #sort! specs. + +commit 714ea4b5245172cc6d5c815ef7399d1a991dd83f +Author: Eero Saynatkari +Date: Wed Feb 13 10:30:22 2008 -0500 + + Improved Array#sort specs a bit. + +commit 8944e873848c610182405c2de466e41e6260573d +Author: Eero Saynatkari +Date: Wed Feb 13 02:34:37 2008 -0500 + + Tuple#swap specs. + +commit 24199f731dba40b72af6d121121dec9f085f890d +Author: Vladimir Sizikov +Date: Thu Feb 14 20:03:16 2008 +0100 + + New rubyspecs for IO#reopen. + +commit 4f70320e5b7089c74b3899216763cd37d8854230 +Author: Vladimir Sizikov +Date: Thu Feb 14 17:27:32 2008 +0100 + + Removed JRuby-specific guards. Please don't use guards to hide bugs. + + Guards to be used only when it is agreed that the JRuby behavior + is intentionally differs from MRI. For plain bugs, guards should + not be used. Instead, we maintain spec exclusions in JRuby repository. + +commit 0198a11b3bdf60983846a6c722dfa11d1b9f57bb +Merge: ef3393e... 1f1e32e... +Author: Mutwin Kraus +Date: Thu Feb 14 15:57:57 2008 +0100 + + Merge branch 'mutle_file_specs_refactoring' + +commit 1f1e32e5e1fd12fb323e2a74a7f5caae96aa867b +Author: Mutwin Kraus +Date: Thu Feb 14 15:18:14 2008 +0100 + + Specs for File#chown #flock and #truncate now pass on JRuby + +commit 3a8e601d5205e050f83179376d2be3e922e80c20 +Author: Adam Gardiner +Date: Thu Feb 14 17:25:02 2008 +1100 + + Fix context specs to wait for debug listener thread + +commit 608d7a99e75d293d6f9786cee940c0dd23156be3 +Author: Mutwin Kraus +Date: Thu Feb 14 12:59:32 2008 +0100 + + Adding guards to only run File#chown and File.chown specs as root. + +commit b3a1069cf6c18b844b9eced32b7bcdb91ad7c558 +Author: Brian Ford +Date: Thu Feb 14 01:31:47 2008 -0800 + + Rework Bignum#==. Change Numeric#== to conform to MRI. + +commit 4eb58ebc45b2ee79f01d75fdb3e9104c73ad66e2 +Author: Eric Hodel +Date: Wed Feb 13 23:56:37 2008 -0800 + + Common implementation for Zlib::Inflate and Zlib::Deflate. + +commit 1804fdacce5c195a90befe502706d1f1e066e886 +Author: Brian Ford +Date: Wed Feb 13 19:31:20 2008 -0800 + + Port of JRuby's File.fnmatch to Ruby (yeah, like writing Java in Ruby). + +commit 1a78da8438535ee8ed231359bdb15ff3624c6b37 +Author: Mutwin Kraus +Date: Thu Feb 14 01:27:52 2008 +0100 + + Adding File#truncate improvements from #325 and #326 + +commit 5b62acbdcf0aab2e89be5ac3e12859ae36cd6950 +Author: Mutwin Kraus +Date: Thu Feb 14 00:47:53 2008 +0100 + + Adding File#truncate with specs + +commit 1a2b3dde4f67abe0936e7ec6fb749e5bb8fda7d2 +Author: Mutwin Kraus +Date: Thu Feb 14 00:36:32 2008 +0100 + + Adding File#chown with specs + +commit e132cd6f11285f0e106a5d2a292e23c8375fa1ee +Author: Mutwin Kraus +Date: Thu Feb 14 00:12:31 2008 +0100 + + Renamed File#flock spec to properly reflect an instance method + +commit 00cd22ccdf2b70fa53693000d4a5bb803c7d6df6 +Author: Mutwin Kraus +Date: Thu Feb 14 00:07:05 2008 +0100 + + Adding File::flock with specs + +commit 3c9b3e4e4272889dd26ec9ddb25f7aaf88c6c380 +Author: Mutwin Kraus +Date: Wed Feb 13 23:25:30 2008 +0100 + + Adding File::chown with specs + + * The spec works fine on OS X, but was not tested anywhere else + +commit c894a6c46b4a3d0b9010020c394d3ba366bf145e +Author: Dirkjan Bussink +Date: Wed Feb 13 11:59:38 2008 +0100 + + Module#undef_method should accept string parameter, not only symbols by Nikolai Lugovoi (#321) + +commit c968d5c29cc3126c789cf5bb2005bd9637e85312 +Author: Dirkjan Bussink +Date: Wed Feb 13 11:52:10 2008 +0100 + + Update excludes for File#truncate + +commit 408e69864546aea061e006073bb452b8db8c4610 +Author: Ragnar Dahlén +Date: Wed Feb 13 11:15:31 2008 +0100 + + Implement File.truncate, passes specs. + + * Adds truncate, ftruncate (not used yet) to posix.rb + + Only tested on Mac OS X 10.5.1. + + Signed-off-by: Dirkjan Bussink + +commit 5c75721d5a78e25a77e9f068bf4c95e729604959 +Author: oleg dashevskii +Date: Wed Feb 13 11:26:34 2008 +0600 + + Remove tests that have been superseded by precedence_spec. + +commit 83a372674786a0be51a206cadcae644d72a1e8d2 +Author: oleg dashevskii +Date: Wed Feb 13 11:05:34 2008 +0600 + + Made a real precedence_spec. + + One test still commented out till the bug with flip2 is fixed. + +commit c3988a4a906594c050e058add8aa6996870dc115 +Author: Dirkjan Bussink +Date: Wed Feb 13 02:10:49 2008 +0100 + + Remove excludes for File#stats specs + +commit 1624b463d0f70a27b6772d90626c94b6eed4e5c4 +Author: Wilson Bilkovich +Date: Tue Feb 12 18:25:05 2008 -0500 + + Add specs for pass subclasses of Module to 'include' + +commit 64b0fb4131276feda0d0ab13301824b20f8d7f8e +Author: oleg dashevskii +Date: Wed Feb 13 00:49:37 2008 +0600 + + Make Dir.chdir spec work when /home is symlinked to /usr/home. + +commit 8cbf6312df160f30e284a4537039f808a42543fe +Author: Wilson Bilkovich +Date: Tue Feb 12 12:30:19 2008 -0500 + + Add failing Array#sort spec and matching exclude + +commit 9bef807b3b469b8790edbe96f1442394d528cb5a +Author: Wilson Bilkovich +Date: Tue Feb 12 12:09:11 2008 -0500 + + Move Time#<=> specs around until the descriptions make sense + +commit 60fbbc62cb04b2fddcd406f01f906482fbc84370 +Author: Vladimir Sizikov +Date: Tue Feb 12 05:33:49 2008 +0100 + + Mark JRuby as not deviating from MRI on unboundmethod specs. + +commit 4e6d8f7e3326f937a6916ed11984172670a71094 +Author: Yehuda Katz +Date: Mon Feb 11 23:26:31 2008 -0800 + + Zlib.adler32 + +commit 2f2d10e1aa57bae79f7fcda5e5a30b2a2ef3e37c +Author: Yehuda Katz +Date: Mon Feb 11 23:12:40 2008 -0800 + + Zlib.crc_table + +commit 49b9e4b624074d151e89f078c4080a0a7584abaa +Author: Yehuda Katz +Date: Mon Feb 11 22:56:52 2008 -0800 + + Zlib#crc32 + +commit 7cb2ebfa008afc96135912ceefdbd81b1cd7e478 +Author: Eric Hodel +Date: Mon Feb 11 17:36:32 2008 -0800 + + Fix class variables for RDoc. + +commit 571d837bbeff221daacebc79c1ccab7de15c77f2 +Author: Brian Ford +Date: Mon Feb 11 08:54:36 2008 -0800 + + Exclude [r]dev_(major|minor) specs. We need some autoconf facilities. + +commit fb2bc81d50bf504e3997d009e3c13f841b859803 +Merge: 55a52f1... 9b58a59... +Author: Jonas Pfenniger +Date: Mon Feb 11 16:19:02 2008 +0100 + + Merge branch 'master' of git@git.rubini.us:code + +commit 55a52f18133fc9f92eef64838008a83dfaab3ffc +Author: Jonas Pfenniger +Date: Mon Feb 11 16:18:37 2008 +0100 + + Removed Math.asinh excludes. + + It wasn't working on OS X. Evan fixed the culprit FFI over the weekend. + +commit 9b58a59ca21c6622d246e629410230bfbe8cd4ce +Author: makoto kuwata +Date: Mon Feb 11 21:24:05 2008 +0900 + + Modified to address differences of SyntaxError class between MRI and Rubinius in 'erb/filename_spec.rb' + + Signed-off-by: Dirkjan Bussink + +commit d1c4280b70b82d6cd541251e3d7e1a3091fb304f +Author: makoto kuwata +Date: Mon Feb 11 20:19:12 2008 +0900 + + Add 'erb/util/shared/url_encode.rb' which is missed file + + Signed-off-by: Dirkjan Bussink + +commit b4b1114ac7dffabd672d462b5857a7e1957e8f07 +Author: makoto kuwata +Date: Mon Feb 11 19:10:58 2008 +0900 + + Add spec files for erb.rb + + Signed-off-by: Dirkjan Bussink + +commit dfdf90968e78f14e0755b5f3279ec878034dbdb5 +Author: Yehuda Katz +Date: Mon Feb 11 00:09:23 2008 -0800 + + Added singleton specs (and reorganized stale one): + * Singleton#_dump + * Singleton._load + * Singleton#instance + * Singleton.instantiate? + * Singleton.new and Singleton.allocate + * Singleton#dup and Singleton#clone + +commit 54c4a4cab187be4328d6a810bae4bc4bd01ca1d8 +Author: Brian Ford +Date: Sun Feb 10 20:19:30 2008 -0800 + + Additional specs for File::Stat#rdev, #rdev_major, #rdev_minor. + +commit 6b2f05af4758c488b3e2e3b19ee9d2e872817932 +Author: Wilson Bilkovich +Date: Sun Feb 10 23:00:41 2008 -0500 + + Rubinius now passes all 'super' specs + +commit 5be84fce241c67bd8439bccbe54cec575b0ea93a +Author: Wilson Bilkovich +Date: Sun Feb 10 22:53:11 2008 -0500 + + Failing spec for 'super' behavior + +commit 975d51e80d3df437eaa8ddd3c3384a5766255b12 +Author: Brian Ford +Date: Sat Feb 9 15:18:37 2008 -0800 + + Exclude Process constants spec until LFS is fixed on 32bit linux. + +commit 236def62bcfa3dca75a6eebf378a68235c4613ed +Author: Yehuda Katz +Date: Sat Feb 9 17:04:59 2008 -0500 + + Fixes exclude + +commit 26bedb481b45e77434b487c6395903c6110ef99e +Author: Yehuda Katz +Date: Sat Feb 9 16:01:58 2008 -0500 + + Moved bad variables spec out + +commit 87efbf9036e5c524e1b40481c89107538d574ba8 +Author: Brian Ford +Date: Sat Feb 9 11:26:30 2008 -0800 + + Revert all File::Stat stuff. We'll fix Dir first. + +commit 1f5bc0f98a23fc90b9bd00048af1551df8e534f7 +Author: Brian Ford +Date: Sat Feb 9 00:39:20 2008 -0800 + + Reduced File::Stat.stat primitive further. Details follow. + + * Added ffi_major and ffi_minor to calculate the major, minor + parts of st_dev and st_rdev. + * Added (temporary) new primitive basic_stat to change the + return type from a tuple to a single MemoryPointer instance. + * Added simple specs for rdev, rdev_major, rdev_minor, nlink. + +commit e478731a2fc558c62cecbe327c5b35882d90b53d +Author: Vladimir Sizikov +Date: Fri Feb 8 16:45:05 2008 +0100 + + One more rubyspec for File#open. + +commit 5f6ac709500cb64df110a44d31e0c0b89dd68aec +Author: Vladimir Sizikov +Date: Fri Feb 8 16:39:47 2008 +0100 + + New rubyspecs for File#umask. + +commit fddaa684bd7e8c403ff96179ca71a5837f609b63 +Author: Vladimir Sizikov +Date: Fri Feb 8 16:30:20 2008 +0100 + + New File#chmod rubyspecs. + +commit ed20c3f9f36f343a37e2ac05ea91d84b54c87bc8 +Author: Wilson Bilkovich +Date: Fri Feb 8 12:00:57 2008 -0500 + + Correctly guard Bignum specs for CI + +commit 36e9749984d6e4412c26d348afa8c501cf043ecf +Author: Vladimir Sizikov +Date: Fri Feb 8 13:09:37 2008 +0100 + + Some more specs for File#new and File#open, and permissions. + +commit 29376695550c5608f466d63d49de76a6ee163e37 +Author: Vladimir Sizikov +Date: Fri Feb 8 12:10:27 2008 +0100 + + New specs for IO#new and IO#open, invoked with permissions parameter.. + +commit 8cf27fcd86f88b75716b65dc1d94b721c01c3af9 +Author: oleg dashevskii +Date: Fri Feb 1 21:03:08 2008 +0600 + + Heredocs and more stuff added to string_spec. + +commit 2cccd38a081c0303f8fa567058e4c26fa354abc5 +Author: Dirkjan Bussink +Date: Fri Feb 8 12:51:53 2008 +0100 + + Add exclude for currently failing for_spec + +commit 031bb1b565a3446ab995ea55e6ae8890573ba6c0 +Author: oleg dashevskii +Date: Fri Feb 1 20:23:56 2008 +0600 + + Added more tests to for expression spec. + +commit 73e40331c6b4c1c1b6e41ae312299f6815e089c2 +Author: Wilson Bilkovich +Date: Thu Feb 7 19:12:46 2008 -0500 + + Add a failing spec for Array#join and then fix it + +commit fa49548fe704252c352a1bc4833b5da20262061a +Author: Dirkjan Bussink +Date: Thu Feb 7 23:12:03 2008 +0100 + + Fix last two Failing Time specs for Time#+ and Time#- + +commit e4e51c6aa39e5a5a61b0df919ba02b88d4878f43 +Author: Dirkjan Bussink +Date: Thu Feb 7 22:35:57 2008 +0100 + + Fix Time.at so it also works with floats + +commit f5505522fd0396c3864fce155681ac577bf2e7e6 +Author: Dirkjan Bussink +Date: Thu Feb 7 17:10:48 2008 +0100 + + Fix Time#<=> for objects other than Time + +commit e8ab7b5eb30da84262a9395e20ac420e83674edf +Author: Eric Hodel +Date: Wed Feb 6 13:59:18 2008 -0800 + + Only call Class#inherited once + +commit dc9ff28ae919292287f5562b8c105ff6310c5920 +Author: Phil Hagelberg +Date: Wed Feb 6 15:00:59 2008 -0800 + + Kernel#eval should be a module function + + Added a spec as well + +commit 7dd83410a2159fd65f951689a8a1297baded4fa6 +Merge: 698ffa4... 339fed9... +Author: Mutwin Kraus +Date: Wed Feb 6 22:54:12 2008 +0100 + + Merge branch 'master' of git@git.rubini.us:code + +commit 698ffa4e04fee58da5c3f2191372c4e4f2bc070d +Author: Mutwin Kraus +Date: Wed Feb 6 22:51:54 2008 +0100 + + Adding missing specs for ftools + + * specs for chmod, compare, copy, install, makedirs, move and safe_unlink + +commit 339fed9821b75de056febc406b32fe52ff9354a9 +Author: Dirkjan Bussink +Date: Wed Feb 6 22:00:46 2008 +0100 + + Forgot to remove spec excludes... + +commit 7ecca7222823a82252ed09b17eefafe6fec9f12e +Author: Dirkjan Bussink +Date: Wed Feb 6 21:46:19 2008 +0100 + + Fix last two failing Dir#glob specs + +commit fec39f27d287ca74becbecc120de8533e346b864 +Author: Vladimir Sizikov +Date: Wed Feb 6 18:13:13 2008 +0100 + + A few more corner cases fo IO#lineno specs. + +commit 3691d3f9a202abb22e11024e41b868d531a549be +Author: Vladimir Sizikov +Date: Wed Feb 6 18:02:50 2008 +0100 + + New rubyspecs for IO#lineno. + +commit a5b0f9aa15c9372f74816e77073926780a9cc219 +Author: Vladimir Sizikov +Date: Wed Feb 6 15:58:26 2008 +0100 + + Improved IO#foreach specs. + +commit 91ea9f304c75592e7454411ef21391a0e34da5e5 +Author: Vladimir Sizikov +Date: Wed Feb 6 15:03:02 2008 +0100 + + New rubyspecs for IO#gets and IO#foreach. + +commit 9c494786fbf400bb295e1f19d142e2c903c21e54 +Author: Vladimir Sizikov +Date: Wed Feb 6 13:37:43 2008 +0100 + + New rubyspecs for IO#foreach. + +commit cdbbeba8f3351fe43f44d732348f380599ad5719 +Author: Vladimir Sizikov +Date: Wed Feb 6 10:40:13 2008 +0100 + + New rubyspecs for IO's sysread and read with buffer argument. + +commit e1cb4410b7e0a0ba2fd580784334bdfd05ba4b8a +Author: Vladimir Sizikov +Date: Wed Feb 6 10:15:29 2008 +0100 + + Excluded rbx failure after spec rename. + +commit 1fde018b9378b55f6d51cb85bd65813b5cef5493 +Author: Charles Nutter +Date: Wed Feb 6 02:50:44 2008 -0600 + + Tidy up an apparent copy/paste mistake in IO#syswrite spec + +commit 1aa624e625dafaeebd70eac11819f02ecf570f8e +Author: Vladimir Sizikov +Date: Wed Feb 6 00:19:39 2008 +0100 + + New IO specs for writing non-string data. + +commit f94a0cffd5fd0e186a9403d97800b55f8c44bdd1 +Author: Eric Hodel +Date: Tue Feb 5 15:13:47 2008 -0800 + + Regenerate zlib stubs. + +commit 52ce0e702170676ce02dcc288305097d58834cf8 +Author: Eric Hodel +Date: Tue Feb 5 13:34:47 2008 -0800 + + Add Zlib spec stubs + +commit b24ad594837b974a3ae3b207d63ce5cdc956a1a0 +Author: Vladimir Sizikov +Date: Tue Feb 5 21:45:09 2008 +0100 + + Added some boundary test cases for Float. + +commit 1a8c9966fa148fc3e912f8aecd42c8c00ca4f89c +Author: Dirkjan Bussink +Date: Tue Feb 5 22:46:18 2008 +0100 + + Removed problematic Marshal spec because 2**40 is a Bignum on some archs and a Fixnum on others + +commit 6ad8a0a25f20fd137bcb7fcb83bc88440a2a2069 +Author: Vladimir Sizikov +Date: Tue Feb 5 17:03:56 2008 +0100 + + Eliminated file descriptors leakage out of IO tests. + + These things make runs unpredictable, causing all kinds + of troubles (non-deterministic failures, fluctating + number of tests). + +commit 95ac3cb9900c52e4819b37166c71840d4bb4e3d9 +Author: Vladimir Sizikov +Date: Tue Feb 5 14:50:31 2008 +0100 + + New rubyspecs for File.open with block. + + Also, IO.open specs improved to handle closing better. + +commit 66f636c346a63853ae37a06f1c3e8b5083370892 +Author: Vladimir Sizikov +Date: Tue Feb 5 13:22:34 2008 +0100 + + New tests for IO.open, and additional cases for IO's inspect and stat. + +commit dfb941da0f7503bce58dc88a85ccfd201615e13b +Author: Brian Ford +Date: Tue Feb 5 01:57:20 2008 -0800 + + Ezra's patch for Regexp#inspect, #309. + +commit c5f9381ee74ed2d9c91cca1dd2ce9719b6f51bd8 +Author: Vladimir Sizikov +Date: Tue Feb 5 10:07:57 2008 +0100 + + Corrected IO test to not interfere with Kernel#puts tests. + +commit 751293c1ea14de1b1d2757bf5d60b082cc771e7a +Author: Ryan Davis +Date: Mon Feb 4 17:43:06 2008 -0800 + + removed empty excludes + +commit 206399aee9ae7845d76c0726702c424b9fc44e80 +Author: Ryan Davis +Date: Mon Feb 4 17:39:45 2008 -0800 + + StringIO#seek now raises if passed bignum offset - should be platform specific, but this'll do for now + +commit b00f04ec0fcd8f8edf9943abb5999f1cca9e9e9e +Author: Ryan Davis +Date: Mon Feb 4 17:06:34 2008 -0800 + + Knocked off the last of the method excludes. + Tightened up the spec for #to_s + +commit 3d4f87741135343a2e8ec6032fa3a69529cfbf69 +Author: Eric Hodel +Date: Mon Feb 4 16:51:37 2008 -0800 + + Fix Hash#key? to work with objects that have the same #hash. + +commit f814a15639f6e6ecd47ab99ad9e37e93fd6bc165 +Author: Eric Hodel +Date: Mon Feb 4 15:17:08 2008 -0800 + + Fix Marshal format version check + +commit 6d9e0afb5600416e5d66d5123abcfa5dd6c40903 +Author: Ryan Davis +Date: Mon Feb 4 14:24:48 2008 -0800 + + removed tmpfiles from dir/fixtures and moved them to tmp where they belong + +commit 4e18d1cf49573b1fa3f484686352734aa39457d0 +Author: Ruben Nine +Date: Mon Feb 4 02:32:26 2008 +0100 + + Added support for tag:yaml.org,2002:sym to RbYAML library. + + Signed-off-by: Jonas Pfenniger + +commit 220ed05f204f4b8fe7f1f303fae9a18988f8879b +Author: Wilson Bilkovich +Date: Mon Feb 4 17:16:44 2008 -0500 + + Use an example number that is actually a Bignum everywhere in compiler Bignum spec + +commit db3f20c8ec905641de887bbd1ed581aa78f73471 +Merge: e33350e... 6e3dad3... +Author: Wilson Bilkovich +Date: Mon Feb 4 16:50:14 2008 -0500 + + Merge branch 'wilson64' + +commit 6e3dad3e5b2e982f96e991e9df2d46de5bf4ee1f +Author: Wilson Bilkovich +Date: Mon Feb 4 16:49:09 2008 -0500 + + Use the correct Fixnum#size spec on 64bit platforms + +commit e33350eddc3441b2ebe06336500e6445406285d1 +Author: Vladimir Sizikov +Date: Mon Feb 4 22:12:14 2008 +0100 + + Moved one IO#inspect spec to File#inspect specs. + + Since the behavior is File-specific. + +commit 1586e3a2c5d12f5438adddb8c84bc90c3defee82 +Author: Vladimir Sizikov +Date: Mon Feb 4 20:57:09 2008 +0100 + + Add more IO specs. God, make it stop! + +commit e64f3b02423acb783ba8a62996847b0393e7f3ee +Author: Vladimir Sizikov +Date: Mon Feb 4 20:33:57 2008 +0100 + + And more IO specs. + +commit 3d584f0ee2cf988720bb5985c20b3bec6c2e143f +Author: Vladimir Sizikov +Date: Mon Feb 4 20:08:26 2008 +0100 + + Next batch of IO methods specs with closed streams. + +commit 6e22a99350195cfa7a40d6049d6d72a9ae7e1168 +Author: Vladimir Sizikov +Date: Mon Feb 4 19:47:06 2008 +0100 + + And yet more specs for IO methods with closed streams. + +commit 12e8d881b90cbd60bc792693e799923fdb1041b0 +Author: Vladimir Sizikov +Date: Mon Feb 4 19:20:39 2008 +0100 + + More rubysecs for IO methods invoked on closed streams. + + Plus some refactoring to move repetitive code to the fixture. + + NOTE: two specs marked as fails_on :rubinius due to fact + that they crash rubinius. + +commit 3ae3cafcb10725953c8e595641af277f36c88677 +Author: Vladimir Sizikov +Date: Mon Feb 4 18:39:34 2008 +0100 + + New rubyspecs for IO#to_io. + +commit 4980bb83f53845e88cd0d1a3b0823fdbf0c0a001 +Author: Vladimir Sizikov +Date: Mon Feb 4 18:09:36 2008 +0100 + + New rubyspecs for IO#ungetc. + + Unfortunately, MRI doesn't follow some of its own + specified behaviors... + +commit f27fe4f3e4ccb298dcaa5014dac69d3148ee169e +Author: Vladimir Sizikov +Date: Mon Feb 4 17:18:08 2008 +0100 + + Added rubyspecs for IO's putc, puts, printf, print and closed streams. + +commit a1d7b67942aed8d1b185476dee6f2d99403ed227 +Author: Vladimir Sizikov +Date: Mon Feb 4 17:07:00 2008 +0100 + + Added rubyspecs for IO#pid. + +commit cfa1ef21ce862a05ae352a4fe49a3ac4c04b9bed +Author: Vladimir Sizikov +Date: Mon Feb 4 16:36:21 2008 +0100 + + Added new rubyspecs for IO#sync and IO#sync=. + +commit b4f6c33c17e57fa44322124af088a97d475905e2 +Author: Vladimir Sizikov +Date: Mon Feb 4 16:20:50 2008 +0100 + + New exclusions for rbx. + +commit 60309280c48b2bd1f1d8a5ea018f401e75b7dac1 +Author: Vladimir Sizikov +Date: Mon Feb 4 16:17:27 2008 +0100 + + Added new tests for IO's pos, pos=, rewind, seek on closed streams. + +commit 40414ad1b39222494ff2a79a0091890a60b7adf1 +Author: Vladimir Sizikov +Date: Mon Feb 4 14:26:13 2008 +0100 + + One more test for IO#eof?. + +commit 29db340f24c043b240fec6722c323fa1567ce855 +Author: Vladimir Sizikov +Date: Mon Feb 4 14:19:46 2008 +0100 + + Added new rubyspecs for IO#getc and IO#getchar. + +commit 0e0a987782fc7834ba95a2e8e2c8ab6cd8dcea81 +Author: Vladimir Sizikov +Date: Mon Feb 4 13:54:59 2008 +0100 + + More rubyspecs for IO#eof?. + +commit f7d1139e4eace4a86f0c0512bf9269964442628d +Author: Dirkjan Bussink +Date: Sun Feb 3 15:55:10 2008 +0100 + + Fix Date#strptime specs + +commit ac4600fcb42928aeba508371aea2f76510e70d5c +Author: Dirkjan Bussink +Date: Sun Feb 3 13:26:52 2008 +0100 + + Fixed Time specs for non Rubinius platforms + +commit a5081ca646e99ec94fedfabf03b7eb0a8d37afc3 +Author: Dirkjan Bussink +Date: Sat Feb 2 23:53:52 2008 +0100 + + Fixed Time specs for 64 bit archs + +commit 26eef47571b921fe6b3228033119e5969c4100db +Author: Eero Saynatkari +Date: Sat Feb 2 13:30:16 2008 -0500 + + Updated IO excludes. + +commit 8edd73d9915f72ee70b661b23e8b42f8b985fa9c +Author: Charles Nutter +Date: Sat Feb 2 10:45:23 2008 -0600 + + Repair IO#sysseek spec to not write to fixture file; uses a tmpfile now. + +commit bf6348c935c816a981672e9c26a40354cf0d722c +Author: Charles Nutter +Date: Sat Feb 2 02:35:42 2008 -0600 + + Additional IO#sysseek spec for the warning after buffered writes + +commit 5241316a1c74e6580fb91940a9f061047e89cdbf +Author: Charles Nutter +Date: Sat Feb 2 02:32:36 2008 -0600 + + Modify IO#seek specs for IO#sysseek, adding appropriate error tests + +commit 7f124cbf66b96fdcdaec73917e86eedfb4a9ddf8 +Author: Ryan Davis +Date: Fri Feb 1 18:05:15 2008 -0800 + + Refactored Marshal#dump specs and merged with fixtures/marshal_data.rb + +commit 3766b3ed41ffba71ecb1bef8079027bffe518e2a +Author: Ryan Davis +Date: Fri Feb 1 16:54:32 2008 -0800 + + Refactored specs for Marshal#load + +commit e134d5bf8e247f4a231bfbfc1c3251b262f219e4 +Author: Eric Hodel +Date: Fri Feb 1 15:36:06 2008 -0800 + + Update excludes for recent failures + +commit 8ded8b443f55b47a1c30b59cfb0d96d8752d5fa9 +Author: Eric Hodel +Date: Fri Feb 1 15:11:37 2008 -0800 + + Process.group spec is missing a suplemental group on OS X + +commit 4b7de6ff839b220115dd29f34b5a9f46cb8f5bef +Author: Evan Phoenix +Date: Thu Jan 31 17:16:13 2008 -0800 + + Add proper primitive failures, fix empty symbol. + +commit dc55c88beee6a3a3a7fd352c1e374ecf84863459 +Author: Vladimir Sizikov +Date: Fri Feb 1 16:17:00 2008 +0100 + + Fixed 2 Date#strptime specs that otherwise would pass only in January. + +commit 86c372d0fb50aeb6235ed1595d18a876e09330db +Author: Vladimir Sizikov +Date: Fri Feb 1 15:32:40 2008 +0100 + + Few specs for Time#<=> with non-Time arguments. + +commit 765ef93acd294922dc22a986213a5842ce3e67a7 +Author: Vladimir Sizikov +Date: Fri Feb 1 14:09:01 2008 +0100 + + Added more specs to Time#+ and Time#-. + +commit af76adac2182e46e34e68d29b3cd8614edd27d50 +Author: Vladimir Sizikov +Date: Fri Feb 1 13:38:20 2008 +0100 + + Added more test cases for Array#join on recursive arrays. + +commit bb15b72393b34d3d10bb644fb1d6ce47b6dc0826 +Author: Vladimir Sizikov +Date: Fri Feb 1 13:15:24 2008 +0100 + + Added more test cases for File::join with recursive arrays. + +commit 7041b2aef1e574dfe220a70da5210c683074f8ae +Author: Eric Hodel +Date: Fri Feb 1 03:18:31 2008 -0800 + + Describe an unambiguous method. + +commit 84edf54799e0ccd09276a5cda3fccf544f971c48 +Author: Eric Hodel +Date: Fri Feb 1 03:07:48 2008 -0800 + + Use fixed Marshal data for all specs and fix many broken or useless specs. + + Clean up spec naming and definition. + + Use descriptive names for test classes. + +commit e0c3aa074c9525450a7a667ec2cc843ff3560e65 +Author: Eero Saynatkari +Date: Thu Jan 31 23:29:57 2008 -0500 + + Hash.new patch from Phil Hagelberg + MethodTable workaround. + + * The Hash.new patch splits a separate #setup method so that subclasses + can override #initialize without problems. + * Because it is part of the core code, MethodTable needs to explicitly + call #setup in its #initialize. + +commit a32f16d9288c5c0822cc6962ce3caed5e1bac5d0 +Author: Eero Saynatkari +Date: Thu Jan 31 15:35:08 2008 -0500 + + Updated Module excludes. + +commit df731f327c4d47373ba6f2fe2f79d5d9acbf398e +Author: Eero Saynatkari +Date: Thu Jan 31 15:30:11 2008 -0500 + + More Module#module_function specs in #eval and #module_eval. + + * #module_eval separates the two scopes but #eval does not. + +commit 6358e5893c52042c10c355173d1ad8441a00bcfa +Author: Eero Saynatkari +Date: Thu Jan 31 15:13:49 2008 -0500 + + Better Module#module_function specs. + +commit 1646bb6e99a6b4190641046ae730ea1be9c8be2a +Author: Eero Saynatkari +Date: Thu Jan 31 13:59:20 2008 -0500 + + Various whitespace removal in preparation to fix #module_function. + +commit 108601d85d2c41d05f9c00945664d9980e0e46c3 +Author: Evan Phoenix +Date: Thu Jan 31 13:39:53 2008 -0800 + + Add meta_send_call instruction, speeds up calling blocks + +commit 50f9c50820b4305877af1c7fd7597c5dc94c623c +Author: Eero Saynatkari +Date: Thu Jan 31 12:20:11 2008 -0500 + + Added LC_ALL=C for all other platforms for Time specs too. + + * If it breaks, report and we will figure out something else. + +commit 26059c1570c5ad2a64a796e2678ff2d9ace23e58 +Author: Pierre Yager +Date: Tue Jan 29 22:48:22 2008 +0100 + + Fix for bin/ci spec failure on localised linux + + * Force system date to be executed against "C" locale + + Signed-off-by: Eero Saynatkari + +commit e5ce9e7c29a34f685f7d3f8a9f855db28aece460 +Author: Eero Saynatkari +Date: Thu Jan 31 11:32:56 2008 -0500 + + Removed trailing whitespace for Time and some Time specs. + +commit 3546f721ac86efa318b3802a2f498d41aa830c9f +Author: Caleb Tennis +Date: Thu Jan 31 10:53:53 2008 -0500 + + Subtend: Add rb_define_private_method, rb_define_protected_method, rb_define_module_method, etc. + +commit 7553cb993a0c7e60c2212800b0ecc033ffc0b206 +Author: Caleb Tennis +Date: Thu Jan 31 10:39:08 2008 -0500 + + Add rb_class2name in subtend + +commit 4570b7c5d837025d765a6a2909d5536c466b9dcb +Author: Ryan Davis +Date: Thu Jan 31 02:26:09 2008 -0800 + + quick addition of 2 exclusions + +commit 53c76326e76869a87ad0fc67adbd3aef9059ee35 +Author: Ryan Davis +Date: Thu Jan 31 02:15:15 2008 -0800 + + Parser spec updates + +commit 8fb2eb68858a1ee1dafb06b833f43d6da817756f +Author: Eero Saynatkari +Date: Wed Jan 30 19:29:25 2008 -0500 + + Slightly more permissive TCPServer.new spec for hostname string. + +commit 2a60dbbf011e806ae51c30ab2cb2b8e7b9b633a5 +Author: Mutwin Kraus +Date: Wed Jan 23 18:41:12 2008 +0100 + + Fixing TCPSocket#new for localhost (with specs for both IPv4 and IPv6) + + Signed-off-by: Eero Saynatkari + +commit f485a6818a754c8110feafa9f6dced42a99187d0 +Author: Jacob Maine +Date: Tue Jan 29 21:13:28 2008 -0500 + + Making Enumerable#inject only accept one paramter + + Uses 'Undefined' idiom, which fixes spec, and cleans up code (thanks + for the pointer Eero) + + Signed-off-by: Eero Saynatkari + +commit ebfa5a0bf9f8e3efe61c0d34fe63a8cd74b7ddf8 +Author: Jacob Maine +Date: Tue Jan 29 20:52:16 2008 -0500 + + spec to verify inject accepts one argument, at the most + + Signed-off-by: Eero Saynatkari + +commit b131b80df72a9ceaa9e920b7f78434f301135a6f +Author: Jacob Maine +Date: Wed Jan 30 00:33:03 2008 -0500 + + Adding Enumerable#count spec, including a few failing specs. + + Signed-off-by: Eero Saynatkari + +commit 82b63bc0f5b79735a8021b6c5c69786dc76fa7f6 +Author: Alexandre Perrin +Date: Tue Jan 29 15:59:22 2008 +0100 + + udpdated language/string_spec.rb + + * added spec for class/global variable with the \# simple interpolation + * added spec for ends of a \# simple interpolation + * added more delimiter character with the percent String construction + + Signed-off-by: Eero Saynatkari + +commit c58110bde52d64b30cf36ba3cb342357f3654812 +Author: Evan Phoenix +Date: Wed Jan 30 17:19:26 2008 -0800 + + Fix break. It now uses LRE to properly return to callsite and appear like + it returned. + +commit 45109c222502de955d705f810333d8e7b331c953 +Author: Dirkjan Bussink +Date: Thu Jan 31 00:42:12 2008 +0100 + + Added Date#strptime specs + +commit fe60e6a022d9e64bb568ccd47494f07a99382c58 +Author: Eero Saynatkari +Date: Wed Jan 30 11:36:46 2008 -0500 + + Updated excludes for Marshal. Looks like Fixnum/Bignum issues. + +commit 209dde412310edc384be7d4a86bdfb0444f3b3bf +Author: Eero Saynatkari +Date: Wed Jan 30 01:45:27 2008 -0500 + + Updated IO/File excludes. + +commit 91031e51e49a1a3ddb9f74da31e2ed65c48e1ef5 +Author: Eero Saynatkari +Date: Wed Jan 30 01:43:11 2008 -0500 + + IO.new and IO#close use stream API. + + * IO.new uses fdopen() to open the given fd which also checks the mode + string for us. The returned FILE* is stored as a MemoryPointer in + @fptr. + * IO#close checks for presence of @fptr and if found, uses fflush() and + fclose() to release the handle instead of going the normal route. + +commit f4d64553a2a53c77235ef9acc3353ac455514057 +Author: Eero Saynatkari +Date: Wed Jan 30 01:20:32 2008 -0500 + + Made probably broken File.open spec compliant_on :ruby. + + * File.open should not take three args. File.new does. + +commit dc496f35502b4642137d3f0f74571c8245a6ae56 +Author: Eero Saynatkari +Date: Wed Jan 30 01:19:29 2008 -0500 + + Slight IO.new spec tweaks. + +commit a9d9288315e88cffd59ec1b27e3c3209ceb1a3a9 +Author: Eero Saynatkari +Date: Tue Jan 29 20:32:44 2008 -0500 + + Combined IO.new and IO.open specs for the shared parts. + +commit effa81cce1d42f7c1bc2e275cb75bd9069e934b8 +Author: Eero Saynatkari +Date: Tue Jan 29 19:14:51 2008 -0500 + + Changed specs to use the two-argument IO.new. + +commit 1394b360fe70966e25809a349b400a69262060ca +Author: Eero Saynatkari +Date: Tue Jan 29 14:56:47 2008 -0500 + + Rewrote IO.new specs (still a bit sparse.) + +commit 94d50eb3e60971ffeff28bffa0beaff405c581bd +Author: Vladimir Sizikov +Date: Wed Jan 30 21:47:35 2008 +0100 + + File#utime specs to use be_close rather than ==. + + On some platforms, direct comparison just doesn't work, + producing non-deterministic test failures. + +commit 0f5574c28ff08c96326298b98b4ea50108168044 +Author: Adam Gardiner +Date: Wed Jan 30 13:13:25 2008 +1100 + + Remove race in debug_context_change specs + +commit 00a62c3476dd0717f5c4caece453914e1392de9d +Author: Eric Hodel +Date: Tue Jan 29 14:39:06 2008 -0800 + + Remove bogus Marshal specs for Fixnum/Bignum changeover + +commit db1b140db0fbecf70f8adda983e010ef2bbe94c4 +Author: Dirkjan Bussink +Date: Tue Jan 29 22:58:18 2008 +0100 + + Finished first version of Date specs. All public methods are specced + +commit 8ee52fd8dfd3ef6048c63b30d8aea71da944abb2 +Author: Vladimir Sizikov +Date: Tue Jan 29 17:21:57 2008 +0100 + + Follow rbx lead, and allow deviation in UnboundMethod#== for JRuby. + +commit f670bcb9e086ac9cc73b6ef6083966b296268f04 +Author: Caleb Tennis +Date: Tue Jan 29 09:46:50 2008 -0500 + + Fix typo + +commit 4e990269fd42aabd48cdc29b4288c78984d0e5cf +Author: Eric Hodel +Date: Tue Jan 29 02:44:20 2008 -0800 + + Add File::join recursive Array spec. + +commit 4d947218e949e19515a9e89af99d4823048f3bb2 +Author: Eric Hodel +Date: Tue Jan 29 02:41:54 2008 -0800 + + Fix File::join spec name, duplication + +commit a38e10ddc19ebd59f8775a01f3e899c5348ba23f +Author: Eric Hodel +Date: Tue Jan 29 02:40:37 2008 -0800 + + Make File::join remove extra / appropriately. + +commit 074251c03093ba40c0fc3558d512a77844ac45aa +Author: Eric Hodel +Date: Tue Jan 29 00:53:23 2008 -0800 + + Make File::join specs more clear, remove whitespace + +commit f968bbe15a27d8ac6716d103119d41c4eef37696 +Author: Eric Hodel +Date: Mon Jan 28 18:07:24 2008 -0800 + + Use const_lookup in Marshal, fix #marshal_load. + +commit 7e00b857f56879564c1bf27f2e694f3c0783a4bb +Author: Eric Hodel +Date: Mon Jan 28 17:23:26 2008 -0800 + + Support nested modules in Marshal + +commit 5c6e2af3d9ace07ca8387c5aecaa5c1d85e8d81f +Author: Eero Saynatkari +Date: Mon Jan 28 20:22:40 2008 -0500 + + Added specs for rest of the filetypes to File::Stat#ftype specs. + +commit 221a077bef5e9007b548993eaf16c86137c6b0b3 +Author: Eero Saynatkari +Date: Mon Jan 28 20:08:12 2008 -0500 + + Added support to spec file type against sockets too. + +commit 9cb4791db10bc79f8c30a86f17e6c099dabeea80 +Author: Eero Saynatkari +Date: Mon Jan 28 19:52:07 2008 -0500 + + Moved File and File::Stat-related fixtures to fixtures. + + * Module FileSpecs defines methods that yield filenames corresponding + to specific file types so they can be easily tested. + +commit c28c85602d3ab6770ed567a64b744baa15795511 +Author: Ben Hughes +Date: Sat Jan 19 16:10:36 2008 -0500 + + Added spec for File::Stat#dev, dev_major, and dev_minor + + * Check that the result values are Integers for each operation + + Signed-off-by: Eero Saynatkari + +commit 86ce52e32a35cb11564d0d5f306f4eea6d6b714d +Author: Adam Gardiner +Date: Tue Jan 22 15:21:38 2008 +1100 + + Added yield_debugger on context change + + Added capability to set a flag in the VM that causes a + yield to the debugger to occur immediately following a + change in the active context. This provides a foundation + for step in logic for the debugger, which need only set + a flag on a task and have a breakpoint triggered at + whatever receiver is activated following a send. + +commit 3904ff2fbb209b8c2d476bb3f4a4ea4825a16f6e +Author: Brian Ford +Date: Mon Jan 28 17:58:35 2008 -0800 + + Fixes for mSpec to coexist with autotest. + +commit 45f2d6de4b025acfa2429d88c729a3eb58a79528 +Author: Ryan Davis +Date: Mon Jan 28 17:08:45 2008 -0800 + + Added more brains to .autotest. Removed bad files that it pointed out + +commit 70eaa7feffcfd552c51b67a651cdf6063c9b549a +Author: Eric Hodel +Date: Mon Jan 28 15:57:24 2008 -0800 + + Add File::Stat#dev. + +commit 020f4bec691ba658fab0f1ff24fa5df5a6f1921f +Author: Ryan Davis +Date: Mon Jan 28 16:13:11 2008 -0800 + + Added enough process spec exclusions to drop the HUP issues + +commit 76b393566f2a89001952dbf1ec46dd52a5750448 +Author: Ryan Davis +Date: Sat Jan 26 10:58:42 2008 -0800 + + Fixed autotest support (needs latest version of zentest). + Minor clean up on bin/ci and kernel/core/module.rb. + Hacked mspec/matchers/base.rb to output with pretty print. + Fixed mspec's runner to output time BEFORE failures. Fixes unit_diff. + Updated Parser excludes. + Deleted a bunch of excludes. + Updated spec/parser/sexp_expectations.rb with latest ParseTreeTestCase. + Started adding a rewriter to make maintaining sexp_expectations easier. + +commit d147f6f0a87e30e240750d8c660bc89f8c84a472 +Author: Jeremy Roach +Date: Mon Jan 28 18:20:38 2008 -0600 + + update CI excludes + +commit 159f17a228fa6a42cea79b9e3663e1f2b9dea9e4 +Author: Jeremy Roach +Date: Mon Jan 28 18:07:56 2008 -0600 + + add Marshal specs + +commit 72e739590b6bbe571607df674e2f4106c64c8042 +Author: Eric Hodel +Date: Mon Jan 28 15:15:26 2008 -0800 + + Fix String#gsub when matching '^'. + +commit fc8c2c5584305b3e0b2a74ba8250a0b7072a372f +Author: Ben Hughes +Date: Sat Jan 19 16:20:55 2008 -0500 + + Added specs for File::Stat#ftype. #264. + + * Tests "file" and "directory" + + Signed-off-by: Eero Saynatkari + +commit 2f9872b66d4dffc82e0a97e617fb9de18105f668 +Author: Eero Saynatkari +Date: Sun Jan 27 19:56:06 2008 -0500 + + Better living through mocks. String#+ spec cleanup & correction. + + * String#+ in fact raises a TypeError when given ANY non-#to_str + object. + * Simplified spec code. + +commit f5a0f1e0e401db8f28727cdd8be99228c9c6aee3 +Author: Eero Saynatkari +Date: Sun Jan 27 19:37:08 2008 -0500 + + Removed trailing whitespace in String. + +commit 03c1c270236786b66930063669b95ac7cbf17f10 +Author: Matthew Draper +Date: Thu Jan 10 22:40:35 2008 +1030 + + String#+(65) throws a TypeError, unlike String#<<(65). + + Signed-off-by: Eero Saynatkari + +commit a17ede3e9c85c1bd2e06efa7381c1e5dbab47f80 +Author: Eero Saynatkari +Date: Sun Jan 27 19:10:33 2008 -0500 + + Minimal IO#open specs, IO#sysseek. Merged from Chen Yufei's patch. + + * Merged patch by hand, most of it was already implemented separately + too. + +commit 30116d672d950687646c1668eac4d9f5b10f4df7 +Author: Eero Saynatkari +Date: Sun Jan 27 18:53:19 2008 -0500 + + IO#readline EOFError spec modified from Chen Yufei's patch. + + * Patch was out-of-date, applied by hand. + +commit 1b81e68249741d53b38857440bba897987d00e43 +Author: Eero Saynatkari +Date: Sun Jan 27 17:44:30 2008 -0500 + + Separated and excluded NUL byte stripping for String#lstrip specs. + + * Rubinius does strip leading NULs, MRI does not. + +commit 55f50888f22288b0fa45298d873dd265d7340aec +Author: Eero Saynatkari +Date: Sun Jan 27 16:52:51 2008 -0500 + + Updated various excludes. + +commit b085f63d66519f93b59e3851b7e6796877e97107 +Author: Eero Saynatkari +Date: Sun Jan 27 16:47:24 2008 -0500 + + Documented Method, deleted unnecessary Method#module spec. + +commit f71f5c91e8ceab59d59614fe885dfeff096d7655 +Author: Eero Saynatkari +Date: Sun Jan 27 15:41:26 2008 -0500 + + More precise specs for Method and UnboundMethod #to_s / #inspect. + + * Checks presence of own class, method name, name of the Module where + the method is defined and name of the Module where the method was + extracted from. + +commit 400b522d27515698e0a35b2507a4a8825ec9bf8f +Author: Eero Saynatkari +Date: Sun Jan 27 15:22:46 2008 -0500 + + Rewrote Method#unbind specs, touch-up for Module#instance_method spec. + +commit 57bddb7b38dbb762b2469c51eb961e01f03c8518 +Author: Eero Saynatkari +Date: Sun Jan 27 14:50:38 2008 -0500 + + Updated UnboundMethod#== spec for Rubinius/MRI difference on Modules. + + * Rubinius' UnboundMethod#== is true for methods from included Modules also. + +commit 8503c92f914d72e72eeeaede225f52242a1afad9 +Author: Eero Saynatkari +Date: Sun Jan 27 14:25:29 2008 -0500 + + Rewrote and added Module#instance_method specs. + +commit 8541f4cf83f8b776276e81ca41eb0f7b595e4fb7 +Author: Eero Saynatkari +Date: Sun Jan 27 13:43:36 2008 -0500 + + Improved/added UnboundMethod#bind specs. + + * Removed unnecessarily specific error message check. Exception type + is plenty. + * Specified correct behaviour only in terms of Method since a Method + is returned and anything after that is not #bind's responsibility. + * Rubinius allows binding to any object that is kind_of? with respect + to the Module that the method is *defined* in. MRI requires that it + can only apply to objects of the same Module that the method was + extracted from. + +commit fc7073c85b5e201265e24a82c19bd6413681f6e1 +Author: Eero Saynatkari +Date: Sun Jan 27 13:02:24 2008 -0500 + + Removed trailing whitespace in UnboundMethod#bind specs. + +commit bde0cacff5f061accab7feb8a27b2417456f2f95 +Author: Eero Saynatkari +Date: Sun Jan 27 12:38:39 2008 -0500 + + Specced Rubinius to deviate in UnboundMethod#==. + + * MRI requires that both UMs were extracted from the exact same + Module. Subclasses etc. are not OK even if the UMs both refer to + the original in the parent. This is somewhat nonsensical and + harder to implement so Rubinius allows it. + +commit e7ba146d3d0ef0aed1d297d157008661458723eb +Author: Eero Saynatkari +Date: Sun Jan 27 12:16:07 2008 -0500 + + Much more comprehensive and precise UnboundMethod#== specs, documented. + + * #== has some stupid behaviour but this is what we get. + * Explanation of criteria in the method doc. + +commit 43f2226c8882900a472f0a5347fa549936e8f000 +Author: Eero Saynatkari +Date: Sun Jan 27 09:37:51 2008 -0500 + + UnboundMethod String representation specs improved. + + * Specs require that the returned String contains this object's class + and the [Module]#[method_name] it was extracted from. + * The spec specifies nothing else about the format or order etc. + +commit 67e3b5993d92776e0c9535549e8ffdb172225d52 +Author: Eero Saynatkari +Date: Sun Jan 27 09:11:19 2008 -0500 + + Trimmed whitespace for kernel/core/method.rb for patching. + +commit c61c5185589cf5a86b58b2e8c8b8d7a26cdc25ec +Author: Scott Taylor +Date: Mon Jan 14 00:23:27 2008 -0500 + + fixing the specs for UnboundMethod#inspect + + Signed-off-by: Eero Saynatkari + +commit 48bcca32329d48a20d5a6f2dd19598ea7b4167ce +Author: Scott Taylor +Date: Mon Jan 14 00:16:24 2008 -0500 + + UnboundMethod#==, plus an extra spec + + Signed-off-by: Eero Saynatkari + +commit 232015fed94b59adf627a7712da0d5d4d44c87d0 +Author: Scott Taylor +Date: Mon Jan 14 00:03:32 2008 -0500 + + UnboundMethod#bind + + Signed-off-by: Eero Saynatkari + +commit fcd0139307fd48f78f122457af1af43a543343ce +Author: Eero Saynatkari +Date: Sun Jan 27 12:11:34 2008 -0500 + + Updated excludes for IO#write. + +commit eed253158fe0cc20b91f6c8dcc06f6a671092d84 +Author: Eric Hodel +Date: Sat Jan 26 14:36:33 2008 -0800 + + IO#write calls #to_s on it's argument + +commit bb5ff251bcc4baceac25a3a1fa64797b94551145 +Author: Eero Saynatkari +Date: Sun Jan 27 03:22:02 2008 -0500 + + Updated #attr_writer spec that was picking up a stray method. + +commit fa985a57f6cf802d6a83a6d02a31dd7fd33ebd36 +Author: Eero Saynatkari +Date: Sun Jan 27 01:20:27 2008 -0500 + + Module#const_get can now access top-level constants for Modules also. + + * Modules explicitly check Object last, Classes already do it since + they all inherit from Object. + * Added some more specs too. + +commit fbc1cfb2d461891ee478802f44de3736959905a7 +Author: Eero Saynatkari +Date: Sun Jan 27 00:47:52 2008 -0500 + + Renamed the Module field 'parent' to 'encloser.' Some docs. + + * When dealing with Modules and Classes, 'parent' is an ambiguous term. + In typical OO literature, 'parent' means the superclass which is not + the case here. Two separate sections of code already showedsome + uncertainty about the intent of this field. + * Added a few bits of documentation to Module. + +commit 92903e92564857350061d83f8eb3b54886986ac3 +Author: Eero Saynatkari +Date: Sun Jan 27 00:36:10 2008 -0500 + + Updated Module excludes. + +commit a705e687ce0d55e7ea184e1a3e67ba8d9d7c610c +Author: Eero Saynatkari +Date: Sun Jan 27 00:24:20 2008 -0500 + + Improved Module#const_get specs for better coverage. + +commit bca6aef9b81166f9c5f4aeaafc673a54710d4d35 +Author: Eero Saynatkari +Date: Sat Jan 26 19:57:33 2008 -0500 + + Corrected semantics of spec statements for Module#const_get. + + * Specs were correct but the description was inaccurate. + * Prettified just a little. + +commit f699c18b68dee73086afb92d15b61745319a5321 +Author: Eero Saynatkari +Date: Sat Jan 26 19:55:40 2008 -0500 + + Module#const_get specs for top-level constants by Le Huy. + +commit f3831a0693ea90271843bcc5910516e5a40ed3c1 +Author: Eero Saynatkari +Date: Sat Jan 26 19:15:27 2008 -0500 + + Module whitespace cleanup before patching. + +commit e3cbe8136351f055bd99f10646d4f77515078430 +Author: Charles Nutter +Date: Sun Jan 27 00:51:55 2008 -0600 + + A few basic IO#write specs for file IO + +commit fd05adfedf70d795d8d91f650d5b76b05104dd7a +Author: Eero Saynatkari +Date: Sat Jan 26 18:54:35 2008 -0500 + + Specs for Enumerable#max_by (Rubinius extension.) + +commit 560b6460745c7821b9479b356c032a10daaa61ec +Author: Eero Saynatkari +Date: Sat Jan 26 18:53:52 2008 -0500 + + Specs for Enumerable#min_by (Rubinius extension.) + +commit 6ab0bc901bdc60bde1e251f72f0028dfb736a2dd +Author: Eero Saynatkari +Date: Sat Jan 26 14:25:41 2008 -0500 + + Updated excludes for Enumerable. + +commit 2e09eedb31e15c791e491e97bc2af1977a629c2b +Author: Eero Saynatkari +Date: Sat Jan 26 14:20:19 2008 -0500 + + Fixed Enumerable#max and #min nil problems using Undefined. + +commit d8e6ebf604fdcc228e9158336250dd29c6d35932 +Author: Jacob Maine +Date: Sat Jan 26 13:22:33 2008 -0500 + + Failing spec for finding max when Enumerable contains nil + + Signed-off-by: Eero Saynatkari + +commit 313ee6badb177c101e39e122c5b5d6ff4d73d93d +Author: Jacob Maine +Date: Sat Jan 26 13:01:06 2008 -0500 + + Failing spec for sorting a list that contains nils + + Signed-off-by: Eero Saynatkari + +commit 467e8a60e0e25003894013e68f6d48e7bd6a22fc +Author: Jacob Maine +Date: Sat Jan 26 12:01:15 2008 -0500 + + adding failing spec for sorting enumerables that contain nils + + Signed-off-by: Eero Saynatkari + +commit 8719a4ad46d7643c6e54aab3dffedb6831bde5f3 +Author: Eero Saynatkari +Date: Sat Jan 26 13:42:11 2008 -0500 + + Whitespace cleanup before applying Enumerable patches. + +commit 015a0d023e8c649160800ddb8a269aa789266d51 +Author: Eero Saynatkari +Date: Sat Jan 26 13:31:00 2008 -0500 + + Added/changed the Dir open specs after previous simplification. + +commit 6735df441af2489d47674b0cc500dab37dd4319e +Author: Jonathan Younger +Date: Thu Jan 24 17:09:49 2008 -0700 + + Simplify Dir#open spec dependencies. + + The "takes a block which yields the Dir instance and closes it after" + expectation was failing because it depended on File.for_fd working + properly with closed file descriptors which it does not. + + This revision removes the dependency on File.for_fd as well as + IO.sysopen (which is not yet implemented in jruby) such that + the spec now passes on ruby, rbx and jruby. + + Signed-off-by: Eero Saynatkari + +commit 27834ebec570c78011eaaf37998272d46ab9d118 +Author: Dirkjan Bussink +Date: Sat Jan 26 21:51:13 2008 +0100 + + Added Date#strftime specs and fixed some constants + +commit 767e58ec38af7c3bc78dd98541fb8235616e9691 +Author: Dirkjan Bussink +Date: Sat Jan 26 21:45:23 2008 +0100 + + Add spec for Rational#round, works because of added Numeric#round + +commit 2497d3b7b9d6112356204dc429c3c368e1a65573 +Author: Brian Ford +Date: Sat Jan 26 12:31:45 2008 -0800 + + Templates for Rational specs. + +commit 5d63550a13cad4acbae3ae67e9ee9f672cbe5e61 +Author: Evan Phoenix +Date: Sat Jan 26 00:47:35 2008 -0800 + + Revert back old date.rb, but use newer date/format.rb, with some fixes + +commit ecd3ee8a0a528f516283558585b86e729bd388ec +Author: Vladimir Sizikov +Date: Fri Jan 25 21:47:38 2008 +0100 + + Updated not_compliant_on --> not_supported_on, where appropriate. + +commit 78ca098893d6231f74386eeadf0c30787f3dd2e6 +Author: Evan Phoenix +Date: Fri Jan 25 12:18:43 2008 -0800 + + A couple of easy fixes, fix Time to handle 2 digit dates, pull in trunk date + +commit fe8433cda8ca49835e2581f35bbf0d31025e84c1 +Author: Vladimir Sizikov +Date: Fri Jan 25 18:42:31 2008 +0100 + + Better detection of AF_INET6 support in socket specs. + +commit 1834801229bf8b2c0abfea4c18448ed105691682 +Author: Vladimir Sizikov +Date: Fri Jan 25 18:16:56 2008 +0100 + + Added a guard for undefined AF_UNIX in Socket specs. + +commit 3af242cc180675272ee24d588f3328bc11342048 +Author: Vladimir Sizikov +Date: Fri Jan 25 14:32:04 2008 +0100 + + New specs for IO#seek, IO#pos=, StringIO#seek and non-fixnum args. + + Rubinius fails all of them. + +commit 0ef7d55ebb5108bd5cf2f951236c8fade3999dfb +Author: Vladimir Sizikov +Date: Fri Jan 25 13:42:08 2008 +0100 + + New specs for String#unpack with 'Q/q' patterns. + +commit 907081db80262a1403f659433934ef707c2ddee0 +Author: Vladimir Sizikov +Date: Fri Jan 25 12:35:07 2008 +0100 + + Adjusted socket specs, so they pass on MacOS (MRI/JRuby). + +commit 9cca76acbe066da357692a19d5af1c8f5e4601c9 +Author: Evan Phoenix +Date: Thu Jan 24 17:31:38 2008 -0800 + + Fix race in compiler version number, fix regex spec + +commit be18fcc2e0ee16f861f1e2bff0636c3288bce8d6 +Author: Evan Phoenix +Date: Thu Jan 24 15:10:00 2008 -0800 + + Be more flexible with set_priority (OSs are a bitch) + +commit 845336d81df42b5d1f93123ef148b78c2b220d25 +Author: Evan Phoenix +Date: Thu Jan 24 13:46:08 2008 -0800 + + Kernel flesh out, passes all but 1 spec now + +commit ab87e7641336dfb07f0ad99cc2881ec59a25053a +Author: Jonas Pfenniger +Date: Thu Jan 24 12:36:50 2008 +0100 + + Changed __const_set__ to handle corner cases. + + * Kernel#__const_set__ is now the catch-all. It triggers on things like : + M = 3 + M::M = 3 + * MAIN#__const_set__ is forwarded to Object + * Module#__const_set__: logic has been moved here, it is no more and alias + of Module#const_set because it needs to trigger a warning on re-assignment. + +commit 13dbdf62e802028cb61f9375196712f0b789ff37 +Author: Vladimir Sizikov +Date: Thu Jan 24 21:30:02 2008 +0100 + + Added some SystemCallError specs. + + And exclusions for rubinius too. + +commit 75e2aac1d4b031fa36c8967549452436521b5eea +Author: Evan Phoenix +Date: Wed Jan 23 19:41:30 2008 -0800 + + Rework Class.new and Module.new to initialize without VM help + +commit 2551e57644d091d44e5e2fa715a017a557a0b18c +Author: Evan Phoenix +Date: Wed Jan 23 19:03:51 2008 -0800 + + Userland now uses __const_set__ for 'A = 3' syntax + +commit 400c5ceaf677aa2cd05a451c22144613ad7bdbe9 +Author: Evan Phoenix +Date: Wed Jan 23 14:32:26 2008 -0800 + + Introduce kernel/user land. Adds use of Module#__add_method__ + +commit 9ee17f227ebe572b09d44b3b0d703b9f95717751 +Author: Eric Hodel +Date: Wed Jan 23 14:50:25 2008 -0800 + + Fix Hash#clone + +commit bf4875d337017736bd94781c1bf4cd7500fae5f5 +Author: Dirkjan Bussink +Date: Wed Jan 23 22:55:02 2008 +0100 + + Implemented Enumerable#inject fix + +commit 0bd07f50ba75910ab579e3356dae93bc32b695bd +Author: Jacob Maine +Date: Wed Jan 23 15:46:06 2008 -0500 + + Enumerable#inject(nil) should yield nil as the first 'memo' + + * Currently yields the first element of the enumerable instead + +commit 319f937284e60acc156c6b7f91e56d460e65ac94 +Author: Vladimir Sizikov +Date: Wed Jan 23 18:12:56 2008 +0100 + + Excluded the IO#new spec. + +commit 04da4120d939603d4a64aab71bbf94ca202e04b2 +Author: Vladimir Sizikov +Date: Wed Jan 23 18:11:45 2008 +0100 + + Added IO#new spec (block should be ignored, warning printed) + +commit 5617c3eb81a3f1d8f9a581695fe7897fadee500a +Author: Vladimir Sizikov +Date: Wed Jan 23 17:50:48 2008 +0100 + + Excluded failures after IO specs additions. + +commit 107a072689bc9b97842f049f4fab2860ab79237d +Author: Vladimir Sizikov +Date: Wed Jan 23 17:48:34 2008 +0100 + + Added specs for IO#open/popen, File#open with close inside block. + +commit bc3393a9041f8116d53bedfa6b604ec6dce3fd19 +Author: Jonas Pfenniger +Date: Wed Jan 23 14:25:13 2008 +0100 + + Revert "Added Module#name memoization spec". dbussink told me this behavior + is not wished. + + This reverts commit ff411600202a59d00ffaca2c51330599c6b84966. + +commit 73e7d61d756cb7a06ea18b7f92c49bbebb06cc3f +Merge: ff41160... 1dab607... +Author: Jonas Pfenniger +Date: Wed Jan 23 13:57:28 2008 +0100 + + Merge branch 'master' of git@git.rubini.us:code + +commit ff411600202a59d00ffaca2c51330599c6b84966 +Author: Jonas Pfenniger +Date: Wed Jan 23 13:55:36 2008 +0100 + + Added Module#name memoization spec + +commit 1dab607a79b79b370eda4776daf07a262451aea0 +Author: Vladimir Sizikov +Date: Wed Jan 23 13:28:29 2008 +0100 + + Added IO#close specs. + +commit c78091236495f4a16aa874de97cce3ec485c1f5b +Author: Vladimir Sizikov +Date: Wed Jan 23 12:54:25 2008 +0100 + + Added IO#close_write and IO#close_read specs. + + And all of them fail in rubinius. (not implemented) + And most of them fail in JRuby. (recent bugs) + They pass just fine on MRI 1.8.6 (p111 and latest) + +commit dc39943a4595855d64f23f9155a4e9cf658c39a3 +Author: Dirkjan Bussink +Date: Wed Jan 23 10:37:45 2008 +0100 + + Small refactor of regexp_new + +commit 132ac4986a648dbf1354216145e5715a727a735b +Author: Eero Saynatkari +Date: Wed Jan 23 00:14:34 2008 -0500 + + Type.coerce_to no longer falls prey to identity fraudsters. + + * Type.obj_kind_of? directly uses the internal kind_of instruction + so that overridden #kind_of? does not get in the way. + * Type.coerce_to uses Type.obj_kind_of? for its checks so that + Core can safely use it without worrying about breakage due to + overridden #kind_of?. + * Specs for both and a little documentation. + +commit 192882902154c9a68554337ccd1b8f3ee9aedd9e +Author: Evan Phoenix +Date: Wed Jan 23 00:42:24 2008 -0800 + + Remove Symbol#to_i and Symbol#to_int. Farewell bastard children. + +commit f854667ff62528fe541c8cf67b9a1b291598d654 +Author: Evan Phoenix +Date: Wed Jan 23 00:35:29 2008 -0800 + + Removed Fixnum#to_sym and Fixnum#id2name, as well as fixed specs + +commit eb6cbc3604c81cc093edb1c182be1e456b05bef6 +Author: Adam Gardiner +Date: Wed Jan 23 16:30:22 2008 +1100 + + Added specs for context iseq manipulation + + Added specs to test MethodContext#reload_method and + +commit fd5fb764ee21b354b75b84f34906663874a24639 +Author: Charles Comstock +Date: Tue Jan 22 22:28:02 2008 -0600 + + spec for DRb method call using a block + +commit a928762b48f7dc84bba0d43125063e9d8d54f183 +Author: Eric Hodel +Date: Tue Jan 22 19:15:10 2008 -0800 + + Better test of #instance_method. + +commit c055a5981bf4ecfd2efc0df74adb071056ff83b9 +Author: Ryan Davis +Date: Tue Jan 22 15:40:59 2008 -0800 + + removed remove_method_excludes.txt + +commit 08cb27454e7ae73e79bb432887dba917feaa1f92 +Author: Ryan Davis +Date: Tue Jan 22 15:36:36 2008 -0800 + + Clarified undef/remove specs a bit. + Fixed remove_method to raise NameError if you're not acting on local method. + Removed some fails_on calls to make specs pass... gonna remove them all soon. + +commit 62d93ac7916ff0d56a5b40ae1b9b501f10081638 +Author: Evan Phoenix +Date: Tue Jan 22 15:35:05 2008 -0800 + + Fix up sysread and syswrite, disable testing for warnings on rubinius + +commit a482b17c4bfe9f40474839ba0cce2a37d8524c62 +Author: Evan Phoenix +Date: Tue Jan 22 15:13:19 2008 -0800 + + Remove stale binding excludes + +commit f45030d33a9e1fe3c6bc111401a893e5649239f7 +Author: Evan Phoenix +Date: Tue Jan 22 15:03:37 2008 -0800 + + Update Proc excludes + +commit 7f932fbdf5fa4e16df10d7731313d458ca21966c +Author: Evan Phoenix +Date: Tue Jan 22 14:55:50 2008 -0800 + + Add Proc#== + +commit 811cbe8ef876ef452051a9b07b3c95dbf57a7d9f +Author: Vladimir Sizikov +Date: Tue Jan 22 23:41:55 2008 +0100 + + Removed debugging stdout from one spec. + +commit 2bf52de43bb90721d921f6d29504a8f098ed09b5 +Author: Ryan Davis +Date: Tue Jan 22 14:11:17 2008 -0800 + + Removed a lot of passing specs from the excludes + +commit b0e5a9ba6577c301f2737682d745128e268ebdab +Author: Ryan Davis +Date: Tue Jan 22 14:02:38 2008 -0800 + + Fixed Symbol::all_symbols + +commit 68ae0b5acd647b9ebd73e53638b728cfaee6b6e0 +Author: Vladimir Sizikov +Date: Tue Jan 22 22:09:45 2008 +0100 + + Revert "Completed MRI's Module#name spec with corner case." + + This reverts commit 970ede321d31ec75dd578866c683defe768fa356. + + This spec seems like an implementation detail rather than + a specified behavior. It was agreed on IRC to revert it, + and that rbx won't support it. + +commit cc0e45cab2167e0fbc1d29308a5dcb4e7e077319 +Author: Vladimir Sizikov +Date: Tue Jan 22 22:06:28 2008 +0100 + + Revert "Module#name memoization work." + + This reverts commit 7cd9fce4908aaeea9a35e273a3f15ed7ee7aa783. + +commit 996f9f4e5fc05f1b3aa618db3e1a4947730780b7 +Author: Evan Phoenix +Date: Tue Jan 22 12:23:39 2008 -0800 + + Fix LongReturnException to be terminated in the correct place + +commit f453121dd2f3b4d9506a3f1c1e61d24e46bc9083 +Author: Vladimir Sizikov +Date: Tue Jan 22 20:53:45 2008 +0100 + + Corrected Module#instance_method failing spec. It was failing on all impls. + +commit c1d59239ddea95e73e2edd3a97ed6e1113a35d3c +Author: Vladimir Sizikov +Date: Tue Jan 22 19:58:32 2008 +0100 + + Corrected Module#instance_method spec, it was failing on MRI/1.8/1.9/JRuby. + +commit ef5f4489caac2ad4bad94783a780aa40a054481c +Author: Vladimir Sizikov +Date: Tue Jan 22 19:30:48 2008 +0100 + + Corrected String#to_f spec. + +commit 7cd9fce4908aaeea9a35e273a3f15ed7ee7aa783 +Author: Charles Nutter +Date: Sun Jan 20 22:00:34 2008 -0600 + + Module#name memoization work. + + * Module#name is now memoized on access if @name is set + * Full module path is lazily calculcated on memoization + * Module#const_set(Ruby) and module_const_set(C) only set @name and @parent. + * The following methods unifily use module_const_set: + * cpu_const_set + * cpu_open_class + * cpu_open_module + * module_setup_name + * Module#calculate_name reworked, hack removed + +commit 7b4ef1344812faa76018ab41cc7fba97a3af8448 +Author: Jeremy Roach +Date: Tue Jan 22 02:47:49 2008 -0600 + + implement more of Marshal.load + + Float, obj._load, obj.marshal_load, IO.read, proc arg + +commit 41f07f0253a8fba205dbb0402e5d5e88c115d76c +Author: Adam Gardiner +Date: Tue Jan 22 16:32:01 2008 +1100 + + Fix Task#get_stack_value to not raise exception from primitive + +commit 14c811adaba3e8cfc5104d70e67c2e89c18cac4d +Author: Brian Ford +Date: Mon Jan 21 19:28:18 2008 -0800 + + Exclude Kernel#require is private spec when running with RSpec. + +commit 05a180e0051a0409c685d326a41e882545faaa53 +Author: Brian Ford +Date: Mon Jan 21 19:26:41 2008 -0800 + + Explicitly run /bin/sh to get around limited /bin/pwd on linux. + +commit c09b3da391995a0e9006055ce19e838d3f180947 +Author: Brian Ford +Date: Mon Jan 21 19:09:07 2008 -0800 + + Protect String#% specs from segfaulting on linux (ubuntu gutsy). + +commit d36b3f65f92b08ae078812788482387077d03380 +Author: Brian Ford +Date: Mon Jan 21 17:55:03 2008 -0800 + + Removed use of `pwd -P` as at least ubuntu bin/pwd doesn't support it. + +commit 6d7a8292fb9a68a0dcfbd3f29f595e0ecf5902ae +Author: Brian Ford +Date: Mon Jan 21 17:54:11 2008 -0800 + + Unexclude Kernel#callcc specs as Evan's recent commits seems to fix it. + +commit eb04d409575772a85510770bd0db4f36490de6aa +Author: Dirkjan Bussink +Date: Mon Jan 21 13:28:06 2008 +0100 + + Fix Regexp error handling + +commit 93e50808eb7355c404a7f5295923083c8cf63549 +Author: Charles Nutter +Date: Sun Jan 20 22:00:34 2008 -0600 + + Quarantining IO#dup spec "sharing" example due to platform differences. + +commit ae9e2829becc495892c7ddce5eae67514f268120 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 10:26:56 2008 +0800 + + Update Module instance_method_specs excludes after revert put it back in + +commit df6c82f97987c233eab0534740054e2d0f0f2f2c +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 10:25:00 2008 +0800 + + Revert "Update CI excludes for Module" + + This reverts commit 8aa00146f2eee9576094daa76c6f158b0deaf2e2. + + * Fails when run with other specs + +commit 6f5245d4c20bf009bc120967f4a93d24faae66ba +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 10:23:15 2008 +0800 + + Revert "Update CI excludes for Symbol.all_symbols spec" + + This reverts commit cb27e31b2a757ad108842bfa579eb9170d6cf244. + + * Returns an F if run with other specs in ./bin/ci + +commit ec9677e593247ed8dfcbfc680151d04ac97936e3 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 10:11:35 2008 +0800 + + Fix Module instance_method_spec to match the inspect with a regexp + +commit 7e3474a16ec20094630e865594405ea7f1658c58 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 10:08:06 2008 +0800 + + Module#instance_method raises TypeError/ArgumentError on invalid arg + + * Fixed spec to expect TypeError when passed nil + * Fixed spec to expect ArgumentError when passed non-symbol/string + +commit 8aa00146f2eee9576094daa76c6f158b0deaf2e2 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 09:41:37 2008 +0800 + + Update CI excludes for Module + +commit 9158b959d30babdceafc416650c1ba3234e5029a +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 09:10:06 2008 +0800 + + Add alias for Proc.to_s from Proc.inspect + +commit cb27e31b2a757ad108842bfa579eb9170d6cf244 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 09:09:40 2008 +0800 + + Update CI excludes for Symbol.all_symbols spec + +commit 7f16f313c907de0e22762d97fbba24e70c3259a3 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 21 08:46:36 2008 +0800 + + Raise TypeError/ArgumentError for invalid Thread key + + * Raise TypeError is key is nil + * Raise ArgumentError is key is not Symbol or String + * Correct the description of Thread's element_set_spec to use #[]= + +commit 0b849f884beae9d11327e315da5c79fe789b8391 +Author: Vladimir Sizikov +Date: Sun Jan 20 23:05:33 2008 +0100 + + Added rubyspecs for Zlib.crc32. + +commit 67b52b6fb92b9e9a037e584474cff2dc97ce0163 +Merge: e6d8a61... 6f08d5e... +Author: Jonas Pfenniger +Date: Sun Jan 20 22:30:52 2008 +0100 + + Merge branch 'master' of git@git.rubini.us:code + +commit e6d8a61771b76198c0784677bb0a8fc97b1988bc +Author: Jonas Pfenniger +Date: Sun Jan 20 22:30:29 2008 +0100 + + Fixed Struct#[] and Struct#[]= with negative indexes. + + * Added corresponding specs + * Fixed kernel/core/struct.rb code + + Example: + + s = Struct.new(:x, :y) + x1 = s.new(:a, :b) + x1[-4] #=> should raise IndexError: offset -2 too small for struct + +commit 6f08d5e21473d0f2adff66a32acd46ddd8945fa0 +Author: Brian Ford +Date: Sun Jan 20 12:47:11 2008 -0800 + + Added spec for Kernel.format. + +commit 6ab2691b455ac07643d98dc58f8a0f45487ab20d +Author: Brian Ford +Date: Sun Jan 20 11:52:56 2008 -0800 + + Added sane handling of non-reals for #format %e, %E. + +commit 1caab1ce237a52d78a402a5f0a7ce1d3ed9ac6b7 +Author: Dirkjan Bussink +Date: Sun Jan 20 18:55:27 2008 +0100 + + Add spec for singleton_methods and fix for Fixnum + +commit d7c46a0a1660f1d53e03a97571f3ec7b2431d0e4 +Author: Dirkjan Bussink +Date: Sun Jan 20 12:29:10 2008 +0100 + + Added failing spec for Regexp#new that could cause a segfailt. Needs error handling as stated in shotgut/lib/regexp.c:122 + +commit d15c6605b7fb7db337d87ac1bd15f9a1371caa42 +Author: Brian Ford +Date: Sat Jan 19 19:28:00 2008 -0800 + + Added language spec for return within a block. + +commit 3b516c028c4c9e064fbe839f0f9402a135eb90b0 +Author: Brian Ford +Date: Sat Jan 19 17:27:13 2008 -0800 + + Added spec for class vars set from Kernel#instance_eval based on #267. + +commit 970ede321d31ec75dd578866c683defe768fa356 +Author: Jonas Pfenniger +Date: Sun Jan 20 00:44:04 2008 +0100 + + Completed MRI's Module#name spec with corner case. + + It looks like Module#name is memoized in MRI + +commit caf440ac6a8037a2c223834c0ca4c5decd8e68ab +Author: Vladimir Sizikov +Date: Sat Jan 19 22:48:31 2008 +0100 + + Revert "Wrapped one spec to prevent JRuby crash." + + This reverts commit 9f266e3c785c7e3edbb6a30271f32debe6c14164. + JRuby issue is resolved. + +commit 9673e2c1c5a1142af52a0d82d8981bdd9e236c27 +Author: Brian Ford +Date: Sat Jan 19 12:46:12 2008 -0800 + + Changed IO#syswrite to use should complain matcher. + +commit 8522186df7050782c4911f40aef381106e5e8c5b +Author: Brian Ford +Date: Sat Jan 19 12:29:12 2008 -0800 + + Added mSpec lambda { .. }.should complain matcher for warnings. + +commit 9f266e3c785c7e3edbb6a30271f32debe6c14164 +Author: Vladimir Sizikov +Date: Sat Jan 19 21:24:13 2008 +0100 + + Wrapped one spec to prevent JRuby crash. + +commit 54d1989997561271553ba72bd99f59ef2deb7c72 +Author: Jonas Pfenniger +Date: Sat Jan 19 19:13:06 2008 +0100 + + Fixed "X::X = 3". It would return a tuple instead of 3. + + * changed shotgun's const_set instruction to push the variable on the stack. + * added corresponding specs under `language' + * found a new problem but added it to excludes + +commit d25ec129902789bc7d636ff5ccda8ff858ae38d3 +Author: Brian Ford +Date: Sat Jan 19 10:21:19 2008 -0800 + + Added spec/README. Reformatted mspec/README. Removed old sprintf spec. + +commit 5eb06e3010707de1e273c23b3f0addf2ceaa824d +Author: Brian Ford +Date: Sat Jan 19 09:37:29 2008 -0800 + + Removed unused Sprintf, rename YSprintf to Sprintf. + +commit c144abc12230175a2a503c4426804ed19c8559e7 +Author: Dirkjan Bussink +Date: Sat Jan 19 16:21:08 2008 +0100 + + File::Stat time functions should return Time objects and added stat and lstat instance methods on File + +commit 177ef99db435a59e942566f7904167fc5e849d8d +Author: Kamal Fariz Mahyuddin +Date: Sat Jan 19 22:24:16 2008 +0800 + + Fix ThreadGroup's add spec + + * Fix is by initializing a new ThreadGroup on Thread setup + +commit 3fad84ec370eda1cab596adf5589e43240dfc381 +Author: Jonas Pfenniger +Date: Sat Jan 19 12:16:55 2008 +0100 + + Removed empty *excludes.txt for better searchability + + `find -name "*excludes.txt" -size 0 -exec git rm {} \;` + +commit 9a2b1e6232f36c7a1508085b4606e25fbcf3cb4a +Author: Jonathan Younger +Date: Fri Jan 18 23:19:16 2008 -0700 + + Additional String#% platform specific formatting failure fixes + +commit aa32d6fcbed79b9e2afedc00f429ea78f5c540d3 +Author: Jonathan Younger +Date: Fri Jan 18 22:12:23 2008 -0700 + + Fix String#% platform specific formatting failure + + Darwin and FreeBSD return a different string format than other platforms, + so a different expectation is needed to match the appropriate format. + +commit c64dfd449dc89ec0016f14afd7f85522dbaa4148 +Author: Jonathan Younger +Date: Fri Jan 18 21:05:44 2008 -0700 + + Moved String#% specs to ruby/1.8 and fixed to work with rbx and MRI + +commit e5e7f44983f1dbbc79726776b56a9cc7cb910e9f +Author: Brian Ford +Date: Fri Jan 18 19:53:50 2008 -0800 + + Updated IO excludes. + +commit b8b549dbc1aaf63e15717c3902d4485c97f845f7 +Author: Charles Nutter +Date: Fri Jan 18 21:40:01 2008 -0600 + + Add regexp matching for output matcher and enable warning specs for syswrite. + +commit 004bd58b597034cbe734d9b7da318135a689190f +Author: Jeremy Roach +Date: Fri Jan 18 21:06:38 2008 -0600 + + implement some of Marshal.load + +commit 2c52db8022f060866d839992aaa6bff0f61963cf +Author: Kamal Fariz Mahyuddin +Date: Sat Jan 19 11:21:55 2008 +0800 + + Fix UnboundMethod#bind + +commit a5680db20cf998f0db292e3b9aa69ed74fb19b10 +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 18 17:49:44 2008 +0800 + + Implement UnboundMethod#== + +commit a1de7b0f405830f6bfe8000c051f4445135d8f63 +Author: Charles Nutter +Date: Fri Jan 18 20:54:11 2008 -0600 + + Added some specs for sysread/syswrite on a file and p flushing to File. + +commit e7bc994d96398519ca205e87dec8e775bb0e67c6 +Author: Dirkjan Bussink +Date: Sat Jan 19 01:31:46 2008 +0100 + + Fixed File#utime segfault + +commit 9887c6135e9353c3094dcf3c76d8e788b98f2bed +Author: Vladimir Sizikov +Date: Fri Jan 18 22:00:10 2008 +0100 + + Added Time#strftime specs for '%U' and '%W' patterns. + +commit 0338fb5adb325e58d1ce61bccc9310fc7284e235 +Author: Vladimir Sizikov +Date: Fri Jan 18 20:35:23 2008 +0100 + + Added two testcases for String#% rubyspecs ('x', 'X'). + + There was a bug in JRuby's sprintf, which wasn't + detected by rubyspecs. + +commit 97db9fb72d6205227d61d92ed3153331b2328f97 +Author: Brian Ford +Date: Fri Jan 18 10:50:00 2008 -0800 + + File#utime spec. Some cleanup of File#open specs. + +commit 10647cf8abfd0ea7a87d39978a22f68fdfa9fbd6 +Author: Vladimir Sizikov +Date: Fri Jan 18 17:08:04 2008 +0100 + + Wrapped 3 IO spec tests into fails_on :jruby. + + Because these tests just break the spec run completely. + +commit cf6195eeabe382c4267e295ab786acedaed89050 +Author: Jonathan Younger +Date: Thu Jan 17 21:55:48 2008 -0700 + + Fix specs that use `pwd` to use -P option so that symlinks are resolved + +commit 1b79705fb965ecd6fc897b6bf14c605d8325dabe +Author: Dirkjan Bussink +Date: Fri Jan 18 15:57:46 2008 +0100 + + Added IPAddr specs by manveru. Closes #262 + +commit f05b96b33970e3f08da5c8992f7c6cb710649f42 +Author: Eric Hodel +Date: Fri Jan 18 02:23:15 2008 -0800 + + Fix spec for IPv6 environments + +commit 2007019ebad7974d7a54e6d599320675548313f0 +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 18 17:39:09 2008 +0800 + + Fix UnboundMethod#inspect to use regexp + + * Also aliased UnboundMethod#to_s to UnboundMethod#inspect + +commit c47b473b99b59074673adb7e8d50a250e34436e7 +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 18 17:37:29 2008 +0800 + + Use a regexp to match the inspect output instead of deviating on rbx + +commit 8dc2a2b3115a49a15ed931301b1999560ee27db5 +Author: Evan Phoenix +Date: Fri Jan 18 01:05:14 2008 -0800 + + Fix up specs and finalize LongReturnException + +commit 7c30ca7337b56a4194eb58952f74662e222b7707 +Author: Evan Phoenix +Date: Thu Jan 17 23:23:27 2008 -0800 + + Add support for return in a block obeying ensure properly + +commit c06fc665c6bf5898163f2854b93d62b8b314216e +Author: Brian Ford +Date: Thu Jan 17 21:20:42 2008 -0800 + + Changed Exception#backtrace to return an MRI compatible one. + + * Exception#awesome_backtrace returns an Rubinius Backtrace + instance, as Exception#backtrace used to. + * Added templates for Backtrace specs. + * UnHACKed lib/test/unit to use the #backtrace as expected. + +commit a29f35c5a45776f10132c3ce0ef058b1e98a4f75 +Author: Wilson Bilkovich +Date: Thu Jan 17 20:18:04 2008 -0500 + + Guard failing Process.setpriority spec, add an exclude to CI + +commit 333d5c6920c01366c8b2887ecc7e33f775210c00 +Author: Brian Ford +Date: Thu Jan 17 16:50:31 2008 -0800 + + Specs and fixes for Class.inherited. + +commit ac90d87a69c19c441b854660105d21ed771989f0 +Author: Ryan Davis +Date: Thu Jan 17 16:37:31 2008 -0800 + + One more step into the foray of bootstrap madness... removed useless 0 from lasgn nodes. needs full clean + +commit 0dbabefd081be4890d0d789a9c3ec122b9196cf8 +Author: Brian Ford +Date: Thu Jan 17 12:00:55 2008 -0800 + + Fixes to enable RSpec 1.1.2 to run the spec/ruby specs. + +commit 9bd611ff5c5b411518c2f4ce5d3cd4b93f4bcebe +Author: Eero Saynatkari +Date: Thu Jan 17 08:25:24 2008 -0500 + + IO#puts, #isatty fixes from Dan Lucraft, slightly modified. + + * The #puts spec exposed an issue with String#suffix? which Ifixed in + fa9a6c which means IO#puts did not need to be changed. + * #puts spec uses output_to_fd. + +commit 3f519a98bbc3a66d59884add5fcd98d5ca095149 +Author: Eero Saynatkari +Date: Thu Jan 17 08:02:51 2008 -0500 + + String#suffix? specs and correct behaviour. + + * The suffix can be the entire string, there is no need for it to + just be a substring always. + +commit ca6fa9cd760b06827f4f953ff28e8baed357f447 +Author: Dirkjan Bussink +Date: Thu Jan 17 12:48:20 2008 +0100 + + Additional Date specs + +commit 8541022ffc918879142ecb3707e977050f774ece +Author: Brian Ford +Date: Thu Jan 17 00:47:04 2008 -0800 + + Added ?d, ?e, ?f to Kernel#test. + +commit e6f36980c2c94414e5c051b35d9ce403c492f1a2 +Author: Charles Nutter +Date: Thu Jan 17 02:18:25 2008 -0600 + + Fix my oops; missing 'do' for the fails_on + +commit 44483d8e414f107b3202cc69b8cdfbbe1222ee33 +Author: Charles Nutter +Date: Thu Jan 17 02:07:57 2008 -0600 + + Add a fails_on guard to IO#printf spec for JRuby; output dies otherwise + +commit 53a36c934ec44fad7e6d18424cb13b37496cf720 +Author: Brian Ford +Date: Wed Jan 16 18:39:34 2008 -0800 + + Updates to compiler and core to protect Fixnum#/. + + * Added compiler plugin SafeMathOperators. + * Added very simplistic way to pass flags to the compiler. + * Added -frbx-safe-math flag + * Changed core Fixnum, Float, Bignum, and Numeric methods + to use #divide rather than #/. Aliased #/ to #divide. + * Updated Rakefile to send flag when compiling core. + +commit feb260b904d87487428b558f7b7e9ac0170c160c +Author: Ryan T Mulligan +Date: Wed Jan 16 22:47:02 2008 -0600 + + very minimal SHA1 specs + +commit 511732d932d2fe934968c78b89cefa46e699b996 +Author: Ryan Davis +Date: Wed Jan 16 16:30:40 2008 -0800 + + Moved old spec excludes to new location and deleted all old + +commit cae6bba077190e158ceee7b8991daf16fd8c55d1 +Author: Dirkjan Bussink +Date: Wed Jan 16 13:36:19 2008 +0100 + + Added more Date specs + +commit 75d49657f31091d37dfdba1fc5487164db861802 +Author: Charles Comstock +Date: Wed Jan 16 15:58:42 2008 -0600 + + more specs for TcpServer and TcpSocket + +commit b1d45cb0fdc573bfe5995456d846c11747d48b90 +Author: Evan Phoenix +Date: Wed Jan 16 13:31:33 2008 -0800 + + Implement ObjectSpace.define_finalizer, using WeakRef. + +commit 4e8a0d264dc7d5a4866a1a1b83238bebb47e4ab6 +Author: Jonas Pfenniger +Date: Wed Jan 16 21:51:52 2008 +0100 + + Removed platform-specific specs in spec/ruby/1.8/core/signal/list_spec.rb + + There is not direct way to know if a signal exists or not, since it all + depends on . In practice, there is no real risk for rubinius + to miss a signal. + +commit 32537f8d3378154f7f52c278cd56a7d4159a3446 +Author: Brian Ford +Date: Wed Jan 16 12:41:54 2008 -0800 + + Added IO#printf, fixed Kernel#printf to use IO's. + +commit e7bccb3f38f6ace3cb25a9f227ab5f6b1d2be346 +Author: Jonas Pfenniger +Date: Wed Jan 16 21:19:48 2008 +0100 + + Signal.list spec now passes. bin/ci removed list_excludes.txt + +commit b3a6461af30f2c144b4ee65e8539c51291e0156b +Author: Jonas Pfenniger +Date: Wed Jan 16 20:03:15 2008 +0100 + + kernel/core/signal is no more platform dependent + + * Now publishing platform.conf with rbx.platform.signal.* (only using the ones + defined in MRI's "signal.c") + * Signal::Names is now published with those values on @after_loaded@ + * New method: Signal.list => Signal::Names + * Added EXIT=>0 and CLD=CHLD exceptions (see "signal.c" in MRI) + * Updated the corresponding specs for more details (on FIXME, please help !) + +commit bdbd712a5953f011f8d6f1142d50a452e1607f65 +Author: Charles Comstock +Date: Wed Jan 16 13:42:01 2008 -0600 + + updated Continuation excludes -- Kernel#callcc specs still bleedover + +commit 22d32d3461660ee7cd29760163b622fc94b6ea5b +Author: Jeremy Roach +Date: Wed Jan 16 01:55:21 2008 -0600 + + apply Marshal.dump patch by Justin Bradford. #252 + +commit 513de8ab67ab9c017285a48108ccceb185ebaf24 +Author: Adam Gardiner +Date: Wed Jan 16 16:44:34 2008 +1100 + + Bunch of Debugger fixes + + * Debugger now has proper quit behaviour, which causes + the debugger to remove all breakpoints, clear the debug + channel, and resume the debuggee. + * Fix singleton(-ish) semantics of Debugger; essentially, + only a single Debugger instance can be instantiated at + one time. + * Added a bunch of specs for the above + * Changed Rubinius::VM.set_debug_channel to accessor + style Rubinius::VM.debug_channel. + +commit 2174009b215ce2f0445fc8df4711e7e6c64b0332 +Author: Jeremy Roach +Date: Tue Jan 15 21:59:30 2008 -0600 + + add Marshal.load specs + +commit ec002dd0f0daddedaa5241c4f8f6d85fad0e9768 +Author: Adam Gardiner +Date: Wed Jan 16 09:47:28 2008 +1100 + + Move VM under Rubinius namespace + +commit 983c54400542a03535accf2705ae227ae58970dc +Author: Charles Nutter +Date: Tue Jan 15 15:24:10 2008 -0600 + + Added spec for File.new coercing filename using to_str. + +commit ff6a081de28711b0d8c1136e6e4272baf769043c +Author: Charles Comstock +Date: Tue Jan 15 14:29:43 2008 -0600 + + since DRbObject is within DRb it should be a subdirectory but mkspec generated the wrong path to helper + +commit a48cbbd3f5da3c971a215423b3e27b058de04196 +Author: Dirkjan Bussink +Date: Tue Jan 15 21:15:09 2008 +0100 + + Add more Date specs + +commit 9de289f1bbae86b12bc383e7e535de404f8aaa5f +Author: Caleb Tennis +Date: Tue Jan 15 14:58:31 2008 -0500 + + Add a servent class to StructGenerator for Socket.getservbyname + + Also, add Socket.htons and Socket.ntohs for byte order encoding + + And complete Socket.getservbyname along with specs. + +commit d9e37ff3c0f975a418fafbc7163ee1a9717dd92b +Author: Kamal Fariz Mahyuddin +Date: Wed Jan 16 03:31:05 2008 +0800 + + Fix Proc#[] calling the wrong #call method + + * Re-aliasing in Proc::Function because aiases don't follow subclass + methods + +commit 2273c919e80ab7186e3139941dc4d73a292bcd2d +Author: Dirkjan Bussink +Date: Tue Jan 15 19:30:23 2008 +0100 + + Add add and minus specs for Date + +commit 1325e22c11c48c366d9f0387823de5941b59df66 +Author: Dirkjan Bussink +Date: Tue Jan 15 16:35:23 2008 +0100 + + First specs for Date object + +commit a3b76d162e58e75b4523151bb6911c840db8319f +Author: Caleb Tennis +Date: Tue Jan 15 12:58:14 2008 -0500 + + Implement Socket#pair (and Socket#socketpair) with corresponding spec. + +commit 836f1cf828ab62606a6b0e2f7313228b7482dcbe +Author: Charles Comstock +Date: Tue Jan 15 11:47:11 2008 -0600 + + generate spec files for DRbObject + +commit ef99f25be36f6ccd33b297bed14c1175847f1ecc +Author: Charles Comstock +Date: Tue Jan 15 11:31:08 2008 -0600 + + generated spec files for DRb with a basic spec for DRb.start_service + +commit 9637cf1e77efd1a3b53e6c4d82a7c7afe8509621 +Author: Vladimir Sizikov +Date: Tue Jan 15 18:48:09 2008 +0100 + + Eliminate stdout from IO#dup spec runs. + + At least, under JRuby it was printing things like: + "No such file or directory". + +commit 30a2fce2a4fd7e840586ce8ae390ecb632c8bee0 +Author: Kamal Fariz Mahyuddin +Date: Tue Jan 15 23:00:32 2008 +0800 + + Implemented Dir#pos which fixes #pos, #pos=, #seek, and #rewind specs + +commit 013ab2e88ecd8d887c6a0009e7f8d2add4849143 +Author: Kamal Fariz Mahyuddin +Date: Tue Jan 15 22:52:26 2008 +0800 + + Revert "Updated CI excludes" + + This reverts commit 15d1c7674496a99bf1d5ec42420864b22bf1569a. + +commit 15d1c7674496a99bf1d5ec42420864b22bf1569a +Author: Kamal Fariz Mahyuddin +Date: Tue Jan 15 21:50:55 2008 +0800 + + Updated CI excludes + +commit 18470055d83a43c3371609aaac4471767adb3b1b +Author: Eric Hodel +Date: Tue Jan 15 04:32:54 2008 -0800 + + Make TCPSocket.new work. Use socket library names for familiarity. + + Make inheritance hierarchy of sockets better match MRI. + + Add syscall names to Errno.handle checks. + + Spec less of the socket library. + +commit bd34303986a068b40cce1366c85ea288fc24a3f5 +Author: Eero Saynatkari +Date: Mon Jan 14 23:55:38 2008 -0500 + + Regexp subclasses work now. Documented Regexp.new. + +commit 343acee55519fc97a35a9d50e8bdcfd679d432b7 +Author: Eero Saynatkari +Date: Mon Jan 14 23:49:47 2008 -0500 + + More Regexp.new specs. + + * Subclass initialization verification. + * Multibyte options are case-insensitive. + +commit 758a468ffafdeea78016dbbce78f21e19f6735f6 +Author: Ryan Davis +Date: Mon Jan 14 18:22:32 2008 -0800 + + Fixed require modifying LOADED_FEATURES even if require raises an exception + +commit 5c8ff74b64f7ec6bd4c413b0e0e93334dff009ca +Author: Eero Saynatkari +Date: Mon Jan 14 21:57:19 2008 -0500 + + Fixed Regexp#kcode specs. + +commit 34867cc1f1f3b7ac3145fb926491c0dc44629312 +Author: Eric Hodel +Date: Mon Jan 14 22:41:58 2008 -0800 + + Add Socket::getaddrinfo. Raise SocketError appropriately. + +commit e2009a38a8e1ef0dff6394b92a677f3120280f72 +Author: Eric Hodel +Date: Mon Jan 14 22:39:55 2008 -0800 + + Remove platform-specific code, remove spec of socket library behavior. + +commit 5afa1c34808c68c17bc02f5f76c42d64efdd7dd2 +Author: Charles Nutter +Date: Tue Jan 15 00:32:14 2008 -0600 + + Modified retry-in-rescue example to test nested blocks and be clearer. + +commit 17fd0cb781ec90d268668c5678e1135eb5f6e323 +Author: Brian Ford +Date: Mon Jan 14 22:28:46 2008 -0800 + + Added Module#autload?. + +commit 96ca83312d1b5a1e38e25f94504f6f69a137b96d +Author: Brian Ford +Date: Mon Jan 14 21:50:37 2008 -0800 + + Updated CI excludes for language. + +commit ef4f49de672d40f43f53dadff1aa8fdbcafe1d45 +Author: Evan Phoenix +Date: Mon Jan 14 21:37:23 2008 -0800 + + Fix specs for dregx change, fix regexp for specs + +commit 5cd2ef2a173394910249d93d8ef433d220f2d9a9 +Author: Adam Gardiner +Date: Tue Jan 15 16:32:15 2008 +1100 + + Fix breakpoint specs + + The breakpoint specs were interfering with one another, + due to the fact that each was modifying the bytecode for + a fixture class that is compiled only once. + + Workaround this by saving off the bytecode and resetting + before each test. + +commit 29bf88b07f87182d94fcf7c550724efc07067239 +Author: Brian Ford +Date: Mon Jan 14 20:00:27 2008 -0800 + + File spec/data/critical.txt is empty! Congrats to everyone! + +commit 8082760cc2215742464a9846295ec4a8a0c49244 +Author: Brian Ford +Date: Mon Jan 14 19:37:33 2008 -0800 + + Removed Module methods from critical excludes. + +commit d075c115087c001d0d35562aeeea21efadc5e3b6 +Author: Brian Ford +Date: Mon Jan 14 18:53:46 2008 -0800 + + Added not_compliant_on :rbx for class variable specs that use Fixnums. + +commit 23f1b523da2478f2ad962f0045dca3e7034f9b56 +Author: Brian Ford +Date: Mon Jan 14 18:16:46 2008 -0800 + + Multiple fixes for #class_variable_get/set. Updated CI excludes. + +commit dbc5675058aa426dbfbbf7489d5393819edb16f8 +Author: Evan Phoenix +Date: Mon Jan 14 16:42:27 2008 -0800 + + Fix attrasgn usage to pass specs + +commit 3e250999d6f1a7fdaf2bb5cd169a1024e2ab5ddc +Author: Brian Ford +Date: Mon Jan 14 16:07:08 2008 -0800 + + Removed leftover excludes for compiler[12]. + +commit a2b8b5511e79b47fa7777e716ee16511fdec3fd4 +Author: Charles Nutter +Date: Mon Jan 14 16:47:21 2008 -0600 + + Remove the goofy Hash#delete spec and replace with two others. + + The old version of this example depended on individual hash buckets having a + specific ordering, which overreaches a bit. The new version, while a little + cumbersome, should work correctly regardless of hash implementation or hash + and bucket ordering. + + I also moved out a few lines that were unrelated to this example into a + separate one. + +commit 67d858885f1841e9c9aa295150da3c472949198d +Author: Gregor Schmidt +Date: Mon Jan 14 14:57:53 2008 +0100 + + Passes Module#extended specs by added extended method to module and adding a call to it in Object#extend + + Signed-off-by: Brian Ford + +commit 302ba965def902ccc5d3e97ed6bd5841f09d8f00 +Author: Brian Ford +Date: Mon Jan 14 14:14:08 2008 -0800 + + Philipp Brüschweiler's patch for String#%, #242. + +commit abaf2efa9e467bb7b5ef3b53b8490f1e056a832e +Author: Caleb Tennis +Date: Mon Jan 14 16:59:23 2008 -0500 + + Another round of socket specs, and add a Rake StructGenerator to find sockaddr_un if it's available + +commit f5d0e435023a80bcc4c101a8d3ab9fc056a14c80 +Author: Caleb Tennis +Date: Mon Jan 14 15:52:47 2008 -0500 + + More socket specs + +commit 7bbc927a9d8a6f9202025be62a3db861ced3216f +Author: Caleb Tennis +Date: Mon Jan 14 14:58:34 2008 -0500 + + More socket functions and specs. + + Namely, this implements a Socket::SockAddr_In class that is a FFI::Struct around the + sockaddr_in C struct. This gives us a better ability to inspect what's going on in the + struct from the Ruby side of things. + +commit f351c6d3d8831705f0398abdae240abba9252a75 +Author: Caleb Tennis +Date: Mon Jan 14 11:09:04 2008 -0500 + + More socket specs update + +commit cb8ce936394cafa00f77008083bccf9cded59f28 +Author: Wilson Bilkovich +Date: Mon Jan 14 14:39:41 2008 -0500 + + Split process/constant expectations into Linux and BSD sections + +commit 0964d53edd80367611f63cd6eb4b294ec898cc8d +Author: Eero Saynatkari +Date: Mon Jan 14 10:04:53 2008 -0500 + + Revert IO#dup spec to unmask errors, removed FileUtils dependency. + + * Any errors occurring in specs should generally be raised normally + so that any potential problem or spec deficiency is exposed. + +commit 04f542e928c5fa0df460d8f11b4d87e008fa343f +Author: Wilson Bilkovich +Date: Mon Jan 14 13:36:49 2008 -0500 + + Update Process::Constants to fetch values from RUBY_CONFIG + Update process/constants_spec so that it passes on MRI as well + +commit 0ad02b57fd040196d11662bd1ab9b259dc2ce6d2 +Author: Jeremy Roach +Date: Mon Jan 14 12:23:20 2008 -0600 + + squash Marshal.dump bugs + + * fix order of evaluation problem + * put more objects in links and symlinks hashes + +commit ed98b9a14459b011f97fee5c781410c4d413ed9a +Author: Vladimir Sizikov +Date: Mon Jan 14 17:36:00 2008 +0100 + + Updated Arry#pack specs to guard for always big-endian JRuby. + +commit 550f07dc7551573a975183209ba8904fdbd62607 +Author: Vladimir Sizikov +Date: Mon Jan 14 15:57:04 2008 +0100 + + More robust cleanup in IO#dup specs. + + Without it, mspec against JRuby was reporting EIGHT + failures, while only 5 tests are actually exist. + +commit 1ea4f82183190c4c87da48c381f1db417c7403ac +Author: Caleb Tennis +Date: Mon Jan 14 09:26:36 2008 -0500 + + Updated some socket specs + +commit e20ab7ea377cd39209011b44204d2688b53611c5 +Author: Eero Saynatkari +Date: Mon Jan 14 02:23:26 2008 -0500 + + Fixed Regexp.new kcode setting, improved Regexp specs. + + * Regexp kcode can be upper- or lowercase. + * More robust Regexp#options and #inspect specs. + * Updated Regexp excludes. + +commit cc71f359aa65101d2c00cfbb0c396b7cdc697ef2 +Author: Evan Phoenix +Date: Mon Jan 14 01:25:21 2008 -0800 + + Improve bytecode performance a tiny bit, fix Kernel#` + +commit c561368c03c605de41746fac2ce5a6386fcf4f54 +Author: Charles Nutter +Date: Mon Jan 14 03:17:15 2008 -0600 + + Quarantine a suspicious TCPSocket.new speck failing on MRI on OS X. + +commit 71a9cf2afbbe6903b8d652b3ee201957e0b0c633 +Author: Eero Saynatkari +Date: Sun Jan 13 22:58:16 2008 -0500 + + Finished IO#print specs. + +commit ff75b95a690051736f49a9a113d21027f7f03e92 +Author: Eero Saynatkari +Date: Sun Jan 13 22:44:21 2008 -0500 + + IO#dup and specs. + +commit 02f1c03f4df3327ce1ddd20e2249a5e9830627a0 +Author: Brian Ford +Date: Sun Jan 13 23:11:48 2008 -0800 + + Justin Bradford's patch for Float failure, #237. + +commit e43c148954ad609c438e5a4f14811c0349239374 +Author: Matthew Draper +Date: Fri Jan 11 21:35:57 2008 +1030 + + Kernel#Integer is very fussy about the strings it accepts. + + Signed-off-by: Brian Ford + +commit adad84f7a26bf40809366f2f7b6acfc61dcfefc2 +Author: Charles Nutter +Date: Mon Jan 14 00:31:12 2008 -0600 + + Class.inherited gets invoked regardless of visibility + +commit c746fad52e9503d04c3cf65de979b0a5a9f9e495 +Author: Matthew Draper +Date: Mon Jan 14 10:25:58 2008 +1030 + + Many of Kernel's methods should be module_functions. + + Signed-off-by: Brian Ford + +commit 0b8a4bfefaed3179f96721fdde35e32ed8ff7263 +Author: Brian Ford +Date: Sun Jan 13 21:43:45 2008 -0800 + + Update CI excludes for Regexp. Remove empty CI exclude files. + +commit 89a87edbc61a877621c6f43266000aff32e92ae7 +Author: Warren Seen +Date: Mon Jan 14 07:12:13 2008 +1100 + + Fixes visibility of methods passed to Module#module_function + + * Make instance methods versions of functions passed to Module#module_function private + * Correctly identify visibility in error message raised in Module#set_visibility + * Added specs for module_function + + Signed-off-by: Brian Ford + +commit 25d6fa558f88732d1aa28c68b0eb7c9910366243 +Author: Eero Saynatkari +Date: Sun Jan 13 20:42:52 2008 -0500 + + Updated Regexp excludes. + +commit 69e200276898f1c9208be527bdc64c318c56f86e +Author: Eero Saynatkari +Date: Sun Jan 13 20:24:42 2008 -0500 + + Shared spec for Object#dup and Object#clone. + +commit 8a6fe609224c126bcf86987edd3f0690fc9e45ff +Author: Eero Saynatkari +Date: Sun Jan 13 18:19:56 2008 -0500 + + VM calls private hook methods now. Object#dup and #clone specs. + + * Object#dup and #clone have rudimentary specs which also partially + confirm the private hook fix through #initialize_copy. + +commit 84773b6ba63ea6f715dcc4e99e0a8a2e2b739152 +Author: Eero Saynatkari +Date: Fri Jan 11 10:46:15 2008 -0500 + + Specs for Regexp.{new,compile}, updated excludes for same. + +commit 6c1603723bba7d58203aa9b03bbf92b4900e53d1 +Author: Brian Ford +Date: Sun Jan 13 18:49:40 2008 -0800 + + Numerous fixes for File::Stat. + + * Implemented readable(_real)?, writable(_real)?, executable(_real)?. + * Implemented a number of helper methods like rowned?, rgrpowned?, + superuser?, rsuperuser?. Made these private. + * Implemented owned?, grpowned?. + +commit d1b05e0bf98a3cdfda8a3d2398e78035a49c0c66 +Author: Adam Gardiner +Date: Mon Jan 14 12:25:41 2008 +1100 + + Deprecate meta_send_stack* opcodes + + The code path for these opcodes is almost identical to + send_stack, and no measurable performance improvement + comes from using them. + +commit 154fe5e1faad94f371c51a979240a6d7f5cd8909 +Author: Caleb Tennis +Date: Sun Jan 13 20:07:12 2008 -0500 + + Implement BasicSocket#setsocketopt for String optvals, and add a spec for it. + +commit c21636d6b2502db344049e7dc62d42ff8c18b040 +Author: Caleb Tennis +Date: Sun Jan 13 19:34:50 2008 -0500 + + Add specs for BasicSocket#getsockopt + +commit 1584f41148b8d8967df4c3ee6376b59919cb7db3 +Author: Caleb Tennis +Date: Sun Jan 13 19:24:58 2008 -0500 + + Add Array.pack for i, s and l arguments. + +commit 7131e187e19bf0889f8ece802495865f7b3f1e5c +Author: Evan Phoenix +Date: Sat Jan 12 13:55:20 2008 -0800 + + Cleanup String#split, add edge case check + +commit 4ff46602c8a54a61697bb8d9eaa9ae89e56f7abe +Merge: 1c95721... 908ccff... +Author: Caleb Tennis +Date: Sun Jan 13 14:49:48 2008 -0500 + + Merge branch 'socketspecs' + +commit 908ccff0a854038372dad0780e1de35727e2d657 +Author: Caleb Tennis +Date: Sun Jan 13 14:49:17 2008 -0500 + + Some TCPSocket spec mods + +commit 1c95721bd873c4b30c187bfa7673cd7e3568a0fb +Author: Brian Ford +Date: Sun Jan 13 10:20:43 2008 -0800 + + Fixed File::Stat specs to output method name correctly with -f s. + +commit acb7505d41aa789157e50962253e686827a702d5 +Author: Jeremy Roach +Date: Sun Jan 13 12:07:06 2008 -0600 + + update CI excludes + +commit c8db419ae06e9642b346e1bcae99367f3b72845f +Author: Jeremy Roach +Date: Sun Jan 13 11:55:50 2008 -0600 + + squash bug in Marshal.dump + + symbols need a separate links hash + +commit eb953ae2c3fdeac4ae13b5461246b9f51b0f39cc +Author: Caleb Tennis +Date: Sun Jan 13 09:40:39 2008 -0500 + + Make the spec text more verbose + +commit db013bc06cef2dea4b77a215d4437e2172b391b6 +Author: Caleb Tennis +Date: Sun Jan 13 09:04:08 2008 -0500 + + Commit an updated spec that shows the failure on rbx and passes MRI, + w.r.t. opening a module and aliasing a private module function from that + module. + +commit 759a9f8bd70ead9b5d2fc67b3872e3bf3bd34001 +Author: Brian Ford +Date: Sun Jan 13 01:45:07 2008 -0800 + + Updated CI excludes for File::Stat. + +commit 107feb74eaf01c09d8c5bd14ac29e53900a5ed26 +Author: Brian Ford +Date: Sun Jan 13 00:38:00 2008 -0800 + + Modified File, File::Stat, FileTest specs. + + * Added templates for File::Stat specs. + * Added shared specs for some File::Stat methods. + * Altered toplevel File shared specs to take the name + of the constant to enable File::Stat to use a fixture + proxy but still have the correct name show with -f s. + * Split out specs for missing files because File.[l]stat + behaves differently than e.g. File.file?. + +commit e1a13f7ecfe7f2d18fd6ac20dd8c63cbd6d11855 +Author: Jeremy Roach +Date: Sun Jan 13 02:26:09 2008 -0600 + + implement more of Marshal.dump + + obj.marshal_dump, IO.write, depth limit, exceptions + +commit bc070232eab1bfa5d294897487339d259a406e74 +Author: Jeremy Roach +Date: Sat Jan 12 15:16:21 2008 -0600 + + implement more of Marshal.dump + + Float, obj._dump + +commit ad7a67ed5a3a1399773dda74c4688e9b00c8f9aa +Author: Kamal Fariz Mahyuddin +Date: Sun Jan 13 01:46:36 2008 +0800 + + Update CI excludes for Process.initgroups + + * It was affected earlier by the Enumerable lambda/Proc-arity issue + +commit 7b7a1e3e4712f35688823543b7a7c3c25405ef77 +Author: Kamal Fariz Mahyuddin +Date: Sun Jan 13 01:40:22 2008 +0800 + + Fix implicit block in Enumerable not passing the arg check in Proc#call + + * Changed instances of lambda to Proc.new and arity once again + returned the correct value. Will investigate, but until then, this + passes. + +commit d9c21aaa18044bd54ed3b1f6ec5daacf9bd250fa +Author: Kamal Fariz Mahyuddin +Date: Sun Jan 13 01:02:00 2008 +0800 + + WIP Fix block argument checking + + * Passes all the proc/lambda call specs + * However, specs for methods that add implicit blocks like + Enumerable#all fail because their arity is somehow 0 - excluded for now + +commit 3d400bc8a91a793f49dcf5655dc28e6141d999d0 +Author: Kamal Fariz Mahyuddin +Date: Sat Jan 12 20:03:26 2008 +0800 + + Update CI excludes and add Module#class_variable_get to critical.txt + +commit 6bf7b8616837649ddd2c1435a54c86ed30910985 +Author: Kamal Fariz Mahyuddin +Date: Sat Jan 12 19:27:12 2008 +0800 + + Move custom classes for NoMethodError specs into fixtures + +commit 10cc61bb816ae67a7fad5b135f66d263d7ee07b1 +Author: Kamal Fariz Mahyuddin +Date: Sat Jan 12 19:20:27 2008 +0800 + + Swap the protected/private method calls around in the NoMethodError spec + +commit 6b2e66d3f9222b52cdae42b57206363ad47949e2 +Author: Vladimir Sizikov +Date: Sat Jan 12 11:51:28 2008 +0100 + + Corrected Module#alias_method spec. + + Now it should pass on both MRI and JRuby. + +commit 67f74a936655b72c689d09c77d9fbe9d7194a0a0 +Author: Kamal Fariz Mahyuddin +Date: Sat Jan 12 17:50:22 2008 +0800 + + Fix proc/lambda/Proc.new arity + +commit 03440114d5e3f07111cdcae3657258cae4c803e7 +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 11 13:24:26 2008 +0800 + + Fix Kernel.Integer parsing of invalid String + +commit 598598c10c66de38b52a8092cdd2fa99604eda6e +Author: Eric Hodel +Date: Sat Jan 12 00:21:22 2008 -0800 + + Clean up expectations to use a common list. + + Update excludes for other things using shared glob specs. + +commit 55aa5a1f10655618e45d0ec84502cc13c982227e +Author: Eric Hodel +Date: Fri Jan 11 23:31:45 2008 -0800 + + Specs for File.fnmatch handling of Regexp specials. + +commit 7c0dc7edfcdf4948047ba051b0cbed7ba761f1dc +Author: Eric Hodel +Date: Fri Jan 11 21:57:10 2008 -0800 + + Dir.glob support for {}. + +commit 9a097fe5634c1109919d1e120b1276827371c332 +Author: Eric Hodel +Date: Fri Jan 11 19:42:31 2008 -0800 + + Exclude {} specs for WIP Dir.glob + +commit ccdc6f5ae5fabbd0d2c32072811e2ecf7cca8987 +Author: Caleb Tennis +Date: Fri Jan 11 21:40:04 2008 -0500 + + In MRI, you can alias private module methods. Not so here. This spec catches it. + +commit 8b402d1e32dc283124375374532024f6cfe7020d +Author: Brian Ford +Date: Fri Jan 11 17:35:21 2008 -0800 + + Added toplevel shared specs. Converted File, FileTest specs. + + * spec/ruby/1.8/shared is the directory for sharing + specs across multiple classes. + * Added methods for FileTest + +commit c6aea2e10d7a4d0ee14175d5b79894e1e11699b1 +Author: Brian Ford +Date: Fri Jan 11 15:59:34 2008 -0800 + + Converted File/FileTest#exist(s)? to toplevel shared specs. + +commit 06a5d8a3d5874303a71e4e9b939b44c204041edf +Author: Caleb Tennis +Date: Fri Jan 11 17:39:39 2008 -0500 + + Fix failing specs in udpsocket/open_spec.rb + +commit 530e40005d09140fdb55608890f0994f3a48d8be +Author: Caleb Tennis +Date: Fri Jan 11 13:11:58 2008 -0500 + + Observer specs + + Signed-off-by: Brian Ford + +commit 0907a20d2bad2207be8e937c403c49634f3a23b6 +Author: Caleb Tennis +Date: Fri Jan 11 12:24:44 2008 -0500 + + Add observer to lib and base-spec file + + Signed-off-by: Brian Ford + +commit ec0ff1dfa1ee9de38d35537bec5071f6bb31cf7f +Author: Jeremy Roach +Date: Fri Jan 11 13:11:02 2008 -0600 + + implement more of Marshal.dump + + Array, Hash, links + +commit 640e81394ad2385b535b08b535a4fca06a5f3eec +Author: Brian Ford +Date: Fri Jan 11 10:24:54 2008 -0800 + + Added CI exclude for failing MD5#== spec. + +commit 3c238cc9f4b32f63bc681bd64a507fc2ff49b017 +Author: Brian Ford +Date: Fri Jan 11 10:00:50 2008 -0800 + + Converted Socket specs to use subdirs for subclasses. + +commit 2b98950eaa33b532fcef079b0997f9793228c608 +Author: Eero Saynatkari +Date: Fri Jan 11 09:23:23 2008 -0500 + + Specs and implementation for IO#print. Closes #222. + + * IO#print without arguments spec excluded due to a lack of a lower + level output matcher. To be added shortly. + +commit d65c8c6899cf8e4a1fa56486cf417451e0c7fce6 +Author: Arthur Schreiber +Date: Fri Jan 11 19:19:56 2008 +0100 + + Fix String#* spec. + +commit 481e075bfeb9f8fb3bd4db645129a463307de09e +Author: Vladimir Sizikov +Date: Fri Jan 11 17:41:07 2008 +0100 + + Improved digest/md5 specs a bit, some new test cases. + +commit 67f48236da3d114638310ab37bcc706719bf7fcd +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 11 11:59:06 2008 +0800 + + Updated Method's specs as Method#inspect and #to_s deviates on Rubinius + +commit 76846154773a87bc8d99c97e91250abda22f6378 +Author: Evan Phoenix +Date: Thu Jan 10 19:24:57 2008 -0800 + + A bunch of fixes found while working on Socket + +commit f69613740662d3ba4f85573c6c860a5987b29765 +Author: Dirkjan Bussink +Date: Fri Jan 11 00:17:14 2008 +0100 + + Fixed Time object for throwing errors where appropriate + +commit 9396386f700646d0c55b9a7a75bc399dfe055d2c +Merge: baae72c... 4d2e53e... +Author: Dirkjan Bussink +Date: Thu Jan 10 22:39:24 2008 +0100 + + Merge branch 'master' of git@git.rubini.us:code + +commit baae72cc47c9c1f41c3478732b7bbfdfe514024a +Author: Dirkjan Bussink +Date: Thu Jan 10 22:39:11 2008 +0100 + + Fixed Time#xmlschema conversion + +commit 4d2e53e7376080e42b84dca486debcf4f153f32f +Author: Jeremy Roach +Date: Thu Jan 10 15:17:00 2008 -0600 + + implement more of Marshal.dump + + negative Fixnum, Bignum, Regexp, Struct + +commit abdbcd70bba99149b7391effa48452971407b4d2 +Author: Brian Ford +Date: Thu Jan 10 13:18:52 2008 -0800 + + Annotate Rubinius spec as non compliant. + +commit 2a2b3a016bfd70eb8cd14b6a043d59f119e0ad7c +Author: Vladimir Sizikov +Date: Thu Jan 10 21:52:18 2008 +0100 + + Re-added divmod specs for Ruby/JRuby, with comments. + +commit 2f079e416e4389b091c8c9b5522d49c6f356c6c9 +Author: Brian Ford +Date: Thu Jan 10 12:29:39 2008 -0800 + + Updated Bignum#divmod specs. + +commit 7a5c79415f2e6555bf2c69e416f6d3189f2e0c3e +Author: Brian Ford +Date: Thu Jan 10 10:46:05 2008 -0800 + + Simplify wording of VM.coerce_to_array specs. + +commit abc1237a0c96ecd77baee6ecbcf71a7bba338139 +Author: Ryan T Mulligan +Date: Thu Jan 10 12:35:23 2008 -0600 + + md5 is now fully 1.8.6 MRI compatible + + * MD5 digest specs + * Specs pass on MRI and RBX + * Platform::POSIX.memcpy hooked + +commit 9f991bd850c51cd624169b51768c2215d4b56edb +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 11 01:35:15 2008 +0800 + + Method#bind raises TypeError when binding a method from a non-descendant + +commit fc029ab13ded7eeb1ba838b99f00e2f14e232d65 +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 11 00:12:44 2008 +0800 + + Implement that Precision.induced_from raises TypeError in certain cases + + * For case when mixer class doesn't define it's own induced_from method + * Update CI exclude for precision + +commit 35d1a7bc3694bdcc327dd5ac89ca0f261e0bd705 +Author: Vladimir Sizikov +Date: Thu Jan 10 16:57:38 2008 +0100 + + Added one more Bignum#divmod testcase, known to fail on some implementations. + + In fact, this test case fails on Rubinius and JRuby. + +commit 955676613f5e38cf029998e2712013e4575dd03e +Author: Dirkjan Bussink +Date: Thu Jan 10 16:12:37 2008 +0100 + + Changed spec failing on MRI + +commit f7b4f3fe02833081cc7f40c0feebbef0e5012f10 +Author: Dirkjan Bussink +Date: Thu Jan 10 15:58:23 2008 +0100 + + Fixed Float#divmod + +commit aff6e1fc1a16eb9e7b7e207ebc2234154d891a92 +Author: Kamal Fariz Mahyuddin +Date: Thu Jan 10 22:53:25 2008 +0800 + + Converted VM specs + +commit 969c0d8e0dbf43caa3999976cf259c623ff05ff1 +Author: Kamal Fariz Mahyuddin +Date: Thu Jan 10 22:50:17 2008 +0800 + + Convert Options specs + +commit cc7c9dcb6697dea991342328a9b00fa01740e809 +Author: Kamal Fariz Mahyuddin +Date: Thu Jan 10 22:28:42 2008 +0800 + + Replace example blocks using 'specify' with 'it' + +commit 7a5fa30a71072346abda17cdb79c2aa3b3922239 +Author: Dirkjan Bussink +Date: Thu Jan 10 15:27:49 2008 +0100 + + Fixed Bignum#quo + +commit f2aafe4a352fd884d217b0361d2e7e617f58ebd5 +Author: Kamal Fariz Mahyuddin +Date: Thu Jan 10 20:38:45 2008 +0800 + + Converted Generator specs and generated new CI exclude files for it + +commit edb7e341d9b3ab1c3bdc08bc57ec55d6bf8ace8b +Author: Kamal Fariz Mahyuddin +Date: Thu Jan 10 19:33:53 2008 +0800 + + Remove generator_spec.rb because of spec conversion + +commit bb4de530c5980f0205875bdb5548e40a22ef6a62 +Author: Brian Ford +Date: Thu Jan 10 01:45:47 2008 -0800 + + Updated CI excludes for library because of spec conversions. + +commit 61a66f69fe3a94d9ad5568ee2dd846cfc0b5211a +Author: Brian Ford +Date: Thu Jan 10 01:45:17 2008 -0800 + + Converted Socket specs. + +commit 167e05039eeeeb959f7aab1f3611268170037296 +Author: Brian Ford +Date: Thu Jan 10 00:59:20 2008 -0800 + + Converted YAML specs and added template files for other methods. + +commit 027f568f79222cdee492f088edf8a2f14250635a +Author: Brian Ford +Date: Thu Jan 10 00:47:35 2008 -0800 + + Converted ostruct specs and added template files for other methods. + +commit e964c9342ade9341518bc46cf998703a2c16aa2b +Author: Brian Ford +Date: Thu Jan 10 00:39:49 2008 -0800 + + Converted ftools specs. + +commit 6263280187c81b0ee27893eae90f9d6a8a511b65 +Author: Brian Ford +Date: Thu Jan 10 00:26:40 2008 -0800 + + Converted Etc specs. + +commit 7b94284063222eef42b9b7ad0d1c820adabe210d +Author: Evan Phoenix +Date: Thu Jan 10 00:39:01 2008 -0800 + + Fix a few more Kernel bugs + +commit 1c58ee51f388da0490a7815c9a1787d21e151aab +Author: Brian Ford +Date: Thu Jan 10 00:08:15 2008 -0800 + + Fixed path for mock dirs in Dir specs. + +commit 320f7e7d3503d53216733f9b6eb75c387155ae5f +Author: Brian Ford +Date: Wed Jan 9 22:44:37 2008 -0800 + + Converted StringIO and Singleton specs. + +commit 5a94a7c3b73103c99a337a089f9cf2c7e601d2bc +Author: Brian Ford +Date: Wed Jan 9 21:50:14 2008 -0800 + + Converted stdlib Singleton specs. + +commit 12864a2057d1b6f5fa392f34d1fa3e8873a8c566 +Author: Adam Gardiner +Date: Mon Dec 24 23:06:12 2007 +1100 + + Initial commit of Rubinius Debugger + + * Created Debugger class for debugging Ruby code in Rubinius + * Added Kernel#debugger convenience method to set a breakpoint + and activate the debugger at the point at which the + debugger statement is encountered. + * Implemented the following debug commands: + - h: get a listing of commands + - b: list breakpoints + - b : set a breakpoint at the start of the method + - n: Step to the next line + - ni: step to the next VM instruction + - c: continue execution until the next breakpoint + - l: list source code around the current breakpoint + - d: decode VM bytecode around the current breakpoint + - v: display local variables and their values + - vs: display the contents of the VM stack + - Anything else is evaluated as a Ruby expression in the + context of the current breakpoint (so you can, e.g. change + the value of locals before resuming, etc) + +commit 01a189cc3e52e8bcc6f22bcc5713e765bba84160 +Author: Eero Saynatkari +Date: Wed Jan 9 10:29:27 2008 -0500 + + Object#is_a?, #kind_of? and #instance_of? specs. + + * Removed obsolete kernel specs for same. + +commit ab9645614bbbd0bca63c215819c12cc85a1507b1 +Author: Brian Ford +Date: Wed Jan 9 21:35:39 2008 -0800 + + Converted specs for stdlib Time. + +commit 733b069f11c7136175036154a45b924cf89cc8ff +Author: Jeremy Roach +Date: Wed Jan 9 20:01:09 2008 -0600 + + update CI excludes + +commit 01e98dee4c24838ca518610443e43473ffdcf43c +Author: Ryan Davis +Date: Wed Jan 9 16:42:07 2008 -0800 + + Fixed block args for |*a| + +commit dc9c1d05dd5e0e828a77acc09220f5894a9aa453 +Author: Brian Ford +Date: Wed Jan 9 16:04:11 2008 -0800 + + Fixed Enumerator spec style. Updated YAML excludes. + +commit 0363685a97df83feb0d07f40a7a5c4d7a78e2a27 +Author: Eric Hodel +Date: Wed Jan 9 14:55:53 2008 -0800 + + Spec for String#sub bug. + +commit 23052eb5f993c959fdb2b327895df08e0a344edb +Author: Eric Hodel +Date: Tue Jan 8 23:47:27 2008 -0800 + + Implement { } matching for Dir.glob + +commit c90b2531d183e4534268d4699634828f29e803cb +Author: Eric Hodel +Date: Tue Jan 8 23:44:35 2008 -0800 + + Spec File::Stat#blksize. + +commit 62d2a1809936a304c0cf0b94fd28f5b83932f58f +Author: Eric Hodel +Date: Tue Jan 8 22:47:37 2008 -0800 + + Implement Dir.glob '{a,b}' + +commit d9430ad1a3e582e830a994a83d6f99e017bfbe4d +Author: Eric Hodel +Date: Tue Jan 8 20:56:37 2008 -0800 + + Fix module X::Y; end; X::Y.name + +commit 1baa9468e0d89777fdb6f23e78e8ab510a19d534 +Author: Jeremy Roach +Date: Wed Jan 9 16:16:35 2008 -0600 + + incomplete Marshal.dump + + an evil ivar_as_index is added to Object to hold + the names of modules that extend the object + +commit 85e98490fe45446e03801840d4628149f8977098 +Author: Evan Phoenix +Date: Wed Jan 9 12:26:19 2008 -0800 + + Move compiler2 => compiler, and Compiler2 => Compiler + +commit 5aa5cc66e2b0196728c80eb394ec3b2dfccd77ae +Author: Eero Saynatkari +Date: Wed Jan 9 10:30:42 2008 -0500 + + Centralised specs for Object#=~. + +commit e1fe9f57c942460338a18e38f66fbf6feb69b4bc +Author: Vladimir Sizikov +Date: Wed Jan 9 13:23:59 2008 +0100 + + Added few edge cases to Numeric#eql? tests. + +commit b8dfd675fad5e82ebfd50c737beb9a9b919a9c8b +Author: Kamal Fariz Mahyuddin +Date: Wed Jan 9 15:52:18 2008 +0800 + + Remove excludes for String#slice with the fixed send in place + +commit d7f69f17ac30f6b3161851e8df6a1e0a7694219d +Author: Evan Phoenix +Date: Tue Jan 8 22:42:43 2008 -0800 + + A couple more fixups + +commit 055d7545c7046102cd92b7054992b1b47f711c4a +Author: Evan Phoenix +Date: Tue Jan 8 22:31:50 2008 -0800 + + A boatload of fixes done while getting flexmock and rake running + +commit fd7c266e52c25d151214512cc801901813630d7a +Author: Kamal Fariz Mahyuddin +Date: Wed Jan 9 11:49:02 2008 +0800 + + Removed last array exclude due to fix in 02e6e28 + +commit 690626f43f7b4ce888de081033eaadfba543acff +Author: Brian Ford +Date: Tue Jan 8 18:58:50 2008 -0800 + + Removed subtend specs from CI run. Increase File#mtime tolerance. + +commit dec4f25a47a9a962b77a97dea47985fe17421e5f +Author: Adam Gardiner +Date: Wed Jan 9 13:39:51 2008 +1100 + + Specs for stack usage + +commit d699f6605db86e6f6bc61d0f3a79fc1535816c70 +Author: Kamal Fariz Mahyuddin +Date: Wed Jan 9 10:38:06 2008 +0800 + + Add spec to illustrate Numeric#divmod bug in MRI and rubinius (excluded) + +commit 8a55f3047dc0fd502bb632dc9f5bdb9668b180fe +Author: Brian Ford +Date: Tue Jan 8 18:32:18 2008 -0800 + + Subtend CI exclude to (hopefully) fix the build server runs. + +commit 33bde75b57a88baa850edccea382e1130ed586da +Author: Brian Ford +Date: Tue Jan 8 17:19:30 2008 -0800 + + Added spec/compiler2 to CI. Added CI excludes for compiler2. + +commit 6964fc5644fddeef2238591674786f035d9db842 +Author: Ryan Davis +Date: Tue Jan 8 17:19:20 2008 -0800 + + Fixed up against evan's changes. ping + +commit 9423d1e8e9ed91fb9f0934b939899c753972cee1 +Author: Wilson Bilkovich +Date: Tue Jan 8 20:11:54 2008 -0500 + + Fix warnings encountered when running compiler2 specs under MRI + +commit d71ad87c14a4378ad2f01c49d90304c29be548f3 +Author: Evan Phoenix +Date: Tue Jan 8 17:02:02 2008 -0800 + + Fix a block_arg bytecode generation case + +commit 57199b5b468c0009512a479e13bbcf086d0d9526 +Author: Ryan Davis +Date: Tue Jan 8 16:16:07 2008 -0800 + + Added new combo bytecode/runtime tests for block args + +commit 8a88699af73d272a61332e11d022bd629aa0460d +Author: Ryan Davis +Date: Tue Jan 8 16:15:24 2008 -0800 + + Improved inspect output for compiler spec objects. Added convencience methods for testing iter bytecode generation. + +commit 0dd9cd298cf735dc13cc2a2410ad6b5195790c11 +Author: Brian Ford +Date: Tue Jan 8 13:19:21 2008 -0800 + + Added subtend specs to CI. Updated subtend excludes. + +commit c07a5273844b32fe39090bb16d0e4ad59ecb0564 +Author: Dirkjan Bussink +Date: Tue Jan 8 21:28:48 2008 +0100 + + Fixed given_spec? because of changed block_given? behavior + +commit 0f9a8dfee9dd1c7af1f8ba69497c8dd85539760a +Author: Nitay +Date: Tue Jan 8 11:49:41 2008 -0800 + + Fix setpgid spec using pipes to avoid race condition + +commit 09feb8677c529d04969e63d1ff4e3746037611cf +Author: Brian Ford +Date: Tue Jan 8 10:00:06 2008 -0800 + + CI excludes for ruby/1.8/library. + +commit cda3d86fa44f1d62fe503e54f42c5c5df361b8f9 +Author: Benjamin Andresen +Date: Tue Jan 8 08:22:49 2008 +0100 + + Added explicit umask to File permission spec so it won't fail on + non-standard umasks. + +commit 6df303e29d7fd04f4a1a0af379f4947854dd4635 +Author: Eero Saynatkari +Date: Mon Jan 7 23:20:22 2008 -0500 + + Method#== and specs from Scott Taylor, slightly tweaked. Closes #137. + +commit 9b86b12be687bb29e25d0292786351d89a698adc +Author: Brian Ford +Date: Mon Jan 7 19:45:24 2008 -0800 + + Added CI exclude for Array#pack. + +commit 17a746b0aa2c89aa9e61b8965d125e962748c20d +Author: Jeremy Roach +Date: Mon Jan 7 21:07:25 2008 -0600 + + adds Marshal.dump and Float#to_s specs + +commit d5c19db2778e0cc3cbee5bf994b511448cb6bd78 +Author: Eric Hodel +Date: Mon Jan 7 15:31:11 2008 -0800 + + Fix IO#pos= + +commit 21f44f03f0aa44b2f172f89ad27797c943dc618b +Author: Vladimir Sizikov +Date: Mon Jan 7 22:03:28 2008 +0100 + + Remove non-needed std output from Array#pack test. + +commit 9ec20509ad6533876bbbc984052e6b7e05d2ea55 +Author: Vladimir Sizikov +Date: Mon Jan 7 21:50:46 2008 +0100 + + Added Array#pack tests with empty array. + +commit 35170103bdba14d824780a41112f12034cb5c79e +Author: Vladimir Sizikov +Date: Mon Jan 7 21:13:47 2008 +0100 + + Added Array#pack tests with 'w' pattern. + +commit 71b00e03ce2c6424fd262d737feb991835605da2 +Author: Vladimir Sizikov +Date: Mon Jan 7 20:46:58 2008 +0100 + + Added Array#pack('U') test with negative values. + +commit 7be0813127635ea54909179c9553c5052c4a3d90 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 7 18:00:03 2008 +0800 + + Add specs for Array.[] + +commit 0b762336e8c6040cbbe794cece64c56bfa46c296 +Author: Evan Phoenix +Date: Sun Jan 6 23:35:35 2008 -0800 + + Fix breakages, comment out at_exit spec (need a better way to test) + +commit 8896e459f1bffb7ae2da2f2aa708419e6316cb4b +Author: Matijs van Zuijlen +Date: Mon Jan 7 14:03:03 2008 +0900 + + Spec to demo failure of cases like "yield 1, *[1, 2]" + +commit 79da85bb1b1d63e617251b3a3ea6b0657c1e8ddb +Author: Adam Gardiner +Date: Mon Jan 7 13:04:22 2008 +1100 + + Modified CompiledMethod#decode to use #local_names + + * CompiledMethod#decode now leverages new #local_names + method to return the names of locals accessed via + push_local/set_local etc. + * Removed excludes, as all decode specs now pass + +commit 89c1026cecbb9fcd09a62139e2d28b24b5658c25 +Author: David Whittington +Date: Mon Jan 7 01:37:10 2008 +0000 + + Added args to NoMethodError raised by Object.method_missing + +commit 0e4a02f0e2fede5d785b15a6b34c582c6ba586f1 +Author: Ryan Davis +Date: Sun Jan 6 12:43:14 2008 -0800 + + Removed redundant Bignum#to_s. Moved private radix_to_s to bottom. Cleaned up to_s spec a bit + +commit 48446c40a759d60b7465d82b40f2911d0f7e444b +Author: Charles Nutter +Date: Sun Jan 6 13:45:22 2008 -0600 + + Add some additional Math.asinh specs from JRuby. + +commit 7c81ca307cd01d3752a08487bc3742c8452d61c4 +Author: Kamal Fariz Mahyuddin +Date: Mon Jan 7 00:41:10 2008 +0800 + + Add failing specs for Time.{local|mktime|utc|gm} + +commit 35816e118b327a150a2d26638f289633f5e51f16 +Author: Kamal Fariz Mahyuddin +Date: Sun Jan 6 17:56:23 2008 +0800 + + Add spec for Time.local to handle string arguments (excluded for now) + +commit f9f36f5bb99ddb62e15cb9a9ddd98414e3df93e2 +Author: Eric Hodel +Date: Sat Jan 5 23:44:33 2008 -0800 + + Allow Regexp to match nil. + +commit e650c39627b81498fc97c51725f2ac1277870e15 +Author: Eric Hodel +Date: Sat Jan 5 23:38:52 2008 -0800 + + Add some IO#read specs + +commit 20257ecce0d3161fae7ac78454f2b8672f2c1de3 +Merge: bc576b8... e549cc5... +Author: Kamal Fariz Mahyuddin +Date: Sun Jan 6 14:45:13 2008 +0800 + + Merge branch 'master' of git@git.rubini.us:code + +commit bc576b8e26fdb43d050df4fe3ad5ed974ec85057 +Author: Kamal Fariz Mahyuddin +Date: Sun Jan 6 14:44:44 2008 +0800 + + Fix handling of string-like second parameter to Time.local + +commit e549cc53a4905f21082a97cd6bcb279ace6d9eae +Author: Eric Hodel +Date: Sat Jan 5 22:31:42 2008 -0800 + + Don't shift more bytes than available in the Buffer. + +commit 71285a2a9a8d0d3e71c678872ff2a146d5b2dc16 +Author: Chris Shoemaker +Date: Sat Jan 5 22:53:51 2008 -0500 + + Fixup the Process specs for setpgrp, getpgrp, setpgid, and getpgid. + + They no longer may unwarranted assumptions about the relationship + between a progress group ids and process ids. + +commit 7b57b3ac6df612f81d60d3a31b030ba054b357a6 +Author: Brian Ford +Date: Sat Jan 5 13:22:51 2008 -0800 + + Patch from Brandon Mitchell for #195, Float#to_s bug. + +commit 70ddfd43fd727122f56e8bdfcf3febd1ac1b5479 +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 4 01:01:11 2008 +0800 + + Fix for Time#yday spec when Time.at might return yday+1 depending on tz + + * Wrapped Time.at in a with_timezone("UTC") for consistent results + + Signed-off-by: Brian Ford + +commit 7d4396e4e69fb7b046efdaaf87d1090a02c883a0 +Author: Kamal Fariz Mahyuddin +Date: Fri Jan 4 02:26:38 2008 +0800 + + Fixed Array#fill behavior when passed index and negative count + + * Added additional spec when negative count is acceptable + * raise ArgumentError when negative count absolute value exceeds index + + Signed-off-by: Brian Ford + +commit 8af2b55313eb55082df6a71cd3e6bd865f2901fc +Author: Charles Nutter +Date: Sat Jan 5 13:33:26 2008 -0600 + + Save mtime during file creation to make mtime spec more reliable. + +commit 7043933af0475370462984c8d2df2b9301e58cfa +Author: Brian Ford +Date: Sat Jan 5 00:45:01 2008 -0800 + + Updated CI excludes after spec description changes. + +commit 3d7650100ba1756a4d67be8044e31498ea96d88e +Author: Brian Ford +Date: Fri Jan 4 21:13:28 2008 -0800 + + Multitudinous style cleanups in spec description strings. + +commit d54ed8791a74661adb87c938e92e037ece924c90 +Author: Evan Phoenix +Date: Sat Jan 5 00:29:22 2008 -0800 + + A real, working eval and friends. + + * Implements binding, eval, etc. + * Passes all eval and instance_eval specs currently + +commit 02ad19ab4132bf5d3ae35c2e11fa1a963d1f1805 +Author: Evan Phoenix +Date: Fri Jan 4 00:25:39 2008 -0800 + + Fix a few more String specs, fix Integer() + +commit d67cfbcf4e7d35641de555ac1edd61b51780def8 +Author: Eric Hodel +Date: Fri Jan 4 19:16:43 2008 -0800 + + Make class variables work with inheritance. + + Move class_variable* to Module. + +commit d79836e04d72796b723cdaab228871c87abe064a +Author: Eric Hodel +Date: Fri Jan 4 16:26:21 2008 -0800 + + Replace Struct with a Struct that can be subclassed + +commit 8efb042a9c160af9e9c177ca14aed220dedcc26f +Author: Ryan Davis +Date: Fri Jan 4 15:41:43 2008 -0800 + + Finished |*args| spec. Fixed MethodDescription and TestGenerator inspect methods + +commit 76cc487434f6cd9d60356560f1bbc3fba000397c +Author: Ryan Davis +Date: Thu Jan 3 16:54:56 2008 -0800 + + Fuck you git. Adds a broken spec to compiler2/masgn for splatted goalpost arg + +commit dd2697b602a732e3e00c131f54f9cc557ae0cbe3 +Author: Eric Hodel +Date: Fri Jan 4 15:40:50 2008 -0800 + + Failing spec for case when with an empty body + +commit 22dcedebd484f655bba51399e38e83c5a14d4053 +Author: Vladimir Sizikov +Date: Fri Jan 4 18:31:48 2008 +0100 + + Added Time#local specs with string-like second arg. + +commit 43ff733a3097fff44ba8a12334f20a1bf77a965f +Author: Adam Gardiner +Date: Thu Jan 3 23:54:34 2008 +1100 + + CompiledMethod#decode now uses local variable names (if avail) + + * CompiledMethod#decode now looks in the bonus tuple for the names + of stack and slot local variables + * Moved compiledmethod specs to spec/core/compiledmethod + + Note: Compiler2 appears not to be setting the bonus tuple, so code + compiled under it cannot decode local names at present. + +commit 06006ec2a053ae49b243fa0aa98fc71c2ea7a524 +Author: Brian Ford +Date: Fri Jan 4 01:24:49 2008 -0800 + + Updated CI excludes. + +commit dff2e75df3c371522b6a3ba4495d269bf793fe97 +Author: Brian Ford +Date: Fri Jan 4 01:01:24 2008 -0800 + + Updated CI excludes for Bignum. + +commit 569fa3b9fc81410ce9fe6568427f0a0bc65b7036 +Author: Brian Ford +Date: Thu Jan 3 19:26:22 2008 -0800 + + Updated CI excludes for String, Regexp. + +commit 7aedec383850eacad5db8248bfcea7615a3d1793 +Author: Evan Phoenix +Date: Thu Jan 3 17:48:32 2008 -0800 + + Fix up setrlimit/getrlimit on darwin + +commit d9aea8bba7276b53ca7c18b8625531be389d2cdc +Author: Evan Phoenix +Date: Thu Jan 3 16:40:09 2008 -0800 + + Refactor $~ out as a global, into Regexp.last_match directly + + * Uses MethodContext to store $~ now, so it's method local. + +commit c19dde305fd751c14a1b4dc798557e0b63c08c8d +Author: Evan Phoenix +Date: Thu Jan 3 16:25:54 2008 -0800 + + Clean up compiler2 specs + + * For is still broken, needs more love + +commit d02603a7e225d3b48ecf7899ea74768880aba7ec +Author: Gregor Schmidt +Date: Wed Dec 12 16:56:11 2007 +0100 + + Add default implementation of Module#method_added + +commit 7ba5d1478106e4e0f5fcf21c66029df2f38d7e2f +Author: Chris Shoemaker +Date: Sun Dec 30 17:55:21 2007 -0500 + + Unquarantine Process.kill specs. + +commit d68b380bdd2e0a0ec3bd968ffabd02f6e30a3aa1 +Author: Chris Shoemaker +Date: Tue Jan 1 22:14:59 2008 -0500 + + Improve kill, wait, detach, and setpriority specs for Process. + + Restore any previously installed signal handler after the spec has run. + + User IO.read(1) instead of IO.getc since rubinius has it implemented. + + Fix a failing Process.detach spec uncovered by the raise_error fix. + +commit da7329d094b6ff437d37e6a1fcaf93883ac9172f +Author: Chris Shoemaker +Date: Mon Dec 31 16:23:24 2007 -0500 + + Add specs for Process.setrlimit, Process.getrlimit, and Process.setsid. + +commit 42bef2feb46434b0ea67bc3f93d941d587c2d9c9 +Author: Brian Ford +Date: Thu Jan 3 12:46:57 2008 -0800 + + Updated Process spec excludes. + +commit ca98172b8a923cce1691b0fcc5d2418417d82662 +Author: Brian Ford +Date: Thu Jan 3 10:07:55 2008 -0800 + + Update CI excludes for IO from Evan's fixes. + +commit cde20d6c32156e4fc06859f1e84414f81f5af69e +Author: Eero Saynatkari +Date: Wed Jan 2 23:40:59 2008 -0500 + + Fixed #require specs. + +commit 06d99a2ac4be06b50848056b381c91531293a49e +Author: Evan Phoenix +Date: Thu Jan 3 01:12:29 2008 -0800 + + Add read buffering to IO, passes 100% of IO specs + +commit 86170283715371b5a87c0518f89c2b882a49bc93 +Author: Charles Nutter +Date: Thu Jan 3 01:21:52 2008 -0600 + + Fix off-by-one on a few signal values. Doh! + +commit c7a64b10410308cec83077a66cda5859b326f296 +Author: Charles Nutter +Date: Thu Jan 3 01:13:27 2008 -0600 + + Modify Signal.list spec to not depend on hash ordering. + +commit f2980d9584c08d873cf1646c281d083946bcbc6c +Author: Nitay +Date: Wed Jan 2 14:36:56 2008 -0800 + + Module#autoload: + * raises a NameError when an invalid constant name is given + * raises an ArgumentError when an empty filename is given + * does not autoload when the specified constant was already set + * registers the given filename to be loaded the first time that the Module + with the given name is accessed + +commit e68bd05defe5ab749110af507c86769c9a036b25 +Author: Charles Nutter +Date: Wed Jan 2 19:04:48 2008 -0600 + + Removing 'Range#initialize can't be called twice' spec. + + Evan agreed that these specs aren't useful, and I don't believe they're + within the bounds of reasonable language specification since they're + going around visibility and testing behaviors no sane programmer would + ever be able to see. + +commit 1870720bac174feb627654f08c1749e1666c2acc +Author: Charles Nutter +Date: Wed Jan 2 18:54:33 2008 -0600 + + Fix inspect spec to guarantee the target thread is actually sleeping. + + A reminder for folks adding Thread specs: You *CAN NOT* know that a target + thread is sleeping unless you are polling for status == 'sleep'. No amount + of channel, lock, or state variable tricks will get around that. Please + use polling if you want to guarantee a target thread is asleep. + +commit df3057a541862bbd1c5c72b8626bb591bb5ae6fd +Author: Eric Hodel +Date: Wed Jan 2 16:39:37 2008 -0800 + + Refactor Module#undef_method spec to #respond_to? and #instance_methods. + + Now only method dispatch is tested for #undef_method. #respond_to? and + +commit d2ecd4119a152370210ccb6c2a816c9dccb9fe90 +Author: Eric Hodel +Date: Wed Jan 2 16:00:09 2008 -0800 + + Fix Rails indenting and whitespace + +commit f5b8afee4931bd09b0ce9fb88fc959c2ea0a1743 +Author: Eric Hodel +Date: Wed Jan 2 15:30:41 2008 -0800 + + Fix Module#undef_method and Module#instance_methods + +commit 82bf31562361a21f85a90d5628a40ff50280c555 +Author: Eric Hodel +Date: Wed Jan 2 14:10:35 2008 -0800 + + Rebuild excludes for #eof? + +commit b2aa0d56b04d7da5d333ba1449acda7c0b64c0c4 +Author: Eric Hodel +Date: Wed Jan 2 14:09:21 2008 -0800 + + Add IO#eof? spec. + +commit addeb47d834d1ce60f8146f747defacf1682e6c4 +Author: Vladimir Sizikov +Date: Wed Jan 2 22:31:30 2008 +0100 + + Removed JRuby spec excludes. + + The JRuby excludes will reside in JRuby repository. + +commit 3239661ed5c38b37c966588341a043d6cdd9445b +Author: Vladimir Sizikov +Date: Wed Jan 2 21:28:11 2008 +0100 + + Corrected String#modulo tests after clarifications from ruby-core. + +commit a0f3ba6632f8486e8f07a21a8e4720d8727ba4d2 +Author: Brian Ford +Date: Wed Jan 2 11:28:42 2008 -0800 + + Fix require_spec_recursive on Ubuntu. + +commit 857c39564df2d8da480f549fff46ec3ab880066e +Author: Brian Ford +Date: Wed Jan 2 00:48:53 2008 -0800 + + A couple fixes. Updated CI excludes for last couple failures. + +commit 58c48ed05b493c71ee445062f27d47909e18b395 +Author: Brian Ford +Date: Tue Jan 1 23:49:14 2008 -0800 + + Updated CI excludes. + +commit 426f5a15eaac05ed1e900433837de0b9d0246c8d +Author: Brian Ford +Date: Tue Jan 1 22:43:47 2008 -0800 + + Moved CI excludes files from .spec dirs to spec/data/*. + +commit a1d6211f3185f23cbc2c929f0352feca05fd079c +Author: Brian Ford +Date: Tue Jan 1 22:03:43 2008 -0800 + + Moved ruby specs to spec/ruby/1.8/... + +commit af55eefd29c8acaf462efe03d2e0b3d95195cb21 +Author: Brian Ford +Date: Tue Jan 1 21:44:35 2008 -0800 + + Updates for bin/ci and bin/mspec. + + * Removed -2 switch from both because compiler 2 is default. + * Added CI_EXCLUDES_DIR and -E switch to bin/ci to allow for + specifying the exclude directories. The default is '.spec' + in each directory containing spec files. Use a path starting + with a '/' to create the exclude directories relative to + that path, otherwise the exclude directories are created + relative to the directories containing the spec files. + * Moved spec/excludes.txt to spec/data/critical.txt + +commit 0e6645eb74f1f63b84f674dbcdfa991153a3ccd0 +Author: Adam Gardiner +Date: Wed Jan 2 12:10:39 2008 +1100 + + Couple of Breakpoint changes + + * Raise ArgumentError if no block given (spec for this + existed, but was masked by RaiseErrorMatcher bug) + * Added line property to Breakpoint + +commit 36a7acddfe74ab25895d13dd775741b042ba3b0c +Author: Adam Gardiner +Date: Tue Jan 1 23:10:13 2008 +1100 + + Reorganise breakpoint specs to new dir layout + +commit 8aa6712dd9e5e870194f77ff74dc8cf11c273805 +Author: Adam Gardiner +Date: Fri Dec 21 16:15:19 2007 +1100 + + Refactored BreakpointTracker in preparation for debugger + + * Moved code from debugger.rb to breakpoint.rb + * Refactored code extensively to support debugger + * Added breakpoint specs + +commit d16e905a67d64f67d7a24ce113f39b4b059c4139 +Author: Brian Ford +Date: Mon Dec 31 17:51:59 2007 -0800 + + Removed the rubinius dir from specs. It was redundant. + +commit 85ed07b6d739f013892a6cbcae5d0bb2c19f6e80 +Author: Brian Ford +Date: Sat Dec 29 15:45:31 2007 -0800 + + Split Ruby specs proper from Rubinius specs. + +commit b8e1466dc1b814bfb2022c1e4319d5ba63f5d762 +Author: Brian Ford +Date: Sat Dec 29 15:02:55 2007 -0800 + + Updated guards in specs. + + * Changed guard names to new, more descriptive names. + * Removed all #extended_on guards for Rubinius-only specs. + +commit 5773ebe9e6f78abec9bfb03f144b5c7a86a27c7e +Author: Brian Ford +Date: Sat Dec 29 00:50:56 2007 -0800 + + Changed Float constants specs to compare against precise values. + +commit 71874fcdc9eaf45a5adecf57d7609831a2a8e6c2 +Author: Charles Nutter +Date: Sat Dec 29 17:26:06 2007 -0600 + + Fix dump_spec to expect 1 or more write calls, rather than exactly 1. + +commit d4bfb39910aa4adf2c0c4e2dee214487bac34093 +Author: Charles Nutter +Date: Sat Dec 29 14:51:38 2007 -0600 + + Add a spec for procs being block-passed and some peculiarities therein. + +commit 1b0333479bf6da2c76c8d3c1e1640dc156086d9f +Author: Vladimir Sizikov +Date: Fri Dec 28 08:24:30 2007 +0100 + + Improved ObjectSpace#each_object spec test. + + Previously, the test was failing from time to time, + depending on Garbage Collector behavior. + +commit 3d7e628acc6699f9652383317bd416d8c75329d5 +Author: Vladimir Sizikov +Date: Fri Dec 28 05:27:24 2007 +0100 + + Updated Time specs to use new :os guard. + + The Time specs use this :os guard to properly detect + which external program with proper parameters to invoke. + +commit 7662638e9afa631f0581fc1c2b2b422b1b926f98 +Author: Jeremy Roach +Date: Sat Dec 29 03:06:51 2007 -0600 + + adds Marshal.dump specs + + for nil, true, false, String, Symbol, Fixnum, Bignum + +commit 93431a28d687372b95f1a1420a3bd1f24e660117 +Author: Charles Nutter +Date: Sat Dec 29 00:15:44 2007 -0600 + + Guard Continuation specs to not run on JRuby (JRuby does not, will not support continuations) + +commit ccf745b9eafe068de6f888de24387bc0a0e68859 +Author: Charles Nutter +Date: Sat Dec 29 00:08:04 2007 -0600 + + Fix for Fixnum size spec to guard "java" platform with 8 byte size + +commit 6f448f0dd72b5df2cc69e28db3d5593f897a9dbd +Author: MenTaLguY +Date: Sat Dec 29 00:58:58 2007 -0500 + + a more modest spec for Thread.pass + +commit b32c2d95d044a4979ab92b5881e32fc8b169d931 +Author: Charles Nutter +Date: Fri Dec 28 23:51:10 2007 -0600 + + Adjust Float MAX spec tolerance to work on both JRuby and MRI, since there's a few powers of precision difference. + +commit af7bb00beeb359fd6183def039b9a1fcd0ce7c48 +Author: Charles Nutter +Date: Fri Dec 28 23:39:15 2007 -0600 + + Expand Float divmod array equality comparisons to use be_close with a default tolerance. + +commit dbdf373751bce2c8a334315c8c5ed21458614c70 +Author: Charles Nutter +Date: Fri Dec 28 23:34:09 2007 -0600 + + Add a tolerance to the Float induced spec around the same scale as the value under test. + +commit a713d277e6a8148d4c53b66a3a8fa3aedbd6a108 +Author: Charles Nutter +Date: Fri Dec 28 23:31:25 2007 -0600 + + Modify Float multiply spec to be_close with a TOLERANCE multiplied by a similar scale as the value under test. + +commit b82d8af43356de31d16b1c36296d9e819ce70d46 +Author: Charles Nutter +Date: Fri Dec 28 23:13:38 2007 -0600 + + Fix Module class_variables spec to sort the variables before checking if they are all there. + +commit 1e60a25b57273dd6fd7e21b0a443da1f5c0be9e5 +Author: Charles Nutter +Date: Fri Dec 28 19:44:51 2007 -0600 + + Mark Process#fork specs as not_compliant_on jruby. + +commit 021a6ff317ed826a46ca2168f4ee9c7540a27214 +Author: Charles Nutter +Date: Fri Dec 28 19:19:14 2007 -0600 + + Remove fail and "failure" guard around the require/extension spec, since an unimplemented spec isn't necessarily a failure of any kind. + +commit 520c423860ef6553dae34eefd85188ab9b4773f6 +Author: Charles Nutter +Date: Fri Dec 28 19:12:52 2007 -0600 + + Modify previous compliance change to callcc spec to use not_compliant_on instead. + +commit ab85bfff2f9fea8e28f9518311aacccd30f380dd +Author: Charles Nutter +Date: Fri Dec 28 17:56:42 2007 -0600 + + Remove compliance guards on identical spec's link/unlink, since they don't blow up now and JRuby supports them. + +commit e79c8af0ad6fb7ddf094b6ba4747932145f9b89b +Author: Charles Nutter +Date: Fri Dec 28 15:19:24 2007 -0600 + + Removing "fail" and "failure" wrapper from unimplemented "loads extension files" spec; an empty or unimplemented spec is not a failing spec. + +commit 0f6b7387bcc8df946ec8d7504cc3935b6d0f9c58 +Author: Charles Nutter +Date: Fri Dec 28 15:06:07 2007 -0600 + + Add compliance to callcc specs, so JRuby doesn't run them (since it never will) + +commit ed43292ce58468e31b771eb4926a39dff8d70793 +Author: Charles Nutter +Date: Fri Dec 28 14:48:45 2007 -0600 + + Make umask spec work with different host process starting umasks, clean up literals to be easier to read through. + +commit 7e9f96741739e544c547f2898e8b5183dec87323 +Author: Charles Nutter +Date: Fri Dec 28 14:43:02 2007 -0600 + + Fix goofed-up paths in requires for rubinius-specific Integer spec + +commit d54fb1e7c3f586a6d8ac200d6de839ebe6cb4c46 +Author: Charles Nutter +Date: Fri Dec 28 14:40:50 2007 -0600 + + Move rubinius-specific spec from core/kernel/Integer_spec to rubinius/core/kernel/Integer_spec. + +commit eb561025707736ebe196eab3b4ff2bd1c98f45a4 +Author: Brian Ford +Date: Thu Dec 27 23:51:42 2007 -0800 + + Fixed language/block specs to guard ruby18 feature. + +commit 5659d057d756effe3acba1037d0ad6d638d930dd +Author: Brian Ford +Date: Thu Dec 27 22:58:17 2007 -0800 + + Changed Bignum specs to use value suitable for all implementations. + +commit 2646b1a17f898f05233622c9251c3c36632e82a7 +Author: Jeremy Roach +Date: Thu Dec 27 23:43:42 2007 -0600 + + implements m directive for String#unpack + + moves a couple misplaced methods from Numeric to Integer + +commit 496d6761d7377081ff76b263a51bb39d0e30d80a +Author: Vladimir Sizikov +Date: Wed Dec 26 18:07:45 2007 +0100 + + Marked one Rubinius-specific Kernel spec test as such. + + Kernel#compile is not official part of Ruby. + +commit 50e35293bd3a117874203a75d214c3435170e5d3 +Author: Vladimir Sizikov +Date: Wed Dec 26 17:43:03 2007 +0100 + + Corrected String#pack with 'DEFG' pattern test. + + Now, numeric comparison of values is used, with precision, + not literal string comparison. + +commit 0ef00fe14a04ef240fcca17d15271f92f2a44525 +Author: Vladimir Sizikov +Date: Wed Dec 26 16:00:20 2007 +0100 + + Added String#inspect test case with malformed UTF-8 string. + +commit 5bbde0cda03ea782090586a9afdb620663633456 +Author: Charles Comstock +Date: Thu Dec 27 13:30:34 2007 -0600 + + switched ThreadGroup specs to use Channels + +commit e3abd8b834b9f923d94ae381e81977feb4a4f6f8 +Author: Charles Comstock +Date: Thu Dec 27 13:22:20 2007 -0600 + + added Thread#stop? and fixed Thread#status + specs for Thread#status + +commit f8835353bc8be47760f70811616991463e4e681e +Author: Vladimir Sizikov +Date: Wed Dec 26 09:18:24 2007 +0100 + + "Unexcluded" one Struct spec test for JRuby. + +commit 8a1b127cb33e43b916b0ccd820c6e16680cd4030 +Author: Vladimir Sizikov +Date: Wed Dec 26 09:12:23 2007 +0100 + + "Unexcluded" 6 Hash tests for JRuby. + +commit ae4ce805fb7611ea6de12b01b2500f501b54bd6a +Author: Vladimir Sizikov +Date: Wed Dec 26 09:01:35 2007 +0100 + + "Unexcluded" 27 Array tests for JRuby. + +commit c84540f96d7e265732a204ed72b3873545624444 +Author: Brian Ford +Date: Thu Dec 27 00:08:06 2007 -0800 + + Fixed Set#delete? spec to actually call delete?. + +commit e137c3279f511b49442ce2cea1b1832c1a0c6ab0 +Author: Brian Ford +Date: Thu Dec 27 00:00:56 2007 -0800 + + Added some specs for Set. + +commit 8054ed86a93a72ad4629d6f52455892d620138b0 +Author: Nitay +Date: Tue Dec 25 17:30:56 2007 -0800 + + require should prevent recursive includes infinite loop + +commit 23fb497a7ba2a853cbdc5e8a38b091df284a377e +Author: Brian Ford +Date: Wed Dec 26 16:50:22 2007 -0800 + + Updated status output options for bin/ci and bin/mspec. + + * Made dotted the default output format for bin/ci. + * Added -m MARKER option to ci and mspec. + * Added "Started" output as requested by autotest folks. + +commit 036b073753763afe86330d3f7fa0f61d755ac991 +Author: Eero Saynatkari +Date: Wed Dec 26 10:41:01 2007 -0500 + + Moved class fixture back to spec/fixtures/. + +commit 91d46b86a86270bb3174909a2d5cbc343ea138c7 +Author: Eero Saynatkari +Date: Tue Dec 25 19:20:18 2007 -0500 + + Added specs for $~, $&, $`, $', $+ and $1..N. + +commit c434614505511b8816548efcf4a4cf56d77220f4 +Author: Eero Saynatkari +Date: Tue Dec 25 19:19:11 2007 -0500 + + Improved language-level class specs, moved fixture. + + * Class fixture copied to spec/language/fixtures/. + * Disabled unnecessary class instance variable check and added + new ones. + +commit 0a49f3485fe7e26cc7d7d5bc3cb800ddf9fd6231 +Author: Eero Saynatkari +Date: Tue Dec 25 19:13:22 2007 -0500 + + Changed strange_block_args_subspec.rb to block_args_subspec_strange.rb + + * Name change to improve alphabetical sorting. + +commit 2ac50215dd32fd7ad2f2c20c7ae06ed73dc9f856 +Author: Vladimir Sizikov +Date: Tue Dec 25 13:32:06 2007 +0100 + + Added tests for Array#pack with "U" pattern. + + Note: The tests are exclided for Rubinius. + +commit 4c0993fa90010322bb823a9799a8b3ccdd585e2e +Author: Jeremy Roach +Date: Wed Dec 26 04:07:02 2007 -0600 + + return excluded spec + +commit 0a69d9cd5a7d3a0be9411fa00c4eeebe5d270a0c +Author: Jeremy Roach +Date: Wed Dec 26 03:55:43 2007 -0600 + + implements @AM directives for String#unpack + + squashes bug in a regexp where an alternation of things + between begin and end assertions wasn't wrapped in group delimiters + +commit ddda4d49f5535577c147d2154ecdae7cb4e32e24 +Author: Brian Ford +Date: Wed Dec 26 01:16:13 2007 -0800 + + Moved Kernel#load/#require fixtures to spec/fixtures. + +commit 0438e9e61c5958c5daf691b025e34bc79e7b2573 +Author: Brian Ford +Date: Wed Dec 26 01:06:12 2007 -0800 + + Reorganized specs to group all Rubinius specs under spec/rubinius. + +commit a4c3e286e44ee3df88395b9b5f44d5804154ed2b +Author: Brian Ford +Date: Tue Dec 25 19:32:55 2007 -0800 + + Updated CI excludes. + +commit 8535481571712cf8c35f437c42ec53dcbfd44bc0 +Author: Brian Ford +Date: Tue Dec 25 15:52:53 2007 -0800 + + Enhanced guard for detecting :ruby, :ruby18, :ruby19 engines. + + Changed dir specs to create the fixture directories with every + run to prevent pollution of the directories from causing spurious + errors. + + Added spec/core/dir/fixtures/mock to .gitignore. + +commit a6b07ec37da7a59f34f45dfc84a66729b12f63b7 +Author: Brian Ford +Date: Mon Dec 24 16:25:16 2007 -0800 + + Removed Dir specs mock directories from version control. + +commit b0e4addbf7c6505c760e143e5fac0dab0109d8ac +Author: Eero Saynatkari +Date: Tue Dec 25 13:17:28 2007 -0500 + + Updated CI excludes for Dir. + +commit 80a9c6c2e2e5cd2acdcb6492c4a06fef258bb49e +Author: Eero Saynatkari +Date: Tue Dec 25 13:17:09 2007 -0500 + + Moved Rubinius-specific parts of #load/#require specs to extensions. + +commit 85f6b6e24518868f39ff39a5014a41a233237671 +Author: Jeremy Roach +Date: Tue Dec 25 02:53:45 2007 -0600 + + implements U directive for String#unpack + + uses only one of the exception messages every time + +commit b414c94db1fa1af8e6cd3382c34fc6de5ed3bd1e +Author: Kevin Clark +Date: Mon Dec 24 16:42:22 2007 -0800 + + Merge identical specs + +commit e0f28c224a2348dbf7c005694971a86f8e6162e1 +Author: Kevin Clark +Date: Mon Dec 24 15:59:24 2007 -0800 + + Kernel.Integer() shouldn't pass a base to String#to_inum + +commit eb93da7c578599469fe209f7b1d30f0f77d148f5 +Author: Vladimir Sizikov +Date: Sun Dec 23 16:43:33 2007 +0100 + + Wrapped one String#crypt case into compliant block for JRuby. + + "hello".crypt("\x00\x00") is not really defined, + and heavily platform dependent. + +commit 7594c89cf2f017cb1fffad16bac6fcc7c9629422 +Author: Vladimir Sizikov +Date: Sun Dec 23 10:53:30 2007 +0100 + + Added JRuby wrapper for String#% test. + + Allow "%e" % (0.0/0) in JRuby to return "NaN", and not "nan". + I think, returning "NaN" is a proper behavior, and + it seems that MRI 1.9 is also following it. + +commit eaf9e328e81f9c1d4e80737a96d0eea6b511fabb +Author: Jeremy Roach +Date: Mon Dec 24 06:55:30 2007 -0600 + + implements BbHhIiLlSs directives for String#unpack + +commit 701945421d6a656f8b0b183052c4535a895e2afd +Author: Brian Ford +Date: Mon Dec 24 00:44:26 2007 -0800 + + Converted specs to use the new #platform guard syntax. + +commit 238fbbc2331a1926f3d3f447d8433b046e7d34ac +Author: Tom Mornini +Date: Sun Dec 23 15:43:26 2007 -0800 + + Clean up language on now understood and fixed alias_method e2mmap spec. + Fixing the alias_method problem has now uncovered something in const_set, + so I've included a very vague test (require 'e2mmap') to document the + problem until it's better understood. + +commit 69149b261ac13cc1a2b7c80c7b103d397fd96b9b +Author: Tom Mornini +Date: Sun Dec 23 14:13:22 2007 -0800 + + Add spec for alias usage that breaks e2mmap.rb + +commit 71d9a4144811b2c9c74edc55f348637c57b0cb84 +Author: Jeremy Roach +Date: Sun Dec 23 05:25:37 2007 -0600 + + implements aDdEeFfGgXx directives for String#unpack + +commit bebafb1383a5126c959c33a1336f3a2e4b6993f6 +Author: Eero Saynatkari +Date: Sat Dec 22 18:16:14 2007 -0500 + + Saner specs for stream-style Dir access. Passes 1.8.6-p111. + + * Dir#read, #tell, #pos, #pos=, #seek and #rewind which are a part + of the stream interface to Dir no longer rely on platform-specific + position values, instead opting to just ensure they work as expected. + +commit a2e4c318a3406c9532404611f14d2790695c0a7a +Author: Eero Saynatkari +Date: Sat Dec 22 16:29:00 2007 -0500 + + Enabled Time#at spec to work with BSD `date`. + +commit 0e983f2e948ab997834dbc703e9eeb11d86a7022 +Author: Eero Saynatkari +Date: Sat Dec 22 16:17:14 2007 -0500 + + String#to_i spec to check for correctly parsing 0x-1 and the like. + + * This was fixed in 1.8.6-p111. + +commit 5e635a46f4733bcc2071b52ea076584614fe5655 +Author: Eero Saynatkari +Date: Sat Dec 22 15:52:03 2007 -0500 + + Fixed various String spec issues and added a few. Passes 1.8.6-p111. + + * String#% with o for octal numbers is still broken but that seems + to be due to MRI's sprintf.c. + +commit 780f22bde03e280f5af2509bef260585341f4e0b +Author: Eero Saynatkari +Date: Sat Dec 22 15:48:55 2007 -0500 + + Fixed incorrect use of #should raise_error. + +commit 0b239b4f66c20ad5690e429639c4bf11a809ab58 +Author: Eero Saynatkari +Date: Sat Dec 22 12:22:20 2007 -0500 + + Hash specs fixed. Pass under 1.8.6-p111. + + * Changed to use HashSpecs#frozen_hash and #empty_frozen_hash + for clarity and being less error-prone. + * Fixed various typos causing problems. + +commit 9a2450e5c51333474cf012c3a1364e95384af9e0 +Author: Eero Saynatkari +Date: Sat Dec 22 12:20:48 2007 -0500 + + Kernel.caller specs revised. Pass 1.8.6-p111. + + * Fuzzier matching of the data in the call stack. + * Fixed specs for omitting frames. + +commit 692f4e8a652e273096c0f77ffe571318c59d2b12 +Author: Eero Saynatkari +Date: Sat Dec 22 10:59:19 2007 -0500 + + File.ftype specs pass on 1.8.6. + + * Use `find` to locate specific file types instead of relying on + predefined paths being correct. + * Re-enabled character devices. + * FreeBSD does not implement block devices. + +commit f1251ebc602311ec305a4b1b35a765ee45b9c164 +Author: Eero Saynatkari +Date: Sat Dec 22 10:11:07 2007 -0500 + + Bignum#div returns an Integer if evenly divided. + +commit b62e1b7a21df1d7736767530f216148b8a93e38a +Author: Eero Saynatkari +Date: Sat Dec 22 09:52:33 2007 -0500 + + Fixed Array spec failures under 1.8.6-p111. + + * Array#fill raises if given a negative count. + * Array#initialize will always raise if frozen. + +commit e3d6a3df6c1dfc37731ff4de5de32dc996bb61bb +Author: Eero Saynatkari +Date: Sat Dec 22 09:37:46 2007 -0500 + + Silence warnings from removing *.rbc in #load and #require specs. + +commit a11171e853b3efb94b4cba03786ea851d81411c6 +Author: Jeremy Roach +Date: Sat Dec 22 07:39:40 2007 -0600 + + implements CcQqVv directives for String#unpack + +commit eea90994f2a1b76ed11b29e05a16c9c299d59235 +Author: Eero Saynatkari +Date: Sat Dec 22 00:23:29 2007 -0500 + + Added __FILE__ specs to #load. + +commit 0e04ca49ebdba35a7a293b6de82d9d67c6ff4ac5 +Author: Eero Saynatkari +Date: Fri Dec 21 22:59:50 2007 -0500 + + Correct __FILE__ information from #load and #require. + +commit 51c2543fe032b680a6c8f8cf8121196070c61c66 +Author: Brian Ford +Date: Fri Dec 21 23:51:37 2007 -0800 + + Replaced use of @path1 with equivalent nil in File#fnmatch. + +commit b9f979393456dc3c93250e3a50b54b489a25c5d1 +Author: Brian Ford +Date: Fri Dec 21 23:29:51 2007 -0800 + + Added -w to bin/mspec to emit warnings. Fixed warning in mSpec. + +commit 16ce249216f490b9f7921aa69932f9e8bd60ca0e +Author: Jeremy Roach +Date: Fri Dec 21 22:50:40 2007 -0800 + + Implements N, n, and Z directives for String#unpack. + + Signed-off-by: Brian Ford + +commit e1d292e28fe409c087f314bb139371a1f248850d +Author: Vladimir Sizikov +Date: Thu Dec 20 15:04:12 2007 +0100 + + Fixed race condition in ThreadGroup#add specs. + +commit 469527ddf33484a4a77f3d73c611e9a393bd48ad +Author: Adam Gardiner +Date: Fri Dec 21 12:02:50 2007 +1100 + + Added CompiledMethod#decode specs + + * Added UnboundMethod#compiled_method accessor + * Improved robustness of ISeq decode when dealing with junk at + the end of an iseq + +commit 08c2f5c29a2debed90ae1fff817c30e269913609 +Author: Eero Saynatkari +Date: Thu Dec 20 23:45:53 2007 -0500 + + Re-enabled purging .rbc files in require_spec and fixed the masked problem. + +commit dd4f3c52e79d01e826918e49fa626d7358f87901 +Author: Evan Phoenix +Date: Thu Dec 20 22:53:48 2007 -0800 + + Clean up a couple of failures seen in ci. spec/core passes. + +commit a5667632ae8d112c0271e00cbba53a274075cd1a +Author: Brian Ford +Date: Thu Dec 20 22:49:05 2007 -0800 + + Removed legacy, unused spec/reports/base.txt. + +commit 853e100b6f7fff24e4aaa40ed30c6add523f8df2 +Author: Evan Phoenix +Date: Thu Dec 20 17:49:36 2007 -0800 + + Fix a bunch of String specs (thanks random8r) + + * Note: rubinius now has the same behavior as MRI for Nan, + Infinity and -Infinity when using String#to_f + +commit b220f4921fd799ac28c60132ca08cf16df6f713e +Author: Charles Comstock +Date: Thu Dec 20 15:37:25 2007 -0600 + + fixed require specs to work correctly on any run including first + +commit 56ac483e3559e1d4913e4c36c9a8f007523fdab0 +Author: Hunter Kelly +Date: Thu Dec 20 19:36:21 2007 +0000 + + Fix typo in spec/core/regexp/union_spec.rb + +commit 634300eed40ef0ded16ab7cac7865dd783486c2d +Author: Charles Nutter +Date: Thu Dec 20 03:46:59 2007 -0600 + + Add 'sleep' checks to threadgroup spec to avoid the same race conditions seen in kernel/sleep_spec. + +commit 72b7123c9b3d1d266f4ce035b4e99dd0c2dbd88d +Author: Eero Saynatkari +Date: Wed Dec 19 22:46:17 2007 -0500 + + New compliant (moreso, anyway) #load and #require. + + * Improved #load and #require. + * Specs for the above. + * File.to_sexp and String#to_sexp allow empty input. They are + processed as a file containing 'nil'. + * Archive#get_object_fuzzy allows no extension or .rb instead + of .rbc only. + +commit fe633062095096fe00599cbb89aa4370ab5ccb3e +Author: Evan Phoenix +Date: Wed Dec 19 23:03:08 2007 -0800 + + Fix Kernel#puts + +commit 364ca08cbbb1848b549d99deb11e2449ad99334a +Author: Brian Ford +Date: Wed Dec 19 23:10:17 2007 -0800 + + Updated CI excludes. + +commit 5f1c381560a8d4d594749d42b5b2feeec341d4e5 +Author: Evan Phoenix +Date: Wed Dec 19 22:24:10 2007 -0800 + + Fix Kernel#open + +commit 3b3ed6304deab01cb448665c5f4b17d813f04e65 +Author: Evan Phoenix +Date: Wed Dec 19 21:43:58 2007 -0800 + + Cleanup more method specs, all pass on rubinius now + +commit 76bbbf275f4e835444f684b2e688b292f20c1ffe +Author: Evan Phoenix +Date: Wed Dec 19 18:08:09 2007 -0800 + + Implement protected methods + + * Added a bit more verbose specs to methods_spec.rb, to show + specific cases. + +commit dce06b35481bb1951c587d36f63abaae069d0ae4 +Author: Vladimir Sizikov +Date: Tue Dec 18 22:28:30 2007 +0100 + + Wrapped one String#unpack test case into compliant block. + + The test case is platform-specific, and not suitable for, say, JRuby. + +commit bb4945ea7b9253150f753508e92633b6e355194a +Author: Vladimir Sizikov +Date: Tue Dec 18 21:30:24 2007 +0100 + + Added new String#unpack test to exclude file for CI. + +commit 1bd8beb8e0b335f1de309d6320312a1b64af1e4d +Author: Vladimir Sizikov +Date: Tue Dec 18 19:51:47 2007 +0100 + + Added more tests for String#unpack with Z/Z* patterns. + + These patterns are known to be tricky, and their + handling was changed during Ruby's life. + See [ruby-talk:98364]. + +commit d26edc2269a77667dbefcfb1ea6212d8ada9ef97 +Author: Evan Phoenix +Date: Wed Dec 19 01:25:27 2007 -0800 + + Fix a bunch of Task GC problems, better memory management. + + * Use ALLOC* macros instead of malloc/calloc directly + * Also, simple fix for Time + * A Kernel#loop implementation + +commit 4143b92e6112241ff2facd64047491ce579bf0e9 +Author: Brian Ford +Date: Wed Dec 19 00:43:32 2007 -0800 + + Finished converting Object.new to mock() in specs. + +commit 9cae61f827d2eeca0a744e551551efd6bc85a2ae +Author: Brian Ford +Date: Wed Dec 19 00:02:46 2007 -0800 + + Spec #it blocks must be inside #describe blocks for RSpec. + +commit 7df00ef6d2471d0b37829e0a4d1ef45edf782a44 +Author: Brian Ford +Date: Tue Dec 18 23:28:43 2007 -0800 + + More conversion of Object.new to mock() in specs. + +commit 12463512d0ad48fae3a1843d9d409649551dd13b +Author: Vladimir Sizikov +Date: Mon Dec 17 22:53:38 2007 +0100 + + Corrected String#* test to pick large enough Bignum, even on x64. + +commit 79cbff2c9a0cb15e9e5767f94242fa4360a0c4a0 +Author: Vladimir Sizikov +Date: Mon Dec 17 16:43:16 2007 +0100 + + Removed race condition from Kernel.sleep spec. + + This problem caused JRuby spec runs to hang. + +commit 3032e60e10dd1ae61ffb40b351f4f6731395602b +Author: Vladimir Sizikov +Date: Mon Dec 17 15:43:42 2007 +0100 + + Enabled one Hash#rehash test case for JRuby + +commit 1808106191856f4f82b948abc5c7e708a747d059 +Author: Vladimir Sizikov +Date: Mon Dec 17 15:40:49 2007 +0100 + + Issue #153: Hash#rehash test enforces unspecified impl detail + + Wrapped the test so that it won't run with JRuby. + +commit efbf30477ea289911d9cafbde89ecbe2c8c65089 +Author: Vladimir Sizikov +Date: Mon Dec 17 15:25:23 2007 +0100 + + Corrected :mri --> :ruby + +commit 60a3ede3c64b62fb26905ed1c236c0e241b64515 +Author: Vladimir Sizikov +Date: Mon Dec 17 15:23:42 2007 +0100 + + Issue #182: String#to_f spec corrections for NaN, Infinity + + JRuby AND Rubinius treat "Infinity".to_f, "-Infinity".to_f , + "NaN".to_f differently than MRI. + + MRI returns 0.0 in all those cases, but JRuby and Rubinius probably + do something more meaningful, they return Infinity, -Infinity and Nan + respectively. + + It was agreed that JRuby's and Rubinius' behavior is a feature rather + than a bug, and worth preserving and checking for. + +commit 521a82d8c325a33b3409423d61b589c7b8681870 +Author: Vladimir Sizikov +Date: Mon Dec 17 15:14:28 2007 +0100 + + Refactored commonly used generators into fixtures. + + Thus reducing copy-paste. + +commit 86820a339c74e3ca8fc9515e5fdf31ad42780201 +Author: Vladimir Sizikov +Date: Mon Dec 17 14:12:43 2007 +0100 + + Initial version of Generator specs. + +commit 91353183ace65d8e751db14a829e8f24d043710c +Author: Charles Comstock +Date: Tue Dec 18 03:18:19 2007 -0600 + + updated excludes for Kernel#sleep and Thread + +commit 6de193c0819f74717eb2e9eff8480f0d801b0e41 +Author: Brian Ford +Date: Tue Dec 18 01:01:31 2007 -0800 + + Replaced Object.new with mock() where appropriate. + +commit 1217fa030ff26712e9718ebecfe351830c543d7e +Author: Charles Comstock +Date: Tue Dec 18 02:16:10 2007 -0600 + + fixed redo in loop by save/restore condmod around loop context in compiler1 + +commit df757142c774becfc2cbc4b38e43e31056acbae2 +Author: Charles Comstock +Date: Tue Dec 18 02:00:27 2007 -0600 + + spec for using redo,next, and break in one loop + +commit 7169fd31b7c22750241212c242bc8aacdafe632f +Author: Vladimir Sizikov +Date: Mon Dec 17 13:08:09 2007 +0100 + + String#sub specs wrapped to correct JRuby test failures. + + JRuby reports Ruby version to be 1.8.5, but in this + particular case it behaves like Ruby 1.8.6 + + Differences between Ruby 1.8.5 and Ruby 1.8.6: + different error raised. + +commit 04e228e131d06cd764d69375ddfdf44e4fec2b38 +Author: Charles Comstock +Date: Tue Dec 18 01:27:00 2007 -0600 + + spec for Thread.pass and updated :mri to :ruby + +commit 11348e25ba30199e3beb05f8c38c18820fbefc3f +Author: Charles Comstock +Date: Tue Dec 18 01:11:38 2007 -0600 + + Some minor fixes in Thread specs + +commit 5cb3bcbf8f1d2a2237200ca0a9a9c6408d478ad6 +Author: Charles Comstock +Date: Tue Dec 18 01:11:05 2007 -0600 + + spec and basic functionality for Thread.stop + +commit 3301fbb3ec43b5252c0aa6d45eb2f0e21581ff0a +Author: Charles Comstock +Date: Tue Dec 18 01:09:49 2007 -0600 + + Thread.sleep doesn't even exist in rubinius + +commit bd964f579f84a39097ecee1271664d672b6553a7 +Author: Brian Ford +Date: Mon Dec 17 21:40:16 2007 -0800 + + Replaced :mri with :ruby for spec guards. + +commit d21810882621356c35dcd101daca5ee5549f6607 +Author: Charles Comstock +Date: Tue Dec 18 00:42:32 2007 -0600 + + Threads should report there status of sleep, aborting, and run + +commit 85a6476a236bd1e65d42ca03846c662a10842f37 +Author: Charles Comstock +Date: Tue Dec 18 00:41:09 2007 -0600 + + Thread#status should return nil if Thread terminates with an exception + +commit ecb4455a75f4af2ae0059ca4960c2282b4ec632a +Author: Charles Comstock +Date: Mon Dec 17 23:50:24 2007 -0600 + + specs for Thread#{key?,keys} and added key type checks for Thread#{[],[]=} + +commit c1a5d7e52b33ba6686441c61652bcc41ae0547f8 +Author: Charles Comstock +Date: Mon Dec 17 22:15:37 2007 -0600 + + specs for Thread#[] + +commit c2c7f0adc6ebbad925adb2471b6064b67528b420 +Author: Charles Comstock +Date: Mon Dec 17 21:47:52 2007 -0600 + + ensure LocalThread#current != Thread.current + +commit d05dac276f36326e143aa75bb43e4ab07bd8ddc9 +Author: Evan Phoenix +Date: Mon Dec 17 21:28:44 2007 -0800 + + Fix a bunch more Kernel specs + +commit 4ddd0e144b4e4f64c51fc8d64952826d92a5e83f +Author: Evan Phoenix +Date: Mon Dec 17 19:45:26 2007 -0800 + + Fix Thread.abort_on_exception and Thread#inspect + +commit a12ad6fbc2589a7864a7c784386fb6ce7dae1db1 +Author: Charles Comstock +Date: Mon Dec 17 21:29:49 2007 -0600 + + Kernel#sleep and Thread#join specs now use locks to maintain automaticity instead of while th.status == 'run' loops + added a Channel fixture to Kernel to support the use of locks in Kernel#sleep specs + +commit 72e3fb453c266e514b817daa66bf6033f1d19e40 +Author: Evan Phoenix +Date: Mon Dec 17 19:01:18 2007 -0800 + + Fix callcc specs, revert all locals back to using the locals tuple + + * compiler1 now does what compiler2 is going to do, ie, only use the + locals tuple to store locals. Storing them on the stack has proved + to be a pain, and wont be used further. + +commit 6e35be2ddef8d055e064462c88a8b3f33eb4fe0f +Author: Evan Phoenix +Date: Mon Dec 17 18:37:16 2007 -0800 + + Faster Class#new, initialize can be private + + * Class#new now uses a bunch of inline assembly to be able + to call a private initialize + * Clean up Module#name a little + * Made machine's rbt a little more robust + +commit 47a5bbf34ef8a60a18c1c8c6130d493a299ff852 +Author: Hunter Kelly +Date: Tue Dec 18 01:10:33 2007 +0000 + + Make sure files opened with "w" are truncated. + +commit 0fdc8c9b7d05cc2e96908b280ac144de0d04f646 +Author: Hunter Kelly +Date: Tue Dec 18 00:16:27 2007 +0000 + + Fix excludes for spec/core/stuct/{new,struct}_spec.rb + +commit 5b1252e6b2d8f8d70343b06f3520114de2040524 +Author: Hunter Kelly +Date: Sat Dec 15 17:31:25 2007 +0000 + + Minor fix to struct_spec to include fixture. + +commit 4fd0356ab9e9bb5c2a805b1f863b3177458966fe +Author: Hunter Kelly +Date: Sat Dec 15 16:29:49 2007 +0000 + + Add case to spec/core/class/new_spec for names of nested classes. + + * Updated spec + * Add some comments where this may be fixed + +commit 86736d564f34a2f97f7c7bedcab09c2472861b01 +Author: Hunter Kelly +Date: Sat Dec 15 16:16:26 2007 +0000 + + Fixes for struct class names. + + * Tighten up specs to show what class names should be. + * Fixes #inspect + * (Partially) solves the larger issue of an anon class getting a name when + assigned to a constant - works now when Module.const_set is called. + +commit ad0d5ff2396baf43c8b2e37a3132765a074b241d +Author: Hunter Kelly +Date: Fri Dec 14 02:33:23 2007 +0000 + + Fixes related to Struct.new + + * Fix to method_table to correctly handle DelegatedMethods + * Changed spec/core/stuct/new_spec.rb to allow :rbx to call + to_sym on objects passed to Struct.new to get the symbol + value. + +commit 18f10dc700fe24f3bd230063bc7c1e8a82e8348f +Author: Brian Ford +Date: Mon Dec 17 08:58:22 2007 -0800 + + Updated spec excludes to run with bin/ci under the new mspec. + +commit 678fb90c5c8aa96e10a9f95f520312f12f8fa3f2 +Author: Brian Ford +Date: Sat Dec 15 22:43:44 2007 -0800 + + Changed true/false/nil specs to not use def in describe block. + +commit 9e132474aafb6a0f0c968c2e085b09bfc07e1a0d +Author: Brian Ford +Date: Sat Dec 15 18:49:31 2007 -0800 + + Fixes to run the specs under RSpec and mSpec. + +commit cd3ecf52645b94921db92393e6e4d295d12bba88 +Author: Brian Ford +Date: Sat Dec 15 00:59:11 2007 -0800 + + Misc fixes to mspec. bin/mspec -t r spec finally runs! + +commit f3b3f70bb47b04e7a67c1dbc3ae38711857b5184 +Author: Brian Ford +Date: Thu Dec 13 22:46:54 2007 -0800 + + Multitudinous miscellaneous fixups for mspec and mmock. + +commit 55ab5b2ee42e4fabcfd8c51d6fac304cdfec31c7 +Author: Brian Ford +Date: Mon Dec 10 01:01:58 2007 -0800 + + Various fixes to mspec to run Rubinius specs. + +commit 86c0f131608b4ad7cba93eabd172a48e5b60ca0f +Author: Brian Ford +Date: Fri Dec 7 15:34:06 2007 -0800 + + Added runner guards to omit specs that will always fail under RSpec. + +commit 75706dbfabbe359b6410f0d3743f0ea682146ac1 +Author: Brian Ford +Date: Fri Dec 7 14:03:46 2007 -0800 + + Added #runner guards to mspec. + +commit 3da390988031bf0066a849934ee758475ebbfa04 +Author: Brian Ford +Date: Fri Dec 7 02:38:13 2007 -0800 + + More fixes to run the specs under RSpec. + +commit 2b0f4e408b733dcd9089a19d78cd8e4cce20b99c +Author: Brian Ford +Date: Thu Dec 6 18:40:33 2007 -0800 + + Yet more spec cleanup to run under RSpec. + +commit 1e4171d4682f55776e01e42f564714548c1d9bd9 +Author: Brian Ford +Date: Thu Dec 6 02:16:42 2007 -0800 + + More changes to run specs under RSpec. + +commit db020d30374e419792f76077757784008953c0a6 +Author: Brian Ford +Date: Wed Dec 5 02:01:30 2007 -0800 + + Various changes to get the specs running under RSpec. + +commit 968c2daa5345a0cddb8d3d5bd2b6bf2eeb0c1d6f +Author: Brian Ford +Date: Tue Dec 4 23:51:52 2007 -0800 + + Convert remaining mocks to RSpec syntax. + +commit e5dc3ac814d1cda923131257dfbc9a30bf501b62 +Author: Brian Ford +Date: Tue Dec 4 23:28:17 2007 -0800 + + Convert remaining 'should_raise' to 'should raise_error'. + +commit a7be230ac71ece2bb8dcece72d629bcd0ce6a5e0 +Author: Brian Ford +Date: Tue Dec 4 18:29:26 2007 -0800 + + Converted specs from mini_mock to RSpec mock syntax. + +commit 4136e2fef4a81eb6e9e14070ff5301638f9acf14 +Author: Brian Ford +Date: Tue Dec 4 02:25:31 2007 -0800 + + Integrated mini mock with mspec. Updated spec_helper for main specs. + +commit d71c0c7412af01d6295d8caab43a80d0221ea16d +Author: Brian Ford +Date: Tue Dec 4 01:49:17 2007 -0800 + + Added #shared and #it_behaves_like to mspec. + +commit da61adc0a079c858385773b12d683e2f5e2cc0e8 +Author: Brian Ford +Date: Tue Dec 4 00:43:15 2007 -0800 + + Converted 'should_be_ancestor_of' to 'should be_ancestor_of'. + +commit 5ed0096aac58fef09fc766d808aea74356aacfa8 +Author: Brian Ford +Date: Mon Dec 3 17:57:56 2007 -0800 + + Replaced dev_null with CaptureOutput. + +commit 62282bd5cb5c555e6447dcf2d6d0da355913fe8b +Author: Brian Ford +Date: Mon Dec 3 17:30:01 2007 -0800 + + Replaced 'should_include' with 'should include'. + +commit f990a7c58a7eee6dbbb3c50df7682942048b959f +Author: Brian Ford +Date: Mon Dec 3 17:21:50 2007 -0800 + + Replaced 'should_be_close' to 'should be_close'. + +commit c53601c56bd222dfacf03f134132869eb71c5146 +Author: Brian Ford +Date: Mon Dec 3 17:10:41 2007 -0800 + + Finished converting should_raise to raise_error. + +commit 165dd99535b0829d2e2364fac24375068969c6ab +Author: Brian Ford +Date: Sun Dec 2 23:22:54 2007 -0800 + + Convert should_raise to should raise_error for RSpec compatibility. + +commit c9ff50a4b4be25614cc0ac2ea5540cfe87a939d3 +Author: Brian Ford +Date: Wed Nov 28 23:30:44 2007 -0800 + + Moved mspec out of spec dir. + +commit e9a40a77b6fa7d08969ea195aabbb930b665fe02 +Author: Brian Ford +Date: Wed Nov 28 23:25:59 2007 -0800 + + MSpec base formatter and specs. + +commit 2fc3ac3f8efbaf0861cadfd59bcdf926d2196284 +Author: Brian Ford +Date: Wed Nov 28 20:32:42 2007 -0800 + + Port fix to ruby engine detection from mainline. + +commit 9a52e660536b4723bf24e2717fec757a1bdfa49f +Author: Brian Ford +Date: Wed Nov 28 01:35:52 2007 -0800 + + Match RUBY_NAME against /^ruby/ to pick up e.g. ruby1.8. + +commit 85536b98862f6abec310bfad03be17652ee65944 +Author: Brian Ford +Date: Wed Nov 28 01:01:46 2007 -0800 + + Implemented mspec matchers. + +commit c953335397c6c8b9d7b27a3d240fde3b3518cb48 +Author: Brian Ford +Date: Tue Nov 27 01:35:07 2007 -0800 + + The rest of the mspec big picture. + +commit 2f598f193eb1b10065c8e1a8d5c2aaa89c689072 +Author: Brian Ford +Date: Tue Nov 27 00:39:59 2007 -0800 + + Added base operator matchers and specs. + +commit 0cc0b5a97661970d4cbb5e46406e7ee06421e637 +Author: Brian Ford +Date: Mon Nov 26 21:03:33 2007 -0800 + + Migrated mspec and ci runners to mspec dir. + + Created stubs in bin/ci and bin/mspec that call the respective + scripts in spec/mspec/scripts. + +commit b98d65eaa90d966fc2f7b8f8387266e241c202de +Author: Brian Ford +Date: Sun Nov 25 01:03:26 2007 -0800 + + Added specs and guards for mspec. + +commit 7bb316d1291c9d0a16904d4a3ee60094a713f215 +Author: Brian Ford +Date: Sat Nov 24 21:40:46 2007 -0800 + + Prevent MSpec's #should(_not) from overriding RSpec's. + +commit c446988257a2104d72abd4a362dc21ca6183aab0 +Author: Brian Ford +Date: Tue Nov 20 22:47:25 2007 -0800 + + Defines #should and #should_not for mspec. + + Specs for #should and #should_not. + Adds example for using mspec "base" layer. + +commit 1aecf8e828dfd3d86f43d8c9c927e7c0ccb16b68 +Author: Brian Ford +Date: Mon Nov 12 00:40:04 2007 -0800 + + The mini rspec big picture. + +commit f2979b03f29e7ac810b81f9087ea53923de5a35c +Author: Charles Lowe +Date: Mon Dec 17 15:18:24 2007 +0100 + + Added missing error checks to Dir.chdir block form. + +commit 028fee4e6d48514cae53f87c143bb68501bf58e9 +Author: Chris Shoemaker +Date: Wed Dec 12 20:01:35 2007 -0500 + + Add further specification of size changes during Hash#delete. + + This was actually failing a while ago but now passes after recent + changes. + +commit f757f4359c86f778ac8e5931b8915511fd03506d +Author: Chris Shoemaker +Date: Wed Dec 12 19:59:28 2007 -0500 + + Tighten another spec in core/hash/equal_value_spec.rb + + Hash#== compares keys with matching hash codes via eql? + + This spec was using hash keys where key.eql?(key) was false. + + That's pretty pathological, but there's probably some real + non-conformance with MRI here. MRI can test for object identity + without calling eql? so a key is still found even if it doesn't + eql? itself. + + That's not really related to the behavior this spec is specifying, + though. So, this patch just uses a less pathological implementation + of eql? + +commit 3f73ddf6bec5c704ceb5ed43481971860293353d +Author: Chris Shoemaker +Date: Wed Dec 12 19:49:49 2007 -0500 + + Tighten spec in core/hash/equal_value_spec.rb + + "Hash does not compare keys with different hash codes via eql?" was + failing because it detected that eql? was called on a key. However, + eql? was not being used to compare keys with different hash codes + from the two hashes. Instead, eql? is used to compare a key to itself + during hash element reference, in order to distinguish between two keys + with equal hash codes that aren't eql?. + + The tightened spec only fails if the keys are compared eql? to each other. + +commit e355e98a32f34619628a17f5052750da6881cda9 +Author: Chris Shoemaker +Date: Wed Dec 12 12:26:15 2007 -0500 + + Add specs for hash stability of various Numeric subclasses. + +commit 0d774c99254b2c5992a17ecb2a5a12dcd2cad05e +Author: Chris Shoemaker +Date: Tue Dec 11 20:37:45 2007 -0500 + + Add a Hash#store spec for storing unequal keys w/ same hash. + +commit edfff4981285007ecac132f565243150a8a8bd7e +Author: Curtis Schofield <123@noself.net> +Date: Tue Nov 27 19:02:45 2007 -0800 + + Specs created for Process#gid and Process#uid + + * both are using the unix system command 'id' + +commit 4e269d01238537cc45f4c347b12053616007d94d +Author: Tilman Sauerbeck +Date: Sun Dec 16 23:46:50 2007 +0100 + + Excluded evil Thread specs. + + This makes bin/ci usable (pass) again on my system, where it was horribly + broken before. + +commit b32c46ba95f2ecdaf646a030b96ee9b3737929a0 +Author: Tilman Sauerbeck +Date: Sun Dec 16 23:37:26 2007 +0100 + + Excluded failing Kernel#eval specs. + +commit 0c56f3a1f84dd94d1f9685af9e9d6e0efd0cfabf +Author: Hunter Kelly +Date: Sun Dec 16 20:24:54 2007 +0000 + + Tighten up specs for what should happen when array shrinks during iteration. + +commit bfa8c532605c9e3b3d7f853516de9aae596c611d +Author: Hunter Kelly +Date: Sun Dec 16 19:11:25 2007 +0000 + + Added specs for Array#each when the array is changed during iteration. + + Ditto for Array#each_index. + +commit b3aa2af4a3467b4eeb8765010286c12bd5adfbf9 +Author: Tilman Sauerbeck +Date: Sat Dec 15 22:53:35 2007 +0100 + + Sanitized Object#id spec. + +commit cce5b7004a774041d78c3b2e55af8063335a9512 +Author: Evan Phoenix +Date: Fri Dec 14 19:02:57 2007 -0800 + + Fix sleep spec, implement Thread sleep status and death detection + +commit c2475838be23ae287075b7e9ea832013f1db77c4 +Merge: 30f20cf... d061b86... +Author: Charles Nutter +Date: Fri Dec 14 10:56:53 2007 +0100 + + Merge branch 'master' of git@git.rubini.us:code + +commit 30f20cfbd67487c426827406890fdb06fac8045c +Author: Charles Nutter +Date: Fri Dec 14 10:56:17 2007 +0100 + + Fix race conditions in Kernel#sleep spec by ensuring target thread is actually sleeping before continuing. + +commit d061b864f636210e40982d961b0aa5afc24543d0 +Author: Evan Phoenix +Date: Thu Dec 13 23:04:27 2007 -0800 + + Fix require specs to not require checked in .rbc or .rba files + +commit 41831976d25a4d5a8e26673199276098cc45b4d3 +Author: Evan Phoenix +Date: Thu Dec 13 22:40:43 2007 -0800 + + Fixed Thread#run, added corruption detection to rbc files + + * Thread#run was confusing the Thread scheduler, cause things the VM + to quit running. + * Added corruption detection to .rbc files in the form of a SHA1 + hash placed in the .rbc, just after the header. + +commit 77f0f29060d5ba3f33dc45029525acb715eb61c2 +Author: Adam Gardiner +Date: Fri Dec 14 15:10:59 2007 +1100 + + Compiler2 fix for attrasgn in masgn + + An attrasgn node contained within an masgn does not include + the assigned value in the attrasgn sexp. This was leading + to the argument count to []= to be understated by 1. + +commit faaa1932fe05ee4d506b768f8d9d884af5345547 +Author: Charles Comstock +Date: Wed Dec 12 19:59:11 2007 -0600 + + fix non-determinism from Thread.sleep by removing blocking sleep + add check for duration of 0 or 0.0 to instant return and added more specs + +commit e98b2d1f9788c1813bef2d920779c95effbd3d9f +Author: Charles Comstock +Date: Tue Dec 11 17:27:43 2007 -0600 + + spec and fix to allow floating point timeouts to Thread#join + +commit 801cb5ef58a6debfd348a33f864737cbce7c3d77 +Author: Charles Comstock +Date: Tue Dec 11 17:26:58 2007 -0600 + + added noncompliant spec showing that sleep(nil) is allowed in rubinius + +commit 84d280810c840d6699b5c9ad094964fe779235df +Author: Charles Comstock +Date: Tue Dec 11 16:30:41 2007 -0600 + + fixed Thread#sleep to allows floats, and switched Thread::sleep, Kernel::sleep to use Thread#sleep on current thread + +commit 3d10a8a10741786ba76a4cc1083934f908d52ec2 +Author: Kevin Clark +Date: Mon Dec 10 17:13:34 2007 -0800 + + Allow Thread.new to take arguments + +commit 4f5258b938a7aacf31e73b5fe6312e3c927d9cf8 +Author: Kevin Clark +Date: Mon Dec 10 00:21:20 2007 -0800 + + Fix rb_define_alloc_func + +commit 7ab0f524de5a6b796ec1000402392cb138150eed +Author: Kevin Clark +Date: Fri Dec 7 15:06:45 2007 -0800 + + Initial ThreadGroup implementation + +commit 53fff95e300b1b26ed16f12c13684eadf8235d7a +Author: Kevin Clark +Date: Fri Dec 7 01:30:55 2007 -0800 + + Add rb_str_substr + +commit ec82de9f67e271718b874c0d777765da696bef88 +Author: Kevin Clark +Date: Sat Dec 8 15:26:17 2007 -0800 + + Add wrapped struct spec + +commit 65998d601aae601b3b43878f534362136a01ff17 +Author: Brian Takita & Nathan Sobo +Date: Fri Dec 7 17:18:24 2007 -0800 + + Added specs for Module#undef_method. + +commit 97f8c9c32b9400ae42d0dc80aa7e17b22864fce9 +Author: Brian Takita & Nathan Sobo +Date: Fri Dec 7 16:13:02 2007 -0800 + + Moved Object#to_a to Kernel#to_a. Added VM.coerce_to_array. + + VM.coerce_to_array will be used for splatting any object. + +commit 865ce7d771a101bc8c2c9ae3a82cbc3f37450c4b +Author: Brian Takita +Date: Fri Dec 7 12:47:08 2007 -0800 + + Merge branch 'array'; commit 'nathan/array' into array + +commit b4541a90f84c898e3cd9851ac4b207d559078a59 +Author: Nathan Sobo +Date: Thu Dec 6 23:33:10 2007 -0800 + + Updated language/array_spec.rb for more detail on splat operator. + +commit 577b2f1c395dc49165842c405fb47bbb7591158a +Author: Nathan Sobo +Date: Thu Dec 6 18:30:16 2007 -0800 + + Fixed :many_if for compiler 1. + + Before it was translating many_ifs to a flat array of if statements instead + of nesting them. Also, multiple boolean expressions in the case were not + expanded to a boolean disjunction. + + Signed-off-by: Brian Ford + +commit 1d555fa07aaed8e59e728cb0013daa10b3b17b25 +Author: Wilson Bilkovich +Date: Thu Dec 6 21:24:44 2007 -0500 + + Add some JRuby-inspired eval specs + +commit 3131fb81eef380d163d028f5587475bbf170befb +Author: Evan Phoenix +Date: Thu Dec 6 12:19:49 2007 -0800 + + Fix minor constant lookup issue and add timing to mspec + +commit 26897cd85c693cac10229d7467436717552088c0 +Author: Evan Phoenix +Date: Thu Dec 6 11:42:36 2007 -0800 + + Fix another constant lookup bug + +commit 06a3f07999aeb4f7379ea40205451d326d1ba596 +Author: Tilman Sauerbeck +Date: Thu Dec 6 16:37:15 2007 +0100 + + Updated CI excludes for IO#each and IO#each_line. + +commit b495ab1019e9ee136e9d099faa51cba03c48e947 +Author: Tilman Sauerbeck +Date: Thu Dec 6 16:01:31 2007 +0100 + + Extended argument checking in IO.read. + + We're now checking that offset isn't negative either. This is done + before the length argument is checked, mirroring MRI's behaviour. + Also fixed a typo in the length check. + +commit 4fa2fbb6b6c27ced5d6cf902e63e3989c2d29b64 +Author: Tilman Sauerbeck +Date: Thu Dec 6 14:56:41 2007 +0100 + + File.truncate raises Errno::ENOENT if the given file doesn't exist. + +commit cb7a0a7315e57f1adff0976bcd6b0c4a1a94d8c5 +Author: Tilman Sauerbeck +Date: Thu Dec 6 14:15:55 2007 +0100 + + Added support for the length and offset arguments to IO.read. + +commit 9fe8f2bd73e28d28b7a9249e629ab7681321e4d5 +Author: Tilman Sauerbeck +Date: Thu Dec 6 14:07:21 2007 +0100 + + IO.read only accepts file names and uses File to open and read them. + +commit 7ab1d9f3434e3f3b021de2f4087f2502e229c7a0 +Author: Tilman Sauerbeck +Date: Thu Dec 6 14:00:19 2007 +0100 + + IO.new(nil) raises TypeError now. + +commit 227f6b4bf45b55eb659d41507d38fe5071ef7424 +Author: Tilman Sauerbeck +Date: Thu Dec 6 13:24:31 2007 +0100 + + Fixed a typo in File.writable?. + +commit 645f30882c9dd39d13f49e45f2f32c43ebe25182 +Author: Kevin Clark +Date: Thu Dec 6 03:51:11 2007 -0800 + + Update Dir excludes + +commit e60ee517013d44c2ec6faf147f7dbd685fa520c2 +Author: Kevin Clark +Date: Thu Dec 6 00:40:17 2007 -0800 + + Fix flag checking in Dir.glob + + Also clean ".", ".." skipping + +commit fa681ad7a3c1d0e1b4fb0702c2fc63cd80ec9377 +Author: Kevin Clark +Date: Wed Dec 5 18:08:41 2007 -0800 + + File.fnmatch? should accept escaped wildcards + + Also fixes more Dir.glob specs + +commit e6b8ce23729606bf6fa748ea63c0e0a59b48a476 +Author: Kevin Clark +Date: Wed Dec 5 15:35:32 2007 -0800 + + Don't unescape leading period in File.fnmatch? + +commit 1cf054a08b0aeea7c348ff26c71ccaf22c02ce70 +Author: Wilson Bilkovich +Date: Thu Dec 6 02:36:54 2007 -0500 + + Rename Array#pretty_inspect to avoid conflict with pp + Hardcore bikeshed action on the way TestGenerators are inspected + Change describe.rb to call the renamed pretty_inspect + +commit 519d1226027623274766641a256e2a9753257266 +Author: Nitay +Date: Wed Dec 5 16:07:25 2007 -0800 + + Fix Constant = Class.new setting of name + + Signed-off-by: Kevin Clark + +commit 568c57ca57d4a9183e492024e17aa1352902d1d2 +Author: Wilson Bilkovich +Date: Wed Dec 5 20:31:43 2007 -0500 + + Clean up mspec output to prepare for unit_diff support + Use pretty_inspect to display compiler2 TestGenerator output + +commit e250521194380f4c942fd6d53664b746ca63e3e3 +Author: Evan Phoenix +Date: Wed Dec 5 17:27:27 2007 -0800 + + Fix constants spec to scope the fixtures + +commit 7010073617a4fa95ea5491284fef97a083d9d4f3 +Author: Evan Phoenix +Date: Wed Dec 5 14:42:27 2007 -0800 + + Vastly simplify and fix constant lookup + + * New constant lookup specs to test behavior + * Added StaticScope object and field on CompiledMethod which stores + a StaticScope instance which indicates the lexical scope of the CM. + +commit 163e56646a817301201af843b45c973da058688c +Author: Tilman Sauerbeck +Date: Wed Dec 5 23:00:16 2007 +0100 + + The spec for Dir#rewind doesn't pass on Rubinius. + + It's not platform specific, but we don't have a working Dir#pos yet + and the Dir#rewind spec relies on it. + +commit 3afe61bd78aa9e850f081b83ca2c478ae297bda1 +Author: Brian Ford +Date: Wed Dec 5 13:51:05 2007 -0800 + + Changed mini_rspec to show failures unless being run by autotest. + + Added dir_entry.rb to .gitignore. + +commit 0c661894b54615ee4915d61569e072ffdfa8826d +Author: Eero Saynatkari +Date: Wed Dec 5 09:17:24 2007 -0500 + + Much-improved tiny option parser lib/options.rb. + + * The Options API is much more user-friendly now, + size is still about 100 LoC + * Specs for the API. + +commit 092e0081c26eeda2ca6561eb19123b468965c84a +Author: Ryan Davis +Date: Wed Dec 5 01:40:31 2007 -0800 + + Added support for autotest. + Requires a new release of ZenTest to actually work. + I'm tired, I'll do that tomorrow + +commit 9e3e41d71d1bab8104ae17ff34aaa2311be3b0b1 +Author: Eric Hodel +Date: Tue Dec 4 22:59:49 2007 -0800 + + Commit miss for require specs + +commit d1a6f0805b739930e54406188e32ac1e0f30a74b +Author: Eric Hodel +Date: Tue Dec 4 22:24:49 2007 -0800 + + Add specs for Kernel#require, never add .rbc files to $LOADED_FEATURES + +commit a60e3bf901b62fbbbef59acb2c6c9f164be1fbbc +Author: Adam Gardiner +Date: Wed Dec 5 14:10:15 2007 +1100 + + Cleanup case spec, update excludes + + * converted case specs to not use should + * separated out case specs with target expressions from those without + * updated excludes for two failing specs under compiler1 + +commit f0de77911ff0b4532a47fb9803685e8d968d51ec +Author: Adam Gardiner +Date: Tue Dec 4 09:42:49 2007 +1100 + + Fixes for compiler2 when_spec failures + + * Added compiler2 spec for when without an arg + * Added spec for when without arg with an else to + spec/language/case_spec.rb + * Implemented many_if sexp compilation + +commit c9c67738ecae341441098993923838a15b64d166 +Author: Tilman Sauerbeck +Date: Tue Dec 4 20:42:27 2007 +0100 + + Test Etc.getgrnam() with "daemon" instead of "root". + + The "root" group seems to be a Linux-ism. + +commit 1fd6d97e8eb20ce9908cc0abd09b7c5555ff5720 +Author: Tilman Sauerbeck +Date: Tue Dec 4 19:31:24 2007 +0100 + + Post-move fix for the Options spec. + +commit ead52428d99549b6b53b8897d969e80072395ef6 +Author: Tilman Sauerbeck +Date: Tue Dec 4 19:12:28 2007 +0100 + + Moved codearchive.rb, options.rb and readline.rb from kernel/core to lib. + +commit 0e4568bcc23011957cc250de2a93031648281b21 +Merge: 78fba04... fbc5ad5... +Author: Charles Nutter +Date: Tue Dec 4 00:50:46 2007 -0600 + + Merge branch 'master' of git@git.rubini.us:code + +commit 78fba04c31e9d97c32862e9e104e3917dcff9137 +Author: Charles Nutter +Date: Tue Dec 4 00:39:05 2007 -0600 + + Making socket spec more reliable by using nonblocking accept for TCPServer and adding a "ready" flag for UDPServer. + +commit 7e925ea53239207f5dd9ac5daddda8e0f1f3b687 +Author: Tilman Sauerbeck +Date: Sun Dec 2 23:33:29 2007 +0100 + + Implemented Etc. + +commit 450778cf5f416f6b9531664d4fff2c159c93cbe7 +Author: Victor Hugo Borja +Date: Sun Dec 2 01:39:28 2007 -0600 + + Shared spec for class_eval. + + - removed method-arguments from describe string + This was causing bin/completeness to report 0 examples for Module#class_eval/module_eval + - examples checking for TypeErrors test the exception is raised, but don't check + the exact message as it is not part of the interface. + +commit cd0d11c7eb23d881f1dd73701bd3edc12c5bd744 +Author: Brian Ford +Date: Sat Dec 1 15:28:41 2007 -0800 + + Updated CI excludes for Dir.[]. + +commit e41e501bcf686937fbd3b8cfc86f325d7e06184d +Author: Brian Ford +Date: Sat Dec 1 10:54:35 2007 -0800 + + CI spec excludes updates. + + * Fixed rake pristine task to whitelist Kernel#require fixtures. + * Updated CI excludes for Dir.glob and Dir.[]. + +commit 8f362a0350238366565a373f1feb9594efe03407 +Author: Tilman Sauerbeck +Date: Sat Dec 1 18:50:55 2007 +0100 + + Make sure we delete the directories we're creating in the mock dir. + +commit dfc1b1cd32f47b48dd358ca50226d614425ef8b2 +Author: Tilman Sauerbeck +Date: Sat Dec 1 18:08:27 2007 +0100 + + Dir.chdir now always resets the working directory when called with a block. + +commit 02f41a92bbafd1a555344e1082970e090cd1f9a5 +Author: Tilman Sauerbeck +Date: Fri Nov 30 23:41:29 2007 +0100 + + Call StringValue on require's and load's argument. + +commit 601fd404ba04f383ee286be015edb7e8c58574d5 +Author: Evan Phoenix +Date: Fri Nov 30 14:27:44 2007 -0800 + + Refactor Kernel#require + + * Refactor a bunch of Kernel#require into Compile#require_feature + * VM.load_library now detects if the extension is already there + and doesn't readd it + * Added specs for #require + +commit 9b903cb7c5c6a3bfbaa3a7a91dc7bad830af7294 +Author: Adam Gardiner +Date: Fri Nov 30 10:56:02 2007 +1100 + + Compiler2 fix for anonymous masgn, e.g. * = 1,2 + +commit 08bc0a2f14494a30d5956d5bdcca9eb37c921780 +Author: Tilman Sauerbeck +Date: Fri Nov 30 00:16:07 2007 +0100 + + Made check_argcount work with methods that don't take any arguments. + +commit 96108240fead7d764f3ec37d5eb20294f3a9dd97 +Author: Tilman Sauerbeck +Date: Thu Nov 29 21:45:32 2007 +0100 + + Updated the CI excludes for Method#call. + +commit 61805ab7fac6ae9855baa05b42aebe66c3a2b3d3 +Author: Tilman Sauerbeck +Date: Thu Nov 29 21:44:49 2007 +0100 + + Made Method#[] an alias for Method#call. + +commit 219d34dedf6ff0ed083cb5f1e8b6a5c437ad366c +Author: Tilman Sauerbeck +Date: Thu Nov 29 21:22:05 2007 +0100 + + Enabled the Kernel#method_missing specs. + + They pass now that they specs aren't confused by the Dir spec helper + methods anymore. + +commit de5320efe8095e612e235bea7053084bb61d300d +Author: Tilman Sauerbeck +Date: Thu Nov 29 21:17:58 2007 +0100 + + Moved the Dir spec helper methods in their own module. + +commit 1ae47b5c091c209597bec7475935bbcff34b50b5 +Author: Brian Ford +Date: Wed Nov 28 23:45:09 2007 -0800 + + Applied patch from #151. + +commit adb5b139afa452869464fe53b710d7cb8b93131b +Author: Brian Ford +Date: Wed Nov 28 20:29:38 2007 -0800 + + Better fix for guards to distinguish ruby, ruby1.x from ruby1.9. + +commit e53f72172e395c7766dcecadd2ffd6c7caf303e7 +Author: Brian Ford +Date: Wed Nov 28 20:15:27 2007 -0800 + + Patches (or modified patches) from #157-162. + +commit b07eeee79ea5a0c0160c34aec2d690f1b46f7380 +Author: Tilman Sauerbeck +Date: Wed Nov 28 14:43:41 2007 +0100 + + Fixed Bignum#modulo and Bignum#remainder. + +commit 85b05b5103aaeb5d946e0f691f77af2dafa6f30a +Author: Tilman Sauerbeck +Date: Wed Nov 28 01:00:07 2007 +0100 + + Unified the File.unlink and File.delete specs. + +commit 2ec59a82f279a4ba6b5b781c90a7714aba767ed9 +Author: Tilman Sauerbeck +Date: Tue Nov 27 23:30:43 2007 +0100 + + Be more specific wrt the expected exception. + +commit 54236949e9b974d4c4dcf95b63318c844c62aca4 +Author: Victor Hugo Borja +Date: Tue Nov 27 19:58:50 2007 -0600 + + Module#<=> is working, Updated CI excludes for Module specs. + +commit 55c7529f4c8b02eff7e0b594f33b28750877fca2 +Author: Victor Hugo Borja +Date: Tue Nov 27 14:53:02 2007 -0600 + + Specs for Module#private/public/protected + +commit 5b693fae3464abb6a5aa05d8236bd8f4610c89d4 +Author: Tilman Sauerbeck +Date: Tue Nov 27 19:36:06 2007 +0100 + + We cannot use File.exists? to check whether a symlink exists. + + Use File.symlink? instead. + +commit e7eb6a8e1e1310c08220db0ed7979ec4c721fccb +Author: Tilman Sauerbeck +Date: Tue Nov 27 19:34:48 2007 +0100 + + Moved the after(:each) block before the specs, so the block is actually run. + +commit 4c284abb32029029ab7002147ef544493c7070f6 +Author: Tilman Sauerbeck +Date: Tue Nov 27 19:00:30 2007 +0100 + + Added a missing Errno.handle to File.readlink. + +commit f163ca7c5e4a03d698a881853d1e0fab8a5be1a4 +Author: Tilman Sauerbeck +Date: Tue Nov 27 18:42:08 2007 +0100 + + readlink() only works with symbolic links. + + This makes the spec pass on MRI. + +commit ca1cb21b5f694b3850a838f88d3ac5ded7de3e1f +Author: Tilman Sauerbeck +Date: Tue Nov 27 18:40:43 2007 +0100 + + Naming convention fixes. + +commit cbf351cb59152a5528f6c6105cee96c67f7f6fcd +Merge: f70d531... 5452983... +Author: Marcus Crafter +Date: Mon Nov 26 19:48:21 2007 +1100 + + Merge branch 'master' of git@git.rubini.us:code + +commit f70d5314fcc75ef2e32fbd484de58bd5f7ed6cbc +Author: Marcus Crafter +Date: Sat Nov 24 17:29:29 2007 +1100 + + Implemented File::symlink and spec. + + Kudos to the Melbourne Railscamp :) + +commit c4a6a804185c18a182206afc1b8d5209d208077e +Author: Marcus Crafter +Date: Sat Nov 24 01:08:41 2007 +1100 + + Removed trailing whitespace. + +commit 9b9820e512f56b2c23c760887251d72c187aa297 +Author: Marcus Crafter +Date: Sat Nov 24 00:50:11 2007 +1100 + + "Added File::readlink spec" + +commit 2dd272afe315dae0ad0b9bd49b6dfa9e98e50b1c +Author: Adam Gardiner +Date: Fri Nov 23 17:11:54 2007 +1100 + + Spec-ed implementation differences on masgn RHS eval order + + Rubinius is (for now) deliberately non-compliant wrt eval + order of RHS expressions in an masgn. + * MRI, JRuby eval left-to-right + * Rubinius evals right-to-left + +commit 361a1adcee182cf069352effd0949064b621bddc +Author: Adam Gardiner +Date: Fri Nov 23 16:16:08 2007 +1100 + + Additional parallel assignment specs - use of to_ary + + * Added spec for when to_ary should be called on the RHS of an masgn + * Added additional example of a complex masgn (from JRuby tests) + +commit 97cb3f5758f102cf8a07262c4c9bef4b22ca88b7 +Author: Victor Hugo Borja +Date: Thu Nov 22 22:04:51 2007 -0600 + + Added specs for metaclasses of true/false/nil on metaclass_spec as suggested by rue. + +commit d4f9eb7cd5fb17e3e8ce52db39e95a96362d3ad0 +Author: Brian Ford +Date: Thu Nov 22 13:05:10 2007 -0800 + + Fixed wording of Rubinius extension Bignum domain specs. + +commit 50e1f80ef54d25aaa69d52a3d422547593836ac6 +Author: Jeremy Durham +Date: Thu Nov 22 17:18:54 2007 -0500 + + Added excludes for Kernel#open and Thread#abort_on_exception + + * Excludes Kernel#open raise specs + * Excludes Thread#abort_on_exception specs + +commit 25607d4d884b4597bc69560e9390cd9dc1f4e44d +Author: Victor Hugo Borja +Date: Thu Nov 22 13:47:50 2007 -0600 + + Specs for Module#alias_method + +commit c207618ad4113501aa5df4adb5d5aa3a60f5b9ff +Author: Evan Phoenix +Date: Thu Nov 22 10:59:34 2007 -0800 + + Use RUBY_ENGINE first, then pull in rbconfig + +commit ed5a46e13b35d6ad48cce1d3eed96c2f78ace049 +Author: Jeremy Durham +Date: Thu Nov 22 11:42:36 2007 -0500 + + Added basic specs for abort_on_exception + + * Added specs for Thread#abort_on_exception ($DEBUG on and off) + * Added specs for Thread#abort_on_exception= + +commit 05ecef9162ba2c4a0da90c966a20a4f45c353d93 +Author: Jeremy Durham +Date: Thu Nov 22 08:39:53 2007 -0500 + + Added specs for when parameters are missing or invalid parameters are given + +commit 25d2c940d561dcac2c06df747762a229dddfbed1 +Author: Adam Gardiner +Date: Thu Nov 22 15:33:17 2007 +1100 + + Another parallel assignment spec - rhs should evaluate l->r + +commit 8e4f8de446b842c13ad45a8e0e2c3c1ebf30bddb +Author: Brian Ford +Date: Wed Nov 21 17:42:17 2007 -0800 + + Stop-gap prevention for Kernel#callcc hanging CI specs. + +commit a9d7163e4d9e8d4fb79c9769691b232676a44bd8 +Author: Charles Comstock +Date: Wed Nov 21 15:50:39 2007 -0600 + + remove compliant(MRI) from callcc specs + +commit 812e922c8e7cda728d6b7f32933b75eb009eef11 +Merge: 2e221b9... f24bb1f... +Author: Nathan Witmer +Date: Wed Nov 21 12:32:18 2007 -0700 + + Merge branch 'callcc_spec' + +commit f24bb1ffdf941df78098da262a62e881653b1a99 +Author: Nathan Witmer +Date: Wed Nov 21 12:31:28 2007 -0700 + + Added scope-related callcc specs, compliant(:ruby) only. + +commit 2e221b9f1d7ffa41431e5bd51fdd36434e7f838f +Author: Arthur Schreiber +Date: Wed Nov 21 14:41:43 2007 +0100 + + Spec and fix some more String#slice bugs when given nil, also use Undefined. + +commit db338d9d8705fd668a5639d483ff47908aa014ca +Author: Arthur Schreiber +Date: Tue Nov 6 16:53:13 2007 +0100 + + Fix String#rindex when given nil as offset. + +commit 6eab3b692a50c1a37cc39c21d743de1488402f64 +Author: Brian Ford +Date: Wed Nov 21 00:00:24 2007 -0800 + + Added MRI stdlib Fcntl to lib/ext with build script. + + Added lib/fcntl to load extension. This may need a better solution. + Added INT2FIX to subtend. + +commit 5268c0b29c1fb07a911fe601e30b21ffe04f7e81 +Author: Adam Gardiner +Date: Wed Nov 21 17:02:14 2007 +1100 + + Additional specs for parallel assignment + + MRI allows parallel assignment to: + - assign via object.method= + - assign via []= + - use a lhs arg as an arg to another lhs assignment + + All three scenarios currently fail in Rubinius, apparently due to + miscalculating the number of args to an assignment method under + parallel assignment. + +commit 462f68b95a70c24e41cad5a40969c4651c7de181 +Author: Jeremy Durham +Date: Tue Nov 20 02:12:44 2007 -0500 + + Added specs for Kernel#open when block is given + +commit ab9e40600fd2522d4abce86f7b8bdc632f6e9018 +Author: Jeremy Durham +Date: Mon Nov 19 23:42:55 2007 -0500 + + Added very basic specs for Kernel#open + +commit b47efc9f9872ecca68a06f6864f39617e06762b0 +Author: Brian Ford +Date: Mon Nov 19 00:24:15 2007 -0800 + + Updated CI excludes. Runs clean on Leopard. + +commit 3ff04e52bc9cb03439567ddb9b3b63b3034b30c3 +Author: Charles Comstock +Date: Mon Nov 19 00:03:58 2007 -0600 + + more specs for Kernel.callcc, ensures callcc return value semantics + +commit 06d5312c51b09faef87d2deb53f3c472eaa94100 +Author: Charles Comstock +Date: Sun Nov 18 17:49:44 2007 -0600 + + basic callcc behavioral specs + +commit 53433f0e9ddba2eac876f7a1fb0f9d292ee37286 +Author: Nathan Witmer +Date: Fri Nov 16 16:51:18 2007 -0700 + + Added Kernel#callcc spec and fix for LocalJumpError with no block given + +commit d324779b8b5c8dd84438c08ec4f2b2574282f93e +Author: Victor Hugo Borja +Date: Fri Nov 16 15:17:21 2007 -0600 + + Added Module.nesting + + Some specs are failing on rubinius because the parent + field is not being properly set. + +commit cff726c9dc3631b2e0ddc3e12bd3af532f7e1ef4 +Author: Victor Hugo Borja +Date: Fri Nov 16 14:49:38 2007 -0600 + + Added spec for calling Module.nesting on root level + +commit 05adb6070889d7021a1e53ab82b855c3554d4f5c +Author: Victor Hugo Borja +Date: Fri Nov 16 14:45:23 2007 -0600 + + Fixed specs for Module#constants + +commit 1f1c857e1d8c37213a91daaad3fc3bfcbf2bef61 +Author: Victor Hugo Borja +Date: Fri Nov 16 14:43:55 2007 -0600 + + Fixed spec description for calling Module.nesting from methods. + +commit 242c947c6e4d007685e8aa0c44ac505c7dab4239 +Author: Victor Hugo Borja +Date: Fri Nov 16 14:38:32 2007 -0600 + + More specs for Module.nesting + +commit 3560fd7ef0d5a65a9cb055d87fa7103ed3bdb029 +Author: Victor Hugo Borja +Date: Fri Nov 16 10:24:45 2007 -0600 + + private keyword specs reflecting problem described on ticket #133 + +commit fd31e1e592237832bd5e605f604d15385df0615a +Author: Victor Hugo Borja +Date: Fri Nov 16 04:15:27 2007 -0600 + + specs for Kernel#block_given? by Francisco Laguna + +commit 87ebce4cf2430198578decdb4c7dc1003db37f8e +Author: Evan Phoenix +Date: Thu Nov 15 22:07:39 2007 -0800 + + Ticket #121 by Jeremy Durham -- File modes + +commit 4a67e0ade233aaaa3a2ff17161b298872f8a5f83 +Author: Victor Hugo Borja +Date: Thu Nov 15 14:20:19 2007 -0600 + + Splitted enumerable_spec.rb into a file per method. + + Added some specs by Francisco Laguna. + +commit 0b933650330f57e7db1bf8574d0b7eecf0635996 +Author: Bryan Helmkamp +Date: Tue Nov 13 11:34:20 2007 -0500 + + Added specs for File.mtime. + +commit 42a7de27c1a6082fee7b9baaf05b9394ffe90ddd +Author: Tilman Sauerbeck +Date: Mon Nov 12 19:57:38 2007 +0100 + + Updated CI excludes for File#atime and File.new. + +commit 3d106d6a9b8ce0b34e7b6f9426da51b83fe5f676 +Author: Tilman Sauerbeck +Date: Mon Nov 12 19:54:43 2007 +0100 + + Added File#path. + +commit 247da25a0120d468fe9f189a6235962f9658b65e +Author: Tilman Sauerbeck +Date: Mon Nov 12 19:23:09 2007 +0100 + + File now deals with numeric modes and accepts a permission argument, too. + +commit 087deaed0dcf4ae2c8dc713eeccfed9a0ebabe6f +Merge: 4355e96... 1c9d213... +Author: Kevin Clark +Date: Sun Nov 11 23:15:01 2007 -0800 + + Merge branch 'master' of git@git.rubini.us:code + +commit 4355e96b05de4d4d086dfa86b8fe19bdcecfbe82 +Author: Kevin Clark +Date: Sun Nov 11 23:14:16 2007 -0800 + + -a + +commit 7e975d1aca38a3bfe07fda431aeaba376bce19c1 +Author: Kevin Clark +Date: Sun Nov 11 23:09:47 2007 -0800 + + Get rid of irrelevant specs + +commit 1c9d2133fc294964ce08e9a7020083c379f74ca0 +Author: David Waite +Date: Sun Nov 11 23:46:08 2007 -0700 + + Remove temp directories within mkdir spec on exception. + +commit 2110fc75dc6a7ab521249f259bc6fdc78d565b11 +Author: Kevin Clark +Date: Sun Nov 11 22:36:08 2007 -0800 + + Update CI Excludes + + * Expected failure of "raise an Exception if it has + the wrong number of argments" due to dispatcher bug + +commit 0f2a183a46ba085d9c99ed4767cd18c0482e6d45 +Author: Kevin Clark +Date: Sun Nov 11 22:14:07 2007 -0800 + + Implement File#atime + +commit 671b340f340ab6b8d9c13b27d52a782ce3268b2a +Author: Kevin Clark +Date: Sun Nov 11 21:24:34 2007 -0800 + + Update CI excludes + +commit f7ba96f6b41de9a3696a03e9efe25c8b037a4f07 +Author: Kevin Clark +Date: Sat Nov 10 11:24:12 2007 -0800 + + Adds spec for File.open + + * In resonse to Lighthouse Ticket #102 + "File.open should throw Errno::EACCES opening non-permitted file" + * Passes MRI, doesn't yet pass RBX + +commit 719329b3f5179766e23a27e427cd0c0846c85ffa +Author: Marcus Crafter +Date: Mon Nov 12 10:38:42 2007 +1100 + + Added IO#to_i implementation and spec. + +commit cc100fc08be101ecdf0daba1966977fb8e39fa6e +Author: Marcus Crafter +Date: Mon Nov 12 10:30:25 2007 +1100 + + Added IO#fileno implementation and spec. + +commit d036f5c16a4836d638be83108be35df532d9221a +Author: Tilman Sauerbeck +Date: Sun Nov 11 23:17:12 2007 +0100 + + Made SystemStackError subclass of StandardError. + +commit bf1c3dc3e463aeaf4e0cee1cbd46b15e7693a395 +Author: Tilman Sauerbeck +Date: Sun Nov 11 20:36:25 2007 +0100 + + Removed an old Method#arity spec exclude. + +commit 72c3495f3513e54c2488292bcdaca9208b6f0339 +Author: Brian Ford +Date: Sun Nov 11 10:44:53 2007 -0800 + + Removed transient dirs from Dir specs. + +commit 11f0ed51b4bfb3bea2b544a82b3158fd3daf2ad8 +Author: Victor Hugo Borja +Date: Fri Nov 2 03:02:28 2007 -0600 + + Specs for Module#remove_const + + Signed-off-by: Brian Ford + +commit e8158f14f0f02e3b0cdcb4182e1277928324cc0c +Author: Nathan Witmer +Date: Sun Nov 11 08:19:10 2007 -0700 + + Fixes for UDPSocket spec + + * Renamed the description to match what was actually being tested + * Uncommented the code and wrapped it in an "it" block, to prevent + conflicts/hangs with bin/completeness runs. + + Signed-off-by: Brian Ford + +commit 9b973a98d2ebddacd50f0fcb58903bb53bdff3f5 +Author: Tilman Sauerbeck +Date: Sun Nov 11 13:30:22 2007 +0100 + + Ticket #98: Dir includes Enumerable now. + +commit 8d957f186cd4d2c5e6b236de4a0878d38b464848 +Author: Tilman Sauerbeck +Date: Sun Nov 11 13:26:00 2007 +0100 + + Implemented Module#included_modules. + +commit 0021b24ba490fe01f96ef17957328feeedfc4c29 +Author: Nathan Witmer +Date: Thu Nov 8 21:42:41 2007 -0700 + + Commented out code in UDPSocket spec so bin/completeness doesn't hang + + Signed-off-by: Brian Ford + +commit 56687aed201fb864587807cca893268a9f1e2050 +Author: Brian Ford +Date: Sun Nov 11 00:10:14 2007 -0800 + + Method fixture for yield specs. + +commit 421aa58f9135807487864adcdcac79f7b6da33c1 +Author: Brian Ford +Date: Sun Nov 11 00:08:35 2007 -0800 + + Specs for yield keyword based on patch in #114. + +commit 5ba0b2030c55474f9d8a096d309678ca24a4699b +Author: Jeremy Durham +Date: Sun Nov 11 01:35:08 2007 +0100 + + Ticket #105: Implemented Bignum#eql?. + +commit 47356fe39033f8571559a4fef933681fda871efd +Author: Tilman Sauerbeck +Date: Sun Nov 11 01:14:26 2007 +0100 + + Made specs for Kernel#respond_to? and Kernel#method pass. + + KernelSpecs::Foo#baz is defined in another spec, so these two specs + may not rely on #baz being undefined. This is a horrible workaround + for the problem that multiple specs make use of the same module and modify + it freely. + +commit 68b4fc7c0192f537fe9727927ac35c440dbdc03a +Author: Akshay Rawat +Date: Thu Nov 8 18:21:03 2007 +0530 + + Updated CI excludes. + +commit a1eee3814a5d054cd00e26b40c063d41880bf6c7 +Author: Chris Pettitt +Date: Sat Nov 10 14:10:47 2007 -0800 + + Refactor IO.gets spec to have less duplication. + +commit 56497d27bdb3a82d549f89b9fc9fcf0709f99b3e +Author: Chris Pettitt +Date: Sat Nov 10 14:07:21 2007 -0800 + + New spec: IO.gets('') should advance the file position to the next non $/ character. + +commit 95158f5a4141d5d3e2893304e49bfeb62cc7b226 +Author: Brian Ford +Date: Sat Nov 10 13:35:03 2007 -0800 + + Added rescue to prevent meltdown until rbx begin/rescue/ensure is fixed. + +commit a39155cb029ca3c1e5e5d69e0e269c685c040f6e +Author: Chen Yufei +Date: Sat Nov 10 12:24:38 2007 +0000 + + Fixed IO#gets when separator is empty. + +commit f9c31ce1d2a68c15def98aad6c6ff35eb56cd523 +Author: Kevin Clark +Date: Fri Nov 9 20:24:40 2007 -0800 + + Clean up Enumerable#include? specs + +commit f017fad69be5d4034a4c5437acf77ec4749b0d75 +Author: Kevin Clark +Date: Sun Nov 4 20:54:06 2007 -0500 + + Clean up Enumerable#(collect, entries, find, find_all) specs + +commit 81550f082396b4455c3681ae966be1371be0a5db +Author: Kevin Clark +Date: Sun Nov 4 13:18:52 2007 -0500 + + Update excludes + +commit d5fd2ee893ea608c7e19cb674a4da7b9f49542e6 +Author: Kevin Clark +Date: Sun Nov 4 12:16:58 2007 -0500 + + Cleanup/rewrite Enumerable#find tests for sanity and clarity + +commit 39f21aa76f6ddc45be79e4e4e978b4c1c2beed71 +Merge: 17d2e4c... c1b9f74... +Author: Kevin Clark +Date: Fri Nov 9 20:25:13 2007 -0800 + + Merge branch 'master' of http://git.rubini.us/code + +commit c1b9f74f88be963e72de763da9130f46869d89fb +Author: Chris Pettitt +Date: Fri Nov 9 12:36:04 2007 -0800 + + Fix some failing specs for IO#each and IO#each_line. + + Also refactor some common code into a helper method. + +commit 74af37b849507e504503359a08245effaad7634a +Author: Chris Pettitt +Date: Fri Nov 9 10:58:33 2007 -0800 + + New specs for IO#each and IO#each_line + + This change adds some new specs for IO#each and IO#each_line factored into + a shared .rb, because one is the alias of the other. Added failing specs to + excludes. + +commit d162a396b566846445328d6c42d3d5f10fcf7ee6 +Author: Matt Pelletier +Date: Fri Nov 9 02:32:25 2007 -0500 + + Add and refactor patches from Andrea OK regarding #send + +commit 63f0ed010e65549597f6bddb0686ba04157ca478 +Author: Brian Ford +Date: Wed Nov 7 09:52:25 2007 -0800 + + Fixed Method#call spec failing from renamed fixture method name. + +commit 1ef46468d7808c52b07388130069cb2e7854bff8 +Author: Matt Pelletier +Date: Wed Nov 7 04:48:39 2007 -0500 + + * Update CompiledMethod#arity to be accurate for cases of required and/or optional arguments, with or without blocks + * CompiledMethod#arity is still inaccurate when splat argument is present (the presence of splat overrides #required) + * Add specs for more thorough coverage of various argument use cases + * Includes known Rubinius-failing specs for splat-related arity + +commit b7726f26dae95936aa1c3fdf2c52dd18ef7413cf +Author: Brian Ford +Date: Mon Nov 5 17:48:51 2007 -0800 + + Updated CI excludes for fixes to public|private_class_method. + +commit eb18f898e3ae8e5a1bf3b01291a12516c6a22301 +Author: Brian Ford +Date: Mon Nov 5 17:18:22 2007 -0800 + + Added Module#protected_method_defined?. Updated CI excludes. + +commit aa8904cdbd8b4851be4f05cec3000b04cfc9f6c1 +Author: Brian Ford +Date: Mon Nov 5 17:14:12 2007 -0800 + + Added Module#private_method_defined?. + + Fixed specs for Module#public_method_defined? and + private_method_defined?. Updated CI excludes. + +commit 063b61759ee86f5def2422d16f1eb854c8b9eb76 +Author: Brian Ford +Date: Mon Nov 5 13:39:21 2007 -0800 + + Updated CI excludes for StringIO. + +commit 3c79f871379d2d4b5431138033f723efbf4a795d +Author: Dr Nic +Date: Sun Nov 4 16:45:30 2007 -0500 + + Extended StringIO spec "flattens a nested array before writing it" to ensure deeper test scenario + + Signed-off-by: Brian Ford + +commit 916d617a60cf83ac26c3090310236193f5842ff6 +Author: Brian Ford +Date: Mon Nov 5 13:11:54 2007 -0800 + + Updated CI excludes for String#scan. + +commit fee1d904197c369c561ae3b11aaf582f1b87d1b0 +Author: Matt Pelletier +Date: Sat Nov 3 16:06:16 2007 -0400 + + Fix test of String#scan. Do not force matches into array using splat. + + Signed-off-by: Brian Ford + +commit cc9182cfcde60a63bf566f73c6004b7e46347e77 +Author: Daniel Lucraft +Date: Thu Nov 1 17:01:03 2007 +0000 + + Added File.rename + + Signed-off-by: Brian Ford + +commit fc2b7aa65ab338d8ff543552659046c93659c3ce +Author: Brian Ford +Date: Mon Nov 5 12:06:13 2007 -0800 + + Commit rework of Carl Drinkwater's patch from #72. + +commit 7f6564b96762d7b3deb9f021789182f5c664a766 +Author: Chris Pettitt +Date: Sun Nov 4 10:38:04 2007 -0800 + + Fixes for two IO#gets spec failures. + + This patch fixes the following two IO#gets spec failures: + + IO#gets assigns the returned line to $_ + IO#gets returns the entire content if the separator is nil + + Signed-off-by: Brian Ford + +commit 705e8e05496167b1af3a1e3ff3446d325ca54e07 +Author: Brian Ford +Date: Mon Nov 5 02:10:56 2007 -0800 + + Added Module#public_method_defined?. + + Updated CI specs for #public_method_defined?. + Small fix to find_method_in_hierarchy to symbolize arg. + Updated some spec wording and removed spec'ing exception string. + +commit 17d2e4c6ae0c40376fe121786a362c8bc8ce951c +Merge: 2b77ee8... c07472c... +Author: Kevin Clark +Date: Sun Nov 4 20:58:42 2007 -0500 + + Merge branch 'master' of http://git.rubini.us/code + +commit bd6c27f4724bdc461a7036e6373a0ad23060020a +Author: Trotter Cashion +Date: Sat Nov 3 15:57:21 2007 -0400 + + Added operator precedence specs for '&&' and 'and'. + +commit 2b77ee8b74373a3251973d96c931422909605e29 +Merge: 30d7618... 76aa72e... +Author: Kevin Clark +Date: Sun Nov 4 13:43:23 2007 -0500 + + Merge branch 'master' of http://git.rubini.us/code + +commit 2c90ce28cc73e08d9fb74b5c7e815807314ba269 +Author: Brian Ford +Date: Sun Nov 4 07:18:09 2007 -0800 + + Updated CI excludes from 85d63b676e. + +commit 85d63b676e463a2bec9a322bc8eeffd2daee433b +Author: Chen Yufei +Date: Sat Nov 3 23:39:23 2007 +0000 + + Added specs for IO#gets + +commit be5b9595f2077080c0c1179ab9689352d8faea3a +Author: Brian Ford +Date: Sun Nov 4 00:31:12 2007 -0700 + + Updated CI excludes. + +commit d46ad4b63d4a5f77609b0880b7f24e8e27404805 +Author: Brian Ford +Date: Sun Nov 4 00:11:52 2007 -0700 + + Updated CI excludes. + +commit 1f307223c673c6744f8b85fc3e707a3419b1a0e8 +Author: Brian Ford +Date: Sun Nov 4 00:06:48 2007 -0700 + + Guarded #freeze specs for MRI and JRuby. + +commit ca50fd7d979c36f8af306e0e1474aac5408dd66d +Author: Brian Ford +Date: Sat Nov 3 23:22:31 2007 -0700 + + Guarded specs for #frozen? for MRI and JRuby. + +commit 16b36030a796b877809d5d6ea556266c4b4a6413 +Author: Brian Ford +Date: Sat Nov 3 20:53:26 2007 -0700 + + Removed NULL characters from language/precedence. + + Enhanced rescue output when loading spec files. + +commit 10510ece16ebb5e0ba921e0be631a4740f3e4453 +Author: Evan Phoenix +Date: Sat Nov 3 23:09:30 2007 -0400 + + Fix a method_missing cache error. + +commit b313c5632b039c03a448ae3b1046701c8b3243a2 +Author: Brian Ford +Date: Sat Nov 3 14:10:29 2007 -0700 + + Changed shared spec behavior to be compatible with RSpec. + +commit 7b825b89e96b3c8e38f9b8bcc8edf2bc6ec6ff22 +Author: Brian Ford +Date: Sat Nov 3 14:09:47 2007 -0700 + + Fixed language/class specs. + +commit 30d76181b0b3a9c5ac99c9d0e22a6a451346eff4 +Author: Kevin Clark +Date: Sat Nov 3 16:47:49 2007 -0400 + + Fix Dir#each/Dir#entries/Dir.foreach specs. They weren't updated for fixtures + +commit f44a8cceb9a186a7127276db2207dfc79957ee8d +Author: Brian Ford +Date: Sat Nov 3 13:06:35 2007 -0700 + + Guard File.(un)link for jruby. + +commit 1ec2c3a99ca562c8944aac1f4a60f8e0af0aaf17 +Author: Tilman Sauerbeck +Date: Sat Nov 3 18:20:48 2007 +0100 + + Properly resize the array in Array#<<. + +commit 8dec9918d8a6233ec2cde29d54687a5d950dc8df +Author: Tilman Sauerbeck +Date: Sat Nov 3 17:13:57 2007 +0100 + + Fixed Array#unshift for the case when @start > 0 && @start < values.size. + + Also extended the Array#unshift specs to cover this case. + +commit 50b90918cd5a9a05e475690703c7867b443d191b +Author: Charles Comstock +Date: Fri Nov 2 13:47:53 2007 -0500 + + added IO::foreach, fixed gets to use string separator, and fixed IO::readlines to use File.open + +commit 3efc01e110473d003ffb0a1376ec179f30e600de +Author: Charles Comstock +Date: Fri Nov 2 13:30:44 2007 -0500 + + specs for IO::foreach and specs to test IO::readlines,IO.readlines with string separator + +commit fad18610b4416dfcfaf35db5029e880dff7e9820 +Author: Charles Comstock +Date: Mon Oct 29 14:10:11 2007 -0500 + + basic exec implementation and a single basic spec (not sure how test test exec) + +commit 730fc3ed9afc54612d14093148fb8583c9e39fe3 +Author: Tilman Sauerbeck +Date: Fri Nov 2 21:41:25 2007 +0100 + + Return mkdir()'s return value from Dir.mkdir. + +commit f5766696e701a069f908b3b5d5cfbccfee15ef1f +Author: Tilman Sauerbeck +Date: Fri Nov 2 20:46:11 2007 +0100 + + Implemented Dir.foreach. + +commit c696f1edc50c58b87270811c0c9aa0e49b356fe7 +Author: Tilman Sauerbeck +Date: Fri Nov 2 20:38:36 2007 +0100 + + Implemented Dir.entries. + +commit e8e6188b252172690c1b584e528e3c71035897cd +Author: Tilman Sauerbeck +Date: Fri Nov 2 18:16:13 2007 +0100 + + Raise an error if the opendir() call in Dir#initialize fails. + +commit 024309b560c6c69f6f331c614df1da221be7054c +Author: Tilman Sauerbeck +Date: Fri Nov 2 16:15:19 2007 +0100 + + The Array#[]= spec seems to work now, so enable it. + +commit 76f118e62a0784326f5edf1c0fe46f6b6e682eee +Author: Tilman Sauerbeck +Date: Fri Nov 2 14:28:49 2007 +0100 + + Made Math.ldexp only accept integers as the second argument. + +commit 22bd7369efd1f738835e9c0a6a4624a26dae02d1 +Author: Tilman Sauerbeck +Date: Fri Nov 2 13:30:50 2007 +0100 + + Implemented some missing File::Stat methods. + +commit d6dc42d9085fed5f8bf482d7f84dd9c5fbd4423c +Author: Brian Ford +Date: Thu Nov 1 12:24:53 2007 -0700 + + Fixed specs failing MRI for File.stat and File.basename. + +commit 46f4de189e987f3071ede57f2bb1f7c892d67bd4 +Author: Tilman Sauerbeck +Date: Thu Nov 1 17:30:47 2007 +0100 + + Fixed ticket #83: Array#push doesn't die anymore after calling Array#shift. + +commit 8debed24e957e48b10d60885d9a43083aab4d923 +Author: Tilman Sauerbeck +Date: Thu Nov 1 11:14:22 2007 +0100 + + In the Numeric#coerce spec, coercing strings to numerics should work. + + We can remove the TypeError checks from this spec, since those + are included in the specs for the Numeric operators. + +commit 28cf656fb25ce38453acb2efdcf2e9ac16bb4460 +Author: Brian Ford +Date: Thu Nov 1 01:33:31 2007 -0700 + + Removed Hash#fetch definition from fetch spec. + + Fixed spelling of Hash#find_unambiguous. + Updated CI excludes. + +commit a6a69b469d94d0912ccbf123fdb9f53cbaf32830 +Author: Akshay Rawat +Date: Tue Oct 30 21:47:51 2007 +0530 + + In the Time#isdst spec, don't depend on the system's current time zone. + +commit 1be129f98e0d548a023cc32f5ab763361e2a9c6b +Author: Daniel Lucraft +Date: Wed Oct 31 21:05:04 2007 +0000 + + Fixed String#split to not return non-matching captures anymore. + +commit 5f7f798ef26fc8ee1e83c5e392c1fb2e60e31382 +Author: Tilman Sauerbeck +Date: Wed Oct 31 21:54:56 2007 +0100 + + In the Numeric#coerce spec, don't try to coerce strings to numerics. + +commit e26b7645af27d5bfc250c2c11f7e72349750f5c7 +Author: Tilman Sauerbeck +Date: Wed Oct 31 10:52:13 2007 +0100 + + Added a failing spec for Ticket #83. + +commit 9144f0c55eb5f40409bec514f08f89bdba61f800 +Author: Daniel Lucraft +Date: Wed Oct 31 13:57:54 2007 +0100 + + Fixed math/constants_spec.rb. + +commit 4b521cacb667ca5245954bc03ebfec67c0ac235c +Author: Adam Gardiner +Date: Wed Oct 31 23:26:45 2007 +1100 + + Modified spec to reflect expected differences in masgn retval behavior + between rbx (true) and MRI (array of rhs vals). + +commit 1f6c50f5c77566e66cb0b842733b7f4f4b24e937 +Author: Tilman Sauerbeck +Date: Tue Oct 30 18:13:47 2007 +0100 + + Use a fixed timezone for the Time#strftime spec. + +commit 60a25e997def085f3ae29773ce70ddc5b7c38d46 +Author: Brian Ford +Date: Tue Oct 30 00:47:38 2007 -0700 + + Fixed Kernel#raise to not output if $VERBOSE == nil. + + Guard Marshal.dump specs to eliminate error output until + a proper Marshal is implemented. + +commit e446f2e329a6dfaacb45b5b86ba43ebd9ec606a3 +Author: Brian Ford +Date: Mon Oct 29 23:17:49 2007 -0700 + + Added IO::SEEK_SET, SEEK_CUR, SEEK_END with FFI. + + Fixed IO#close to raise IOError if already closed. + +commit fcb1ac4d076c07065878c2e65bf7bb44ddef400c +Author: Brian Ford +Date: Mon Oct 29 22:37:23 2007 -0700 + + Specs for IO#seek and IO::SEEK_SET, SEEK_CUR, SEEK_END. + +commit a3570f6702dabd303fcd10d4cfc0e753cff69bb5 +Author: Arthur Schreiber +Date: Mon Oct 29 22:33:34 2007 +0100 + + Make Module.new actually work. + +commit 9709fa96b91afe5140f76726f1f7d4b89f8a6d54 +Author: Brian Donovan +Date: Mon Oct 29 11:30:32 2007 -0700 + + Ticket #75: Fixed Enumerable#sort_by. + + We must not call the comparison proc when the object and pivot are + identical. + +commit 52e97da6bbcd28ec4349abcf25b089648d085652 +Author: Akshay Rawat +Date: Tue Oct 30 02:18:56 2007 +0530 + + Enabled Math specs that were fixed by Ticket #59. + +commit 0f98800d4ab1db526304f1d26597ca3880c811da +Author: Tilman Sauerbeck +Date: Mon Oct 29 19:29:16 2007 +0100 + + Ticket #59: Kernel#send now calls private methods, too. + +commit 754e48c223c3464c7d048452585c07b8d0b3d8c7 +Author: Brian Ford +Date: Mon Oct 29 00:11:41 2007 -0700 + + Specs for IO#rewind. + +commit 0bfd6bcca8fb287899fadeae81dd7c00b05d07e9 +Author: Tilman Sauerbeck +Date: Sun Oct 28 23:40:37 2007 +0100 + + Module#public_instance_methods now handles attribute accessor methods. + +commit 0a22b36b9bc50a34f1da1d0e994f1a6689195652 +Author: Tilman Sauerbeck +Date: Sun Oct 28 23:26:17 2007 +0100 + + Added a failing spec for Enumerable#sort_by. + +commit 006534b173a186c25c228b653d5ac9d81b20f57e +Author: Arthur Schreiber +Date: Sun Oct 28 21:51:40 2007 +0100 + + Update Module spec excludes. + +commit 496df827eeeb600858fa8c7b26482aa3f653fee1 +Author: Arthur Schreiber +Date: Sun Oct 28 21:50:47 2007 +0100 + + Make Module#ancestors specs pass. + +commit 18185cde2b47374c304e2528a084ea1f7b5178d2 +Author: Tilman Sauerbeck +Date: Sun Oct 28 19:46:22 2007 +0100 + + Added a failing spec for building an array that includes a splatted array. + +commit f120a470a5e07a7e53b1e006173942d58956e86b +Author: Arthur Schreiber +Date: Sun Oct 28 19:33:10 2007 +0100 + + Extended Kernel#` specs. + +commit 7e184c5bbc7be16cc8f7be01713543f222edd267 +Author: Arthur Schreiber +Date: Sun Oct 28 19:00:52 2007 +0100 + + Removed Exception message dependencies and extended Kernel.String specs. + +commit 2853dc58209b3b8d122cee66c7d83e967d0879de +Author: Adam Gardiner +Date: Sun Oct 28 23:01:54 2007 +1100 + + Fix for multi-arg operator assign through [], e.g. x[0,3] += 5 + +commit 91b88710ddc1e8553e51c404cee4039f4d6abf24 +Author: Arthur Schreiber +Date: Sun Oct 28 13:05:08 2007 +0100 + + Fixed File.ftype specs. + +commit 448fdc2def3a9ab249dadf9335568ca30b76f70e +Author: Brian Ford +Date: Sun Oct 28 00:36:11 2007 -0700 + + Updated compiler specs. + +commit 0fb510c14ee8787b5965d5665e50da92a988faa6 +Author: Brian Ford +Date: Sun Oct 28 00:32:41 2007 -0700 + + Fixed Bignum#coerce specs. + +commit 5e52a259b91e81fe5497f44107dee6ffd613b3be +Author: Brian Ford +Date: Sun Oct 28 00:22:40 2007 -0700 + + Fixed wording on Rubinius Bignum#coerce extension specs. + +commit 19e0259ca51691afe341b1217ab92862b307fe17 +Author: Brian Ford +Date: Sun Oct 28 00:15:18 2007 -0700 + + Revert "Remove invalid and failing Bignum#coerce spec." + + This reverts commit 2371b920ca3f956213ab9e406a3b5d2afab4f18e. + +commit 4986ec283ee5aa9e392065c74d64952d36554b91 +Author: Brian Ford +Date: Sat Oct 27 23:50:05 2007 -0700 + + Updated CI excludes for Array#pack. + +commit 5472f30201d7cddd4465adb246fa32927fe03d91 +Author: Alan Hurdle +Date: Sat Oct 27 18:38:30 2007 +1000 + + Bunch of fixes to Array#pack to pass the current set of pack specs + +commit 20210a617a3f31c5dc0eda9fa371c49200c11f67 +Author: Arthur Schreiber +Date: Sun Oct 28 02:30:02 2007 +0100 + + Updated spec excludes for Module specs. + +commit 712e3cc6a5d8a69834449e1039dfae3e07fcdcc2 +Author: Arthur Schreiber +Date: Sun Oct 28 02:18:00 2007 +0100 + + Removed the dependency on some Exception messages. + +commit 2371b920ca3f956213ab9e406a3b5d2afab4f18e +Author: Arthur Schreiber +Date: Sat Oct 27 16:15:33 2007 +0200 + + Remove invalid and failing Bignum#coerce spec. + +commit e1e62e7749d47c838d6b7cd1e95863c0c90d3de0 +Author: Tilman Sauerbeck +Date: Sat Oct 27 20:59:49 2007 +0200 + + Fixnum#div now always rounds towards negative infinity. + +commit 9a4ccbe8381db5b6280c9d1dfcf6fa21a4838c4e +Author: Arthur Schreiber +Date: Sat Oct 27 15:09:57 2007 +0200 + + Updated Spec excludes for Bignum and Fixnum specs. + +commit 889c939a668b9b1a4fd8f5a0cfd8bad85c3a5977 +Author: Arthur Schreiber +Date: Sat Oct 27 14:24:27 2007 +0200 + + Fix Integer#[] when given a Bignum. + +commit 45d97332f4ec5a174884024c954ceeb6eb852f5f +Author: Arthur Schreiber +Date: Sat Oct 27 13:46:51 2007 +0200 + + Partial fix for #68: Fix Hash#fetch to correctly handle yielding with a default value passed. + +commit a785ea28f39c71a98007a7fafc23985dd21b596f +Author: Tilman Sauerbeck +Date: Sat Oct 27 13:39:42 2007 +0200 + + Updated CI excludes for recent Hash fixes. + +commit 6dd909fede466ed813ec7c5d207c5deeb69c9eb7 +Author: Arthur Schreiber +Date: Sat Oct 27 11:33:22 2007 +0200 + + Fix for #67: Enumerable#sort should not depend on #size. + +commit 720489aa52bfabd492c307330204772b5eba6755 +Author: Brian Ford +Date: Fri Oct 26 00:21:37 2007 -0700 + + Updated CI excludes for spec/language. + +commit 41e8a07252b2df9c1e858922195a72f9a40c882c +Author: Akshay Rawat +Date: Fri Oct 26 02:37:18 2007 +0530 + + private keyword should mark a Module method private + +commit a201e631cbaabcc5964cfa3eb28a9fa8be1bf347 +Author: Brian Ford +Date: Fri Oct 26 00:11:01 2007 -0700 + + Added spec/language into CI specs. + + Updated CI excludes for spec/language. + +commit 833fe76de0c0b900ef5255b2abecd19943404c0c +Author: Brian Ford +Date: Thu Oct 25 23:53:35 2007 -0700 + + Updated compiler specs and CI excludes. + +commit f31e5af358d2b5c3ff4afd3819b3a3e571427f8e +Author: Brian Ford +Date: Thu Oct 25 23:28:13 2007 -0700 + + Migrated Adam Gardiner's compiler patch 236d213de8 to stable. + + Updated CI excludes for language/variables specs. + +commit bd9e47b1b7624df5e2ae0f31a7fd53c787ecc7e4 +Author: Brian Ford +Date: Thu Oct 25 22:50:22 2007 -0700 + + Fixed language/variables spec to use fixture class. + + Added CI exclude file for language/variables specs. + +commit 688f03ac452f698105812c28c29dcc7162b7037c +Author: Adam Gardiner +Date: Thu Oct 25 22:43:20 2007 +1000 + + Added specs for operator assignment, i.e. +=, *=, ||= etc + +commit 982dfee01bedb55e8dbf62d279bc4a375e58ec50 +Author: Tilman Sauerbeck +Date: Thu Oct 25 16:53:45 2007 +0200 + + Don't hardcode the result of Hash#to_a in the Hash#shift spec. + +commit 4bf7c8d2d387c002004da5df9f9c2a06fb65e61e +Author: Brian Ford +Date: Thu Oct 25 01:42:32 2007 -0700 + + Commit tilman's language block spec additions with some modifications. + +commit ff8f6f5b5f5285b0fcf361d84523bf21320074b2 +Author: Tilman Sauerbeck +Date: Wed Oct 24 20:20:12 2007 +0200 + + Extended the Hash#[] spec with a test case for ticket #65. + +commit dc61b1e771c70e54f98859da3dd31a4ea61384e1 +Author: Brian Ford +Date: Wed Oct 24 07:11:07 2007 -0700 + + Specs after(:each) MUST come before it blocks. + + Updated CI excludes for IO.read specs. + +commit 06539bad037e0ef7368ea5cbb5780fce7bbea443 +Author: Marcus Crafter +Date: Wed Oct 24 21:26:49 2007 +1000 + + Initial IO::read specs. + +commit a2f26d7a7b1997510edff1792eaec6507ba38208 +Author: Tilman Sauerbeck +Date: Tue Oct 23 19:30:46 2007 +0200 + + Implemented Numeric#remainder. + +commit 2bc5fcee6be4db3e0e0c46aa7c1b8ef5a5c57957 +Author: Tilman Sauerbeck +Date: Tue Oct 23 19:10:54 2007 +0200 + + Fixed Bignum#modulo(0.0). + +commit 68965dc12ea369f6db64c208cb2ce123c1398bb8 +Author: Tilman Sauerbeck +Date: Tue Oct 23 10:20:04 2007 +0200 + + Extended the Numeric#nonzero? spec a bit. + +commit 290ddde29d6c64e9c81f69780b7b0c967b2b4901 +Author: Tilman Sauerbeck +Date: Tue Oct 23 10:15:57 2007 +0200 + + Added Numeric#nonzero? and killed bad Fixnum#nonzero? in bootstrap. + + This fixes the Numeric#nonzero? specs. + +commit e9a1b257fc95c181e46c679d301a324134a725d4 +Author: Tilman Sauerbeck +Date: Tue Oct 23 16:07:58 2007 +0200 + + Enabled the Numeric#step spec now that it works fine. + +commit f4016db94eee2ec93a2cc487181c9ec2fa0d59d0 +Author: Evan Phoenix +Date: Wed Oct 17 13:51:04 2007 -0700 + + A number of fixes found while debugging test/unit and optparse + +commit 7c7920c3e7727c3514b493ba299a52c5e5cde8f6 +Author: Charles Comstock +Date: Mon Oct 22 18:12:42 2007 -0500 + + Numeric#step is capable of floats and passes all tests + +commit ebc6ec5be0239bba544c55ff77fdc88903f4bb28 +Author: Tilman Sauerbeck +Date: Mon Oct 22 20:56:33 2007 +0200 + + Made the Symbol#inspect spec pass. + +commit f0db8c3d1bb5dc444fc72ed5ca222f4cf5df8b35 +Author: Arthur Schreiber +Date: Sun Oct 21 14:00:30 2007 +0200 + + Fixed a bug in process_op_asgn1 and added a simple spec for it. + +commit 4fbce6e8a6ab8fbb6b69944677678611db68bcf2 +Author: Arthur Schreiber +Date: Sat Oct 20 20:03:50 2007 +0200 + + Fixed a failure in String#sub specs + +commit ca0332f9edb9e01ae216dee90674fb6f9809951c +Author: Tilman Sauerbeck +Date: Sat Oct 20 22:33:42 2007 +0200 + + Fixed the Bignum#size spec for Rubinius' implementation. + +commit ae9c2ac3fe9dc59a027af7571d6d3083bcccb490 +Author: Arthur Schreiber +Date: Sat Oct 20 14:14:49 2007 +0200 + + Don't rely on #respond_to? calls. + +commit 94938622aaf74e1f068c3b7ec8bfeccf763792ba +Author: Tilman Sauerbeck +Date: Fri Oct 19 20:56:50 2007 +0200 + + Extended the private spec. + +commit bf000a15edcfdd30c43ae6563b5766617f245a60 +Author: Brian Ford +Date: Fri Oct 19 00:49:16 2007 -0700 + + Updated CI excludes. + +commit ad146fc7ae22bfc26a536a40cf8dc4c0338cf25c +Author: Tilman Sauerbeck +Date: Thu Oct 18 19:45:27 2007 +0200 + + Added a spec for the 'private' keyword. + +commit f15b5a8c818932d0ab5bea46f48a326e468b3511 +Author: Tilman Sauerbeck +Date: Thu Oct 18 19:44:54 2007 +0200 + + Added Object#should_not_include. + +commit 80fdbd626d8ff99dc7ba4cf23a05d44ad98bd0cb +Author: Yehuda Katz +Date: Thu Oct 18 09:31:51 2007 -0700 + + Updates Hash so: + * No longer freezes keys + * Specs reflect the lack of freezing + * shift spec doesn't fail purely because to_a is broken + +commit 197f36b6626b61203709704db869324a539764d5 +Author: Charles Comstock +Date: Wed Oct 17 20:11:50 2007 -0500 + + moved File::expand_path to platform and made several fixes + new specs + +commit 7e8506fd510ab4e6f07e6d36456fdfef6e5b080a +Author: Tilman Sauerbeck +Date: Wed Oct 17 21:07:44 2007 +0200 + + Make sure that File.dirname doesn't modify its argument. + +commit 271bc31ba814e68fb414ebf29cf9648f57fe4cb6 +Author: David Altenburg +Date: Wed Oct 17 00:39:59 2007 -0500 + + Added to fork spec: check that fork returns a nil pid for the child process + +commit 5d45341c55400a51d8cae3128bba265e7d441fab +Author: Charles Comstock +Date: Wed Oct 17 05:19:49 2007 -0500 + + moved File.basename to platform, added specs, and fixed specs for all but a disputable behavior + +commit ae7afd794881a4dedadf876f61369e5e88da695b +Author: Charles Comstock +Date: Wed Oct 17 04:47:24 2007 -0500 + + added platform/file.rb and fixed File.dirname for all and updated specs + +commit 71bf9b1c9cd7fb81692186d21b133fde43e8a6b7 +Author: Charles Comstock +Date: Wed Oct 17 03:44:33 2007 -0500 + + spec to check if break exits all types of yields and loops correctly + +commit 3a546b40271d35bf7c60bb56a68ca49089ac9a34 +Author: Charles Comstock +Date: Wed Oct 17 03:01:40 2007 -0500 + + clarified include_spec + +commit 40d8ed96fa689daf31039e71f91ed5520a821aa2 +Author: Charles Comstock +Date: Wed Oct 17 02:38:46 2007 -0500 + + specs Module#include and Class#include that check to see if constants, public_methods and instance_methods are imported correctly + +commit ff462080a58aaa61759830e18f3a5757a883980d +Merge: 5a2c858... 5cf41ab... +Author: Jon Guymon +Date: Wed Oct 17 01:55:33 2007 -0400 + + Merge branch 'puts_specs' + +commit 5cf41abaff9dc04cdba5fe50492d4ebfdde2a274 +Author: Jon Guymon +Date: Wed Oct 17 01:55:01 2007 -0400 + + normalized specs for IO#puts StringIO#puts and Kernel#puts + +commit 5a2c858086c1b02a54864ff82c12d4bf3a559535 +Author: Charles Comstock +Date: Wed Oct 17 00:43:24 2007 -0500 + + fixed posix File.join and added edge cases to specs + +commit e7972b8617b8b0ef2a19a1f7ddedd4d93ab80f5c +Author: Brian Ford +Date: Tue Oct 16 21:17:21 2007 -0700 + + Commit gnarg's loop specs (#49). + +commit 3fcdd60b4c9fc20081987bb13aab37b9419939a3 +Author: Tilman Sauerbeck +Date: Tue Oct 16 19:46:36 2007 +0200 + + Make this spec usable by loading pathname. + +commit 276b6cc5620a5a3629d56b04ade7c397a48c2488 +Author: Brian Ford +Date: Tue Oct 16 00:53:11 2007 -0700 + + Exclude metaclass instances from Module#ancestors list. + +commit e108e7f3f8a6ef7cf2acf4bb7e7a6609900a3ebc +Author: Brian Ford +Date: Tue Oct 16 00:06:54 2007 -0700 + + Updated CI excludes. + + Updated compiler specs to match recent changes. + Added compiled core/string.rb from changes in edeffe90517. + +commit 1a1410f394b3de23b63560f2a5c1312cc6451d2e +Author: Charles Comstock +Date: Mon Oct 15 19:49:40 2007 -0500 + + spec for __FILE__ added + +commit e158c3130f033a1029ae26888b8e7e541f2b388a +Author: Charles Comstock +Date: Mon Oct 15 19:46:58 2007 -0500 + + spec for __LINE__ added + +commit d09ad9e6b28c91f5d00db5d0b369c4932eabbe2b +Author: Charles Comstock +Date: Mon Oct 15 18:45:48 2007 -0500 + + spec for throw/catch inside of ensure reverted and clarified + +commit 0e5336f1572fc1ad766cff61d8843410d28df9db +Author: Charles Comstock +Date: Mon Oct 15 18:33:27 2007 -0500 + + spec super refactored into fixtures and expanded to test all methods on each class + +commit b998ec8e682c3a0f2160066bb84f40b68f748407 +Author: Evan Phoenix +Date: Mon Oct 15 16:26:58 2007 -0700 + + Implement undef and Module#undef_method + +commit fed8486110930cabce64e0421638a867740e4d21 +Author: Evan Phoenix +Date: Mon Oct 15 15:54:44 2007 -0700 + + A bunch more language spec cleanups. + +commit 66086cb333432a29d4c4ce4fec6a01c0ac88c5a5 +Author: Evan Phoenix +Date: Fri Oct 12 19:33:59 2007 -0700 + + Bunch of compiler fixes to pass more language specs + +commit 680e0ca4cafb20fa053f0dd5cd72915da9fbc86f +Author: Charles Comstock +Date: Mon Oct 15 17:36:09 2007 -0500 + + specs for super involving inheritence, modules and metaclasses + +commit 167febd232f5cf4696cf8e81a96d1c9d80744e36 +Author: Brian Ford +Date: Mon Oct 15 15:32:48 2007 -0700 + + Update CI excludes on OS X. + +commit 948e2573800859931e8c61e72069c21e9a50193b +Author: Brian Ford +Date: Mon Oct 15 14:23:02 2007 -0700 + + Update compiler specs to match recent changes. + +commit a015bac050e1080548bd947c5f59b344175a809d +Author: Tilman Sauerbeck +Date: Mon Oct 15 23:51:01 2007 +0200 + + Enabled the remaining Bignum#& spec. + +commit 0b37b2946772ea41fe7b14c762be7fdbaa4a6f8d +Author: Arthur Schreiber +Date: Mon Oct 15 18:20:56 2007 +0200 + + Updated Spec excludes for Bignum. + +commit ae613272bcf0c260ad3da00ffd14d4a76422ac46 +Author: Tilman Sauerbeck +Date: Mon Oct 15 08:30:11 2007 +0200 + + Added the beginnings of a File.stat spec. + +commit 81147d2eadb0397c2bcc1b9dd620bb55b6e0e53d +Author: Brian Ford +Date: Sun Oct 14 11:33:21 2007 -0700 + + Fixed Float#to_s for numbers of the form "\d+.0". + +commit 2f9ba53190ca19ca425126d1319f47b3bbce12f6 +Author: Arthur Schreiber +Date: Sun Oct 14 18:57:04 2007 +0200 + + Updated Bignum excludes. + +commit 0c81822cf703da13f6a8783cc6cd4ad453d2ff74 +Author: Arthur Schreiber +Date: Sun Oct 14 18:05:45 2007 +0200 + + Add some OpenStruct specs. + +commit add2a900029530cb35b6463525313f432b7f36f4 +Author: Arthur Schreiber +Date: Sun Oct 14 17:29:38 2007 +0200 + + Extended some more Bignum specs. + +commit 45cf3a275d390d4ae1d995eca89e10ba82d2288f +Author: Arthur Schreiber +Date: Sun Oct 14 17:29:01 2007 +0200 + + Extended the Fixnum#to_s specs a bit. + +commit d8a42cdd57967ee07cccfa5f3f814d97353c48c9 +Author: Tilman Sauerbeck +Date: Sat Oct 13 23:16:37 2007 +0200 + + Fixed the bug that broke 'case' blocks with a single 'when' statement. + + Acked-by: Wilson Bilkovich + +commit 5624627fd61378fce65aebf2ffacc39c45ac5ee6 +Author: Tilman Sauerbeck +Date: Sat Oct 13 23:48:32 2007 +0200 + + Ticket #37: Fixed Bignum#& segfaults when the argument isn't a bignum. + +commit 2081e5f53ba80cb9aa2ee272d4e543db1d4e732e +Author: Arthur Schreiber +Date: Sat Oct 13 23:43:08 2007 +0200 + + Modify and extend Bignum specs a bit. + +commit 7917f4f8a538a3251e3cb17d55e4cf2a523af8d5 +Author: Arthur Schreiber +Date: Sat Oct 13 23:42:44 2007 +0200 + + Modify the fixnum specs a bit. + + Remove dependencies on Exception messages. + +commit 171f25c25865da618c2e2a9a7b221abda613efa4 +Author: Arthur Schreiber +Date: Sat Oct 13 02:02:12 2007 +0200 + + Extended Fixnum#<=> specs a bit. + +commit f708429a161c52dd713b4239527247c57fa158af +Author: Martin Kuehl +Date: Sat Oct 13 23:30:37 2007 +0200 + + Guard bin/ci from running the new Bignum#& specs, which segfault (on OS X at least). + +commit 19f40e0ae1b60c037d0c38537a0924ad5726902a +Author: Ben Curren +Date: Sat Oct 13 13:46:35 2007 -0700 + + Refactored const_name_to_sym to share logic with Class#attr. + +commit ee9daad614fa746a3fe2fc1b9123c65dbb0814c7 +Author: Brian Ford +Date: Sat Oct 13 11:52:08 2007 -0700 + + Identify which ruby platform and version before changing this spec! + + Revert "Fix spec to expect correct result." + + This reverts commit 8268469c563943cba6c1afce5d84defbc35f1789. + +commit 14a7781944491e5a1c3f5c664adcac4e1c383f2f +Author: Tilman Sauerbeck +Date: Sat Oct 13 17:15:24 2007 +0200 + + Added a failing spec for 'case' with only one 'when' statement. + +commit 8268469c563943cba6c1afce5d84defbc35f1789 +Author: Tom Mornini +Date: Sat Oct 13 03:22:47 2007 -0700 + + Fix spec to expect correct result. + +commit 671f93c69e74976c3f5886c7fe8eb32402ccd338 +Author: Charles Comstock +Date: Fri Oct 12 19:06:07 2007 -0500 + + specs exiting threads using return, raise, and throw + +commit 119154a3ea5ecee20e77726b38fb58ee4b536d48 +Author: Charles Comstock +Date: Fri Oct 12 18:59:47 2007 -0500 + + spec to ensure throw exits from correct nesting and can return a value + +commit f36f68f075b34b5436257aba1ae41c14c04adcae +Author: Arthur Schreiber +Date: Sat Oct 13 01:54:42 2007 +0200 + + Method#[] specs should include the fixture classes. + +commit 22d32a24eb799307e42af55b04752c74ff500080 +Author: Arthur Schreiber +Date: Sat Oct 13 01:40:01 2007 +0200 + + Extend Bignum#coerce, Bignum#&, Bignum#| and Bignum#^ specs. + +commit e1f682e27d2486d65297cf2121c354a99954a56e +Author: Arthur Schreiber +Date: Sat Oct 13 01:38:55 2007 +0200 + + Removed a dependency on an exception message in Fixnum#coerce specs. + +commit 9754ed5e74eeb6d62f0015f3615d077aa2e58a6f +Author: Arthur Schreiber +Date: Fri Oct 12 21:41:56 2007 +0200 + + Fixed File.chmod and File#chmod specs on win32. + +commit 5ad3a4b7035bdade48586191c6e26cde1e74976c +Author: Martin Kuehl +Date: Sat Oct 13 01:37:46 2007 +0200 + + Remove wrong spec from Bignum#divmod. + Update CI excludes. + +commit b068c8634b56cd9129f9fc7c309bfa81869209c8 +Author: Charles Comstock +Date: Fri Oct 12 18:21:52 2007 -0500 + + specs for behavior of throw/catch and how they interact with ensure + +commit 45d4a8be8f20b2b70d32d0fdd340feb8897a1ad7 +Author: Martin Kuehl +Date: Sat Oct 13 01:21:21 2007 +0200 + + Add (skeletal) Process::Status. + Set $? in Kernel#system and Kernel#`. + +commit ef1499962a16a7ce85bffe9e61863d2806caf6ec +Author: Martin Kuehl +Date: Fri Oct 12 23:52:16 2007 +0200 + + Regenerate core/dir.rbc and CI excludes for Dir. + +commit b999f31ded2a7eccb856d95653a2826a3a190204 +Author: Martin Kuehl +Date: Fri Oct 12 23:02:39 2007 +0200 + + Fix typo in Dir.mkdir. + +commit de235630aa08df803be0420084b0a61ee35f5448 +Author: Brian Ford +Date: Fri Oct 12 13:56:13 2007 -0700 + + Added dev_null spec helper for capturing or silencing $stderr, etc. + + Fixed failing specs on OS X MRI 1.8.5. + +commit 01e27ea5fa0e0f0d170cd88f128adfbb2a2703bd +Author: Martin Kuehl +Date: Fri Oct 12 22:01:43 2007 +0200 + + Add spec for backticks and their setting of $?. + +commit f7b18c19e47c15f3ab05f8fa548eff034206b0d8 +Author: Brian Ford +Date: Fri Oct 12 13:21:03 2007 -0700 + + Added guards to make specs pass on ubuntu feisty MRI 1.8.5. + + Fixed rspec_helper should_include to take multiple args. + +commit b2d25d4a502dca79ea98f60d937be7dbd8f496d2 +Author: Charles Comstock +Date: Fri Oct 12 14:19:55 2007 -0500 + + spec for retry/redo to control order of an enumeration + +commit eec535a19dc2b20156349720dc3bb526c9fa4f1e +Author: Martin Kuehl +Date: Fri Oct 12 18:53:33 2007 +0200 + + Revert "Fix Kernel#`: set $? to the subprocesses exit status. Add a spec to check that." + + This reverts commit 40da2d5c68196c3c9002c4ca75ead0fefc520bef. + +commit 40da2d5c68196c3c9002c4ca75ead0fefc520bef +Author: Martin Kuehl +Date: Fri Oct 12 18:43:44 2007 +0200 + + Fix Kernel#`: set $? to the subprocesses exit status. Add a spec to check that. + +commit 5da57253750e854bd9baf5378684222a895e7fd9 +Author: Martin Kuehl +Date: Fri Oct 12 16:24:18 2007 +0200 + + Shield "strange block args" spec from being run by mspec. + +commit 2a8f7d7dd6b0f7f800320f84d16d5d089357e085 +Author: Martin Kuehl +Date: Fri Oct 12 14:48:42 2007 +0200 + + Fix block specs for MRI. + +commit ade6c39f6199198e0015558698bc7d0333f7bcd0 +Author: Arthur Schreiber +Date: Fri Oct 12 12:07:49 2007 +0200 + + Fix Array#delete specs. + +commit 825af45d5effb6909bb0832f92621b96e51dc380 +Author: Brian Ford +Date: Fri Oct 12 01:15:23 2007 -0700 + + Updated CI excludes. + +commit e1bfb47d3560929512cbdf5c27f56c92435ce29f +Author: Brian Ford +Date: Fri Oct 12 00:40:53 2007 -0700 + + Removed printing summary at exit in mini_rspec. + + Set $VERBOSE=nil when running the specs. + Fixed specs failing MRI. + +commit 1f1a041d8bcfaeb8dd3cb17f7d31b21281e690a2 +Author: Brian Ford +Date: Thu Oct 11 18:26:14 2007 -0700 + + Moved shared specs to shared directories. + + Rewrote Module#method_missing specs. + +commit f9177eb198003b495f485a13910808fe603030ad +Author: Ben Curren +Date: Thu Oct 11 20:54:22 2007 -0700 + + Added a failing test for setting and getting constants on an instance of a module. + +commit d9cbad87fd4d578e4f637627fa03cab312882a36 +Author: Ben Curren +Date: Thu Oct 11 20:51:27 2007 -0700 + + Updated the excludes for module tests. + +commit 938f034bad41f4fe3391b941e536bce9e1be0af6 +Author: Ben Curren +Date: Thu Oct 11 20:49:57 2007 -0700 + + Updated const_set_spec to not create a new instance of Module for testing purposes. + +commit 550caaf78723b00f95c5f8f38215b15a0940698a +Author: Ben Curren +Date: Thu Oct 11 18:21:01 2007 -0700 + + Updated the excludes for the tests that are now passing. + +commit 82c51fc652e215f0dc421099329c03a37af8e8f8 +Author: Ben Curren +Date: Thu Oct 11 17:56:48 2007 -0700 + + Updated const_set and refactored the valid_const_name? further. + +commit c6323c74d0ee7b554d2cfbff3bd8d85ea910e0c9 +Author: Ben Curren +Date: Thu Oct 11 12:01:27 2007 -0700 + + Added logic to remove Object and empty from a recursive string for const_get and const_defined? + +commit c108d2a623f5041f46b6efa32d0b331f4f91d669 +Author: Ben Curren +Date: Thu Oct 11 11:58:04 2007 -0700 + + Added back the recursive case for const_get and const_defined? + +commit b976f184d8ade3b5d32d3e7ec11027c21c2bce2a +Author: Ben Curren +Date: Thu Oct 11 11:33:41 2007 -0700 + + Refactored const_defined to use const_get. + +commit 82c5c14b948cedbf3bed5f7996634b0238e4de55 +Author: Evan Phoenix +Date: Thu Oct 11 17:38:05 2007 -0700 + + Bunch of compiler fixes, almost have test/unit and optparse running. + + * Adds support for /ao#{name}/o (aka dregx_once) + * Invalid redo's raise an exception at runtime instead of compile time + * defined?(a.foo) works + * Lots of work on getting the block arg semantics right, including a new + instruction, passed_blockarg which is used to detect at runtime how + many block args were passed in. + * bug in 'yield 1, 2' versus 'yield [1, 2]' fixed + * A little better error reporting on compile errors + * Fixed Class#<, added #>, #<=, and #>= + * Fixed Hash.new + * Fixed nested case problem + +commit 1369b104a3f966dd4d279362afdc6ccb72f06de3 +Author: Arthur Schreiber +Date: Thu Oct 11 22:47:16 2007 +0200 + + Fix String#eql? specs. + +commit b190009707c120edb257a9ad92697145092c5612 +Author: Evan Phoenix +Date: Thu Oct 11 10:57:10 2007 -0700 + + Shield parse errors in block args properly + +commit 0509ecbd6aeb973061866c5e04c590f975174b41 +Author: Ben Curren +Date: Thu Oct 11 10:21:08 2007 -0700 + + Updated tests to test FixNum being passed to const_defined? + +commit 80116298779dc5afd3294cd83d758d76d0dcdf50 +Author: Ben Curren +Date: Thu Oct 11 10:16:46 2007 -0700 + + Added error checking to const_defined? + +commit eed3ae097b1dae17e45cdb959b75d1fa7cf21c1b +Author: Ben Curren +Date: Thu Oct 11 09:25:53 2007 -0700 + + Convert paramter to_str if it responds to the method. + +commit 348df85a082eee56c301bce594d6c522050e34dd +Author: Charles Comstock +Date: Wed Oct 10 18:49:34 2007 -0500 + + specs for language/retry and updated redo to show differences between them + +commit e924e2bb206317e8f5375c979f5e4e1046fccca9 +Author: Tom Mornini +Date: Wed Oct 10 00:55:47 2007 -0700 + + Add Class#attr, refactor Class#attr_reader, Class#attr_writer and + Class#attr_accessor, pass all specs for Class#attr_*, fix a couple of issues + with said specs. + +commit 21b0bdc67c6a8cc4ad4b9d3942a2608fb45da31d +Author: Brian Ford +Date: Tue Oct 9 15:10:04 2007 -0700 + + Commit #198, patch from Will for Module#(private|public)_class_method. + +commit 2cf5f0b4683d0a65181c1450d0714c4e165db1cd +Author: Arthur Schreiber +Date: Tue Oct 9 21:00:13 2007 +0200 + + Moved shared specs. + + Moved shared specs into the 'shared'-subdirectory so specs don't depend on each other anymore. Added some more shared specs. + +commit e558fab61ce9f7c5211d005aff2c5e8fc1b39931 +Author: Arthur Schreiber +Date: Tue Oct 9 19:53:46 2007 +0200 + + Fixed a failing Array#each spec. Closes #14. + +commit 0d77eefd718c826e02376edc8643364eb511773d +Author: Arthur Schreiber +Date: Tue Oct 9 19:09:17 2007 +0200 + + Removed remaining dependencies on Exception messages in Fixnum specs. + +commit dd4063ba46eb313a57957d76dce3608dd8e5c161 +Author: Arthur Schreiber +Date: Tue Oct 9 18:58:01 2007 +0200 + + Fixed String#crypt spec. + +commit d5a2bb2b000fae7391e512c5bcab054ce967de3b +Author: Brian Ford +Date: Tue Oct 9 09:35:51 2007 -0700 + + Updated CI excludes after applying Akshay's Precision specs patch. + +commit 10cd5764bbf51f1defa6815f8b07fcdce0de8875 +Author: Akshay Rawat +Date: Thu Oct 4 21:48:15 2007 +0530 + + Specs for the module Precision + +commit 2e711c30e4e9ce50d9c20ab14a3b99ea47be32e9 +Author: Martin Kuehl +Date: Tue Oct 9 13:56:45 2007 +0200 + + Make IO#puts specs pass in MRI. + +commit 13dc28c47c3211f01663d002847badb50277f277 +Author: Martin Kuehl +Date: Tue Oct 9 13:44:51 2007 +0200 + + Silence warnings when running Hash specs in MRI. + +commit 52f903938f4eacf4465f7a36cacb25aa662aa559 +Author: Martin Kuehl +Date: Tue Oct 9 13:44:02 2007 +0200 + + Turn Hash#values_at into a shared spec. + +commit 1e02ced5a68f16b8a65809136d954c68c9fdc590 +Author: Martin Kuehl +Date: Tue Oct 9 12:59:58 2007 +0200 + + Add a few more specs for Struct#new. + Regenerate CI excludes for Struct. + +commit 94ea8c1f25761384796e9499e0b4b3faeba9da66 +Author: Martin Kuehl +Date: Tue Oct 9 12:48:03 2007 +0200 + + Silence warnings when running Struct specs in MRI. + +commit 5ea6b219a8465cfad86dae9ae12d6a8d85812532 +Author: Martin Kuehl +Date: Tue Oct 9 12:42:58 2007 +0200 + + Apply patch from ticker #23 by Jon Guymon (gnarg). + Make Struct specs not depend on method argument evaluation order. + +commit 8cbf7b94300e6ebcc0ee3cbe0de8123ef3563e96 +Author: Brian Ford +Date: Mon Oct 8 22:08:29 2007 -0700 + + Fixed that including a module includes the whole chain. + + Fixed that Module#include only allows modules. + Simplified some module fixtures. + Updated CI specs for module. + +commit 4e7e2768d50392831a4d26f236d4cff733418225 +Author: Ben Curren +Date: Mon Oct 8 11:54:51 2007 -0700 + + Added puts spec for io and updated IO implementation to match MRI's. Updated Kernal#puts to delegate to $stdout.puts. + +commit edc724086e84725995ed1720d4fa7a781fd9c3c6 +Author: Brian Ford +Date: Mon Oct 8 09:24:40 2007 -0700 + + Updated CI excludes for Module#define_method. + +commit a53ddb723a10d692223f05a49679e17f403fa128 +Author: Arthur Schreiber +Date: Mon Oct 8 01:05:04 2007 +0200 + + Fixed some Fixnum specs to not depend on error messages. + +commit d6bc4b47f3f395980c92f323cf029da1ccdba709 +Author: Arthur Schreiber +Date: Sun Oct 7 23:42:12 2007 +0200 + + Added another failing spec for Module#define_method. + +commit 49435e31289f593a118377b3513ec9e7cdfea06b +Author: Arthur Schreiber +Date: Sun Oct 7 23:37:19 2007 +0200 + + Added failing specs for Module#define_method when given an UnboundMethod. + +commit 821c0114777fb2a77f1c85f216ee54e4c5340943 +Author: Martin Kuehl +Date: Sun Oct 7 22:29:47 2007 +0200 + + Apply patch from ticket #15 by Jon Guymon (gnarg). + Add Struct#eql?. + Rebuild core/struct.rbc. + Add more struct specs. + Update CI excludes for Struct. + +commit 1f14c3510d4563930d11155f36717c0fb851c678 +Author: Arthur Schreiber +Date: Sun Oct 7 18:13:33 2007 +0200 + + Added some GetoptLong Specs. + +commit f9c8c00649212b924561300abbd0cb037c1d278d +Author: Brian Ford +Date: Sat Oct 6 23:00:05 2007 -0700 + + Update CI excludes for File#executable. + +commit 6d4427d07fc474e2404cdd2b6f3b925d99d90e67 +Author: Brian Ford +Date: Sat Oct 6 22:50:45 2007 -0700 + + Updated spec/excludes.txt from 72 items to 17. + + Added -V | --verbose flag to bin/mspec. + Updated CI excludes. + +commit d4f5e44a8e2f8e682b779f45d12d060e83eb9fc7 +Merge: a035e9d... 42abc5e... +Author: Jason Yates +Date: Sat Oct 6 15:18:45 2007 -0400 + + Merge branch 'master' of git@git.rubini.us:code + +commit a035e9d3d204cf7e5ddb2fec72ca471ff33c3b9b +Author: Jason Yates +Date: Sat Oct 6 15:13:28 2007 -0400 + + fix a bug in the File.executable? spec + +commit 3c23c945aaf143aa8706b1cd2956908a71940e26 +Author: Jason Yates +Date: Sat Oct 6 15:09:55 2007 -0400 + + fix bug in File.executable? spec + +commit 94e59065bf921ae167a6b04edfef40de336978a1 +Author: Jason Yates +Date: Sat Oct 6 15:03:14 2007 -0400 + + Revert "Revert "Add a few more Proc#call specs. And CI excludes."" + + This reverts commit 7658362c3882c6be2ef67f6b57d6c6796ff5de98. + +commit 42abc5ed6e1ced2fa86e9dc9379c6bed4da4537e +Author: Brian Ford +Date: Sat Oct 6 11:40:49 2007 -0700 + + Updated CI excludes for Kernel specs. + +commit 7658362c3882c6be2ef67f6b57d6c6796ff5de98 +Author: Jason Yates +Date: Sat Oct 6 14:29:28 2007 -0400 + + Revert "Add a few more Proc#call specs. And CI excludes." + + This reverts commit 567659dee34014d037d4797bf0c171597e0ac05d. + +commit 567659dee34014d037d4797bf0c171597e0ac05d +Author: Martin Kuehl +Date: Sat Oct 6 16:23:54 2007 +0200 + + Add a few more Proc#call specs. And CI excludes. + +commit d8e737b09f8ed984e57b4fbbd5c016a7643aa67d +Author: Martin Kuehl +Date: Sat Oct 6 15:56:33 2007 +0200 + + Fix Array#slice specs. Regenerate CI excludes. + +commit c79eeb620296a1802e6d194463063555638911bf +Author: Martin Kuehl +Date: Sat Oct 6 14:34:05 2007 +0200 + + Fix Array specs that depended on respond_to? being called on coercion. + Regenerate CI excludes for core/array. + +commit 8a60522fd8237fdfa36ef5518c9642216c66f8d6 +Author: Martin Kuehl +Date: Sat Oct 6 14:08:22 2007 +0200 + + Guard Array specs for #freeze. Fix MRI Array specs for #freeze. + Regenerate CI excludes. + +commit eaaab65c54c3b81397441169761774ef95867297 +Author: Martin Kuehl +Date: Sat Oct 6 12:47:55 2007 +0200 + + Add specs for Proc#call. Regenerate CI excludes. + (Most of these seem to be from e6cf8978.) + +commit 19bcb0f6ec1b2247985823492f0c25f0aa5d94ab +Author: Martin Kuehl +Date: Sat Oct 6 12:09:29 2007 +0200 + + Slightly amend the core/hash specs. + Regenerate CI excludes. + +commit 6d1afe325098a73757980bb208fb2c8c64bd016b +Merge: ac9365e... 0d22ef5... +Author: Jason Yates +Date: Sat Oct 6 01:13:59 2007 -0400 + + Merge branch 'master' of git@git.rubini.us:code + +commit ac9365e384263b6f062353f2afab3e33d8f84f3e +Author: Jason Yates +Date: Sat Oct 6 01:13:23 2007 -0400 + + added spec for OpenStruct + +commit e6cf8978a6dd441d5d4793c48438fab4150ca750 +Author: Jason Yates +Date: Sat Oct 6 00:14:01 2007 -0400 + + added several tests to Proc + added tests for Kernel#lambda and Kernel#proc + +commit 7b69ae066cab2252375d1ad19c6f17b365c47c32 +Author: Brian Ford +Date: Fri Oct 5 18:51:11 2007 -0700 + + Updated CI excludes for Array. + +commit f216e89033d10f3500798561282e48aa0e5b5537 +Author: Jason Yates +Date: Fri Oct 5 20:45:41 2007 -0400 + + really simple spec for the Singleton class. + +commit d682b176237a988afa7ebdb3460d64ea41fab919 +Author: Arthur Schreiber +Date: Sat Oct 6 01:54:31 2007 +0200 + + Applied esomnies + +commit b3018362c0cad86a5a026eb39b5f6ea4a8af1192 +Author: Martin Kuehl +Date: Sat Oct 6 01:24:05 2007 +0200 + + Guard Hash specs for #freeze. + +commit a185f463adbab1d6f82126dde9abe88a29e83283 +Author: Martin Kuehl +Date: Sat Oct 6 01:15:23 2007 +0200 + + Fix workarounds in core/hash specs. + +commit d796eb3d8a5d9a070b105f1fe0e9f46be5bfaaee +Author: Charles Comstock +Date: Fri Oct 5 17:09:48 2007 -0500 + + specs for UDPSocket client/server + +commit 46a9c1a0d0610865b659c289293b444f0b3d6ae9 +Author: Brian Ford +Date: Fri Oct 5 15:32:56 2007 -0700 + + Fixed bin/ci to not load spec/excludes.txt for every file. + + Fixed bin/mkspec to not overwrite an existing spec file. + Updated Fixnum CI excludes. + +commit 1099f49c06de5621aff36216179f46c308e60a38 +Author: Brian Ford +Date: Fri Oct 5 14:40:24 2007 -0700 + + Added basic IO#readlines spec and implementation. + +commit edc438039ee503c7b9d1fb83b04bd9bc1664cda5 +Author: Martin Kuehl +Date: Sat Oct 6 00:09:41 2007 +0200 + + Regenerate core/hash.rbc, core/hash CI excludes. + +commit 2ad7d015a316620a488ed1cdeb45fe696b9d410a +Author: Martin Kuehl +Date: Fri Oct 5 23:56:59 2007 +0200 + + Extend core/hash specs to check for LocalJumpErrors. + +commit 42d961f0ab8a7e23a822b41ca82aaed5a48da2bf +Author: Martin Kuehl +Date: Fri Oct 5 23:21:20 2007 +0200 + + Refactor and fix Hash#inspect. + +commit b51402d8724478b85789d19857a2a48442470fcb +Author: Brian Ford +Date: Fri Oct 5 11:29:08 2007 -0700 + + Fixed bin/mkspec to not create the spec file stub if the file exists. + +commit efe79de398db491ce97666a3f4f3b38265c1ab95 +Author: Brian Ford +Date: Fri Oct 5 10:55:52 2007 -0700 + + Updated CI specs. Guarded String specs for #freeze. + +commit 8647951df433d427be31bec060edf5b7efb46e46 +Author: Arthur Schreiber +Date: Fri Oct 5 17:04:25 2007 +0200 + + Replaced all occurrences of Object#coerce_to with Type.coerce_to and removed Object#coerce_to. + +commit 778e11d2df647cf91a712bb30df34152c71dbc3f +Author: Martin Kuehl +Date: Fri Oct 5 11:42:40 2007 +0200 + + Don't work around rbx bugs in Hash specs. + +commit 4b42923eb2cdebe43f9e9dd80fff98d9ded26e4b +Author: Martin Kuehl +Date: Fri Oct 5 11:29:33 2007 +0200 + + Fix Hash specs to pass in MRI again. (Doh!) + +commit 8a4f0b1c0d95b7a87ed99583797cf7d3710fb15a +Author: Brian Ford +Date: Fri Oct 5 02:24:30 2007 -0700 + + Updated CI excludes for Hash. + +commit cecbf342546f37f4923728e8381e7fafcb039633 +Author: Brian Ford +Date: Fri Oct 5 02:22:41 2007 -0700 + + Changed bin/ci to run in a single process. Updated CI excludes. + +commit ba0f4ef5405665c84af4410d70be5ef911a93195 +Author: Yehuda Katz +Date: Thu Oct 4 22:58:50 2007 -0700 + + Adds the intern spec from Ticket #8 + +commit 0721f6ea40f51202ec9d2d421061be92e05a18b9 +Author: Jason Yates +Date: Thu Oct 4 22:39:00 2007 -0400 + + Commit #207 Xavier Shay Enhanced specs for Hash + fixed Binding#dup spec + +commit ce4a1866ef65e041fbed224c3f694ca534d0a0d1 +Author: Yehuda Katz +Date: Thu Oct 4 18:24:21 2007 -0700 + + makes String#delete faster + +commit 7ec0eeadf554150159f0a04468b16de8f06c2e8a +Author: Martin Kuehl +Date: Fri Oct 5 00:32:25 2007 +0200 + + Whoops, two more frozen TypeErrors I forgot. + +commit 78a3de42bf0e6f1478b2aac903c25143fd56195a +Author: Martin Kuehl +Date: Thu Oct 4 23:17:42 2007 +0200 + + Regenerate CI excludes and core/hash.rbc after revert. + +commit beaa5d022b19cb70213d4fe14e10d7f1a5f90a3a +Merge: c7ea881... 8cb4b0b... +Author: Charles Comstock +Date: Thu Oct 4 16:12:41 2007 -0500 + + Merge branch 'spec_block_parameters' + +commit 8cb4b0b2c3c766618a523a0ef9a83106761ee2f8 +Author: Charles Comstock +Date: Thu Oct 4 16:11:15 2007 -0500 + + specs for setting variables in block parameters + +commit c7ea8812f74184e6ee33bb236766f32fface2f95 +Author: Martin Kuehl +Date: Thu Oct 4 22:57:09 2007 +0200 + + Regenerate CI excludes for spec/core/hash. New hash.rbc. + +commit 939e8c533fb70586f8c7c6f3506d6be13f492d78 +Author: Martin Kuehl +Date: Thu Oct 4 22:11:18 2007 +0200 + + Fix Hash specs: don't depend on coercion to call respond_to? + +commit 422e45f210ec9dc2438ae3b11544823bb6ffdd50 +Author: Jason Yates +Date: Thu Oct 4 16:21:27 2007 -0400 + + added specs for FileTest#exists? and FileTest#exist? + +commit e43466b52184a042bd38d33273acf7afa4580a96 +Author: Jason Yates +Date: Thu Oct 4 16:12:58 2007 -0400 + + added spec for File.exists? + +commit 9f69c8193d92752a2be7c21d23dfe90fb9765f11 +Author: Jason Yates +Date: Thu Oct 4 16:03:33 2007 -0400 + + fixed grammar error in Method#clone + +commit 9321aacf703eaec6d6bc26cce83ed7475cb27d46 +Author: Jason Yates +Date: Thu Oct 4 16:00:31 2007 -0400 + + added several specs for Module + +commit a282c1c4c137e1bdeae34f2f9cd58bc73f257809 +Author: Jason Yates +Date: Thu Oct 4 14:19:07 2007 -0400 + + added specs for Kernel#binding and Binding + +commit 3e92b4528dbf47b80a25232979554f7e4309460a +Author: Brian Ford +Date: Thu Oct 4 10:45:10 2007 -0700 + + Fixed ffi_sprintf_[fd]. Updated String spec CI excludes. + +commit 9e9a292a8a4befeb8a928d476119b02ac0df976e +Author: Martin Kuehl +Date: Thu Oct 4 18:53:47 2007 +0200 + + Alias Object#object_id to Object#__id__ + Regenerate CI excludes for spec/core/kernel + +commit 8790e93c6d231baf7da07f11d02b91a38d28375e +Author: Evan Phoenix +Date: Wed Oct 3 18:23:42 2007 -0700 + + Superclass checking and loop {} fix. + +commit 718ae6f28223e94b8ca0f3af7ce321c81a597804 +Author: Martin Kuehl +Date: Thu Oct 4 15:05:59 2007 +0200 + + Slightly extend Object#kind_of? specs. + +commit 7fa087a7058fe8872bb9743abd6dd472cd2119d7 +Author: Arthur Schreiber +Date: Thu Oct 4 08:43:55 2007 +0200 + + Fixed some String#slice specs. + +commit b9be176a9e64669f2a787c9bdebc1ba30e344d97 +Author: Paul Meserve +Date: Thu Oct 4 01:37:56 2007 -0400 + + adding String#each_char + +commit c1b17108a78a4dc5d3e224158f8f9d76232003e6 +Author: Charles Comstock +Date: Wed Oct 3 17:42:06 2007 -0500 + + basic specs for Kernel#sleep + +commit 1fe895518bdccb93991f85a92f53876ed3d4df13 +Author: Charles Comstock +Date: Wed Oct 3 14:16:14 2007 -0500 + + added spec for Kernel.local_variables + +commit 00417283b36dcb58da82c6fc2e9be7580de945b0 +Author: Charles Comstock +Date: Wed Oct 3 14:15:25 2007 -0500 + + added specs for Kernel#global_variables + +commit 5b944520099f129462c3b03fa6ee7d1bb0636fc0 +Author: Arthur Schreiber +Date: Wed Oct 3 14:07:09 2007 +0200 + + Fix String#index specs. + +commit c9cdef77c7fa8dda92c91cee5a47624b9c9dc9e8 +Author: Arthur Schreiber +Date: Wed Oct 3 14:05:34 2007 +0200 + + Fix String#hex specs + +commit 4625a7afe509545f782cd4631632b4d7a58011aa +Author: Yehuda Katz +Date: Tue Oct 2 23:53:20 2007 -0700 + + String#% works with a few exceptions: + + * %u doesn't work (it's aliased to %d for now, as in 1.9) + * There's a weird glitch in Float(10_1_0.5_5_5) that I can't track down + +commit 54ab6f559093e66f78cfa30db8aa6587061552d6 +Author: Martin Kuehl +Date: Wed Oct 3 02:05:41 2007 +0200 + + Extend Kernel#kind_of? specs. + Regenerate CI excludes. + +commit 4583be7e7ed76e5843dcb396f8bae735f341de73 +Author: Martin Kuehl +Date: Wed Oct 3 02:03:58 2007 +0200 + + Remove superfluous whitespace from Object#kind_of? specs. + +commit 5c237626469f4b0f4d227916752dd2e03510fcf9 +Author: Martin Kuehl +Date: Wed Oct 3 01:50:33 2007 +0200 + + Remove superfluous comment from Object#kind_of? specs. + +commit 7a2c673d04b0d0506e89073ff231104e21c3304c +Author: Martin Kuehl +Date: Wed Oct 3 01:47:56 2007 +0200 + + Simplify Kernel#freeze specs. + Move Object#extend vs. frozen? spec to extend_spec. + Rebuild CI excludes for Object#extend specs. + +commit 3f6a27603b0b1f91ce32b9ff2a5fe3222fa7220b +Author: Brian Ford +Date: Tue Oct 2 17:03:37 2007 -0700 + + Commit Charles Comstock's language return specs. + +commit ad4e7affcbaae4f0e967c97da485313168595a5e +Author: Martin Kuehl +Date: Wed Oct 3 01:36:27 2007 +0200 + + Fix Kernel#caller. + New core/kernel.rbc. + Regenerate CI excludes for Kernel#caller. + +commit 73b2ef1c889c940d22da6ad6bb8882eef66592fa +Author: Martin Kuehl +Date: Wed Oct 3 01:34:10 2007 +0200 + + Add spec for checking the default argument value for Kernel#caller. + +commit 180ecd6a7fa1d32e1932a322b1f8f82efd558e7f +Author: Martin Kuehl +Date: Wed Oct 3 01:32:29 2007 +0200 + + Add spec for checking that Kernel#caller returns nil. + +commit 9ad0c1428df70c9fd9e0081651e3b60cf5773267 +Author: Martin Kuehl +Date: Wed Oct 3 01:30:46 2007 +0200 + + Add spec for checking the argument handling of Kernel#caller. + +commit 7a9483b823115b3122a4e42b21dfcb5b0f369a54 +Author: Martin Kuehl +Date: Wed Oct 3 01:23:17 2007 +0200 + + Refactor the tedious part of the Kernel#caller specs. + +commit e204755859e4dc147217816ab3bf587db3d51dd6 +Author: Martin Kuehl +Date: Wed Oct 3 01:22:26 2007 +0200 + + Cleanup description of Kernel#caller specs. + +commit dadbbb7930b62a4a6e47c8c32a4d9f26fcea38b4 +Author: Martin Kuehl +Date: Tue Oct 2 23:52:49 2007 +0200 + + Regenerate CI excludes for spec/core/kernel. Again. Because I fixed the typos. + +commit 4a3587da37fd27effb30356fdd2e496f8c898be7 +Author: Martin Kuehl +Date: Tue Oct 2 23:45:33 2007 +0200 + + Fix CI specs to pass in MRI 1.8.6. + +commit a46f5085f3c2a4849fe709044e447e5d6dacda4f +Author: Martin Kuehl +Date: Tue Oct 2 23:27:51 2007 +0200 + + Fix typos in spec/core/kernel specs. + +commit cb3d5867ec91a59a2a75136eb5210a10540b0ce0 +Author: Martin Kuehl +Date: Tue Oct 2 23:27:23 2007 +0200 + + Regenerate spec/core/kernel excludes. + +commit 22e6fe0ef8dbf13aa01124447e09c9dc96f63fe3 +Author: Martin Kuehl +Date: Tue Oct 2 23:26:51 2007 +0200 + + Fix Kernel#Array spec: don't depend on the exception message. + +commit 7b79130f38925cb48712be617cef5a80c71f0ba4 +Author: Brian Ford +Date: Tue Oct 2 14:29:42 2007 -0700 + + Commit #5 (LH) Charles Comstock Process.wait2 spec. + +commit 859c119a48909030f29a2085fbe0a80ed96d2408 +Author: Martin Kuehl +Date: Tue Oct 2 20:57:31 2007 +0200 + + Fix Float#to_i and add a spec to catch the old misbehaviour. + +commit f2b5b2304588b4fb0efd9818c79a4b9774b2c850 +Author: Arthur Schreiber +Date: Tue Oct 2 19:07:49 2007 +0200 + + Fix some String#scan specs. + +commit 8e0ce11df329181efa440cbd55f29848e12188bf +Author: Kevin Clark +Date: Tue Oct 2 00:39:15 2007 -0700 + + Add error handling for Dir.mkdir/rmdir + + Add aliases for Dir.delete/unlink + +commit 20e66dd965bfceb29e4939090a0fd543d05392a3 +Author: Brian Ford +Date: Tue Oct 2 00:21:49 2007 -0700 + + Commit #206 Jason Yates' UnboundMethod specs. + +commit 00d7d22b7d106c6aac5d9664cb444e14811171b0 +Author: Yehuda Katz +Date: Mon Oct 1 17:57:29 2007 -0700 + + First pass at actually getting my modulo impl working + +commit c90766a09c7e1fe7a2261f8b09d9caa8eaf2214e +Author: Arthur Schreiber +Date: Mon Oct 1 21:21:34 2007 +0200 + + Fix the String#crypt spec. + +commit a0f6f8e51a6f7a65230f8f2ea53587ccb09f9270 +Author: Arthur Schreiber +Date: Mon Oct 1 20:18:29 2007 +0200 + + Changed the String#to_f specs a bit. + +commit 21d43e565bb55a31b45dc9fabbefface156ec516 +Author: Wilson Bilkovich +Date: Mon Oct 1 16:13:28 2007 -0400 + + Add failing Array#pack spec for use case taken from Mongrel + +commit b1d70b4a847fc1c8df3eb4a219c4318420121e82 +Author: Brian Ford +Date: Mon Oct 1 09:04:03 2007 -0700 + + Commit #205 Jason Yates' Method specs. + +commit de7e0f0183d072f101e0781635fc2fdb1af1b851 +Author: Martin Kuehl +Date: Mon Oct 1 00:11:12 2007 +0200 + + Fix compiler specs to expect sret when appropriate. + Rewrite compiler specs for multiple assignments. + Regenerate CI excludes for compiler specs. + +commit 1d1e704306fca4453d600259e87175dcdc9de314 +Author: Martin Kuehl +Date: Sun Sep 30 23:27:12 2007 +0200 + + Add edge cases for File#extname specs. + +commit 108d757e6c24447b89fa785b2bf091b72d29933d +Author: Martin Kuehl +Date: Sun Sep 30 22:34:15 2007 +0200 + + Regenerate CI excludes for Dir specs. + +commit 20c6c3cd9b5d59b9782b702ac6afeb828e895d5f +Author: Martin Kuehl +Date: Sun Sep 30 21:49:52 2007 +0200 + + Make Dir specs pass in MRI. + +commit dcd172338bce5b70cb367db3c1e6f4653c05f9e8 +Author: Martin Kuehl +Date: Sun Sep 30 21:10:36 2007 +0200 + + Fix case comparison with Symbols. + +commit 2a45cd71d1eb90a7c11ff62d81371df9479b0d43 +Author: Martin Kuehl +Date: Sun Sep 30 13:28:32 2007 +0200 + + Add specs for String#tr! and String#tr_s!. + Cleanup specs for String#tr! and String#tr_s!. + Regenerate CI excludes for String#tr! and String#tr_s!. + +commit 6418bd672c8ea031d8ac2364c8a98bb631e53deb +Author: Martin Kuehl +Date: Sun Sep 30 13:26:19 2007 +0200 + + Add specs for multiple asignments with splats vs. Array#dup. + +commit 10b04881d286acab9dc97147750743e03ee4509f +Author: Martin Kuehl +Date: Sun Sep 30 13:25:14 2007 +0200 + + Fix Class.new to raise TypeError when the superclass is not a class. + +commit 0b9debba0672305f8551f5d5f35cdd3aaf16c1f1 +Author: Martin Kuehl +Date: Sun Sep 30 13:20:49 2007 +0200 + + Add specs for the names of classes generated with Class.new. + +commit e15cef6eb851318838351a4d8717b708bb09d31d +Author: Martin Kuehl +Date: Sun Sep 30 13:15:43 2007 +0200 + + Remove debugging output from spec/core/dir/chdir_spec.rb + +commit f3251ba0e8ade79f157bc02dca550481488bf888 +Author: Martin Kuehl +Date: Sun Sep 30 13:15:17 2007 +0200 + + Remove debugging output from spec/core/extensions/rubinius/options_spec.rb + +commit 5d4d2abaf02aef6caa7e532208da5e5f57bc6373 +Author: Wilson Bilkovich +Date: Sun Sep 30 20:54:14 2007 -0400 + + Working Socket implementation and specs. Still needs readpartial to support Mongrel. + +commit b514e53f589d509c287516d8dd985f96e66d9a1c +Author: Wilson Bilkovich +Date: Sat Sep 29 15:44:06 2007 -0400 + + Working IPSocket#peername implementation + +commit 965ed2d88527ae8aa4ac962e8ca84180f61e6345 +Author: Wilson Bilkovich +Date: Mon Sep 24 02:34:26 2007 -0400 + + Yet another interim socket commit + +commit ecd54e981a1098c4b3abf14587212e7d1d9049a6 +Author: Wilson Bilkovich +Date: Thu Sep 20 16:29:21 2007 -0400 + + Another interim Socket commit + +commit 351bbdf08f190e24328328df7b3f995b8dc27a9f +Author: Wilson Bilkovich +Date: Wed Sep 19 22:17:52 2007 -0400 + + Interim commit of Socket work so I can generate a patch + +commit 65a73cdfea95c5991f2044bee150e53643216ad3 +Author: Arthur Schreiber +Date: Wed Sep 26 13:15:45 2007 +0200 + + Added some Marshal#load and Marshal#dump specs. + +commit 89e1b91c606dfe18581c3ed3923340b952471d8e +Author: Arthur Schreiber +Date: Wed Sep 26 13:15:19 2007 +0200 + + Extended Specs for Kernel.Float and Kernel.Array + +commit 5d46933362b8c54cb5d0370bbf61e063459de514 +Author: Arthur Schreiber +Date: Wed Sep 26 13:14:41 2007 +0200 + + Added specs for Symbol.all_symbols. + +commit b3324808584d7b4ee6af58d98eeb7c2162c31208 +Author: Brian Ford +Date: Thu Sep 27 10:57:46 2007 -0700 + + Commit #180, Jason Barton's specs for Module#undef_method, #remove_method. + +commit 2e1219fc03d9cb673074ee34b1f8af4bdffe9c0e +Author: Arthur Schreiber +Date: Thu Sep 27 18:49:26 2007 +0200 + + Fix Kernel.Integer by making use of String#to_inum. Add some more Kernel.Integer specs. + +commit b0d4747cab49f4a17e9899392171087d7b67f687 +Author: Arthur Schreiber +Date: Thu Sep 27 18:23:42 2007 +0200 + + Fixed String#to_i. Added String#to_inum. Extended String#to_i specs. + +commit 2799c392b3f5383e9e74745ceb9cf7a52f82918b +Author: Paul Meserve +Date: Thu Sep 27 01:20:00 2007 -0400 + + fix for Struct#new and a small struct spec changes + + (also re-ran bin/ci on struct specs - most of the changes were from previous commits though) + +commit 30d9bf1f6ef9dcff067d427d6226bbce985f5e69 +Author: Paul Meserve +Date: Wed Sep 26 19:08:37 2007 -0400 + + raise proper error when passing non-block args to Enumerable#all? + +commit 1ef1e0ef65ef3c86558da3d313dee5cada6dd4c5 +Author: Paul Meserve +Date: Wed Sep 26 17:25:17 2007 -0400 + + adding alias for Float#quo to fix a couple number specs, and some modifications to Enumerable#min/#max, along with a couple new spec assertions. fixes failing specs and implementation should be a lot closer to MRI + +commit 547dd89791d92f061afcaef7184f054affae871d +Author: Brian Ford +Date: Wed Sep 26 09:46:50 2007 -0700 + + Fixed placement of after(:each) block in numerous File specs. + +commit fa3dcbfdd623a7a7cdb15bc29b38ae47bb4056d5 +Author: Arthur Schreiber +Date: Tue Sep 25 20:25:36 2007 +0200 + + Added specs for UnboundMethod#arity + +commit 33783408b8ce1bdfcd205fd02bc3848119a632cc +Author: Arthur Schreiber +Date: Tue Sep 25 18:00:10 2007 +0200 + + Added specs for Class. + +commit 0edea3c3a7dda5c453c527b2cef3ffba1eef1396 +Author: Arthur Schreiber +Date: Tue Sep 25 17:25:40 2007 +0200 + + Created some specs for Proc. + +commit 62c92f1c3aef6c2ff7ab8cbcd49eefb236d5caed +Merge: b79d04d... 2d9c698... +Author: Brian Ford +Date: Wed Sep 26 01:09:42 2007 -0700 + + Merge branch 'dir' + +commit 2d9c69848f4ca34685b95b07e17d1b5fe1ec2391 +Author: Brian Ford +Date: Wed Sep 26 01:09:28 2007 -0700 + + Updated CI spec excludes for Array, Dir, Fixnum, Enumerable, Hash. + +commit b79d04db673d9b7b5cc47f2918bccf1b0400bdbd +Author: Brian Ford +Date: Wed Sep 26 00:56:01 2007 -0700 + + Commit crayz's patch from #195, #196, #197. + +commit ec960578671a327469d9545d6ced827736ceafa0 +Author: Brian Ford +Date: Tue Sep 25 19:40:54 2007 -0700 + + Fixed Dir specs failing MRI after conversion. + +commit 00b398352ed0f4cbcd326d56b7f8a4469056ee0d +Author: Brian Ford +Date: Tue Sep 25 00:54:06 2007 -0700 + + Many cleanups of Dir specs. + + Added Dir specs fixture directories and files. + +commit b75cfa7c0a9871dc34b8b315ca2311e65000b2f3 +Author: Brian Ford +Date: Sun Sep 23 00:41:36 2007 -0700 + + Converted Dir.glob specs. + +commit 3d8ea2a55e67cc6cfb85d2f4f7845a45984f6504 +Author: Martin Kuehl +Date: Tue Sep 25 21:52:54 2007 +0200 + + Fix a few Struct specs. Failures are down to 15. + +commit a477ff678e9f5f39d3d2b94e559c77b34f0c56c5 +Author: Martin Kuehl +Date: Tue Sep 25 20:51:38 2007 +0200 + + Fix compiler warnings in Subtend spec extensions. + +commit db3e1be4e25b7e8cc463443d050afe9a5acaa7de +Author: Arthur Schreiber +Date: Mon Sep 24 22:31:03 2007 +0200 + + Extended Fixnum specs. + +commit d136b779736af52d1eac08a40814ab4a47de93b3 +Author: Yehuda Katz +Date: Sun Sep 23 17:47:30 2007 -0700 + + Array#each explosion spec + +commit 8a24a71ff13aac465f7f4a14587981c3c23dc800 +Author: Arthur Schreiber +Date: Sun Sep 23 20:06:04 2007 +0200 + + Extended Comparable specs. + +commit ea9ba046ddfe91601d4453972a6d6f8fce96c392 +Author: Arthur Schreiber +Date: Sun Sep 23 12:17:17 2007 +0200 + + Fix Numeric#divmod and Fixnum#divmod. + +commit 1c5ecb9d2e9066c66b9f0625d65cc4cefaee1f83 +Author: Arthur Schreiber +Date: Sun Sep 23 12:00:16 2007 +0200 + + Fix Float#to_i for infinite, negative values. Fix Numeric#/ and Numeric#div. Add Numeric#do_coerce. + +commit 2d67d024e11e887eb07622963bfc36b0ec377746 +Author: Yehuda Katz +Date: Sat Sep 22 20:08:38 2007 -0700 + + Added Onig 5 and got rindex working with it + +commit adc26eb525447010e28fc884eaa54b9d2228f4d6 +Author: Arthur Schreiber +Date: Sun Sep 23 01:35:11 2007 +0200 + + Fix Kernel.Float specs + +commit 9a2ecf258fee3bda410776b3d3b77366590d64fe +Author: Arthur Schreiber +Date: Sat Sep 22 23:18:46 2007 +0200 + + Extended more Fixnum specs. Removed the spec/fixnum/induced_from_spec.rb. + +commit 53b0042824bb1b1c523d790cb3645aec6b789abe +Author: Arthur Schreiber +Date: Sat Sep 22 22:35:53 2007 +0200 + + Extended many Fixnum specs. + +commit 8661cdb78cd2c4afa0fb231aa9cc959e338e097e +Author: Arthur Schreiber +Date: Sat Sep 22 22:35:11 2007 +0200 + + Extended nil#to_s spec. + +commit 4008d8b39032ffa5667e95fb445ff816b1428330 +Author: Arthur Schreiber +Date: Sat Sep 22 14:25:27 2007 +0200 + + Added some more specs and fixed some bugs in Range#initialize, Range#step and Range#each. + +commit 254e3d57dc6e859616ca7e0c44058d4b73211f68 +Author: Arthur Schreiber +Date: Sat Sep 22 11:31:02 2007 +0200 + + Some updates to Range specs. + +commit b87e28bfc06e81fe5c4c3d6e285947f635f79f61 +Author: Brian Ford +Date: Fri Sep 21 20:12:18 2007 -0700 + + Commit wycats Regexp.regexp_match_region primitive. + +commit eaa56811836b4b5ed09a5e26d00f26eb004f2853 +Author: Brian Ford +Date: Fri Sep 21 18:44:25 2007 -0700 + + Added StringValue to some File methods. + + Upated File CI spec excludes. + +commit 83f7b6020dbc881fbd6bd13da6ebb049d6080c2c +Author: Brian Ford +Date: Fri Sep 21 17:54:53 2007 -0700 + + Upated spec excludes for Float, Fixnum, and Math. + +commit af0b0c8da8357fcae7437f6cdfd7797f03ffd73b +Author: Yehuda Katz +Date: Thu Sep 20 22:10:54 2007 -0700 + + Fixed a number of string-related issues: + + String#inspect respects $KCODE + String#inspect returns tainted subclass + String#dump is no longer a copy of inspect, and does not respect $KCODE + String#match tries to call #to_str if it can before throwing an error + +commit 9409ace6d3e97946d10f9f7fcefa69ebcae43c47 +Author: Yehuda Katz +Date: Thu Sep 20 16:55:11 2007 -0700 + + String#index works + + Tweaked spec because [[x,y], [x,y]].each{|x,y| ... } wasn't working + +commit 6a72e4c4defef170f53a31c930c112022934dbc4 +Author: Wilson Bilkovich +Date: Thu Sep 20 15:22:28 2007 -0400 + + Correct StringIO#reopen specs and implementation. Submitted patch for 1.8.x stable. + +commit 2b10dd99c2de4f97b5faa45060eba929d02052c4 +Author: Marcus Crafter +Date: Wed Sep 19 22:20:33 2007 +0200 + + Implemented File.identical? + +commit 2c3c1fc7bd2f6365b28262cae46872eb0925c2e7 +Author: Marcus Crafter +Date: Wed Sep 19 21:48:13 2007 +0200 + + Implemented File.link. + + Kudos to the Frankfurt Rails User Group! :) + +commit 0330b22006e39b5b173c78800f412831296de59a +Author: Yehuda Katz +Date: Thu Sep 20 01:32:43 2007 -0700 + + String#gsub works correctly + + There was a weird segfault issue which I tracked down to a use of gsub inside of gsub. + I got things working by extracting that functionality into a mini-gsub for just that + use case, but we should fix it. + +commit 6c825ce63ca0eb7d6f882a767ee0e6a597219883 +Author: Wilson Bilkovich +Date: Wed Sep 19 22:14:31 2007 -0400 + + Discover and fix an edge case in StringIO + +commit 6bd7adcfa115f11829a7efe8f526fabbb56d5c4c +Author: Brian Ford +Date: Tue Sep 18 19:00:20 2007 -0700 + + File.fnmatch(?) now passes all existing specs. + +commit f626f4199b88ca09a2ba75127270c0bec2ec2c86 +Author: Yehuda Katz +Date: Mon Sep 17 15:04:44 2007 -0700 + + Adds a new match_all primitive + + * will be used as the base of regex-related String functions + * is called match_all instead of scan because it's more primitive than Ruby's scan + +commit 0b42d4d2610ec36a4ae5e21c37d5b587f2b9dcf8 +Author: Wilson Bilkovich +Date: Mon Sep 17 02:06:50 2007 -0400 + + Avoid using $/ in IO#puts and StringIO#puts + Additional StringIO specs and fixes for failures + +commit 75969031a57bea50e4a6450bbc9ae9e5adf76fa7 +Author: Brian Ford +Date: Sun Sep 16 18:26:29 2007 -0700 + + Replaced File.fnmatch FFI version with custom version. + + The custom version is needed to be as compliant as possible + with MRI on different platforms. + + Lots of fixups to File.fnmatch specs. + +commit 18d098062cb1b996a571a946740eea7c52421e12 +Author: Wilson Bilkovich +Date: Mon Sep 17 01:11:45 2007 -0400 + + Some failing sprintf specs and then the fixes for said failures + +commit 84f94ab41d72012e4ec3d0d236b183fd8a51fbe0 +Author: Wilson Bilkovich +Date: Mon Sep 17 00:53:30 2007 -0400 + + Additional StringIO specs and fixes for the failures that arose + +commit 9b44df55c682f239f036d46efe45edc2190a7345 +Author: Wilson Bilkovich +Date: Sun Sep 16 12:55:27 2007 -0400 + + Add StringIO spec for $/ global handling + +commit dfcba62eb69f88d373359c75c3fa7fe827e24c69 +Author: Yehuda Katz +Date: Sun Sep 16 00:43:41 2007 -0700 + + Fixes string/equal_spec to pass + +commit 5026350a166b94fc5fffff70dae510fa2abf2094 +Author: Yehuda Katz +Date: Sun Sep 16 00:20:52 2007 -0700 + + String#slice works + +commit a4f3aa09d3aeb8d2b0a640ca9f659a5945692e04 +Author: Brian Ford +Date: Sat Sep 15 13:49:42 2007 -0700 + + Added ability to read/write to pointer to int or double in FFI. + + * Fixed Math.frexp to use pointer to int to return exponent. + * Completed Math specs. + +commit 1a88ca4def8d7aa566a4254eebee3236a1359fc8 +Author: Wilson Bilkovich +Date: Fri Sep 14 20:57:01 2007 -0400 + + mini_rspec.expectation_messages.yak_shave! + +commit 64f53c8b40b3a80b41a2c27b4ac7255b7aad5f4d +Author: Wilson Bilkovich +Date: Fri Sep 14 02:18:16 2007 -0400 + + Hopefully full StringIO coverage now + +commit 7cf9fe62bc9a5a00ae69ed3cac82e50012f3bb69 +Author: Wilson Bilkovich +Date: Fri Sep 14 00:54:33 2007 -0400 + + Yet more StringIO specs + +commit 92da0550bd32db984fbb54f105b9701867d4faf9 +Author: Wilson Bilkovich +Date: Fri Sep 14 00:38:55 2007 -0400 + + More StringIO specs + +commit 29826669197f44850d323910c7e60897e1ef7796 +Author: Wilson Bilkovich +Date: Fri Sep 14 00:23:45 2007 -0400 + + Beginnings of StringIO specs + +commit 0c19e3557125dd366ddd119a34451715bfe5e7a1 +Author: Evan Phoenix +Date: Wed Sep 12 18:11:49 2007 -0700 + + Fixed object allocation bug and Bignum spec + +commit e42a1b960f530a987527d8795a98b2de18fea824 +Author: Wilson Bilkovich +Date: Thu Sep 13 19:19:03 2007 -0400 + + Re-implement Module#define_method. Passes existing specs. + +commit a8b1a148e5fbfeb3c91558fd6caccc95006a5617 +Author: Charles Nutter +Date: Thu Sep 13 03:55:15 2007 -0500 + + Adding a non-compiling spec to case_spec and updating core.rba that didn't seem to get updated correctly. + +commit bbd682ba2e12ba5907fe2edf2f14f11fb110cac8 +Author: Wilson Bilkovich +Date: Thu Sep 13 04:04:33 2007 -0400 + + Fix anonymous 'rest' arguments + Suppress stray STDOUT traffic from 'defined?' + +commit 8f11498019eb49a4dd8bf52c4361432ebb1175d5 +Author: Brian Ford +Date: Wed Sep 12 22:21:30 2007 -0700 + + Updated CI process. + + * Changed bin/ci to generate an exclude file per file put + in .spec directory. + * Generated CI spec excludes files. + * Updated .gitignore to not exclude .spec directory. + * Moved the critical excludes file to spec/excludes.txt + +commit b736263ff325efabb907f300c1c69a2e63bd5620 +Author: Brian Ford +Date: Wed Sep 12 22:21:30 2007 -0700 + + Updated CI process. + + * Changed bin/ci to generate an exclude file per file put + in .spec directory. + * Generated CI spec excludes files. + * Updated .gitignore to not exclude .spec directory. + * Moved the critical excludes file to spec/excludes.txt + +commit 58ff9428a2a20e93e3682f834e32f754ed2c47d4 +Author: Brian Ford +Date: Wed Sep 12 18:39:48 2007 -0700 + + Fixed specs failing MRI. + +commit f54b1dffb9372e5cb1c71d93c67f2407fce0a1d0 +Author: Brian Ford +Date: Wed Sep 12 17:17:46 2007 -0700 + + Fixes to specs failing MRI for hash, float, fixnum, file, enumerable. + +commit e1d359eec451a69deb67ffdedd09b86d00774cc2 +Author: Brian Ford +Date: Wed Sep 12 14:34:50 2007 -0700 + + Fixes to Hash specs based on Ruby version differences. + +commit ee5eec5d32bb42fbf549068905ddebe215fbcf70 +Author: Brian Ford +Date: Wed Sep 12 08:53:47 2007 -0700 + + Fixed failing specs in kernel, module, numeric, string. + +commit 4f0af824f132428762f1e06409ca16c1022867bc +Author: Brian Ford +Date: Wed Sep 12 06:24:16 2007 -0700 + + Added #platform and #version guards for specs. + +commit 355c602704cd402a1d7cbadc9b4d8fae0b34f1f4 +Author: Brian Ford +Date: Tue Sep 11 00:35:03 2007 -0700 + + Misc changes to specs to cleanup after the breakup. + +commit 85336c6a83736b01d63b645baf0e7e18bb5ce569 +Author: Brian Ford +Date: Mon Sep 10 23:21:24 2007 -0700 + + Converted exception, kernel, struct, object specs. + +commit e10bc8cbbaa26123724dad9f97f44d82e8cbf600 +Author: Brian Ford +Date: Mon Sep 10 21:48:15 2007 -0700 + + Converted string and numeric specs. + +commit 8ec64d24811a7951756c840c98a66a7c7d2ae7c8 +Author: Brian Ford +Date: Mon Sep 10 18:35:18 2007 -0700 + + Converted array and module specs. + +commit 2849f4a41b3fbda6c626d934bbf3d7476ea31848 +Author: Brian Ford +Date: Mon Sep 10 15:22:36 2007 -0700 + + Converted enumerable, file, hash, process, regexp, thread. + + Fixed bin/mkspec to remove '=' from string for file name. + +commit ffa5328aa8ed7ea079c0cc8b4228ababa5919cf6 +Author: Brian Ford +Date: Mon Sep 10 13:16:14 2007 -0700 + + Converted time and bignum specs. + +commit 7798952047471d28a8e12a796092c4df7ae002f2 +Author: Brian Ford +Date: Mon Sep 10 01:21:38 2007 -0700 + + Converted range, math, matchdata, integer, float, fixnum, comparable, io. + +commit 423d85f4a7eb4b40d2eea83a462f5c38c4a6aee3 +Author: Brian Ford +Date: Sun Sep 9 23:43:14 2007 -0700 + + Added dir and files for ENV. Converted true, false, nil, symbol, process. + + Added .spec to .gitignore. + +commit 8274bdcd0c747c21806065feb743e7794231f48f +Author: Brian Ford +Date: Sun Sep 9 22:40:37 2007 -0700 + + Converted kernel specs. + +commit bc1917d630d7938b62a866c3825dfa08e5ec99e1 +Author: Brian Ford +Date: Sun Sep 9 21:57:03 2007 -0700 + + Initial create of spec/core subdirectories and files. + + Updated bin/mkspec to exclude Exception subclasses and + OptionParser (which is in Object.constants because of + the script requiring it). Also normalize TrueClass etc. + to directory 'true'. + +commit b941eceb681c57d23d35f952b11b2a2d3a1ea4dd +Author: Wilson Bilkovich +Date: Thu Sep 13 01:00:22 2007 -0400 + + Add a minimal spec for the 'undef' keyword + +commit 3af389594f3828763a85d8eef65f773b183e1b46 +Author: Charles Nutter +Date: Wed Sep 12 19:33:36 2007 -0500 + + Adding a bunch of default argument specs to language/def_spec. + +commit 6b4936e834a2814602be54f01e08dcdc1f9433b5 +Author: Arthur Schreiber +Date: Sun Sep 9 16:58:35 2007 +0200 + + Another fix for multiple mock expectations on the same method. + +commit f686ff256289263eb473249dd734cf2214c41cc2 +Author: Arthur Schreiber +Date: Sun Sep 9 16:13:53 2007 +0200 + + Mocks now support multiple expectations of the same method with different arguments. + +commit 860e0d08adc8cdee9ac4d9ff3bd0e30d5d3aaa49 +Author: Brian Ford +Date: Sat Sep 8 02:36:19 2007 -0700 + + Added to critical-excludes and ci-excludes. bin/ci -f s -C runs to completion. + + rake build:core compiled string.rb, so checking in core.rba. + +commit c9c79c910a57e5628d1743f3b440c0066875500e +Author: Brian Ford +Date: Fri Sep 7 23:24:44 2007 -0700 + + Added Math methods using FFI. + + Added Kernel.coerce_to and rewrote Float(), Integer(), + Array(), and String() with it. Float() needs to be fixed + to raise on strings like rb_cstr_to_dbl does. + + Fixed -C options for bin/ci and bin/mspec. + +commit b8d8b8c8475fde1ce3519e29788a34780dffae8c +Author: Yehuda Katz +Date: Fri Sep 7 19:40:02 2007 -0700 + + Fixes String#<< + + * Added taint if other has taint + * Fixed 10 spec failures + +commit b0b85547ab9dd16ba88a75c64a91c3ae0d079b27 +Author: Yehuda Katz +Date: Fri Sep 7 15:36:44 2007 -0700 + + Added bus error to critical-excludes + +commit ed13a10112d0a262a48c8e5db7d1eaaa3e076e55 +Author: Yehuda Katz +Date: Fri Sep 7 15:19:51 2007 -0700 + + String specs work in 1.8.6 MRI + +commit 6fc507c96e990139c311900c73c7e31447879071 +Author: Brian Ford +Date: Fri Sep 7 12:03:09 2007 -0700 + + Changed VERSION and RUBY_VERSION to shadow MRI (currently 1.8.6). + + Added RBX_VERSION (currently 0.8.0). Updated loader -v to display + RBX_VERSION and RUBY_VERSION and truncated BUILDREV. + + Enabled before|after(:all) for mini_rspec. + +commit 8ce602f80b35f5859c58730968a9a7053a87bd59 +Author: Eero Saynatkari +Date: Fri Sep 7 11:16:16 2007 -0400 + + Array#uniq, #uniq! simplified. 72 failures. + +commit 38f271a1b7d49074d8db9285553756fb75ffe78b +Author: Yehuda Katz +Date: Fri Sep 7 11:23:36 2007 -0700 + + define_attr added to subtend + +commit 843706d585334c30943c8bbdd3ef6ca22297d42d +Author: Yehuda Katz +Date: Fri Sep 7 10:48:51 2007 -0700 + + Added rb_cstr2inum to subtend + +commit 97a22e2144b623a62780995333d65986c98c4ba2 +Author: Yehuda Katz +Date: Fri Sep 7 10:42:30 2007 -0700 + + Added rb_const_defined to subtend + + It also seems that my fixture for require didn't make it in; adding that as well + +commit 0ac9ec2b0f381bf2fb3a36cd0b6f30748771e818 +Author: Yehuda Katz +Date: Fri Sep 7 10:21:28 2007 -0700 + + Added check_*_type to subtend + + * check_array_type + * check_string_type + * check_convert_type + +commit b2bf1c44fa7c2663e6fc0b27127aa4f5e38e073f +Author: Yehuda Katz +Date: Fri Sep 7 09:45:03 2007 -0700 + + Added rb_attr_get to subtend + +commit 81605662ab8acc5a50536f1fc613e7d24e142df1 +Author: Yehuda Katz +Date: Fri Sep 7 00:27:38 2007 -0700 + + int2inum added to subtend as well as INT2NUM + +commit e80084e6b245173c17403891c65d86db1e6b3022 +Author: Yehuda Katz +Date: Fri Sep 7 00:02:06 2007 -0700 + + rb_str_split added to subtend + +commit 90f1fa95825caa8b21e147248d1a7d999579b937 +Author: Yehuda Katz +Date: Thu Sep 6 23:51:56 2007 -0700 + + rb_require in subtend added + +commit 5bb87f516b2a26f77a864a15636331102d6d8499 +Author: Yehuda Katz +Date: Thu Sep 6 22:58:29 2007 -0700 + + rb_to_id added + +commit e0532b3fb62089da7b7362ec2232997878a1221d +Author: Yehuda Katz +Date: Thu Sep 6 22:31:30 2007 -0700 + + Added specs + +commit 75f7a1d2b37067f55099dd117e8fcd905baa748d +Author: Yehuda Katz +Date: Thu Sep 6 21:59:12 2007 -0700 + + string subtend fixes + + * rb_str_cmp added + * rb_str_cat fixed with working spec + +commit f058cea3e5291c349f5b2b1cfbdad4d734240f95 +Author: Yehuda Katz +Date: Thu Sep 6 21:34:52 2007 -0700 + + added rb_define_const + +commit ec7ca7f45ef32794afb919851e4bfd5e8d7aa46d +Author: Yehuda Katz +Date: Thu Sep 6 21:03:44 2007 -0700 + + rb_include_module added + +commit 5b3471544508e973ba6afbd16daf47f5796f8b30 +Author: Yehuda Katz +Date: Thu Sep 6 20:41:09 2007 -0700 + + float_new added and some functions missing in ruby.h added + +commit cd2af0bae996a4addfe23baa0558125a8a5523e7 +Author: Evan Phoenix +Date: Thu Sep 6 17:09:06 2007 -0700 + + Fixed subtend. Added diagram of the context chain. + + NMCs (NativeMethodContext) now use the proper context stack. + The stack maintenance was all screwed up when calling in and out of + native methods, which was the source of a few problems. + +commit da5f9e6f942c11d906760e952debae4d05b3d872 +Author: Brian Ford +Date: Tue Sep 4 22:26:48 2007 -0700 + + Added load-order dependency generation to rake build:(core|platform) task. + + Added 'depends on:' declarations to kernel/platform and kernel/core files. + + Updated ci-excludes.txt to reflect recent spec checkins. + +commit 7b1ca6f305e33b34a99e8c9e049843a76cceeca7 +Author: Wilson Bilkovich +Date: Sun Sep 2 21:29:49 2007 -0400 + + Add more 'alias' specs. Show singleton methods in 'public_methods' output. + +commit 5c0b5fcb2a0c9f47a04e6a5d5027484224d0a942 +Merge: 4896039... 01c2126... +Author: Wilson Bilkovich +Date: Sun Sep 2 18:58:53 2007 -0400 + + Merge branch 'master' of git@git.rubini.us:code + +commit 48960394ab7f36ccd1b18609677b40721c30d7a2 +Author: Wilson Bilkovich +Date: Sun Sep 2 18:57:29 2007 -0400 + + Add some 'alias' specs that fail on rbx and pass on MRI + +commit 01c2126b705327d99aa183d51dc014169e8b4e07 +Merge: 04602c6... b6d92ec... +Author: Florian Gross +Date: Sun Sep 2 19:45:25 2007 +0200 + + Merge branch 'master' of git@git.rubini.us:code + + Conflicts: + + spec/core/string_spec.rb + +commit 04602c6756a9199b64e7d909c01dc995b25fa8a7 +Author: Florian Gross +Date: Sun Sep 2 19:32:47 2007 +0200 + + * New specs for String#tr_s(!) and upcase(!) + * Improved specs for String#capitalize!, downcase(!) and swapcase + +commit c94f83b20f7b11dc48c523c84de59b9ac6f76cce +Author: Eero Saynatkari +Date: Fri Aug 31 23:15:07 2007 -0400 + + Array#reverse_each, #rindex fixed and cleaned. 80 failures. + +commit f531f812f87283b950c62648e3cf08a7400c2779 +Author: Eero Saynatkari +Date: Fri Aug 31 22:03:05 2007 -0400 + + Array#replace fixed, specs. 88 failures. * Disabled specs for #initialize_copy which is private. + +commit 1656b8a04a40bc5a43adec88ffd1480d9da6ba28 +Author: Eero Saynatkari +Date: Fri Aug 31 11:50:56 2007 -0400 + + Array#reject, #reject!, specs fixed and cleaned * Added spec to check #reject returning Array and corrected implementation. + +commit e1c499c6feafc19788addd098a6da052904cb09c +Author: Eero Saynatkari +Date: Fri Aug 31 11:25:45 2007 -0400 + + Fixed logic in Array#rassoc, specs. 91 failures. + +commit da79b116d3fdc5fd4cd04f5ad1ad44b2c269ad77 +Author: Eero Saynatkari +Date: Fri Aug 31 10:38:39 2007 -0400 + + Array#push checks frozenness, specs. 92 failures. + +commit 290aa6fe561453821f59be3fa92695d0b0d77c04 +Author: Eero Saynatkari +Date: Fri Aug 31 10:26:25 2007 -0400 + + Uncommented most of Array#pack specs. * float -> int conversions still hang both C and c and are therefore disabled. + +commit 885f2522244c1792f45260194aba085028d5c919 +Author: Eero Saynatkari +Date: Fri Aug 31 09:59:30 2007 -0400 + + Fixed logic errors in Array#assoc, Array#include? * Both, contrary to docs, compare elem == obj, not the other way around. + +commit d825038a409f4d931e80736e2de49ff0752857a9 +Author: Brian Ford +Date: Fri Aug 31 00:12:06 2007 -0700 + + bin/ci supports options for separately running spec files + + Use bin/ci -s to separately process each spec file. Use + bin/ci -m to run all the spec files in a single VM process. + -s is the default for --create, but -m is the default for + everything else. + + Updated ci-excludes.txt and critical-excludes.txt. + +commit 7dfe5cb7936051685a2c79effb6295b9aa179810 +Author: Brian Ford +Date: Thu Aug 30 23:44:28 2007 -0700 + + Updated ci-excludes.txt to only exclude failing specs. + +commit fb09f0a7a6969adffd8d99bf869eb50c91eef097 +Author: Wilson Bilkovich +Date: Thu Aug 30 20:31:20 2007 -0400 + + Fix object and array specs that failed under MRI + +commit 6fb73244537b61a20538c1f3d5a060a40a358be5 +Author: Yehuda Katz +Date: Thu Aug 30 17:08:12 2007 -0700 + + Fixed two typos in the subtend string specs + +commit 00256f41d4e3ebfcdafdc25e27bfbf4bc7d3de3f +Author: Eero Saynatkari +Date: Thu Aug 30 11:19:09 2007 -0400 + + Array#last fixed. 48 failures. + +commit 887d41c64c6bdff693f6ecd8d3078f8453669648 +Author: Eero Saynatkari +Date: Thu Aug 30 11:02:27 2007 -0400 + + Array#insert, specs corrected. 49 failures. + * Fixed Array#insert + * Re-complianced frozenness specs for Array#inspect. + +commit 1bc536e1128bc76b1c9efae593340f67bdcb5fb5 +Author: Eero Saynatkari +Date: Thu Aug 30 10:42:05 2007 -0400 + + Array#indexes correct implementation. 51 failures. + * Array#indexes and #indices is now correct although + both methods are deprecated in favour of #values_at. + +commit 5de09c707b1ce43bf689e8ded9ea19784e77a49e +Author: Eero Saynatkari +Date: Thu Aug 30 10:09:27 2007 -0400 + + Array#include? implemented. 53 failures. + * Replaced use of Enumerable#include? + * Re-complianced to a simpler Array#include? spec to + avoid implementation-dependedness. + +commit 14ca6c2533764eea508b24b0ec89475a7aae5e94 +Author: Eero Saynatkari +Date: Thu Aug 30 09:32:40 2007 -0400 + + Array#hash spec compliance change. 54 failures. + * Disabled an Array#hash spec for Rubinius because it + relies too much on implementation details. + +commit fc4f392fa7fba88b36bfdec61db3acaa1f1fadc2 +Author: Eero Saynatkari +Date: Thu Aug 30 00:42:03 2007 -0400 + + Array#flatten, #flatten!. Improved Array specs. 384 ex, 55 failures. + * Array#flatten, #flatten! implementation improved, they + also work recursively now. + * Re-enabled Array#flatten, #flatten! specs and the + recursive test for Array#inspect. + +commit efeaa622994e9868b9324247b0ff1fd5743792ac +Author: Yehuda Katz +Date: Wed Aug 29 23:29:36 2007 -0700 + + A series of rb_str functions in subtend, plus fixes to some of the tests earlier committed + +commit e4f5281148799ed716065c489d384a42d208290d +Author: Yehuda Katz +Date: Wed Aug 29 19:16:26 2007 -0700 + + rb_str_append() added + +commit a05c376478f7407da4e0aa2a6a7e3de98176a63b +Author: Yehuda Katz +Date: Wed Aug 29 13:25:17 2007 -0700 + + Updated subtend array functions + + * rb_ary_reverse() added + * tests added for rb_ary_join() and rb_ary_reverse() + +commit 779fb97c35b78b9749cbb118fcb555096957e4c6 +Merge: 2793a99... e17987e... +Author: Yehuda Katz +Date: Wed Aug 29 12:40:33 2007 -0700 + + Merge branch 'master' of git@git.rubini.us:code + +commit 2793a9917f8f5cc2f0fc14ba605cec499532e680 +Author: Yehuda Katz +Date: Wed Aug 29 12:40:21 2007 -0700 + + Slightly improved rb_ary_pop() test + +commit c196c60b6cd32c85b18bdab31ee000cf097309b5 +Author: Me +Date: Tue Aug 28 23:49:46 2007 -0400 + + Array#fill fixed, cleaned up. 375 examples, 59 failures. + +commit 7736413f262357479c2f3354a73533fd89b3c9a6 +Author: Yehuda Katz +Date: Tue Aug 28 17:54:28 2007 -0700 + + added rb_ary_join() to subtend + + Trying to get tests working but it's hard to see if I'm correct without the ability to run them. + I'll take care of making sure there are passing tests as soon as I can. + +commit 0effcaf3e948d80ae3ad17b33f0483313d85cdbe +Author: Eero Saynatkari +Date: Tue Aug 28 00:24:41 2007 -0400 + + Array#fetch, specs, slightly cleaned array.rb. 62 failures. + * Array#fetch uses to_int. + * Specs for Array#fetch check for correct block var. + * Removed extra comments from array.rb. + * Removed old implementations from array.rb. + +commit 51737d35c24f853a23e14f7a227138d4d0f6b457 +Author: Brian Ford +Date: Mon Aug 27 21:46:30 2007 -0700 + + Added failing File specs to ci-excludes + +commit a195970e2b2d34fa4388e6a72e91ada13b4b0d32 +Author: Marcus Crafter +Date: Tue Aug 28 09:20:33 2007 +1000 + + Ensure exists? is prefixed by File. + +commit 804b6f3358c1bb73492beaa0e978d4df8dbac138 +Author: Marcus Crafter +Date: Tue Aug 28 00:18:41 2007 +1000 + + Minor refactoring work on file spec. + + Removed duplicated constant tests. + +commit ef18eaaaa1a79b964667900b19f3f10e1b67032b +Author: Brian Ford +Date: Mon Aug 27 14:25:06 2007 -0700 + + Updates to enable bin/ci to run to completion. + + Changed mini_rspec to not use File.open with a block to work + around IO#read failing to catch EOFError. + + Commented out object_spec.rb specs that need to be completely redone. + + Updated spec/reports exclude files to enable bin/ci to work. + +commit 505617b26829d5f489c4488ed934a6dc720f64f0 +Author: Florian Gross +Date: Sun Aug 26 22:53:42 2007 +0200 + + A few new specs for String#sum, #to_i, #to_s, #to_str, #tr and #tr! + +commit a6a24a97dce2a4072a6ea17e48259b76f0c3681a +Author: Eero Saynatkari +Date: Sun Aug 26 02:52:55 2007 -0400 + + Array#delete, Array#delete_at, Array#delete_if. 63 failures. + * Fixed the three delete* methods, they still need clean-up + * The specs reflect difference in frozen handling for rbx and r18 + +commit 063f8c25d45e0934bca236ecb8af36dcb517187f +Author: Eero Saynatkari +Date: Sun Aug 26 02:26:15 2007 -0400 + + Array#concat fixed. 69 failures. + * Array#concat checks frozenness and cleaned up + * Improved specs for #concat + +commit a9f3593593948cf72d94712765d05bfcc27f2e78 +Author: Eero Saynatkari +Date: Sun Aug 26 01:53:05 2007 -0400 + + Array#dup, Array#compact, Array#compact!. 71 failing. + * Array#dup properly returns subclass + * Array#compact(!) improved to pass specs + +commit 7be3bc12ea2e5432e442cb44103b4b1c6d981163 +Author: Eero Saynatkari +Date: Sat Aug 25 23:57:38 2007 -0400 + + Array#clear, Array#frozen?. 372 examples, 73 failures. * Array#frozen? checks for sorting freezes * Array#clear fails on frozen Arrays + +commit 5c958242fe25f8a18cd8d315f81fb3db80dc7a40 +Author: Eero Saynatkari +Date: Sat Aug 25 17:19:23 2007 -0400 + + Array#==, Array#assoc improvements. 75 failures + * Cleaned up Array#== + * Array#assoc processes correctly + +commit f82f8a300ee394f9f1038cc84de1cf6b132d7ef5 +Author: Eero Saynatkari +Date: Sat Aug 25 16:24:22 2007 -0400 + + Array#* improved. 371 specs, 81 failures + * Array#* processes to_int and to_ary correctly and forwards + to #join when needed. + +commit 91e16f06d5b5b16f1fa7ffc1d3673d7f1c681587 +Author: Eero Saynatkari +Date: Sat Aug 25 15:54:12 2007 -0400 + + Array#join can process recursive Arrays. + * Rubinius cannot create recursive Arrays so this is somewhat moot. + +commit 2d7427bb638f1af6d7437beed4beafde5274dbdf +Author: Eero Saynatkari +Date: Sat Aug 25 13:46:45 2007 -0400 + + Array#|, better Array#&. 371 examples, 86 failures. + * Array#& explicitly uses #eql? semantics + * Cleaned up Array#|, uses to_ary + +commit 2b8707466f763662d52efaeab71b4789b132bb40 +Merge: c61b1e5... 76be87f... +Author: Eero Saynatkari +Date: Sat Aug 25 12:41:06 2007 -0400 + + Merge branch 'array' + +commit 76be87f74d352d79425e9c46d3df55678257fda9 +Author: Eero Saynatkari +Date: Sat Aug 25 12:28:32 2007 -0400 + + Array#<< improvements. 371 examples, 89 failures. + * Specced and fixed resizing bug in Array#<< + +commit c61b1e54cc11c297b9e9a9eca70cb6a354ed21d9 +Merge: 3618a8b... c6cc98f... +Author: Pedro Del Gallego Vida +Date: Sat Aug 25 18:16:45 2007 +0200 + + Merge branch 'master' of git@git.rubini.us:code + +commit 3618a8bc588588ef8fb0dcc4753bc42606b86c13 +Author: Pedro Del Gallego Vida +Date: Sat Aug 25 18:16:05 2007 +0200 + + Update object specs + Update the object_spec.rb file + * add more specs + * refactor using it_behave_like + +commit 294e5aacda8a74a9d8f57d05bb433f2fadcd08f1 +Author: Eero Saynatkari +Date: Sat Aug 25 12:07:06 2007 -0400 + + Array#[], modified parts of array_spec. 369 examples, 90 failures. + * Array#[] passes its specs + * Disabled some parts of array_spec while fixing Array. These + will be re-enabled as soon as possible. + +commit 1369465aefcd1d50ddd268ba9af968c62137e2b2 +Author: Pedro Del Gallego Vida +Date: Fri Aug 24 11:14:20 2007 +0200 + + Array#new correct implementation + +commit e321427a52878ef9d9c7c04aa7c3c4f1e3a6c940 +Merge: bff7c05... 69c0407... +Author: Pedro Del Gallego Vida +Date: Fri Aug 24 11:14:20 2007 +0200 + + Merge branch 'master' of git@git.rubini.us:code + +commit 07c7f93a64fc37f3cf94a0a2c272468d015a7fb3 +Author: Brian Ford +Date: Thu Aug 23 21:18:13 2007 -0700 + + Converted Regexp specs to new describe style. + + Fixed String specs to be compatible with bin/completeness. + Fixed bin/completeness to use dotted reporter instead of CI + reporter since the latter no longer outputs summary info. + +commit 6776e1478fa7e78a0944a1ee59c55c3839f51ea4 +Author: Marcus Crafter +Date: Fri Aug 24 13:14:47 2007 +1000 + + Added implementation of File.split and updated specs. + +commit 4053b9076b4b996f544095a75317453967723faa +Author: Marcus Crafter +Date: Wed Aug 22 21:58:13 2007 +1000 + + Added spec for mocking methods on a class + +commit 54cae1196db08f6a734c35079db8df62e491f300 +Author: Evan Phoenix +Date: Thu Aug 23 17:38:40 2007 -0700 + + Added more IO stuff and platform methods. + + I'm still a little unhappy with the input buffer situation. + (ie, there is none.) + + Adds IO#sysread and IO#syswrite as well as a bunch of POSIX stuff. + +commit 12a755004c0a8a0319212965da61385738166f98 +Author: Evan Phoenix +Date: Tue Aug 21 12:44:48 2007 -0700 + + Beginnings of ftools spec, fix backtrace generation. + + Backtraces were failing to be properly built if the sender was a Block. + +commit bff7c05ce12c79ef111422ecf4525f1a65e7a5f0 +Author: Pedro Del Gallego Vida +Date: Fri Aug 24 01:15:41 2007 +0200 + + More specs dor enumerable + * add inject, min, grep, find, detect, find_all, select + +commit ed9a8fefcc384bb6548a7f66bbafb97192ec8fd3 +Merge: 4ef0b9f... 8dd800e... +Author: Arthur Schreiber +Date: Thu Aug 23 11:01:25 2007 +0200 + + Merge branch 'specs' + +commit 8dd800e8189f616dc54390c0ebf96c331de41230 +Author: schreiber.arthur@gmail.com +Date: Thu Aug 23 10:45:56 2007 +0200 + + * Some more Module Specs. + +commit 0f414f56f9050d86011df75e7fd23428fe378996 +Author: schreiber.arthur@gmail.com +Date: Thu Aug 23 10:44:59 2007 +0200 + + * Added :count => :any option to mock expectations + +commit 4ef0b9feddfebfd1b6177fce6e3a1a4077f4f098 +Author: Brian Ford +Date: Wed Aug 22 23:10:22 2007 -0700 + + Updated exclusion list for CI specs. + Updated rake spec:ci task. + Changed ci spec run action to execute all specs in one process. + Added guard on file specs to prevent compilation exception. + +commit c3b61b239fa6a02327e5651513986d998d826eaf +Author: Brian Ford +Date: Wed Aug 22 21:48:08 2007 -0700 + + Updated CI spec process to exclude specs failing on compilation. + + Added critical failures to enable running especially spec/core. + Added failure guards to struct specs. + +commit f339a284c66357bc52749e5fe9c0d59bbbdc7ade +Author: Brian Ford +Date: Mon Aug 20 22:31:56 2007 -0700 + + Fleshed out bin/ci constructs for running specs. + +commit 4f750d59adfff6c1751372c0d2853778dc7ae16d +Author: Pedro Del Gallego Vida +Date: Tue Aug 21 22:40:33 2007 +0200 + + * more enumerable specs + * refactor to it_behaves_like + +commit 3c79d5cf67b40b945602d5c5fa77589e0d7bae2c +Author: Pedro Del Gallego Vida +Date: Tue Aug 21 20:52:16 2007 +0200 + + update file_spec.rb + +commit b2a64089bffe5afb9148a665ecb6e70c3bc62b67 +Merge: 6865b97... 1b6a8a1... +Author: Brian Ford +Date: Sun Aug 19 16:22:53 2007 -0700 + + merge 1b6a8a157 + +commit bf54767922eb8d494c683ed8d57c6ffb5164fc29 +Merge: 6c6032e... 37d71c9... +Author: Brian Ford +Date: Sun Aug 19 15:24:21 2007 -0700 + + merge from e83bcd022 + +commit 05db33909c319231ac375812025ea2378710a299 +Author: Marcus Crafter +Date: Sun Aug 19 21:26:15 2007 +1000 + + Add conditional when deleting a file in after(:each) block to prevent an exception if the file is missing. + +commit 138ab001175987cd38aff092a850e515745f9292 +Author: Marcus Crafter +Date: Sun Aug 19 21:18:22 2007 +1000 + + Converted remaining context/specify spec's into describe/it, and followed class/method naming convention. + +commit 749b883d0260326573c581cc63eab67e1a4bc590 +Author: Marcus Crafter +Date: Sun Aug 19 20:23:11 2007 +1000 + + Added implementation of exists? blockdev? chardev? zero? size size? writable_real? executable_real? readable_real? unlink delete and chmod using ffi where needed. Specs for most of these methods existed already, added specs for those that weren't. Fixed a few typos. + +commit ddcb14f9f2311ec843a1f1f8d2b3fa868384ff0d +Author: Brian Ford +Date: Sat Aug 18 23:19:32 2007 -0700 + + more misc changes to get ci specs working + added alias for File.exists? and File.exist? + added Dir.getcwd + added empty File.delete + +commit afb252fd6170ed051e97f1911e5f1200414ebf98 +Author: Brian Ford +Date: Sat Aug 18 22:20:10 2007 -0700 + + updated compiler specs. + +commit d0e6b658d9065b0fbc9180cd5d19139834f64f59 +Author: Brian Ford +Date: Sat Aug 18 21:04:18 2007 -0700 + + changes to support better CI specs + hat mini_rspec will take a filename as an exclude/include argument and read the actual excludes/includes from the file. + added that mspec will take -o FILE to use an alternate to STDOUT for the spec reporter output. + updated spec tasks. misc spec changes. + added naive implementation of IO#each. + +commit 541bcb521a8ee589c7d28c095ad7ee1489af42db +Author: Evan Phoenix +Date: Fri Aug 17 19:30:05 2007 -0700 + + Complete reorganization of bootstrap. Addition of kernel/platform. FFI fixed. + + New restrictions for meta-programming in core bodies (not in methods). + kernel/platform is now where platform specific code, mainly related to FFI, lives. + A bunch of FFI bugs have been fixed and it should be working much better now. + + FFI Note: you may now only specify :state as your first argument, and you must + leave it off when you call the method (rather than passing nil like before). + +commit af245dfbc80ff942de62408e70db7499a798fb0a +Author: schreiber.arthur@gmail.com +Date: Tue Aug 14 01:30:09 2007 +0200 + + Forgot to add the autoloaded file for Module#autoload + +commit b946940f463028de067ef2e082c96fe431c94b0a +Author: schreiber.arthur@gmail.com +Date: Tue Aug 14 01:09:10 2007 +0200 + + Updated Module Specs + +commit 6cd6aa53a5d20c78941442f7e367ef8c7aee17c2 +Author: Brian Ford +Date: Fri Aug 10 00:14:09 2007 +0000 + + converted array specs with a few edits. + +commit c075f7f70da2a029c69f3fff1f9caec419db64d5 +Author: Arthur +Date: Wed Aug 8 12:47:18 2007 +0000 + + fix a small typo + +commit c7262df9ee1c2544890b001574c8cb0f8ae26a75 +Author: Brian Ford +Date: Wed Aug 8 01:24:25 2007 +0000 + + converted exception specs. added #should_be_ancestor_of. use ExpectationNotMetError in mini_rspec like rspec does. + +commit f591e18978b73c508505db73f274f4bd69c372c5 +Author: Arthur +Date: Tue Aug 7 08:36:15 2007 +0000 + + * String#to_str specs should actually use String#to_str + +commit 80f69571c5378d6bbb2e7a118ada00db66226797 +Author: Brian Ford +Date: Tue Aug 7 06:47:53 2007 +0000 + + converted range specs. + +commit aca62d253a6b2df891ca4ec4b177ea95b621d636 +Author: Brian Ford +Date: Tue Aug 7 03:52:46 2007 +0000 + + fixed mini_rspec -e option, allows multiples. converted hash specs. + +commit 928c9a392102fa7b7945f332480a7477ec203467 +Author: Florian Gross +Date: Mon Aug 6 22:04:16 2007 +0000 + + New specs for String#swapcase(!), to_f, to_i, to_str, to_sym + +commit 6d0a6b0051a55af32743d9d98d6425489a622ebe +Author: Pedro Del Gallego +Date: Mon Aug 6 21:58:55 2007 +0000 + + * more specs for File.open. Specs for File.truncate + +commit 8b19b683a8593b4dd5024841d8023df827a44875 +Author: Brian Ford +Date: Sun Aug 5 23:39:36 2007 +0000 + + fixed completeness to not over match methods. converted comparable specs. + +commit c6f4d90df72b103884fa5470a433f5513d2c524d +Author: Pedro Del Gallego +Date: Sun Aug 5 22:52:17 2007 +0000 + + * more specs for File.open. Some of them are plataform dependent + +commit 38bfff9d014b90409e272ddf041dc63f53d48f5d +Author: Brian Ford +Date: Sun Aug 5 22:45:31 2007 +0000 + + converted bignum specs. misc cleanup. + +commit 14890b68c447731417ce53ca2e4310175e39b440 +Author: Florian Gross +Date: Sun Aug 5 22:00:04 2007 +0000 + + Small spec fix + +commit b6c3cfca5cf1b2cb85dc216180ad21a6bf653a10 +Author: Brian Ford +Date: Sun Aug 5 17:20:32 2007 +0000 + + converted time specs. according to completeness, need to spec 9 more methods. + +commit 8829cf7e94ec0434f642fafa7dbf117a860045b9 +Author: Brian Ford +Date: Sun Aug 5 07:53:47 2007 +0000 + + more converted specs. + +commit 5bf174780e893b7ee9b82b6ca3964db7cad84e30 +Author: Brian Ford +Date: Sun Aug 5 07:08:08 2007 +0000 + + converted fixnum specs to describe per method. + +commit 88023701a88c1113e4874c193d26c6bf21fad383 +Author: Brian Ford +Date: Sat Aug 4 22:19:18 2007 +0000 + + misc noise cleanup in specs. use bin/completeness to find missing specs rather than warns. + +commit 3d960a021cb9ac2bdc2a204f94b4f024f3ef60a4 +Author: Brian Ford +Date: Sat Aug 4 22:06:12 2007 +0000 + + fixed completeness to pass correct spec example string for class methods. changed float specs to describe per method. + +commit 8a7abb5996e5bdf8b9d6c5884e0e0d8ae73d060e +Author: Brian Ford +Date: Sat Aug 4 20:10:38 2007 +0000 + + beginning of a completeness reporter. use 'bin/completeness -t ruby' to report on the completeness of the specs against MRI. use 'bin/completeness' to report on the completeness of rbx relative to MRI. use -t target for other implementations. updated some specs to the 'describe Class#method' style. + +commit 463f13be4462e22bc3f4491a475658624c5832ab +Author: Pedro Del Gallego +Date: Sat Aug 4 17:09:25 2007 +0000 + + * fix some bugs in bignum_spec + * changed the File::Foo.shouid == bar assert to defined?(File::Foo).should == "constant". The specific value dependence on OS. + +commit 989d72394f1e175b058f55ccf3e60f09a2c76401 +Author: Brian Ford +Date: Sat Aug 4 04:15:01 2007 +0000 + + fixed mini_rspec specdox reporter to not output describe message until examples are executed. fixed specdox and dotted reporter to distinguish between errors and failures. + +commit f98fe7f211e5784a35e99643fb52c9350b20d7ae +Author: Florian Gross +Date: Sat Aug 4 01:40:52 2007 +0000 + + More compatibility and a few small fixes + +commit c78ba9f96d7d4d229d6b1b1b11cf314fb5a0271d +Author: Florian Gross +Date: Sat Aug 4 00:40:42 2007 +0000 + + Compatibility for USE_RSPEC=1, sanity, some clean-up + +commit 761d05b5cbd92339f9d02e65d005a65c5155618e +Author: Wilson Bilkovich +Date: Fri Aug 3 21:49:26 2007 +0000 + + * Compiler and Normalizer fixes for method definitions without bodies + +commit ad7abe4d61171f9650d08b277d45c7f680f37950 +Author: Brian Ford +Date: Fri Aug 3 17:07:19 2007 +0000 + + fixed mini_rspec shared behavior to be compatible with rspec. + +commit 1e1ccb902d11547e9f67db82c31a5898e6227d67 +Author: Brian Ford +Date: Fri Aug 3 16:39:07 2007 +0000 + + added an implementation of shared behavior for mini_rspec. altered Array#[] and Array#slice specs to use shared behavior. + +commit 7697b2ae3db6ed1d8697010a7e0f52f8e3587c8a +Author: Brian Ford +Date: Thu Aug 2 20:41:43 2007 +0000 + + added SpecRunner class to mini_rspec to properly encapsulate behavior. added --example option to specify a regexp to match examples to execute. + +commit b80bb3d295d3648988b15a29553189f219d8ac0a +Author: Pedro Del Gallego +Date: Thu Aug 2 15:56:07 2007 +0000 + + * added specs for file_spec + +commit c277fd3de82678f055693422af19c3f45ffc2a88 +Author: Florian Gross +Date: Thu Aug 2 00:46:27 2007 +0000 + + mspec: Add -x option for excluding specs by RE + +commit aa53967c694ed7621aa1a8a8b542d067d9e58925 +Author: Florian Gross +Date: Thu Aug 2 00:05:53 2007 +0000 + + New specs for String#succ(!) + +commit eafa5b0fd43168b4ae649b145f9528f7deae3aa7 +Author: Florian Gross +Date: Wed Aug 1 22:59:38 2007 +0000 + + New specs for String#sub(!) + +commit 3406e64032251a2a9849da3f6c27d872dd339175 +Author: Florian Gross +Date: Wed Aug 1 22:17:18 2007 +0000 + + New specs for String#squeeze(!) and String#strip(!) + +commit fa4d66576528725085ef47cca27c5c85c55b3150 +Author: Brian Ford +Date: Wed Aug 1 21:08:26 2007 +0000 + + added Object#(public|private|protected)_methods and Module#(public|private|protected)_instance_methods. added Tuple#first, last. + +commit 4ca071ba4a48aa984308e0ba9448718a6e214d7a +Author: Brian Ford +Date: Tue Jul 31 20:56:37 2007 +0000 + + Some Object#methods et al specs. + +commit 67be404ac0714ec01c1c92c77465915d90fd794b +Author: Florian Gross +Date: Tue Jul 31 18:41:54 2007 +0000 + + Renamed variables_spec.rb to assignment_spec.rb + +commit c0187db3e51297dfffabebe9acb6d6321bd04578 +Author: Florian Gross +Date: Tue Jul 31 18:22:41 2007 +0000 + + New specs for String#split + +commit 06cb5ab7c39866c99bb8d9a5fbb678f2f8a19cf2 +Author: Brian Ford +Date: Sun Jul 22 06:47:54 2007 +0000 + + Removed the .rbc files from externals dir and set svn:ignore. Added Tuple specs, fixed a couple small problems with Tuple. Modified mspec to pass -I, -r to the target; added -n RUBY_NAME to affect which specs are run. Added hashi dir as an experiment to implement a bootstrap that could be run on e.g. MRI or JRuby to allow the core libs to be run and tested against the specs. + +commit 567d4f710bc232fc9223972e22a7d92e4abe940d +Author: Evan Phoenix +Date: Tue Jul 17 17:49:53 2007 +0000 + + Stack allocated lvars, GC fixes, compiler changes, oh my! + + This is a biggy (too big in fact). It started as a change to allow + arguments to be accessed directly from the stack, and turned into a + monster. + + Arguments and some lvars can now be accessed directly from the stack, + making them cheaper to create and use. This turned out to expose + a large number of bugs in the VM related to stack access, as well + as some in the GC. + + The big GC change here is that the mark/sweep GC is actually run now, + as opposed to before when it would just allocated more and more memory + (the source of memory issues I suspect). + +commit 564ac024e14a790f4a3d257ddf1d9fa0cb93ee3b +Author: Florian Gross +Date: Tue Jul 17 00:20:52 2007 +0000 + + $~ specs all over the place; + Revised % format string specs (match MRI trunk); + Revised hex and oct specs (match MRI trunk); + Merged slice together with []; + New specs for scan and slice! + +commit 2389eb4b36d86732dbb621be1cad3edca0e36aa5 +Author: Pedro Del Gallego +Date: Mon Jul 16 09:26:20 2007 +0000 + + * added specs for file_spec + +commit a6453b6184353633d14c271533c2e2af7a6c4b12 +Author: Florian Gross +Date: Sun Jul 15 17:53:58 2007 +0000 + + A few specs for char numbers outside of 0..255; + A few specs for modifying strings while iterating; + New specs for hex, index, initialize(_copy), ljust, lstrip(!), match, next(!), oct, replace, reverse(!), rindex, rjust, rstrip(!); + Small additions, fixes & refactoring + +commit 217dd5dae127c146559dd1512edac23a94565ae9 +Author: Florian Gross +Date: Sat Jul 14 23:52:52 2007 +0000 + + Taintedness specs all over the place; + String subclass specs for String#%; + Range subclass specs for access methods; + str[idx, count] = str specs (contributed by John Lam); + New cases for capitalize(!) / center / gsub(!); + Refactoring + +commit b8b0c3dd380335260c3870934ca51dce736ce15d +Author: Florian Gross +Date: Sat Jul 14 23:39:38 2007 +0000 + + Added custom range subclass cases for access methods + +commit 558552ec549fd605bed2c8f5c384e8c944e780a2 +Author: Pedro Del Gallego +Date: Fri Jul 13 14:40:15 2007 +0000 + + * added cases for file_spec + +commit 5218708c630bd8a631522a00aa6cba4e91cbec54 +Author: Pedro Del Gallego +Date: Fri Jul 13 13:36:53 2007 +0000 + + * added cases for file_spec + * refactoring numeric_spec.rb + +commit 3e9dbc15a81950e55a15a7fcca0ab04a5fd5353f +Author: Florian Gross +Date: Wed Jul 11 22:09:49 2007 +0000 + + Add specs for to_* calls having correct semantics with method_missing() and respond_to?(); + Small clean up + +commit 23961f46af6f74d2d6b9019972e451a5ae12b728 +Author: Florian Gross +Date: Wed Jul 11 22:00:26 2007 +0000 + + Add specs for to_* calls having correct semantics with method_missing() and respond_to?(); + Removed a few duplicate specs (probably resulting from a mismerge) + +commit 639c64ca0965ff79401989ca7dbde862815f13fb +Author: Florian Gross +Date: Wed Jul 11 21:09:33 2007 +0000 + + Add specs for to_* calls having correct semantics with method_missing() and respond_to?(); + Fixed String#%'s %E/e/f/G/g and %b/d/i/o/u/X/x specs to verify Kernel#Float / Kernel#Integer semantics instead of to_f / to_i ones + +commit e97879670bbc8425810a3c83f15a523066899a89 +Author: Arthur +Date: Tue Jul 10 20:04:48 2007 +0000 + + * fix a typo + +commit 1e8890613a215c61ef90629b8b6023ac4612c499 +Author: Arthur +Date: Tue Jul 10 20:03:44 2007 +0000 + + * Update Symbol Specs to the new format. + * make Symbol#to_int show a warning as in MRI. + +commit 1262f24460463628c7cc4e275b7c814048937b57 +Author: Florian Gross +Date: Tue Jul 10 18:28:06 2007 +0000 + + New specs for gsub() without block + +commit 0ba87f6edc183385551e4cf8c05212fadaf36427 +Author: Florian Gross +Date: Tue Jul 10 16:48:46 2007 +0000 + + New specs for capitalize, chomp, concat, crypt, eql?; + Added missing methods chop, chop!, count; + Small improvements and refactoring + +commit 226942caef6bd217a13dc235a89c5ccf4a18f98b +Author: Florian Gross +Date: Mon Jul 9 21:29:19 2007 +0000 + + Strings specs for letters c through e + +commit e41c027537f1e4f8ea4b8b5b6fe90df9a21e3aff +Author: Pedro Del Gallego +Date: Fri Jul 6 17:13:39 2007 +0000 + + * added 62 cases for file_spec + * remove a bug from obejct#method_missing_spec that break the specs + +commit 06d2fd71b847e139a39ab3b7a132ab041a8d4c1e +Author: Pedro Del Gallego +Date: Wed Jul 4 16:05:21 2007 +0000 + + * add 84 cases to the numeric_spec.rb + +commit b8d334f575322c65932279346bba61caead61555 +Author: Pedro Del Gallego +Date: Wed Jul 4 10:28:27 2007 +0000 + + * 14 cases for Object.method_missing + * Add File#atime, File.atime, File#ctime, File.ctime, File.delete, File.executable?, File.executable? + +commit f2276130c4bf1894ffb6efb451203dcbfe9322bb +Author: Charles Nutter +Date: Wed Jul 4 08:48:57 2007 +0000 + + Added a spec for Process::times...it's not great, but it's something. + +commit f71bb57b3fc69c35d34abdb9959e27efb71bbdff +Author: Charles Nutter +Date: Wed Jul 4 06:14:06 2007 +0000 + + Fixes for #150; handle Time - Time correctly, don't assume it's a number of seconds. + +commit 895f1abdc0bfcdb213f97067704b1bb87a7e6d17 +Author: Florian Gross +Date: Tue Jul 3 17:01:27 2007 +0000 + + New specs for casecmp + +commit 2aa7cb37925cd92c3b23d4a33a6d7bc7c2b66737 +Author: Florian Gross +Date: Tue Jul 3 14:07:56 2007 +0000 + + New #[] and #[]=, capitalize and casecmp specs; + Converted "should work" messages to "works" using a few regular expressions -- I'm still going through the file so bad replacements (if any) will be fixed + +commit 719ff3b8959d93d7da8165d6e5b44989afde92d7 +Author: Pedro Del Gallego +Date: Tue Jul 3 00:33:59 2007 +0000 + + * new NoMethodErro_spec.rb + +commit 5363324044fdc1457cfbf1b738dd931d3255b191 +Author: Florian Gross +Date: Mon Jul 2 23:58:09 2007 +0000 + + Some more new specs + +commit fa1b3694e366bf087a8d1ac107257c38ce447251 +Author: Pedro Del Gallego +Date: Mon Jul 2 20:29:20 2007 +0000 + + * Add to_s with a base spec + * Change foo.aMethod.to_s.should == "bar" with foo.aMethod.should_be_close(bar,TOLERANCE) because floats representation are plataform/implementation dependents, but not changed aBignum.to_s.should = ... + +commit 2a5c93afd4ddfef7c30de17c531f49849e9bb957 +Author: Pedro Del Gallego +Date: Mon Jul 2 00:10:06 2007 +0000 + + * Add Float::Constant specs + * Change foo.aMethod.to_s.should == "bar" with foo.aMethod.should_be_close(bar,TOLERANCE) because floats representation are plataform/implementation dependents + +commit 7d3dcc24cb72d6548cf44d8519691f4cd7344801 +Author: Florian Gross +Date: Sun Jul 1 21:00:37 2007 +0000 + + Heavily extended and refactored String#% specs; + some cleanup + +commit 79ce6628df39d20d03efcd715ea42ba70ae9f03e +Author: Florian Gross +Date: Sun Jul 1 20:59:38 2007 +0000 + + Add support for MRI as :mri for failure() and similar methods + +commit 7e43cd858c0380aaf17dd7bacd8a24cef96bb309 +Author: Florian Gross +Date: Sun Jul 1 17:11:08 2007 +0000 + + rindex terror specs + +commit e5b7cf88092cf59357124e3d8f35bc19f8ee589a +Author: Florian Gross +Date: Sun Jul 1 01:14:09 2007 +0000 + + Added a few new format specs (Most of these should probably be moved to Kernel::format later) + +commit a533693824608a03ab6a66882b607fecab3a3a75 +Author: Florian Gross +Date: Sat Jun 30 00:29:29 2007 +0000 + + A few more specs, clean up and compatibility with MRI 1.9 head + +commit b9e8936562ec23db63879f9c127dadeadd8adf2e +Author: Florian Gross +Date: Fri Jun 29 02:41:40 2007 +0000 + + New specs for [], default(), delete(), shift(), yield argument count semantics and modifying hashes while iterating over them; + Also removed some warnings and cleaned up the code a bit + +commit 72d1b106c1de4b00b9af184eb890e950854a9c77 +Author: Florian Gross +Date: Fri Jun 29 02:37:55 2007 +0000 + + Adding spec for join passing along separator argument for nested arrays + +commit 6e2848b7143cd0ae47a7b9ac632a567df7fd30fc +Author: Florian Gross +Date: Fri Jun 29 02:35:09 2007 +0000 + + Adding message argument for should_raise() + +commit 31591886dde4bfd9b4e9de34c26960e45566b7ee +Author: Brian Ford +Date: Wed Jun 27 05:15:15 2007 +0000 + + Commiting (#147) math specs by pedro (modified for style, structure, and legibility). + +commit c7d623ee836363d0f3d443ba1c676ef0f86e34f7 +Author: Brian Ford +Date: Tue Jun 26 04:06:49 2007 +0000 + + enhanced spec:ci to take a target on the command line, invoke like: SPEC_TARGET=jruby rake spec:ci. removed deprecated #only and #except from spec_helper. + +commit c13a588cb7e37c20ce7e8a9430d854cc51be7b00 +Author: Brian Ford +Date: Mon Jun 25 08:28:10 2007 +0000 + + misc cleanups to specs to eliminate interaction effects. + +commit 280296208bd699cb574c662f92b585519a739c6b +Author: Brian Ford +Date: Mon Jun 25 02:42:07 2007 +0000 + + removed extension dir and added README for subtend specs. + +commit 7ce8d4addc77ea9da0daf3ea3dc1fc7b00030b29 +Author: Brian Ford +Date: Mon Jun 25 02:30:15 2007 +0000 + + reorganized subtend specs. stragglers from spec/language reorg. + +commit d499ebfd98d8fb9bd50c0f7a46b3587aa1f28c8a +Author: Brian Ford +Date: Sun Jun 24 19:44:12 2007 +0000 + + significant reorganization of spec/language, added files that correspond to the desired layout of this section. there remains to be done a large amount of cleanup for existing language specs, and especially spec description strings. + +commit bc0d0965bb5a6b3966884b63edd37218359aa46d +Author: Tilman +Date: Sun Jun 24 14:52:15 2007 +0000 + + Extended specs for File.join. + +commit a4e189f31a8c256821564041c4dbce2a832ba78e +Author: Tilman +Date: Sun Jun 24 13:00:33 2007 +0000 + + Fixed a typo. + +commit ecaf1abafeb69994b05463742ca4220797f62ad3 +Author: Eero Saynatkari +Date: Fri Jun 22 23:25:13 2007 +0000 + + * Array specs by Josh Susser (hasmanyjosh) + +commit 1c8987b6195d356126ebc3cc9c21e473be915240 +Author: Arthur +Date: Fri Jun 22 21:22:30 2007 +0000 + + * Heavily extended String specs + +commit 663e2cbe0c026aa7e792b6aab682301570ccd766 +Author: Tilman +Date: Fri Jun 22 08:59:42 2007 +0000 + + Added specs for Time#dup. + +commit f9dd8149bd7d794e8686053e8dca010ea71eacba +Author: Brian Ford +Date: Thu Jun 21 06:47:57 2007 +0000 + + added methods #compliant, #noncompliant, #extension, #failure. Please read the comments for them in spec_helper.rb. #only, #except are deprecated but have not yet been removed. + +commit 389b3cef5176b0244f78294a3c820cc84797e0df +Author: Brian Ford +Date: Thu Jun 21 05:22:36 2007 +0000 + + added -f i (immediate) reporter for mini_rspec. + +commit d113f855e32d09abaa74bb0ccafa4a65ffce66b1 +Author: Florian Gross +Date: Wed Jun 20 21:59:27 2007 +0000 + + A few more hash order consistency specs + +commit 8df2a605937c29b0ca4e89fae37b725e7244fbee +Author: Florian Gross +Date: Wed Jun 20 21:24:01 2007 +0000 + + Small spec improvements all over the place + +commit 5c2472584637b6f5accaaf2450d4c23904b0bbd7 +Author: Florian Gross +Date: Tue Jun 19 23:06:33 2007 +0000 + + Small tweaks to let us run specs against Ruby 1.9 + +commit 18b06659146f00f0ecf72846c445b03268305328 +Author: Florian Gross +Date: Tue Jun 19 15:56:02 2007 +0000 + + More specs, including frozen hash ones + +commit 3cc17a6c7d4c4e4d13b67da4e2bd8937160916f0 +Author: Florian Gross +Date: Tue Jun 19 15:02:21 2007 +0000 + + Specs for methods involving to_hash and more + +commit a33e72ba27dc7c80fb7c3947d4fe86521b8987e0 +Author: Florian Gross +Date: Tue Jun 19 13:35:14 2007 +0000 + + New specs for each, each_key, each_pair, each_value, fetch, has_value?, index, initialize_copy, inspect, invert, key?, keys, length, merge, merge!, rehash, to_a and value? + +commit f5ec55b0233fd6b7825b04afc6157caac0c529ce +Author: Florian Gross +Date: Tue Jun 19 13:18:33 2007 +0000 + + Using except(:rbx) for "inspect should handle recursive arrays" instead of commenting it out + +commit 7fec6cb5534d22dbfa4dd245cf3b0c0776b3b465 +Author: Florian Gross +Date: Tue Jun 19 00:56:35 2007 +0000 + + New specs for Hash.new, #==, #[], #[]=, #clear, #default=, #delete and #empty? + +commit e3085af8e97177f8b7e4ff1c2aad2f306a4f474d +Author: Florian Gross +Date: Tue Jun 19 00:53:43 2007 +0000 + + Specs for how Array#uniq should use eql?() and hash() + +commit 01799e95c71453e8dff9730dd283bf76989e75e5 +Author: Florian Gross +Date: Mon Jun 18 11:22:47 2007 +0000 + + Adding new specs from rue plus more. The diff is a bit chaotic, but everything should be OK. + +commit 890deed76153d05c6874b46ec29c474eb4e36e41 +Author: Florian Gross +Date: Mon Jun 18 11:12:04 2007 +0000 + + Moving only() to general spec helpers, adding expect() + +commit bf89af6c3b632b88e3cc74bead42f21561da58a7 +Author: Brian Ford +Date: Mon Jun 18 07:19:15 2007 +0000 + + (Jason Toy) added some specs for File (with some modifications) (#130). + +commit a7a6d8e336f8d331c60e973fb8f9e0aac1fb61ac +Author: Brian Ford +Date: Mon Jun 18 05:20:29 2007 +0000 + + (Jason Toy) initial specs for YAML (#123). + +commit 4c2f70040050e35da28a8684296f913a3dd4a198 +Author: Brian Ford +Date: Mon Jun 18 05:14:41 2007 +0000 + + (nitay) patch for Bignum#size (#120). + +commit b1e57c9c718acfc7f1e61ae1fb60f10b918f8e5c +Author: Brian Ford +Date: Mon Jun 18 01:40:29 2007 +0000 + + Range specs and code from Ryan Mulligan (#141). + +commit 9fa70f392bf83c55d67e682c36d9ebd247cff62c +Author: Florian Gross +Date: Sun Jun 17 22:02:48 2007 +0000 + + New specs from Ryan Mulligan (#140) + +commit 64c970bddeb754115ed193d2f786c797ea90dab3 +Author: Brian Ford +Date: Sun Jun 17 20:08:40 2007 +0000 + + reorganized specs to put implementation-specific extensions in a subdirectory within the logical division of the specs into core, library, language. + +commit 8d437f0f63d4d3f9eea6e4436a28f437e6e76053 +Author: Florian Gross +Date: Sun Jun 17 19:39:37 2007 +0000 + + Initial work on hash specs -- a few new cases and a bit of reorganization + +commit d8222049004ba0d6ec51db0c962b5200bb180aec +Author: Brian Ford +Date: Sun Jun 17 03:53:45 2007 +0000 + + (yipstar) module specs for undef_method, define_method, remove_method. all pass MRI. + +commit 0162cfe6a443ded5d6c8e01a866f5a8d1fbce901 +Author: Florian Gross +Date: Sat Jun 16 23:11:31 2007 +0000 + + Added specs for * / join / to_s with recursive arrays + +commit 0744e57d7860b9f6eefcc8e14962d7ee777d2ec4 +Author: Florian Gross +Date: Sat Jun 16 22:05:32 2007 +0000 + + A few more specs for array sub classes with to_ary [ruby-core:11472] + +commit 07e0df5111c8ceeda83e50ef434948ee17e92aae +Author: Brian Ford +Date: Sat Jun 16 19:09:19 2007 +0000 + + commented out specs that cause the VM to seriously misbehave allocating memory without bound or causing SIGBUS. currently 434 examples, 130 failures at r1357 on MBP. + +commit 2abc6e6dc5df662e8f0587636bd1cf3573e39f28 +Author: Tilman +Date: Sat Jun 16 16:41:07 2007 +0000 + + Don't use timezone names that aren't portable. + Instead, specify timezones by their standard name and the offset from GMT. + This makes the specs pass on FreeBSD w/ MRI. + +commit d4106115c2ca9a4678b7060b6ac0091d66312624 +Author: Brian Ford +Date: Sat Jun 16 08:20:29 2007 +0000 + + a first, big step to making the entire set of specs more agnostic about the ruby implementation/engine by, paradoxically, qualifying certain specs to only run under certain ruby engines. removed incompatible and rubinius directories. folded in specs where appropriate and moved spec files (e.g. bytearray, tuple, compression) into the appropriate directories. the spec/parser and spec/compiler dividing line is not clearly defined given the range of types of implementations and perhaps should be merged. + +commit 3de0340e693e20b5e32c643f1f4dae7e1943e077 +Author: Brian Ford +Date: Sat Jun 16 07:07:41 2007 +0000 + + migrated more tests from shotgun-tests to specs. + +commit 369813306643d98c277841c1e9b400f6b60d3316 +Author: Florian Gross +Date: Fri Jun 15 22:43:05 2007 +0000 + + Add spec for elements returning odd stuff on <=> + +commit d70ab64def5ebdcb0e1946618b06e810270eb2aa +Author: Florian Gross +Date: Fri Jun 15 22:17:03 2007 +0000 + + New specs for frozen arrays + +commit fda7128521254d2db2668fec55ef7ce9337ecf1a +Author: Florian Gross +Date: Fri Jun 15 21:55:30 2007 +0000 + + More specs, mostly for array subclasses + +commit 2d57166d2ef2ff63f333b7ee0c196f5c4e10b8cf +Author: Florian Gross +Date: Fri Jun 15 16:56:25 2007 +0000 + + More than five bazillion new specs including a ton for the very evil []= method (letters i through s) + +commit b905a952af41a96f72499750c4635b1352b237ba +Author: Florian Gross +Date: Thu Jun 14 23:06:33 2007 +0000 + + A few more specs. + +commit e17aa3690f7dcd2dab346bf7def0dd26b38072f1 +Author: Florian Gross +Date: Thu Jun 14 22:16:54 2007 +0000 + + A bunch of new specs. Includes highly exotic corner cases. + +commit 7845d8928d138353ad03bd496d1800c03e82b538 +Author: Florian Gross +Date: Thu Jun 14 20:52:23 2007 +0000 + + Fix cleanup to work with symbolic method names like == + +commit c677ac036baa847cef3de6a34b2b56c9fd09213c +Author: Florian Gross +Date: Thu Jun 14 18:04:43 2007 +0000 + + Fixed the test for DATA to match MRI (it's only supposed to be defined when the main file contains __END__); + Added test for TOPLEVEL_BINDING + +commit 27b2767cd21b5a69ee10a2a629a68de64fd8ae5a +Author: Florian Gross +Date: Thu Jun 14 18:03:26 2007 +0000 + + Introducing RUBY_NAME so we can spawn a new Ruby for things that can't be tested otherwise + +commit 4c4a96f51b4c06dd6896f996ae0e87a68152a3bc +Author: Tilman +Date: Thu Jun 14 15:15:56 2007 +0000 + + Don't call Time.now multiple times when comparing their values. + +commit cde774be8b188f5870b1ee387b5e5fffd9948163 +Author: Tilman +Date: Thu Jun 14 15:00:17 2007 +0000 + + Fixed a typo. + +commit 6e51eee65c310255183d81d97a98be313ca68afc +Author: Brian Ford +Date: Mon Jun 11 04:07:03 2007 +0000 + + put guards on imcompatible specs to prevent ruby, jruby from running them. work around for Dir ** globbing being broken. + +commit 03dfae6b896a6b67ac6066e94284d992833afac5 +Author: Brian Ford +Date: Mon Jun 11 02:00:25 2007 +0000 + + Added rake task spec:ci. added svn:executable for bin/mspec. enhanced readability of system command in mspec. added globbing across directories to mspec command. + +commit 96425667fbff044333c13c5a88c3b8cad156504f +Author: Brian Ford +Date: Sun Jun 10 21:45:52 2007 +0000 + + modified mini_rspec to include proper reporters. converted mspec to use ruby to generate a command line to run specs. mspec usage should be similar to spec: mspec spec/core spec/language/class_spec.rb will execute any spec/core/*_spec.rb plus spec/language/class_spec.rb and output a single summary of exceptions, examples, failures. + +commit 1075f2cf34a81c00a1b06d2474c78300ae013161 +Author: Evan Phoenix +Date: Sun Jun 10 06:59:40 2007 +0000 + + Cleaned up MatchData, added support for accessing named groups. + +commit c678d79f125d67328e267001e5fe353f5ef38a2c +Author: Eero Saynatkari +Date: Sat Jun 9 04:20:51 2007 +0000 + + * =~ for regexps to mini_rspec by dean (Ticket #136) + * Kernel.caller spec relocation for reliability + +commit 00eec364dbf3cef03915a68a359ed06b7e501553 +Author: Brian Ford +Date: Fri Jun 8 07:10:56 2007 +0000 + + reintegrated String specs. all specs in core follow the convention that there is one file named _spec. + +commit 9f9817bbb9fea5cc02eb280f01eb50e45ea03118 +Author: Arthur +Date: Thu Jun 7 23:49:47 2007 +0000 + + * String#replace, String#chop! and String#chop behave now as in MRI. + * Specs + +commit 129e492fcacf937473bb1c602176b48a352f8572 +Author: Arthur +Date: Thu Jun 7 23:39:00 2007 +0000 + + * Fix String#<=> when the given object is not a String. + * Fix String#to_sym for invalid symbols. + * String#to_i raises an error when invalid radix is given. + * More specs. + +commit 8d7a9e21874d9e44c63d17dd8f6832b942805707 +Author: Arthur +Date: Thu Jun 7 23:31:25 2007 +0000 + + * Make String#capitalize!, String#downcase! and String#upcase! check for the 'frozenness' of self + * Specs + +commit 10087a0c92217d1fbadfede9fdb0099c7eb195e6 +Author: Arthur +Date: Thu Jun 7 23:25:08 2007 +0000 + + * Spec for String#== + +commit e51ca54f6f4e9e3dabd48895fa2cb746fb0d3c17 +Author: Arthur +Date: Thu Jun 7 23:23:23 2007 +0000 + + * Fix String#=~ to work as in MRI. + * Specs + +commit d2c7d6e6bb624c23e994888b6a235022486e0c69 +Author: Arthur +Date: Thu Jun 7 23:16:35 2007 +0000 + + * Alias String#size to String#length. + * String#<< now correctly checks and converts (where applicable) arguments. + * String#<< raises an error if used on frozen string. + * Added extended specs for String#<<. + +commit c389493556e3394cce846698aa4fc6a67a5f4b40 +Author: Tilman +Date: Thu Jun 7 18:14:02 2007 +0000 + + Time#zone_offset and #zone_utc? are also in stdlib already. + +commit 7dcd86dca123edaec7edec2853cba2af27ca6d52 +Author: Tilman +Date: Thu Jun 7 18:07:59 2007 +0000 + + Moved the specs for Time methods that should be in stdlib rather than in core to spec/lib/time_spec.rb + +commit 953dfdfdea395ead465a9e19339d94f8b8c7d684 +Author: Hapk +Date: Thu Jun 7 17:36:20 2007 +0000 + + Added more specs for Array#pack: covering %#bB. + +commit 7aa84bc102ad6943aed46cbb357f233ac4b6d3f9 +Author: Florian Gross +Date: Thu Jun 7 16:47:03 2007 +0000 + + Make next with arg match MRI; Fixed typos and a copy&paste error + +commit 40c354444d6d7a2fd3aeb940c3bdcf6fd2a28940 +Author: Florian Gross +Date: Thu Jun 7 16:43:17 2007 +0000 + + Fixed to run on MRI (uses Object.const_get now) + +commit 95dd0ca698d0b1f79a11f4a556c171bea33ba176 +Author: Florian Gross +Date: Thu Jun 7 12:52:40 2007 +0000 + + Make behaviour match MRI, fixed copy&paste errors, did some refactoring + +commit 191d4e80406a6e4ffd08594ddeada47fb3219bf0 +Author: Florian Gross +Date: Thu Jun 7 01:41:08 2007 +0000 + + Fix chaos introduced in rev 1283 + +commit e0f5184493607f7d9c216146207dc298893a744c +Author: Florian Gross +Date: Thu Jun 7 01:28:55 2007 +0000 + + Fix Dir.chdir test on OS X (/tmp is symlinked to /private/tmp) + +commit a32062d6099556a93afee61f0b0000a28675fbb6 +Author: Tilman +Date: Wed Jun 6 21:44:00 2007 +0000 + + Deleted old specs. + +commit de7b3ecaa61c4d3514c8a3534805e9c8d0d1bb2f +Author: Tilman +Date: Wed Jun 6 21:09:48 2007 +0000 + + Made the Time#to_a spec pass with any time zone, too. MRI passes all the specs for me now. + +commit ac1e295da36f23184e583f5e62a4909959550dbd +Author: Tilman +Date: Wed Jun 6 21:06:32 2007 +0000 + + Fixed specs for Time#localtime, #gmtime and friends. + +commit 064a5967f5550cf4a2e10885ab2c9f22afa59da2 +Author: Tilman +Date: Wed Jun 6 20:56:41 2007 +0000 + + Fixed a few more Time specs. Instead of the imaginary time zone "PDT" + I'm now using Asia/Kuwait for some specs, because those lucky people + don't have DST, which means we don't have to flip the spec expectations + every 6 months. + +commit 8f6b6a5a5f8e62631a58061b6cdeeaff5654aeb0 +Author: Florian Gross +Date: Wed Jun 6 20:41:32 2007 +0000 + + module_function specs matched to MRI (module_function makes the instance methods private) + +commit 8cc656b95cb71ea476e787ce635df889090f8050 +Author: Tilman +Date: Wed Jun 6 20:41:02 2007 +0000 + + Fixed the Time#year spec. Using CET, too, since specc'ing using 1969 just feels wrong. + +commit ed7be9eaa73b8b1d85964f5c8fdfc250e2e8f62f +Author: Tilman +Date: Wed Jun 6 20:38:43 2007 +0000 + + Oops, fixed a stupid error in r1281. + +commit e81d716092e0c6a8b52775ca2b9e0d44c4e3ef7f +Author: Florian Gross +Date: Wed Jun 6 20:35:42 2007 +0000 + + Divmod matched to MRI (Special casing darwin on MRI because it doesn't raise FloatDomainError exceptions in some cases) + +commit fc5f461c47b7212f05f8699110e313aeab46d46d +Author: Tilman +Date: Wed Jun 6 20:35:34 2007 +0000 + + Fixed the Time#hour, #min and #day specs. + +commit 6847a1e49ff4ad4dfe7c6e9e4d7352f92d72b4cd +Author: Tilman +Date: Wed Jun 6 20:26:26 2007 +0000 + + Don't call Time#seconds as MRI doesn't have it and it doesn't seem to be needed anyway. + +commit b9a6ccc89d46ffa910c9bdc0fece9d013440872d +Author: Tilman +Date: Wed Jun 6 20:14:15 2007 +0000 + + Time#asctime needs to use %e to print the day of the month. + +commit 7311fdf31481ba7a4373d5b007efb62c1f84c389 +Author: Tilman +Date: Wed Jun 6 20:03:30 2007 +0000 + + Hardcode output for Time.gm(...).inspect. This makes utctime superfluous for now. + +commit 229e7eefe7944df92ab2f84e553992fc0c868dbd +Author: Tilman +Date: Wed Jun 6 19:19:38 2007 +0000 + + Fixed two more Time specs. + +commit 7f490d752ed97bfb9b830d24f4e2c2f44107c141 +Author: Tilman +Date: Wed Jun 6 18:41:47 2007 +0000 + + Make sure that with_timezone resets . + +commit 9c8d25c4d072510215407209fa52a06b85e95d35 +Author: Tilman +Date: Wed Jun 6 15:57:32 2007 +0000 + + Made the wday spec work with any timezone. + +commit 4882f8a676d5234339296d6a4489f3a7134cf5fd +Author: Tilman +Date: Tue Jun 5 20:08:22 2007 +0000 + + Fixed a typo. + +commit fb63faa2165cafdc7907f173344638609f884833 +Author: Tilman +Date: Tue Jun 5 20:03:06 2007 +0000 + + Make sure that a GMT Time object returns true from gmt? + +commit deae4911588b555264f529e765dc5baf7a2c6e69 +Author: Tilman +Date: Tue Jun 5 20:00:45 2007 +0000 + + Use with_timezone for the gmtoff specs. + +commit 625dd8e366f846cf15165e323a719199627ae422 +Author: Tilman +Date: Tue Jun 5 19:47:29 2007 +0000 + + Added a helper method to temporarily override the TZ env var and fixed one of the specs by using it. + +commit b359eb74d41749919c6177ec2af6b5b516308bd8 +Author: Tilman +Date: Tue Jun 5 19:33:01 2007 +0000 + + Provided 'date' calls for coreutils' date program. + Checking for coreutils using RUBY_PLATFORM isn't the right way, + but it will do for now. + +commit b19c3c8d886885adc08ac69469792b14e47ba265 +Author: Eero Saynatkari +Date: Mon Jun 4 23:42:20 2007 +0000 + + * Improved Array#sort with additional spec by wycats (ticket #135) + +commit 8513e72a5af7596c8782ee45dff39607489dbf12 +Author: Tilman +Date: Mon Jun 4 16:39:53 2007 +0000 + + Ticket #132: Implemented ENV in core and removed read-only ENV from shotgun. + +commit 03d75a78855213f6267fb8f80f9c71d0a031641f +Author: Mojombo +Date: Sun Jun 3 20:49:41 2007 +0000 + + Implemented most of Time, updated time specs accordingly + +commit 36975b1b79abbd38de68223cdc8ecbca9ba0feee +Author: Bremac +Date: Sun Jun 3 16:20:59 2007 +0000 + + Add Functions::abort, Functions::printf, and Functions::sprintf, and minimal specs. Closes ticket 87. + +commit 9d25d95a4aa3fd22d3f3a340427d40410488a770 +Author: Eero Saynatkari +Date: Sun Jun 3 12:41:49 2007 +0000 + + * Lots of new assignment specs by crafterm (Marcus Crafter) (Ticket #133) + * MatchData#inspect and #to_s now indicate it is an MD object + * -dc notes which file/method is being compiled + +commit ec0fb5beed68155c9e1ff67185cc2c8e4e474c04 +Author: Arthur +Date: Fri Jun 1 22:23:39 2007 +0000 + + * Fix String#[] and add some edge cases to the specs + +commit 068b48538ec574558ab787d59b14ebd2925f1126 +Author: Tilman +Date: Fri Jun 1 18:12:10 2007 +0000 + + Renamed module 'B' to something more meaningful to avoid name clashes. + +commit eb3de8af03d070b03216daa4fc0c2216d9d3e2a2 +Author: Arthur +Date: Fri Jun 1 18:09:52 2007 +0000 + + * Extend String#slice! specs + +commit db4775403d57ea29165165b9cbf0110739d91e2a +Author: Wilson Bilkovich +Date: Fri Jun 1 02:26:30 2007 +0000 + + * Ticket #128 - Patch by Marcus Crafter to enhance assignment specs + +commit 6098aa16357ce4261feb51bdf083c02442b1f074 +Author: Tilman +Date: Thu May 31 19:05:45 2007 +0000 + + Implemented rb_obj_alloc() and friends in subtend. + The spec for rb_class_new_instance() is still failing because of strange + subtend behavior. + +commit ae2c3cb4502ba9475111eeef10b2b70780a1b9f4 +Author: Eero Saynatkari +Date: Thu May 31 13:54:24 2007 +0000 + + * Fixed shotgun compile failure (possibly r1204) + * Various casts to avoid warnings + +commit d317d336412d0c1778d4c015dfe96287c44e1bd9 +Author: Hapk +Date: Thu May 31 11:12:02 2007 +0000 + + Added specs for "break", "next" and "redo" keywords. + +commit a33f801f8f142d997a553e41cde9f5b10d75ee65 +Author: Eero Saynatkari +Date: Thu May 31 04:23:33 2007 +0000 + + * Kernel.caller spec by jtoy (Ticket #112) + (kernel_spec is badly broken still) + +commit ce15eb69e721820e75b0f7aeae6488701cbe9555 +Author: Eero Saynatkari +Date: Thu May 31 03:55:27 2007 +0000 + + * Fixed String#dump spec expectation (Ticket #105) + * Moved RUBY_ENGINE to rubinius-specific variables_spec (Ticket #109) + +commit 7d2c575164dfbbd436d7c6ff400c088a68b29fa8 +Author: Hapk +Date: Thu May 31 00:42:50 2007 +0000 + + Added "if" specs for variable scoping. + +commit ceb4430f9c713f409f2567a0aa324f19afb09b79 +Author: Hapk +Date: Thu May 31 00:35:49 2007 +0000 + + Added specs for while / until condition/block evaluation order. + Added specs for next and redo statements. + +commit 4adeecf9333236800396bd77f928fb902e785692 +Author: Eero Saynatkari +Date: Wed May 30 22:17:04 2007 +0000 + + * Fixes to RSpec cleanliness of specs, most patches by zimbatm. + (Tickets #97 #98 #100 #103, partially #99) + +commit bbb0714b6ae60adb0af7445ed56544ceec3bc890 +Author: Eero Saynatkari +Date: Wed May 30 21:41:03 2007 +0000 + + * Array specs are RSpec/MRI-clean and pass (Ticket #95) + * Fix to should_raise for MRI by zimbatm + +commit d627ab36d3839745e057d9f5de781269ba7b154a +Author: Hapk +Date: Wed May 30 21:15:39 2007 +0000 + + Added specs for 'while' and 'until' statements. + +commit 8a2177c7f23181909c5dcb51dd6df1e5b930fdbe +Author: Wilson Bilkovich +Date: Wed May 30 20:32:13 2007 +0000 + + * Enhanced hatefulness of 'for' specs + +commit 9f64f2a1c584420fcabfc73a7c464aa3b1ba2e90 +Author: Hapk +Date: Wed May 30 19:46:57 2007 +0000 + + Added specs for postfix "if" form. + +commit 9dd8a846adf356d75ceac566612f35d1d30840bf +Author: Hapk +Date: Wed May 30 19:34:50 2007 +0000 + + Moved loop specs from spec/language/expressions into spec/core/kernel_spec.rb. + Added more specs for Kernel#loop. + +commit ddf2e3169c3a8b587f9abfb0ddf196635aec5186 +Author: Hapk +Date: Wed May 30 18:57:38 2007 +0000 + + Removed obsolete "elsif" statement spec. + +commit 3b6dca92ea810aa7866a2c7aa0b8812e72b6630e +Author: Hapk +Date: Wed May 30 18:56:00 2007 +0000 + + Updated specs for "if" statement to cover more cases. + +commit 678f609bfe826538ec16e75f7362bcb3f50c8d6d +Author: Arthur +Date: Wed May 30 17:09:09 2007 +0000 + + * Extend Specs for expressions + * Break the Specs up + +commit df60cc21e4213ac8344b5ed91e802d8cbbfa47a0 +Author: Tilman +Date: Wed May 30 17:00:17 2007 +0000 + + Implemented rb_ary_store() in subtend. The last spec still fails, but we'll fix that later. + +commit 8e0fea820c1683913625dfe95c7d3210d4548814 +Author: Arthur +Date: Wed May 30 10:09:55 2007 +0000 + + * Extend Specs for Class Definitions (nested class definitions, class definitions that extend objects, Multiple Definitions of the same class). + * Lots of them are failing in rbx. + +commit ce16f2b568ea89cb5f13660d3175165b105e4233 +Author: Wilson Bilkovich +Date: Wed May 30 01:18:39 2007 +0000 + + * OK, that should really be in 'rubinius', not 'incompatible' + +commit 888b777539baa116eedc14191ac85d57aec54349 +Author: Wilson Bilkovich +Date: Wed May 30 01:15:37 2007 +0000 + + * 'Options' is a Rubinius-only class, and should therefore be in 'incompatible'. Closes ticket #102 + +commit 46a58344fa2f03fb4154b78f34239a815b2d9944 +Author: Bremac +Date: Tue May 29 23:52:52 2007 +0000 + + Fix typos in specc'ing module_function, and make that spec play nicer with MRI. + +commit e0cedb691f76af4554bfc7522a7668ff861492f0 +Author: Bremac +Date: Tue May 29 20:49:42 2007 +0000 + + More complete raise implementation and specs: Handle instantiation. + +commit 9f3a3bfe9d2610dd7e9e752c86a1b8aba47f7fdf +Author: Hapk +Date: Tue May 29 20:39:07 2007 +0000 + + Added 'case' spec for case with empty 'else' body. + +commit 8f888bd3d0a01afc945c45c0502a0b97f3227c48 +Author: Arthur +Date: Tue May 29 20:34:18 2007 +0000 + + Extend Class specs. Some specs failing in rbx. + +commit f52d9faadc1eac31e7b92c1edb1cf45ca1d42c89 +Author: Tilman +Date: Tue May 29 20:29:53 2007 +0000 + + Implemented rb_str_new2() in subtend. + +commit d4d5e3d1eb8e5ee44acff6697a29a37b9eca25b2 +Author: Wilson Bilkovich +Date: Tue May 29 20:00:51 2007 +0000 + + * Patch by HaPK - Add specs for 'case', and enhance specs for 'for' + +commit f3e736731e852dacbf90e8e3e33d840384909354 +Author: Tilman +Date: Tue May 29 18:56:41 2007 +0000 + + Actually exercise rb_ary_unshift() in the spec. + +commit 69d756ae17fce1fb53be5e7a1b5b7169b69c4aa2 +Author: Tilman +Date: Tue May 29 18:55:32 2007 +0000 + + Implemented rb_ary_shift() in subtend. + +commit 9f84a5ecddae6c0daf1fd7e46815275c7d7429db +Author: Eero Saynatkari +Date: Tue May 29 18:50:41 2007 +0000 + + * Fix to Array#to_a for subclasses + +commit 2bbc87fc1b5261b57927a02f75915829b398b478 +Author: Tilman +Date: Tue May 29 18:50:33 2007 +0000 + + Implemented rb_ary_unshift() in subtend. + +commit 4e69b95ad7991a57fd3f9b7cbf350cb5b13c6a5c +Author: Tilman +Date: Tue May 29 18:46:53 2007 +0000 + + Implemented rb_ary_dup() in subtend. + +commit 122a9cfbe79e872146116c8e045a243fffd333e6 +Author: Tilman +Date: Tue May 29 18:31:48 2007 +0000 + + Marked SubtendArray method functions as static. + +commit 0e3319c07aa1d536343343fbbe1004c4cdce2df0 +Author: Tilman +Date: Tue May 29 18:28:05 2007 +0000 + + Implemented rb_ary_clear() in subtend. + +commit 621f0082fca85140791e2c40aabc8ad3fe3318a6 +Author: Tilman +Date: Tue May 29 17:58:31 2007 +0000 + + Implemented rb_ary_entry() in subtend. + +commit 9c7d05c0bb19e65f57fc6aab778785e2a727c4a4 +Author: Tilman +Date: Tue May 29 17:42:40 2007 +0000 + + Ticket #91: Made spec/subtend/rake_helper.rb more portable. + +commit 5dba201079bdf8da63364ea760342f3cef85df74 +Author: Wilson Bilkovich +Date: Tue May 29 15:56:01 2007 +0000 + + * Add specs for Array#pack, patch by HaPK + +commit 2d71e18c6f08144d4fb402904a9226a8500343bd +Author: Wilson Bilkovich +Date: Tue May 29 15:28:45 2007 +0000 + + * Better fix to method argument scoping, to support: def foo(a, b=a.length) + +commit f9deebb38b80cdea3dff44d7461404e5f501f566 +Author: Defunkt +Date: Tue May 29 09:59:00 2007 +0000 + + * Add spec for Struct subclasses. Closes ticket #110 + * Add failing specs for Class.new. Closes tickets #89 and #94 + +commit 3be02f950f32a288fac1cd5cff0ae014057c96fb +Author: Defunkt +Date: Tue May 29 09:12:00 2007 +0000 + + * Add should_include convenience method to make rspec more compatible with mspec. Closes ticket #106 + +commit 68e716e1874e7dd4186c7eef2aea5e25157a44fd +Author: Vagabond +Date: Tue May 29 03:46:33 2007 +0000 + + * Add HaPk's fix to Numeric#== to handle failed coersion with corresponding specs + * Removed duplicate definition of Numeric#== + +commit a63e6fcb08e34c625957d8d23bbe602964863c5b +Author: Wilson Bilkovich +Date: Tue May 29 02:59:43 2007 +0000 + + * Add Kernel#eval. Probably lacks some crazy MRI semantics at the moment + * Method definitions should properly create a clean scope for locals + * Support wacky default arguments, such as blah = lambda {|z| z.foo(another_arg) } + * Optional label prefixes in assembly output, for easier debugging + +commit f1295ac58d2b601f539efe0e660dfed9d043d1d7 +Author: Vagabond +Date: Tue May 29 01:39:46 2007 +0000 + + Change Time specs to use ENV['TZ'] instead of `date` in hopes of being more portable + +commit 404faeca93c007f3eb9b3df52c2bde7673565113 +Author: Wilson Bilkovich +Date: Mon May 28 19:20:27 2007 +0000 + + * Fix some mistakes in method_spec that caused it not to pass under MRI + +commit d336078c02e1306acb4b2664a427b63e93b02788 +Author: Wilson Bilkovich +Date: Sun May 27 06:03:25 2007 +0000 + + * Fix local scoping to allow for method definitions on local variables + * Pass all the horrible method definition specs + +commit 7759a0f91f794d05a32d48dd2e67d05c0b1dace7 +Author: Wilson Bilkovich +Date: Sun May 27 05:03:53 2007 +0000 + + * Rename Thread.yield to Thread.pass + * Implementation of 'module_function' by bremac, with minor tweaks + +commit 55f30c5e59d16ebbf045be93a7d406fed9a4dcbd +Author: Vagabond +Date: Sun May 27 00:34:44 2007 +0000 + + Added defunkt's implementation of Module#const_set and const_get and associated specs (Ticket #72) + +commit 9cebe0c56fda41b83ab14d39275e327daf0bdcc9 +Author: Eero Saynatkari +Date: Sun May 27 00:31:47 2007 +0000 + + * Fixes to mini_mock by bremac (Tickets #85 and #86) + +commit f33756f22597bd280e453d5c7ad97685fa284579 +Author: Eero Saynatkari +Date: Sun May 27 00:24:51 2007 +0000 + + * Fix typos in splat_spec (Ticket #77) by tilman + +commit ea13a828e5fc19694fc24da25b2224a75462a88c +Author: Vagabond +Date: Sun May 27 00:11:58 2007 +0000 + + Add Chris Wanstrath's (defunkt) Struct patches. Tested working against MRI. + +commit 3ca0ddcc2c39fec74f10b75df2af5c1581b9eaa3 +Author: Eero Saynatkari +Date: Sat May 26 23:56:26 2007 +0000 + + * Array fixes. Only spec failure remaining is #pack + +commit 4d91aa707a47189398455eb1c40b341dc3766ccf +Author: Eero Saynatkari +Date: Sat May 26 20:05:13 2007 +0000 + + * Array fixes to pass specs (including HaPK's code). + All remaining failing Array specs except #pack are + not Array bugs. Test this heavily. + +commit 6793b34a54ab8e24e8a66a8af026a34315ac9f5b +Author: Vagabond +Date: Sat May 26 07:34:12 2007 +0000 + + Add cdcarter's Enumerator implementation and specs translated from his test/unit tests + +commit fd10c39192825aeef68c8843c2813cf50b8137f2 +Author: Vagabond +Date: Fri May 25 20:12:22 2007 +0000 + + Some fixes to rand with associated specs + +commit 60d37d28b715854f5186598c90101824665ce715 +Author: Wilson Bilkovich +Date: Fri May 25 19:43:11 2007 +0000 + + * Properly normalize default method arguments, to support: def(x, y=puts('hi')) + * Fix 'for' loops so that they use 'create_block' in the proper way + * Move all 'for' processing out of compiler.rb + +commit 3c04a44e8ff9e84f48fbd2d3afabb886494b5a98 +Author: Wilson Bilkovich +Date: Fri May 25 06:35:48 2007 +0000 + + * Patch by HaPK - Fixes String#dump / inspect / upto + +commit 9e2442110ec33ff9ca4875407b227f2cf79a606a +Author: Wilson Bilkovich +Date: Fri May 25 05:36:31 2007 +0000 + + * Use a random pivot point for better worst-case Array#sort performance + * Add 'rake pristine' task to kill all .rbc files + * More tricky specs for splats and multiple-assignments + +commit 7bea77d8d3e8f190dba4f34fead888551fd07730 +Author: Wilson Bilkovich +Date: Thu May 24 23:50:06 2007 +0000 + + * HaPK's patch to String#<=>, along with its specs + +commit 0e6007e7eb9eee5e3ab1acdf55da00f4ab8c4be0 +Author: Wilson Bilkovich +Date: Thu May 24 23:29:49 2007 +0000 + + * Add spec for masgn semantics + * Fix numerous multiple assignment bugs + +commit 6d68d22efd7d2dba75c77cf957edb28dca6df6ef +Author: Wilson Bilkovich +Date: Thu May 24 07:05:00 2007 +0000 + + * New Array#sort implementation, fixes several Array and Hash specs + * Add a warning comment to bytearray.rb about some incorrect <=> behavior + * Prevent unimplemented Array specs from crashing the spec run + +commit 0ca089c7354ec96103cb637f861751ca7df01136 +Author: Wilson Bilkovich +Date: Thu May 24 01:15:40 2007 +0000 + + * Support all kinda crazy splat syntax + * Updated some compiler specs, though some TODOs remain + * Added a comment above unshift_tuple, since it really shifts + +commit bbe0b73b07a393f94724964941d2fdd717a2d72e +Author: Wilson Bilkovich +Date: Wed May 23 19:58:10 2007 +0000 + + * Add compiler support for: yield(*args) + * Update some compiler specs to match recent fixes + +commit 32a7082205d3d214ad43a477286270a96076b140 +Author: Kev +Date: Wed May 23 17:32:06 2007 +0000 + + Make spec titles consistent (describing C api behavior) + +commit 699c66f8c8304522fbb3589356fe2bcd298277c8 +Author: Wilson Bilkovich +Date: Wed May 23 06:56:25 2007 +0000 + + * Use yield instead of &prc.call when initializing a thread. + * Fixes VM crash / closes ticket #68 + * TODO - Why the HELL does this fix it? + +commit f8b6e1ff9e19e786b08fee30988eb874eae748b5 +Author: Wilson Bilkovich +Date: Wed May 23 03:18:26 2007 +0000 + + * Implement Thread.main + * Prevent Object#inspect from crashing the VM when the inspected object has itself as one of its instance variables + +commit f24f573608ee5569b29754a017769db0f866cf4c +Author: Wilson Bilkovich +Date: Tue May 22 22:35:14 2007 +0000 + + * Implement 'class_variables' method + * instance_variables and class_variables now accept an optional argument, causing them to return symbols instead of strings + * Support defined?(@@class_var) + * Support defined?(a_vcall) + * Fix false-positives in defined_spec.rb + +commit da540b51c47b2349b0ab8d4ca0bd11124138f9ce +Author: Wilson Bilkovich +Date: Tue May 22 20:52:30 2007 +0000 + + * Add compiler support for begin/rescue/else/end syntax + * Default rescue clause should be StandardError, not RuntimeError + * Add specs for 'else' and empty begin sections + * All Exception specs now pass + +commit 82abf73fd99ec45f7cb6d98d19b219a61af59a61 +Author: Vagabond +Date: Tue May 22 18:39:59 2007 +0000 + + * Fix Object#instance_eval to bring it into line with the specs and MRI + * Fix Object#instance_variable_validate to not accept fixnums as instance variable names + * Add another Object#send spec that tests exception raising for missing singleton method names + +commit 3b624f3f49c0433289224baf656b3d7be78cecd8 +Author: Evan Phoenix +Date: Tue May 22 08:15:48 2007 +0000 + + Fix the block arg scoping problem, also add a missing file from the compiler specs. + +commit 59af7028c060c8e3f9b9c107fb750a71dd37a1d6 +Author: Evan Phoenix +Date: Tue May 22 07:14:54 2007 +0000 + + A bunch of yummy-ness. Local variables now conform to the 'standard' behavior, ie they're allocated at different depths inside blocks (this is the yarv/jruby behavior). + + Cleaned up a couple of subtend things. + +commit f8ed63efac6fa661dd39db2c207b66c34d132546 +Author: Vagabond +Date: Tue May 22 03:28:17 2007 +0000 + + Add specs for Object #method, #respond_to? and #__send__. These currently fail with singleton methods on rubinius. + +commit 0d6e6b7109014c97d8f8be136166b3279d5a1108 +Author: Wilson Bilkovich +Date: Tue May 22 02:06:19 2007 +0000 + + * Handle 'call' nodes containing newlines, e.g. x = [5,6,7,8];p Hash[*x] + * This is probably the wrong implementation, but it does work + +commit 518f7d34112e536d726cecfb2473c7b3db9ec33e +Author: Wilson Bilkovich +Date: Tue May 22 00:52:01 2007 +0000 + + * Fix mini_mock's cleanup process + * Add the ParseTree sexp test cases as specs. Currently in serious need of auditing + +commit 17ad76c162ff0cfe9662c20d418f455581389b42 +Author: Evan Phoenix +Date: Mon May 21 21:50:13 2007 +0000 + + Add a failing spec + +commit 1744773b7f57c766c75d188b04e55540d45e19d0 +Author: Vagabond +Date: Mon May 21 20:22:04 2007 +0000 + + Do some env trickery to make Kernel#at_exit testable + Improve implementation and specs for Kernel#warn + +commit 45733aa44e8daee9e8c5e552ac9312f21163fe39 +Author: Vagabond +Date: Mon May 21 20:16:18 2007 +0000 + + Convert time specs to compare against output of the date command + Change Time#inspect to use %z (GMT offset) instead of %Z (timezone) + +commit e58ef35a05d2a565befeaf3600bc00f21203a84c +Author: Evan Phoenix +Date: Mon May 21 20:12:34 2007 +0000 + + Add spec for breakage caused by 1089. + +commit a5d54efe9a45f3acc1cdb0183a8c13ce6ed5e327 +Author: Eero Saynatkari +Date: Mon May 21 04:57:41 2007 +0000 + + * Options implements a minimal lightweight option parser + +commit b28b77af82d99a7a3ec5a78f6ab8b4e138ac577b +Author: Kev +Date: Mon May 21 03:54:33 2007 +0000 + + Add missing hash spec + +commit 81496352bdc2b6b27e293b7542908c6be54b9b6b +Author: Brian Ford +Date: Mon May 21 02:08:57 2007 +0000 + + added specs from ticket #38 (David Anderson), but not patch because implementation was invalid (e.g. [].first(0) => [] not nil) and superceded by recent patches. fixed Array#[i,0] => [] exposed by the added specs. + +commit 4ed6afc81262a4197f1ddc646ada94277cd9abe6 +Author: Brian Ford +Date: Mon May 21 01:07:36 2007 +0000 + + HaPK's patch to Array#[] with specs. Knocks 14 failures down to 6. Ticket #60. + +commit b267aee1c10b6092d954c72d5776f4eafc109e51 +Author: Kev +Date: Sun May 20 21:09:49 2007 +0000 + + rb_raise, rb_const_get, exception definitions. Wooooo exceptions from C + +commit 37793ed650e6ce7352a7547cf4bc68f2ceb2f0b4 +Author: Wilson Bilkovich +Date: Sat May 19 08:42:23 2007 +0000 + + * Added nastier multiple-assignment-with-splat specs + +commit 39c9817fa1932f9fe708a8ba78f43cb39e7cb68b +Author: Brian Ford +Date: Sat May 19 08:30:06 2007 +0000 + + twifkak's Kernel.fail patch with slightly modified specs. + +commit 264a42e8c11d08afa895b415453d59e1e1efe2e1 +Author: Wilson Bilkovich +Date: Sat May 19 08:01:47 2007 +0000 + + * Remove a misleading comment in exception_spec + * Pre-compile bin/*.rb after a make install + +commit 7608e585e02283677275aaf5e5283e397ed2d671 +Author: Brian Ford +Date: Fri May 18 23:57:09 2007 +0000 + + Vagabond's Kernel.warn. + +commit 9a41c5a21bbc822ff9ff758eb2962ba80e2d454b +Author: Wilson Bilkovich +Date: Fri May 18 18:52:36 2007 +0000 + + * Re-enable tr and unpack String specs + * Change 'Nan' to 'nan' in Sprintf to match MRI + +commit fdc7032c6e4823727312cc7e5c33386cf9d91429 +Author: Mental +Date: Fri May 18 06:01:20 2007 +0000 + + add spec for ensure result elision + +commit 37438dc826624c3fee3afc1d30a9f661bbb1ab8d +Author: Mental +Date: Fri May 18 05:45:28 2007 +0000 + + basic thread spec + +commit d89b7728d148ba8c1ddd74323aa8f9e3dae79691 +Author: Wilson Bilkovich +Date: Fri May 18 02:37:55 2007 +0000 + + * Fix 'should_raise' in mspec and rspec helpers + * Added some new Module specs, and fixed existing failures + +commit f63e0cf797158a239f65714918debf7a6c1bb687 +Author: Eero Saynatkari +Date: Thu May 17 04:12:58 2007 +0000 + + * First draft of a mock lib for mini_rspec + +commit c7fd82a8b4b84088de45463dbc25ae7eea5aabe2 +Author: Wilson Bilkovich +Date: Wed May 16 22:38:27 2007 +0000 + + * at_exit handlers should run in reverse order of registration + +commit 2fb5c6e46f1682d927be8a9e116a609c75ec8be5 +Author: Wilson Bilkovich +Date: Wed May 16 22:14:21 2007 +0000 + + * Fix Kernel.Array(). All core/kernel specs pass now + * Add Kernel#at_exit specs + * Fix Kernel#` + * Move AtExit handler array from Ruby namespace to Rubinius + * It's spelled 'occurred', not 'occured' + +commit 3d1605a3ca731b05b5c03ebd8a6edcf386612930 +Author: Brian Ford +Date: Wed May 16 06:04:16 2007 +0000 + + added incompatible specs for #instance_methods returning symbols. + +commit 8ba8409ae0ab94a33cd082f02a81d4d1eab35b59 +Author: Wilson Bilkovich +Date: Wed May 16 04:27:22 2007 +0000 + + * Patch by shadowfiend - Enhance Module specs and implement Module#instance_methods + * Make sure instance_methods always returns symbols, not strings + +commit 1e9b0066d712d4507260be02cf2bf116b2519af2 +Author: Wilson Bilkovich +Date: Wed May 16 03:24:35 2007 +0000 + + * MethodTable 'is a' Hash, and does not need its own fields in the bootstrap. Fixes 'Object.methods.keys' + +commit 8c57dd0e26cb5468c1b0150c5d9c5d80ae6f2de2 +Author: Brian Ford +Date: Mon May 14 06:18:42 2007 +0000 + + fixed class specs to pass on MRI. put rbx-specific integer specs in spec/rubinius. + +commit 8b43acd25a14f540447a9f958f7671822f836817 +Author: Brian Ford +Date: Mon May 14 05:59:06 2007 +0000 + + moved rbx-specific proc specs to a new home. made core proc_specs pass MRI. + +commit 4feb384d0a02b272bd1a3581dd4070ef475b25af +Author: Brian Ford +Date: Mon May 14 02:59:22 2007 +0000 + + added RUBY_ENGINE == 'rbx' to global constants and exposed Rubinius:: on Object like MRI. converted sprintf specs. + +commit 34ad791d5f60177de7992a24f07992bb0d6c8b09 +Author: Eero Saynatkari +Date: Mon May 14 02:25:06 2007 +0000 + + * Disabled Lightning's dissembler on amd64 + * Split specrunner into bin/mspec and a wrapper + +commit 8796b1f00501813c62676266508a6f89a82ec48e +Author: Brian Ford +Date: Sun May 13 22:47:28 2007 +0000 + + minor reorganization, cleanup of spec dir. + +commit 9be73815e2037dcc5347c2ef9876e76316efc504 +Author: Brian Ford +Date: Sun May 13 22:06:54 2007 +0000 + + specrunner outputs summary with 'examples'. converted language/literals, keywords, straggler method_spec. + +commit 647fe38ce5f132b7944cca8550233249d8b3c113 +Author: Brian Ford +Date: Sun May 13 21:21:04 2007 +0000 + + converted language, parser, library specs. + +commit d9e8f1fd3bc70231c89a1bdc17a9af5a46fce819 +Author: Brian Ford +Date: Sun May 13 08:28:21 2007 +0000 + + converted incompatible specs. + +commit 9a07bb52c526ce8883c53d437077d78510b0ac73 +Author: Brian Ford +Date: Sun May 13 08:09:34 2007 +0000 + + added Object#coerce_string that should act like 1.8.x StringValue function. added String#crypt and a couple other String things. + +commit 2bae9b5e3baa33da21c1335e84c2eab062eac3a4 +Author: Kev +Date: Sun May 13 06:21:06 2007 +0000 + + add rb_hash_delete + +commit 0ca1a5baa94b5984b0812365a408688420168d24 +Author: Evan Phoenix +Date: Sun May 13 05:31:24 2007 +0000 + + Imported GNU Lightning. subtend's rb_define_method_ now generates stub's to pop the args and call the function. Next step, add type conversion to call functions that don't take handles. + +commit 141e795d5042cb4ea398c9b8eaa9cd7045f5625e +Author: Kev +Date: Sun May 13 05:15:33 2007 +0000 + + Add rb_hash_aset + +commit 0988a253d8e23b400a738ad74637e8b3655eae8c +Author: Brian Ford +Date: Sat May 12 19:40:35 2007 +0000 + + new .rba's with rue's changes. converted spec/shotgun specs. added specs for Tuple. added aliases size, length for Tuple#fields. + +commit abd44484b4b2a28a4c7f0bf7acdf12ff30123729 +Author: Eero Saynatkari +Date: Sat May 12 19:32:06 2007 +0000 + + * Fixed class variables, should work everywhere now + * Specs for cvar behaviour + +commit 8ec7dac58577cea314ff0fcd976219b23591bc4d +Author: Brian Ford +Date: Sat May 12 07:40:42 2007 +0000 + + reimplemented Object#instance_variable_get|set and #instance_variables. now works with immediate values, and classes with no __ivars__. + +commit 30c4dd441243277ec5b814ad9b4d4697e87641d0 +Author: Brian Ford +Date: Fri May 11 07:56:14 2007 +0000 + + added primitives for instance_variable_get|set so that methods operate identically on objects that do not have an __ivars__ field (e.g. Array, String). this needs more work because an exception occurs when attempting to set|get instance vars on an immediate value. + +commit 998a0ab62542f36f9e36bdd497116349421951ce +Author: Eero Saynatkari +Date: Wed May 9 23:07:35 2007 +0000 + + * Converted rest of spec/core/ + * mini_rspec/specrunner improvements + +commit 0cac71dd1e4dbb728bd3401e73fda5b3fbe95e38 +Author: Brian Ford +Date: Wed May 9 08:18:59 2007 +0000 + + updated expectations to be the actual compiler output. these specs should be carefully reviewed. + +commit 8d551887fd1fabc7700f9f0a432b728829dcef96 +Author: Kev +Date: Wed May 9 07:42:15 2007 +0000 + + Pull out bundle that got caught in the commit + +commit 736916decc6d9bfd7096079a0118f41a168d735d +Author: Kev +Date: Wed May 9 07:41:42 2007 +0000 + + Add hash specs, and impl of rb_hash_new + +commit 2352f0a526be0f277f2e5d60f18acddc216045c1 +Author: Brian Ford +Date: Wed May 9 07:06:45 2007 +0000 + + converted test/bytecode/test_compiler to specs. + +commit 1f1d30f9ca690214a61f299a4bb408c2d28ef004 +Author: Eero Saynatkari +Date: Wed May 9 06:08:41 2007 +0000 + + * Converted MatchData specs + * Default warnings for empty spec files + * Improved specrunner + +commit 04c03e648ca83de2c2aee37f9aef9079d0493bd7 +Author: Eero Saynatkari +Date: Tue May 8 06:24:48 2007 +0000 + + * Converted Integer and Kernel specs + +commit a202ef1dfb21cebf3ee33376775d86b9dc89269d +Author: Brian Ford +Date: Tue May 8 05:45:33 2007 +0000 + + added before, after methods to mini_rspec. started adding compiler specs as conversion from test/bytecode/test_compiler. removed all host/target junk. thanks. bye. + +commit 692da2d89089bc94c95915c90da756480a057dc1 +Author: Brian Ford +Date: Tue May 8 03:36:51 2007 +0000 + + converted object specs. these really blow up rubinius. + +commit 714f5df86f583158d73eda366e2f2527156c3b8e +Author: Evan Phoenix +Date: Mon May 7 17:29:56 2007 +0000 + + The first compiler spec, testing the masgn assembly. Some are commented out because they don't yet work. + +commit 8ccfe13ca0eca4ceae6a201905a64666a75dd6ba +Author: Eero Saynatkari +Date: Mon May 7 05:52:41 2007 +0000 + + * Converted and reviewed Hash specs + * specrunner reports specifications and failures + +commit aa32b8e94de5c1ccd49a9d6ddca5836d6303c460 +Author: Brian Ford +Date: Sun May 6 06:50:52 2007 +0000 + + finished converting fixnum specs. + +commit 7c55264dc15ed2b8a1b341a5d605701c6626ad34 +Author: Eero Saynatkari +Date: Sat May 5 06:22:37 2007 +0000 + + * Converted Enumerable specs + +commit 1c660edd87fa91d8c244289b00eb9252d5654c3d +Author: Evan Phoenix +Date: Fri May 4 23:45:08 2007 +0000 + + Fix array_append as well as the logic to call the extension function so the arguments are correct. + +commit 6b9c27b8f8d12be443d37635e17b23b7f0d76388 +Author: Kev +Date: Fri May 4 07:54:10 2007 +0000 + + Complete rspec coverage of subtend string compat to date. + +commit 32db2e9a157cee24ae883b7b8fd563d98fc2dce5 +Author: Kev +Date: Fri May 4 07:11:21 2007 +0000 + + Add loading of C extensions via require. + Stop grammar.c from generating every fricking time + Cleanup formatting on subtend + Add proper minispec tests for subtend + Remove old subtend test extension + +commit 851fbe6e587596fd074b4c99e42c43865118ae00 +Author: Brian Ford +Date: Fri May 4 06:58:35 2007 +0000 + + converted (but not to the new new style) fixnum specs. fixed mini_rspec to rescue backtrace.show on MRI. + +commit 3e8deacb57ef80684281b1329778bc52681a8601 +Author: Brian Ford +Date: Fri May 4 06:22:53 2007 +0000 + + converted module specs. added incompatible spec for const_defined?(Some::Class). made mini_rspec print backtrace on error. + +commit 3c1cc4ff4f6bf4fa28f65d9909a74f77f6524aa8 +Author: Brian Ford +Date: Fri May 4 05:45:50 2007 +0000 + + converted math and exception. added two helper methods: should_be_close, should_include. I think spec_translator should handle converting these to 0.9.x syntax as soon as rspec runs. + +commit 28e3cdba63f2853b9e9a084f27ad764437830799 +Author: Brian Ford +Date: Thu May 3 15:26:39 2007 +0000 + + converted float specs. added ignore for *.rbc on externals/rspec-0.9.1. + +commit 69ea5db15fb0562d8a4114d4e8ec54f2e19ad8fd +Author: Brian Ford +Date: Thu May 3 06:40:17 2007 +0000 + + converted range, nil, regexp specs. added ignore *.rbc on rspec dirs. + +commit 6cc364770406e4e04ef7baf2fdaab7425a7f5a6c +Author: Eero Saynatkari +Date: Thu May 3 03:27:04 2007 +0000 + + * Converted Dir specs + +commit 5e39be7f97d5cd131b0cf564746d881245030f7d +Author: Brian Ford +Date: Thu May 3 03:04:24 2007 +0000 + + okay, rue insists that we use describe ... it now. (see spec_translator with rspec 0.9.1). + +commit 99f05b9d6572600ed0bf6a732048c1c4a2d2bb0b +Author: Brian Ford +Date: Thu May 3 03:00:06 2007 +0000 + + converted bignum specs. 100% pass on MRI. + +commit f9e4df4bdb721eb32c4ac7e5abd4a646daaf20d2 +Author: Brian Ford +Date: Thu May 3 00:06:19 2007 +0000 + + fixed mini_rspec aliases for specify, etc. to work around exception: No method 'alias_method' on an instance of Object. (NoMethodError) + +commit 89d3ca0681816afd389907cbb52f7e0372dbecef +Author: Eero Saynatkari +Date: Wed May 2 07:13:56 2007 +0000 + + * Converted spec/core/ binding, class and continuation + +commit 086f889a9bae2e40dd6a8b1ffa80113070f3ad46 +Author: Eero Saynatkari +Date: Wed May 2 04:22:12 2007 +0000 + + * bin/specrunner is a small bash kludge for running mini_rspec over + a directory (recursively) or a single file + * Rakefile allows diffing a current spec run against a base run to + easily see all changes among the thousands of specs as well as + storing a base run + * specrunner produces decent output for the minimal spec output + from mini_rspec + +commit ba89b2c015d2754b6470b324a013f018d8202cfe +Author: Eero Saynatkari +Date: Tue May 1 22:57:22 2007 +0000 + + * Converted spec/core/array_spec.rb to mini_rspec + * Reviewed and fixed some specs for Array + * Spec-style output to mini_rspec (manual comment/uncomment to switch) + * should_raise for slightly more natural exception verification + +commit 0330bcc23fa1609db291cd382cb13fc168ec5bf3 +Author: Wilson Bilkovich +Date: Tue May 1 22:44:40 2007 +0000 + + * Implement correct behavior for String#split when called with a zero-width Regexp + +commit c8e806e2dafd237fa8117ead21553a195900613e +Author: Brian Ford +Date: Tue May 1 02:29:35 2007 +0000 + + converted symbol_spec. 100% pass. + +commit 76e31065df70ebc5790fdb604f1b07d28ffaa81c +Author: Brian Ford +Date: Tue May 1 02:19:33 2007 +0000 + + commented out specs that cause rubinius to hang. String#delete and #tr (and methods that are implemented in terms of these). + +commit 2ae8aea13161a71c3fb4ca8e0486acd55c897579 +Author: Brian Ford +Date: Tue May 1 01:17:14 2007 +0000 + + converted core/string_spec to regular syntax. added mspec_helper. + +commit ff84053991295b259ca8b1c17adff95f5d471961 +Author: Brian Ford +Date: Mon Apr 30 22:31:26 2007 +0000 + + converted false_spec. added svn:ignore *.rbc on all spec dirs. + +commit 3fc864ba235c56118e1db66dbf9537d6ff8c0c5f +Author: Brian Ford +Date: Mon Apr 30 22:00:12 2007 +0000 + + Let the breakage begin. Converting all specs to use mini_rspec with 100% compatible syntax with rspec proper. usage: 'USE_RSPEC=1 spec spec/core/false_spec.rb' for any specs that use example {} method. spec spec/core/true_spec.rb OR ./shotgun/rubinius spec/core/true_spec.rb for converted specs. + +commit 4c6c7f406d0e5504a72c52b1ae5339a9dba36865 +Author: Brian Ford +Date: Sun Apr 29 17:28:11 2007 +0000 + + added setup method and print to STDERR and STDOUT to support a shell script runner. + +commit 958a0e9b1a066cf2d825b960b66788b05c928f36 +Author: Brian Ford +Date: Sun Apr 29 08:26:09 2007 +0000 + + mini rspec implementation. example {} method is dead. + +commit a323b3d424f226322cf20e65e87f8a4e962ed497 +Author: Brian Ford +Date: Sun Apr 29 03:23:17 2007 +0000 + + Added Array#first, Array#last that take numeric args to core. Added a bunch of failing specs for Array#[]. #first and #last are implemented using #[] so several of the specs for those fail, but the implementation of #first and #last was tested in MRI. + +commit 3897c943069582b1e5d1649a097bd77c0c895e0a +Author: Hurdlea +Date: Thu Mar 29 13:51:13 2007 +0000 + + * Support for Floats in Sprintf + - Sprintf is still missing support for unsigned twos complement + * String#% now implemented + * Fixed a minor issue in the Rakefile + +commit 5ed87ff88793f8d44cfe34b443eb032d27dc2a4c +Author: Hurdlea +Date: Thu Mar 15 05:08:34 2007 +0000 + + * Added Sprintf core module and classes for string % and Kernel + - Still needs some work with floats and requires a couple of + primitives to achieve this. + * Fixed a small issue with String#Index(Fixnum, offset) + +commit 982c09b15710429fc97d8d43d9f24a3a0badb6d5 +Author: Brian Ford +Date: Sat Mar 10 22:35:18 2007 +0000 + + Fixed array spec for #sort which improperly depended on the accidental order in which two elements of the array were being compared. + +commit 32fe004da7f35e9b7dcc96f8e57e1acb37164748 +Author: Hornbeck +Date: Sat Mar 10 06:38:23 2007 +0000 + + Two tests in the ObjectSpace spec. It was bare and needed love. + +commit e1530bb1999118bf88037dccc27d78f54bdbe5e4 +Author: Tlockney +Date: Sat Mar 3 23:28:27 2007 +0000 + + updated all rspec exceptions. exception specs all pass in MRI. still a few rubinius exception spec issues + +commit 2c278533cbfe0efc7076d2c947323640be5f207a +Author: Brian Ford +Date: Sat Mar 3 21:29:55 2007 +0000 + + Committing tlockney's additions to core/exception_spec.rb. These pass on MRI but illustrate areas to fix on rubinius. + +commit fd8993c0996e4524440a6572c45dad4ab112fb2f +Author: Hornbeck +Date: Fri Feb 23 15:58:33 2007 +0000 + + committing Aki Reijonen's Hash patches for hash.rb and the hash_spec.rb. Also included is Thomas Lockney's exception_spec.rb patches. + +commit 08e6d924b8c0175242c1c40322ed3e45855a86c2 +Author: Brian Ford +Date: Sun Feb 18 07:48:46 2007 +0000 + + Altered Object#instance_variable_[get|set] rearranging flow control. Added specs for instance_variable_[get|set] for Array, IO, String. + +commit 2a2385413c03f21dfc038e110f46a7a3bd2fc9c7 +Author: Brian Ford +Date: Sun Feb 18 06:05:47 2007 +0000 + + Increased time out value when running rspec error report. Minor changes to text in class specs. + +commit bd0d7fcf72546a0a3a5a6a59b1a6f2aadd8e4262 +Author: Mae +Date: Sun Feb 18 03:53:00 2007 +0000 + + Integer#bits for future refactoring of shift + * added Integer#bits which calculates minimum bit storage required for (signed int) form of the Integer + * spec'd it too + +commit 4e6b39d5e69c04d92ceac76ce5a5bd792fb65f39 +Author: Mae +Date: Sun Feb 18 02:55:55 2007 +0000 + + Object#extend-a-gogo + *Fixed Small bug in rubinius_target where failures wouldn't be reported + *Implemented Object#extend and changed math.rb to use it accordingly + +commit 5472c10579cef38f9f28c904710246509633a040 +Author: Mae +Date: Sun Feb 18 01:21:11 2007 +0000 + + A great Time patch from John Hornbeck : + + A more complete Time diff. This includes many of the instance methods for Time and a new primitive for usec. This diff also includes some failing specs as I went ahead and added the specs for the rest of the class methods. Also included is the constants for Time. + + Keep the good work coming John! + +commit 83ab11e0ab6679b1c9eefc5095d3f20af9a61661 +Author: Mae +Date: Sat Feb 17 23:26:33 2007 +0000 + + Patch from Aki Reijonen without the Float.induced_from part + + Summary of the changes: + + ** Added methods ** + + Numeric#integer? + Numeric#div + + Integer#to_int + Integer#round + Integer#truncate + Integer#next + Integer#succ + Integer#integer? + + String#slice! + + Object#to_a + + Kernel#Array + Kernel#String + + ** Fixed methods ** + Float.induced_from + - Now return the passed object if it's an Float insted of calling #to_f + + ** Removed methods ** + Fixnum#div was broken, the end result should be converted to Integer, + not the number passed as a argument. (superceded by Numeric#div) + + -- + Aki Reijonen + +commit 243a4e9ba46149b8ba39c7238f8ff3d5f267689e +Author: Brian Ford +Date: Sat Feb 17 06:17:44 2007 +0000 + + Ditched all the instance vars in array specs since we've got locals now. + +commit be5363e22e04b8baf26cb4abd8a8a67e7dd3cc0c +Author: Eero Saynatkari +Date: Wed Feb 14 01:05:49 2007 +0000 + + Fixed * varargs to work in method definitions. Currently still + does not work as the single named parameter (foo(*a)). This + means that lib/bytecode/encoder.rb now compiles under Shotgun. + +commit 3bb810688e848c90d5c20929c630f36a32796d2d +Author: Brian Ford +Date: Tue Feb 13 18:42:09 2007 +0000 + + Added Object#instance_variable_set and specs. + +commit 54392c99dc3db5b58c85799416cc528c60b12533 +Author: Brian Ford +Date: Tue Feb 13 18:25:24 2007 +0000 + + Added Object#instance_variable_get and specs. Uncommented Math module constants specs. + +commit 84267901502ca1e8e8b13afa0e3a16e0cdc8e493 +Author: Mae +Date: Sun Feb 11 10:30:13 2007 +0000 + + * Primitive Specs + - Added spec for bignum_div (and fixed a problem where it would always fail) + - Fixed primitive_spec_helper (because it broke the old specs last time) + - Removed magic method chaining because it sucks + - DISCLAIMER: primitive specs atm just test functional things, _NOT_ stateful side-effects + - We can do this properly once rubinius can run rspec + + * SIRB + - Made it so that => wouldn't get printed before the command prompt if you typed "exit" + - Made Kernel#p, Kernel#puts, Kernel#print return nil (like MRI) + - added #!shotgun/rubinius to top of sirb and symlinked it to bin/sirb.rb as well (for convenience) + + * Removed unused local from __loader + +commit efce7d8a56748ab1831a34d21b8c92ff8b2eb977 +Author: Mae +Date: Sun Feb 11 07:25:51 2007 +0000 + + Moved math to math_spec; added object_spec for primitives; made primitives_spec_helper maybe too smart? -- they chain methods on to the remote target + +commit 087a5e5a6e89e4a53a39e025ffe08d21e96b8f6e +Author: Mae +Date: Sun Feb 11 05:51:30 2007 +0000 + + * Made rubinius_target and example much more helpful + - backtraces are shown on failure now + - you can do this: example { 1 + nil }.should_raise(TypeError) and it works :) (with bt and all) + - injected some extra code in example snippets so try(exc) syntax still works + - Float, Nil, True, False specs all pass 100 % + - made rubinius_target make use of @src (used in bignum) + + * Made Kernel.Float() and Kernel.Integer() behave appropriately like MRI _with_ the exception of Float() also checking for to_i method + - Integer(nil) => 0 and Float(nil) => 0.0 + - lots of spec coverage + + * Numeric#coerce was slightly tweaked to use new Float() and Integer() behavior + - Specifically complains about other being nil (so 1 == nil doesn't work) + + * Made Float.induced_from() more anal retentive (only accepts core Fixnum, Bignum, Float types like mri) + - specs cover it + + * Fixed infinite loop on Bignum#& and moved & out of Numeric into Integer (Float doesn't have &) + +commit eadf1ead754d3dbfaf703c205f6f5e8f4dc5c430 +Author: Brian Ford +Date: Sun Feb 11 03:00:25 2007 +0000 + + Put object flags values into a single include file. Added Object#taint, tainted?, freeze, frozen?. Neither of these states actually effect execution yet. Fixed up Object specs. + +commit 38e7f757e67b4ec985835e0e93ba4d32bbee5ca4 +Author: Mae +Date: Sat Feb 10 23:57:37 2007 +0000 + + - Created specs for math_sqrt primitive (and created spec/primitives/math_spec.rb) + - Tweaked primitives_spec_helper to properly transport NaN's to testing environment + - Removed non-needed self parameter from math_sqrt c function + +commit a4267a136d7f0bf7f92421fcebd8011600a1d92e +Author: Mae +Date: Sat Feb 10 22:29:46 2007 +0000 + + Float.induced_from love + - made Float.induced_from work for any to_f item (controversial whether this should be done in Kernel.Float() or not) + - apparently this fixed some float failures + - made Float.induced_from safer because it now complains if to_f returns a non-float + - wrote specs for new induced_from behavior + - try (spec_helper) needs to be investigated, manual running of the premises of 'Float divmod should raise FloatDomainError if other is zero' show this to be a spec that _should_ pass + +commit 65a4e8abfc7f690456e4f44e7e4cc38911288516 +Author: Brian Ford +Date: Sat Feb 10 18:54:15 2007 +0000 + + Added spec files for the rest of the core classes documented in Pickaxe book. Add simple class hierarchy specs for exception classes. + +commit c7a2f68c36dd95f51af88e8fa62b24b71d68578a +Author: Brian Ford +Date: Sat Feb 10 17:29:46 2007 +0000 + + Commit of scoopr's Math module beginnings. Thanks scoopr. + +commit b1e8d150460f2ae9ea2e5ef87d0df3b705c1d0d6 +Author: Mae +Date: Fri Feb 9 08:54:25 2007 +0000 + + Bignum primitive specs + - Added spec and changed to metaprogramming style for primitives: bignum_add, bignum_compare, bignum_equal, bignum_and, bignum_divmod + - fixed bug that bignum_divmod spec found where bignum_divmod would always fail on divide by zero GUARD + +commit 0487a39ec9995af8eb5a8dff5ec64492261852e7 +Author: Mae +Date: Fri Feb 9 07:48:56 2007 +0000 + + Primitive Spec Sexiness + - Added spec and changed to metaprogramming style for primitive fixnum_to_f + - Made usage of run_primitive(:add, 1, 5) more sexy i.e. 1.prim.add(5) + - converted fixnum spes to use sexiness + +commit 95fa48f29eaa7e3f10ccd63d385fb3f582f57eea +Author: Hurdlea +Date: Fri Feb 9 01:36:14 2007 +0000 + + * Added String#delete, delete!, tr, tr!, tr_s, tr_s! + - String#count and squeeze to follow ... + * String#<< now accepts Fixnums + +commit 017bdc57602e2e5d55705de070c07edba46a347f +Author: Mae +Date: Thu Feb 8 09:23:55 2007 +0000 + + - removed noop from primitives (it does nothing) + - removed noop primitive spec + - changed CPU::Primitives.name_to_index to offset by +1 (to leave room for special 0 value) + - still having same closed parens issue with spec:primitives: + syntax error, unexpected $end, expecting ')' (SyntaxError) + +commit 18a3347bb32d8ac5269438376f0100ecce2c9e73 +Author: Mae +Date: Thu Feb 8 08:20:14 2007 +0000 + + - made shotgun/lib/primitives.rb have less dependencies + - fixed bug where if a false was popped of the stack it wouldn't be recognized as an argument in primitives_spec_helper (nil will only do this now) + - fixed regression in primitives_spec_helper where the proper code wasn't showed when shotgun crashes + - noop_spec works again + +commit 16b08e446b69344da1edbc1f793e0161deac8e6c +Author: Mae +Date: Wed Feb 7 23:59:14 2007 +0000 + + More Primitive Goodness, Conform to unified rspec standards + - Added specs and changed to new metaprogramming style for the following primitives: fixnum_and, fixnum_or, fixnum_xor, fixnum_invert, fixnum_neg + +commit cb2ac85b45a41a63100cac673919ad8db1f93f43 +Author: Eero Saynatkari +Date: Wed Feb 7 23:16:35 2007 +0000 + + Basic specs for Symbol literals. + +commit 3ab7aced51f3a63c8f76706a2f159d0d5753dc64 +Author: Brian Ford +Date: Wed Feb 7 20:35:27 2007 +0000 + + Changed Fixnum primitives specs to be in a single file, spec/primitives/fixnum_spec.rb. Added back the alternative example of writing specs for others to evaluate or use. Updated the wiki specs page to lay down the law on spec files. Kindly follow it. + +commit cf16d691990f43f5bf8807bbef2ba1876892be57 +Author: Mae +Date: Wed Feb 7 18:27:15 2007 +0000 + + Autotest, C warnings cleanup, and some primitive_spec usage cleanup + - Added Autotest Facilities for primitive bin/autotest/primitives + - need to gem install zentest to use this (and some diff gem i can't remember) + - Removed ugly require statement from cpu/primitives it was causing annoying ruby errors + - Added missing prototypes to cpu.h and regexp.h (primitives.gen was complaining) + - Localized bt and bt_size variable declarations to where they would be included by the preprocessor to make more warnings go away + - Added newlines to the end of numeric.c, numeric.h, float.c + - Used one of brixens suggestions (injection of primitive spec helper automatically) + - Removed extraneous primitive helper inclusion in each spec + +commit f4bbce9d761d27e1381b95a4ff6076e85577074d +Author: Brian Ford +Date: Wed Feb 7 16:56:21 2007 +0000 + + This shows an example to 'fix' mae's rubyesquely-challenged (bluntly, ugly) primitive specs. Also, there should be one spec file for a group of related contexts. In this case, the group is the class Fixnum. So, mae, fixnum_spec.rb, NOT fixnum_xxx_spec.rb. Sorry. Cry tyrany, weep and gnash thy teeth, howl in protest, but please fix it. This is non-negotiable. Thank you and good work on the primitive specs. :) + +commit e5f6215824a40beb0ca678575596bd06afa8dd3a +Author: Mae +Date: Wed Feb 7 09:54:54 2007 +0000 + + - Added specs for primitives: add, sub, fixnum_mul, fixnum_size, fixnum_div, fixnum_modulo, fixnum_divmod, fixnum_to_s + - Updated primitives to new metaprogramming style: sub, fixnum_mul, fixnum_size, fixnum_div, fixnum_modulo, fixnum_divmod, fixnum_to_s + - Changed wording in a couple primitive spec files to be more explicit + - Made reporting by primitive_spec_helper more helpful when shotgun crashes from injected code + +commit 3032c6bd869a04c1517508850f94119975c36e54 +Author: Brian Ford +Date: Wed Feb 7 02:26:47 2007 +0000 + + Fixed up String#to_i a bit; added a bunch more specs for it. + +commit d8a24ffa8d9983a85b0f03784a89bfa667af1615 +Author: Eero Saynatkari +Date: Wed Feb 7 01:46:30 2007 +0000 + + Added very basic set of specs for assignment and multiple assignment semantics. + +commit edb7c82523b36b26e24437de42fd2638eef1653f +Author: Brian Ford +Date: Tue Feb 6 19:02:46 2007 +0000 + + Added specs for and methods CType#isalnum, isdigit. + +commit e84ba1b12c51331d00bdd06684dcff96ea229322 +Author: Brian Ford +Date: Tue Feb 6 17:30:55 2007 +0000 + + Added spec/shotgun/bytearray_spec.rb. Added spec for ByteArray#[], []=. Modified various string methods to use BA#[], []= instead of get_byte, set_byte. + +commit be9589cc47cbf35edd94ca22407de4b1527a3fdb +Author: Mae +Date: Tue Feb 6 11:26:31 2007 +0000 + + Tweaks to primitive metaprogramming and addition of noop spec + - Added types 'qnil' 'qtrue' 'qfalse' to be used in primitive metaprogramming + - Added spec for noop primitive + - Added run_asm method to primitives_spec_helper for those tricky tests + - Converted noop primitive to new metaprogramming style + +commit e31f1af903dd8dd31427e34a718b30f5c63af8df +Author: Brian Ford +Date: Tue Feb 6 10:33:00 2007 +0000 + + Some fixes to String#to_i and additions to specs. Added String#oct and hex. + +commit 2a157827bd72b6c7ce8a025928cdd9d7f2f8d00f +Author: Mae +Date: Tue Feb 6 10:18:16 2007 +0000 + + moved equal and compare to use new primitive technique + - also updated equal_spec to ask for ArgumentError instead + - uploaded new rba *glares at brixen* + +commit 771d0fede3086ce58d225ac4001ea0934f3bb0e3 +Author: Mae +Date: Tue Feb 6 09:33:03 2007 +0000 + + ARITY macro raises ArgumentError directly from the primtive now and made specs pass + - Made ARITY macro raise an argument error exception instead of just ambiguously failing + - Made _ret return TRUE on arity failure (exception directly raised) + - Moved GUARD and POP macros to shotgun/lib/cpu_primitives.c where they belong (localized) + - Removed side-effect printf in cpu_raise_arg_error since stack trace is fine now + - Tweaked primitives_spec_helper should_raise to work for all exceptions + - Made specs for equal/compare pass again (expect ArgumentError instead of PrimitiveFailure) + - New compiler.rba (update these ppl!) + +commit e89190c8fdc4a71c7b8cd9c8b873a63b9d1888c5 +Author: Hurdlea +Date: Tue Feb 6 07:51:08 2007 +0000 + + * Finished String#[]= for string index + * found odd bug with spec where string[1,2]="foo" is not interpreted correctly + changed methods to use send(:[]=, ... and the tests pass + +commit c6e1bb68e930a537bd51d77afd37cdc8b5d62d31 +Author: Hurdlea +Date: Tue Feb 6 07:18:15 2007 +0000 + + * Added String#[]= slice functionality + +commit e405d4f5f32fd8192c435b3488f394b2635c7db7 +Author: Brian Ford +Date: Tue Feb 6 06:37:31 2007 +0000 + + Added String#chomp[(bang)]. + +commit ff48a6c333f34c1b1882c260db7145facce3d71f +Author: Brian Ford +Date: Tue Feb 6 06:00:56 2007 +0000 + + Added String#replace_if that calls replace if self != other else returns nil; Added upcase, downcase. + +commit f46d747eba82c215fa07b067a30f2a2e8868d284 +Author: Brian Ford +Date: Tue Feb 6 05:31:13 2007 +0000 + + Implemented String#reverse directly rather than with String#<<. Modified some string specs for [lr]strip but forgot to commit them earlier. + +commit d3b0e71e810a985f3b8f2e5f5c7d5c4619f151f9 +Author: Hurdlea +Date: Tue Feb 6 03:00:28 2007 +0000 + + * Added NilClass specs to detect NilClass coercion + +commit c570ca475cabeb3fcfcca26d4c57b8e57f6606b4 +Author: Brian Ford +Date: Tue Feb 6 02:01:19 2007 +0000 + + Added module CType mixin for Integer to provide isspace, isupper and friends. Added specs for CType in spec/shotgun. Implemented String#capitalize[(bang)]. Moved ByteArray into it's own file. Updated various string specs and commented out temporarily index spec. + +commit 4694d1511e880e43dfccb3e3f5309f0920395ba0 +Author: Hurdlea +Date: Tue Feb 6 00:22:17 2007 +0000 + + * Fixed operation of Regexp#=~ + * Updated Regexp spec for =~ + * String#== now works correcly for duck typed objects + +commit 8e42aa9c789fcc9bc475d460e7158f2adcc8ab64 +Author: Mae +Date: Mon Feb 5 22:46:21 2007 +0000 + + Added arity checking for primitives + -for use in primitives: #define ARITY(length) GUARD( (length) == num_args ) + -for instance if i have a primitive that takes one argument (self + arg) i put ARITY(1) at the top + -changed specs with regard to arity accordingly + -made block_given conform to the "self rule for primitives" by padding Qtrue where self would be + -fixed block_given? to pass the right arity (0) in the compiler + -specs for compare and equal pass now + -added primitives_spec_helper (forgot last time) + + NOTE: Binary .rbc compatibility is broken now since there are arity checks done on block_given? + -the rba's i uploaded should be fine but if they arent... + do find -type f | grep .rbc | grep -v .svn | xargs rm + then rake build:rubinius + +commit 2d5c9bc3170bf959390627def10c0208088b48ee +Author: Brian Ford +Date: Mon Feb 5 22:39:08 2007 +0000 + + Added Integer#isspace and spec in spec/incompatible. Added spec/incompatible/string_spec to describe behavior of stripping runs of whitespace and nulls from end of a string. Added or modified String#lstrip, lstrip(bang), strip, strip(bang), rstrip, rstrip(bang). + +commit d4b07b06ca85543423a308f12b82ae4671bdd0c2 +Author: Mae +Date: Mon Feb 5 21:01:59 2007 +0000 + + -split out common primitive testing functionality to primitives_spec_helper.rb + -added spec for primitive "equal" + -again primitive specs are rake spec:primitives + +commit c1a7896f24e018df13af7f0d3d60db9f461130a5 +Author: Brian Ford +Date: Mon Feb 5 20:58:21 2007 +0000 + + Added that Module#include passes off to append_features. Added Module#include that takes multiple args later in the bootstrap sequence. Added specs for include and append_features. + +commit d407ecab13722599b75fecc20bdebd86c9f76fa6 +Author: Mae +Date: Mon Feb 5 20:48:02 2007 +0000 + + Misc Changes to Tweak primitive specs + -Added spec to test arity restrictions of compare + -Fixed bug where should_raise for primitives was not catching the error condition + -Changed wording of some specs to be english rather than engrish :) + +commit f68ad63065002d4a3c9a0742770da4a112780aa7 +Author: Mae +Date: Mon Feb 5 20:02:24 2007 +0000 + + - Remove printf from cpu_raise_primitive_failure so that the screen doesn't get littered + - Created PrimitiveSpecHelper and a "primitives" spec subdirectory + - Added rake task spec:primitives + - Added compare_spec as an example + - New rba's + +commit f997d3791099912001d09a427f24252182ba1d6c +Author: Hurdlea +Date: Sat Feb 3 08:10:48 2007 +0000 + + * Added MatchData#values_at + * Fixed implementation of MatchData#select + * Updated specs for MatchData#values_at, select + +commit 4f8301aeb3a5a296a64b887b0f164ca02be2a71f +Author: Brian Ford +Date: Fri Feb 2 17:00:10 2007 +0000 + + Added specs for and empty module methods private, protected, public as a first approximation to allow code that uses them to not choke. Added String#match. + +commit 15c3678ddcc365891fd92cc9cd33eb22308916e8 +Author: Brian Ford +Date: Fri Feb 2 10:43:58 2007 +0000 + + Committing Adam Ritter's patch to recognize 'for i in ...' expression, and associated spec. Uncommented line in float_spec. + +commit 370d7a955bf6e41c4ea7cf0f9217128ae7a72fd4 +Author: Hurdlea +Date: Fri Feb 2 07:06:22 2007 +0000 + + * Added MatchData#inspect, select, to_a, size, to_s + * MatchData#[] is now more compliant - behaves more like Array#[] + * Added Regexp#hash + * Added Regexp#hash spec + +commit b496d50c0ebf7d5c523efe2ef5383dd8043aa3f0 +Author: Eero Saynatkari +Date: Fri Feb 2 00:36:37 2007 +0000 + + Added specs for Dir and modified rubinius_target to allow + specs to change directories safely. + +commit 6ddf4051f3a6be7076e947bf3eccbc5dd9a7803f +Author: Brian Ford +Date: Thu Feb 1 19:11:14 2007 +0000 + + Fixed Float#divmod, returning 0 guard on primitive, raising FloatDomainError rather than ZeroDivisionError. Fixed Float#% when other is zero. Spec try helper doesn't yet work with rubinius_target + +commit 679f3fbe54960a690f4e41e1403fdc8f50c0f346 +Author: Brian Ford +Date: Thu Feb 1 18:36:44 2007 +0000 + + Added more zero division behavior specs. + +commit b4d739a7cb68d6f82d657b69aee00923e0bfdbb4 +Author: Brian Ford +Date: Thu Feb 1 18:12:02 2007 +0000 + + oops, damn keystrokes. Previous commit msg should just include Fixnum. This change points out a problem that I'm not sure about: 1.quo(0) => Infinity in irb and run from a file, but in the spec I get zero division error. wth? + +commit 4e24fe43a7d6c55a53880a1c347e836f12937ed4 +Author: Brian Ford +Date: Thu Feb 1 18:03:13 2007 +0000 + + Added more specs around zero division behavior for Float and Fixnum. + +commit b8f412ee2bf3701acd211372d28ec596d6858ac8 +Author: Brian Ford +Date: Thu Feb 1 18:01:44 2007 +0000 + + Added more specs around zero division behavior for Float and Fixnum. + +commit 17a17e3008422bab9e91f8464d1ce2823c13ce78 +Author: Brian Ford +Date: Thu Feb 1 17:51:47 2007 +0000 + + Added try spec helper method for spec'ing things that raise exceptions. Added more Float specs that describe division by 0 behavior. + +commit 2c2bfc3663a34fbf4fd70a5787236ec8b9a87024 +Author: Brian Ford +Date: Thu Feb 1 17:22:39 2007 +0000 + + Added spec for Float#% when other is zero to show current implementation is broken. + +commit d1ddd71d5bd45df0c16651ecad2db3c1b75d90f8 +Author: Brian Ford +Date: Thu Feb 1 17:06:56 2007 +0000 + + Reverted mae's breakage to Float. seriously mae: you did NOT run the float specs and you did NOT write new specs for the behavior you were changing so you did NOT understand what you were doing. As a good CS student, you can negate the above to know what you SHOULD do. ;) Please, WRITE and RUN your specs. + +commit b2e08a170d1ab222d67d8767fa880a5e21c5bf74 +Author: Mae +Date: Thu Feb 1 10:53:48 2007 +0000 + + know when to shoot your baby in the crib -- cleanup outdated unused code + +commit aac75dd658c96cf930852d86dbc79b66830bace5 +Author: Eero Saynatkari +Date: Thu Feb 1 09:12:03 2007 +0000 + + Improved specs for Hash.[]. + +commit 40f637f2685e969f097fbbb2ffa3f0173e6f9866 +Author: Mae +Date: Thu Feb 1 02:44:14 2007 +0000 + + Fixed my pure ruby Numeric#floor and Numeric#ceil methods + - Please smash your c primitive brix BWAHAHA + - Also implemented eql? for float and now all 32 float specs pass + - Added some edge cases for ceil/float that were not previously covered in specs + +commit 5cc6f6b6068e945c6f5896370ee20567e57122e7 +Author: Brian Ford +Date: Wed Jan 31 11:24:23 2007 +0000 + + Added Float#round. We now have 32 of 32 float specs passing. Please confirm on your platform. + +commit 9b902a80a008120a86ae18d4abff04d42efefc8f +Author: Brian Ford +Date: Wed Jan 31 06:22:10 2007 +0000 + + Folded in coerce specs. + +commit 09e61132d5b9e9b08d27f2f51db9580808bb370e +Author: Brian Ford +Date: Wed Jan 31 03:22:23 2007 +0000 + + Created spec/incompatible for specs that show where Rubinius is incompatible with other implementations. Added bignum_spec and fixnum_spec to incompatible dir. Under MRI, Bignum.coerce(Fixnum) => [Bignum, Bignum] whereas Fixnum.coerce(Bignum) => [Float, Float]. Since Bignum should be a seamless extension of Fixnum, this behavior in MRI seems less than consistent. Under Rubinius, mixed Fixnum and Bignum promote to Bignum uniformly, and this makes much more sense. There are other places where Rubinius implementation may deviate from MRI, so spec/incompatible is for describing those behaviors. Updated coerce specs. + +commit cb52bb9633d0e323d2f7d6c90879fb7decfea7d7 +Author: Brian Ford +Date: Tue Jan 30 10:44:46 2007 +0000 + + Added Numeric#coerce primitive. Reimplemented a number of primitves and methods on Fixnum, Float, Bignum to use Numeric#coerce rather than implementing knowledge of one another all over the place. Folded in mae's coerce specs. There is currently a (desirable IMHO) incompatibility in Numeric#coerce in that Bignum.coerce(Fixnum) == Fixnum.coerce(Bignum). There are a lot of other methods that need to be reimplemented using Numeric#coerce. Also, bignum_compare needs to be implemented (just returns 0 atm). + +commit e1aa382f2d596a73ef20dfde4184af7a721724e9 +Author: Mae +Date: Tue Jan 30 00:26:11 2007 +0000 + + added specs for coercion of Fixnum, Bignum, and Float + +commit bd292d64a511eba51ea1569870bcf0fa365c903d +Author: Cabo +Date: Sun Jan 28 21:05:10 2007 +0000 + + include yesterday's failed cases + +commit 7cac7f32e5c80e78aa75dfed7f4822e65d1ab4df +Author: Brian Ford +Date: Sun Jan 28 08:08:12 2007 +0000 + + committing rue's continuation specs. doomo arigatoo. + +commit f542b93031f8982daa13777d2eada81068e96ad5 +Author: Brian Ford +Date: Sun Jan 28 07:03:46 2007 +0000 + + committing rue's class specs. thanks rue! + +commit 319b6f194d3c699a75de8da2ba3b53b8a4feffb1 +Author: Brian Ford +Date: Sun Jan 28 04:49:01 2007 +0000 + + Commiting rue's binding specs. + +commit d8326b1321cc09c0aa64f441d2a81df6735603fd +Author: Cabo +Date: Sat Jan 27 00:02:33 2007 +0000 + + remove superfluous p from "& should create an array with no + duplicates" (which now passes) + +commit 7d3baf10a79c1500e660fe5566ba8f3107d5a826 +Author: Hurdlea +Date: Thu Jan 25 05:43:50 2007 +0000 + + * Added MatchData specs + +commit 2d9966c9c30e541c18ac77ca646a1af41daf702e +Author: Hurdlea +Date: Thu Jan 25 05:42:44 2007 +0000 + + * Added Match2 and correct Match3 in compiler.rb + * Added alias String#to_str + * Tweaked a few regexep specs for string return types + * Fixed MatchData#length so it uses Tuple#fields to get the no. items + +commit 0d9f9e21c2a268e0710c963c745f07d494e2ab1f +Author: Brian Ford +Date: Wed Jan 24 21:43:17 2007 +0000 + + Commiting zimbatm's update to exception_spec. + +commit 037d8b29872f1c4a81108a0713afd78cbdf9b484 +Author: Brian Ford +Date: Sat Jan 20 04:03:20 2007 +0000 + + a few more tweaks to get string specs to execute with rcompile and shotgun. + +commit cfe7a6b4c87ac3ffccaeb7e70b9e6c386054e052 +Author: Brian Ford +Date: Sat Jan 20 03:49:01 2007 +0000 + + added parser dir under spec. added parser/symbol_spec.rb to capture parsing a complex symbol like :' for one or two', which rcompile and shotgun choke on at the moment. removed this from core/string_spec because it crashes shotgun and makes it impossible to run all the specs. + +commit d39040ab1563f063192a3835723cfbae7bf147cb +Author: Cabo +Date: Fri Jan 19 23:45:52 2007 +0000 + + lib/kernel.rbc is no longer a required (or wanted) command line argument + +commit 4774788e0ae9b24b3ff0b769aede0ba2de3f00b1 +Author: Brian Ford +Date: Fri Jan 19 21:05:10 2007 +0000 + + added correct guard on Array#first to return nil when array is empty. added specs for #first and #last to describe this behavior. Thanks to cabo for finding this. + +commit baf1453678c9906c65b2f7c82bdb0e179e22d1b8 +Author: Brian Ford +Date: Fri Jan 19 02:49:02 2007 +0000 + + added some minor changes to structure and wording of language/expression_spec. removed array and defined spec from language directory since they were added to language/literals directory. renamed several spec files to follow naming conventions. + +commit 7a24923ab9b79b226b6d8831e834ab509d5d2b76 +Author: Brian Ford +Date: Fri Jan 19 01:27:10 2007 +0000 + + committing zimbatm's patch to language specs. super nice and thank you. please give zimbatm a commit bit! + +commit 9cd8c779a88f48604733afbe4357b7101a487669 +Author: Vic +Date: Thu Jan 18 22:20:34 2007 +0000 + + Added Proc.given, the analog of MethodContext.current + Proc.given obtains the proc given to the current MethodContext. + Later will be able to get a Proc from a given Binding. + + Original author: Victor Hugo Borja + Date: 2007-01-18 16:16:06+00:00 + +commit 50c42413d33951397a46edb55ca910a2e8fb87e6 +Author: Vic +Date: Thu Jan 18 21:34:29 2007 +0000 + + No output is available for specs if the returning value is a Numeric or Symbol + When the :example execution on shotgun evaluates to a Numeric or Symbol, no method + :stdout is added, because these object do not have singleton-classes on MRI. + If you really need both, stdout and a Numeric/Symbol, your evaluation may lead to + an array containing that Numeric/Symbol. + + Also saved MRI from getting eval errors in cases like the following: + + example do + class A; end + A.new + end + + This leads to the following being evaled by MRI: [ # , stdout] + which causes an error because of # being invalid ruby syntax. + This patch fixes this situation by converting # into "#" + + Original author: Victor Hugo Borja + Date: 2007-01-18 15:23:55+00:00 + +commit ec5bea103b4b96ecde54668e47ab9e10ac8ec4ee +Author: Hurdlea +Date: Thu Jan 18 21:04:22 2007 +0000 + + * Added bitwsie operators to Fixnum & | ^ << >> ~ + * Split the fixnum specs into coerced and non-coerced tests + * Added primitives to support fixnum bitwise ops + * Fixed a bounds tests in Interger#chr + +commit 238d7e0611e9198c28a5e0ebe684bc7f1f03bf0f +Author: Vic +Date: Thu Jan 18 20:11:58 2007 +0000 + + [rAdded specs for the new STDOUT support] Empty log message + + Original author: Victor Hugo Borja + Date: 2007-01-18 13:44:19+00:00 + +commit 859b26f38749f160a706ed9dbb8f2a80886e94ef +Author: Vic +Date: Thu Jan 18 19:37:03 2007 +0000 + + Allow to specs to test what is written to STDOUT + + also added String#unindent on spec_helper to help make output heredocs more readable. + + You can access both the evaluation result and the stdout produced, ej: + + context "Rubinius target" do + + specify "should allow to get the resulting STDOUT" do + example do + puts "hola" + puts "space is significant in this heredoc" + puts "unindent removes the first blanks found on the first line" + puts "on each of these lines" + puts "adios" + end.stdout.should == <<-OUT.unindent + hola + space is significant in this heredoc + unindent removes the first blanks found on the first line + on each of these lines + adios + OUT + end + + specify "should allow to get the lines written to STDOUT" do + example do + puts "hello" + end.stdout_lines.length == 1 + + example do + print "bye" + end.stdout_lines.first.should == "bye" + end + + specify "should allow to access the evaluation result along with STDOUT" do + result = example do + puts "ok" + Object.new.class + end + result.should == Object + result.stdout_lines.should == ["ok\n"] + end + + end + +commit e17069925d139c93acec00161a7111e6c78d54bb +Author: Brian Ford +Date: Thu Jan 18 08:44:33 2007 +0000 + + converted shotgun-test/test_sexp to spec/shotgun/sexp_spec. thanks to Victor Borja's recent additions to rubinius_target, it was a breeze. + +commit 46e9a259bc2212dee1fa7efa8ead468e63970731 +Author: Brian Ford +Date: Wed Jan 17 08:02:35 2007 +0000 + + filled in the rest of the documented String instance methods except #pack. + +commit dfd08d6536ea497cf86d06ca503206d54b19479d +Author: Cabo +Date: Tue Jan 16 10:24:18 2007 +0000 + + A bit more array fun (and lots of FIXMEs) + +commit b1e50e43d8d79a5dbd82345134ecd4bdffc6d182 +Author: Brian Ford +Date: Tue Jan 16 08:46:48 2007 +0000 + + and yet a few more string specs. these will asymptotically approach done. + +commit 4048d3dfa90a6de54ea2ed0aec2ec6adafb50b0c +Author: Cabo +Date: Tue Jan 16 07:02:50 2007 +0000 + + I want to see what 'Shotgun has crashed' means, beautiful backtrace and all + +commit 0387baa914cb35c589c7872f7f98cf9f8ee10711 +Author: Cabo +Date: Tue Jan 16 02:19:45 2007 +0000 + + Fix Array#slice! bug workarounds + +commit c6b110b47667c5d6750492177492434f4c0446f8 +Author: Cabo +Date: Tue Jan 16 01:33:18 2007 +0000 + + add shift spec and fix String#strip so it works + +commit 19bcc086b7674f12e01f879a6ca83f3289feb770 +Author: Cabo +Date: Tue Jan 16 00:06:03 2007 +0000 + + Integer#chr should return a new string (spec) + +commit 5aa81499711ad5e57f5dfc03417f23705eb79b44 +Author: Brian Ford +Date: Mon Jan 15 23:48:16 2007 +0000 + + a few more string specs. + +commit 8cd873e183c62b8929305ea54b9a437ca22ddb28 +Author: Brian Ford +Date: Mon Jan 15 22:42:24 2007 +0000 + + committing Victor's define_method patch. + +commit cd04f4c570cd95fb869f025c4dac6e9342e2ba2a +Author: Brian Ford +Date: Mon Jan 15 22:02:23 2007 +0000 + + committing Victor Hugo Borja's instance_eval patch. + +commit a481142988d585bb8fa54e0186f5c9cf88ada8d9 +Author: Brian Ford +Date: Mon Jan 15 20:13:03 2007 +0000 + + added a code method to mri_target and jruby_target to parallel the behavior of the code method for rubinius_target. now core/proc_spec.rb is passing with mri target. + +commit a4c621e8319349eda766f73ed9ca55f2a9323ac2 +Author: Brian Ford +Date: Mon Jan 15 19:08:22 2007 +0000 + + checked in nicksieger's patch to spec_helper that enables specs to run on jruby, woohoo! + +commit 6b02aac6107b01258f85f9d15a77b498ad15e5b0 +Author: Brian Ford +Date: Mon Jan 15 18:06:23 2007 +0000 + + checking in cabo's changes to target specs for jruby and rubinius. modified rubinius_target specs that compared paths to use should_match because a hash is used to generate part of the path. + +commit 6aa175d3367d76152476888ff1c52479530c56a2 +Author: Brian Ford +Date: Mon Jan 15 17:57:25 2007 +0000 + + committing cabo's changes to remove heredocs from a number of specs. It is still possible to pass code as a source string to the example method. Use this if the block method is causing rubinius to choke on the ruby2ruby generated source. soon, soon, we'll have rspec running (I hope\!). + +commit 6679194f8e6afdbbb71f5213508bb81f12fdb2e7 +Author: Brian Ford +Date: Sun Jan 14 17:28:06 2007 +0000 + + incorporated nicksieger's changes to mri_target removing needless requires. added jruby_target.rb and spec to parallel mri_target.rb. + +commit f573b9c16efccb92eec98d923831deafc7a3c809 +Author: Brian Ford +Date: Sun Jan 14 08:10:24 2007 +0000 + + converted mri_target to use eval, yield to execute specs. converted some specs to new style. addressed issues with hash specs that implicitly relied on hash ordering, fixed numerous issues that result from loss of floating-point precision by using #inspect where necessary (more of these issues may arise in the specs on different platforms). added spec templates for documented String instance methods (many of which need to be filled in). + +commit 917cd03e5bee749d18d8d0c257381bca2362abbd +Author: Brian Ford +Date: Sun Jan 14 00:11:17 2007 +0000 + + checking in Alan Hurdles patch to allow running specific files, e.g. COMPILER=rcompile rake spec:core:array . + +commit 508eaacf9aaf67465a78ac53284ba6f06c3bcb3d +Author: Brian Ford +Date: Sat Jan 13 21:09:51 2007 +0000 + + added integer specs. these should be platform independent but other platforms may have some trouble with spec for 'chr' ;) + +commit 21463a87bac2121fa61c1c99927cdb039c724d89 +Author: Brian Ford +Date: Sat Jan 13 20:37:37 2007 +0000 + + implemented the rest of the hash specs, this should cover the documented class, instance methods. + +commit 3656a95a1829b0be1a8b0d968e0a9e433ef9c847 +Author: Frederick +Date: Sat Jan 13 13:14:41 2007 +0000 + + Implements Fixnum#size + +commit ca38e49022f6bdf41b0e98409d3fec3528e59bfd +Author: Frederick +Date: Sat Jan 13 12:54:30 2007 +0000 + + shotgun/string_spec.rb now follow new spec conventions + +commit 1b684385fe970f11a526e280d15c3f147a826886 +Author: Frederick +Date: Sat Jan 13 12:42:38 2007 +0000 + + language/expression_spec.rb and language/exception_spec.rb now use new spec style + +commit f62c2539a1eef27b356e4d809d76c4f9ddecd2a5 +Author: Brian Ford +Date: Sat Jan 13 08:14:12 2007 +0000 + + new style specs for hash. numerous of these need to be implemented but there should be templates for all the documented class and instance methods. + +commit 28083a6e6dc89502d1c76e2a16f0003a589f01e0 +Author: Brian Ford +Date: Sat Jan 13 08:12:37 2007 +0000 + + one more, bignum. + +commit 076aab00b795ff777c5ac11955130f12f69e1377 +Author: Brian Ford +Date: Sat Jan 13 08:07:30 2007 +0000 + + new style specs for float, fixnum, symbol, string, object, file, module, regexp, range. + +commit 0161ab3527e91674eed4eeaad029eee654325155 +Author: Brian Ford +Date: Sat Jan 13 07:39:26 2007 +0000 + + true, false, nil, enumerable specs are new style. + +commit bb11cce41a472606312eb0a62948c4a339f23dd9 +Author: Brian Ford +Date: Sat Jan 13 07:27:05 2007 +0000 + + converted existing class and comparable specs to new style. + +commit 595b83a75044772136b83eaf84402ed73eb79da5 +Author: Brian Ford +Date: Sat Jan 13 07:08:14 2007 +0000 + + ladies and gentlemen, a huge round of applause and gratitude to headius for inspiration and help getting specs in a form that will easily run on MRI, JRuby, and Rubinius. Checking in the modified spec/core/array_spec.rb. The rest to follow. The mri_target is still using the sub-process method, but that should be superfluous now. + +commit ae75e76915432757be3c9a7126c2ee8c6656652c +Author: Brian Ford +Date: Fri Jan 12 08:13:29 2007 +0000 + + added a bunch more specs for array. two still need to be filled out. I think that covers all the documented class and instance methods. + +commit 8daf38e0f99eed3e42d654086a98e673d9855bef +Author: Brian Ford +Date: Fri Jan 12 01:14:35 2007 +0000 + + checking in more of cabo's changes to kernel/core/array.rb and array specs. + +commit 0a349583aba629748d5e85de0ede6f38730512f1 +Author: Brian Ford +Date: Fri Jan 12 00:57:22 2007 +0000 + + checking in cabo's changes to array and array spec. + +commit 052512fea9b74e532ef6b68612c81061ad84e4f2 +Author: Brian Ford +Date: Thu Jan 11 05:16:47 2007 +0000 + + finished Bignum specs for documented instance methods. + +commit 7f4786c85b2e0e95abc2728492ed1a7424d01dbe +Author: Frederick +Date: Wed Jan 10 23:44:34 2007 +0000 + + Added File.mtime, File.atime, File.ctime + Avoid reusing old .rbc is .rb is newer + Remove useless CHECK_PTR + +commit 70458d6446f0858570571a64b5294c0bb4ac358f +Author: Brian Ford +Date: Wed Jan 10 17:40:36 2007 +0000 + + moved exception and expression specs to spec/language. added stub for time_spec in spec/library. updated a string spec that was failing. added specs for all (I think) float and fixnum instance methods. added specs for bignum, but about half need examples. + +commit ffe4a7a48dcc116f73b89b9a046d4430ed51975a +Author: Brian Ford +Date: Tue Jan 9 18:07:31 2007 +0000 + + removed duplicate bk task from Rakefile. removed shotgun-tests/test-array.rb as all tests have corresponding specs in spec/core. added beginning of specs for bignum separated into spec/core for stuff that should be indendent of mri or rubinius, and spec/shotgun for implementation specific. + +commit 946d0b42293ea081666e71e13c4b77d5b5dba886 +Author: Brian Ford +Date: Sun Jan 7 05:38:33 2007 +0000 + + checking in Alan Hurdles patches to regexp, string, and spec_string. + +commit 5f035040c4b3ce842fff4b39d1ca657c97deb7a4 +Author: Brian Ford +Date: Sun Jan 7 03:03:31 2007 +0000 + + updated mri and rubinius target impl specs. added environment option for running rubinius target using obsolete.rcompile, e.g.: COMPILER=rcompile spec spec/core/symbol_spec -f s. If you don't use the COMPILER env var, rubinius target will use shotgun to compile. + +commit 6ea911ae5740508cdbd8feb5cddba5b8bf7fe1c3 +Author: Brian Ford +Date: Sun Jan 7 02:32:35 2007 +0000 + + changed some Hash specs to use instance vars rather than local vars because some versions of Ruby2Ruby output borked sexp for block local vars. E.g. use @h rather than h. + +commit 256fe9a8cada7ed512556e1701a5264670c6c28f +Author: Mae +Date: Sat Jan 6 07:55:32 2007 +0000 + + made regression spec for buggy behavior of [1,2,3][2..-1] + +commit 0ffe8a3e6cd92bc5cd872cc22919885ea80366a0 +Author: Brian Ford +Date: Fri Jan 5 20:05:03 2007 +0000 + + added spec for String#reverse! to Laurent Julliard's spec for String#reverse and his implementation of both methods. + +commit 170737d2c77a4b2de862380cb87f7705560cca64 +Author: Wilson Bilkovich +Date: Fri Jan 5 18:24:32 2007 +0000 + + * Much better implementation of Hash#key? + * Added working support for default Hash values and procs + * Added hash_get_undef for situations where nil and undefined hash values need to be differentiated + +commit 332378a8900f09009626cb7c4dbf0c8740a657c7 +Author: Brian Ford +Date: Fri Jan 5 07:31:56 2007 +0000 + + added specs for aliases of Hash#key? + +commit 27c3b2aeaa3208a7e0218f051f623b93e2e635d8 +Author: Brian Ford +Date: Fri Jan 5 07:28:29 2007 +0000 + + added spec for Hash#key? + +commit 2e88d941bc1b1ea506396a915e3c3c4e3dfd1601 +Author: Brian Ford +Date: Fri Jan 5 06:35:09 2007 +0000 + + updated and simplified float and fixnum specs. + +commit e4a5b6d8529d60e62875004bb60f33c6452ccf98 +Author: Wilson Bilkovich +Date: Fri Jan 5 06:28:04 2007 +0000 + + * defined?() now handles: defined?(Kernel.puts) flavors of arguments. + +commit 27660379c09e561590cf1bc48a9459e29fc00e9c +Author: Mae +Date: Fri Jan 5 06:10:11 2007 +0000 + + Added spec for cvar declaration in class bodies + +commit a9b7b9f7db02ba78514eb869c2c52d4e5067f8d2 +Author: Brian Ford +Date: Thu Jan 4 08:08:59 2007 +0000 + + added class def source code to specs. + +commit cdfa499ee238671c655800a81b51611704383500 +Author: Mae +Date: Thu Jan 4 07:54:20 2007 +0000 + + fixed typo in spec still 7 failing specs *GLARES AT DEFILER* + +commit 03be9ca7da61363f8f0a02ee951bbafaf297c31b +Author: Brian Ford +Date: Thu Jan 4 07:43:58 2007 +0000 + + added specs for Module#const_defined?. + +commit c511d4c7a75001f2597b13f9fc2e910d2dd4d9a2 +Author: Mae +Date: Thu Jan 4 06:54:07 2007 +0000 + + changed defined spec to be more dumb and just figure out whether its a true/false evaluation + +commit 4d9135f9694e4b692faf6a4c7b8dcd59f79f5069 +Author: Brian Ford +Date: Thu Jan 4 06:38:57 2007 +0000 + + added library spec (beginning) for enumerator. trivial update for comparable specs. added specs for enumerable. + +commit 3de6e339526b1402f8995a5acc38fde707ec0695 +Author: Brian Ford +Date: Thu Jan 4 00:34:50 2007 +0000 + + added specs for comparable methods. + +commit ea25c6b17533c280e640dc97e3fec1207fb4be7b +Author: Mae +Date: Wed Jan 3 17:47:09 2007 +0000 + + almost done with defined? spec -- still need 'yield' and 'zsuper' test cases + from project dir: SPEC_TARGET=mri spec spec/language/defined_spec.rb + change SPEC_TARGET to rubinius to test on rubinius + +commit b795f6c2dc98952e7fa7231cded9156ade962b18 +Author: Mae +Date: Wed Jan 3 17:25:31 2007 +0000 + + added incomplete specs for defined? behavior -- more work to be done + +commit 25e30e4668f1ef814bfb1182e032449263651590 +Author: Brian Ford +Date: Wed Jan 3 17:17:03 2007 +0000 + + small fix to mri_target to generate reasonable cache soure name. added a couple specs. + +commit 3b5bc977ea2a4a3ad42ff83bcab2966459c262c0 +Author: Brian Ford +Date: Wed Jan 3 08:51:18 2007 +0000 + + added object_spec for methods provided by Object, even mixed in ones. added a few specs for basic class, module, exceptions. + +commit c4f4dd722658b2a09ae273092744b76e65ce05b2 +Author: Brian Ford +Date: Wed Jan 3 02:36:58 2007 +0000 + + renamed flow_control_spec to expression_spec as these are all covered under heading expressions in pickaxe. run expression spec with mri target and then rubinius target to see an interesting rubinius failure. updated an incorrect string splice spec. + +commit ef8aa2f74896944134f5a8884ccc723ca9b472c1 +Author: Brian Ford +Date: Tue Jan 2 01:40:16 2007 +0000 + + separated specs that are shotgun specific methods (e.g. String#prefix?) into spec/shotgun/... fixed wrong specs so that all pass under mri/mir configuration. + +commit 8661488b40b9fccccf356889834c6a9162c8bebf +Author: Brian Ford +Date: Mon Jan 1 22:37:41 2007 +0000 + + very quick n' dirty implementation of example et al to run specs under mri like under rubinius. + +commit 0ebfa43e222ca4794243d36f10c2e429e930f527 +Author: Brian Ford +Date: Mon Jan 1 20:47:13 2007 +0000 + + added methods for TrueClass and FalseClass, updated specs for each. + +commit e9bb50ced8bf997535f0bc9c6deeeffb86c40879 +Author: Brian Ford +Date: Mon Jan 1 20:29:37 2007 +0000 + + added specs for true, false, nil. + +commit c30b0e38b88c686c11c6f7442e027d685d405505 +Author: Brian Ford +Date: Mon Jan 1 18:59:59 2007 +0000 + + added spec templates for true, false, nil, class, module, enumerable, comparable, flow_control, exception. added specs to various others. + +commit b26777261970c213506e444b90412543f39b3c59 +Author: Brian Ford +Date: Mon Jan 1 18:58:37 2007 +0000 + + added that rubinius target example method takes a default argument to pass strings of code to allow for creating classes, since classes can't be defined in a method body and for now example puts the block code into a method using ruby2ruby. + +commit 560c5a1331c76bce07289f1e5950b816fe7c9c24 +Author: Brian Ford +Date: Mon Jan 1 01:17:44 2007 +0000 + + added more string specs. + +commit 807864c76b701f6f976f3f2935599ba875fcc10e +Author: Brian Ford +Date: Sun Dec 31 23:13:16 2006 +0000 + + added more core specs (or templates for specs) to cover existing tests in shotgun-tests. + +commit 8f83b600b12722d46b9791b2e2c3a399618474a0 +Author: Brian Ford +Date: Sun Dec 31 21:23:07 2006 +0000 + + removed shotgun/primitives_spec as spec/shotgun should be for shotgun-specific code. created spec/core for ruby core classes. spec/library is now for ruby stdlib classes. added more array specs. + +commit 58cc3ce5bd8b84a151b1a6e2334845f268ab894a +Author: Brian Ford +Date: Sun Dec 31 08:58:50 2006 +0000 + + added specs for class methods of Regexp. added alias Regexp.compile for Regexp.new. + +commit 0a5a31bc6ef539eeda3de951ab633f5152d58153 +Author: Brian Ford +Date: Sun Dec 31 02:01:34 2006 +0000 + + added specs for symbol methods. added aliases to symbol for to_i, to_int, and id2name. + +commit cdfa28e492b8edfa55b950b06ce05ebb04b64643 +Author: Brian Ford +Date: Sat Dec 30 17:27:18 2006 +0000 + + changed example method for rubinius_target to raise exception if compile fails. added specs for range. + +commit 5316e652084b8624828d0a9306f580bfc93184dc +Author: Brian Ford +Date: Fri Dec 29 20:37:46 2006 +0000 + + added spec files in spec/library for basic types (according to pickaxe book). added specs for all the methods in Regexp. there are many failing specs for a variety of reasons, but the goal is to get a good overview of where work needs to be done. more specs to follow. + +commit 06cd5ad6da819f2894996e42f4da70321767c9c7 +Author: Mae +Date: Thu Dec 28 07:36:21 2006 +0000 + + Added Array expressions gleaned from spec/library/array_spec.rb as proof of concept for rapid compatibility testing + - A thought occurred to me that the scope for this type of testing might be limited severely to simple compatibility testing + - Its not very human understandable as a spec -- it just unravels incompatibilities given no hint as to why things are the way they are. + - It lets the ruby rval speak for itself + - Is this useful for rubinius?! + +commit 1c1fc9335aee4acbcd692c555b0ca194c5301013 +Author: Frederick +Date: Wed Dec 27 22:46:43 2006 +0000 + + Fix a bug in the allocation of a string. The underlying storage (byte array) did not have the correct size, leading to a write in a non allocated memory area. + The rationale is that, the storage are should be able to store the string plus a terminal \0. As we're allocating per block of 4 bytes (a word) we need to get the nearest multiple of 4. + This patch adds a spec to highlight the bug, and a fix to .. well, fix it ;) + +commit cdfdc272b71bbfca23b4c17e5572ebd2b966615e +Author: Mae +Date: Tue Dec 26 17:52:24 2006 +0000 + + Added my idea of a sanity check against MRI for compatibility purposes + - try it out! + - rake spec:compatibility + - example compatibility expressions go in spec/compatibility/expressions/* + - all the expression files are line-separated ruby expressions that return something basic and eval-able + +commit f9887648c7f239f8c862158b39f44b2410377204 +Author: Brian Ford +Date: Fri Dec 22 19:20:58 2006 +0000 + + added spec file for String methods. + +commit 8178e4478977c81940ac4bdcd8bea608be11708b +Author: Brian Ford +Date: Fri Dec 22 17:14:46 2006 +0000 + + fixed that ruby2ruby was not generating correct ruby source when a local var was used in a block. changed local var to instance var and it works, converted primitives_spec to new block-style. + +commit 8a00080a1edb864af7573e8f9761f65fa1202d07 +Author: Brian Ford +Date: Fri Dec 22 06:58:28 2006 +0000 + + changed array_spec to not use local variables in blocks where possible because rubytoruby is not converting them to ruby source correctly. Array#uniq! fails at the moment. re-added that compile checks code-cache first so specs run faster. + +commit 4a199c559eebe73cd21d0997126b799f5d4e2be5 +Author: Brian Ford +Date: Fri Dec 22 06:00:21 2006 +0000 + + converted array_spec.rb to block-style specs, but they still depend on strings to be output. rewrote spec_helper based on nicksieger's example code. some specs are failing due to bugs converting to sexp and back to ruby source. + +commit 854bbc3617559a2ceac975d79a57ffa825a5cda6 +Author: Brian Ford +Date: Fri Dec 22 03:34:18 2006 +0000 + + added spec/targets for specs for 'target' part of host/target spec runner configuration. added mri_target and rubinius_target and specs. + +commit db81559c9c914413d2064b4202ec8ce43e503af2 +Author: Brian Ford +Date: Thu Dec 21 05:51:55 2006 +0000 + + added spec:targets task to run specs for target part of host/target spec configuration. added specs for mri_target and rubinius_target. + +commit a243a70bd17ec7e9839b69bb18e63d5d943b6095 +Author: Brian Ford +Date: Wed Dec 20 19:02:01 2006 +0000 + + updated primitives_spec to use example method. + +commit 9ce077283dd10c21377add499c5bcc4ea87cfe0f +Author: Brian Ford +Date: Wed Dec 20 17:11:00 2006 +0000 + + Changed method from rubinius to example for specs. This is in anticipation of having independent 'host' (system running rspec) and 'target' (system executing spec). Created parallel arrayb_spec that illustrates this with a mri/mri configuration. Also added spec_bhelper that is a *very rough* beginning for having mri/rubinius configuration using block-style specs. + +commit a8ad71a0da9e1a866521074743ea1dfcceb596cb +Author: Wilson Bilkovich +Date: Wed Dec 20 15:39:36 2006 +0000 + + Applying 'array patch' from Jason Perkins (2006-12-20 8:30 EST) + +commit 82b3d880131e7080ebc6b4289b3954d89a988c13 +Author: Brian Ford +Date: Tue Dec 19 17:30:21 2006 +0000 + + added more specs to array_spec. most of these are failing, so there seems to be a lot of Array that needs implementing. + +commit c24f0e83b4d446afd541ffefbcb313f199b684ee +Author: Brian Ford +Date: Tue Dec 19 08:24:00 2006 +0000 + + added specs for Array#* and <<, simplified other specs. + +commit 62b0737ce69768a8292bd3d9f13401ec8056f6eb +Author: Brian Ford +Date: Tue Dec 19 07:46:39 2006 +0000 + + fixed messed up spec for &. + +commit 7635389b2abf5c492952a49eb8251d6fb34250c7 +Author: Brian Ford +Date: Tue Dec 19 07:40:01 2006 +0000 + + added spec for Array#&, which is currently unimplemented. updated spec_helper. + +commit 42b5e9fc47bcf6bcd403d25795c9bcf07bb5c007 +Author: Brian Ford +Date: Tue Dec 19 06:13:15 2006 +0000 + + added spec tasks :only to run only spec, :language for high level language conformance spcs, :library for ruby stdlib implementation specs, and :shotgun for specs related to shotgun. rake spec will run all specs and tests. removed spec/spec_suite.rb because all specs can be run from rake. minor updates to spec_helper. + +commit da4c42890f4b8163b8d49de64bdb76c16b0e5d1f +Author: Brian Ford +Date: Mon Dec 18 17:41:44 2006 +0000 + + added shotgun dir under spec for things that relate to shotgun implementation of VM, like the prmitives_spec, while reserving spec/library for general ruby implementation of the std lib. + +commit 925cbf9f84f78a48189f7030c205942d266a6f66 +Author: Brian Ford +Date: Mon Dec 18 09:45:25 2006 +0000 + + added Fixnum#% primitive implementation. changed array_spec to use Fixnum#%. fixed test_primitive test for Fixnum#%. + +commit 06e50e48a92e3fa7d1fc4d6b681872d08e5aeba3 +Author: Brian Ford +Date: Mon Dec 18 09:10:37 2006 +0000 + + Added Fixnum#% primitive test, spec, cpu/primitive, stub. + +commit e0fbcf29f46dde89f65d13c1b6d7601a470cf223 +Author: Brian Ford +Date: Mon Dec 18 07:54:02 2006 +0000 + + all ports of test_array test to array_spec are now passing. + +commit c2330c2ff65cfa964d954340fb0eb2507972efd5 +Author: Brian Ford +Date: Mon Dec 18 07:39:55 2006 +0000 + + updated spec_helper to ensure code-cache dir exists. fixed several failing specs in array_spec by correcting expected value. + +commit 81cc03c6f7ce499da563543f00d273d3a9c3a184 +Author: Brian Ford +Date: Sun Dec 17 06:27:15 2006 +0000 + + Ported the rest of test_array.rb tests to specs. Several of these specs are not passing but ported them all to illustrate behavior of specs. + +commit b4cb073931f403119a5ed9b63a2c915612a9c46f +Author: Brian Ford +Date: Sun Dec 17 04:42:55 2006 +0000 + + spec/spec_helper.rb rubinius method is a very naive port of shotgun-tests/helper.rb run_code method. spec/library/array_spec.rb is several ports of the tests in shotgun-tests/test_array.rb, which is testing /kernel/array.rb. In other words, you can now create specs that run under RSpec (which is running under MRI 1.8.x) that exercises the rubinius vm, shotgun, and the stdlib that is being written in ruby. Confused? Read the source, Luke. :) + +commit 1b37cd1ee800060fb215a52d2902c3f4b778a656 +Author: Brian Ford +Date: Sat Dec 16 07:55:41 2006 +0000 + + Added spec dir with spec_suite.rb and spec_helper.rb provided by nullstyle. diff --git a/spec/rubyspec/CONTRIBUTING.md b/spec/rubyspec/CONTRIBUTING.md new file mode 100644 index 0000000000..e675a61afa --- /dev/null +++ b/spec/rubyspec/CONTRIBUTING.md @@ -0,0 +1,64 @@ +Contributions are much appreciated. +Please open a pull request or add an issue to discuss what you intend to work on. +If the pull requests passes the CI and conforms to the existing style of specs, it will be merged. + +### File organization + +Spec are grouped in 5 separate top-level groups: + +* `command_line`: for the ruby executable command-line flags (`-v`, `-e`, etc) +* `language`: for the language keywords and syntax constructs (`if`, `def`, `A::B`, etc) +* `core`: for the core methods (`Fixnum#+`, `String#upcase`, no need to require anything) +* `library`: for the standard libraries methods (`CSV.new`, `YAML.parse`, need to require the stdlib) +* `optional/capi`: for functions available to the Ruby C-extension API + +The exact file for methods is decided by the `#owner` of a method, for instance for `#group_by`: +```ruby +> [].method(:group_by) +=> # +> [].method(:group_by).owner +=> Enumerable +``` +Which should therefore be specified in `core/enumerable/group_by_spec.rb`. + +### MkSpec - a tool to generate the spec structure + +If you want to create new specs, you should use `mkspec`, part of [MSpec](http://github.com/ruby/mspec). + + $ ../mspec/bin/mkspec -h + +#### Creating files for unspecified modules or classes + +For instance, to create specs for `forwardable`: + + $ ../mspec/bin/mkspec -b library -rforwardable -c Forwardable + +Specify `core` or `library` as the `base`. + +#### Finding unspecified core methods + +This is very easy, just run the command below in your `spec` directory. +`ruby` must be a recent version of MRI. + + $ ruby --disable-gem ../mspec/bin/mkspec + +You might also want to search for: + + it "needs to be reviewed for spec completeness" + +which indicates the file was generated but the method unspecified. + +### Guards + +Different guards are available as defined by mspec. +In general, the usage of guards should be minimized as possible. + +There are no guards to define implementation-specific behavior because +the Ruby Spec Suite defines common behavior and not implementation details. +Use the implementation test suite for these. + +If an implementation does not support some feature, simply tag the related specs as failing instead. + +### Style + +Do not leave any trailing space and respect the existing style. diff --git a/spec/rubyspec/Gemfile b/spec/rubyspec/Gemfile new file mode 100644 index 0000000000..c9e44987cd --- /dev/null +++ b/spec/rubyspec/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'mspec', :github => 'ruby/mspec' diff --git a/spec/rubyspec/LICENSE b/spec/rubyspec/LICENSE new file mode 100644 index 0000000000..d581dd1c9f --- /dev/null +++ b/spec/rubyspec/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2008 Engine Yard, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/spec/rubyspec/README.md b/spec/rubyspec/README.md new file mode 100644 index 0000000000..d1657c568b --- /dev/null +++ b/spec/rubyspec/README.md @@ -0,0 +1,88 @@ +# The Ruby Spec Suite + +[![Build Status](https://travis-ci.org/ruby/spec.svg)](https://travis-ci.org/ruby/spec) +[![Build Status](https://ci.appveyor.com/api/projects/status/1gs6f399320o44b1?svg=true)](https://ci.appveyor.com/project/eregon/spec-x948i) + +The Ruby Spec Suite is a test suite for the behavior of the Ruby programming language. + +It is not a standardized specification like the ISO one, and does not aim to become one. +Instead, it is a practical tool to describe and test the behavior of Ruby with code. + +Every example code has a textual description, which presents several advantages: + +* It is easier to understand the intent of the author +* It documents how recent versions of Ruby should behave +* It helps Ruby implementations to agree on a common behavior + +The specs are written with syntax similar to RSpec 2. +They are run with MSpec, the purpose-built framework for running the Ruby Spec Suite. +For more information, see the [MSpec](http://github.com/ruby/mspec) project. + +The specs describe the [language syntax](language/), the [core library](core/), the [standard library](library/), the [C API for extensions](optional/capi) and the [command line flags](command_line/). +The language specs are grouped by keyword while the core and standard library specs are grouped by class and method. + +ruby/spec is known to be tested in these implementations for every commit: +* [MRI](http://rubyci.org/) on 30 platforms and 4 versions +* [JRuby](https://github.com/jruby/jruby/tree/master/spec/ruby) on Travis for both 1.7 and 9.x +* [TruffleRuby](https://github.com/graalvm/truffleruby) on Travis +* [Opal](https://github.com/opal/opal/tree/master/spec) on Travis + +### Running the specs + +First, clone this repository: + + $ git clone https://github.com/ruby/spec.git + +Then move to it: + + $ cd spec + +Clone [MSpec](http://github.com/ruby/mspec): + + $ git clone https://github.com/ruby/mspec.git ../mspec + +And run the spec suite: + + $ ../mspec/bin/mspec + +This will execute all the specs using the executable named `ruby` on your current PATH. + +### Running Specs with a Specific Ruby Implementation + +Use the `-t` option to specify the Ruby implementation with which to run the specs. +The argument may be a full path to the Ruby binary. + + $ ../mspec/bin/mspec -t /path/to/some/bin/ruby + +### Running Selected Specs + +To run a single spec file, pass the filename to `mspec`: + + $ ../mspec/bin/mspec core/kernel/kind_of_spec.rb + +You can also pass a directory, in which case all specs in that directories will be run: + + $ ../mspec/bin/mspec core/kernel + +Finally, you can also run them per group as defined in `default.mspec`. +The following command will run all language specs: + + $ ../mspec/bin/mspec :language + +In similar fashion, the following commands run the respective specs: + + $ ../mspec/bin/mspec :core + $ ../mspec/bin/mspec :library + $ ../mspec/bin/mspec :capi + +### Contributing + +See [CONTRIBUTING.md](https://github.com/ruby/spec/blob/master/CONTRIBUTING.md). + +### History and RubySpec + +This project was originally born from [Rubinius](https://github.com/rubinius/rubinius) tests being converted to the spec style. +These specs were later extracted to their own project, RubySpec, with a specific vision and principles. +At the end of 2014, Brian Shirai, the creator of RubySpec, decided to [end RubySpec](http://rubinius.com/2014/12/31/matz-s-ruby-developers-don-t-use-rubyspec/). +A couple months later, the different repositories were merged and [the project was revived](http://eregon.github.io/rubyspec/2015/07/29/rubyspec-is-reborn.html). +On 12 January 2016, the name was changed to "The Ruby Spec Suite" for clarity and to let the RubySpec ideology rest in peace. diff --git a/spec/rubyspec/TODO b/spec/rubyspec/TODO new file mode 100644 index 0000000000..f81f070d71 --- /dev/null +++ b/spec/rubyspec/TODO @@ -0,0 +1,8 @@ +* Decide a way to test methods that are only visible given a specific + command-line option. For example, Kernel#gsub with -n/-p on 1.9. +* Look at automating discovery of guarded bugs which have been fixed. +* Use mocks for all Math functions that coerce with #to_f; currently a fixture + is used. +* investigate slow specs (run with -fp) and make them faster. +* restore some caller specs from 642bf529 +* restore refinements specs and update. See 56c5528f and f20a62e8. diff --git a/spec/rubyspec/appveyor.yml b/spec/rubyspec/appveyor.yml new file mode 100644 index 0000000000..b139c74081 --- /dev/null +++ b/spec/rubyspec/appveyor.yml @@ -0,0 +1,18 @@ +--- +version: "{build}" +clone_depth: 5 +environment: + matrix: + - RUBY_VERSION: 23-x64 +install: + - SET PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% + - ruby --version + - git clone https://github.com/ruby/mspec.git ../mspec +build: off +test_script: + - SET CHECK_LEAKS=true + - ../mspec/bin/mspec -ff command_line language core library +branches: + only: + - master + - /^try/ diff --git a/spec/rubyspec/command_line/dash_a_spec.rb b/spec/rubyspec/command_line/dash_a_spec.rb new file mode 100644 index 0000000000..65f79ec208 --- /dev/null +++ b/spec/rubyspec/command_line/dash_a_spec.rb @@ -0,0 +1,17 @@ +describe "The -a command line option" do + before :each do + @names = fixture __FILE__, "full_names.txt" + end + + it "runs the code in loop conditional on Kernel.gets()" do + ruby_exe("puts $F.last", options: "-n -a", escape: true, + args: " < #{@names}").should == + "jones\nfield\ngrey\n" + end + + it "sets $-a" do + ruby_exe("puts $-a", options: "-n -a", escape: true, + args: " < #{@names}").should == + "true\ntrue\ntrue\n" + end +end diff --git a/spec/rubyspec/command_line/dash_c_spec.rb b/spec/rubyspec/command_line/dash_c_spec.rb new file mode 100644 index 0000000000..375d945a07 --- /dev/null +++ b/spec/rubyspec/command_line/dash_c_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The -c command line option" do + it "checks syntax in given file" do + ruby_exe(nil, args: "-c #{__FILE__}").chomp.should == "Syntax OK" + end + + it "checks syntax in -e strings" do + ruby_exe(nil, args: "-c -e 'puts 1' -e 'hello world'").chomp.should == "Syntax OK" + end + + #Also needs spec for reading from STDIN +end diff --git a/spec/rubyspec/command_line/dash_d_spec.rb b/spec/rubyspec/command_line/dash_d_spec.rb new file mode 100644 index 0000000000..009a14e16c --- /dev/null +++ b/spec/rubyspec/command_line/dash_d_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The -d command line option" do + before :each do + @script = fixture __FILE__, "debug.rb" + end + + it "sets $DEBUG to true" do + ruby_exe(@script, options: "-d", + args: "0 2> #{File::NULL}").chomp.should == "$DEBUG true" + end + + it "sets $VERBOSE to true" do + ruby_exe(@script, options: "-d", + args: "1 2> #{File::NULL}").chomp.should == "$VERBOSE true" + end + + it "sets $-d to true" do + ruby_exe(@script, options: "-d", + args: "2 2> #{File::NULL}").chomp.should == "$-d true" + end +end diff --git a/spec/rubyspec/command_line/dash_e_spec.rb b/spec/rubyspec/command_line/dash_e_spec.rb new file mode 100644 index 0000000000..86c6990058 --- /dev/null +++ b/spec/rubyspec/command_line/dash_e_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The -e command line option" do + it "evaluates the given string" do + ruby_exe("puts 'foo'").chomp.should == "foo" + end + + it "joins multiple strings with newlines" do + ruby_exe(nil, args: %Q{-e "puts 'hello" -e "world'" 2>&1}).chomp.should == "hello\nworld" + end + + it "uses 'main' as self" do + ruby_exe("puts self", escape: false).chomp.should == "main" + end + + it "uses '-e' as file" do + ruby_exe("puts __FILE__", escape: false).chomp.should == "-e" + end + + #needs to test return => LocalJumpError + +quarantine! do # For some unknown reason, running these under `bundle exec` or with -rbundler/setup fails + describe "with -n and a Fixnum range" do + before :each do + @script = "-ne 'print if %s' #{fixture(__FILE__, "conditional_range.txt")}" + end + + it "mimics an awk conditional by comparing an inclusive-end range with $." do + ruby_exe(nil, args: (@script % "2..3")).should == "2\n3\n" + ruby_exe(nil, args: (@script % "2..2")).should == "2\n" + end + + it "mimics a sed conditional by comparing an exclusive-end range with $." do + ruby_exe(nil, args: (@script % "2...3")).should == "2\n3\n" + ruby_exe(nil, args: (@script % "2...2")).should == "2\n3\n4\n5\n" + end + end +end +end diff --git a/spec/rubyspec/command_line/dash_n_spec.rb b/spec/rubyspec/command_line/dash_n_spec.rb new file mode 100644 index 0000000000..f4dd9f1851 --- /dev/null +++ b/spec/rubyspec/command_line/dash_n_spec.rb @@ -0,0 +1,34 @@ +describe "The -n command line option" do + before :each do + @names = fixture __FILE__, "names.txt" + end + + it "runs the code in loop conditional on Kernel.gets()" do + ruby_exe("puts $_", options: "-n", escape: true, + args: " < #{@names}").should == + "alice\nbob\njames\n" + end + + it "only evaluates BEGIN blocks once" do + ruby_exe("BEGIN { puts \"hi\" }; puts $_", options: "-n", escape: true, + args: " < #{@names}").should == + "hi\nalice\nbob\njames\n" + end + + it "only evaluates END blocks once" do + ruby_exe("puts $_; END {puts \"bye\"}", options: "-n", escape: true, + args: " < #{@names}").should == + "alice\nbob\njames\nbye\n" + end + + it "allows summing over a whole file" do + script = <<-script + BEGIN { $total = 0 } + $total += 1 + END { puts $total } + script + ruby_exe(script, options: "-n", escape: true, + args: " < #{@names}").should == + "3\n" + end +end diff --git a/spec/rubyspec/command_line/dash_p_spec.rb b/spec/rubyspec/command_line/dash_p_spec.rb new file mode 100644 index 0000000000..67562b5bc3 --- /dev/null +++ b/spec/rubyspec/command_line/dash_p_spec.rb @@ -0,0 +1,17 @@ +describe "The -p command line option" do + before :each do + @names = fixture __FILE__, "names.txt" + end + + it "runs the code in loop conditional on Kernel.gets() and prints $_" do + ruby_exe("$_ = $_.upcase", options: "-p", escape: true, + args: " < #{@names}").should == + "ALICE\nBOB\nJAMES\n" + end + + it "sets $-p" do + ruby_exe("$_ = $-p", options: "-p", escape: true, + args: " < #{@names}").should == + "truetruetrue" + end +end diff --git a/spec/rubyspec/command_line/dash_r_spec.rb b/spec/rubyspec/command_line/dash_r_spec.rb new file mode 100644 index 0000000000..3d3abcf0b7 --- /dev/null +++ b/spec/rubyspec/command_line/dash_r_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The -r command line option" do + before :each do + @script = fixture __FILE__, "require.rb" + @test_file = fixture __FILE__, "test_file" + end + + it "requires the specified file" do + result = ruby_exe(@script, options: "-r #{@test_file}") + result.should include(@test_file + ".rb") + end +end diff --git a/spec/rubyspec/command_line/dash_s_spec.rb b/spec/rubyspec/command_line/dash_s_spec.rb new file mode 100644 index 0000000000..70e41208e0 --- /dev/null +++ b/spec/rubyspec/command_line/dash_s_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The -s command line option" do + describe "when using -- to stop parsing" do + it "sets the value to true without an explicit value" do + ruby_exe(nil, options: "-s -e 'p $n'", + args: "-- -n").chomp.should == "true" + end + + it "parses single letter args into globals" do + ruby_exe(nil, options: "-s -e 'puts $n'", + args: "-- -n=blah").chomp.should == "blah" + end + + it "parses long args into globals" do + ruby_exe(nil, options: "-s -e 'puts $_name'", + args: "-- --name=blah").chomp.should == "blah" + end + + it "converts extra dashes into underscores" do + ruby_exe(nil, options: "-s -e 'puts $___name__test__'", + args: "-- ----name--test--=blah").chomp.should == "blah" + end + end + + describe "when running a script" do + before :all do + @script = fixture __FILE__, "dash_s_script.rb" + end + + it "sets the value to true without an explicit value" do + ruby_exe(@script, options: "-s", + args: "-n 0").chomp.should == "true" + end + + it "parses single letter args into globals" do + ruby_exe(@script, options: "-s", + args: "-n=blah 1").chomp.should == "blah" + end + + it "parses long args into globals" do + ruby_exe(@script, options: "-s", + args: "--name=blah 2").chomp.should == "blah" + end + + it "converts extra dashes into underscores" do + ruby_exe(@script, options: "-s", + args: "----name--test--=blah 3").chomp.should == "blah" + end + + end +end diff --git a/spec/rubyspec/command_line/dash_upper_c_spec.rb b/spec/rubyspec/command_line/dash_upper_c_spec.rb new file mode 100644 index 0000000000..e8a54b01c1 --- /dev/null +++ b/spec/rubyspec/command_line/dash_upper_c_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe 'The -C command line option' do + before :all do + @script = fixture(__FILE__, 'dash_upper_c_script.rb') + @tempdir = File.dirname(@script) + end + + it 'changes the PWD when using a file' do + output = ruby_exe(@script, options: "-C #{@tempdir}") + output.should == @tempdir + end + + it 'changes the PWD when using -e' do + output = ruby_exe(nil, options: "-C #{@tempdir} -e 'print Dir.pwd'") + output.should == @tempdir + end +end diff --git a/spec/rubyspec/command_line/dash_upper_e_spec.rb b/spec/rubyspec/command_line/dash_upper_e_spec.rb new file mode 100644 index 0000000000..716f1304b7 --- /dev/null +++ b/spec/rubyspec/command_line/dash_upper_e_spec.rb @@ -0,0 +1,7 @@ +describe "ruby -E" do + it "raises a RuntimeError if used with -U" do + ruby_exe("p 1", + options: '-Eascii:ascii -U', + args: '2>&1').should =~ /RuntimeError/ + end +end diff --git a/spec/rubyspec/command_line/dash_upper_f_spec.rb b/spec/rubyspec/command_line/dash_upper_f_spec.rb new file mode 100644 index 0000000000..020968b1f9 --- /dev/null +++ b/spec/rubyspec/command_line/dash_upper_f_spec.rb @@ -0,0 +1,11 @@ +describe "the -F command line option" do + before :each do + @passwd = fixture __FILE__, "passwd_file.txt" + end + + it "specifies the field separator pattern for -a" do + ruby_exe("puts $F[0]", options: "-naF:", escape: true, + args: " < #{@passwd}").should == + "nobody\nroot\ndaemon\n" + end +end diff --git a/spec/rubyspec/command_line/dash_upper_i_spec.rb b/spec/rubyspec/command_line/dash_upper_i_spec.rb new file mode 100644 index 0000000000..0a00059949 --- /dev/null +++ b/spec/rubyspec/command_line/dash_upper_i_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The -I command line option" do + before :each do + @script = fixture __FILE__, "loadpath.rb" + end + + it "adds the path to the load path ($:)" do + ruby_exe(@script, options: "-I fixtures").should include("fixtures") + end +end diff --git a/spec/rubyspec/command_line/dash_upper_k_spec.rb b/spec/rubyspec/command_line/dash_upper_k_spec.rb new file mode 100644 index 0000000000..3c3b9fa4d3 --- /dev/null +++ b/spec/rubyspec/command_line/dash_upper_k_spec.rb @@ -0,0 +1,33 @@ +describe 'The -K command line option sets __ENCODING__' do + it "to Encoding::ASCII_8BIT with -Ka" do + ruby_exe("print __ENCODING__", options: '-Ka').should == Encoding::ASCII_8BIT.to_s + end + + it "to Encoding::ASCII_8BIT with -KA" do + ruby_exe("print __ENCODING__", options: '-KA').should == Encoding::ASCII_8BIT.to_s + end + + it "to Encoding::EUC_JP with -Ke" do + ruby_exe("print __ENCODING__", options: '-Ke').should == Encoding::EUC_JP.to_s + end + + it "to Encoding::EUC_JP with -KE" do + ruby_exe("print __ENCODING__", options: '-KE').should == Encoding::EUC_JP.to_s + end + + it "to Encoding::UTF_8 with -Ku" do + ruby_exe("print __ENCODING__", options: '-Ku').should == Encoding::UTF_8.to_s + end + + it "to Encoding::UTF_8 with -KU" do + ruby_exe("print __ENCODING__", options: '-KU').should == Encoding::UTF_8.to_s + end + + it "to Encoding::Windows_31J with -Ks" do + ruby_exe("print __ENCODING__", options: '-Ks').should == Encoding::Windows_31J.to_s + end + + it "to Encoding::Windows_31J with -KS" do + ruby_exe("print __ENCODING__", options: '-KS').should == Encoding::Windows_31J.to_s + end +end diff --git a/spec/rubyspec/command_line/dash_upper_u_spec.rb b/spec/rubyspec/command_line/dash_upper_u_spec.rb new file mode 100644 index 0000000000..6cd52a3647 --- /dev/null +++ b/spec/rubyspec/command_line/dash_upper_u_spec.rb @@ -0,0 +1,41 @@ +describe "ruby -U" do + it "sets Encoding.default_internal to UTF-8" do + ruby_exe('print Encoding.default_internal.name', + options: '-U').should == 'UTF-8' + end + + it "does nothing different if specified multiple times" do + ruby_exe('print Encoding.default_internal.name', + options: '-U -U').should == 'UTF-8' + end + + it "is overruled by Encoding.default_internal=" do + ruby_exe('Encoding.default_internal="ascii"; print Encoding.default_internal.name', + options: '-U').should == 'US-ASCII' + end + + it "does not affect the default external encoding" do + ruby_exe('Encoding.default_external="ascii"; print Encoding.default_external.name', + options: '-U').should == 'US-ASCII' + end + + it "does not affect the source encoding" do + ruby_exe("print __ENCODING__.name", + options: '-U -KE').should == 'EUC-JP' + ruby_exe("print __ENCODING__.name", + options: '-KE -U').should == 'EUC-JP' + end + + # I assume IO redirection will break on Windows... + it "raises a RuntimeError if used with -Eext:int" do + ruby_exe("p 1", + options: '-U -Eascii:ascii', + args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError if used with -E:int" do + ruby_exe("p 1", + options: '-U -E:ascii', + args: '2>&1').should =~ /RuntimeError/ + end +end diff --git a/spec/rubyspec/command_line/dash_upper_w_spec.rb b/spec/rubyspec/command_line/dash_upper_w_spec.rb new file mode 100644 index 0000000000..4e517a422a --- /dev/null +++ b/spec/rubyspec/command_line/dash_upper_w_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../shared/verbose', __FILE__) + +describe "The -W command line option" do + before :each do + @script = fixture __FILE__, "verbose.rb" + end + + it "with 0 sets $VERBOSE to nil" do + ruby_exe(@script, options: "-W0").chomp.should == "nil" + end + + it "with 1 sets $VERBOSE to false" do + ruby_exe(@script, options: "-W1").chomp.should == "false" + end +end + +describe "The -W command line option with 2" do + it_behaves_like :command_line_verbose, "-W2" +end diff --git a/spec/rubyspec/command_line/dash_v_spec.rb b/spec/rubyspec/command_line/dash_v_spec.rb new file mode 100644 index 0000000000..f690d7baf1 --- /dev/null +++ b/spec/rubyspec/command_line/dash_v_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../shared/verbose', __FILE__) + +describe "The -v command line option" do + it_behaves_like :command_line_verbose, "-v" +end diff --git a/spec/rubyspec/command_line/dash_w_spec.rb b/spec/rubyspec/command_line/dash_w_spec.rb new file mode 100644 index 0000000000..bb038cb10c --- /dev/null +++ b/spec/rubyspec/command_line/dash_w_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../shared/verbose', __FILE__) + +describe "The -w command line option" do + it_behaves_like :command_line_verbose, "-w" +end diff --git a/spec/rubyspec/command_line/dash_x_spec.rb b/spec/rubyspec/command_line/dash_x_spec.rb new file mode 100644 index 0000000000..96b5d01156 --- /dev/null +++ b/spec/rubyspec/command_line/dash_x_spec.rb @@ -0,0 +1,12 @@ +describe "The -x command line option" do + before :each do + @file = fixture __FILE__, "embedded_ruby.txt" + end + + it "runs code after the first /\#!.*ruby.*/-ish line in target file" do + result = ruby_exe(@file, options: '-x') + result.should == "success\n" + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/command_line/error_message_spec.rb b/spec/rubyspec/command_line/error_message_spec.rb new file mode 100644 index 0000000000..6212452739 --- /dev/null +++ b/spec/rubyspec/command_line/error_message_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The error message caused by an exception" do + it "is not printed to stdout" do + out = ruby_exe("this_does_not_exist", args: "2> #{File::NULL}") + out.chomp.empty?.should == true + + out = ruby_exe("end #syntax error", args: "2> #{File::NULL}") + out.chomp.empty?.should == true + end +end diff --git a/spec/rubyspec/command_line/fixtures/bad_syntax.rb b/spec/rubyspec/command_line/fixtures/bad_syntax.rb new file mode 100644 index 0000000000..595e616ddf --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/bad_syntax.rb @@ -0,0 +1 @@ +f { \ No newline at end of file diff --git a/spec/rubyspec/command_line/fixtures/conditional_range.txt b/spec/rubyspec/command_line/fixtures/conditional_range.txt new file mode 100644 index 0000000000..8a1218a102 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/conditional_range.txt @@ -0,0 +1,5 @@ +1 +2 +3 +4 +5 diff --git a/spec/rubyspec/command_line/fixtures/dash_s_script.rb b/spec/rubyspec/command_line/fixtures/dash_s_script.rb new file mode 100644 index 0000000000..500eccbb84 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/dash_s_script.rb @@ -0,0 +1,12 @@ +which = ARGV.shift.to_i + +case which +when 0 + p $n +when 1 + puts $n +when 2 + puts $_name +when 3 + puts $___name__test__ +end diff --git a/spec/rubyspec/command_line/fixtures/dash_upper_c_script.rb b/spec/rubyspec/command_line/fixtures/dash_upper_c_script.rb new file mode 100644 index 0000000000..abe244705f --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/dash_upper_c_script.rb @@ -0,0 +1 @@ +print Dir.pwd diff --git a/spec/rubyspec/command_line/fixtures/debug.rb b/spec/rubyspec/command_line/fixtures/debug.rb new file mode 100644 index 0000000000..2d84c5faf6 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/debug.rb @@ -0,0 +1,10 @@ +which = ARGV.first.to_i + +case which +when 0 + puts "$DEBUG #{$DEBUG}" +when 1 + puts "$VERBOSE #{$VERBOSE}" +when 2 + puts "$-d #{$-d}" +end diff --git a/spec/rubyspec/command_line/fixtures/debug_info.rb b/spec/rubyspec/command_line/fixtures/debug_info.rb new file mode 100644 index 0000000000..ee607910c0 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/debug_info.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true +a = 'string' +b = a +c = b +d = c +e = d +begin + a << 'new part' +rescue Exception => e + print e.message +end diff --git a/spec/rubyspec/command_line/fixtures/embedded_ruby.txt b/spec/rubyspec/command_line/fixtures/embedded_ruby.txt new file mode 100644 index 0000000000..c556bf0b71 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/embedded_ruby.txt @@ -0,0 +1,3 @@ +@@@This line is not value Ruby +#!ruby +puts 'success' \ No newline at end of file diff --git a/spec/rubyspec/command_line/fixtures/freeze_flag_across_files.rb b/spec/rubyspec/command_line/fixtures/freeze_flag_across_files.rb new file mode 100644 index 0000000000..b258249f3a --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/freeze_flag_across_files.rb @@ -0,0 +1,3 @@ +require_relative 'freeze_flag_required' + +p "abc".object_id == $second_literal_id diff --git a/spec/rubyspec/command_line/fixtures/freeze_flag_across_files_diff_enc.rb b/spec/rubyspec/command_line/fixtures/freeze_flag_across_files_diff_enc.rb new file mode 100644 index 0000000000..e9f045e9ea --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/freeze_flag_across_files_diff_enc.rb @@ -0,0 +1,3 @@ +require_relative 'freeze_flag_required_diff_enc' + +p "abc".object_id != $second_literal_id diff --git a/spec/rubyspec/command_line/fixtures/freeze_flag_one_literal.rb b/spec/rubyspec/command_line/fixtures/freeze_flag_one_literal.rb new file mode 100644 index 0000000000..3718899d61 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/freeze_flag_one_literal.rb @@ -0,0 +1,2 @@ +ids = Array.new(2) { "abc".object_id } +p ids.first == ids.last diff --git a/spec/rubyspec/command_line/fixtures/freeze_flag_required.rb b/spec/rubyspec/command_line/fixtures/freeze_flag_required.rb new file mode 100644 index 0000000000..e09232a5f4 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/freeze_flag_required.rb @@ -0,0 +1 @@ +$second_literal_id = "abc".object_id diff --git a/spec/rubyspec/command_line/fixtures/freeze_flag_required_diff_enc.rb b/spec/rubyspec/command_line/fixtures/freeze_flag_required_diff_enc.rb new file mode 100644 index 0000000000..fa348d59e7 Binary files /dev/null and b/spec/rubyspec/command_line/fixtures/freeze_flag_required_diff_enc.rb differ diff --git a/spec/rubyspec/command_line/fixtures/freeze_flag_two_literals.rb b/spec/rubyspec/command_line/fixtures/freeze_flag_two_literals.rb new file mode 100644 index 0000000000..074092c9d9 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/freeze_flag_two_literals.rb @@ -0,0 +1 @@ +p "abc".object_id == "abc".object_id diff --git a/spec/rubyspec/command_line/fixtures/full_names.txt b/spec/rubyspec/command_line/fixtures/full_names.txt new file mode 100644 index 0000000000..602a20b9dd --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/full_names.txt @@ -0,0 +1,3 @@ +alice jones +bob field +james grey diff --git a/spec/rubyspec/command_line/fixtures/loadpath.rb b/spec/rubyspec/command_line/fixtures/loadpath.rb new file mode 100644 index 0000000000..d7fdf45d46 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/loadpath.rb @@ -0,0 +1 @@ +puts $: diff --git a/spec/rubyspec/command_line/fixtures/names.txt b/spec/rubyspec/command_line/fixtures/names.txt new file mode 100644 index 0000000000..ae4bf4c8ad --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/names.txt @@ -0,0 +1,3 @@ +alice +bob +james diff --git a/spec/rubyspec/command_line/fixtures/passwd_file.txt b/spec/rubyspec/command_line/fixtures/passwd_file.txt new file mode 100644 index 0000000000..08a4b23bbd --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/passwd_file.txt @@ -0,0 +1,3 @@ +nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false +root:*:0:0:System Administrator:/var/root:/bin/sh +daemon:*:1:1:System Services:/var/root:/usr/bin/false diff --git a/spec/rubyspec/command_line/fixtures/require.rb b/spec/rubyspec/command_line/fixtures/require.rb new file mode 100644 index 0000000000..0be7049c66 --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/require.rb @@ -0,0 +1 @@ +puts $" diff --git a/spec/rubyspec/command_line/fixtures/rubyopt.rb b/spec/rubyspec/command_line/fixtures/rubyopt.rb new file mode 100644 index 0000000000..48d81e1bca --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/rubyopt.rb @@ -0,0 +1 @@ +puts "rubyopt.rb required" diff --git a/spec/rubyspec/command_line/fixtures/test_file.rb b/spec/rubyspec/command_line/fixtures/test_file.rb new file mode 100644 index 0000000000..961e3c0b0c --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/test_file.rb @@ -0,0 +1 @@ +"test file" diff --git a/spec/rubyspec/command_line/fixtures/verbose.rb b/spec/rubyspec/command_line/fixtures/verbose.rb new file mode 100644 index 0000000000..2aa99ed44d --- /dev/null +++ b/spec/rubyspec/command_line/fixtures/verbose.rb @@ -0,0 +1 @@ +puts $VERBOSE.inspect diff --git a/spec/rubyspec/command_line/frozen_strings_spec.rb b/spec/rubyspec/command_line/frozen_strings_spec.rb new file mode 100644 index 0000000000..f3ee797c78 --- /dev/null +++ b/spec/rubyspec/command_line/frozen_strings_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../spec_helper', __FILE__) + +ruby_version_is "2.3" do + describe "The --enable-frozen-string-literal flag causes string literals to" do + + it "produce the same object each time" do + ruby_exe(fixture(__FILE__, "freeze_flag_one_literal.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end + + it "produce the same object for literals with the same content" do + ruby_exe(fixture(__FILE__, "freeze_flag_two_literals.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end + + it "produce the same object for literals with the same content in different files" do + ruby_exe(fixture(__FILE__, "freeze_flag_across_files.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end + + it "produce different objects for literals with the same content in different files if they have different encodings" do + ruby_exe(fixture(__FILE__, "freeze_flag_across_files_diff_enc.rb"), options: "--enable-frozen-string-literal").chomp.should == "true" + end + end + + describe "The --debug flag produces" do + it "debugging info on attempted frozen string modification" do + error_str = ruby_exe(fixture(__FILE__, 'debug_info.rb'), options: '--debug', args: "2>&1") + error_str.should include("can't modify frozen String, created at ") + error_str.should include("command_line/fixtures/debug_info.rb:2") + end + end +end diff --git a/spec/rubyspec/command_line/rubyopt_spec.rb b/spec/rubyspec/command_line/rubyopt_spec.rb new file mode 100644 index 0000000000..a80cb51a94 --- /dev/null +++ b/spec/rubyspec/command_line/rubyopt_spec.rb @@ -0,0 +1,160 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "Processing RUBYOPT" do + before (:each) do + @rubyopt, ENV["RUBYOPT"] = ENV["RUBYOPT"], nil + end + + after (:each) do + ENV["RUBYOPT"] = @rubyopt + end + + it "adds the -I path to $LOAD_PATH" do + ENV["RUBYOPT"] = "-Ioptrubyspecincl" + result = ruby_exe("puts $LOAD_PATH.grep(/byspecin/)", escape: true) + result.chomp[-15..-1].should == "optrubyspecincl" + end + + it "sets $DEBUG to true for '-d'" do + ENV["RUBYOPT"] = '-d' + command = %[puts "value of $DEBUG is \#{$DEBUG}"] + result = ruby_exe(command, escape: true, args: "2>&1") + result.should =~ /value of \$DEBUG is true/ + end + + it "prints the version number for '-v'" do + ENV["RUBYOPT"] = '-v' + ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION + end + + it "sets $VERBOSE to true for '-w'" do + ENV["RUBYOPT"] = '-w' + ruby_exe("p $VERBOSE", escape: true).chomp.should == "true" + end + + it "sets $VERBOSE to true for '-W'" do + ENV["RUBYOPT"] = '-W' + ruby_exe("p $VERBOSE", escape: true).chomp.should == "true" + end + + it "sets $VERBOSE to nil for '-W0'" do + ENV["RUBYOPT"] = '-W0' + ruby_exe("p $VERBOSE", escape: true).chomp.should == "nil" + end + + it "sets $VERBOSE to false for '-W1'" do + ENV["RUBYOPT"] = '-W1' + ruby_exe("p $VERBOSE", escape: true).chomp.should == "false" + end + + it "sets $VERBOSE to true for '-W2'" do + ENV["RUBYOPT"] = '-W2' + ruby_exe("p $VERBOSE", escape: true).chomp.should == "true" + end + + it "requires the file for '-r'" do + f = fixture __FILE__, "rubyopt" + ENV["RUBYOPT"] = "-r#{f}" + ruby_exe("0", args: '2>&1').should =~ /^rubyopt.rb required/ + end + + it "raises a RuntimeError for '-a'" do + ENV["RUBYOPT"] = '-a' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-p'" do + ENV["RUBYOPT"] = '-p' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-n'" do + ENV["RUBYOPT"] = '-n' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-y'" do + ENV["RUBYOPT"] = '-y' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-c'" do + ENV["RUBYOPT"] = '-c' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-s'" do + ENV["RUBYOPT"] = '-s' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-h'" do + ENV["RUBYOPT"] = '-h' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--help'" do + ENV["RUBYOPT"] = '--help' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-l'" do + ENV["RUBYOPT"] = '-l' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-S'" do + ENV["RUBYOPT"] = '-S irb' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-e'" do + ENV["RUBYOPT"] = '-e0' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-i'" do + ENV["RUBYOPT"] = '-i.bak' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-x'" do + ENV["RUBYOPT"] = '-x' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-C'" do + ENV["RUBYOPT"] = '-C' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-X'" do + ENV["RUBYOPT"] = '-X.' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-F'" do + ENV["RUBYOPT"] = '-F' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '-0'" do + ENV["RUBYOPT"] = '-0' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--copyright'" do + ENV["RUBYOPT"] = '--copyright' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--version'" do + ENV["RUBYOPT"] = '--version' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end + + it "raises a RuntimeError for '--yydebug'" do + ENV["RUBYOPT"] = '--yydebug' + ruby_exe("", args: '2>&1').should =~ /RuntimeError/ + end +end diff --git a/spec/rubyspec/command_line/shared/verbose.rb b/spec/rubyspec/command_line/shared/verbose.rb new file mode 100644 index 0000000000..457fe3006a --- /dev/null +++ b/spec/rubyspec/command_line/shared/verbose.rb @@ -0,0 +1,9 @@ +describe :command_line_verbose, shared: true do + before :each do + @script = fixture __FILE__, "verbose.rb" + end + + it "sets $VERBOSE to true" do + ruby_exe(@script, options: @method).chomp.split.last.should == "true" + end +end diff --git a/spec/rubyspec/command_line/syntax_error_spec.rb b/spec/rubyspec/command_line/syntax_error_spec.rb new file mode 100644 index 0000000000..71cee32e23 --- /dev/null +++ b/spec/rubyspec/command_line/syntax_error_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The interpreter" do + it "prints an error when given a file with invalid syntax" do + out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), args: "2>&1") + out.should include "syntax error" + end + + it "prints an error when given code via -e with invalid syntax" do + out = ruby_exe(nil, args: "-e 'a{' 2>&1") + out.should include "syntax error" + end +end diff --git a/spec/rubyspec/core/argf/argf_spec.rb b/spec/rubyspec/core/argf/argf_spec.rb new file mode 100644 index 0000000000..b47e77c17f --- /dev/null +++ b/spec/rubyspec/core/argf/argf_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF" do + it "is extended by the Enumerable module" do + ARGF.should be_kind_of(Enumerable) + end + + it "is an instance of ARGF.class" do + ARGF.should be_an_instance_of(ARGF.class) + end +end diff --git a/spec/rubyspec/core/argf/argv_spec.rb b/spec/rubyspec/core/argf/argv_spec.rb new file mode 100644 index 0000000000..e294b3993f --- /dev/null +++ b/spec/rubyspec/core/argf/argv_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.argv" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + it "returns ARGV for the initial ARGF" do + ARGF.argv.should equal ARGV + end + + it "returns the remaining arguments to treat" do + argf [@file1, @file2] do + # @file1 is stored in current file + @argf.argv.should == [@file2] + end + end +end diff --git a/spec/rubyspec/core/argf/binmode_spec.rb b/spec/rubyspec/core/argf/binmode_spec.rb new file mode 100644 index 0000000000..3daf4f4345 --- /dev/null +++ b/spec/rubyspec/core/argf/binmode_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.binmode" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + @bin_file = fixture __FILE__, "bin_file.txt" + end + + it "returns self" do + ruby_exe("puts(ARGF.binmode == ARGF)", args: @bin_file).chomp.should == 'true' + end + + platform_is :windows do + it "puts reading into binmode" do + argf [@bin_file, @bin_file] do + @argf.gets.should == "test\n" + @argf.binmode + @argf.gets.should == "test\r\n" + end + end + + it "puts alls subsequent stream reading through ARGF into binmode" do + argf [@bin_file, @bin_file] do + @argf.binmode + @argf.gets.should == "test\r\n" + @argf.gets.should == "test\r\n" + end + end + end + + platform_is_not :windows do + # This does nothing on Unix but it should not raise any errors. + it "does not raise an error" do + ruby_exe("ARGF.binmode", args: @bin_file) + $?.should be_kind_of(Process::Status) + $?.to_i.should == 0 + end + end + + it "sets the file's encoding to ASCII-8BIT" do + script = fixture __FILE__, "encoding.rb" + output = "true\n#{Encoding::ASCII_8BIT}\n#{Encoding::ASCII_8BIT}\n" + ruby_exe(script, args: [@bin_file, @file1]).should == output + end +end diff --git a/spec/rubyspec/core/argf/bytes_spec.rb b/spec/rubyspec/core/argf/bytes_spec.rb new file mode 100644 index 0000000000..01a3a3db0d --- /dev/null +++ b/spec/rubyspec/core/argf/bytes_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_byte', __FILE__) + +describe "ARGF.bytes" do + it_behaves_like :argf_each_byte, :bytes +end diff --git a/spec/rubyspec/core/argf/chars_spec.rb b/spec/rubyspec/core/argf/chars_spec.rb new file mode 100644 index 0000000000..8c9e4844c0 --- /dev/null +++ b/spec/rubyspec/core/argf/chars_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_char', __FILE__) + +describe "ARGF.chars" do + it_behaves_like :argf_each_char, :chars +end diff --git a/spec/rubyspec/core/argf/close_spec.rb b/spec/rubyspec/core/argf/close_spec.rb new file mode 100644 index 0000000000..b56f7f5564 --- /dev/null +++ b/spec/rubyspec/core/argf/close_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.close" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + end + + it "closes the current open stream" do + argf [@file1_name, @file2_name] do + io = @argf.to_io + @argf.close + io.closed?.should be_true + end + end + + it "returns self" do + argf [@file1_name, @file2_name] do + @argf.close.should equal(@argf) + end + end + + ruby_version_is ""..."2.3" do + it "raises an IOError if called on a closed stream" do + argf [@file1_name] do + lambda { @argf.close }.should_not raise_error + lambda { @argf.close }.should raise_error(IOError) + end + end + end + + ruby_version_is "2.3" do + it "doesn't raise an IOError if called on a closed stream" do + argf [@file1_name] do + lambda { @argf.close }.should_not raise_error + lambda { @argf.close }.should_not raise_error + end + end + end +end + +describe "ARGF.close" do + it "does not close STDIN" do + ruby_exe("ARGV.replace(['-']); ARGF.close; print ARGF.closed?").should == "false" + end +end diff --git a/spec/rubyspec/core/argf/closed_spec.rb b/spec/rubyspec/core/argf/closed_spec.rb new file mode 100644 index 0000000000..745f102484 --- /dev/null +++ b/spec/rubyspec/core/argf/closed_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.closed?" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + end + + it "returns true if the current stream has been closed" do + argf [@file1_name, @file2_name] do + stream = @argf.to_io + stream.close + + @argf.closed?.should be_true + stream.reopen(@argf.filename, 'r') + end + end +end diff --git a/spec/rubyspec/core/argf/codepoints_spec.rb b/spec/rubyspec/core/argf/codepoints_spec.rb new file mode 100644 index 0000000000..cd839cbb8c --- /dev/null +++ b/spec/rubyspec/core/argf/codepoints_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_codepoint', __FILE__) + +describe "ARGF.codepoints" do + it_behaves_like :argf_each_codepoint, :codepoints +end diff --git a/spec/rubyspec/core/argf/each_byte_spec.rb b/spec/rubyspec/core/argf/each_byte_spec.rb new file mode 100644 index 0000000000..8b4a62c2b1 --- /dev/null +++ b/spec/rubyspec/core/argf/each_byte_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_byte', __FILE__) + +describe "ARGF.each_byte" do + it_behaves_like :argf_each_byte, :each_byte +end diff --git a/spec/rubyspec/core/argf/each_char_spec.rb b/spec/rubyspec/core/argf/each_char_spec.rb new file mode 100644 index 0000000000..58ac0bf783 --- /dev/null +++ b/spec/rubyspec/core/argf/each_char_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_char', __FILE__) + +describe "ARGF.each_char" do + it_behaves_like :argf_each_char, :each_char +end diff --git a/spec/rubyspec/core/argf/each_codepoint_spec.rb b/spec/rubyspec/core/argf/each_codepoint_spec.rb new file mode 100644 index 0000000000..d0725841a7 --- /dev/null +++ b/spec/rubyspec/core/argf/each_codepoint_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_codepoint', __FILE__) + +describe "ARGF.each_codepoint" do + it_behaves_like :argf_each_codepoint, :each_codepoint +end diff --git a/spec/rubyspec/core/argf/each_line_spec.rb b/spec/rubyspec/core/argf/each_line_spec.rb new file mode 100644 index 0000000000..d37968af3d --- /dev/null +++ b/spec/rubyspec/core/argf/each_line_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_line', __FILE__) + +describe "ARGF.each_line" do + it_behaves_like :argf_each_line, :each_line +end diff --git a/spec/rubyspec/core/argf/each_spec.rb b/spec/rubyspec/core/argf/each_spec.rb new file mode 100644 index 0000000000..5a1e0dc73c --- /dev/null +++ b/spec/rubyspec/core/argf/each_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_line', __FILE__) + +describe "ARGF.each" do + it_behaves_like :argf_each_line, :each +end diff --git a/spec/rubyspec/core/argf/eof_spec.rb b/spec/rubyspec/core/argf/eof_spec.rb new file mode 100644 index 0000000000..534bf1f8d8 --- /dev/null +++ b/spec/rubyspec/core/argf/eof_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eof', __FILE__) + +describe "ARGF.eof" do + it_behaves_like :argf_eof, :eof +end + +describe "ARGF.eof?" do + it_behaves_like :argf_eof, :eof? +end diff --git a/spec/rubyspec/core/argf/file_spec.rb b/spec/rubyspec/core/argf/file_spec.rb new file mode 100644 index 0000000000..248ffeff17 --- /dev/null +++ b/spec/rubyspec/core/argf/file_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.file" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns the current file object on each file" do + argf [@file1, @file2] do + result = [] + # returns first current file even when not yet open + result << @argf.file.path + result << @argf.file.path while @argf.gets + # returns last current file even when closed + result << @argf.file.path + result.should == [@file1, @file1, @file1, @file2, @file2, @file2] + end + end +end diff --git a/spec/rubyspec/core/argf/filename_spec.rb b/spec/rubyspec/core/argf/filename_spec.rb new file mode 100644 index 0000000000..f7ebafa7c5 --- /dev/null +++ b/spec/rubyspec/core/argf/filename_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/filename', __FILE__) + +describe "ARGF.filename" do + it_behaves_like :argf_filename, :filename +end diff --git a/spec/rubyspec/core/argf/fileno_spec.rb b/spec/rubyspec/core/argf/fileno_spec.rb new file mode 100644 index 0000000000..b35047695c --- /dev/null +++ b/spec/rubyspec/core/argf/fileno_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/fileno', __FILE__) + +describe "ARGF.fileno" do + it_behaves_like :argf_fileno, :fileno +end diff --git a/spec/rubyspec/core/argf/fixtures/bin_file.txt b/spec/rubyspec/core/argf/fixtures/bin_file.txt new file mode 100644 index 0000000000..2545e9037e --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/bin_file.txt @@ -0,0 +1,2 @@ +test +test diff --git a/spec/rubyspec/core/argf/fixtures/encoding.rb b/spec/rubyspec/core/argf/fixtures/encoding.rb new file mode 100644 index 0000000000..6e87e64346 --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/encoding.rb @@ -0,0 +1,5 @@ +ARGF.binmode +puts ARGF.binmode? +puts ARGF.gets.encoding +ARGF.skip +puts ARGF.read.encoding diff --git a/spec/rubyspec/core/argf/fixtures/file1.txt b/spec/rubyspec/core/argf/fixtures/file1.txt new file mode 100644 index 0000000000..1c89bfbd82 --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/file1.txt @@ -0,0 +1,2 @@ +file1.1 +file1.2 diff --git a/spec/rubyspec/core/argf/fixtures/file2.txt b/spec/rubyspec/core/argf/fixtures/file2.txt new file mode 100644 index 0000000000..62e8dba00b --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/file2.txt @@ -0,0 +1,2 @@ +line2.1 +line2.2 diff --git a/spec/rubyspec/core/argf/fixtures/filename.rb b/spec/rubyspec/core/argf/fixtures/filename.rb new file mode 100644 index 0000000000..599c97dd57 --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/filename.rb @@ -0,0 +1,3 @@ +puts $FILENAME while ARGF.gets +# returns last current file even when closed +puts $FILENAME diff --git a/spec/rubyspec/core/argf/fixtures/lineno.rb b/spec/rubyspec/core/argf/fixtures/lineno.rb new file mode 100644 index 0000000000..079cc92e8e --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/lineno.rb @@ -0,0 +1,5 @@ +puts $. +ARGF.gets +puts $. +ARGF.gets +puts $. diff --git a/spec/rubyspec/core/argf/fixtures/rewind.rb b/spec/rubyspec/core/argf/fixtures/rewind.rb new file mode 100644 index 0000000000..90a334cd89 --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/rewind.rb @@ -0,0 +1,5 @@ +puts ARGF.lineno +ARGF.gets +puts ARGF.lineno +ARGF.rewind +puts ARGF.lineno diff --git a/spec/rubyspec/core/argf/fixtures/stdin.txt b/spec/rubyspec/core/argf/fixtures/stdin.txt new file mode 100644 index 0000000000..063cdcb1d4 --- /dev/null +++ b/spec/rubyspec/core/argf/fixtures/stdin.txt @@ -0,0 +1,2 @@ +stdin.1 +stdin.2 diff --git a/spec/rubyspec/core/argf/getc_spec.rb b/spec/rubyspec/core/argf/getc_spec.rb new file mode 100644 index 0000000000..de5e6fa73c --- /dev/null +++ b/spec/rubyspec/core/argf/getc_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/getc', __FILE__) + +describe "ARGF.getc" do + it_behaves_like :argf_getc, :getc +end + +describe "ARGF.getc" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + it "returns nil when end of stream reached" do + argf [@file1, @file2] do + @argf.read + @argf.getc.should == nil + end + end +end diff --git a/spec/rubyspec/core/argf/gets_spec.rb b/spec/rubyspec/core/argf/gets_spec.rb new file mode 100644 index 0000000000..b65aa076bd --- /dev/null +++ b/spec/rubyspec/core/argf/gets_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gets', __FILE__) + +describe "ARGF.gets" do + it_behaves_like :argf_gets, :gets +end + +describe "ARGF.gets" do + it_behaves_like :argf_gets_inplace_edit, :gets +end + +describe "ARGF.gets" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @file1 = File.readlines @file1_name + @file2 = File.readlines @file2_name + end + + it "returns nil when reaching end of files" do + argf [@file1_name, @file2_name] do + total = @file1.size + @file2.size + total.times { @argf.gets } + @argf.gets.should == nil + end + end + + with_feature :encoding do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::UTF_8 + Encoding.default_internal = nil + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "reads the contents of the file with default encoding" do + Encoding.default_external = Encoding::US_ASCII + argf [@file1_name, @file2_name] do + @argf.gets.encoding.should == Encoding::US_ASCII + end + end + end + +end diff --git a/spec/rubyspec/core/argf/lineno_spec.rb b/spec/rubyspec/core/argf/lineno_spec.rb new file mode 100644 index 0000000000..13b1916fb1 --- /dev/null +++ b/spec/rubyspec/core/argf/lineno_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.lineno" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + # TODO: break this into four specs + it "returns the current line number on each file" do + argf [@file1, @file2] do + @argf.lineno = 0 + @argf.gets + @argf.lineno.should == 1 + @argf.gets + @argf.lineno.should == 2 + @argf.gets + @argf.lineno.should == 3 + @argf.gets + @argf.lineno.should == 4 + end + end + + it "aliases to $." do + script = fixture __FILE__, "lineno.rb" + out = ruby_exe(script, args: [@file1, @file2]) + out.should == "0\n1\n2\n" + end +end diff --git a/spec/rubyspec/core/argf/lines_spec.rb b/spec/rubyspec/core/argf/lines_spec.rb new file mode 100644 index 0000000000..ea3578e3a2 --- /dev/null +++ b/spec/rubyspec/core/argf/lines_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each_line', __FILE__) + +describe "ARGF.lines" do + it_behaves_like :argf_each_line, :lines +end diff --git a/spec/rubyspec/core/argf/path_spec.rb b/spec/rubyspec/core/argf/path_spec.rb new file mode 100644 index 0000000000..098de8693a --- /dev/null +++ b/spec/rubyspec/core/argf/path_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/filename', __FILE__) + +describe "ARGF.path" do + it_behaves_like :argf_filename, :path +end diff --git a/spec/rubyspec/core/argf/pos_spec.rb b/spec/rubyspec/core/argf/pos_spec.rb new file mode 100644 index 0000000000..b6e5c3a254 --- /dev/null +++ b/spec/rubyspec/core/argf/pos_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "ARGF.pos" do + it_behaves_like :argf_pos, :pos +end + +describe "ARGF.pos=" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @file1 = File.readlines @file1_name + @file2 = File.readlines @file2_name + end + + # NOTE: this test assumes that fixtures files have two lines each + it "sets the correct position in files" do + argf [@file1_name, @file2_name] do + @argf.pos = @file1.first.size + @argf.gets.should == @file1.last + @argf.pos = 0 + @argf.gets.should == @file1.first + + # finish reading file1 + @argf.gets + + @argf.gets + @argf.pos = 1 + @argf.gets.should == @file2.first[1..-1] + + @argf.pos = @file2.first.size + @file2.last.size - 1 + @argf.gets.should == @file2.last[-1,1] + @argf.pos = 1000 + @argf.read.should == "" + end + end +end diff --git a/spec/rubyspec/core/argf/read_nonblock_spec.rb b/spec/rubyspec/core/argf/read_nonblock_spec.rb new file mode 100644 index 0000000000..8176a206e5 --- /dev/null +++ b/spec/rubyspec/core/argf/read_nonblock_spec.rb @@ -0,0 +1,82 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/read', __FILE__) + +platform_is_not :windows do + describe 'ARGF.read_nonblock' do + it_behaves_like :argf_read, :read_nonblock + + before do + @file1_name = fixture(__FILE__, 'file1.txt') + @file2_name = fixture(__FILE__, 'file2.txt') + + @file1 = File.read(@file1_name) + @file2 = File.read(@file2_name) + + @chunk1 = File.read(@file1_name, 4) + @chunk2 = File.read(@file2_name, 4) + end + + it 'reads up to the given amount of bytes' do + argf [@file1_name] do + @argf.read_nonblock(4).should == @chunk1 + end + end + + describe 'when using multiple files' do + it 'reads up to the given amount of bytes from the first file' do + argf [@file1_name, @file2_name] do + @argf.read_nonblock(4).should == @chunk1 + end + end + + it 'returns an empty String when reading after having read the first file in its entirety' do + argf [@file1_name, @file2_name] do + @argf.read_nonblock(File.size(@file1_name)).should == @file1 + @argf.read_nonblock(4).should == '' + end + end + end + + it 'reads up to the given bytes from STDIN' do + stdin = ruby_exe('print ARGF.read_nonblock(4)', :args => "< #{@file1_name}") + + stdin.should == @chunk1 + end + + it 'reads up to the given bytes from a file when a file and STDIN are present' do + stdin = ruby_exe("print ARGF.read_nonblock(4)", :args => "#{@file1_name} - < #{@file2_name}") + + stdin.should == @chunk1 + end + + context "with STDIN" do + before do + @r, @w = IO.pipe + @stdin = $stdin + $stdin = @r + end + + after do + $stdin = @stdin + @w.close + @r.close unless @r.closed? + end + + it 'raises IO::EAGAINWaitReadable when empty' do + argf ['-'] do + lambda { + @argf.read_nonblock(4) + }.should raise_error(IO::EAGAINWaitReadable) + end + end + + ruby_version_is "2.3" do + it 'returns :wait_readable when the :exception is set to false' do + argf ['-'] do + @argf.read_nonblock(4, nil, exception: false).should == :wait_readable + end + end + end + end + end +end diff --git a/spec/rubyspec/core/argf/read_spec.rb b/spec/rubyspec/core/argf/read_spec.rb new file mode 100644 index 0000000000..4eaaea46bb --- /dev/null +++ b/spec/rubyspec/core/argf/read_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/read', __FILE__) + +describe "ARGF.read" do + it_behaves_like :argf_read, :read + + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + @stdin_name = fixture __FILE__, "stdin.txt" + + @file1 = File.read @file1_name + @file2 = File.read @file2_name + @stdin = File.read @stdin_name + end + + it "reads the contents of a file" do + argf [@file1_name] do + @argf.read().should == @file1 + end + end + + it "treats first nil argument as no length limit" do + argf [@file1_name] do + @argf.read(nil).should == @file1 + end + end + + it "reads the contents of two files" do + argf [@file1_name, @file2_name] do + @argf.read.should == @file1 + @file2 + end + end + + it "reads the contents of one file and some characters from the second" do + argf [@file1_name, @file2_name] do + len = @file1.size + (@file2.size / 2) + @argf.read(len).should == (@file1 + @file2)[0,len] + end + end + + it "reads across two files consecutively" do + argf [@file1_name, @file2_name] do + @argf.read(@file1.size - 2).should == @file1[0..-3] + @argf.read(2+5).should == @file1[-2..-1] + @file2[0,5] + end + end + + it "reads the contents of stdin" do + stdin = ruby_exe("print ARGF.read", args: "< #{@stdin_name}") + stdin.should == @stdin + end + + it "reads the contents of one file and stdin" do + stdin = ruby_exe("print ARGF.read", args: "#{@file1_name} - < #{@stdin_name}") + stdin.should == @file1 + @stdin + end + + it "reads the contents of the same file twice" do + argf [@file1_name, @file1_name] do + @argf.read.should == @file1 + @file1 + end + end + + with_feature :encoding do + + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::UTF_8 + Encoding.default_internal = nil + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "reads the contents of the file with default encoding" do + Encoding.default_external = Encoding::US_ASCII + argf [@file1_name, @file2_name] do + @argf.read.encoding.should == Encoding::US_ASCII + end + end + end +end diff --git a/spec/rubyspec/core/argf/readchar_spec.rb b/spec/rubyspec/core/argf/readchar_spec.rb new file mode 100644 index 0000000000..71e2a6c742 --- /dev/null +++ b/spec/rubyspec/core/argf/readchar_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/getc', __FILE__) + +describe "ARGF.getc" do + it_behaves_like :argf_getc, :readchar +end + +describe "ARGF.readchar" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + it "raises EOFError when end of stream reached" do + argf [@file1, @file2] do + lambda { while @argf.readchar; end }.should raise_error(EOFError) + end + end +end diff --git a/spec/rubyspec/core/argf/readline_spec.rb b/spec/rubyspec/core/argf/readline_spec.rb new file mode 100644 index 0000000000..d31cfce415 --- /dev/null +++ b/spec/rubyspec/core/argf/readline_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gets', __FILE__) + +describe "ARGF.readline" do + it_behaves_like :argf_gets, :readline +end + +describe "ARGF.readline" do + it_behaves_like :argf_gets_inplace_edit, :readline +end + +describe "ARGF.readline" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + it "raises an EOFError when reaching end of files" do + argf [@file1, @file2] do + lambda { while @argf.readline; end }.should raise_error(EOFError) + end + end +end diff --git a/spec/rubyspec/core/argf/readlines_spec.rb b/spec/rubyspec/core/argf/readlines_spec.rb new file mode 100644 index 0000000000..8e68429aa8 --- /dev/null +++ b/spec/rubyspec/core/argf/readlines_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/readlines', __FILE__) + +describe "ARGF.readlines" do + it_behaves_like :argf_readlines, :readlines +end diff --git a/spec/rubyspec/core/argf/readpartial_spec.rb b/spec/rubyspec/core/argf/readpartial_spec.rb new file mode 100644 index 0000000000..61c330182c --- /dev/null +++ b/spec/rubyspec/core/argf/readpartial_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/read', __FILE__) + +describe "ARGF.readpartial" do + it_behaves_like :argf_read, :readpartial + + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + @stdin_name = fixture __FILE__, "stdin.txt" + + @file1 = File.read @file1_name + @file2 = File.read @file2_name + @stdin = File.read @stdin_name + end + + it "raises an ArgumentError if called without a maximum read length" do + argf [@file1_name] do + lambda { @argf.readpartial }.should raise_error(ArgumentError) + end + end + + it "reads maximum number of bytes from one file at a time" do + argf [@file1_name, @file2_name] do + len = @file1.size + @file2.size + @argf.readpartial(len).should == @file1 + end + end + + it "clears output buffer even if EOFError is raised because @argf is at end" do + begin + output = "to be cleared" + + argf [@file1_name] do + @argf.read + @argf.readpartial(1, output) + end + rescue EOFError + output.should == "" + end + end + + it "reads maximum number of bytes from one file at a time" do + argf [@file1_name, @file2_name] do + len = @file1.size + @file2.size + @argf.readpartial(len).should == @file1 + end + end + + it "returns an empty string if EOFError is raised while reading any but the last file" do + argf [@file1_name, @file2_name] do + @argf.readpartial(@file1.size) + @argf.readpartial(1).should == "" + end + end + + ruby_version_is "2.3" do + it "raises an EOFError if the exception was raised while reading the last file" do + argf [@file1_name, @file2_name] do + @argf.readpartial(@file1.size) + @argf.readpartial(1) + @argf.readpartial(@file2.size) + lambda { @argf.readpartial(1) }.should raise_error(EOFError) + lambda { @argf.readpartial(1) }.should raise_error(EOFError) + end + end + end + + it "raises an EOFError if the exception was raised while reading STDIN" do + ruby_str = <<-STR + print ARGF.readpartial(#{@stdin.size}) + ARGF.readpartial(1) rescue print $!.class + STR + stdin = ruby_exe(ruby_str, args: "< #{@stdin_name}", escape: true) + stdin.should == @stdin + "EOFError" + end +end diff --git a/spec/rubyspec/core/argf/rewind_spec.rb b/spec/rubyspec/core/argf/rewind_spec.rb new file mode 100644 index 0000000000..d6abc3a856 --- /dev/null +++ b/spec/rubyspec/core/argf/rewind_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.rewind" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @file1 = File.readlines @file1_name + @file2 = File.readlines @file2_name + end + + # NOTE: this test assumes that fixtures files have two lines each + it "goes back to beginning of current file" do + argf [@file1_name, @file2_name] do + @argf.gets + @argf.rewind + @argf.gets.should == @file1.first + + @argf.gets # finish reading file1 + + @argf.gets + @argf.rewind + @argf.gets.should == @file2.first + end + end + + it "resets ARGF.lineno to 0" do + script = fixture __FILE__, "rewind.rb" + out = ruby_exe(script, args: [@file1_name, @file2_name]) + out.should == "0\n1\n0\n" + end + + it "raises an ArgumentError when end of stream reached" do + argf [@file1_name, @file2_name] do + @argf.read + lambda { @argf.rewind }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/argf/seek_spec.rb b/spec/rubyspec/core/argf/seek_spec.rb new file mode 100644 index 0000000000..97dacb6cfc --- /dev/null +++ b/spec/rubyspec/core/argf/seek_spec.rb @@ -0,0 +1,63 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.seek" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @file1 = File.readlines @file1_name + @file2 = File.readlines @file2_name + end + + it "sets the absolute position relative to beginning of file" do + argf [@file1_name, @file2_name] do + @argf.seek 2 + @argf.gets.should == @file1.first[2..-1] + @argf.seek @file1.first.size + @argf.gets.should == @file1.last + @argf.seek 0, IO::SEEK_END + @argf.gets.should == @file2.first + end + end + + it "sets the position relative to current position in file" do + argf [@file1_name, @file2_name] do + @argf.seek(0, IO::SEEK_CUR) + @argf.gets.should == @file1.first + @argf.seek(-@file1.first.size+2, IO::SEEK_CUR) + @argf.gets.should == @file1.first[2..-1] + @argf.seek(1, IO::SEEK_CUR) + @argf.gets.should == @file1.last[1..-1] + @argf.seek(3, IO::SEEK_CUR) + @argf.gets.should == @file2.first + @argf.seek(@file1.last.size, IO::SEEK_CUR) + @argf.gets.should == nil + end + end + + it "sets the absolute position relative to end of file" do + argf [@file1_name, @file2_name] do + @argf.seek(-@file1.first.size-@file1.last.size, IO::SEEK_END) + @argf.gets.should == @file1.first + @argf.seek(-6, IO::SEEK_END) + @argf.gets.should == @file1.last[-6..-1] + @argf.seek(-4, IO::SEEK_END) + @argf.gets.should == @file1.last[4..-1] + @argf.gets.should == @file2.first + @argf.seek(-6, IO::SEEK_END) + @argf.gets.should == @file2.last[-6..-1] + end + end +end + +describe "ARGF.seek" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + end + + it "takes at least one argument (offset)" do + argf [@file1_name] do + lambda { @argf.seek }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/argf/set_encoding_spec.rb b/spec/rubyspec/core/argf/set_encoding_spec.rb new file mode 100644 index 0000000000..16d49a8c44 --- /dev/null +++ b/spec/rubyspec/core/argf/set_encoding_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +# These specs need to be run to a separate process as there is no way to reset ARGF encoding +describe "ARGF.set_encoding" do + before :each do + @file = fixture __FILE__, "file1.txt" + end + + it "sets the external encoding when passed an encoding instance" do + enc = ruby_exe('ARGF.set_encoding(Encoding::UTF_8); print ARGF.gets.encoding', args: [@file]) + enc.should == "UTF-8" + end + + it "sets the external encoding when passed an encoding name" do + enc = ruby_exe('ARGF.set_encoding("utf-8"); print ARGF.gets.encoding', args: [@file]) + enc.should == "UTF-8" + end + + it "sets the external, internal encoding when passed two encoding instances" do + enc = ruby_exe('ARGF.set_encoding(Encoding::UTF_8, Encoding::EUC_JP); print ARGF.gets.encoding', args: [@file]) + enc.should == "EUC-JP" + end + + it "sets the external, internal encoding when passed 'ext:int' String" do + enc = ruby_exe('ARGF.set_encoding("utf-8:euc-jp"); print ARGF.gets.encoding', args: [@file]) + enc.should == "EUC-JP" + end +end diff --git a/spec/rubyspec/core/argf/shared/each_byte.rb b/spec/rubyspec/core/argf/shared/each_byte.rb new file mode 100644 index 0000000000..6b1dc1dae2 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/each_byte.rb @@ -0,0 +1,58 @@ +describe :argf_each_byte, shared: true do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @bytes = [] + File.read(@file1_name).each_byte { |b| @bytes << b } + File.read(@file2_name).each_byte { |b| @bytes << b } + end + + it "yields each byte of all streams to the passed block" do + argf [@file1_name, @file2_name] do + bytes = [] + @argf.send(@method) { |b| bytes << b } + bytes.should == @bytes + end + end + + it "returns self when passed a block" do + argf [@file1_name, @file2_name] do + @argf.send(@method) {}.should equal(@argf) + end + end + + it "returns an Enumerator when passed no block" do + argf [@file1_name, @file2_name] do + enum = @argf.send(@method) + enum.should be_an_instance_of(Enumerator) + + bytes = [] + enum.each { |b| bytes << b } + bytes.should == @bytes + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf [@file1_name, @file2_name] do + enum = @argf.send(@method) + enum.should be_an_instance_of(Enumerator) + + bytes = [] + enum.each { |b| bytes << b } + bytes.should == @bytes + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf [@file1_name, @file2_name] do + @argf.send(@method).size.should == nil + end + end + end + end + end +end diff --git a/spec/rubyspec/core/argf/shared/each_char.rb b/spec/rubyspec/core/argf/shared/each_char.rb new file mode 100644 index 0000000000..9e333ecc5b --- /dev/null +++ b/spec/rubyspec/core/argf/shared/each_char.rb @@ -0,0 +1,58 @@ +describe :argf_each_char, shared: true do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @chars = [] + File.read(@file1_name).each_char { |c| @chars << c } + File.read(@file2_name).each_char { |c| @chars << c } + end + + it "yields each char of all streams to the passed block" do + argf [@file1_name, @file2_name] do + chars = [] + @argf.send(@method) { |c| chars << c } + chars.should == @chars + end + end + + it "returns self when passed a block" do + argf [@file1_name, @file2_name] do + @argf.send(@method) {}.should equal(@argf) + end + end + + it "returns an Enumerator when passed no block" do + argf [@file1_name, @file2_name] do + enum = @argf.send(@method) + enum.should be_an_instance_of(Enumerator) + + chars = [] + enum.each { |c| chars << c } + chars.should == @chars + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf [@file1_name, @file2_name] do + enum = @argf.send(@method) + enum.should be_an_instance_of(Enumerator) + + chars = [] + enum.each { |c| chars << c } + chars.should == @chars + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf [@file1_name, @file2_name] do + @argf.send(@method).size.should == nil + end + end + end + end + end +end diff --git a/spec/rubyspec/core/argf/shared/each_codepoint.rb b/spec/rubyspec/core/argf/shared/each_codepoint.rb new file mode 100644 index 0000000000..e2a2dfff46 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/each_codepoint.rb @@ -0,0 +1,58 @@ +describe :argf_each_codepoint, shared: true do + before :each do + file1_name = fixture __FILE__, "file1.txt" + file2_name = fixture __FILE__, "file2.txt" + @filenames = [file1_name, file2_name] + + @codepoints = File.read(file1_name).codepoints + @codepoints.concat File.read(file2_name).codepoints + end + + it "is a public method" do + argf @filenames do + @argf.public_methods(false).should include(@method) + end + end + + it "does not require arguments" do + argf @filenames do + @argf.method(@method).arity.should == 0 + end + end + + it "returns self when passed a block" do + argf @filenames do + @argf.send(@method) {}.should equal(@argf) + end + end + + it "returns an Enumerator when passed no block" do + argf @filenames do + @argf.send(@method).should be_an_instance_of(Enumerator) + end + end + + it "yields each codepoint of all streams" do + argf @filenames do + @argf.send(@method).to_a.should == @codepoints + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf @filenames do + @argf.send(@method).should be_an_instance_of(Enumerator) + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf @filenames do + @argf.send(@method).size.should == nil + end + end + end + end + end +end diff --git a/spec/rubyspec/core/argf/shared/each_line.rb b/spec/rubyspec/core/argf/shared/each_line.rb new file mode 100644 index 0000000000..c0ef77dc54 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/each_line.rb @@ -0,0 +1,62 @@ +describe :argf_each_line, shared: true do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @lines = File.readlines @file1_name + @lines += File.readlines @file2_name + end + + it "is a public method" do + argf [@file1_name, @file2_name] do + @argf.public_methods(false).should include(@method) + end + end + + it "requires multiple arguments" do + argf [@file1_name, @file2_name] do + @argf.method(@method).arity.should < 0 + end + end + + it "reads each line of files" do + argf [@file1_name, @file2_name] do + lines = [] + @argf.send(@method) { |b| lines << b } + lines.should == @lines + end + end + + it "returns self when passed a block" do + argf [@file1_name, @file2_name] do + @argf.send(@method) {}.should equal(@argf) + end + end + + describe "with a separator" do + it "yields each separated section of all streams" do + argf [@file1_name, @file2_name] do + @argf.send(@method, '.').to_a.should == + (File.readlines(@file1_name, '.') + File.readlines(@file2_name, '.')) + end + end + end + + describe "when no block is given" do + it "returns an Enumerator" do + argf [@file1_name, @file2_name] do + @argf.send(@method).should be_an_instance_of(Enumerator) + end + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + argf [@file1_name, @file2_name] do + @argf.send(@method).size.should == nil + end + end + end + end + end +end diff --git a/spec/rubyspec/core/argf/shared/eof.rb b/spec/rubyspec/core/argf/shared/eof.rb new file mode 100644 index 0000000000..bba18ede50 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/eof.rb @@ -0,0 +1,24 @@ +describe :argf_eof, shared: true do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns true when reaching the end of a file" do + argf [@file1, @file2] do + result = [] + while @argf.gets + result << @argf.send(@method) + end + result.should == [false, true, false, true] + end + end + + it "raises IOError when called on a closed stream" do + argf [@file1] do + @argf.read + lambda { @argf.send(@method) }.should raise_error(IOError) + end + end +end diff --git a/spec/rubyspec/core/argf/shared/filename.rb b/spec/rubyspec/core/argf/shared/filename.rb new file mode 100644 index 0000000000..f47c673dc0 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/filename.rb @@ -0,0 +1,28 @@ +describe :argf_filename, shared: true do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns the current file name on each file" do + argf [@file1, @file2] do + result = [] + # returns first current file even when not yet open + result << @argf.send(@method) + result << @argf.send(@method) while @argf.gets + # returns last current file even when closed + result << @argf.send(@method) + + result.map! { |f| File.expand_path(f) } + result.should == [@file1, @file1, @file1, @file2, @file2, @file2] + end + end + + # NOTE: this test assumes that fixtures files have two lines each + it "sets the $FILENAME global variable with the current file name on each file" do + script = fixture __FILE__, "filename.rb" + out = ruby_exe(script, args: [@file1, @file2]) + out.should == "#{@file1}\n#{@file1}\n#{@file2}\n#{@file2}\n#{@file2}\n" + end +end diff --git a/spec/rubyspec/core/argf/shared/fileno.rb b/spec/rubyspec/core/argf/shared/fileno.rb new file mode 100644 index 0000000000..891e250ad9 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/fileno.rb @@ -0,0 +1,24 @@ +describe :argf_fileno, shared: true do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns the current file number on each file" do + argf [@file1, @file2] do + result = [] + # returns first current file even when not yet open + result << @argf.send(@method) while @argf.gets + # returns last current file even when closed + result.map { |d| d.class }.should == [Fixnum, Fixnum, Fixnum, Fixnum] + end + end + + it "raises an ArgumentError when called on a closed stream" do + argf [@file1] do + @argf.read + lambda { @argf.send(@method) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/argf/shared/getc.rb b/spec/rubyspec/core/argf/shared/getc.rb new file mode 100644 index 0000000000..8be39c60b6 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/getc.rb @@ -0,0 +1,17 @@ +describe :argf_getc, shared: true do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + + @chars = File.read @file1 + @chars += File.read @file2 + end + + it "reads each char of files" do + argf [@file1, @file2] do + chars = "" + @chars.size.times { chars << @argf.send(@method) } + chars.should == @chars + end + end +end diff --git a/spec/rubyspec/core/argf/shared/gets.rb b/spec/rubyspec/core/argf/shared/gets.rb new file mode 100644 index 0000000000..160d24c27b --- /dev/null +++ b/spec/rubyspec/core/argf/shared/gets.rb @@ -0,0 +1,99 @@ +describe :argf_gets, shared: true do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + @stdin_name = fixture __FILE__, "stdin.txt" + + @file1 = File.readlines @file1_name + @file2 = File.readlines @file2_name + @stdin = File.read @stdin_name + end + + it "reads one line of a file" do + argf [@file1_name] do + @argf.send(@method).should == @file1.first + end + end + + it "reads all lines of a file" do + argf [@file1_name] do + lines = [] + @file1.size.times { lines << @argf.send(@method) } + lines.should == @file1 + end + end + + it "reads all lines of stdin" do + total = @stdin.count $/ + stdin = ruby_exe( + "#{total}.times { print ARGF.send(#{@method.inspect}) }", + args: "< #{@stdin_name}") + stdin.should == @stdin + end + + it "reads all lines of two files" do + argf [@file1_name, @file2_name] do + total = @file1.size + @file2.size + lines = [] + total.times { lines << @argf.send(@method) } + lines.should == @file1 + @file2 + end + end + + it "sets $_ global variable with each line read" do + argf [@file1_name, @file2_name] do + total = @file1.size + @file2.size + total.times do + line = @argf.send(@method) + $_.should == line + end + end + end +end + +describe :argf_gets_inplace_edit, shared: true do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @tmp1_name = tmp "file1.txt" + @tmp2_name = tmp "file2.txt" + + @tmp1_name_bak = @tmp1_name + ".bak" + @tmp2_name_bak = @tmp2_name + ".bak" + + cp @file1_name, @tmp1_name + cp @file2_name, @tmp2_name + + method = "ARGF.send(#{@method.inspect})" + @code = "begin while line = #{method} do puts 'x' end rescue EOFError; end" + end + + after :each do + rm_r @tmp1_name, @tmp2_name, @tmp1_name_bak, @tmp2_name_bak + end + + # -i with no backup extension is not supported on Windows + platform_is_not :windows do + it "modifies the files when in place edit mode is on" do + ruby_exe(@code, + options: "-i", + args: "#{@tmp1_name} #{@tmp2_name}") + + File.read(@tmp1_name).should == "x\nx\n" + File.read(@tmp2_name).should == "x\nx\n" + end + end + + it "modifies and backups two files when in place edit mode is on" do + ruby_exe(@code, + options: "-i.bak", + args: "#{@tmp1_name} #{@tmp2_name}") + + File.read(@tmp1_name).should == "x\nx\n" + File.read(@tmp2_name).should == "x\nx\n" + + File.read(@tmp1_name_bak).should == "file1.1\nfile1.2\n" + File.read(@tmp2_name_bak).should == "line2.1\nline2.2\n" + end +end diff --git a/spec/rubyspec/core/argf/shared/pos.rb b/spec/rubyspec/core/argf/shared/pos.rb new file mode 100644 index 0000000000..f7184f3d7c --- /dev/null +++ b/spec/rubyspec/core/argf/shared/pos.rb @@ -0,0 +1,31 @@ +describe :argf_pos, shared: true do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + it "gives the correct position for each read operation" do + argf [@file1, @file2] do + size1 = File.size(@file1) + size2 = File.size(@file2) + + @argf.read(2) + @argf.send(@method).should == 2 + @argf.read(size1-2) + @argf.send(@method).should == size1 + @argf.read(6) + @argf.send(@method).should == 6 + @argf.rewind + @argf.send(@method).should == 0 + @argf.read(size2) + @argf.send(@method).should == size2 + end + end + + it "raises an ArgumentError when called on a closed stream" do + argf [@file1] do + @argf.read + lambda { @argf.send(@method) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/argf/shared/read.rb b/spec/rubyspec/core/argf/shared/read.rb new file mode 100644 index 0000000000..fe903983c0 --- /dev/null +++ b/spec/rubyspec/core/argf/shared/read.rb @@ -0,0 +1,58 @@ +describe :argf_read, shared: true do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @stdin_name = fixture __FILE__, "stdin.txt" + + @file1 = File.read @file1_name + @stdin = File.read @stdin_name + end + + it "treats second nil argument as no output buffer" do + argf [@file1_name] do + @argf.send(@method, @file1.size, nil).should == @file1 + end + end + + it "treats second argument as an output buffer" do + argf [@file1_name] do + buffer = "" + @argf.send(@method, @file1.size, buffer) + buffer.should == @file1 + end + end + + it "clears output buffer before appending to it" do + argf [@file1_name] do + buffer = "to be cleared" + @argf.send(@method, @file1.size, buffer) + buffer.should == @file1 + end + end + + it "reads a number of bytes from the first file" do + argf [@file1_name] do + @argf.send(@method, 5).should == @file1[0, 5] + end + end + + it "reads from a single file consecutively" do + argf [@file1_name] do + @argf.send(@method, 1).should == @file1[0, 1] + @argf.send(@method, 2).should == @file1[1, 2] + @argf.send(@method, 3).should == @file1[3, 3] + end + end + + it "reads a number of bytes from stdin" do + stdin = ruby_exe("print ARGF.#{@method}(10)", :args => "< #{@stdin_name}") + stdin.should == @stdin[0, 10] + end + + platform_is_not :windows do + it "reads the contents of a special device file" do + argf ['/dev/zero'] do + @argf.send(@method, 100).should == "\000" * 100 + end + end + end +end diff --git a/spec/rubyspec/core/argf/shared/readlines.rb b/spec/rubyspec/core/argf/shared/readlines.rb new file mode 100644 index 0000000000..505fa94acb --- /dev/null +++ b/spec/rubyspec/core/argf/shared/readlines.rb @@ -0,0 +1,22 @@ +describe :argf_readlines, shared: true do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + + @lines = File.readlines(@file1) + @lines += File.readlines(@file2) + end + + it "reads all lines of all files" do + argf [@file1, @file2] do + @argf.send(@method).should == @lines + end + end + + it "returns an empty Array when end of stream reached" do + argf [@file1, @file2] do + @argf.read + @argf.send(@method).should == [] + end + end +end diff --git a/spec/rubyspec/core/argf/skip_spec.rb b/spec/rubyspec/core/argf/skip_spec.rb new file mode 100644 index 0000000000..5f5e9eb79a --- /dev/null +++ b/spec/rubyspec/core/argf/skip_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.skip" do + before :each do + @file1_name = fixture __FILE__, "file1.txt" + @file2_name = fixture __FILE__, "file2.txt" + + @file2 = File.readlines @file2_name + end + + it "skips the current file" do + argf [@file1_name, @file2_name] do + @argf.read(1) + @argf.skip + @argf.gets.should == @file2.first + end + end + + it "has no effect when called twice in a row" do + argf [@file1_name, @file2_name] do + @argf.read(1) + @argf.skip + @argf.skip + @argf.gets.should == @file2.first + end + end + + it "has no effect at end of stream" do + argf [@file1_name, @file2_name] do + @argf.read + @argf.skip + @argf.gets.should == nil + end + end + + # This bypasses argf helper because the helper will call argf.file + # which as a side-effect calls argf.file which will initialize + # internals of ARGF enough for this to work. + it "has no effect when nothing has been processed yet" do + lambda { ARGF.class.new(@file1_name).skip }.should_not raise_error + end +end diff --git a/spec/rubyspec/core/argf/tell_spec.rb b/spec/rubyspec/core/argf/tell_spec.rb new file mode 100644 index 0000000000..bcd824f087 --- /dev/null +++ b/spec/rubyspec/core/argf/tell_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "ARGF.tell" do + it_behaves_like :argf_pos, :tell +end diff --git a/spec/rubyspec/core/argf/to_a_spec.rb b/spec/rubyspec/core/argf/to_a_spec.rb new file mode 100644 index 0000000000..75b5c10c9b --- /dev/null +++ b/spec/rubyspec/core/argf/to_a_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/readlines', __FILE__) + +describe "ARGF.to_a" do + it_behaves_like :argf_readlines, :to_a +end diff --git a/spec/rubyspec/core/argf/to_i_spec.rb b/spec/rubyspec/core/argf/to_i_spec.rb new file mode 100644 index 0000000000..27359014af --- /dev/null +++ b/spec/rubyspec/core/argf/to_i_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/fileno', __FILE__) + +describe "ARGF.to_i" do + it_behaves_like :argf_fileno, :to_i +end diff --git a/spec/rubyspec/core/argf/to_io_spec.rb b/spec/rubyspec/core/argf/to_io_spec.rb new file mode 100644 index 0000000000..0575c35f25 --- /dev/null +++ b/spec/rubyspec/core/argf/to_io_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.to_io" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + # NOTE: this test assumes that fixtures files have two lines each + it "returns the IO of the current file" do + argf [@file1, @file2] do + result = [] + 4.times do + @argf.gets + result << @argf.to_io + end + + result.each { |io| io.should be_kind_of(IO) } + result[0].should == result[1] + result[2].should == result[3] + end + end +end diff --git a/spec/rubyspec/core/argf/to_s_spec.rb b/spec/rubyspec/core/argf/to_s_spec.rb new file mode 100644 index 0000000000..0128049c3f --- /dev/null +++ b/spec/rubyspec/core/argf/to_s_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ARGF.to_s" do + before :each do + @file1 = fixture __FILE__, "file1.txt" + @file2 = fixture __FILE__, "file2.txt" + end + + it "returns 'ARGF'" do + argf [@file1, @file2] do + @argf.to_s.should == "ARGF" + end + end +end diff --git a/spec/rubyspec/core/array/allocate_spec.rb b/spec/rubyspec/core/array/allocate_spec.rb new file mode 100644 index 0000000000..bb5168cb74 --- /dev/null +++ b/spec/rubyspec/core/array/allocate_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array.allocate" do + it "returns an instance of Array" do + ary = Array.allocate + ary.should be_an_instance_of(Array) + end + + it "returns a fully-formed instance of Array" do + ary = Array.allocate + ary.size.should == 0 + ary << 1 + ary.should == [1] + end + + it "does not accept any arguments" do + lambda { Array.allocate(1) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/array/any_spec.rb b/spec/rubyspec/core/array/any_spec.rb new file mode 100644 index 0000000000..7e9863420f --- /dev/null +++ b/spec/rubyspec/core/array/any_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#any?" do + describe 'with no block given (a default block of { |x| x } is implicit)' do + it "is false if the array is empty" do + empty_array = [] + empty_array.any?.should == false + end + + it "is false if the array is not empty, but all the members of the array are falsy" do + falsy_array = [false, nil, false] + falsy_array.any?.should == false + end + + it "is true if the array has any truthy members" do + not_empty_array = ['anything', nil] + not_empty_array.any?.should == true + end + end + + describe 'with a block given' do + it 'is false if the array is empty' do + empty_array = [] + empty_array.any? {|v| 1 == 1 }.should == false + end + + it 'is true if the block returns true for any member of the array' do + array_with_members = [false, false, true, false] + array_with_members.any? {|v| v == true }.should == true + end + + it 'is false if the block returns false for all members of the array' do + array_with_members = [false, false, true, false] + array_with_members.any? {|v| v == 42 }.should == false + end + end +end diff --git a/spec/rubyspec/core/array/append_spec.rb b/spec/rubyspec/core/array/append_spec.rb new file mode 100644 index 0000000000..4c65004c58 --- /dev/null +++ b/spec/rubyspec/core/array/append_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#<<" do + it "pushes the object onto the end of the array" do + ([ 1, 2 ] << "c" << "d" << [ 3, 4 ]).should == [1, 2, "c", "d", [3, 4]] + end + + it "returns self to allow chaining" do + a = [] + b = a + (a << 1).should equal(b) + (a << 2 << 3).should equal(b) + end + + it "correctly resizes the Array" do + a = [] + a.size.should == 0 + a << :foo + a.size.should == 1 + a << :bar << :baz + a.size.should == 3 + + a = [1, 2, 3] + a.shift + a.shift + a.shift + a << :foo + a.should == [:foo] + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array << 5 }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/array_spec.rb b/spec/rubyspec/core/array/array_spec.rb new file mode 100644 index 0000000000..186bd40f10 --- /dev/null +++ b/spec/rubyspec/core/array/array_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array" do + it "includes Enumerable" do + Array.include?(Enumerable).should == true + end +end diff --git a/spec/rubyspec/core/array/assoc_spec.rb b/spec/rubyspec/core/array/assoc_spec.rb new file mode 100644 index 0000000000..37b0357806 --- /dev/null +++ b/spec/rubyspec/core/array/assoc_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#assoc" do + it "returns the first array whose 1st item is == obj or nil" do + s1 = ["colors", "red", "blue", "green"] + s2 = [:letters, "a", "b", "c"] + s3 = [4] + s4 = ["colors", "cyan", "yellow", "magenda"] + s5 = [:letters, "a", "i", "u"] + s_nil = [nil, nil] + a = [s1, s2, s3, s4, s5, s_nil] + a.assoc(s1.first).should equal(s1) + a.assoc(s2.first).should equal(s2) + a.assoc(s3.first).should equal(s3) + a.assoc(s4.first).should equal(s1) + a.assoc(s5.first).should equal(s2) + a.assoc(s_nil.first).should equal(s_nil) + a.assoc(4).should equal(s3) + a.assoc("key not in array").should be_nil + end + + it "calls == on first element of each array" do + key1 = 'it' + key2 = mock('key2') + items = [['not it', 1], [ArraySpecs::AssocKey.new, 2], ['na', 3]] + + items.assoc(key1).should equal(items[1]) + items.assoc(key2).should be_nil + end + + it "ignores any non-Array elements" do + [1, 2, 3].assoc(2).should be_nil + s1 = [4] + s2 = [5, 4, 3] + a = ["foo", [], s1, s2, nil, []] + a.assoc(s1.first).should equal(s1) + a.assoc(s2.first).should equal(s2) + end +end diff --git a/spec/rubyspec/core/array/at_spec.rb b/spec/rubyspec/core/array/at_spec.rb new file mode 100644 index 0000000000..e40c26f2cc --- /dev/null +++ b/spec/rubyspec/core/array/at_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#at" do + it "returns the (n+1)'th element for the passed index n" do + a = [1, 2, 3, 4, 5, 6] + a.at(0).should == 1 + a.at(1).should == 2 + a.at(5).should == 6 + end + + it "returns nil if the given index is greater than or equal to the array's length" do + a = [1, 2, 3, 4, 5, 6] + a.at(6).should == nil + a.at(7).should == nil + end + + it "returns the (-n)'th elemet from the last, for the given negative index n" do + a = [1, 2, 3, 4, 5, 6] + a.at(-1).should == 6 + a.at(-2).should == 5 + a.at(-6).should == 1 + end + + it "returns nil if the given index is less than -len, where len is length of the array" do + a = [1, 2, 3, 4, 5, 6] + a.at(-7).should == nil + a.at(-8).should == nil + end + + it "does not extend the array unless the given index is out of range" do + a = [1, 2, 3, 4, 5, 6] + a.length.should == 6 + a.at(100) + a.length.should == 6 + a.at(-100) + a.length.should == 6 + end + + it "tries to convert the passed argument to an Integer using #to_int" do + a = ["a", "b", "c"] + a.at(0.5).should == "a" + + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + a.at(obj).should == "c" + end + + it "raises a TypeError when the passed argument can't be coerced to Integer" do + lambda { [].at("cat") }.should raise_error(TypeError) + end + + it "raises an ArgumentError when 2 or more arguments is passed" do + lambda { [:a, :b].at(0,1) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/array/bsearch_index_spec.rb b/spec/rubyspec/core/array/bsearch_index_spec.rb new file mode 100644 index 0000000000..1ed11876b4 --- /dev/null +++ b/spec/rubyspec/core/array/bsearch_index_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +ruby_version_is "2.3" do + describe "Array#bsearch_index" do + context "when not passed a block" do + before :each do + @enum = [1, 2, 42, 100, 666].bsearch_index + end + + it "returns an Enumerator" do + @enum.should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator with unknown size" do + @enum.size.should be_nil + end + + it "returns index of element when block condition is satisfied" do + @enum.each { |x| x >= 33 }.should == 2 + end + end + + it "raises a TypeError when block returns a String" do + lambda { [1, 2, 3].bsearch_index { "not ok" } }.should raise_error(TypeError) + end + + it "returns nil when block is empty" do + [1, 2, 3].bsearch_index {}.should be_nil + end + + context "minimum mode" do + before :each do + @array = [0, 4, 7, 10, 12] + end + + it "returns index of first element which satisfies the block" do + @array.bsearch_index { |x| x >= 4 }.should == 1 + @array.bsearch_index { |x| x >= 6 }.should == 2 + @array.bsearch_index { |x| x >= -1 }.should == 0 + end + + it "returns nil when block condition is never satisfied" do + @array.bsearch_index { false }.should be_nil + @array.bsearch_index { |x| x >= 100 }.should be_nil + end + end + + context "find any mode" do + before :each do + @array = [0, 4, 7, 10, 12] + end + + it "returns the index of any matched elements where element is between 4 <= x < 8" do + [1, 2].should include(@array.bsearch_index { |x| 1 - x / 4 }) + end + + it "returns the index of any matched elements where element is between 8 <= x < 10" do + @array.bsearch_index { |x| 4 - x / 2 }.should be_nil + end + + it "returns nil when block never returns 0" do + @array.bsearch_index { |x| 1 }.should be_nil + @array.bsearch_index { |x| -1 }.should be_nil + end + + it "returns the middle element when block always returns zero" do + @array.bsearch_index { |x| 0 }.should == 2 + end + + context "magnitude does not effect the result" do + it "returns the index of any matched elements where element is between 4n <= xn < 8n" do + [1, 2].should include(@array.bsearch_index { |x| (1 - x / 4) * (2**100) }) + end + + it "returns nil when block never returns 0" do + @array.bsearch_index { |x| 1 * (2**100) }.should be_nil + @array.bsearch_index { |x| (-1) * (2**100) }.should be_nil + end + + it "handles values from Bignum#coerce" do + [1, 2].should include(@array.bsearch_index { |x| (2**100).coerce((1 - x / 4) * (2**100)).first }) + end + end + end + end +end diff --git a/spec/rubyspec/core/array/bsearch_spec.rb b/spec/rubyspec/core/array/bsearch_spec.rb new file mode 100644 index 0000000000..71e945f390 --- /dev/null +++ b/spec/rubyspec/core/array/bsearch_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Array#bsearch" do + it "returns an Enumerator when not passed a block" do + [1].bsearch.should be_an_instance_of(Enumerator) + end + + it_behaves_like :enumeratorized_with_unknown_size, :bsearch, [1,2,3] + + it "raises a TypeError if the block returns an Object" do + lambda { [1].bsearch { Object.new } }.should raise_error(TypeError) + end + + it "raises a TypeError if the block returns a String" do + lambda { [1].bsearch { "1" } }.should raise_error(TypeError) + end + + context "with a block returning true or false" do + it "returns nil if the block returns false for every element" do + [0, 1, 2, 3].bsearch { |x| x > 3 }.should be_nil + end + + it "returns nil if the block returns nil for every element" do + [0, 1, 2, 3].bsearch { |x| nil }.should be_nil + end + + it "returns element at zero if the block returns true for every element" do + [0, 1, 2, 3].bsearch { |x| x < 4 }.should == 0 + + end + + it "returns the element at the smallest index for which block returns true" do + [0, 1, 3, 4].bsearch { |x| x >= 2 }.should == 3 + [0, 1, 3, 4].bsearch { |x| x >= 1 }.should == 1 + end + end + + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns less than zero for every element" do + [0, 1, 2, 3].bsearch { |x| x <=> 5 }.should be_nil + end + + it "returns nil if the block returns greater than zero for every element" do + [0, 1, 2, 3].bsearch { |x| x <=> -1 }.should be_nil + + end + + it "returns nil if the block never returns zero" do + [0, 1, 3, 4].bsearch { |x| x <=> 2 }.should be_nil + end + + it "accepts (+/-)Float::INFINITY from the block" do + [0, 1, 3, 4].bsearch { |x| Float::INFINITY }.should be_nil + [0, 1, 3, 4].bsearch { |x| -Float::INFINITY }.should be_nil + end + + it "returns an element at an index for which block returns 0.0" do + result = [0, 1, 2, 3, 4].bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end + + it "returns an element at an index for which block returns 0" do + result = [0, 1, 2, 3, 4].bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + [1, 2].should include(result) + end + end + + context "with a block that calls break" do + it "returns nil if break is called without a value" do + ['a', 'b', 'c'].bsearch { |v| break }.should be_nil + end + + it "returns nil if break is called with a nil value" do + ['a', 'b', 'c'].bsearch { |v| break nil }.should be_nil + end + + it "returns object if break is called with an object" do + ['a', 'b', 'c'].bsearch { |v| break 1234 }.should == 1234 + ['a', 'b', 'c'].bsearch { |v| break 'hi' }.should == 'hi' + ['a', 'b', 'c'].bsearch { |v| break [42] }.should == [42] + end + end +end diff --git a/spec/rubyspec/core/array/clear_spec.rb b/spec/rubyspec/core/array/clear_spec.rb new file mode 100644 index 0000000000..851c90d654 --- /dev/null +++ b/spec/rubyspec/core/array/clear_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#clear" do + it "removes all elements" do + a = [1, 2, 3, 4] + a.clear.should equal(a) + a.should == [] + end + + it "returns self" do + a = [1] + oid = a.object_id + a.clear.object_id.should == oid + end + + it "leaves the Array empty" do + a = [1] + a.clear + a.empty?.should == true + a.size.should == 0 + end + + it "keeps tainted status" do + a = [1] + a.taint + a.tainted?.should be_true + a.clear + a.tainted?.should be_true + end + + it "does not accept any arguments" do + lambda { [1].clear(true) }.should raise_error(ArgumentError) + end + + it "keeps untrusted status" do + a = [1] + a.untrust + a.untrusted?.should be_true + a.clear + a.untrusted?.should be_true + end + + it "raises a RuntimeError on a frozen array" do + a = [1] + a.freeze + lambda { a.clear }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/clone_spec.rb b/spec/rubyspec/core/array/clone_spec.rb new file mode 100644 index 0000000000..c88e10337f --- /dev/null +++ b/spec/rubyspec/core/array/clone_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/clone', __FILE__) + +describe "Array#clone" do + it_behaves_like :array_clone, :clone + + it "copies frozen status from the original" do + a = [1, 2, 3, 4] + b = [1, 2, 3, 4] + a.freeze + aa = a.clone + bb = b.clone + + aa.frozen?.should == true + bb.frozen?.should == false + end + + it "copies singleton methods" do + a = [1, 2, 3, 4] + b = [1, 2, 3, 4] + def a.a_singleton_method; end + aa = a.clone + bb = b.clone + + a.respond_to?(:a_singleton_method).should be_true + b.respond_to?(:a_singleton_method).should be_false + aa.respond_to?(:a_singleton_method).should be_true + bb.respond_to?(:a_singleton_method).should be_false + end +end diff --git a/spec/rubyspec/core/array/collect_spec.rb b/spec/rubyspec/core/array/collect_spec.rb new file mode 100644 index 0000000000..1c2c28c6bd --- /dev/null +++ b/spec/rubyspec/core/array/collect_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Array#collect" do + it_behaves_like(:array_collect, :collect) +end + +describe "Array#collect!" do + it_behaves_like(:array_collect_b, :collect!) +end diff --git a/spec/rubyspec/core/array/combination_spec.rb b/spec/rubyspec/core/array/combination_spec.rb new file mode 100644 index 0000000000..7869783d1e --- /dev/null +++ b/spec/rubyspec/core/array/combination_spec.rb @@ -0,0 +1,74 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#combination" do + before :each do + @array = [1, 2, 3, 4] + end + + it "returns an enumerator when no block is provided" do + @array.combination(2).should be_an_instance_of(Enumerator) + end + + it "returns self when a block is given" do + @array.combination(2){}.should equal(@array) + end + + it "yields nothing for out of bounds length and return self" do + @array.combination(5).to_a.should == [] + @array.combination(-1).to_a.should == [] + end + + it "yields the expected combinations" do + @array.combination(3).to_a.sort.should == [[1,2,3],[1,2,4],[1,3,4],[2,3,4]] + end + + it "yields nothing if the argument is out of bounds" do + @array.combination(-1).to_a.should == [] + @array.combination(5).to_a.should == [] + end + + it "yields a copy of self if the argument is the size of the receiver" do + r = @array.combination(4).to_a + r.should == [@array] + r[0].should_not equal(@array) + end + + it "yields [] when length is 0" do + @array.combination(0).to_a.should == [[]] # one combination of length 0 + [].combination(0).to_a.should == [[]] # one combination of length 0 + end + + it "yields a partition consisting of only singletons" do + @array.combination(1).to_a.sort.should == [[1],[2],[3],[4]] + end + + it "generates from a defensive copy, ignoring mutations" do + accum = [] + @array.combination(2) do |x| + accum << x + @array[0] = 1 + end + accum.should == [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "returns 0 when the number of combinations is < 0" do + @array.combination(-1).size.should == 0 + [].combination(-2).size.should == 0 + end + it "returns the binomial coeficient between the array size the number of combinations" do + @array.combination(5).size.should == 0 + @array.combination(4).size.should == 1 + @array.combination(3).size.should == 4 + @array.combination(2).size.should == 6 + @array.combination(1).size.should == 4 + @array.combination(0).size.should == 1 + [].combination(0).size.should == 1 + [].combination(1).size.should == 0 + end + end + end + end +end diff --git a/spec/rubyspec/core/array/compact_spec.rb b/spec/rubyspec/core/array/compact_spec.rb new file mode 100644 index 0000000000..b80f0214ec --- /dev/null +++ b/spec/rubyspec/core/array/compact_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#compact" do + it "returns a copy of array with all nil elements removed" do + a = [1, 2, 4] + a.compact.should == [1, 2, 4] + a = [1, nil, 2, 4] + a.compact.should == [1, 2, 4] + a = [1, 2, 4, nil] + a.compact.should == [1, 2, 4] + a = [nil, 1, 2, 4] + a.compact.should == [1, 2, 4] + end + + it "does not return self" do + a = [1, 2, 3] + a.compact.should_not equal(a) + end + + it "does not return subclass instance for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3, nil].compact.should be_an_instance_of(Array) + end + + it "does not keep tainted status even if all elements are removed" do + a = [nil, nil] + a.taint + a.compact.tainted?.should be_false + end + + it "does not keep untrusted status even if all elements are removed" do + a = [nil, nil] + a.untrust + a.compact.untrusted?.should be_false + end +end + +describe "Array#compact!" do + it "removes all nil elements" do + a = ['a', nil, 'b', false, 'c'] + a.compact!.should equal(a) + a.should == ["a", "b", false, "c"] + a = [nil, 'a', 'b', false, 'c'] + a.compact!.should equal(a) + a.should == ["a", "b", false, "c"] + a = ['a', 'b', false, 'c', nil] + a.compact!.should equal(a) + a.should == ["a", "b", false, "c"] + end + + it "returns self if some nil elements are removed" do + a = ['a', nil, 'b', false, 'c'] + a.compact!.object_id.should == a.object_id + end + + it "returns nil if there are no nil elements to remove" do + [1, 2, false, 3].compact!.should == nil + end + + it "keeps tainted status even if all elements are removed" do + a = [nil, nil] + a.taint + a.compact! + a.tainted?.should be_true + end + + it "keeps untrusted status even if all elements are removed" do + a = [nil, nil] + a.untrust + a.compact! + a.untrusted?.should be_true + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.compact! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/comparison_spec.rb b/spec/rubyspec/core/array/comparison_spec.rb new file mode 100644 index 0000000000..e5a5f4da10 --- /dev/null +++ b/spec/rubyspec/core/array/comparison_spec.rb @@ -0,0 +1,97 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#<=>" do + it "calls <=> left to right and return first non-0 result" do + [-1, +1, nil, "foobar"].each do |result| + lhs = Array.new(3) { mock("#{result}") } + rhs = Array.new(3) { mock("#{result}") } + + lhs[0].should_receive(:<=>).with(rhs[0]).and_return(0) + lhs[1].should_receive(:<=>).with(rhs[1]).and_return(result) + lhs[2].should_not_receive(:<=>) + + (lhs <=> rhs).should == result + end + end + + it "returns 0 if the arrays are equal" do + ([] <=> []).should == 0 + ([1, 2, 3, 4, 5, 6] <=> [1, 2, 3, 4, 5.0, 6.0]).should == 0 + end + + it "returns -1 if the array is shorter than the other array" do + ([] <=> [1]).should == -1 + ([1, 1] <=> [1, 1, 1]).should == -1 + end + + it "returns +1 if the array is longer than the other array" do + ([1] <=> []).should == +1 + ([1, 1, 1] <=> [1, 1]).should == +1 + end + + it "returns -1 if the arrays have same length and a pair of corresponding elements returns -1 for <=>" do + eq_l = mock('an object equal to the other') + eq_r = mock('an object equal to the other') + eq_l.should_receive(:<=>).with(eq_r).any_number_of_times.and_return(0) + + less = mock('less than the other') + greater = mock('greater then the other') + less.should_receive(:<=>).with(greater).any_number_of_times.and_return(-1) + + rest = mock('an rest element of the arrays') + rest.should_receive(:<=>).with(rest).any_number_of_times.and_return(0) + lhs = [eq_l, eq_l, less, rest] + rhs = [eq_r, eq_r, greater, rest] + + (lhs <=> rhs).should == -1 + end + + it "returns +1 if the arrays have same length and a pair of corresponding elements returns +1 for <=>" do + eq_l = mock('an object equal to the other') + eq_r = mock('an object equal to the other') + eq_l.should_receive(:<=>).with(eq_r).any_number_of_times.and_return(0) + + greater = mock('greater then the other') + less = mock('less than the other') + greater.should_receive(:<=>).with(less).any_number_of_times.and_return(+1) + + rest = mock('an rest element of the arrays') + rest.should_receive(:<=>).with(rest).any_number_of_times.and_return(0) + lhs = [eq_l, eq_l, greater, rest] + rhs = [eq_r, eq_r, less, rest] + + (lhs <=> rhs).should == +1 + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + (empty <=> empty).should == 0 + (empty <=> []).should == 1 + ([] <=> empty).should == -1 + + (ArraySpecs.recursive_array <=> []).should == 1 + ([] <=> ArraySpecs.recursive_array).should == -1 + + (ArraySpecs.recursive_array <=> ArraySpecs.empty_recursive_array).should == nil + + array = ArraySpecs.recursive_array + (array <=> array).should == 0 + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('to_ary') + obj.stub!(:to_ary).and_return([1, 2, 3]) + ([4, 5] <=> obj).should == ([4, 5] <=> obj.to_ary) + end + + it "does not call #to_ary on Array subclasses" do + obj = ArraySpecs::ToAryArray[5, 6, 7] + obj.should_not_receive(:to_ary) + ([5, 6, 7] <=> obj).should == 0 + end + + it "returns nil when the argument is not array-like" do + ([] <=> false).should be_nil + end +end diff --git a/spec/rubyspec/core/array/concat_spec.rb b/spec/rubyspec/core/array/concat_spec.rb new file mode 100644 index 0000000000..86ec557bde --- /dev/null +++ b/spec/rubyspec/core/array/concat_spec.rb @@ -0,0 +1,132 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#concat" do + it "returns the array itself" do + ary = [1,2,3] + ary.concat([4,5,6]).equal?(ary).should be_true + end + + it "appends the elements in the other array" do + ary = [1, 2, 3] + ary.concat([9, 10, 11]).should equal(ary) + ary.should == [1, 2, 3, 9, 10, 11] + ary.concat([]) + ary.should == [1, 2, 3, 9, 10, 11] + end + + it "does not loop endlessly when argument is self" do + ary = ["x", "y"] + ary.concat(ary).should == ["x", "y", "x", "y"] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('to_ary') + obj.should_receive(:to_ary).and_return(["x", "y"]) + [4, 5, 6].concat(obj).should == [4, 5, 6, "x", "y"] + end + + it "does not call #to_ary on Array subclasses" do + obj = ArraySpecs::ToAryArray[5, 6, 7] + obj.should_not_receive(:to_ary) + [].concat(obj).should == [5, 6, 7] + end + + it "raises a RuntimeError when Array is frozen and modification occurs" do + lambda { ArraySpecs.frozen_array.concat [1] }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError when Array is frozen and no modification occurs" do + lambda { ArraySpecs.frozen_array.concat([]) }.should raise_error(RuntimeError) + end + + it "keeps tainted status" do + ary = [1, 2] + ary.taint + ary.concat([3]) + ary.tainted?.should be_true + ary.concat([]) + ary.tainted?.should be_true + end + + it "is not infected by the other" do + ary = [1,2] + other = [3]; other.taint + ary.tainted?.should be_false + ary.concat(other) + ary.tainted?.should be_false + end + + it "keeps the tainted status of elements" do + ary = [ Object.new, Object.new, Object.new ] + ary.each {|x| x.taint } + + ary.concat([ Object.new ]) + ary[0].tainted?.should be_true + ary[1].tainted?.should be_true + ary[2].tainted?.should be_true + ary[3].tainted?.should be_false + end + + it "keeps untrusted status" do + ary = [1, 2] + ary.untrust + ary.concat([3]) + ary.untrusted?.should be_true + ary.concat([]) + ary.untrusted?.should be_true + end + + it "is not infected untrustedness by the other" do + ary = [1,2] + other = [3]; other.untrust + ary.untrusted?.should be_false + ary.concat(other) + ary.untrusted?.should be_false + end + + it "keeps the untrusted status of elements" do + ary = [ Object.new, Object.new, Object.new ] + ary.each {|x| x.untrust } + + ary.concat([ Object.new ]) + ary[0].untrusted?.should be_true + ary[1].untrusted?.should be_true + ary[2].untrusted?.should be_true + ary[3].untrusted?.should be_false + end + + it "appends elements to an Array with enough capacity that has been shifted" do + ary = [1, 2, 3, 4, 5] + 2.times { ary.shift } + 2.times { ary.pop } + ary.concat([5, 6]).should == [3, 5, 6] + end + + it "appends elements to an Array without enough capacity that has been shifted" do + ary = [1, 2, 3, 4] + 3.times { ary.shift } + ary.concat([5, 6]).should == [4, 5, 6] + end + + ruby_version_is "2.4" do + it "takes multiple arguments" do + ary = [1, 2] + ary.concat [3, 4] + ary.should == [1, 2, 3, 4] + end + + it "concatenates the initial value when given arguments contain 2 self" do + ary = [1, 2] + ary.concat ary, ary + ary.should == [1, 2, 1, 2, 1, 2] + end + + it "returns self when given no arguments" do + ary = [1, 2] + ary.concat.should equal(ary) + ary.should == [1, 2] + end + end +end diff --git a/spec/rubyspec/core/array/constructor_spec.rb b/spec/rubyspec/core/array/constructor_spec.rb new file mode 100644 index 0000000000..8ec2e5de1e --- /dev/null +++ b/spec/rubyspec/core/array/constructor_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array.[]" do + it "returns a new array populated with the given elements" do + obj = Object.new + Array.[](5, true, nil, 'a', "Ruby", obj).should == [5, true, nil, "a", "Ruby", obj] + + a = ArraySpecs::MyArray.[](5, true, nil, 'a', "Ruby", obj) + a.should be_an_instance_of(ArraySpecs::MyArray) + a.inspect.should == [5, true, nil, "a", "Ruby", obj].inspect + end +end + +describe "Array[]" do + it "is a synonym for .[]" do + obj = Object.new + Array[5, true, nil, 'a', "Ruby", obj].should == Array.[](5, true, nil, "a", "Ruby", obj) + + a = ArraySpecs::MyArray[5, true, nil, 'a', "Ruby", obj] + a.should be_an_instance_of(ArraySpecs::MyArray) + a.inspect.should == [5, true, nil, "a", "Ruby", obj].inspect + end +end diff --git a/spec/rubyspec/core/array/count_spec.rb b/spec/rubyspec/core/array/count_spec.rb new file mode 100644 index 0000000000..52314d8579 --- /dev/null +++ b/spec/rubyspec/core/array/count_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#count" do + it "returns the number of elements" do + [:a, :b, :c].count.should == 3 + end + + it "returns the number of elements that equal the argument" do + [:a, :b, :b, :c].count(:b).should == 2 + end + + it "returns the number of element for which the block evaluates to true" do + [:a, :b, :c].count { |s| s != :b }.should == 2 + end +end diff --git a/spec/rubyspec/core/array/cycle_spec.rb b/spec/rubyspec/core/array/cycle_spec.rb new file mode 100644 index 0000000000..2e60798c8c --- /dev/null +++ b/spec/rubyspec/core/array/cycle_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Array#cycle" do + before :each do + ScratchPad.record [] + + @array = [1, 2, 3] + @prc = lambda { |x| ScratchPad << x } + end + + it "does not yield and returns nil when the array is empty and passed value is an integer" do + [].cycle(6, &@prc).should be_nil + ScratchPad.recorded.should == [] + end + + it "does not yield and returns nil when the array is empty and passed value is nil" do + [].cycle(nil, &@prc).should be_nil + ScratchPad.recorded.should == [] + end + + it "does not yield and returns nil when passed 0" do + @array.cycle(0, &@prc).should be_nil + ScratchPad.recorded.should == [] + end + + it "iterates the array 'count' times yielding each item to the block" do + @array.cycle(2, &@prc) + ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3] + end + + it "iterates indefinitely when not passed a count" do + @array.cycle do |x| + ScratchPad << x + break if ScratchPad.recorded.size > 7 + end + ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3, 1, 2] + end + + it "iterates indefinitely when passed nil" do + @array.cycle(nil) do |x| + ScratchPad << x + break if ScratchPad.recorded.size > 7 + end + ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3, 1, 2] + end + + it "does not rescue StopIteration when not passed a count" do + lambda do + @array.cycle { raise StopIteration } + end.should raise_error(StopIteration) + end + + it "does not rescue StopIteration when passed a count" do + lambda do + @array.cycle(3) { raise StopIteration } + end.should raise_error(StopIteration) + end + + it "iterates the array Integer(count) times when passed a Float count" do + @array.cycle(2.7, &@prc) + ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3] + end + + it "calls #to_int to convert count to an Integer" do + count = mock("cycle count 2") + count.should_receive(:to_int).and_return(2) + + @array.cycle(count, &@prc) + ScratchPad.recorded.should == [1, 2, 3, 1, 2, 3] + end + + it "raises a TypeError if #to_int does not return an Integer" do + count = mock("cycle count 2") + count.should_receive(:to_int).and_return("2") + + lambda { @array.cycle(count, &@prc) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a String" do + lambda { @array.cycle("4") { } }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an Object" do + lambda { @array.cycle(mock("cycle count")) { } }.should raise_error(TypeError) + end + + it "raises a TypeError if passed true" do + lambda { @array.cycle(true) { } }.should raise_error(TypeError) + end + + it "raises a TypeError if passed false" do + lambda { @array.cycle(false) { } }.should raise_error(TypeError) + end + + before :all do + @object = [1, 2, 3, 4] + @empty_object = [] + end + it_should_behave_like :enumeratorized_with_cycle_size +end diff --git a/spec/rubyspec/core/array/delete_at_spec.rb b/spec/rubyspec/core/array/delete_at_spec.rb new file mode 100644 index 0000000000..1d73ceb33a --- /dev/null +++ b/spec/rubyspec/core/array/delete_at_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#delete_at" do + it "removes the element at the specified index" do + a = [1, 2, 3, 4] + a.delete_at(2) + a.should == [1, 2, 4] + a.delete_at(-1) + a.should == [1, 2] + end + + it "returns the removed element at the specified index" do + a = [1, 2, 3, 4] + a.delete_at(2).should == 3 + a.delete_at(-1).should == 4 + end + + it "returns nil and makes no modification if the index is out of range" do + a = [1, 2] + a.delete_at(3).should == nil + a.should == [1, 2] + a.delete_at(-3).should == nil + a.should == [1, 2] + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(-1) + [1, 2].delete_at(obj).should == 2 + end + + it "accepts negative indices" do + a = [1, 2] + a.delete_at(-2).should == 1 + end + + it "raises a RuntimeError on a frozen array" do + lambda { [1,2,3].freeze.delete_at(0) }.should raise_error(RuntimeError) + end + + it "keeps tainted status" do + ary = [1, 2] + ary.taint + ary.tainted?.should be_true + ary.delete_at(0) + ary.tainted?.should be_true + ary.delete_at(0) # now empty + ary.tainted?.should be_true + end + + it "keeps untrusted status" do + ary = [1, 2] + ary.untrust + ary.untrusted?.should be_true + ary.delete_at(0) + ary.untrusted?.should be_true + ary.delete_at(0) # now empty + ary.untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/array/delete_if_spec.rb b/spec/rubyspec/core/array/delete_if_spec.rb new file mode 100644 index 0000000000..4276a1fb65 --- /dev/null +++ b/spec/rubyspec/core/array/delete_if_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorize', __FILE__) +require File.expand_path('../shared/delete_if', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Array#delete_if" do + before do + @a = [ "a", "b", "c" ] + end + + it "removes each element for which block returns true" do + @a = [ "a", "b", "c" ] + @a.delete_if { |x| x >= "b" } + @a.should == ["a"] + end + + it "returns self" do + @a.delete_if{ true }.equal?(@a).should be_true + end + + it_behaves_like :enumeratorize, :delete_if + + it "returns self when called on an Array emptied with #shift" do + array = [1] + array.shift + array.delete_if { |x| true }.should equal(array) + end + + it "returns an Enumerator if no block given, and the enumerator can modify the original array" do + enum = @a.delete_if + enum.should be_an_instance_of(Enumerator) + @a.should_not be_empty + enum.each { true } + @a.should be_empty + end + + it "returns an Enumerator if no block given, and the array is frozen" do + @a.freeze.delete_if.should be_an_instance_of(Enumerator) + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.delete_if {} }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError on an empty frozen array" do + lambda { ArraySpecs.empty_frozen_array.delete_if {} }.should raise_error(RuntimeError) + end + + it "keeps tainted status" do + @a.taint + @a.tainted?.should be_true + @a.delete_if{ true } + @a.tainted?.should be_true + end + + it "keeps untrusted status" do + @a.untrust + @a.untrusted?.should be_true + @a.delete_if{ true } + @a.untrusted?.should be_true + end + + it_behaves_like :enumeratorized_with_origin_size, :delete_if, [1,2,3] + it_behaves_like :delete_if, :delete_if +end diff --git a/spec/rubyspec/core/array/delete_spec.rb b/spec/rubyspec/core/array/delete_spec.rb new file mode 100644 index 0000000000..7b6bf3930c --- /dev/null +++ b/spec/rubyspec/core/array/delete_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#delete" do + it "removes elements that are #== to object" do + x = mock('delete') + def x.==(other) 3 == other end + + a = [1, 2, 3, x, 4, 3, 5, x] + a.delete mock('not contained') + a.should == [1, 2, 3, x, 4, 3, 5, x] + + a.delete 3 + a.should == [1, 2, 4, 5] + end + + it "calculates equality correctly for reference values" do + a = ["foo", "bar", "foo", "quux", "foo"] + a.delete "foo" + a.should == ["bar","quux"] + end + + it "returns object or nil if no elements match object" do + [1, 2, 4, 5].delete(1).should == 1 + [1, 2, 4, 5].delete(3).should == nil + end + + it "may be given a block that is executed if no element matches object" do + [1].delete(1) {:not_found}.should == 1 + [].delete('a') {:not_found}.should == :not_found + end + + it "returns nil if the array is empty due to a shift" do + a = [1] + a.shift + a.delete(nil).should == nil + end + + it "returns nil on a frozen array if a modification does not take place" do + [1, 2, 3].freeze.delete(0).should == nil + end + + it "raises a RuntimeError on a frozen array" do + lambda { [1, 2, 3].freeze.delete(1) }.should raise_error(RuntimeError) + end + + it "keeps tainted status" do + a = [1, 2] + a.taint + a.tainted?.should be_true + a.delete(2) + a.tainted?.should be_true + a.delete(1) # now empty + a.tainted?.should be_true + end + + it "keeps untrusted status" do + a = [1, 2] + a.untrust + a.untrusted?.should be_true + a.delete(2) + a.untrusted?.should be_true + a.delete(1) # now empty + a.untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/array/dig_spec.rb b/spec/rubyspec/core/array/dig_spec.rb new file mode 100644 index 0000000000..9c20b2d160 --- /dev/null +++ b/spec/rubyspec/core/array/dig_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.3' do + describe "Array#dig" do + + it "returns #at with one arg" do + ['a'].dig(0).should == 'a' + ['a'].dig(1).should be_nil + end + + it "recurses array elements" do + a = [ [ 1, [2, '3'] ] ] + a.dig(0, 0).should == 1 + a.dig(0, 1, 1).should == '3' + a.dig(0, -1, 0).should == 2 + end + + it "returns the nested value specified if the sequence includes a key" do + a = [42, { foo: :bar }] + a.dig(1, :foo).should == :bar + end + + it "raises a TypeError for a non-numeric index" do + lambda { + ['a'].dig(:first) + }.should raise_error(TypeError) + end + + it "raises a TypeError if any intermediate step does not respond to #dig" do + a = [1, 2] + lambda { + a.dig(0, 1) + }.should raise_error(TypeError) + end + + it "raises an ArgumentError if no arguments provided" do + lambda { + [10].dig() + }.should raise_error(ArgumentError) + end + + it "returns nil if any intermediate step is nil" do + a = [[1, [2, 3]]] + a.dig(1, 2, 3).should == nil + end + + it "calls #dig on the result of #at with the remaining arguments" do + h = [[nil, [nil, nil, 42]]] + h[0].should_receive(:dig).with(1, 2).and_return(42) + h.dig(0, 1, 2).should == 42 + end + + end +end diff --git a/spec/rubyspec/core/array/drop_spec.rb b/spec/rubyspec/core/array/drop_spec.rb new file mode 100644 index 0000000000..763b45e05a --- /dev/null +++ b/spec/rubyspec/core/array/drop_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#drop" do + it "removes the specified number of elements from the start of the array" do + [1, 2, 3, 4, 5].drop(2).should == [3, 4, 5] + end + + it "raises an ArgumentError if the number of elements specified is negative" do + lambda { [1, 2].drop(-3) }.should raise_error(ArgumentError) + end + + it "returns an empty Array if all elements are dropped" do + [1, 2].drop(2).should == [] + end + + it "returns an empty Array when called on an empty Array" do + [].drop(0).should == [] + end + + it "does not remove any elements when passed zero" do + [1, 2].drop(0).should == [1, 2] + end + + it "returns an empty Array if more elements than exist are dropped" do + [1, 2].drop(3).should == [] + end + + it 'acts correctly after a shift' do + ary = [nil, 1, 2] + ary.shift + ary.drop(1).should == [2] + end +end diff --git a/spec/rubyspec/core/array/drop_while_spec.rb b/spec/rubyspec/core/array/drop_while_spec.rb new file mode 100644 index 0000000000..40cc29d6b3 --- /dev/null +++ b/spec/rubyspec/core/array/drop_while_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#drop_while" do + it "removes elements from the start of the array while the block evaluates to true" do + [1, 2, 3, 4].drop_while { |n| n < 4 }.should == [4] + end + + it "removes elements from the start of the array until the block returns nil" do + [1, 2, 3, nil, 5].drop_while { |n| n }.should == [nil, 5] + end + + it "removes elements from the start of the array until the block returns false" do + [1, 2, 3, false, 5].drop_while { |n| n }.should == [false, 5] + end +end diff --git a/spec/rubyspec/core/array/dup_spec.rb b/spec/rubyspec/core/array/dup_spec.rb new file mode 100644 index 0000000000..01ad12523d --- /dev/null +++ b/spec/rubyspec/core/array/dup_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/clone', __FILE__) + +describe "Array#dup" do + it_behaves_like :array_clone, :dup # FIX: no, clone and dup are not alike + + it "does not copy frozen status from the original" do + a = [1, 2, 3, 4] + b = [1, 2, 3, 4] + a.freeze + aa = a.dup + bb = b.dup + + aa.frozen?.should be_false + bb.frozen?.should be_false + end + + it "does not copy singleton methods" do + a = [1, 2, 3, 4] + b = [1, 2, 3, 4] + def a.a_singleton_method; end + aa = a.dup + bb = b.dup + + a.respond_to?(:a_singleton_method).should be_true + b.respond_to?(:a_singleton_method).should be_false + aa.respond_to?(:a_singleton_method).should be_false + bb.respond_to?(:a_singleton_method).should be_false + end +end diff --git a/spec/rubyspec/core/array/each_index_spec.rb b/spec/rubyspec/core/array/each_index_spec.rb new file mode 100644 index 0000000000..8872c00f8c --- /dev/null +++ b/spec/rubyspec/core/array/each_index_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorize', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +# Modifying a collection while the contents are being iterated +# gives undefined behavior. See +# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 + +describe "Array#each_index" do + before :each do + ScratchPad.record [] + end + + it "passes the index of each element to the block" do + a = ['a', 'b', 'c', 'd'] + a.each_index { |i| ScratchPad << i } + ScratchPad.recorded.should == [0, 1, 2, 3] + end + + it "returns self" do + a = [:a, :b, :c] + a.each_index { |i| }.should equal(a) + end + + it "is not confused by removing elements from the front" do + a = [1, 2, 3] + + a.shift + ScratchPad.record [] + a.each_index { |i| ScratchPad << i } + ScratchPad.recorded.should == [0, 1] + + a.shift + ScratchPad.record [] + a.each_index { |i| ScratchPad << i } + ScratchPad.recorded.should == [0] + end + + it_behaves_like :enumeratorize, :each_index + it_behaves_like :enumeratorized_with_origin_size, :each_index, [1,2,3] +end diff --git a/spec/rubyspec/core/array/each_spec.rb b/spec/rubyspec/core/array/each_spec.rb new file mode 100644 index 0000000000..a8bac6442e --- /dev/null +++ b/spec/rubyspec/core/array/each_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorize', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +# Modifying a collection while the contents are being iterated +# gives undefined behavior. See +# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 + +describe "Array#each" do + it "yields each element to the block" do + a = [] + x = [1, 2, 3] + x.each { |item| a << item }.should equal(x) + a.should == [1, 2, 3] + end + + it "yields each element to a block that takes multiple arguments" do + a = [[1, 2], :a, [3, 4]] + b = [] + + a.each { |x, y| b << x } + b.should == [1, :a, 3] + + b = [] + a.each { |x, y| b << y } + b.should == [2, nil, 4] + end + + it_behaves_like :enumeratorize, :each + it_behaves_like :enumeratorized_with_origin_size, :each, [1,2,3] +end diff --git a/spec/rubyspec/core/array/element_reference_spec.rb b/spec/rubyspec/core/array/element_reference_spec.rb new file mode 100644 index 0000000000..55b6b73d1e --- /dev/null +++ b/spec/rubyspec/core/array/element_reference_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/slice', __FILE__) + +describe "Array#[]" do + it_behaves_like(:array_slice, :[]) +end + +describe "Array.[]" do + it "[] should return a new array populated with the given elements" do + array = Array[1, 'a', nil] + array[0].should == 1 + array[1].should == 'a' + array[2].should == nil + end + + it "when applied to a literal nested array, unpacks its elements into the containing array" do + Array[1, 2, *[3, 4, 5]].should == [1, 2, 3, 4, 5] + end + + it "when applied to a nested referenced array, unpacks its elements into the containing array" do + splatted_array = Array[3, 4, 5] + Array[1, 2, *splatted_array].should == [1, 2, 3, 4, 5] + end + + it "can unpack 2 or more nested referenced array" do + splatted_array = Array[3, 4, 5] + splatted_array2 = Array[6, 7, 8] + Array[1, 2, *splatted_array, *splatted_array2].should == [1, 2, 3, 4, 5, 6, 7, 8] + end + + it "constructs a nested Hash for tailing key-value pairs" do + Array[1, 2, 3 => 4, 5 => 6].should == [1, 2, { 3 => 4, 5 => 6 }] + end + + describe "with a subclass of Array" do + before :each do + ScratchPad.clear + end + + it "returns an instance of the subclass" do + ArraySpecs::MyArray[1, 2, 3].should be_an_instance_of(ArraySpecs::MyArray) + end + + it "does not call #initialize on the subclass instance" do + ArraySpecs::MyArray[1, 2, 3].should == [1, 2, 3] + ScratchPad.recorded.should be_nil + end + end +end diff --git a/spec/rubyspec/core/array/element_set_spec.rb b/spec/rubyspec/core/array/element_set_spec.rb new file mode 100644 index 0000000000..280b507fc4 --- /dev/null +++ b/spec/rubyspec/core/array/element_set_spec.rb @@ -0,0 +1,418 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#[]=" do + it "sets the value of the element at index" do + a = [1, 2, 3, 4] + a[2] = 5 + a[-1] = 6 + a[5] = 3 + a.should == [1, 2, 5, 6, nil, 3] + + a = [] + a[4] = "e" + a.should == [nil, nil, nil, nil, "e"] + a[3] = "d" + a.should == [nil, nil, nil, "d", "e"] + a[0] = "a" + a.should == ["a", nil, nil, "d", "e"] + a[-3] = "C" + a.should == ["a", nil, "C", "d", "e"] + a[-1] = "E" + a.should == ["a", nil, "C", "d", "E"] + a[-5] = "A" + a.should == ["A", nil, "C", "d", "E"] + a[5] = "f" + a.should == ["A", nil, "C", "d", "E", "f"] + a[1] = [] + a.should == ["A", [], "C", "d", "E", "f"] + a[-1] = nil + a.should == ["A", [], "C", "d", "E", nil] + end + + it "sets the section defined by [start,length] to other" do + a = [1, 2, 3, 4, 5, 6] + a[0, 1] = 2 + a[3, 2] = ['a', 'b', 'c', 'd'] + a.should == [2, 2, 3, "a", "b", "c", "d", 6] + end + it "replaces the section defined by [start,length] with the given values" do + a = [1, 2, 3, 4, 5, 6] + a[3, 2] = 'a', 'b', 'c', 'd' + a.should == [1, 2, 3, "a", "b", "c", "d", 6] + end + + it "just sets the section defined by [start,length] to other even if other is nil" do + a = ['a', 'b', 'c', 'd', 'e'] + a[1, 3] = nil + a.should == ["a", nil, "e"] + end + + it "returns nil if the rhs is nil" do + a = [1, 2, 3] + (a[1, 3] = nil).should == nil + (a[1..3] = nil).should == nil + end + + it "sets the section defined by range to other" do + a = [6, 5, 4, 3, 2, 1] + a[1...2] = 9 + a[3..6] = [6, 6, 6] + a.should == [6, 9, 4, 6, 6, 6] + end + + it "replaces the section defined by range with the given values" do + a = [6, 5, 4, 3, 2, 1] + a[3..6] = :a, :b, :c + a.should == [6, 5, 4, :a, :b, :c] + end + + it "just sets the section defined by range to other even if other is nil" do + a = [1, 2, 3, 4, 5] + a[0..1] = nil + a.should == [nil, 3, 4, 5] + end + + it 'expands and nil-pads the array if section assigned by range is outside array boundaries' do + a = ['a'] + a[3..4] = ['b', 'c'] + a.should == ['a', nil, nil, 'b', 'c'] + end + + it "calls to_int on its start and length arguments" do + obj = mock('to_int') + obj.stub!(:to_int).and_return(2) + + a = [1, 2, 3, 4] + a[obj, 0] = [9] + a.should == [1, 2, 9, 3, 4] + a[obj, obj] = [] + a.should == [1, 2, 4] + a[obj] = -1 + a.should == [1, 2, -1] + end + + it "checks frozen before attempting to coerce arguments" do + a = [1,2,3,4].freeze + lambda {a[:foo] = 1}.should raise_error(RuntimeError) + lambda {a[:foo, :bar] = 1}.should raise_error(RuntimeError) + end + + it "sets elements in the range arguments when passed ranges" do + ary = [1, 2, 3] + rhs = [nil, [], ["x"], ["x", "y"]] + (0 .. ary.size + 2).each do |a| + (a .. ary.size + 3).each do |b| + rhs.each do |c| + ary1 = ary.dup + ary1[a .. b] = c + ary2 = ary.dup + ary2[a, 1 + b-a] = c + ary1.should == ary2 + + ary1 = ary.dup + ary1[a ... b] = c + ary2 = ary.dup + ary2[a, b-a] = c + ary1.should == ary2 + end + end + end + end + + it "inserts the given elements with [range] which the range is zero-width" do + ary = [1, 2, 3] + ary[1...1] = 0 + ary.should == [1, 0, 2, 3] + ary[1...1] = [5] + ary.should == [1, 5, 0, 2, 3] + ary[1...1] = :a, :b, :c + ary.should == [1, :a, :b, :c, 5, 0, 2, 3] + end + + it "inserts the given elements with [start, length] which length is zero" do + ary = [1, 2, 3] + ary[1, 0] = 0 + ary.should == [1, 0, 2, 3] + ary[1, 0] = [5] + ary.should == [1, 5, 0, 2, 3] + ary[1, 0] = :a, :b, :c + ary.should == [1, :a, :b, :c, 5, 0, 2, 3] + end + + # Now we only have to test cases where the start, length interface would + # have raise an exception because of negative size + it "inserts the given elements with [range] which the range has negative width" do + ary = [1, 2, 3] + ary[1..0] = 0 + ary.should == [1, 0, 2, 3] + ary[1..0] = [4, 3] + ary.should == [1, 4, 3, 0, 2, 3] + ary[1..0] = :a, :b, :c + ary.should == [1, :a, :b, :c, 4, 3, 0, 2, 3] + end + + it "just inserts nil if the section defined by range is zero-width and the rhs is nil" do + ary = [1, 2, 3] + ary[1...1] = nil + ary.should == [1, nil, 2, 3] + end + + it "just inserts nil if the section defined by range has negative width and the rhs is nil" do + ary = [1, 2, 3] + ary[1..0] = nil + ary.should == [1, nil, 2, 3] + end + + it "does nothing if the section defined by range is zero-width and the rhs is an empty array" do + ary = [1, 2, 3] + ary[1...1] = [] + ary.should == [1, 2, 3] + end + it "does nothing if the section defined by range has negative width and the rhs is an empty array" do + ary = [1, 2, 3, 4, 5] + ary[1...0] = [] + ary.should == [1, 2, 3, 4, 5] + ary[-2..2] = [] + ary.should == [1, 2, 3, 4, 5] + end + + it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + a = [1, 2, 3, 4] + + a[from .. to] = ["a", "b", "c"] + a.should == [1, "a", "b", "c", 4] + + a[to .. from] = ["x"] + a.should == [1, "a", "b", "x", "c", 4] + lambda { a["a" .. "b"] = [] }.should raise_error(TypeError) + lambda { a[from .. "b"] = [] }.should raise_error(TypeError) + end + + it "raises an IndexError when passed indexes out of bounds" do + a = [1, 2, 3, 4] + lambda { a[-5] = "" }.should raise_error(IndexError) + lambda { a[-5, -1] = "" }.should raise_error(IndexError) + lambda { a[-5, 0] = "" }.should raise_error(IndexError) + lambda { a[-5, 1] = "" }.should raise_error(IndexError) + lambda { a[-5, 2] = "" }.should raise_error(IndexError) + lambda { a[-5, 10] = "" }.should raise_error(IndexError) + + lambda { a[-5..-5] = "" }.should raise_error(RangeError) + lambda { a[-5...-5] = "" }.should raise_error(RangeError) + lambda { a[-5..-4] = "" }.should raise_error(RangeError) + lambda { a[-5...-4] = "" }.should raise_error(RangeError) + lambda { a[-5..10] = "" }.should raise_error(RangeError) + lambda { a[-5...10] = "" }.should raise_error(RangeError) + + # ok + a[0..-9] = [1] + a.should == [1, 1, 2, 3, 4] + end + + it "calls to_ary on its rhs argument for multi-element sets" do + obj = mock('to_ary') + def obj.to_ary() [1, 2, 3] end + ary = [1, 2] + ary[0, 0] = obj + ary.should == [1, 2, 3, 1, 2] + ary[1, 10] = obj + ary.should == [1, 1, 2, 3] + end + + it "does not call to_ary on rhs array subclasses for multi-element sets" do + ary = [] + ary[0, 0] = ArraySpecs::ToAryArray[5, 6, 7] + ary.should == [5, 6, 7] + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array[0, 0] = [] }.should raise_error(RuntimeError) + end +end + +describe "Array#[]= with [index]" do + it "returns value assigned if idx is inside array" do + a = [1, 2, 3, 4, 5] + (a[3] = 6).should == 6 + end + + it "returns value assigned if idx is right beyond right array boundary" do + a = [1, 2, 3, 4, 5] + (a[5] = 6).should == 6 + end + + it "returns value assigned if idx far beyond right array boundary" do + a = [1, 2, 3, 4, 5] + (a[10] = 6).should == 6 + end + + it "sets the value of the element at index" do + a = [1, 2, 3, 4] + a[2] = 5 + a[-1] = 6 + a[5] = 3 + a.should == [1, 2, 5, 6, nil, 3] + end + + it "sets the value of the element if it is right beyond the array boundary" do + a = [1, 2, 3, 4] + a[4] = 8 + a.should == [1, 2, 3, 4, 8] + end + +end + +describe "Array#[]= with [index, count]" do + it "returns non-array value if non-array value assigned" do + a = [1, 2, 3, 4, 5] + (a[2, 3] = 10).should == 10 + end + + it "returns array if array assigned" do + a = [1, 2, 3, 4, 5] + (a[2, 3] = [4, 5]).should == [4, 5] + end + + it "just sets the section defined by [start,length] to nil even if the rhs is nil" do + a = ['a', 'b', 'c', 'd', 'e'] + a[1, 3] = nil + a.should == ["a", nil, "e"] + end + + it "just sets the section defined by [start,length] to nil if negative index within bounds, cnt > 0 and the rhs is nil" do + a = ['a', 'b', 'c', 'd', 'e'] + a[-3, 2] = nil + a.should == ["a", "b", nil, "e"] + end + + it "replaces the section defined by [start,length] to other" do + a = [1, 2, 3, 4, 5, 6] + a[0, 1] = 2 + a[3, 2] = ['a', 'b', 'c', 'd'] + a.should == [2, 2, 3, "a", "b", "c", "d", 6] + end + + it "replaces the section to other if idx < 0 and cnt > 0" do + a = [1, 2, 3, 4, 5, 6] + a[-3, 2] = ["x", "y", "z"] + a.should == [1, 2, 3, "x", "y", "z", 6] + end + + it "replaces the section to other even if cnt spanning beyond the array boundary" do + a = [1, 2, 3, 4, 5] + a[-1, 3] = [7, 8] + a.should == [1, 2, 3, 4, 7, 8] + end + + it "pads the Array with nils if the span is past the end" do + a = [1, 2, 3, 4, 5] + a[10, 1] = [1] + a.should == [1, 2, 3, 4, 5, nil, nil, nil, nil, nil, 1] + + b = [1, 2, 3, 4, 5] + b[10, 0] = [1] + a.should == [1, 2, 3, 4, 5, nil, nil, nil, nil, nil, 1] + end + + it "inserts other section in place defined by idx" do + a = [1, 2, 3, 4, 5] + a[3, 0] = [7, 8] + a.should == [1, 2, 3, 7, 8, 4, 5] + + b = [1, 2, 3, 4, 5] + b[1, 0] = b + b.should == [1, 1, 2, 3, 4, 5, 2, 3, 4, 5] + end + + it "raises an IndexError when passed start and negative length" do + a = [1, 2, 3, 4] + lambda { a[-2, -1] = "" }.should raise_error(IndexError) + lambda { a[0, -1] = "" }.should raise_error(IndexError) + lambda { a[2, -1] = "" }.should raise_error(IndexError) + lambda { a[4, -1] = "" }.should raise_error(IndexError) + lambda { a[10, -1] = "" }.should raise_error(IndexError) + lambda { [1, 2, 3, 4, 5][2, -1] = [7, 8] }.should raise_error(IndexError) + end +end + +describe "Array#[]= with [m..n]" do + it "returns non-array value if non-array value assigned" do + a = [1, 2, 3, 4, 5] + (a[2..4] = 10).should == 10 + end + + it "returns array if array assigned" do + a = [1, 2, 3, 4, 5] + (a[2..4] = [7, 8]).should == [7, 8] + end + + it "just sets the section defined by range to nil even if the rhs is nil" do + a = [1, 2, 3, 4, 5] + a[0..1] = nil + a.should == [nil, 3, 4, 5] + end + + it "just sets the section defined by range to nil if m and n < 0 and the rhs is nil" do + a = [1, 2, 3, 4, 5] + a[-3..-2] = nil + a.should == [1, 2, nil, 5] + end + + it "replaces the section defined by range" do + a = [6, 5, 4, 3, 2, 1] + a[1...2] = 9 + a[3..6] = [6, 6, 6] + a.should == [6, 9, 4, 6, 6, 6] + end + + it "replaces the section if m and n < 0" do + a = [1, 2, 3, 4, 5] + a[-3..-2] = [7, 8, 9] + a.should == [1, 2, 7, 8, 9, 5] + end + + it "replaces the section if m < 0 and n > 0" do + a = [1, 2, 3, 4, 5] + a[-4..3] = [8] + a.should == [1, 8, 5] + end + + it "inserts the other section at m if m > n" do + a = [1, 2, 3, 4, 5] + a[3..1] = [8] + a.should == [1, 2, 3, 8, 4, 5] + end + + it "accepts Range subclasses" do + a = [1, 2, 3, 4] + range_incl = ArraySpecs::MyRange.new(1, 2) + range_excl = ArraySpecs::MyRange.new(-3, -1, true) + + a[range_incl] = ["a", "b"] + a.should == [1, "a", "b", 4] + a[range_excl] = ["A", "B"] + a.should == [1, "A", "B", 4] + end +end + +describe "Array#[] after a shift" do + it "works for insertion" do + a = [1,2] + a.shift + a.shift + a[0,0] = [3,4] + a.should == [3,4] + end +end + diff --git a/spec/rubyspec/core/array/empty_spec.rb b/spec/rubyspec/core/array/empty_spec.rb new file mode 100644 index 0000000000..d6235114b7 --- /dev/null +++ b/spec/rubyspec/core/array/empty_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#empty?" do + it "returns true if the array has no elements" do + [].empty?.should == true + [1].empty?.should == false + [1, 2].empty?.should == false + end +end diff --git a/spec/rubyspec/core/array/eql_spec.rb b/spec/rubyspec/core/array/eql_spec.rb new file mode 100644 index 0000000000..7621316e07 --- /dev/null +++ b/spec/rubyspec/core/array/eql_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "Array#eql?" do + it_behaves_like :array_eql, :eql? + + it "returns false if any corresponding elements are not #eql?" do + [1, 2, 3, 4].send(@method, [1, 2, 3, 4.0]).should be_false + end + + it "returns false if other is not a kind of Array" do + obj = mock("array eql?") + obj.should_not_receive(:to_ary) + obj.should_not_receive(@method) + + [1, 2, 3].send(@method, obj).should be_false + end +end diff --git a/spec/rubyspec/core/array/equal_value_spec.rb b/spec/rubyspec/core/array/equal_value_spec.rb new file mode 100644 index 0000000000..d923d0e503 --- /dev/null +++ b/spec/rubyspec/core/array/equal_value_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "Array#==" do + it_behaves_like :array_eql, :== + + it "compares with an equivalent Array-like object using #to_ary" do + obj = mock('array-like') + obj.should_receive(:respond_to?).at_least(1).with(:to_ary).and_return(true) + obj.should_receive(:==).with([1]).at_least(1).and_return(true) + + ([1] == obj).should be_true + ([[1]] == [obj]).should be_true + ([[[1], 3], 2] == [[obj, 3], 2]).should be_true + + # recursive arrays + arr1 = [[1]] + arr1 << arr1 + arr2 = [obj] + arr2 << arr2 + (arr1 == arr2).should be_true + (arr2 == arr1).should be_true + end + + it "returns false if any corresponding elements are not #==" do + a = ["a", "b", "c"] + b = ["a", "b", "not equal value"] + a.should_not == b + + c = mock("c") + c.should_receive(:==).and_return(false) + ["a", "b", c].should_not == a + end + + it "returns true if corresponding elements are #==" do + [].should == [] + ["a", "c", 7].should == ["a", "c", 7] + + [1, 2, 3].should == [1.0, 2.0, 3.0] + + obj = mock('5') + obj.should_receive(:==).and_return(true) + [obj].should == [5] + end + + # As per bug #1720 + it "returns false for [NaN] == [NaN]" do + [nan_value].should_not == [nan_value] + end +end diff --git a/spec/rubyspec/core/array/fetch_spec.rb b/spec/rubyspec/core/array/fetch_spec.rb new file mode 100644 index 0000000000..5adf96fed8 --- /dev/null +++ b/spec/rubyspec/core/array/fetch_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#fetch" do + it "returns the element at the passed index" do + [1, 2, 3].fetch(1).should == 2 + [nil].fetch(0).should == nil + end + + it "counts negative indices backwards from end" do + [1, 2, 3, 4].fetch(-1).should == 4 + end + + it "raises an IndexError if there is no element at index" do + lambda { [1, 2, 3].fetch(3) }.should raise_error(IndexError) + lambda { [1, 2, 3].fetch(-4) }.should raise_error(IndexError) + lambda { [].fetch(0) }.should raise_error(IndexError) + end + + it "returns default if there is no element at index if passed a default value" do + [1, 2, 3].fetch(5, :not_found).should == :not_found + [1, 2, 3].fetch(5, nil).should == nil + [1, 2, 3].fetch(-4, :not_found).should == :not_found + [nil].fetch(0, :not_found).should == nil + end + + it "returns the value of block if there is no element at index if passed a block" do + [1, 2, 3].fetch(9) { |i| i * i }.should == 81 + [1, 2, 3].fetch(-9) { |i| i * i }.should == 81 + end + + it "passes the original index argument object to the block, not the converted Integer" do + o = mock('5') + def o.to_int(); 5; end + + [1, 2, 3].fetch(o) { |i| i }.should equal(o) + end + + it "gives precedence to the default block over the default argument" do + lambda { + @result = [1, 2, 3].fetch(9, :foo) { |i| i * i } + }.should complain(/block supersedes default value argument/) + @result.should == 81 + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + ["a", "b", "c"].fetch(obj).should == "c" + end + + it "raises a TypeError when the passed argument can't be coerced to Integer" do + lambda { [].fetch("cat") }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/array/fill_spec.rb b/spec/rubyspec/core/array/fill_spec.rb new file mode 100644 index 0000000000..5ff7f8a250 --- /dev/null +++ b/spec/rubyspec/core/array/fill_spec.rb @@ -0,0 +1,317 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#fill" do + before :all do + @never_passed = lambda do |i| + raise ExpectationNotMetError, "the control path should not pass here" + end + end + + it "returns self" do + ary = [1, 2, 3] + ary.fill(:a).should equal(ary) + end + + it "is destructive" do + ary = [1, 2, 3] + ary.fill(:a) + ary.should == [:a, :a, :a] + end + + it "does not replicate the filler" do + ary = [1, 2, 3, 4] + str = "x" + ary.fill(str).should == [str, str, str, str] + str << "y" + ary.should == [str, str, str, str] + ary[0].should equal(str) + ary[1].should equal(str) + ary[2].should equal(str) + ary[3].should equal(str) + end + + it "replaces all elements in the array with the filler if not given a index nor a length" do + ary = ['a', 'b', 'c', 'duh'] + ary.fill(8).should == [8, 8, 8, 8] + + str = "x" + ary.fill(str).should == [str, str, str, str] + end + + it "replaces all elements with the value of block (index given to block)" do + [nil, nil, nil, nil].fill { |i| i * 2 }.should == [0, 2, 4, 6] + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.fill('x') }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError on an empty frozen array" do + lambda { ArraySpecs.empty_frozen_array.fill('x') }.should raise_error(RuntimeError) + end + + it "raises an ArgumentError if 4 or more arguments are passed when no block given" do + lambda { [].fill('a') }.should_not raise_error(ArgumentError) + + lambda { [].fill('a', 1) }.should_not raise_error(ArgumentError) + + lambda { [].fill('a', 1, 2) }.should_not raise_error(ArgumentError) + lambda { [].fill('a', 1, 2, true) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if no argument passed and no block given" do + lambda { [].fill }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if 3 or more arguments are passed when a block given" do + lambda { [].fill() {|i|} }.should_not raise_error(ArgumentError) + + lambda { [].fill(1) {|i|} }.should_not raise_error(ArgumentError) + + lambda { [].fill(1, 2) {|i|} }.should_not raise_error(ArgumentError) + lambda { [].fill(1, 2, true) {|i|} }.should raise_error(ArgumentError) + end +end + +describe "Array#fill with (filler, index, length)" do + it "replaces length elements beginning with the index with the filler if given an index and a length" do + ary = [1, 2, 3, 4, 5, 6] + ary.fill('x', 2, 3).should == [1, 2, 'x', 'x', 'x', 6] + end + + it "replaces length elements beginning with the index with the value of block" do + [true, false, true, false, true, false, true].fill(1, 4) { |i| i + 3 }.should == [true, 4, 5, 6, 7, false, true] + end + + it "replaces all elements after the index if given an index and no length" do + ary = [1, 2, 3] + ary.fill('x', 1).should == [1, 'x', 'x'] + ary.fill(1){|i| i*2}.should == [1, 2, 4] + end + + it "replaces all elements after the index if given an index and nil as a length" do + a = [1, 2, 3] + a.fill('x', 1, nil).should == [1, 'x', 'x'] + a.fill(1, nil){|i| i*2}.should == [1, 2, 4] + a.fill('y', nil).should == ['y', 'y', 'y'] + end + + it "replaces the last (-n) elements if given an index n which is negative and no length" do + a = [1, 2, 3, 4, 5] + a.fill('x', -2).should == [1, 2, 3, 'x', 'x'] + a.fill(-2){|i| i.to_s}.should == [1, 2, 3, '3', '4'] + end + + it "replaces the last (-n) elements if given an index n which is negative and nil as a length" do + a = [1, 2, 3, 4, 5] + a.fill('x', -2, nil).should == [1, 2, 3, 'x', 'x'] + a.fill(-2, nil){|i| i.to_s}.should == [1, 2, 3, '3', '4'] + end + + it "makes no modifications if given an index greater than end and no length" do + [1, 2, 3, 4, 5].fill('a', 5).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill(5, &@never_passed).should == [1, 2, 3, 4, 5] + end + + it "makes no modifications if given an index greater than end and nil as a length" do + [1, 2, 3, 4, 5].fill('a', 5, nil).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill(5, nil, &@never_passed).should == [1, 2, 3, 4, 5] + end + + it "replaces length elements beginning with start index if given an index >= 0 and a length >= 0" do + [1, 2, 3, 4, 5].fill('a', 2, 0).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill('a', 2, 2).should == [1, 2, "a", "a", 5] + + [1, 2, 3, 4, 5].fill(2, 0, &@never_passed).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill(2, 2){|i| i*2}.should == [1, 2, 4, 6, 5] + end + + it "increases the Array size when necessary" do + a = [1, 2, 3] + a.size.should == 3 + a.fill 'a', 0, 10 + a.size.should == 10 + end + + it "pads between the last element and the index with nil if given an index which is greater than size of the array" do + [1, 2, 3, 4, 5].fill('a', 8, 5).should == [1, 2, 3, 4, 5, nil, nil, nil, 'a', 'a', 'a', 'a', 'a'] + [1, 2, 3, 4, 5].fill(8, 5){|i| 'a'}.should == [1, 2, 3, 4, 5, nil, nil, nil, 'a', 'a', 'a', 'a', 'a'] + end + + it "replaces length elements beginning with the (-n)th if given an index n < 0 and a length > 0" do + [1, 2, 3, 4, 5].fill('a', -2, 2).should == [1, 2, 3, "a", "a"] + [1, 2, 3, 4, 5].fill('a', -2, 4).should == [1, 2, 3, "a", "a", "a", "a"] + + [1, 2, 3, 4, 5].fill(-2, 2){|i| 'a'}.should == [1, 2, 3, "a", "a"] + [1, 2, 3, 4, 5].fill(-2, 4){|i| 'a'}.should == [1, 2, 3, "a", "a", "a", "a"] + end + + it "starts at 0 if the negative index is before the start of the array" do + [1, 2, 3, 4, 5].fill('a', -25, 3).should == ['a', 'a', 'a', 4, 5] + [1, 2, 3, 4, 5].fill('a', -10, 10).should == %w|a a a a a a a a a a| + + [1, 2, 3, 4, 5].fill(-25, 3){|i| 'a'}.should == ['a', 'a', 'a', 4, 5] + [1, 2, 3, 4, 5].fill(-10, 10){|i| 'a'}.should == %w|a a a a a a a a a a| + end + + it "makes no modifications if the given length <= 0" do + [1, 2, 3, 4, 5].fill('a', 2, 0).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill('a', -2, 0).should == [1, 2, 3, 4, 5] + + [1, 2, 3, 4, 5].fill('a', 2, -2).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill('a', -2, -2).should == [1, 2, 3, 4, 5] + + [1, 2, 3, 4, 5].fill(2, 0, &@never_passed).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill(-2, 0, &@never_passed).should == [1, 2, 3, 4, 5] + + [1, 2, 3, 4, 5].fill(2, -2, &@never_passed).should == [1, 2, 3, 4, 5] + [1, 2, 3, 4, 5].fill(-2, -2, &@never_passed).should == [1, 2, 3, 4, 5] + end + + # See: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17481 + it "does not raise an exception if the given length is negative and its absolute value does not exceed the index" do + lambda { [1, 2, 3, 4].fill('a', 3, -1)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill('a', 3, -2)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill('a', 3, -3)}.should_not raise_error(ArgumentError) + + lambda { [1, 2, 3, 4].fill(3, -1, &@never_passed)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill(3, -2, &@never_passed)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill(3, -3, &@never_passed)}.should_not raise_error(ArgumentError) + end + + it "does not raise an exception even if the given length is negative and its absolute value exceeds the index" do + lambda { [1, 2, 3, 4].fill('a', 3, -4)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill('a', 3, -5)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill('a', 3, -10000)}.should_not raise_error(ArgumentError) + + lambda { [1, 2, 3, 4].fill(3, -4, &@never_passed)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill(3, -5, &@never_passed)}.should_not raise_error(ArgumentError) + lambda { [1, 2, 3, 4].fill(3, -10000, &@never_passed)}.should_not raise_error(ArgumentError) + end + + it "tries to convert the second and third arguments to Integers using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2, 2) + filler = mock('filler') + filler.should_not_receive(:to_int) + [1, 2, 3, 4, 5].fill(filler, obj, obj).should == [1, 2, filler, filler, 5] + end + + it "raises a TypeError if the index is not numeric" do + lambda { [].fill 'a', true }.should raise_error(TypeError) + + obj = mock('nonnumeric') + lambda { [].fill('a', obj) }.should raise_error(TypeError) + end + + not_supported_on :opal do + it "raises an ArgumentError or RangeError for too-large sizes" do + arr = [1, 2, 3] + lambda { arr.fill(10, 1, fixnum_max) }.should raise_error(ArgumentError) + lambda { arr.fill(10, 1, bignum_value) }.should raise_error(RangeError) + end + end +end + +describe "Array#fill with (filler, range)" do + it "replaces elements in range with object" do + [1, 2, 3, 4, 5, 6].fill(8, 0..3).should == [8, 8, 8, 8, 5, 6] + [1, 2, 3, 4, 5, 6].fill(8, 0...3).should == [8, 8, 8, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', 4..6).should == [1, 2, 3, 4, 'x', 'x', 'x'] + [1, 2, 3, 4, 5, 6].fill('x', 4...6).should == [1, 2, 3, 4, 'x', 'x'] + [1, 2, 3, 4, 5, 6].fill('x', -2..-1).should == [1, 2, 3, 4, 'x', 'x'] + [1, 2, 3, 4, 5, 6].fill('x', -2...-1).should == [1, 2, 3, 4, 'x', 6] + [1, 2, 3, 4, 5, 6].fill('x', -2...-2).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', -2..-2).should == [1, 2, 3, 4, 'x', 6] + [1, 2, 3, 4, 5, 6].fill('x', -2..0).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', 0...0).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', 1..1).should == [1, 'x', 3, 4, 5, 6] + end + + it "replaces all elements in range with the value of block" do + [1, 1, 1, 1, 1, 1].fill(1..6) { |i| i + 1 }.should == [1, 2, 3, 4, 5, 6, 7] + end + + it "increases the Array size when necessary" do + [1, 2, 3].fill('x', 1..6).should == [1, 'x', 'x', 'x', 'x', 'x', 'x'] + [1, 2, 3].fill(1..6){|i| i+1}.should == [1, 2, 3, 4, 5, 6, 7] + end + + it "raises a TypeError with range and length argument" do + lambda { [].fill('x', 0 .. 2, 5) }.should raise_error(TypeError) + end + + it "replaces elements between the (-m)th to the last and the (n+1)th from the first if given an range m..n where m < 0 and n >= 0" do + [1, 2, 3, 4, 5, 6].fill('x', -4..4).should == [1, 2, 'x', 'x', 'x', 6] + [1, 2, 3, 4, 5, 6].fill('x', -4...4).should == [1, 2, 'x', 'x', 5, 6] + + [1, 2, 3, 4, 5, 6].fill(-4..4){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6] + [1, 2, 3, 4, 5, 6].fill(-4...4){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6] + end + + it "replaces elements between the (-m)th and (-n)th to the last if given an range m..n where m < 0 and n < 0" do + [1, 2, 3, 4, 5, 6].fill('x', -4..-2).should == [1, 2, 'x', 'x', 'x', 6] + [1, 2, 3, 4, 5, 6].fill('x', -4...-2).should == [1, 2, 'x', 'x', 5, 6] + + [1, 2, 3, 4, 5, 6].fill(-4..-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6] + [1, 2, 3, 4, 5, 6].fill(-4...-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6] + end + + it "replaces elements between the (m+1)th from the first and (-n)th to the last if given an range m..n where m >= 0 and n < 0" do + [1, 2, 3, 4, 5, 6].fill('x', 2..-2).should == [1, 2, 'x', 'x', 'x', 6] + [1, 2, 3, 4, 5, 6].fill('x', 2...-2).should == [1, 2, 'x', 'x', 5, 6] + + [1, 2, 3, 4, 5, 6].fill(2..-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', '5', 6] + [1, 2, 3, 4, 5, 6].fill(2...-2){|i| (i+1).to_s}.should == [1, 2, '3', '4', 5, 6] + end + + it "makes no modifications if given an range which implies a section of zero width" do + [1, 2, 3, 4, 5, 6].fill('x', 2...2).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', -4...2).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', -4...-4).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', 2...-4).should == [1, 2, 3, 4, 5, 6] + + [1, 2, 3, 4, 5, 6].fill(2...2, &@never_passed).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill(-4...2, &@never_passed).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill(-4...-4, &@never_passed).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill(2...-4, &@never_passed).should == [1, 2, 3, 4, 5, 6] + end + + it "makes no modifications if given an range which implies a section of negative width" do + [1, 2, 3, 4, 5, 6].fill('x', 2..1).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', -4..1).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', -2..-4).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill('x', 2..-5).should == [1, 2, 3, 4, 5, 6] + + [1, 2, 3, 4, 5, 6].fill(2..1, &@never_passed).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill(-4..1, &@never_passed).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill(-2..-4, &@never_passed).should == [1, 2, 3, 4, 5, 6] + [1, 2, 3, 4, 5, 6].fill(2..-5, &@never_passed).should == [1, 2, 3, 4, 5, 6] + end + + it "raises an exception if some of the given range lies before the first of the array" do + lambda { [1, 2, 3].fill('x', -5..-3) }.should raise_error(RangeError) + lambda { [1, 2, 3].fill('x', -5...-3) }.should raise_error(RangeError) + lambda { [1, 2, 3].fill('x', -5..-4) }.should raise_error(RangeError) + + lambda { [1, 2, 3].fill(-5..-3, &@never_passed) }.should raise_error(RangeError) + lambda { [1, 2, 3].fill(-5...-3, &@never_passed) }.should raise_error(RangeError) + lambda { [1, 2, 3].fill(-5..-4, &@never_passed) }.should raise_error(RangeError) + end + + it "tries to convert the start and end of the passed range to Integers using #to_int" do + obj = mock('to_int') + def obj.<=>(rhs); rhs == self ? 0 : nil end + obj.should_receive(:to_int).twice.and_return(2) + filler = mock('filler') + filler.should_not_receive(:to_int) + [1, 2, 3, 4, 5].fill(filler, obj..obj).should == [1, 2, filler, 4, 5] + end + + it "raises a TypeError if the start or end of the passed range is not numeric" do + obj = mock('nonnumeric') + def obj.<=>(rhs); rhs == self ? 0 : nil end + lambda { [].fill('a', obj..obj) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/array/find_index_spec.rb b/spec/rubyspec/core/array/find_index_spec.rb new file mode 100644 index 0000000000..522b4b31c6 --- /dev/null +++ b/spec/rubyspec/core/array/find_index_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/index', __FILE__) + +describe "Array#find_index" do + it_behaves_like :array_index, :find_index +end diff --git a/spec/rubyspec/core/array/first_spec.rb b/spec/rubyspec/core/array/first_spec.rb new file mode 100644 index 0000000000..60a0a76594 --- /dev/null +++ b/spec/rubyspec/core/array/first_spec.rb @@ -0,0 +1,93 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#first" do + it "returns the first element" do + %w{a b c}.first.should == 'a' + [nil].first.should == nil + end + + it "returns nil if self is empty" do + [].first.should == nil + end + + it "returns the first count elements if given a count" do + [true, false, true, nil, false].first(2).should == [true, false] + end + + it "returns an empty array when passed count on an empty array" do + [].first(0).should == [] + [].first(1).should == [] + [].first(2).should == [] + end + + it "returns an empty array when passed count == 0" do + [1, 2, 3, 4, 5].first(0).should == [] + end + + it "returns an array containing the first element when passed count == 1" do + [1, 2, 3, 4, 5].first(1).should == [1] + end + + it "raises an ArgumentError when count is negative" do + lambda { [1, 2].first(-1) }.should raise_error(ArgumentError) + end + + it "raises a RangeError when count is a Bignum" do + lambda { [].first(bignum_value) }.should raise_error(RangeError) + end + + it "returns the entire array when count > length" do + [1, 2, 3, 4, 5, 9].first(10).should == [1, 2, 3, 4, 5, 9] + end + + it "returns an array which is independent to the original when passed count" do + ary = [1, 2, 3, 4, 5] + ary.first(0).replace([1,2]) + ary.should == [1, 2, 3, 4, 5] + ary.first(1).replace([1,2]) + ary.should == [1, 2, 3, 4, 5] + ary.first(6).replace([1,2]) + ary.should == [1, 2, 3, 4, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.first.should equal(empty) + + ary = ArraySpecs.head_recursive_array + ary.first.should equal(ary) + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + [1, 2, 3, 4, 5].first(obj).should == [1, 2] + end + + it "raises a TypeError if the passed argument is not numeric" do + lambda { [1,2].first(nil) }.should raise_error(TypeError) + lambda { [1,2].first("a") }.should raise_error(TypeError) + + obj = mock("nonnumeric") + lambda { [1,2].first(obj) }.should raise_error(TypeError) + end + + it "does not return subclass instance when passed count on Array subclasses" do + ArraySpecs::MyArray[].first(0).should be_an_instance_of(Array) + ArraySpecs::MyArray[].first(2).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].first(0).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].first(1).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].first(2).should be_an_instance_of(Array) + end + + it "is not destructive" do + a = [1, 2, 3] + a.first + a.should == [1, 2, 3] + a.first(2) + a.should == [1, 2, 3] + a.first(3) + a.should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/core/array/fixtures/classes.rb b/spec/rubyspec/core/array/fixtures/classes.rb new file mode 100644 index 0000000000..4292554724 --- /dev/null +++ b/spec/rubyspec/core/array/fixtures/classes.rb @@ -0,0 +1,525 @@ +class Object + # This helper is defined here rather than in MSpec because + # it is only used in #pack specs. + def pack_format(count=nil, repeat=nil) + format = "#{instance_variable_get(:@method)}#{count}" + format *= repeat if repeat + format + end +end + +module ArraySpecs + SampleRange = 0..1000 + SampleCount = 1000 + + def self.frozen_array + frozen_array = [1,2,3] + frozen_array.freeze + frozen_array + end + + def self.empty_frozen_array + frozen_array = [] + frozen_array.freeze + frozen_array + end + + def self.recursive_array + a = [1, 'two', 3.0] + 5.times { a << a } + a + end + + def self.head_recursive_array + a = [] + 5.times { a << a } + a << 1 << 'two' << 3.0 + a + end + + def self.empty_recursive_array + a = [] + a << a + a + end + + class MyArray < Array + # The #initialize method has a different signature than Array to help + # catch places in the specs that do not assert the #initialize is not + # called when Array methods make new instances. + def initialize(a, b) + self << a << b + ScratchPad.record :my_array_initialize + end + end + + class Sexp < Array + def initialize(*args) + super(args) + end + end + + # TODO: replace specs that use this with #should_not_receive(:to_ary) + # expectations on regular objects (e.g. Array instances). + class ToAryArray < Array + def to_ary() ["to_ary", "was", "called!"] end + end + + class MyRange < Range; end + + class AssocKey + def ==(other); other == 'it'; end + end + + class D + def <=>(obj) + return 4 <=> obj unless obj.class == D + 0 + end + end + + class SubArray < Array + def initialize(*args) + ScratchPad.record args + end + end + + class ArrayConvertable + attr_accessor :called + def initialize(*values, &block) + @values = values; + end + + def to_a + self.called = :to_a + @values + end + + def to_ary + self.called = :to_ary + @values + end + end + + class SortSame + def <=>(other); 0; end + def ==(other); true; end + end + + class UFOSceptic + def <=>(other); raise "N-uh, UFO:s do not exist!"; end + end + + class MockForCompared + @@count = 0 + @@compared = false + def initialize + @@compared = false + @order = (@@count += 1) + end + def <=>(rhs) + @@compared = true + return rhs.order <=> self.order + end + def self.compared? + @@compared + end + + protected + attr_accessor :order + end + + class ComparableWithFixnum + include Comparable + def initialize(num) + @num = num + end + + def <=>(fixnum) + @num <=> fixnum + end + end + + class Uncomparable + def <=>(obj) + nil + end + end + + def self.universal_pack_object + obj = mock("string float int") + obj.stub!(:to_int).and_return(1) + obj.stub!(:to_str).and_return("1") + obj.stub!(:to_f).and_return(1.0) + 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_constaint_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_constaint_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 + + def to_ary + [1, 2, 3] + end + end +end diff --git a/spec/rubyspec/core/array/fixtures/encoded_strings.rb b/spec/rubyspec/core/array/fixtures/encoded_strings.rb new file mode 100644 index 0000000000..e31e247afe --- /dev/null +++ b/spec/rubyspec/core/array/fixtures/encoded_strings.rb @@ -0,0 +1,69 @@ +# encoding: utf-8 +module ArraySpecs + def self.array_with_usascii_and_7bit_utf8_strings + [ + 'foo'.force_encoding('US-ASCII'), + 'bar' + ] + end + + def self.array_with_usascii_and_utf8_strings + [ + 'foo'.force_encoding('US-ASCII'), + 'báz' + ] + end + + def self.array_with_7bit_utf8_and_usascii_strings + [ + 'bar', + 'foo'.force_encoding('US-ASCII') + ] + end + + def self.array_with_utf8_and_usascii_strings + [ + 'báz', + 'bar', + 'foo'.force_encoding('US-ASCII') + ] + end + + def self.array_with_usascii_and_utf8_strings + [ + 'foo'.force_encoding('US-ASCII'), + 'bar', + 'báz' + ] + end + + def self.array_with_utf8_and_7bit_ascii8bit_strings + [ + 'bar', + 'báz', + 'foo'.force_encoding('ASCII-8BIT') + ] + end + + def self.array_with_utf8_and_ascii8bit_strings + [ + 'bar', + 'báz', + [255].pack('C').force_encoding('ASCII-8BIT') + ] + end + + def self.array_with_usascii_and_7bit_ascii8bit_strings + [ + 'bar'.force_encoding('US-ASCII'), + 'foo'.force_encoding('ASCII-8BIT') + ] + end + + def self.array_with_usascii_and_ascii8bit_strings + [ + 'bar'.force_encoding('US-ASCII'), + [255].pack('C').force_encoding('ASCII-8BIT') + ] + end +end diff --git a/spec/rubyspec/core/array/flatten_spec.rb b/spec/rubyspec/core/array/flatten_spec.rb new file mode 100644 index 0000000000..554b711a55 --- /dev/null +++ b/spec/rubyspec/core/array/flatten_spec.rb @@ -0,0 +1,270 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#flatten" do + it "returns a one-dimensional flattening recursively" do + [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []].flatten.should == [1, 2, 3, 2, 3, 4, 4, 5, 5, 1, 2, 3, 4] + end + + it "takes an optional argument that determines the level of recursion" do + [ 1, 2, [3, [4, 5] ] ].flatten(1).should == [1, 2, 3, [4, 5]] + end + + it "returns dup when the level of recursion is 0" do + a = [ 1, 2, [3, [4, 5] ] ] + a.flatten(0).should == a + a.flatten(0).should_not equal(a) + end + + it "ignores negative levels" do + [ 1, 2, [ 3, 4, [5, 6] ] ].flatten(-1).should == [1, 2, 3, 4, 5, 6] + [ 1, 2, [ 3, 4, [5, 6] ] ].flatten(-10).should == [1, 2, 3, 4, 5, 6] + end + + it "tries to convert passed Objects to Integers using #to_int" do + obj = mock("Converted to Integer") + obj.should_receive(:to_int).and_return(1) + + [ 1, 2, [3, [4, 5] ] ].flatten(obj).should == [1, 2, 3, [4, 5]] + end + + it "raises a TypeError when the passed Object can't be converted to an Integer" do + obj = mock("Not converted") + lambda { [ 1, 2, [3, [4, 5] ] ].flatten(obj) }.should raise_error(TypeError) + end + + it "does not call flatten on elements" do + obj = mock('[1,2]') + obj.should_not_receive(:flatten) + [obj, obj].flatten.should == [obj, obj] + + obj = [5, 4] + obj.should_not_receive(:flatten) + [obj, obj].flatten.should == [5, 4, 5, 4] + end + + it "raises an ArgumentError on recursive arrays" do + x = [] + x << x + lambda { x.flatten }.should raise_error(ArgumentError) + + x = [] + y = [] + x << y + y << x + lambda { x.flatten }.should raise_error(ArgumentError) + end + + it "flattens any element which responds to #to_ary, using the return value of said method" do + x = mock("[3,4]") + x.should_receive(:to_ary).at_least(:once).and_return([3, 4]) + [1, 2, x, 5].flatten.should == [1, 2, 3, 4, 5] + + y = mock("MyArray[]") + y.should_receive(:to_ary).at_least(:once).and_return(ArraySpecs::MyArray[]) + [y].flatten.should == [] + + z = mock("[2,x,y,5]") + z.should_receive(:to_ary).and_return([2, x, y, 5]) + [1, z, 6].flatten.should == [1, 2, 3, 4, 5, 6] + end + + ruby_version_is "2.3" do + it "does not call #to_ary on elements beyond the given level" do + obj = mock("1") + obj.should_not_receive(:to_ary) + [[obj]].flatten(1) + end + end + + it "returns subclass instance for Array subclasses" do + ArraySpecs::MyArray[].flatten.should be_an_instance_of(ArraySpecs::MyArray) + ArraySpecs::MyArray[1, 2, 3].flatten.should be_an_instance_of(ArraySpecs::MyArray) + ArraySpecs::MyArray[1, [2], 3].flatten.should be_an_instance_of(ArraySpecs::MyArray) + ArraySpecs::MyArray[1, [2, 3], 4].flatten.should == ArraySpecs::MyArray[1, 2, 3, 4] + [ArraySpecs::MyArray[1, 2, 3]].flatten.should be_an_instance_of(Array) + end + + it "is not destructive" do + ary = [1, [2, 3]] + ary.flatten + ary.should == [1, [2, 3]] + end + + describe "with a non-Array object in the Array" do + before :each do + @obj = mock("Array#flatten") + ScratchPad.record [] + end + + it "does not call #to_ary if the method is not defined" do + [@obj].flatten.should == [@obj] + end + + it "does not raise an exception if #to_ary returns nil" do + @obj.should_receive(:to_ary).and_return(nil) + [@obj].flatten.should == [@obj] + end + + it "raises a TypeError if #to_ary does not return an Array" do + @obj.should_receive(:to_ary).and_return(1) + lambda { [@obj].flatten }.should raise_error(TypeError) + end + + it "does not call #to_ary if not defined when #respond_to_missing? returns false" do + def @obj.respond_to_missing?(*args) ScratchPad << args; false end + + [@obj].flatten.should == [@obj] + ScratchPad.recorded.should == [[:to_ary, false]] + end + + it "calls #to_ary if not defined when #respond_to_missing? returns true" do + def @obj.respond_to_missing?(*args) ScratchPad << args; true end + + lambda { [@obj].flatten }.should raise_error(NoMethodError) + ScratchPad.recorded.should == [[:to_ary, false]] + end + + it "calls #method_missing if defined" do + @obj.should_receive(:method_missing).with(:to_ary).and_return([1, 2, 3]) + [@obj].flatten.should == [1, 2, 3] + end + end + + it "returns a tainted array if self is tainted" do + [].taint.flatten.tainted?.should be_true + end + + it "returns an untrusted array if self is untrusted" do + [].untrust.flatten.untrusted?.should be_true + end + + it "performs respond_to? and method_missing-aware checks when coercing elements to array" do + bo = BasicObject.new + [bo].flatten.should == [bo] + + def bo.method_missing(name, *) + [1,2] + end + + [bo].flatten.should == [1,2] + + def bo.respond_to?(name, *) + false + end + + [bo].flatten.should == [bo] + + def bo.respond_to?(name, *) + true + end + + [bo].flatten.should == [1,2] + end +end + +describe "Array#flatten!" do + it "modifies array to produce a one-dimensional flattening recursively" do + a = [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []] + a.flatten! + a.should == [1, 2, 3, 2, 3, 4, 4, 5, 5, 1, 2, 3, 4] + end + + it "returns self if made some modifications" do + a = [[[1, [2, 3]],[2, 3, [4, [4, [5, 5]], [1, 2, 3]]], [4]], []] + a.flatten!.should equal(a) + end + + it "returns nil if no modifications took place" do + a = [1, 2, 3] + a.flatten!.should == nil + a = [1, [2, 3]] + a.flatten!.should_not == nil + end + + it "should not check modification by size" do + a = [1, 2, [3]] + a.flatten!.should_not == nil + a.should == [1, 2, 3] + end + + it "takes an optional argument that determines the level of recursion" do + [ 1, 2, [3, [4, 5] ] ].flatten!(1).should == [1, 2, 3, [4, 5]] + end + + # redmine #1440 + it "returns nil when the level of recursion is 0" do + a = [ 1, 2, [3, [4, 5] ] ] + a.flatten!(0).should == nil + end + + it "treats negative levels as no arguments" do + [ 1, 2, [ 3, 4, [5, 6] ] ].flatten!(-1).should == [1, 2, 3, 4, 5, 6] + [ 1, 2, [ 3, 4, [5, 6] ] ].flatten!(-10).should == [1, 2, 3, 4, 5, 6] + end + + it "tries to convert passed Objects to Integers using #to_int" do + obj = mock("Converted to Integer") + obj.should_receive(:to_int).and_return(1) + + [ 1, 2, [3, [4, 5] ] ].flatten!(obj).should == [1, 2, 3, [4, 5]] + end + + it "raises a TypeError when the passed Object can't be converted to an Integer" do + obj = mock("Not converted") + lambda { [ 1, 2, [3, [4, 5] ] ].flatten!(obj) }.should raise_error(TypeError) + end + + it "does not call flatten! on elements" do + obj = mock('[1,2]') + obj.should_not_receive(:flatten!) + [obj, obj].flatten!.should == nil + + obj = [5, 4] + obj.should_not_receive(:flatten!) + [obj, obj].flatten!.should == [5, 4, 5, 4] + end + + it "raises an ArgumentError on recursive arrays" do + x = [] + x << x + lambda { x.flatten! }.should raise_error(ArgumentError) + + x = [] + y = [] + x << y + y << x + lambda { x.flatten! }.should raise_error(ArgumentError) + end + + it "flattens any elements which responds to #to_ary, using the return value of said method" do + x = mock("[3,4]") + x.should_receive(:to_ary).at_least(:once).and_return([3, 4]) + [1, 2, x, 5].flatten!.should == [1, 2, 3, 4, 5] + + y = mock("MyArray[]") + y.should_receive(:to_ary).at_least(:once).and_return(ArraySpecs::MyArray[]) + [y].flatten!.should == [] + + z = mock("[2,x,y,5]") + z.should_receive(:to_ary).and_return([2, x, y, 5]) + [1, z, 6].flatten!.should == [1, 2, 3, 4, 5, 6] + + ary = [ArraySpecs::MyArray[1, 2, 3]] + ary.flatten! + ary.should be_an_instance_of(Array) + ary.should == [1, 2, 3] + end + + it "raises a RuntimeError on frozen arrays when the array is modified" do + nested_ary = [1, 2, []] + nested_ary.freeze + lambda { nested_ary.flatten! }.should raise_error(RuntimeError) + end + + # see [ruby-core:23663] + it "raises a RuntimeError on frozen arrays when the array would not be modified" do + lambda { ArraySpecs.frozen_array.flatten! }.should raise_error(RuntimeError) + lambda { ArraySpecs.empty_frozen_array.flatten! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/frozen_spec.rb b/spec/rubyspec/core/array/frozen_spec.rb new file mode 100644 index 0000000000..6c8384f5f7 --- /dev/null +++ b/spec/rubyspec/core/array/frozen_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#frozen?" do + it "returns true if array is frozen" do + a = [1, 2, 3] + a.frozen?.should == false + a.freeze + a.frozen?.should == true + end + + it "returns false for an array being sorted by #sort" do + a = [1, 2, 3] + a.sort { |x,y| a.frozen?.should == false; x <=> y } + end +end diff --git a/spec/rubyspec/core/array/hash_spec.rb b/spec/rubyspec/core/array/hash_spec.rb new file mode 100644 index 0000000000..b576cbbdc6 --- /dev/null +++ b/spec/rubyspec/core/array/hash_spec.rb @@ -0,0 +1,83 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#hash" do + it "returns the same fixnum for arrays with the same content" do + [].respond_to?(:hash).should == true + + [[], [1, 2, 3]].each do |ary| + ary.hash.should == ary.dup.hash + ary.hash.should be_an_instance_of(Fixnum) + end + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + lambda { empty.hash }.should_not raise_error + + array = ArraySpecs.recursive_array + lambda { array.hash }.should_not raise_error + end + + it "returns the same hash for equal recursive arrays" do + rec = []; rec << rec + rec.hash.should == [rec].hash + rec.hash.should == [[rec]].hash + # This is because rec.eql?([[rec]]) + # Remember that if two objects are eql? + # then the need to have the same hash + # Check the Array#eql? specs! + end + + it "returns the same hash for equal recursive arrays through hashes" do + h = {} ; rec = [h] ; h[:x] = rec + rec.hash.should == [h].hash + rec.hash.should == [{x: rec}].hash + # Like above, this is because rec.eql?([{x: rec}]) + end + + it "calls to_int on result of calling hash on each element" do + ary = Array.new(5) do + obj = mock('0') + obj.should_receive(:hash).and_return(obj) + obj.should_receive(:to_int).and_return(0) + obj + end + + ary.hash + + + hash = mock('1') + hash.should_receive(:to_int).and_return(1.hash) + + obj = mock('@hash') + obj.instance_variable_set(:@hash, hash) + def obj.hash() @hash end + + [obj].hash.should == [1].hash + end + + it "ignores array class differences" do + ArraySpecs::MyArray[].hash.should == [].hash + ArraySpecs::MyArray[1, 2].hash.should == [1, 2].hash + end + + it "returns same hash code for arrays with the same content" do + a = [1, 2, 3, 4] + a.fill 'a', 0..3 + b = %w|a a a a| + a.hash.should == b.hash + end + + it "returns the same value if arrays are #eql?" do + a = [1, 2, 3, 4] + a.fill 'a', 0..3 + b = %w|a a a a| + a.hash.should == b.hash + a.should eql(b) + end + + it "produces different hashes for nested arrays with different values and empty terminator" do + [1, [1, []]].hash.should_not == [2, [2, []]].hash + end +end diff --git a/spec/rubyspec/core/array/include_spec.rb b/spec/rubyspec/core/array/include_spec.rb new file mode 100644 index 0000000000..26d788afeb --- /dev/null +++ b/spec/rubyspec/core/array/include_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#include?" do + it "returns true if object is present, false otherwise" do + [1, 2, "a", "b"].include?("c").should == false + [1, 2, "a", "b"].include?("a").should == true + end + + it "determines presence by using element == obj" do + o = mock('') + + [1, 2, "a", "b"].include?(o).should == false + + def o.==(other); other == 'a'; end + + [1, 2, o, "b"].include?('a').should == true + + [1, 2.0, 3].include?(2).should == true + end + + it "calls == on elements from left to right until success" do + key = "x" + one = mock('one') + two = mock('two') + three = mock('three') + one.should_receive(:==).any_number_of_times.and_return(false) + two.should_receive(:==).any_number_of_times.and_return(true) + three.should_not_receive(:==) + ary = [one, two, three] + ary.include?(key).should == true + end +end diff --git a/spec/rubyspec/core/array/index_spec.rb b/spec/rubyspec/core/array/index_spec.rb new file mode 100644 index 0000000000..55ed7b2a94 --- /dev/null +++ b/spec/rubyspec/core/array/index_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/index', __FILE__) + +describe "Array#index" do + it_behaves_like(:array_index, :index) +end diff --git a/spec/rubyspec/core/array/initialize_spec.rb b/spec/rubyspec/core/array/initialize_spec.rb new file mode 100644 index 0000000000..0c37c6136d --- /dev/null +++ b/spec/rubyspec/core/array/initialize_spec.rb @@ -0,0 +1,156 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#initialize" do + before :each do + ScratchPad.clear + end + + it "is private" do + Array.should have_private_instance_method("initialize") + end + + it "is called on subclasses" do + b = ArraySpecs::SubArray.new :size_or_array, :obj + + b.should == [] + ScratchPad.recorded.should == [:size_or_array, :obj] + end + + it "preserves the object's identity even when changing its value" do + a = [1, 2, 3] + a.send(:initialize).should equal(a) + a.should_not == [1, 2, 3] + end + + it "raises an ArgumentError if passed 3 or more arguments" do + lambda do + [1, 2].send :initialize, 1, 'x', true + end.should raise_error(ArgumentError) + lambda do + [1, 2].send(:initialize, 1, 'x', true) {} + end.should raise_error(ArgumentError) + end + + it "raises a RuntimeError on frozen arrays" do + lambda do + ArraySpecs.frozen_array.send :initialize + end.should raise_error(RuntimeError) + lambda do + ArraySpecs.frozen_array.send :initialize, ArraySpecs.frozen_array + end.should raise_error(RuntimeError) + end + + it "calls #to_ary to convert the value to an array, even if it's private" do + a = ArraySpecs::PrivateToAry.new + [].send(:initialize, a).should == [1, 2, 3] + end +end + +describe "Array#initialize with no arguments" do + it "makes the array empty" do + [1, 2, 3].send(:initialize).should be_empty + end + + it "does not use the given block" do + lambda{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error + end +end + +describe "Array#initialize with (array)" do + it "replaces self with the other array" do + b = [4, 5, 6] + [1, 2, 3].send(:initialize, b).should == b + end + + it "does not use the given block" do + lambda{ [1, 2, 3].send(:initialize) { raise } }.should_not raise_error + end + + it "calls #to_ary to convert the value to an array" do + a = mock("array") + a.should_receive(:to_ary).and_return([1, 2]) + a.should_not_receive(:to_int) + [].send(:initialize, a).should == [1, 2] + end + + it "does not call #to_ary on instances of Array or subclasses of Array" do + a = [1, 2] + a.should_not_receive(:to_ary) + [].send(:initialize, a).should == a + end + + it "raises a TypeError if an Array type argument and a default object" do + lambda { [].send(:initialize, [1, 2], 1) }.should raise_error(TypeError) + end +end + +describe "Array#initialize with (size, object=nil)" do + it "sets the array to size and fills with the object" do + a = [] + obj = [3] + a.send(:initialize, 2, obj).should == [obj, obj] + a[0].should equal(obj) + a[1].should equal(obj) + + b = [] + b.send(:initialize, 3, 14).should == [14, 14, 14] + b.should == [14, 14, 14] + end + + it "sets the array to size and fills with nil when object is omitted" do + [].send(:initialize, 3).should == [nil, nil, nil] + end + + it "raises an ArgumentError if size is negative" do + lambda { [].send(:initialize, -1, :a) }.should raise_error(ArgumentError) + lambda { [].send(:initialize, -1) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if size is too large" do + lambda { [].send(:initialize, fixnum_max+1) }.should raise_error(ArgumentError) + end + + it "calls #to_int to convert the size argument to an Integer when object is given" do + obj = mock('1') + obj.should_receive(:to_int).and_return(1) + [].send(:initialize, obj, :a).should == [:a] + end + + it "calls #to_int to convert the size argument to an Integer when object is not given" do + obj = mock('1') + obj.should_receive(:to_int).and_return(1) + [].send(:initialize, obj).should == [nil] + end + + it "raises a TypeError if the size argument is not an Integer type" do + obj = mock('nonnumeric') + obj.stub!(:to_ary).and_return([1, 2]) + lambda{ [].send(:initialize, obj, :a) }.should raise_error(TypeError) + end + + it "yields the index of the element and sets the element to the value of the block" do + [].send(:initialize, 3) { |i| i.to_s }.should == ['0', '1', '2'] + end + + it "uses the block value instead of using the default value" do + lambda { + @result = [].send(:initialize, 3, :obj) { |i| i.to_s } + }.should complain(/block supersedes default value argument/) + @result.should == ['0', '1', '2'] + end + + it "returns the value passed to break" do + [].send(:initialize, 3) { break :a }.should == :a + end + + it "sets the array to the values returned by the block before break is executed" do + a = [1, 2, 3] + a.send(:initialize, 3) do |i| + break if i == 2 + i.to_s + end + + a.should == ['0', '1'] + end +end diff --git a/spec/rubyspec/core/array/insert_spec.rb b/spec/rubyspec/core/array/insert_spec.rb new file mode 100644 index 0000000000..cdf870df2a --- /dev/null +++ b/spec/rubyspec/core/array/insert_spec.rb @@ -0,0 +1,78 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#insert" do + it "returns self" do + ary = [] + ary.insert(0).should equal(ary) + ary.insert(0, :a).should equal(ary) + end + + it "inserts objects before the element at index for non-negative index" do + ary = [] + ary.insert(0, 3).should == [3] + ary.insert(0, 1, 2).should == [1, 2, 3] + ary.insert(0).should == [1, 2, 3] + + # Let's just assume insert() always modifies the array from now on. + ary.insert(1, 'a').should == [1, 'a', 2, 3] + ary.insert(0, 'b').should == ['b', 1, 'a', 2, 3] + ary.insert(5, 'c').should == ['b', 1, 'a', 2, 3, 'c'] + ary.insert(7, 'd').should == ['b', 1, 'a', 2, 3, 'c', nil, 'd'] + ary.insert(10, 5, 4).should == ['b', 1, 'a', 2, 3, 'c', nil, 'd', nil, nil, 5, 4] + end + + it "appends objects to the end of the array for index == -1" do + [1, 3, 3].insert(-1, 2, 'x', 0.5).should == [1, 3, 3, 2, 'x', 0.5] + end + + it "inserts objects after the element at index with negative index" do + ary = [] + ary.insert(-1, 3).should == [3] + ary.insert(-2, 2).should == [2, 3] + ary.insert(-3, 1).should == [1, 2, 3] + ary.insert(-2, -3).should == [1, 2, -3, 3] + ary.insert(-1, []).should == [1, 2, -3, 3, []] + ary.insert(-2, 'x', 'y').should == [1, 2, -3, 3, 'x', 'y', []] + ary = [1, 2, 3] + end + + it "pads with nils if the index to be inserted to is past the end" do + [].insert(5, 5).should == [nil, nil, nil, nil, nil, 5] + end + + it "can insert before the first element with a negative index" do + [1, 2, 3].insert(-4, -3).should == [-3, 1, 2, 3] + end + + it "raises an IndexError if the negative index is out of bounds" do + lambda { [].insert(-2, 1) }.should raise_error(IndexError) + lambda { [1].insert(-3, 2) }.should raise_error(IndexError) + end + + it "does nothing of no object is passed" do + [].insert(0).should == [] + [].insert(-1).should == [] + [].insert(10).should == [] + [].insert(-2).should == [] + end + + it "tries to convert the passed position argument to an Integer using #to_int" do + obj = mock('2') + obj.should_receive(:to_int).and_return(2) + [].insert(obj, 'x').should == [nil, nil, 'x'] + end + + it "raises an ArgumentError if no argument passed" do + lambda { [].insert() }.should raise_error(ArgumentError) + end + + it "raises a RuntimeError on frozen arrays when the array is modified" do + lambda { ArraySpecs.frozen_array.insert(0, 'x') }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on frozen arrays when the array would not be modified" do + lambda { ArraySpecs.frozen_array.insert(0) }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/inspect_spec.rb b/spec/rubyspec/core/array/inspect_spec.rb new file mode 100644 index 0000000000..9896406fd5 --- /dev/null +++ b/spec/rubyspec/core/array/inspect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "Array#inspect" do + it_behaves_like :array_inspect, :inspect +end diff --git a/spec/rubyspec/core/array/intersection_spec.rb b/spec/rubyspec/core/array/intersection_spec.rb new file mode 100644 index 0000000000..9eabe590d9 --- /dev/null +++ b/spec/rubyspec/core/array/intersection_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#&" do + it "creates an array with elements common to both arrays (intersection)" do + ([] & []).should == [] + ([1, 2] & []).should == [] + ([] & [1, 2]).should == [] + ([ 1, 3, 5 ] & [ 1, 2, 3 ]).should == [1, 3] + end + + it "creates an array with no duplicates" do + ([ 1, 1, 3, 5 ] & [ 1, 2, 3 ]).uniq!.should == nil + end + + it "creates an array with elements in order they are first encountered" do + ([ 1, 2, 3, 2, 5 ] & [ 5, 2, 3, 4 ]).should == [2, 3, 5] + end + + it "does not modify the original Array" do + a = [1, 1, 3, 5] + a & [1, 2, 3] + a.should == [1, 1, 3, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + (empty & empty).should == empty + + (ArraySpecs.recursive_array & []).should == [] + ([] & ArraySpecs.recursive_array).should == [] + + (ArraySpecs.recursive_array & ArraySpecs.recursive_array).should == [1, 'two', 3.0, ArraySpecs.recursive_array] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('[1,2,3]') + obj.should_receive(:to_ary).and_return([1, 2, 3]) + ([1, 2] & obj).should == ([1, 2]) + end + + it "determines equivalence between elements in the sense of eql?" do + not_supported_on :opal do + ([5.0, 4.0] & [5, 4]).should == [] + end + + str = "x" + ([str] & [str.dup]).should == [str] + + obj1 = mock('1') + obj2 = mock('2') + obj1.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:hash).at_least(1).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(true) + + ([obj1] & [obj2]).should == [obj1] + ([obj1, obj1, obj2, obj2] & [obj2]).should == [obj1] + + obj1 = mock('3') + obj2 = mock('4') + obj1.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:hash).at_least(1).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(false) + + ([obj1] & [obj2]).should == [] + ([obj1, obj1, obj2, obj2] & [obj2]).should == [obj2] + end + + it "does return subclass instances for Array subclasses" do + (ArraySpecs::MyArray[1, 2, 3] & []).should be_an_instance_of(Array) + (ArraySpecs::MyArray[1, 2, 3] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + ([] & ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + ([5, 6] & ArraySpecs::ToAryArray[1, 2, 5, 6]).should == [5, 6] + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + ([x] & [x]).should == [x] + end +end diff --git a/spec/rubyspec/core/array/join_spec.rb b/spec/rubyspec/core/array/join_spec.rb new file mode 100644 index 0000000000..c4c6277c87 --- /dev/null +++ b/spec/rubyspec/core/array/join_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/join', __FILE__) + +describe "Array#join" do + it_behaves_like :array_join_with_string_separator, :join + it_behaves_like :array_join_with_default_separator, :join + + it "does not separate elements when the passed separator is nil" do + [1, 2, 3].join(nil).should == '123' + end + + it "calls #to_str to convert the separator to a String" do + sep = mock("separator") + sep.should_receive(:to_str).and_return(", ") + [1, 2].join(sep).should == "1, 2" + end + + it "does not call #to_str on the separator if the array is empty" do + sep = mock("separator") + sep.should_not_receive(:to_str) + [].join(sep).should == "" + end + + it "raises a TypeError if the separator cannot be coerced to a String by calling #to_str" do + obj = mock("not a string") + lambda { [1, 2].join(obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed false as the separator" do + lambda { [1, 2].join(false) }.should raise_error(TypeError) + end +end + +describe "Array#join with $," do + before :each do + @before_separator = $, + end + + after :each do + $, = @before_separator + end + + it "separates elements with default separator when the passed separator is nil" do + $, = "_" + [1, 2, 3].join(nil).should == '1_2_3' + end +end diff --git a/spec/rubyspec/core/array/keep_if_spec.rb b/spec/rubyspec/core/array/keep_if_spec.rb new file mode 100644 index 0000000000..2657d5e3b6 --- /dev/null +++ b/spec/rubyspec/core/array/keep_if_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../shared/keep_if', __FILE__) + +describe "Array#keep_if" do + it "returns the same array if no changes were made" do + array = [1, 2, 3] + array.keep_if { true }.should equal(array) + end + + it_behaves_like :keep_if, :keep_if +end diff --git a/spec/rubyspec/core/array/last_spec.rb b/spec/rubyspec/core/array/last_spec.rb new file mode 100644 index 0000000000..c707e3ff7e --- /dev/null +++ b/spec/rubyspec/core/array/last_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#last" do + it "returns the last element" do + [1, 1, 1, 1, 2].last.should == 2 + end + + it "returns nil if self is empty" do + [].last.should == nil + end + + it "returns the last count elements if given a count" do + [1, 2, 3, 4, 5, 9].last(3).should == [4, 5, 9] + end + + it "returns an empty array when passed a count on an empty array" do + [].last(0).should == [] + [].last(1).should == [] + end + + it "returns an empty array when count == 0" do + [1, 2, 3, 4, 5].last(0).should == [] + end + + it "returns an array containing the last element when passed count == 1" do + [1, 2, 3, 4, 5].last(1).should == [5] + end + + it "raises an ArgumentError when count is negative" do + lambda { [1, 2].last(-1) }.should raise_error(ArgumentError) + end + + it "returns the entire array when count > length" do + [1, 2, 3, 4, 5, 9].last(10).should == [1, 2, 3, 4, 5, 9] + end + + it "returns an array which is independent to the original when passed count" do + ary = [1, 2, 3, 4, 5] + ary.last(0).replace([1,2]) + ary.should == [1, 2, 3, 4, 5] + ary.last(1).replace([1,2]) + ary.should == [1, 2, 3, 4, 5] + ary.last(6).replace([1,2]) + ary.should == [1, 2, 3, 4, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.last.should equal(empty) + + array = ArraySpecs.recursive_array + array.last.should equal(array) + end + + it "tries to convert the passed argument to an Integer usinig #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + [1, 2, 3, 4, 5].last(obj).should == [4, 5] + end + + it "raises a TypeError if the passed argument is not numeric" do + lambda { [1,2].last(nil) }.should raise_error(TypeError) + lambda { [1,2].last("a") }.should raise_error(TypeError) + + obj = mock("nonnumeric") + lambda { [1,2].last(obj) }.should raise_error(TypeError) + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[].last(0).should be_an_instance_of(Array) + ArraySpecs::MyArray[].last(2).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].last(0).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].last(1).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].last(2).should be_an_instance_of(Array) + end + + it "is not destructive" do + a = [1, 2, 3] + a.last + a.should == [1, 2, 3] + a.last(2) + a.should == [1, 2, 3] + a.last(3) + a.should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/core/array/length_spec.rb b/spec/rubyspec/core/array/length_spec.rb new file mode 100644 index 0000000000..6f4469dda5 --- /dev/null +++ b/spec/rubyspec/core/array/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Array#length" do + it_behaves_like(:array_length, :length) +end diff --git a/spec/rubyspec/core/array/map_spec.rb b/spec/rubyspec/core/array/map_spec.rb new file mode 100644 index 0000000000..c23bb4241a --- /dev/null +++ b/spec/rubyspec/core/array/map_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Array#map" do + it_behaves_like(:array_collect, :map) +end + +describe "Array#map!" do + it_behaves_like(:array_collect_b, :map!) +end diff --git a/spec/rubyspec/core/array/max_spec.rb b/spec/rubyspec/core/array/max_spec.rb new file mode 100644 index 0000000000..cf6a48c2e3 --- /dev/null +++ b/spec/rubyspec/core/array/max_spec.rb @@ -0,0 +1,112 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#max" do + it "returns nil with no values" do + [].max.should == nil + end + + it "returns only element in one element array" do + [1].max.should == 1 + end + + it "returns largest value with multiple elements" do + [1,2].max.should == 2 + [2,1].max.should == 2 + end + + describe "given a block with one argument" do + it "yields in turn the last length-1 values from the array" do + ary = [] + result = [1,2,3,4,5].max {|x| ary << x; x} + + ary.should == [2,3,4,5] + result.should == 5 + end + end +end + +# From Enumerable#max, copied for better readability +describe "Array#max" do + before :each do + @a = [2, 4, 6, 8, 10] + + @e_strs = ["333", "22", "666666", "1", "55555", "1010101010"] + @e_ints = [333, 22, 666666, 55555, 1010101010] + end + + it "max should return the maximum element" do + [18, 42].max.should == 42 + [2, 5, 3, 6, 1, 4].max.should == 6 + end + + it "returns the maximum element (basics cases)" do + [55].max.should == 55 + + [11,99].max.should == 99 + [99,11].max.should == 99 + [2, 33, 4, 11].max.should == 33 + + [1,2,3,4,5].max.should == 5 + [5,4,3,2,1].max.should == 5 + [1,4,3,5,2].max.should == 5 + [5,5,5,5,5].max.should == 5 + + ["aa","tt"].max.should == "tt" + ["tt","aa"].max.should == "tt" + ["2","33","4","11"].max.should == "4" + + @e_strs.max.should == "666666" + @e_ints.max.should == 1010101010 + end + + it "returns nil for an empty Enumerable" do + [].max.should == nil + end + + it "raises a NoMethodError for elements without #<=>" do + lambda do + [BasicObject.new, BasicObject.new].max + end.should raise_error(NoMethodError) + end + + it "raises an ArgumentError for incomparable elements" do + lambda do + [11,"22"].max + end.should raise_error(ArgumentError) + lambda do + [11,12,22,33].max{|a, b| nil} + end.should raise_error(ArgumentError) + end + + it "returns the maximum element (with block)" do + # with a block + ["2","33","4","11"].max {|a,b| a <=> b }.should == "4" + [ 2 , 33 , 4 , 11 ].max {|a,b| a <=> b }.should == 33 + + ["2","33","4","11"].max {|a,b| b <=> a }.should == "11" + [ 2 , 33 , 4 , 11 ].max {|a,b| b <=> a }.should == 2 + + @e_strs.max {|a,b| a.length <=> b.length }.should == "1010101010" + + @e_strs.max {|a,b| a <=> b }.should == "666666" + @e_strs.max {|a,b| a.to_i <=> b.to_i }.should == "1010101010" + + @e_ints.max {|a,b| a <=> b }.should == 1010101010 + @e_ints.max {|a,b| a.to_s <=> b.to_s }.should == 666666 + end + + it "returns the minimum for enumerables that contain nils" do + arr = [nil, nil, true] + arr.max { |a, b| + x = a.nil? ? 1 : a ? 0 : -1 + y = b.nil? ? 1 : b ? 0 : -1 + x <=> y + }.should == nil + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = [[1,2], [3,4,5], [6,7,8,9]] + multi.max.should == [6, 7, 8, 9] + end + +end diff --git a/spec/rubyspec/core/array/min_spec.rb b/spec/rubyspec/core/array/min_spec.rb new file mode 100644 index 0000000000..53fe4e0692 --- /dev/null +++ b/spec/rubyspec/core/array/min_spec.rb @@ -0,0 +1,117 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#min" do + it "returns nil with no values" do + [].min.should == nil + end + + it "returns only element in one element array" do + [1].min.should == 1 + end + + it "returns smallest value with multiple elements" do + [1,2].min.should == 1 + [2,1].min.should == 1 + end + + describe "given a block with one argument" do + it "yields in turn the last length-1 values from the array" do + ary = [] + result = [1,2,3,4,5].min {|x| ary << x; x} + + ary.should == [2,3,4,5] + result.should == 1 + end + end +end + +# From Enumerable#min, copied for better readability +describe "Array#min" do + before :each do + @a = [2, 4, 6, 8, 10] + + @e_strs = ["333", "22", "666666", "1", "55555", "1010101010"] + @e_ints = [ 333, 22, 666666, 55555, 1010101010] + end + + it "min should return the minimum element" do + [18, 42].min.should == 18 + [2, 5, 3, 6, 1, 4].min.should == 1 + end + + it "returns the minimum (basic cases)" do + [55].min.should == 55 + + [11,99].min.should == 11 + [99,11].min.should == 11 + [2, 33, 4, 11].min.should == 2 + + [1,2,3,4,5].min.should == 1 + [5,4,3,2,1].min.should == 1 + [4,1,3,5,2].min.should == 1 + [5,5,5,5,5].min.should == 5 + + ["aa","tt"].min.should == "aa" + ["tt","aa"].min.should == "aa" + ["2","33","4","11"].min.should == "11" + + @e_strs.min.should == "1" + @e_ints.min.should == 22 + end + + it "returns nil for an empty Enumerable" do + [].min.should be_nil + end + + it "raises a NoMethodError for elements without #<=>" do + lambda do + [BasicObject.new, BasicObject.new].min + end.should raise_error(NoMethodError) + end + + it "raises an ArgumentError for incomparable elements" do + lambda do + [11,"22"].min + end.should raise_error(ArgumentError) + lambda do + [11,12,22,33].min{|a, b| nil} + end.should raise_error(ArgumentError) + end + + it "returns the minimum when using a block rule" do + ["2","33","4","11"].min {|a,b| a <=> b }.should == "11" + [ 2 , 33 , 4 , 11 ].min {|a,b| a <=> b }.should == 2 + + ["2","33","4","11"].min {|a,b| b <=> a }.should == "4" + [ 2 , 33 , 4 , 11 ].min {|a,b| b <=> a }.should == 33 + + [ 1, 2, 3, 4 ].min {|a,b| 15 }.should == 1 + + [11,12,22,33].min{|a, b| 2 }.should == 11 + @i = -2 + [11,12,22,33].min{|a, b| @i += 1 }.should == 12 + + @e_strs.min {|a,b| a.length <=> b.length }.should == "1" + + @e_strs.min {|a,b| a <=> b }.should == "1" + @e_strs.min {|a,b| a.to_i <=> b.to_i }.should == "1" + + @e_ints.min {|a,b| a <=> b }.should == 22 + @e_ints.min {|a,b| a.to_s <=> b.to_s }.should == 1010101010 + end + + it "returns the minimum for enumerables that contain nils" do + arr = [nil, nil, true] + arr.min { |a, b| + x = a.nil? ? -1 : a ? 0 : 1 + y = b.nil? ? -1 : b ? 0 : 1 + x <=> y + }.should == nil + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = [[1,2], [3,4,5], [6,7,8,9]] + multi.min.should == [1, 2] + end + +end diff --git a/spec/rubyspec/core/array/minus_spec.rb b/spec/rubyspec/core/array/minus_spec.rb new file mode 100644 index 0000000000..5ef90385eb --- /dev/null +++ b/spec/rubyspec/core/array/minus_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#-" do + it "creates an array minus any items from other array" do + ([] - [ 1, 2, 4 ]).should == [] + ([1, 2, 4] - []).should == [1, 2, 4] + ([ 1, 2, 3, 4, 5 ] - [ 1, 2, 4 ]).should == [3, 5] + end + + it "removes multiple items on the lhs equal to one on the rhs" do + ([1, 1, 2, 2, 3, 3, 4, 5] - [1, 2, 4]).should == [3, 3, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + (empty - empty).should == [] + + ([] - ArraySpecs.recursive_array).should == [] + + array = ArraySpecs.recursive_array + (array - array).should == [] + end + + it "tries to convert the passed arguments to Arrays using #to_ary" do + obj = mock('[2,3,3,4]') + obj.should_receive(:to_ary).and_return([2, 3, 3, 4]) + ([1, 1, 2, 2, 3, 4] - obj).should == [1, 1] + end + + it "raises a TypeError if the argument cannot be coerced to an Array by calling #to_ary" do + obj = mock('not an array') + lambda { [1, 2, 3] - obj }.should raise_error(TypeError) + end + + it "does not return subclass instance for Array subclasses" do + (ArraySpecs::MyArray[1, 2, 3] - []).should be_an_instance_of(Array) + (ArraySpecs::MyArray[1, 2, 3] - ArraySpecs::MyArray[]).should be_an_instance_of(Array) + ([1, 2, 3] - ArraySpecs::MyArray[]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + ([5, 6, 7] - ArraySpecs::ToAryArray[7]).should == [5, 6] + end + + it "removes an item identified as equivalent via #hash and #eql?" do + obj1 = mock('1') + obj2 = mock('2') + obj1.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:hash).at_least(1).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(true) + + ([obj1] - [obj2]).should == [] + ([obj1, obj1, obj2, obj2] - [obj2]).should == [] + end + + it "doesn't remove an item with the same hash but not #eql?" do + obj1 = mock('1') + obj2 = mock('2') + obj1.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:hash).at_least(1).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(false) + + ([obj1] - [obj2]).should == [obj1] + ([obj1, obj1, obj2, obj2] - [obj2]).should == [obj1, obj1] + end + + it "removes an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + ([x] - [x]).should == [] + end + + it "is not destructive" do + a = [1, 2, 3] + a - [] + a.should == [1, 2, 3] + a - [1] + a.should == [1, 2, 3] + a - [1,2,3] + a.should == [1, 2, 3] + a - [:a, :b, :c] + a.should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/core/array/multiply_spec.rb b/spec/rubyspec/core/array/multiply_spec.rb new file mode 100644 index 0000000000..ecd5eba5f7 --- /dev/null +++ b/spec/rubyspec/core/array/multiply_spec.rb @@ -0,0 +1,132 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/join', __FILE__) + +describe "Array#*" do + it "tries to convert the passed argument to a String using #to_str" do + obj = mock('separator') + obj.should_receive(:to_str).and_return('::') + ([1, 2, 3, 4] * obj).should == '1::2::3::4' + end + + it "tires to convert the passed argument to an Integer using #to_int" do + obj = mock('count') + obj.should_receive(:to_int).and_return(2) + ([1, 2, 3, 4] * obj).should == [1, 2, 3, 4, 1, 2, 3, 4] + end + + it "raises a TypeError if the argument can neither be converted to a string nor an integer" do + obj = mock('not a string or integer') + lambda{ [1,2] * obj }.should raise_error(TypeError) + end + + it "converts the passed argument to a String rather than an Integer" do + obj = mock('2') + def obj.to_int() 2 end + def obj.to_str() "2" end + ([:a, :b, :c] * obj).should == "a2b2c" + end + + it "raises a TypeError is the passed argument is nil" do + lambda{ [1,2] * nil }.should raise_error(TypeError) + end + + it "raises an ArgumentError when passed 2 or more arguments" do + lambda{ [1,2].send(:*, 1, 2) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed no arguments" do + lambda{ [1,2].send(:*) }.should raise_error(ArgumentError) + end +end + +describe "Array#* with an integer" do + it "concatenates n copies of the array when passed an integer" do + ([ 1, 2, 3 ] * 0).should == [] + ([ 1, 2, 3 ] * 1).should == [1, 2, 3] + ([ 1, 2, 3 ] * 3).should == [1, 2, 3, 1, 2, 3, 1, 2, 3] + ([] * 10).should == [] + end + + it "does not return self even if the passed integer is 1" do + ary = [1, 2, 3] + (ary * 1).should_not equal(ary) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + (empty * 0).should == [] + (empty * 1).should == empty + (empty * 3).should == [empty, empty, empty] + + array = ArraySpecs.recursive_array + (array * 0).should == [] + (array * 1).should == array + end + + it "raises an ArgumentError when passed a negative integer" do + lambda { [ 1, 2, 3 ] * -1 }.should raise_error(ArgumentError) + lambda { [] * -1 }.should raise_error(ArgumentError) + end + + describe "with a subclass of Array" do + before :each do + ScratchPad.clear + + @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] + end + + it "returns a subclass instance" do + (@array * 0).should be_an_instance_of(ArraySpecs::MyArray) + (@array * 1).should be_an_instance_of(ArraySpecs::MyArray) + (@array * 2).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "does not call #initialize on the subclass instance" do + (@array * 2).should == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] + ScratchPad.recorded.should be_nil + end + end + + it "copies the taint status of the original array even if the passed count is 0" do + ary = [1, 2, 3] + ary.taint + (ary * 0).tainted?.should == true + end + + it "copies the taint status of the original array even if the array is empty" do + ary = [] + ary.taint + (ary * 3).tainted?.should == true + end + + it "copies the taint status of the original array if the passed count is not 0" do + ary = [1, 2, 3] + ary.taint + (ary * 1).tainted?.should == true + (ary * 2).tainted?.should == true + end + + it "copies the untrusted status of the original array even if the passed count is 0" do + ary = [1, 2, 3] + ary.untrust + (ary * 0).untrusted?.should == true + end + + it "copies the untrusted status of the original array even if the array is empty" do + ary = [] + ary.untrust + (ary * 3).untrusted?.should == true + end + + it "copies the untrusted status of the original array if the passed count is not 0" do + ary = [1, 2, 3] + ary.untrust + (ary * 1).untrusted?.should == true + (ary * 2).untrusted?.should == true + end +end + +describe "Array#* with a string" do + it_behaves_like :array_join_with_string_separator, :* +end diff --git a/spec/rubyspec/core/array/new_spec.rb b/spec/rubyspec/core/array/new_spec.rb new file mode 100644 index 0000000000..4d26024ff2 --- /dev/null +++ b/spec/rubyspec/core/array/new_spec.rb @@ -0,0 +1,122 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array.new" do + it "returns an instance of Array" do + Array.new.should be_an_instance_of(Array) + end + + it "returns an instance of a subclass" do + ArraySpecs::MyArray.new(1, 2).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "raises an ArgumentError if passed 3 or more arguments" do + lambda do + [1, 2].send :initialize, 1, 'x', true + end.should raise_error(ArgumentError) + lambda do + [1, 2].send(:initialize, 1, 'x', true) {} + end.should raise_error(ArgumentError) + end +end + +describe "Array.new with no arguments" do + it "returns an empty array" do + Array.new.should be_empty + end + + it "does not use the given block" do + lambda{ Array.new { raise } }.should_not raise_error + end +end + +describe "Array.new with (array)" do + it "returns an array initialized to the other array" do + b = [4, 5, 6] + Array.new(b).should == b + end + + it "does not use the given block" do + lambda{ Array.new([1, 2]) { raise } }.should_not raise_error + end + + it "calls #to_ary to convert the value to an array" do + a = mock("array") + a.should_receive(:to_ary).and_return([1, 2]) + a.should_not_receive(:to_int) + Array.new(a).should == [1, 2] + end + + it "does not call #to_ary on instances of Array or subclasses of Array" do + a = [1, 2] + a.should_not_receive(:to_ary) + Array.new(a) + end + + it "raises a TypeError if an Array type argument and a default object" do + lambda { Array.new([1, 2], 1) }.should raise_error(TypeError) + end +end + +describe "Array.new with (size, object=nil)" do + it "returns an array of size filled with object" do + obj = [3] + a = Array.new(2, obj) + a.should == [obj, obj] + a[0].should equal(obj) + a[1].should equal(obj) + + Array.new(3, 14).should == [14, 14, 14] + end + + it "returns an array of size filled with nil when object is omitted" do + Array.new(3).should == [nil, nil, nil] + end + + it "raises an ArgumentError if size is negative" do + lambda { Array.new(-1, :a) }.should raise_error(ArgumentError) + lambda { Array.new(-1) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if size is too large" do + lambda { Array.new(fixnum_max+1) }.should raise_error(ArgumentError) + end + + it "calls #to_int to convert the size argument to an Integer when object is given" do + obj = mock('1') + obj.should_receive(:to_int).and_return(1) + Array.new(obj, :a).should == [:a] + end + + it "calls #to_int to convert the size argument to an Integer when object is not given" do + obj = mock('1') + obj.should_receive(:to_int).and_return(1) + Array.new(obj).should == [nil] + end + + it "raises a TypeError if the size argument is not an Integer type" do + obj = mock('nonnumeric') + obj.stub!(:to_ary).and_return([1, 2]) + lambda{ Array.new(obj, :a) }.should raise_error(TypeError) + end + + it "yields the index of the element and sets the element to the value of the block" do + Array.new(3) { |i| i.to_s }.should == ['0', '1', '2'] + end + + it "uses the block value instead of using the default value" do + lambda { + @result = Array.new(3, :obj) { |i| i.to_s } + }.should complain(/block supersedes default value argument/) + @result.should == ['0', '1', '2'] + end + + it "returns the value passed to break" do + a = Array.new(3) do |i| + break if i == 2 + i.to_s + end + + a.should == nil + end +end diff --git a/spec/rubyspec/core/array/pack/a_spec.rb b/spec/rubyspec/core/array/pack/a_spec.rb new file mode 100644 index 0000000000..e7fbdcd179 --- /dev/null +++ b/spec/rubyspec/core/array/pack/a_spec.rb @@ -0,0 +1,59 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/string', __FILE__) + +describe "Array#pack with format 'A'" do + it_behaves_like :array_pack_basic, 'A' + it_behaves_like :array_pack_basic_non_float, 'A' + it_behaves_like :array_pack_no_platform, 'A' + it_behaves_like :array_pack_string, 'A' + + it "adds all the bytes to the output when passed the '*' modifier" do + ["abc"].pack("A*").should == "abc" + end + + it "padds the output with spaces when the count exceeds the size of the String" do + ["abc"].pack("A6").should == "abc " + end + + it "adds a space when the value is nil" do + [nil].pack("A").should == " " + end + + it "pads the output with spaces when the value is nil" do + [nil].pack("A3").should == " " + end + + it "does not pad with spaces when passed the '*' modifier and the value is nil" do + [nil].pack("A*").should == "" + end +end + +describe "Array#pack with format 'a'" do + it_behaves_like :array_pack_basic, 'a' + it_behaves_like :array_pack_basic_non_float, 'a' + it_behaves_like :array_pack_no_platform, 'a' + it_behaves_like :array_pack_string, 'a' + + it "adds all the bytes to the output when passed the '*' modifier" do + ["abc"].pack("a*").should == "abc" + end + + it "padds the output with NULL bytes when the count exceeds the size of the String" do + ["abc"].pack("a6").should == "abc\x00\x00\x00" + end + + it "adds a NULL byte when the value is nil" do + [nil].pack("a").should == "\x00" + end + + it "pads the output with NULL bytes when the value is nil" do + [nil].pack("a3").should == "\x00\x00\x00" + end + + it "does not pad with NULL bytes when passed the '*' modifier and the value is nil" do + [nil].pack("a*").should == "" + end +end diff --git a/spec/rubyspec/core/array/pack/at_spec.rb b/spec/rubyspec/core/array/pack/at_spec.rb new file mode 100644 index 0000000000..dd538e8951 --- /dev/null +++ b/spec/rubyspec/core/array/pack/at_spec.rb @@ -0,0 +1,30 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "Array#pack with format '@'" do + it_behaves_like :array_pack_basic, '@' + it_behaves_like :array_pack_basic_non_float, '@' + it_behaves_like :array_pack_no_platform, '@' + + it "moves the insertion point to the index specified by the count modifier" do + [1, 2, 3, 4, 5].pack("C4@2C").should == "\x01\x02\x05" + end + + it "does not consume any elements" do + [1, 2, 3].pack("C@3C").should == "\x01\x00\x00\x02" + end + + it "extends the string with NULL bytes if the string size is less than the count" do + [1, 2, 3].pack("@3C*").should == "\x00\x00\x00\x01\x02\x03" + end + + it "truncates the string if the string size is greater than the count" do + [1, 2, 3].pack("Cx5@2C").should == "\x01\x00\x02" + end + + it "implicitly has a count of one when no count modifier is passed" do + [1, 2, 3].pack("C*@").should == "\x01" + end +end diff --git a/spec/rubyspec/core/array/pack/b_spec.rb b/spec/rubyspec/core/array/pack/b_spec.rb new file mode 100644 index 0000000000..62294ab8d1 --- /dev/null +++ b/spec/rubyspec/core/array/pack/b_spec.rb @@ -0,0 +1,105 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/encodings', __FILE__) + +describe "Array#pack with format 'B'" do + it_behaves_like :array_pack_basic, 'B' + it_behaves_like :array_pack_basic_non_float, 'B' + it_behaves_like :array_pack_arguments, 'B' + it_behaves_like :array_pack_hex, 'B' + + it "calls #to_str to convert an Object to a String" do + obj = mock("pack H string") + obj.should_receive(:to_str).and_return("``abcdef") + [obj].pack("B*").should == "\x2a" + end + + it "encodes one bit for each character starting with the most significant bit" do + [ [["0"], "\x00"], + [["1"], "\x80"] + ].should be_computed_by(:pack, "B") + end + + it "implicitly has a count of one when not passed a count modifier" do + ["1"].pack("B").should == "\x80" + end + + it "implicitly has count equal to the string length when passed the '*' modifier" do + [ [["00101010"], "\x2a"], + [["00000000"], "\x00"], + [["11111111"], "\xff"], + [["10000000"], "\x80"], + [["00000001"], "\x01"] + ].should be_computed_by(:pack, "B*") + end + + it "encodes the least significant bit of a character other than 0 or 1" do + [ [["bbababab"], "\x2a"], + [["^&#&#^#^"], "\x2a"], + [["(()()()("], "\x2a"], + [["@@%@%@%@"], "\x2a"], + [["ppqrstuv"], "\x2a"], + [["rqtvtrqp"], "\x42"] + ].should be_computed_by(:pack, "B*") + end + + it "returns an ASCII-8BIT string" do + ["1"].pack("B").encoding.should == Encoding::ASCII_8BIT + end + + it "encodes the string as a sequence of bytes" do + ["ああああああああ"].pack("B*").should == "\xdbm\xb6" + end +end + +describe "Array#pack with format 'b'" do + it_behaves_like :array_pack_basic, 'b' + it_behaves_like :array_pack_basic_non_float, 'b' + it_behaves_like :array_pack_arguments, 'b' + it_behaves_like :array_pack_hex, 'b' + + it "calls #to_str to convert an Object to a String" do + obj = mock("pack H string") + obj.should_receive(:to_str).and_return("`abcdef`") + [obj].pack("b*").should == "\x2a" + end + + it "encodes one bit for each character starting with the least significant bit" do + [ [["0"], "\x00"], + [["1"], "\x01"] + ].should be_computed_by(:pack, "b") + end + + it "implicitly has a count of one when not passed a count modifier" do + ["1"].pack("b").should == "\x01" + end + + it "implicitly has count equal to the string length when passed the '*' modifier" do + [ [["0101010"], "\x2a"], + [["00000000"], "\x00"], + [["11111111"], "\xff"], + [["10000000"], "\x01"], + [["00000001"], "\x80"] + ].should be_computed_by(:pack, "b*") + end + + it "encodes the least significant bit of a character other than 0 or 1" do + [ [["bababab"], "\x2a"], + [["&#&#^#^"], "\x2a"], + [["()()()("], "\x2a"], + [["@%@%@%@"], "\x2a"], + [["pqrstuv"], "\x2a"], + [["qrtrtvs"], "\x41"] + ].should be_computed_by(:pack, "b*") + end + + it "returns an ASCII-8BIT string" do + ["1"].pack("b").encoding.should == Encoding::ASCII_8BIT + end + + it "encodes the string as a sequence of bytes" do + ["ああああああああ"].pack("b*").should == "\xdb\xb6m" + end +end diff --git a/spec/rubyspec/core/array/pack/c_spec.rb b/spec/rubyspec/core/array/pack/c_spec.rb new file mode 100644 index 0000000000..74afa72f56 --- /dev/null +++ b/spec/rubyspec/core/array/pack/c_spec.rb @@ -0,0 +1,75 @@ +# -*- encoding: ascii-8bit -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) + +describe :array_pack_8bit, shared: true do + it "encodes the least significant eight bits of a positive number" do + [ [[49], "1"], + [[0b11111111], "\xFF"], + [[0b100000000], "\x00"], + [[0b100000001], "\x01"] + ].should be_computed_by(:pack, pack_format) + end + + it "encodes the least significant eight bits of a negative number" do + [ [[-1], "\xFF"], + [[-0b10000000], "\x80"], + [[-0b11111111], "\x01"], + [[-0b100000000], "\x00"], + [[-0b100000001], "\xFF"] + ].should be_computed_by(:pack, pack_format) + end + + it "encodes a Float truncated as an Integer" do + [ [[5.2], "\x05"], + [[5.8], "\x05"] + ].should be_computed_by(:pack, pack_format) + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(5) + [obj].pack(pack_format).should == "\x05" + end + + it "encodes the number of array elements specified by the count modifier" do + [ [[1, 2, 3], pack_format(3), "\x01\x02\x03"], + [[1, 2, 3], pack_format(2) + pack_format(1), "\x01\x02\x03"] + ].should be_computed_by(:pack) + end + + it "encodes all remaining elements when passed the '*' modifier" do + [1, 2, 3, 4, 5].pack(pack_format('*')).should == "\x01\x02\x03\x04\x05" + end + + it "ignores NULL bytes between directives" do + [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02" + end + + it "ignores spaces between directives" do + [1, 2, 3].pack(pack_format(' ', 2)).should == "\x01\x02" + end +end + +describe "Array#pack with format 'C'" do + it_behaves_like :array_pack_basic, 'C' + it_behaves_like :array_pack_basic_non_float, 'C' + it_behaves_like :array_pack_8bit, 'C' + it_behaves_like :array_pack_arguments, 'C' + it_behaves_like :array_pack_numeric_basic, 'C' + it_behaves_like :array_pack_integer, 'C' + it_behaves_like :array_pack_no_platform, 'C' +end + +describe "Array#pack with format 'c'" do + it_behaves_like :array_pack_basic, 'c' + it_behaves_like :array_pack_basic_non_float, 'c' + it_behaves_like :array_pack_8bit, 'c' + it_behaves_like :array_pack_arguments, 'c' + it_behaves_like :array_pack_numeric_basic, 'c' + it_behaves_like :array_pack_integer, 'c' + it_behaves_like :array_pack_no_platform, 'c' +end diff --git a/spec/rubyspec/core/array/pack/comment_spec.rb b/spec/rubyspec/core/array/pack/comment_spec.rb new file mode 100644 index 0000000000..00c5fb6ecd --- /dev/null +++ b/spec/rubyspec/core/array/pack/comment_spec.rb @@ -0,0 +1,25 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Array#pack" do + it "ignores directives text from '#' to the first newline" do + [1, 2, 3].pack("c#this is a comment\nc").should == "\x01\x02" + end + + it "ignores directives text from '#' to the end if no newline is present" do + [1, 2, 3].pack("c#this is a comment c").should == "\x01" + end + + it "ignores comments at the start of the directives string" do + [1, 2, 3].pack("#this is a comment\nc").should == "\x01" + end + + it "ignores the entire directive string if it is a comment" do + [1, 2, 3].pack("#this is a comment").should == "" + end + + it "ignores multiple comments" do + [1, 2, 3].pack("c#comment\nc#comment\nc#c").should == "\x01\x02\x03" + end +end diff --git a/spec/rubyspec/core/array/pack/d_spec.rb b/spec/rubyspec/core/array/pack/d_spec.rb new file mode 100644 index 0000000000..40f28d1e52 --- /dev/null +++ b/spec/rubyspec/core/array/pack/d_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "Array#pack with format 'D'" do + it_behaves_like :array_pack_basic, 'D' + it_behaves_like :array_pack_basic_float, 'D' + it_behaves_like :array_pack_arguments, 'D' + it_behaves_like :array_pack_no_platform, 'D' + it_behaves_like :array_pack_numeric_basic, 'D' + it_behaves_like :array_pack_float, 'D' + + little_endian do + it_behaves_like :array_pack_double_le, 'D' + end + + big_endian do + it_behaves_like :array_pack_double_be, 'D' + end +end + +describe "Array#pack with format 'd'" do + it_behaves_like :array_pack_basic, 'd' + it_behaves_like :array_pack_basic_float, 'd' + it_behaves_like :array_pack_arguments, 'd' + it_behaves_like :array_pack_no_platform, 'd' + it_behaves_like :array_pack_numeric_basic, 'd' + it_behaves_like :array_pack_float, 'd' + + little_endian do + it_behaves_like :array_pack_double_le, 'd' + end + + big_endian do + it_behaves_like :array_pack_double_be, 'd' + end +end diff --git a/spec/rubyspec/core/array/pack/e_spec.rb b/spec/rubyspec/core/array/pack/e_spec.rb new file mode 100644 index 0000000000..9c6a1b5485 --- /dev/null +++ b/spec/rubyspec/core/array/pack/e_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "Array#pack with format 'E'" do + it_behaves_like :array_pack_basic, 'E' + it_behaves_like :array_pack_basic_float, 'E' + it_behaves_like :array_pack_arguments, 'E' + it_behaves_like :array_pack_no_platform, 'E' + it_behaves_like :array_pack_numeric_basic, 'E' + it_behaves_like :array_pack_float, 'E' + it_behaves_like :array_pack_double_le, 'E' +end + +describe "Array#pack with format 'e'" do + it_behaves_like :array_pack_basic, 'e' + it_behaves_like :array_pack_basic_float, 'e' + it_behaves_like :array_pack_arguments, 'e' + it_behaves_like :array_pack_no_platform, 'e' + it_behaves_like :array_pack_numeric_basic, 'e' + it_behaves_like :array_pack_float, 'e' + it_behaves_like :array_pack_float_le, 'e' +end diff --git a/spec/rubyspec/core/array/pack/empty_spec.rb b/spec/rubyspec/core/array/pack/empty_spec.rb new file mode 100644 index 0000000000..701e20b0af --- /dev/null +++ b/spec/rubyspec/core/array/pack/empty_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Array#pack with empty format" do + it "returns an empty String" do + [1, 2, 3].pack("").should == "" + end + + it "returns a String with US-ASCII encoding" do + [1, 2, 3].pack("").encoding.should == Encoding::US_ASCII + end +end diff --git a/spec/rubyspec/core/array/pack/f_spec.rb b/spec/rubyspec/core/array/pack/f_spec.rb new file mode 100644 index 0000000000..94ce57f34d --- /dev/null +++ b/spec/rubyspec/core/array/pack/f_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "Array#pack with format 'F'" do + it_behaves_like :array_pack_basic, 'F' + it_behaves_like :array_pack_basic_float, 'F' + it_behaves_like :array_pack_arguments, 'F' + it_behaves_like :array_pack_no_platform, 'F' + it_behaves_like :array_pack_numeric_basic, 'F' + it_behaves_like :array_pack_float, 'F' + + little_endian do + it_behaves_like :array_pack_float_le, 'F' + end + + big_endian do + it_behaves_like :array_pack_float_be, 'F' + end +end + +describe "Array#pack with format 'f'" do + it_behaves_like :array_pack_basic, 'f' + it_behaves_like :array_pack_basic_float, 'f' + it_behaves_like :array_pack_arguments, 'f' + it_behaves_like :array_pack_no_platform, 'f' + it_behaves_like :array_pack_numeric_basic, 'f' + it_behaves_like :array_pack_float, 'f' + + little_endian do + it_behaves_like :array_pack_float_le, 'f' + end + + big_endian do + it_behaves_like :array_pack_float_be, 'f' + end +end diff --git a/spec/rubyspec/core/array/pack/g_spec.rb b/spec/rubyspec/core/array/pack/g_spec.rb new file mode 100644 index 0000000000..a0a902ebbe --- /dev/null +++ b/spec/rubyspec/core/array/pack/g_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "Array#pack with format 'G'" do + it_behaves_like :array_pack_basic, 'G' + it_behaves_like :array_pack_basic_float, 'G' + it_behaves_like :array_pack_arguments, 'G' + it_behaves_like :array_pack_no_platform, 'G' + it_behaves_like :array_pack_numeric_basic, 'G' + it_behaves_like :array_pack_float, 'G' + it_behaves_like :array_pack_double_be, 'G' +end + +describe "Array#pack with format 'g'" do + it_behaves_like :array_pack_basic, 'g' + it_behaves_like :array_pack_basic_float, 'g' + it_behaves_like :array_pack_arguments, 'g' + it_behaves_like :array_pack_no_platform, 'g' + it_behaves_like :array_pack_numeric_basic, 'g' + it_behaves_like :array_pack_float, 'g' + it_behaves_like :array_pack_float_be, 'g' +end diff --git a/spec/rubyspec/core/array/pack/h_spec.rb b/spec/rubyspec/core/array/pack/h_spec.rb new file mode 100644 index 0000000000..2412bf57c9 --- /dev/null +++ b/spec/rubyspec/core/array/pack/h_spec.rb @@ -0,0 +1,197 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/encodings', __FILE__) + +describe "Array#pack with format 'H'" do + it_behaves_like :array_pack_basic, 'H' + it_behaves_like :array_pack_basic_non_float, 'H' + it_behaves_like :array_pack_arguments, 'H' + it_behaves_like :array_pack_hex, 'H' + + it "calls #to_str to convert an Object to a String" do + obj = mock("pack H string") + obj.should_receive(:to_str).and_return("a") + [obj].pack("H").should == "\xa0" + end + + it "encodes the first character as the most significant nibble when passed no count modifier" do + ["ab"].pack("H").should == "\xa0" + end + + it "implicitly has count equal to the string length when passed the '*' modifier" do + ["deadbeef"].pack("H*").should == "\xde\xad\xbe\xef" + end + + it "encodes count nibbles when passed a count modifier exceeding the string length" do + ["ab"].pack('H8').should == "\xab\x00\x00\x00" + end + + it "encodes the first character as the most significant nibble of a hex value" do + [ [["0"], "\x00"], + [["1"], "\x10"], + [["2"], "\x20"], + [["3"], "\x30"], + [["4"], "\x40"], + [["5"], "\x50"], + [["6"], "\x60"], + [["7"], "\x70"], + [["8"], "\x80"], + [["9"], "\x90"], + [["a"], "\xa0"], + [["b"], "\xb0"], + [["c"], "\xc0"], + [["d"], "\xd0"], + [["e"], "\xe0"], + [["f"], "\xf0"], + [["A"], "\xa0"], + [["B"], "\xb0"], + [["C"], "\xc0"], + [["D"], "\xd0"], + [["E"], "\xe0"], + [["F"], "\xf0"] + ].should be_computed_by(:pack, "H") + end + + it "encodes the second character as the least significant nibble of a hex value" do + [ [["00"], "\x00"], + [["01"], "\x01"], + [["02"], "\x02"], + [["03"], "\x03"], + [["04"], "\x04"], + [["05"], "\x05"], + [["06"], "\x06"], + [["07"], "\x07"], + [["08"], "\x08"], + [["09"], "\x09"], + [["0a"], "\x0a"], + [["0b"], "\x0b"], + [["0c"], "\x0c"], + [["0d"], "\x0d"], + [["0e"], "\x0e"], + [["0f"], "\x0f"], + [["0A"], "\x0a"], + [["0B"], "\x0b"], + [["0C"], "\x0c"], + [["0D"], "\x0d"], + [["0E"], "\x0e"], + [["0F"], "\x0f"] + ].should be_computed_by(:pack, "H2") + end + + it "encodes the least significant nibble of a non alphanumeric character as the most significant nibble of the hex value" do + [ [["^"], "\xe0"], + [["*"], "\xa0"], + [["#"], "\x30"], + [["["], "\xb0"], + [["]"], "\xd0"], + [["@"], "\x00"], + [["!"], "\x10"], + [["H"], "\x10"], + [["O"], "\x80"], + [["T"], "\xd0"], + [["Z"], "\x30"], + ].should be_computed_by(:pack, "H") + end + + it "returns an ASCII-8BIT string" do + ["41"].pack("H").encoding.should == Encoding::ASCII_8BIT + end +end + +describe "Array#pack with format 'h'" do + it_behaves_like :array_pack_basic, 'h' + it_behaves_like :array_pack_basic_non_float, 'h' + it_behaves_like :array_pack_arguments, 'h' + it_behaves_like :array_pack_hex, 'h' + + it "calls #to_str to convert an Object to a String" do + obj = mock("pack H string") + obj.should_receive(:to_str).and_return("a") + [obj].pack("h").should == "\x0a" + end + + it "encodes the first character as the least significant nibble when passed no count modifier" do + ["ab"].pack("h").should == "\x0a" + end + + it "implicitly has count equal to the string length when passed the '*' modifier" do + ["deadbeef"].pack("h*").should == "\xed\xda\xeb\xfe" + end + + it "encodes count nibbles when passed a count modifier exceeding the string length" do + ["ab"].pack('h8').should == "\xba\x00\x00\x00" + end + + it "encodes the first character as the least significant nibble of a hex value" do + [ [["0"], "\x00"], + [["1"], "\x01"], + [["2"], "\x02"], + [["3"], "\x03"], + [["4"], "\x04"], + [["5"], "\x05"], + [["6"], "\x06"], + [["7"], "\x07"], + [["8"], "\x08"], + [["9"], "\x09"], + [["a"], "\x0a"], + [["b"], "\x0b"], + [["c"], "\x0c"], + [["d"], "\x0d"], + [["e"], "\x0e"], + [["f"], "\x0f"], + [["A"], "\x0a"], + [["B"], "\x0b"], + [["C"], "\x0c"], + [["D"], "\x0d"], + [["E"], "\x0e"], + [["F"], "\x0f"] + ].should be_computed_by(:pack, "h") + end + + it "encodes the second character as the most significant nibble of a hex value" do + [ [["00"], "\x00"], + [["01"], "\x10"], + [["02"], "\x20"], + [["03"], "\x30"], + [["04"], "\x40"], + [["05"], "\x50"], + [["06"], "\x60"], + [["07"], "\x70"], + [["08"], "\x80"], + [["09"], "\x90"], + [["0a"], "\xa0"], + [["0b"], "\xb0"], + [["0c"], "\xc0"], + [["0d"], "\xd0"], + [["0e"], "\xe0"], + [["0f"], "\xf0"], + [["0A"], "\xa0"], + [["0B"], "\xb0"], + [["0C"], "\xc0"], + [["0D"], "\xd0"], + [["0E"], "\xe0"], + [["0F"], "\xf0"] + ].should be_computed_by(:pack, "h2") + end + + it "encodes the least significant nibble of a non alphanumeric character as the least significant nibble of the hex value" do + [ [["^"], "\x0e"], + [["*"], "\x0a"], + [["#"], "\x03"], + [["["], "\x0b"], + [["]"], "\x0d"], + [["@"], "\x00"], + [["!"], "\x01"], + [["H"], "\x01"], + [["O"], "\x08"], + [["T"], "\x0d"], + [["Z"], "\x03"], + ].should be_computed_by(:pack, "h") + end + + it "returns an ASCII-8BIT string" do + ["41"].pack("h").encoding.should == Encoding::ASCII_8BIT + end +end diff --git a/spec/rubyspec/core/array/pack/i_spec.rb b/spec/rubyspec/core/array/pack/i_spec.rb new file mode 100644 index 0000000000..c22f367a65 --- /dev/null +++ b/spec/rubyspec/core/array/pack/i_spec.rb @@ -0,0 +1,133 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "Array#pack with format 'I'" do + it_behaves_like :array_pack_basic, 'I' + it_behaves_like :array_pack_basic_non_float, 'I' + it_behaves_like :array_pack_arguments, 'I' + it_behaves_like :array_pack_numeric_basic, 'I' + it_behaves_like :array_pack_integer, 'I' +end + +describe "Array#pack with format 'i'" do + it_behaves_like :array_pack_basic, 'i' + it_behaves_like :array_pack_basic_non_float, 'i' + it_behaves_like :array_pack_arguments, 'i' + it_behaves_like :array_pack_numeric_basic, 'i' + it_behaves_like :array_pack_integer, 'i' +end + +describe "Array#pack with format 'I'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_32bit_le, 'I<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'I<_' + it_behaves_like :array_pack_32bit_le, 'I_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'I'" do + it_behaves_like :array_pack_32bit_be, 'I>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :array_pack_32bit_be, 'I>_' + it_behaves_like :array_pack_32bit_be, 'I_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'I>!' + it_behaves_like :array_pack_32bit_be, 'I!>' + end +end + +describe "Array#pack with format 'i'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_32bit_le, 'i<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'i<_' + it_behaves_like :array_pack_32bit_le, 'i_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'i'" do + it_behaves_like :array_pack_32bit_be, 'i>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :array_pack_32bit_be, 'i>_' + it_behaves_like :array_pack_32bit_be, 'i_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'i>!' + it_behaves_like :array_pack_32bit_be, 'i!>' + end +end + +little_endian do + describe "Array#pack with format 'I'" do + it_behaves_like :array_pack_32bit_le, 'I' + end + + describe "Array#pack with format 'I' with modifier '_'" do + it_behaves_like :array_pack_32bit_le_platform, 'I_' + end + + describe "Array#pack with format 'I' with modifier '!'" do + it_behaves_like :array_pack_32bit_le_platform, 'I!' + end + + describe "Array#pack with format 'i'" do + it_behaves_like :array_pack_32bit_le, 'i' + end + + describe "Array#pack with format 'i' with modifier '_'" do + it_behaves_like :array_pack_32bit_le_platform, 'i_' + end + + describe "Array#pack with format 'i' with modifier '!'" do + it_behaves_like :array_pack_32bit_le_platform, 'i!' + end +end + +big_endian do + describe "Array#pack with format 'I'" do + it_behaves_like :array_pack_32bit_be, 'I' + end + + describe "Array#pack with format 'I' with modifier '_'" do + it_behaves_like :array_pack_32bit_be_platform, 'I_' + end + + describe "Array#pack with format 'I' with modifier '!'" do + it_behaves_like :array_pack_32bit_be_platform, 'I!' + end + + describe "Array#pack with format 'i'" do + it_behaves_like :array_pack_32bit_be, 'i' + end + + describe "Array#pack with format 'i' with modifier '_'" do + it_behaves_like :array_pack_32bit_be_platform, 'i_' + end + + describe "Array#pack with format 'i' with modifier '!'" do + it_behaves_like :array_pack_32bit_be_platform, 'i!' + end +end diff --git a/spec/rubyspec/core/array/pack/j_spec.rb b/spec/rubyspec/core/array/pack/j_spec.rb new file mode 100644 index 0000000000..6c68a6e8df --- /dev/null +++ b/spec/rubyspec/core/array/pack/j_spec.rb @@ -0,0 +1,222 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +ruby_version_is '2.3' do + # To handle the special case of x64-mingw32 + pointer_size = RUBY_PLATFORM =~ /\bx64\b/ ? 64 : 1.size * 8 + + if pointer_size == 64 then + describe "Array#pack with format 'J'" do + it_behaves_like :array_pack_basic, 'J' + it_behaves_like :array_pack_basic_non_float, 'J' + it_behaves_like :array_pack_arguments, 'J' + it_behaves_like :array_pack_numeric_basic, 'J' + it_behaves_like :array_pack_integer, 'J' + end + + describe "Array#pack with format 'j'" do + it_behaves_like :array_pack_basic, 'j' + it_behaves_like :array_pack_basic_non_float, 'j' + it_behaves_like :array_pack_arguments, 'j' + it_behaves_like :array_pack_numeric_basic, 'j' + it_behaves_like :array_pack_integer, 'j' + end + + little_endian do + describe "Array#pack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_64bit_le, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_64bit_le, 'J!' + end + end + + describe "Array#pack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_64bit_le, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_64bit_le, 'j!' + end + end + end + + big_endian do + describe "Array#pack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_64bit_be, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_64bit_be, 'J!' + end + end + + describe "Array#pack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_64bit_be, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_64bit_be, 'j!' + end + end + end + + describe "Array#pack with format 'J'" do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_64bit_le, 'J<_' + it_behaves_like :array_pack_64bit_le, 'J_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_64bit_le, 'J' and '_'" do + it_behaves_like :array_pack_64bit_be, 'J>_' + it_behaves_like :array_pack_64bit_be, 'J_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_64bit_be, 'J>!' + it_behaves_like :array_pack_64bit_be, 'J!>' + end + end + + describe "Array#pack with format 'j'" do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_64bit_le, 'j<_' + it_behaves_like :array_pack_64bit_le, 'j_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_64bit_le, 'j' and '_'" do + it_behaves_like :array_pack_64bit_be, 'j>_' + it_behaves_like :array_pack_64bit_be, 'j_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_64bit_be, 'j>!' + it_behaves_like :array_pack_64bit_be, 'j!>' + end + end + end + + if pointer_size == 32 then + describe "Array#pack with format 'J'" do + it_behaves_like :array_pack_basic, 'J' + it_behaves_like :array_pack_basic_non_float, 'J' + it_behaves_like :array_pack_arguments, 'J' + it_behaves_like :array_pack_numeric_basic, 'J' + it_behaves_like :array_pack_integer, 'J' + end + + describe "Array#pack with format 'j'" do + it_behaves_like :array_pack_basic, 'j' + it_behaves_like :array_pack_basic_non_float, 'j' + it_behaves_like :array_pack_arguments, 'j' + it_behaves_like :array_pack_numeric_basic, 'j' + it_behaves_like :array_pack_integer, 'j' + end + + big_endian do + describe "Array#pack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_32bit_be, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_32bit_be, 'J!' + end + end + + describe "Array#pack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_32bit_be, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_32bit_be, 'j!' + end + end + end + + little_endian do + describe "Array#pack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_32bit_le, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_32bit_le, 'J!' + end + end + + describe "Array#pack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :array_pack_32bit_le, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :array_pack_32bit_le, 'j!' + end + end + end + + describe "Array#pack with format 'J'" do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'J<_' + it_behaves_like :array_pack_32bit_le, 'J_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'J' and '_'" do + it_behaves_like :array_pack_32bit_be, 'J>_' + it_behaves_like :array_pack_32bit_be, 'J_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'J>!' + it_behaves_like :array_pack_32bit_be, 'J!>' + end + end + + describe "Array#pack with format 'j'" do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'j<_' + it_behaves_like :array_pack_32bit_le, 'j_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'j' and '_'" do + it_behaves_like :array_pack_32bit_be, 'j>_' + it_behaves_like :array_pack_32bit_be, 'j_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'j>!' + it_behaves_like :array_pack_32bit_be, 'j!>' + end + end + end +end diff --git a/spec/rubyspec/core/array/pack/l_spec.rb b/spec/rubyspec/core/array/pack/l_spec.rb new file mode 100644 index 0000000000..8066b23e90 --- /dev/null +++ b/spec/rubyspec/core/array/pack/l_spec.rb @@ -0,0 +1,309 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "Array#pack with format 'L'" do + it_behaves_like :array_pack_basic, 'L' + it_behaves_like :array_pack_basic_non_float, 'L' + it_behaves_like :array_pack_arguments, 'L' + it_behaves_like :array_pack_numeric_basic, 'L' + it_behaves_like :array_pack_integer, 'L' +end + +describe "Array#pack with format 'l'" do + it_behaves_like :array_pack_basic, 'l' + it_behaves_like :array_pack_basic_non_float, 'l' + it_behaves_like :array_pack_arguments, 'l' + it_behaves_like :array_pack_numeric_basic, 'l' + it_behaves_like :array_pack_integer, 'l' +end + +describe "Array#pack with format 'L'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_32bit_le, 'L<' + end + + describe "with modifier '>'" do + it_behaves_like :array_pack_32bit_be, 'L>' + end + + platform_is wordsize: 32 do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'L<_' + it_behaves_like :array_pack_32bit_le, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'L' and '_'" do + it_behaves_like :array_pack_32bit_be, 'L>_' + it_behaves_like :array_pack_32bit_be, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'L>!' + it_behaves_like :array_pack_32bit_be, 'L!>' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_64bit_le, 'L<_' + it_behaves_like :array_pack_64bit_le, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_64bit_le, 'L' and '_'" do + it_behaves_like :array_pack_64bit_be, 'L>_' + it_behaves_like :array_pack_64bit_be, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_64bit_be, 'L>!' + it_behaves_like :array_pack_64bit_be, 'L!>' + end + end + + platform_is :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'L<_' + it_behaves_like :array_pack_32bit_le, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'L' and '_'" do + it_behaves_like :array_pack_32bit_be, 'L>_' + it_behaves_like :array_pack_32bit_be, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'L>!' + it_behaves_like :array_pack_32bit_be, 'L!>' + end + end + end +end + +describe "Array#pack with format 'l'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_32bit_le, 'l<' + end + + describe "with modifier '>'" do + it_behaves_like :array_pack_32bit_be, 'l>' + end + + platform_is wordsize: 32 do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'l<_' + it_behaves_like :array_pack_32bit_le, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'l' and '_'" do + it_behaves_like :array_pack_32bit_be, 'l>_' + it_behaves_like :array_pack_32bit_be, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'l>!' + it_behaves_like :array_pack_32bit_be, 'l!>' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_64bit_le, 'l<_' + it_behaves_like :array_pack_64bit_le, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_64bit_le, 'l' and '_'" do + it_behaves_like :array_pack_64bit_be, 'l>_' + it_behaves_like :array_pack_64bit_be, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_64bit_be, 'l>!' + it_behaves_like :array_pack_64bit_be, 'l!>' + end + end + + platform_is :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_32bit_le, 'l<_' + it_behaves_like :array_pack_32bit_le, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_32bit_le, 'l' and '_'" do + it_behaves_like :array_pack_32bit_be, 'l>_' + it_behaves_like :array_pack_32bit_be, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_32bit_be, 'l>!' + it_behaves_like :array_pack_32bit_be, 'l!>' + end + end + end +end + +little_endian do + describe "Array#pack with format 'L'" do + it_behaves_like :array_pack_32bit_le, 'L' + end + + describe "Array#pack with format 'l'" do + it_behaves_like :array_pack_32bit_le, 'l' + end + + platform_is wordsize: 32 do + describe "Array#pack with format 'L' with modifier '_'" do + it_behaves_like :array_pack_32bit_le, 'L_' + end + + describe "Array#pack with format 'L' with modifier '!'" do + it_behaves_like :array_pack_32bit_le, 'L!' + end + + describe "Array#pack with format 'l' with modifier '_'" do + it_behaves_like :array_pack_32bit_le, 'l_' + end + + describe "Array#pack with format 'l' with modifier '!'" do + it_behaves_like :array_pack_32bit_le, 'l!' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "Array#pack with format 'L' with modifier '_'" do + it_behaves_like :array_pack_64bit_le, 'L_' + end + + describe "Array#pack with format 'L' with modifier '!'" do + it_behaves_like :array_pack_64bit_le, 'L!' + end + + describe "Array#pack with format 'l' with modifier '_'" do + it_behaves_like :array_pack_64bit_le, 'l_' + end + + describe "Array#pack with format 'l' with modifier '!'" do + it_behaves_like :array_pack_64bit_le, 'l!' + end + end + + platform_is :mingw32 do + describe "Array#pack with format 'L' with modifier '_'" do + it_behaves_like :array_pack_32bit_le, 'L_' + end + + describe "Array#pack with format 'L' with modifier '!'" do + it_behaves_like :array_pack_32bit_le, 'L!' + end + + describe "Array#pack with format 'l' with modifier '_'" do + it_behaves_like :array_pack_32bit_le, 'l_' + end + + describe "Array#pack with format 'l' with modifier '!'" do + it_behaves_like :array_pack_32bit_le, 'l!' + end + end + end +end + +big_endian do + describe "Array#pack with format 'L'" do + it_behaves_like :array_pack_32bit_be, 'L' + end + + describe "Array#pack with format 'l'" do + it_behaves_like :array_pack_32bit_be, 'l' + end + + platform_is wordsize: 32 do + describe "Array#pack with format 'L' with modifier '_'" do + it_behaves_like :array_pack_32bit_be, 'L_' + end + + describe "Array#pack with format 'L' with modifier '!'" do + it_behaves_like :array_pack_32bit_be, 'L!' + end + + describe "Array#pack with format 'l' with modifier '_'" do + it_behaves_like :array_pack_32bit_be, 'l_' + end + + describe "Array#pack with format 'l' with modifier '!'" do + it_behaves_like :array_pack_32bit_be, 'l!' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "Array#pack with format 'L' with modifier '_'" do + it_behaves_like :array_pack_64bit_be, 'L_' + end + + describe "Array#pack with format 'L' with modifier '!'" do + it_behaves_like :array_pack_64bit_be, 'L!' + end + + describe "Array#pack with format 'l' with modifier '_'" do + it_behaves_like :array_pack_64bit_be, 'l_' + end + + describe "Array#pack with format 'l' with modifier '!'" do + it_behaves_like :array_pack_64bit_be, 'l!' + end + end + + platform_is :mingw32 do + describe "Array#pack with format 'L' with modifier '_'" do + it_behaves_like :array_pack_32bit_be, 'L_' + end + + describe "Array#pack with format 'L' with modifier '!'" do + it_behaves_like :array_pack_32bit_be, 'L!' + end + + describe "Array#pack with format 'l' with modifier '_'" do + it_behaves_like :array_pack_32bit_be, 'l_' + end + + describe "Array#pack with format 'l' with modifier '!'" do + it_behaves_like :array_pack_32bit_be, 'l!' + end + end + end +end diff --git a/spec/rubyspec/core/array/pack/m_spec.rb b/spec/rubyspec/core/array/pack/m_spec.rb new file mode 100644 index 0000000000..36d996cba6 --- /dev/null +++ b/spec/rubyspec/core/array/pack/m_spec.rb @@ -0,0 +1,306 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "Array#pack with format 'M'" do + it_behaves_like :array_pack_basic, 'M' + it_behaves_like :array_pack_basic_non_float, 'M' + it_behaves_like :array_pack_arguments, 'M' + + it "encodes an empty string as an empty string" do + [""].pack("M").should == "" + end + + it "encodes nil as an empty string" do + [nil].pack("M").should == "" + end + + it "appends a soft line break at the end of an encoded string" do + ["a"].pack("M").should == "a=\n" + end + + it "does not append a soft break if the string ends with a newline" do + ["a\n"].pack("M").should == "a\n" + end + + it "encodes one element for each directive" do + ["a", "b", "c"].pack("MM").should == "a=\nb=\n" + end + + it "encodes byte values 33..60 directly" do + [ [["!\"\#$%&'()*+,-./"], "!\"\#$%&'()*+,-./=\n"], + [["0123456789"], "0123456789=\n"], + [[":;<"], ":;<=\n"] + ].should be_computed_by(:pack, "M") + end + + it "encodes byte values 62..126 directly" do + [ [[">?@"], ">?@=\n"], + [["ABCDEFGHIJKLMNOPQRSTUVWXYZ"], "ABCDEFGHIJKLMNOPQRSTUVWXYZ=\n"], + [["[\\]^_`"], "[\\]^_`=\n"], + [["abcdefghijklmnopqrstuvwxyz"], "abcdefghijklmnopqrstuvwxyz=\n"], + [["{|}~"], "{|}~=\n"] + ].should be_computed_by(:pack, "M") + end + + it "encodes an '=' character in hex format" do + ["="].pack("M").should == "=3D=\n" + end + + it "encodes an embedded space directly" do + ["a b"].pack("M").should == "a b=\n" + end + + it "encodes a space at the end of the string directly" do + ["a "].pack("M").should == "a =\n" + end + + it "encodes an embedded tab directly" do + ["a\tb"].pack("M").should == "a\tb=\n" + end + + it "encodes a tab at the end of the string directly" do + ["a\t"].pack("M").should == "a\t=\n" + end + + it "encodes an embedded newline directly" do + ["a\nb"].pack("M").should == "a\nb=\n" + end + + it "encodes 0..31 except tab and newline in hex format" do + [ [["\x00\x01\x02\x03\x04\x05\x06"], "=00=01=02=03=04=05=06=\n"], + [["\a\b\v\f\r"], "=07=08=0B=0C=0D=\n"], + [["\x0e\x0f\x10\x11\x12\x13\x14"], "=0E=0F=10=11=12=13=14=\n"], + [["\x15\x16\x17\x18\x19\x1a"], "=15=16=17=18=19=1A=\n"], + [["\e"], "=1B=\n"], + [["\x1c\x1d\x1e\x1f"], "=1C=1D=1E=1F=\n"] + ].should be_computed_by(:pack, "M") + end + + it "encodes a tab followed by a newline with an encoded newline" do + ["\t\n"].pack("M").should == "\t=\n\n" + end + + it "encodes 127..255 in hex format" do + [ [["\x7f\x80\x81\x82\x83\x84\x85\x86"], "=7F=80=81=82=83=84=85=86=\n"], + [["\x87\x88\x89\x8a\x8b\x8c\x8d\x8e"], "=87=88=89=8A=8B=8C=8D=8E=\n"], + [["\x8f\x90\x91\x92\x93\x94\x95\x96"], "=8F=90=91=92=93=94=95=96=\n"], + [["\x97\x98\x99\x9a\x9b\x9c\x9d\x9e"], "=97=98=99=9A=9B=9C=9D=9E=\n"], + [["\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6"], "=9F=A0=A1=A2=A3=A4=A5=A6=\n"], + [["\xa7\xa8\xa9\xaa\xab\xac\xad\xae"], "=A7=A8=A9=AA=AB=AC=AD=AE=\n"], + [["\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6"], "=AF=B0=B1=B2=B3=B4=B5=B6=\n"], + [["\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe"], "=B7=B8=B9=BA=BB=BC=BD=BE=\n"], + [["\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"], "=BF=C0=C1=C2=C3=C4=C5=C6=\n"], + [["\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce"], "=C7=C8=C9=CA=CB=CC=CD=CE=\n"], + [["\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6"], "=CF=D0=D1=D2=D3=D4=D5=D6=\n"], + [["\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde"], "=D7=D8=D9=DA=DB=DC=DD=DE=\n"], + [["\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6"], "=DF=E0=E1=E2=E3=E4=E5=E6=\n"], + [["\xe7\xe8\xe9\xea\xeb\xec\xed\xee"], "=E7=E8=E9=EA=EB=EC=ED=EE=\n"], + [["\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6"], "=EF=F0=F1=F2=F3=F4=F5=F6=\n"], + [["\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"], "=F7=F8=F9=FA=FB=FC=FD=FE=\n"], + [["\xff"], "=FF=\n"] + ].should be_computed_by(:pack, "M") + end + + it "emits a soft line break when the output exceeds 72 characters when passed '*', 0, 1, or no count modifier" do + s1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + r1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=\na=\n" + s2 = "\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19\x19" + r2 = "=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=19=\n=19=\n" + s3 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x15a" + r3 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=15=\na=\n" + s4 = "\x15aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x15a" + r4 = "=15aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=\na=15a=\n" + + [ [[s1], "M", r1], + [[s1], "M0", r1], + [[s1], "M1", r1], + [[s2], "M", r2], + [[s2], "M0", r2], + [[s2], "M1", r2], + [[s3], "M", r3], + [[s3], "M0", r3], + [[s3], "M1", r3], + [[s4], "M", r4], + [[s4], "M0", r4], + [[s4], "M1", r4] + ].should be_computed_by(:pack) + end + + it "emits a soft line break when the output exceeds count characters" do + [ [["abcdefghi"], "M2", "abc=\ndef=\nghi=\n"], + [["abcdefghi"], "M3", "abcd=\nefgh=\ni=\n"], + [["abcdefghi"], "M4", "abcde=\nfghi=\n"], + [["abcdefghi"], "M5", "abcdef=\nghi=\n"], + [["abcdefghi"], "M6", "abcdefg=\nhi=\n"], + [["\x19\x19\x19\x19"], "M2", "=19=\n=19=\n=19=\n=19=\n"], + [["\x19\x19\x19\x19"], "M3", "=19=19=\n=19=19=\n"], + [["\x19\x19\x19\x19"], "M4", "=19=19=\n=19=19=\n"], + [["\x19\x19\x19\x19"], "M5", "=19=19=\n=19=19=\n"], + [["\x19\x19\x19\x19"], "M6", "=19=19=19=\n=19=\n"], + [["\x19\x19\x19\x19"], "M7", "=19=19=19=\n=19=\n"] + ].should be_computed_by(:pack) + end + + it "encodes a recursive array" do + empty = ArraySpecs.empty_recursive_array + empty.pack('M').should be_an_instance_of(String) + + array = ArraySpecs.recursive_array + array.pack('M').should == "1=\n" + end + + it "calls #to_s to convert an object to a String" do + obj = mock("pack M string") + obj.should_receive(:to_s).and_return("packing") + + [obj].pack("M").should == "packing=\n" + end + + it "converts the object to a String representation if #to_s does not return a String" do + obj = mock("pack M non-string") + obj.should_receive(:to_s).and_return(2) + + [obj].pack("M").should be_an_instance_of(String) + end + + it "encodes a Symbol as a String" do + [:symbol].pack("M").should == "symbol=\n" + end + + it "encodes an Integer as a String" do + [ [[1], "1=\n"], + [[bignum_value], "#{bignum_value}=\n"] + ].should be_computed_by(:pack, "M") + end + + it "encodes a Float as a String" do + [1.0].pack("M").should == "1.0=\n" + end + + it "converts Floats to the minimum unique representation" do + [1.0 / 3.0].pack("M").should == "0.3333333333333333=\n" + end + + it "sets the output string to US-ASCII encoding" do + ["abcd"].pack("M").encoding.should == Encoding::US_ASCII + end +end + +describe "Array#pack with format 'm'" do + it_behaves_like :array_pack_basic, 'm' + it_behaves_like :array_pack_basic_non_float, 'm' + it_behaves_like :array_pack_arguments, 'm' + + it "encodes an empty string as an empty string" do + [""].pack("m").should == "" + end + + it "appends a newline to the end of the encoded string" do + ["a"].pack("m").should == "YQ==\n" + end + + it "encodes one element per directive" do + ["abc", "DEF"].pack("mm").should == "YWJj\nREVG\n" + end + + it "encodes 1, 2, or 3 characters in 4 output characters (Base64 encoding)" do + [ [["a"], "YQ==\n"], + [["ab"], "YWI=\n"], + [["abc"], "YWJj\n"], + [["abcd"], "YWJjZA==\n"], + [["abcde"], "YWJjZGU=\n"], + [["abcdef"], "YWJjZGVm\n"], + [["abcdefg"], "YWJjZGVmZw==\n"], + ].should be_computed_by(:pack, "m") + end + + it "emits a newline after complete groups of count / 3 input characters when passed a count modifier" do + ["abcdefg"].pack("m3").should == "YWJj\nZGVm\nZw==\n" + end + + it "implicitly has a count of 45 when passed '*', 1, 2 or no count modifier" do + s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + r = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh\nYWFhYWE=\n" + [ [[s], "m", r], + [[s], "m*", r], + [[s], "m1", r], + [[s], "m2", r], + ].should be_computed_by(:pack) + end + + it "encodes all ascii characters" do + [ [["\x00\x01\x02\x03\x04\x05\x06"], "AAECAwQFBg==\n"], + [["\a\b\t\n\v\f\r"], "BwgJCgsMDQ==\n"], + [["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"], "Dg8QERITFBUW\n"], + [["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"], "FxgZGhscHR4f\n"], + [["!\"\#$%&'()*+,-./"], "ISIjJCUmJygpKissLS4v\n"], + [["0123456789"], "MDEyMzQ1Njc4OQ==\n"], + [[":;<=>?@"], "Ojs8PT4/QA==\n"], + [["ABCDEFGHIJKLMNOPQRSTUVWXYZ"], "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\n"], + [["[\\]^_`"], "W1xdXl9g\n"], + [["abcdefghijklmnopqrstuvwxyz"], "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=\n"], + [["{|}~"], "e3x9fg==\n"], + [["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"], "f8KAwoHCgsKD\n"], + [["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"], "woTChcKGwofC\n"], + [["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"], "iMKJworCi8KM\n"], + [["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"], "wo3CjsKPwpDC\n"], + [["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"], "kcKSwpPClMKV\n"], + [["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"], "wpbCl8KYwpnC\n"], + [["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"], "msKbwpzCncKe\n"], + [["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"], "wp/CoMKhwqLC\n"], + [["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"], "o8KkwqXCpsKn\n"], + [["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"], "wqjCqcKqwqvC\n"], + [["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"], "rMKtwq7Cr8Kw\n"], + [["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"], "wrHCssKzwrTC\n"], + [["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"], "tcK2wrfCuMK5\n"], + [["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"], "wrrCu8K8wr3C\n"], + [["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"], "vsK/w4DDgcOC\n"], + [["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"], "w4PDhMOFw4bD\n"], + [["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"], "h8OIw4nDisOL\n"], + [["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"], "w4zDjcOOw4/D\n"], + [["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"], "kMORw5LDk8OU\n"], + [["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"], "w5XDlsOXw5jD\n"], + [["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"], "mcOaw5vDnMOd\n"], + [["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"], "w57Dn8Ogw6HD\n"], + [["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"], "osOjw6TDpcOm\n"], + [["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"], "w6fDqMOpw6rD\n"], + [["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"], "q8Osw63DrsOv\n"], + [["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"], "w7DDscOyw7PD\n"], + [["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"], "tMO1w7bDt8O4\n"], + [["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"], "w7nDusO7w7zD\n"], + [["\xbd\xc3\xbe\xc3\xbf"], "vcO+w78=\n"] + ].should be_computed_by(:pack, "m") + end + + it "calls #to_str to convert an object to a String" do + obj = mock("pack m string") + obj.should_receive(:to_str).and_return("abc") + [obj].pack("m").should == "YWJj\n" + end + + it "raises a TypeError if #to_str does not return a String" do + obj = mock("pack m non-string") + lambda { [obj].pack("m") }.should raise_error(TypeError) + end + + it "raises a TypeError if passed nil" do + lambda { [nil].pack("m") }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an Integer" do + lambda { [0].pack("m") }.should raise_error(TypeError) + lambda { [bignum_value].pack("m") }.should raise_error(TypeError) + end + + it "does not emit a newline if passed zero as the count modifier" do + s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + r = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE=" + [s].pack("m0").should == r + end + + it "sets the output string to US-ASCII encoding" do + ["abcd"].pack("m").encoding.should == Encoding::US_ASCII + end +end diff --git a/spec/rubyspec/core/array/pack/n_spec.rb b/spec/rubyspec/core/array/pack/n_spec.rb new file mode 100644 index 0000000000..72a83e082b --- /dev/null +++ b/spec/rubyspec/core/array/pack/n_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "Array#pack with format 'N'" do + it_behaves_like :array_pack_basic, 'N' + it_behaves_like :array_pack_basic_non_float, 'N' + it_behaves_like :array_pack_arguments, 'N' + it_behaves_like :array_pack_numeric_basic, 'N' + it_behaves_like :array_pack_integer, 'N' + it_behaves_like :array_pack_no_platform, 'N' + it_behaves_like :array_pack_32bit_be, 'N' +end + +describe "Array#pack with format 'n'" do + it_behaves_like :array_pack_basic, 'n' + it_behaves_like :array_pack_basic_non_float, 'n' + it_behaves_like :array_pack_arguments, 'n' + it_behaves_like :array_pack_numeric_basic, 'n' + it_behaves_like :array_pack_integer, 'n' + it_behaves_like :array_pack_no_platform, 'n' + it_behaves_like :array_pack_16bit_be, 'n' +end diff --git a/spec/rubyspec/core/array/pack/p_spec.rb b/spec/rubyspec/core/array/pack/p_spec.rb new file mode 100644 index 0000000000..65a08281e2 --- /dev/null +++ b/spec/rubyspec/core/array/pack/p_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "Array#pack with format 'P'" do + it_behaves_like :array_pack_basic_non_float, 'P' +end + +describe "Array#pack with format 'p'" do + it_behaves_like :array_pack_basic_non_float, 'p' +end diff --git a/spec/rubyspec/core/array/pack/percent_spec.rb b/spec/rubyspec/core/array/pack/percent_spec.rb new file mode 100644 index 0000000000..55d6de3424 --- /dev/null +++ b/spec/rubyspec/core/array/pack/percent_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Array#pack with format '%'" do + it "raises an Argument Error" do + lambda { [1].pack("%") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/array/pack/q_spec.rb b/spec/rubyspec/core/array/pack/q_spec.rb new file mode 100644 index 0000000000..83e115c54a --- /dev/null +++ b/spec/rubyspec/core/array/pack/q_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "Array#pack with format 'Q'" do + it_behaves_like :array_pack_basic, 'Q' + it_behaves_like :array_pack_basic_non_float, 'Q' + it_behaves_like :array_pack_arguments, 'Q' + it_behaves_like :array_pack_numeric_basic, 'Q' + it_behaves_like :array_pack_integer, 'Q' +end + +describe "Array#pack with format 'q'" do + it_behaves_like :array_pack_basic, 'q' + it_behaves_like :array_pack_basic_non_float, 'q' + it_behaves_like :array_pack_arguments, 'q' + it_behaves_like :array_pack_numeric_basic, 'q' + it_behaves_like :array_pack_integer, 'q' +end + +describe "Array#pack with format 'Q'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_64bit_le, 'Q<' + end + + describe "with modifier '>'" do + it_behaves_like :array_pack_64bit_be, 'Q>' + end +end + +describe "Array#pack with format 'q'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_64bit_le, 'q<' + end + + describe "with modifier '>'" do + it_behaves_like :array_pack_64bit_be, 'q>' + end +end + +little_endian do + describe "Array#pack with format 'Q'" do + it_behaves_like :array_pack_64bit_le, 'Q' + end + + describe "Array#pack with format 'q'" do + it_behaves_like :array_pack_64bit_le, 'q' + end +end + +big_endian do + describe "Array#pack with format 'Q'" do + it_behaves_like :array_pack_64bit_be, 'Q' + end + + describe "Array#pack with format 'q'" do + it_behaves_like :array_pack_64bit_be, 'q' + end +end diff --git a/spec/rubyspec/core/array/pack/s_spec.rb b/spec/rubyspec/core/array/pack/s_spec.rb new file mode 100644 index 0000000000..b2f8cb48f8 --- /dev/null +++ b/spec/rubyspec/core/array/pack/s_spec.rb @@ -0,0 +1,133 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "Array#pack with format 'S'" do + it_behaves_like :array_pack_basic, 'S' + it_behaves_like :array_pack_basic_non_float, 'S' + it_behaves_like :array_pack_arguments, 'S' + it_behaves_like :array_pack_numeric_basic, 'S' + it_behaves_like :array_pack_integer, 'S' +end + +describe "Array#pack with format 's'" do + it_behaves_like :array_pack_basic, 's' + it_behaves_like :array_pack_basic_non_float, 's' + it_behaves_like :array_pack_arguments, 's' + it_behaves_like :array_pack_numeric_basic, 's' + it_behaves_like :array_pack_integer, 's' +end + +describe "Array#pack with format 'S'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_16bit_le, 'S<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_16bit_le, 'S<_' + it_behaves_like :array_pack_16bit_le, 'S_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_16bit_le, 'S'" do + it_behaves_like :array_pack_16bit_be, 'S>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :array_pack_16bit_be, 'S>_' + it_behaves_like :array_pack_16bit_be, 'S_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_16bit_be, 'S>!' + it_behaves_like :array_pack_16bit_be, 'S!>' + end +end + +describe "Array#pack with format 's'" do + describe "with modifier '<'" do + it_behaves_like :array_pack_16bit_le, 's<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :array_pack_16bit_le, 's<_' + it_behaves_like :array_pack_16bit_le, 's_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :array_pack_16bit_le, 's'" do + it_behaves_like :array_pack_16bit_be, 's>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :array_pack_16bit_be, 's>_' + it_behaves_like :array_pack_16bit_be, 's_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :array_pack_16bit_be, 's>!' + it_behaves_like :array_pack_16bit_be, 's!>' + end +end + +little_endian do + describe "Array#pack with format 'S'" do + it_behaves_like :array_pack_16bit_le, 'S' + end + + describe "Array#pack with format 'S' with modifier '_'" do + it_behaves_like :array_pack_16bit_le, 'S_' + end + + describe "Array#pack with format 'S' with modifier '!'" do + it_behaves_like :array_pack_16bit_le, 'S!' + end + + describe "Array#pack with format 's'" do + it_behaves_like :array_pack_16bit_le, 's' + end + + describe "Array#pack with format 's' with modifier '_'" do + it_behaves_like :array_pack_16bit_le, 's_' + end + + describe "Array#pack with format 's' with modifier '!'" do + it_behaves_like :array_pack_16bit_le, 's!' + end +end + +big_endian do + describe "Array#pack with format 'S'" do + it_behaves_like :array_pack_16bit_be, 'S' + end + + describe "Array#pack with format 'S' with modifier '_'" do + it_behaves_like :array_pack_16bit_be, 'S_' + end + + describe "Array#pack with format 'S' with modifier '!'" do + it_behaves_like :array_pack_16bit_be, 'S!' + end + + describe "Array#pack with format 's'" do + it_behaves_like :array_pack_16bit_be, 's' + end + + describe "Array#pack with format 's' with modifier '_'" do + it_behaves_like :array_pack_16bit_be, 's_' + end + + describe "Array#pack with format 's' with modifier '!'" do + it_behaves_like :array_pack_16bit_be, 's!' + end +end diff --git a/spec/rubyspec/core/array/pack/shared/basic.rb b/spec/rubyspec/core/array/pack/shared/basic.rb new file mode 100644 index 0000000000..39ab15308d --- /dev/null +++ b/spec/rubyspec/core/array/pack/shared/basic.rb @@ -0,0 +1,65 @@ +describe :array_pack_arguments, shared: true do + it "raises an ArgumentError if there are fewer elements than the format requires" do + lambda { [].pack(pack_format(1)) }.should raise_error(ArgumentError) + end +end + +describe :array_pack_basic, shared: true do + before :each do + @obj = ArraySpecs.universal_pack_object + end + + it "raises a TypeError when passed nil" do + lambda { [@obj].pack(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed an Integer" do + lambda { [@obj].pack(1) }.should raise_error(TypeError) + end +end + +describe :array_pack_basic_non_float, shared: true do + before :each do + @obj = ArraySpecs.universal_pack_object + end + + it "ignores whitespace in the format string" do + [@obj, @obj].pack("a \t\n\v\f\r"+pack_format).should be_an_instance_of(String) + end + + it "calls #to_str to coerce the directives string" do + d = mock("pack directive") + d.should_receive(:to_str).and_return("x"+pack_format) + [@obj, @obj].pack(d).should be_an_instance_of(String) + end + + it "taints the output string if the format string is tainted" do + [@obj, @obj].pack("x"+pack_format.taint).tainted?.should be_true + end +end + +describe :array_pack_basic_float, shared: true do + it "ignores whitespace in the format string" do + [9.3, 4.7].pack(" \t\n\v\f\r"+pack_format).should be_an_instance_of(String) + end + + it "calls #to_str to coerce the directives string" do + d = mock("pack directive") + d.should_receive(:to_str).and_return("x"+pack_format) + [1.2, 4.7].pack(d).should be_an_instance_of(String) + end + + it "taints the output string if the format string is tainted" do + [3.2, 2.8].pack("x"+pack_format.taint).tainted?.should be_true + end +end + +describe :array_pack_no_platform, shared: true do + it "raises ArgumentError when the format modifier is '_'" do + lambda{ [1].pack(pack_format("_")) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when the format modifier is '!'" do + lambda{ [1].pack(pack_format("!")) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/array/pack/shared/encodings.rb b/spec/rubyspec/core/array/pack/shared/encodings.rb new file mode 100644 index 0000000000..3724a5d859 --- /dev/null +++ b/spec/rubyspec/core/array/pack/shared/encodings.rb @@ -0,0 +1,16 @@ +describe :array_pack_hex, shared: true do + it "encodes no bytes when passed zero as the count modifier" do + ["abc"].pack(pack_format(0)).should == "" + end + + it "raises a TypeError if the object does not respond to #to_str" do + obj = mock("pack hex non-string") + lambda { [obj].pack(pack_format) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_str does not return a String" do + obj = mock("pack hex non-string") + obj.should_receive(:to_str).and_return(1) + lambda { [obj].pack(pack_format) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/array/pack/shared/float.rb b/spec/rubyspec/core/array/pack/shared/float.rb new file mode 100644 index 0000000000..082de27acd --- /dev/null +++ b/spec/rubyspec/core/array/pack/shared/float.rb @@ -0,0 +1,249 @@ +# -*- encoding: ascii-8bit -*- + +describe :array_pack_float_le, shared: true do + it "encodes a positive Float" do + [1.42].pack(pack_format).should == "\x8f\xc2\xb5?" + end + + it "encodes a negative Float" do + [-34.2].pack(pack_format).should == "\xcd\xcc\x08\xc2" + end + + it "converts an Integer to a Float" do + [8].pack(pack_format).should == "\x00\x00\x00A" + end + + it "raises a TypeError if passed a String representation of a floating point number" do + lambda { ["13"].pack(pack_format) }.should raise_error(TypeError) + end + + it "encodes the number of array elements specified by the count modifier" do + [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "\x9a\x999@33\xb3?" + end + + it "encodes all remaining elements when passed the '*' modifier" do + [2.9, 1.4, 8.2].pack(pack_format("*")).should == "\x9a\x999@33\xb3?33\x03A" + end + + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A" + end + + it "ignores spaces between directives" do + [5.3, 9.2].pack(pack_format(" ", 2)).should == "\x9a\x99\xa9@33\x13A" + end + + it "encodes positive Infinity" do + [infinity_value].pack(pack_format).should == "\x00\x00\x80\x7f" + end + + it "encodes negative Infinity" do + [-infinity_value].pack(pack_format).should == "\x00\x00\x80\xff" + end + + platform_is "86" do # x86 / x86_64 + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\x00\x00\xc0\xff" + end + end + + platform_is "powerpc64" do + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\x00\x00\xc0\x7f" + end + end + + it "encodes a positive Float outside the range of a single precision float" do + [1e150].pack(pack_format).should == "\x00\x00\x80\x7f" + end + + it "encodes a negative Float outside the range of a single precision float" do + [-1e150].pack(pack_format).should == "\x00\x00\x80\xff" + end +end + +describe :array_pack_float_be, shared: true do + it "encodes a positive Float" do + [1.42].pack(pack_format).should == "?\xb5\xc2\x8f" + end + + it "encodes a negative Float" do + [-34.2].pack(pack_format).should == "\xc2\x08\xcc\xcd" + end + + it "converts an Integer to a Float" do + [8].pack(pack_format).should == "A\x00\x00\x00" + end + + it "raises a TypeError if passed a String representation of a floating point number" do + lambda { ["13"].pack(pack_format) }.should raise_error(TypeError) + end + + it "encodes the number of array elements specified by the count modifier" do + [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "@9\x99\x9a?\xb333" + end + + it "encodes all remaining elements when passed the '*' modifier" do + [2.9, 1.4, 8.2].pack(pack_format("*")).should == "@9\x99\x9a?\xb333A\x0333" + end + + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333" + end + + it "ignores spaces between directives" do + [5.3, 9.2].pack(pack_format(" ", 2)).should == "@\xa9\x99\x9aA\x1333" + end + + it "encodes positive Infinity" do + [infinity_value].pack(pack_format).should == "\x7f\x80\x00\x00" + end + + it "encodes negative Infinity" do + [-infinity_value].pack(pack_format).should == "\xff\x80\x00\x00" + end + + platform_is "86" do # x86 / x86_64 + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\xff\xc0\x00\x00" + end + end + + platform_is "powerpc64" do + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\x7f\xc0\x00\x00" + end + end + + it "encodes a positive Float outside the range of a single precision float" do + [1e150].pack(pack_format).should == "\x7f\x80\x00\x00" + end + + it "encodes a negative Float outside the range of a single precision float" do + [-1e150].pack(pack_format).should == "\xff\x80\x00\x00" + end +end + +describe :array_pack_double_le, shared: true do + it "encodes a positive Float" do + [1.42].pack(pack_format).should == "\xb8\x1e\x85\xebQ\xb8\xf6?" + end + + it "encodes a negative Float" do + [-34.2].pack(pack_format).should == "\x9a\x99\x99\x99\x99\x19A\xc0" + end + + it "converts an Integer to a Float" do + [8].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\x20@" + end + + it "raises a TypeError if passed a String representation of a floating point number" do + lambda { ["13"].pack(pack_format) }.should raise_error(TypeError) + end + + it "encodes the number of array elements specified by the count modifier" do + [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "333333\x07@ffffff\xf6?" + end + + it "encodes all remaining elements when passed the '*' modifier" do + [2.9, 1.4, 8.2].pack(pack_format("*")).should == "333333\x07@ffffff\xf6?ffffff\x20@" + end + + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@" + end + + it "ignores spaces between directives" do + [5.3, 9.2].pack(pack_format(" ", 2)).should == "333333\x15@ffffff\x22@" + end + + it "encodes positive Infinity" do + [infinity_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf0\x7f" + end + + it "encodes negative Infinity" do + [-infinity_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf0\xff" + end + + platform_is "86" do # x86 / x86_64 + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf8\xff" + end + end + + platform_is "powerpc64" do + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xf8\x7f" + end + end + + it "encodes a positive Float outside the range of a single precision float" do + [1e150].pack(pack_format).should == "\xaf\x96P\x2e5\x8d\x13_" + end + + it "encodes a negative Float outside the range of a single precision float" do + [-1e150].pack(pack_format).should == "\xaf\x96P\x2e5\x8d\x13\xdf" + end +end + +describe :array_pack_double_be, shared: true do + it "encodes a positive Float" do + [1.42].pack(pack_format).should == "?\xf6\xb8Q\xeb\x85\x1e\xb8" + end + + it "encodes a negative Float" do + [-34.2].pack(pack_format).should == "\xc0A\x19\x99\x99\x99\x99\x9a" + end + + it "converts an Integer to a Float" do + [8].pack(pack_format).should == "@\x20\x00\x00\x00\x00\x00\x00" + end + + it "raises a TypeError if passed a String representation of a floating point number" do + lambda { ["13"].pack(pack_format) }.should raise_error(TypeError) + end + + it "encodes the number of array elements specified by the count modifier" do + [2.9, 1.4, 8.2].pack(pack_format(nil, 2)).should == "@\x07333333?\xf6ffffff" + end + + it "encodes all remaining elements when passed the '*' modifier" do + [2.9, 1.4, 8.2].pack(pack_format("*")).should == "@\x07333333?\xf6ffffff@\x20ffffff" + end + + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff" + end + + it "ignores spaces between directives" do + [5.3, 9.2].pack(pack_format(" ", 2)).should == "@\x15333333@\x22ffffff" + end + + it "encodes positive Infinity" do + [infinity_value].pack(pack_format).should == "\x7f\xf0\x00\x00\x00\x00\x00\x00" + end + + it "encodes negative Infinity" do + [-infinity_value].pack(pack_format).should == "\xff\xf0\x00\x00\x00\x00\x00\x00" + end + + platform_is "86" do # x86 / x86_64 + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\xff\xf8\x00\x00\x00\x00\x00\x00" + end + end + + platform_is "powerpc64" do + it "encodes NaN" do + [nan_value].pack(pack_format).should == "\x7f\xf8\x00\x00\x00\x00\x00\x00" + end + end + + it "encodes a positive Float outside the range of a single precision float" do + [1e150].pack(pack_format).should == "_\x13\x8d5\x2eP\x96\xaf" + end + + it "encodes a negative Float outside the range of a single precision float" do + [-1e150].pack(pack_format).should == "\xdf\x13\x8d5\x2eP\x96\xaf" + end +end diff --git a/spec/rubyspec/core/array/pack/shared/integer.rb b/spec/rubyspec/core/array/pack/shared/integer.rb new file mode 100644 index 0000000000..0df03bbfd1 --- /dev/null +++ b/spec/rubyspec/core/array/pack/shared/integer.rb @@ -0,0 +1,381 @@ +# -*- encoding: ascii-8bit -*- + +describe :array_pack_16bit_le, shared: true do + it "encodes the least significant 16 bits of a positive number" do + [ [[0x0000_0021], "\x21\x00"], + [[0x0000_4321], "\x21\x43"], + [[0x0065_4321], "\x21\x43"], + [[0x7865_4321], "\x21\x43"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the least significant 16 bits of a negative number" do + [ [[-0x0000_0021], "\xdf\xff"], + [[-0x0000_4321], "\xdf\xbc"], + [[-0x0065_4321], "\xdf\xbc"], + [[-0x7865_4321], "\xdf\xbc"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes a Float truncated as an Integer" do + [ [[2019902241.2], "\x21\x43"], + [[2019902241.8], "\x21\x43"], + [[-2019902241.2], "\xdf\xbc"], + [[-2019902241.8], "\xdf\xbc"] + ].should be_computed_by(:pack, pack_format()) + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(0x1234_5678) + [obj].pack(pack_format()).should == "\x78\x56" + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2)) + str.should == "\x78\x65\xcd\xab" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*')) + str.should == "\x78\x65\xcd\xab\x21\x43" + end + + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x78\x65\xcd\xab" + end + + it "ignores spaces between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2)) + str.should == "\x78\x65\xcd\xab" + end +end + +describe :array_pack_16bit_be, shared: true do + it "encodes the least significant 16 bits of a positive number" do + [ [[0x0000_0021], "\x00\x21"], + [[0x0000_4321], "\x43\x21"], + [[0x0065_4321], "\x43\x21"], + [[0x7865_4321], "\x43\x21"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the least significant 16 bits of a negative number" do + [ [[-0x0000_0021], "\xff\xdf"], + [[-0x0000_4321], "\xbc\xdf"], + [[-0x0065_4321], "\xbc\xdf"], + [[-0x7865_4321], "\xbc\xdf"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes a Float truncated as an Integer" do + [ [[2019902241.2], "\x43\x21"], + [[2019902241.8], "\x43\x21"], + [[-2019902241.2], "\xbc\xdf"], + [[-2019902241.8], "\xbc\xdf"] + ].should be_computed_by(:pack, pack_format()) + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(0x1234_5678) + [obj].pack(pack_format()).should == "\x56\x78" + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2)) + str.should == "\x65\x78\xab\xcd" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*')) + str.should == "\x65\x78\xab\xcd\x43\x21" + end + + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x65\x78\xab\xcd" + end + + it "ignores spaces between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2)) + str.should == "\x65\x78\xab\xcd" + end +end + +describe :array_pack_32bit_le, shared: true do + it "encodes the least significant 32 bits of a positive number" do + [ [[0x0000_0021], "\x21\x00\x00\x00"], + [[0x0000_4321], "\x21\x43\x00\x00"], + [[0x0065_4321], "\x21\x43\x65\x00"], + [[0x7865_4321], "\x21\x43\x65\x78"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the least significant 32 bits of a negative number" do + [ [[-0x0000_0021], "\xdf\xff\xff\xff"], + [[-0x0000_4321], "\xdf\xbc\xff\xff"], + [[-0x0065_4321], "\xdf\xbc\x9a\xff"], + [[-0x7865_4321], "\xdf\xbc\x9a\x87"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes a Float truncated as an Integer" do + [ [[2019902241.2], "\x21\x43\x65\x78"], + [[2019902241.8], "\x21\x43\x65\x78"], + [[-2019902241.2], "\xdf\xbc\x9a\x87"], + [[-2019902241.8], "\xdf\xbc\x9a\x87"] + ].should be_computed_by(:pack, pack_format()) + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(0x1234_5678) + [obj].pack(pack_format()).should == "\x78\x56\x34\x12" + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2)) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*')) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78" + end + + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + end + + it "ignores spaces between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2)) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + end +end + +describe :array_pack_32bit_be, shared: true do + it "encodes the least significant 32 bits of a positive number" do + [ [[0x0000_0021], "\x00\x00\x00\x21"], + [[0x0000_4321], "\x00\x00\x43\x21"], + [[0x0065_4321], "\x00\x65\x43\x21"], + [[0x7865_4321], "\x78\x65\x43\x21"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the least significant 32 bits of a negative number" do + [ [[-0x0000_0021], "\xff\xff\xff\xdf"], + [[-0x0000_4321], "\xff\xff\xbc\xdf"], + [[-0x0065_4321], "\xff\x9a\xbc\xdf"], + [[-0x7865_4321], "\x87\x9a\xbc\xdf"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes a Float truncated as an Integer" do + [ [[2019902241.2], "\x78\x65\x43\x21"], + [[2019902241.8], "\x78\x65\x43\x21"], + [[-2019902241.2], "\x87\x9a\xbc\xdf"], + [[-2019902241.8], "\x87\x9a\xbc\xdf"] + ].should be_computed_by(:pack, pack_format()) + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(0x1234_5678) + [obj].pack(pack_format()).should == "\x12\x34\x56\x78" + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2)) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*')) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21" + end + + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + end + + it "ignores spaces between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format(' ', 2)) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + end +end + +describe :array_pack_32bit_le_platform, shared: true do + it "encodes the least significant 32 bits of a number" do + [ [[0x7865_4321], "\x21\x43\x65\x78"], + [[-0x7865_4321], "\xdf\xbc\x9a\x87"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2)) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*')) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78" + end + + platform_is wordsize: 64 do + it "encodes the least significant 32 bits of a number that is greater than 32 bits" do + [ [[0xff_7865_4321], "\x21\x43\x65\x78"], + [[-0xff_7865_4321], "\xdf\xbc\x9a\x87"] + ].should be_computed_by(:pack, pack_format()) + end + end +end + +describe :array_pack_32bit_be_platform, shared: true do + it "encodes the least significant 32 bits of a number" do + [ [[0x7865_4321], "\x78\x65\x43\x21"], + [[-0x7865_4321], "\x87\x9a\xbc\xdf"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format(2)) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0x1243_6578, 0xdef0_abcd, 0x7865_4321].pack(pack_format('*')) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21" + end + + platform_is wordsize: 64 do + it "encodes the least significant 32 bits of a number that is greater than 32 bits" do + [ [[0xff_7865_4321], "\x78\x65\x43\x21"], + [[-0xff_7865_4321], "\x87\x9a\xbc\xdf"] + ].should be_computed_by(:pack, pack_format()) + end + end +end + +describe :array_pack_64bit_le, shared: true do + it "encodes the least significant 64 bits of a positive number" do + [ [[0x0000_0000_0000_0021], "\x21\x00\x00\x00\x00\x00\x00\x00"], + [[0x0000_0000_0000_4321], "\x21\x43\x00\x00\x00\x00\x00\x00"], + [[0x0000_0000_0065_4321], "\x21\x43\x65\x00\x00\x00\x00\x00"], + [[0x0000_0000_7865_4321], "\x21\x43\x65\x78\x00\x00\x00\x00"], + [[0x0000_0090_7865_4321], "\x21\x43\x65\x78\x90\x00\x00\x00"], + [[0x0000_ba90_7865_4321], "\x21\x43\x65\x78\x90\xba\x00\x00"], + [[0x00dc_ba90_7865_4321], "\x21\x43\x65\x78\x90\xba\xdc\x00"], + [[0x7edc_ba90_7865_4321], "\x21\x43\x65\x78\x90\xba\xdc\x7e"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the least significant 64 bits of a negative number" do + [ [[-0x0000_0000_0000_0021], "\xdf\xff\xff\xff\xff\xff\xff\xff"], + [[-0x0000_0000_0000_4321], "\xdf\xbc\xff\xff\xff\xff\xff\xff"], + [[-0x0000_0000_0065_4321], "\xdf\xbc\x9a\xff\xff\xff\xff\xff"], + [[-0x0000_0000_7865_4321], "\xdf\xbc\x9a\x87\xff\xff\xff\xff"], + [[-0x0000_0090_7865_4321], "\xdf\xbc\x9a\x87\x6f\xff\xff\xff"], + [[-0x0000_ba90_7865_4321], "\xdf\xbc\x9a\x87\x6f\x45\xff\xff"], + [[-0x00dc_ba90_7865_4321], "\xdf\xbc\x9a\x87\x6f\x45\x23\xff"], + [[-0x7edc_ba90_7865_4321], "\xdf\xbc\x9a\x87\x6f\x45\x23\x81"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes a Float truncated as an Integer" do + [ [[9.14138647331322368e+18], "\x00\x44\x65\x78\x90\xba\xdc\x7e"], + [[-9.14138647331322368e+18], "\x00\xbc\x9a\x87\x6f\x45\x23\x81"] + ].should be_computed_by(:pack, pack_format()) + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(0x1234_5678_90ab_cdef) + [obj].pack(pack_format()).should == "\xef\xcd\xab\x90\x78\x56\x34\x12" + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1234_5678_90ab_cdef, + 0xdef0_abcd_3412_7856, + 0x7865_4321_dcba_def0].pack(pack_format(2)) + str.should == "\xef\xcd\xab\x90\x78\x56\x34\x12\x56\x78\x12\x34\xcd\xab\xf0\xde" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format('*')) + str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" + end + + it "ignores NULL bytes between directives" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" + end + + it "ignores spaces between directives" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format(' ', 2)) + str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" + end +end + +describe :array_pack_64bit_be, shared: true do + it "encodes the least significant 64 bits of a positive number" do + [ [[0x0000_0000_0000_0021], "\x00\x00\x00\x00\x00\x00\x00\x21"], + [[0x0000_0000_0000_4321], "\x00\x00\x00\x00\x00\x00\x43\x21"], + [[0x0000_0000_0065_4321], "\x00\x00\x00\x00\x00\x65\x43\x21"], + [[0x0000_0000_7865_4321], "\x00\x00\x00\x00\x78\x65\x43\x21"], + [[0x0000_0090_7865_4321], "\x00\x00\x00\x90\x78\x65\x43\x21"], + [[0x0000_ba90_7865_4321], "\x00\x00\xba\x90\x78\x65\x43\x21"], + [[0x00dc_ba90_7865_4321], "\x00\xdc\xba\x90\x78\x65\x43\x21"], + [[0x7edc_ba90_7865_4321], "\x7e\xdc\xba\x90\x78\x65\x43\x21"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes the least significant 64 bits of a negative number" do + [ [[-0x0000_0000_0000_0021], "\xff\xff\xff\xff\xff\xff\xff\xdf"], + [[-0x0000_0000_0000_4321], "\xff\xff\xff\xff\xff\xff\xbc\xdf"], + [[-0x0000_0000_0065_4321], "\xff\xff\xff\xff\xff\x9a\xbc\xdf"], + [[-0x0000_0000_7865_4321], "\xff\xff\xff\xff\x87\x9a\xbc\xdf"], + [[-0x0000_0090_7865_4321], "\xff\xff\xff\x6f\x87\x9a\xbc\xdf"], + [[-0x0000_ba90_7865_4321], "\xff\xff\x45\x6f\x87\x9a\xbc\xdf"], + [[-0x00dc_ba90_7865_4321], "\xff\x23\x45\x6f\x87\x9a\xbc\xdf"], + [[-0x7edc_ba90_7865_4321], "\x81\x23\x45\x6f\x87\x9a\xbc\xdf"] + ].should be_computed_by(:pack, pack_format()) + end + + it "encodes a Float truncated as an Integer" do + [ [[9.14138647331322368e+18], "\x7e\xdc\xba\x90\x78\x65\x44\x00"], + [[-9.14138647331322368e+18], "\x81\x23\x45\x6f\x87\x9a\xbc\x00"] + ].should be_computed_by(:pack, pack_format()) + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(0x1234_5678_90ab_cdef) + [obj].pack(pack_format()).should == "\x12\x34\x56\x78\x90\xab\xcd\xef" + end + + it "encodes the number of array elements specified by the count modifier" do + str = [0x1234_5678_90ab_cdef, + 0xdef0_abcd_3412_7856, + 0x7865_4321_dcba_def0].pack(pack_format(2)) + str.should == "\x12\x34\x56\x78\x90\xab\xcd\xef\xde\xf0\xab\xcd\x34\x12\x78\x56" + end + + it "encodes all remaining elements when passed the '*' modifier" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format('*')) + str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" + end + + it "ignores NULL bytes between directives" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" + end + + it "ignores spaces between directives" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format(' ', 2)) + str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" + end +end diff --git a/spec/rubyspec/core/array/pack/shared/numeric_basic.rb b/spec/rubyspec/core/array/pack/shared/numeric_basic.rb new file mode 100644 index 0000000000..9224d6080e --- /dev/null +++ b/spec/rubyspec/core/array/pack/shared/numeric_basic.rb @@ -0,0 +1,44 @@ +describe :array_pack_numeric_basic, shared: true do + it "returns an empty String if count is zero" do + [1].pack(pack_format(0)).should == "" + end + + it "raises a TypeError when passed nil" do + lambda { [nil].pack(pack_format) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed true" do + lambda { [true].pack(pack_format) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed false" do + lambda { [false].pack(pack_format) }.should raise_error(TypeError) + end + + it "returns an ASCII-8BIT string" do + [0xFF].pack(pack_format).encoding.should == Encoding::ASCII_8BIT + [0xE3, 0x81, 0x82].pack(pack_format(3)).encoding.should == Encoding::ASCII_8BIT + end +end + +describe :array_pack_integer, shared: true do + it "raises a TypeError when the object does not respond to #to_int" do + obj = mock('not an integer') + lambda { [obj].pack(pack_format) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { ["5"].pack(pack_format) }.should raise_error(TypeError) + end +end + +describe :array_pack_float, shared: true do + it "raises a TypeError if a String does not represent a floating point number" do + lambda { ["a"].pack(pack_format) }.should raise_error(TypeError) + end + + it "raises a TypeError when the object does not respond to #to_f" do + obj = mock('not an float') + lambda { [obj].pack(pack_format) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/array/pack/shared/string.rb b/spec/rubyspec/core/array/pack/shared/string.rb new file mode 100644 index 0000000000..cedb0886e2 --- /dev/null +++ b/spec/rubyspec/core/array/pack/shared/string.rb @@ -0,0 +1,80 @@ +# -*- encoding: binary -*- +describe :array_pack_string, shared: true do + it "adds count bytes of a String to the output" do + ["abc"].pack(pack_format(2)).should == "ab" + end + + it "implicitly has a count of one when no count is specified" do + ["abc"].pack(pack_format).should == "a" + end + + it "does not add any bytes when the count is zero" do + ["abc"].pack(pack_format(0)).should == "" + end + + it "is not affected by a previous count modifier" do + ["abcde", "defg"].pack(pack_format(3)+pack_format).should == "abcd" + end + + it "raises an ArgumentError when the Array is empty" do + lambda { [].pack(pack_format) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the Array has too few elements" do + lambda { ["a"].pack(pack_format(nil, 2)) }.should raise_error(ArgumentError) + end + + it "calls #to_str to convert the element to a String" do + obj = mock('pack string') + obj.should_receive(:to_str).and_return("abc") + + [obj].pack(pack_format).should == "a" + end + + it "raises a TypeError when the object does not respond to #to_str" do + obj = mock("not a string") + lambda { [obj].pack(pack_format) }.should raise_error(TypeError) + end + + it "returns a tainted string when a pack argument is tainted" do + ["abcd".taint, 0x20].pack(pack_format("3C")).tainted?.should be_true + end + + it "does not return a tainted string when the array is tainted" do + ["abcd", 0x20].taint.pack(pack_format("3C")).tainted?.should be_false + end + + it "returns a tainted string when the format is tainted" do + ["abcd", 0x20].pack(pack_format("3C").taint).tainted?.should be_true + end + + it "returns a tainted string when an empty format is tainted" do + ["abcd", 0x20].pack("".taint).tainted?.should be_true + end + + it "returns a untrusted string when the format is untrusted" do + ["abcd", 0x20].pack(pack_format("3C").untrust).untrusted?.should be_true + end + + it "returns a untrusted string when the empty format is untrusted" do + ["abcd", 0x20].pack("".untrust).untrusted?.should be_true + end + + it "returns a untrusted string when a pack argument is untrusted" do + ["abcd".untrust, 0x20].pack(pack_format("3C")).untrusted?.should be_true + end + + it "returns a trusted string when the array is untrusted" do + ["abcd", 0x20].untrust.pack(pack_format("3C")).untrusted?.should be_false + end + + it "returns a string in encoding of common to the concatenated results" do + f = pack_format("*") + [ [["\u{3042 3044 3046 3048}", 0x2000B].pack(f+"U"), Encoding::ASCII_8BIT], + [["abcde\xd1", "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::ASCII_8BIT], + [["a".force_encoding("ascii"), "\xFF\xFe\x81\x82"].pack(f+"u"), Encoding::ASCII_8BIT], + # under discussion [ruby-dev:37294] + [["\u{3042 3044 3046 3048}", 1].pack(f+"N"), Encoding::ASCII_8BIT] + ].should be_computed_by(:encoding) + end +end diff --git a/spec/rubyspec/core/array/pack/shared/unicode.rb b/spec/rubyspec/core/array/pack/shared/unicode.rb new file mode 100644 index 0000000000..e16110c491 --- /dev/null +++ b/spec/rubyspec/core/array/pack/shared/unicode.rb @@ -0,0 +1,94 @@ +# -*- encoding: utf-8 -*- + +describe :array_pack_unicode, shared: true do + it "encodes ASCII values as a Unicode codepoint" do + [ [[0], "\x00"], + [[1], "\x01"], + [[8], "\x08"], + [[15], "\x0f"], + [[24], "\x18"], + [[31], "\x1f"], + [[127], "\x7f"], + [[128], "\xc2\x80"], + [[129], "\xc2\x81"], + [[255], "\xc3\xbf"] + ].should be_computed_by(:pack, "U") + end + + it "encodes UTF-8 BMP codepoints" do + [ [[0x80], "\xc2\x80"], + [[0x7ff], "\xdf\xbf"], + [[0x800], "\xe0\xa0\x80"], + [[0xffff], "\xef\xbf\xbf"] + ].should be_computed_by(:pack, "U") + end + + it "constructs strings with valid encodings" do + str = [0x85].pack("U*") + str.should == "\xc2\x85" + str.valid_encoding?.should be_true + end + + it "encodes values larger than UTF-8 max codepoints" do + [ + [[0x00110000], [244, 144, 128, 128].pack('C*').force_encoding('utf-8')], + [[0x04000000], [252, 132, 128, 128, 128, 128].pack('C*').force_encoding('utf-8')], + [[0x7FFFFFFF], [253, 191, 191, 191, 191, 191].pack('C*').force_encoding('utf-8')] + ].should be_computed_by(:pack, "U") + end + + it "encodes UTF-8 max codepoints" do + [ [[0x10000], "\xf0\x90\x80\x80"], + [[0xfffff], "\xf3\xbf\xbf\xbf"], + [[0x100000], "\xf4\x80\x80\x80"], + [[0x10ffff], "\xf4\x8f\xbf\xbf"] + ].should be_computed_by(:pack, "U") + end + + it "encodes the number of array elements specified by the count modifier" do + [ [[0x41, 0x42, 0x43, 0x44], "U2", "\x41\x42"], + [[0x41, 0x42, 0x43, 0x44], "U2U", "\x41\x42\x43"] + ].should be_computed_by(:pack) + end + + it "encodes all remaining elements when passed the '*' modifier" do + [0x41, 0x42, 0x43, 0x44].pack("U*").should == "\x41\x42\x43\x44" + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(5) + [obj].pack("U").should == "\x05" + end + + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return("5") + lambda { [obj].pack("U") }.should raise_error(TypeError) + end + + it "ignores NULL bytes between directives" do + [1, 2, 3].pack("U\x00U").should == "\x01\x02" + end + + it "ignores spaces between directives" do + [1, 2, 3].pack("U U").should == "\x01\x02" + end + + it "raises a RangeError if passed a negative number" do + lambda { [-1].pack("U") }.should raise_error(RangeError) + end + + it "raises a RangeError if passed a number larger than an unsigned 32-bit integer" do + lambda { [2**32].pack("U") }.should raise_error(RangeError) + end + + it "sets the output string to UTF-8 encoding" do + [ [[0x00].pack("U"), Encoding::UTF_8], + [[0x41].pack("U"), Encoding::UTF_8], + [[0x7F].pack("U"), Encoding::UTF_8], + [[0x80].pack("U"), Encoding::UTF_8], + [[0x10FFFF].pack("U"), Encoding::UTF_8] + ].should be_computed_by(:encoding) + end +end diff --git a/spec/rubyspec/core/array/pack/u_spec.rb b/spec/rubyspec/core/array/pack/u_spec.rb new file mode 100644 index 0000000000..0bc78fcb88 --- /dev/null +++ b/spec/rubyspec/core/array/pack/u_spec.rb @@ -0,0 +1,128 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/unicode', __FILE__) + +describe "Array#pack with format 'U'" do + it_behaves_like :array_pack_basic, 'U' + it_behaves_like :array_pack_basic_non_float, 'U' + it_behaves_like :array_pack_arguments, 'U' + it_behaves_like :array_pack_unicode, 'U' +end + +describe "Array#pack with format 'u'" do + it_behaves_like :array_pack_basic, 'u' + it_behaves_like :array_pack_basic_non_float, 'u' + it_behaves_like :array_pack_arguments, 'u' + + it "encodes an empty string as an empty string" do + [""].pack("u").should == "" + end + + it "appends a newline to the end of the encoded string" do + ["a"].pack("u").should == "!80``\n" + end + + it "encodes one element per directive" do + ["abc", "DEF"].pack("uu").should == "#86)C\n#1$5&\n" + end + + it "prepends the length of each segment of the input string as the first character (+32) in each line of the output" do + ["abcdefghijklm"].pack("u7").should == "&86)C9&5F\n&9VAI:FML\n!;0``\n" + end + + it "encodes 1, 2, or 3 characters in 4 output characters (uuencoding)" do + [ [["a"], "!80``\n"], + [["ab"], "\"86(`\n"], + [["abc"], "#86)C\n"], + [["abcd"], "$86)C9```\n"], + [["abcde"], "%86)C9&4`\n"], + [["abcdef"], "&86)C9&5F\n"], + [["abcdefg"], "'86)C9&5F9P``\n"], + ].should be_computed_by(:pack, "u") + end + + it "emits a newline after complete groups of count / 3 input characters when passed a count modifier" do + ["abcdefg"].pack("u3").should == "#86)C\n#9&5F\n!9P``\n" + end + + it "implicitly has a count of 45 when passed '*', 0, 1, 2 or no count modifier" do + s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + r = "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n%86%A86$`\n" + [ [[s], "u", r], + [[s], "u*", r], + [[s], "u0", r], + [[s], "u1", r], + [[s], "u2", r], + ].should be_computed_by(:pack) + end + + it "encodes all ascii characters" do + [ [["\x00\x01\x02\x03\x04\x05\x06"], "'``$\"`P0%!@``\n"], + [["\a\b\t\n\v\f\r"], "'!P@)\"@L,#0``\n"], + [["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"], ")\#@\\0$1(3%!46\n"], + [["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"], ")%Q@9&AL<'1X?\n"], + [["!\"\#$%&'()*+,-./"], "/(2(C)\"4F)R@I*BLL+2XO\n"], + [["0123456789"], "*,\#$R,S0U-C?@"], "'.CL\\/3X_0```\n"], + [["ABCDEFGHIJKLMNOPQRSTUVWXYZ"], ":04)#1$5&1TA)2DM,34Y/4%%24U155E=865H`\n"], + [["[\\]^_`"], "&6UQ=7E]@\n"], + [["abcdefghijklmnopqrstuvwxyz"], ":86)C9&5F9VAI:FML;6YO<'%R7H`\n"], + [["{|}~"], "$>WQ]?@``\n"], + [["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"], ")?\\*`PH'\"@L*#\n"], + [["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"], ")PH3\"A<*&PH?\"\n"], + [["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"], ")B,*)PHK\"B\\*,\n"], + [["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"], ")PHW\"CL*/PI#\"\n"], + [["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"], ")D<*2PI/\"E,*5\n"], + [["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"], ")PI;\"E\\*8PIG\"\n"], + [["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"], ")FL*;PIS\"G<*>\n"], + [["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"], ")PI_\"H,*APJ+\"\n"], + [["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"], ")H\\*DPJ7\"IL*G\n"], + [["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"], ")PJC\"J<*JPJO\"\n"], + [["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"], ")K,*MPJ[\"K\\*P\n"], + [["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"], ")PK'\"LL*SPK3\"\n"], + [["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"], ")M<*VPK?\"N,*Y\n"], + [["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"], ")PKK\"N\\*\\PKW\"\n"], + [["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"], ")OL*_PX#\#@<.\"\n"], + [["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"], ")PX/#A,.%PX;#\n"], + [["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"], ")A\\.(PXG#BL.+\n"], + [["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"], ")PXS#C<..PX_#\n"], + [["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"], ")D,.1PY+#D\\.4\n"], + [["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"], ")PY7#EL.7PYC#\n"], + [["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"], ")F<.:PYO#G,.=\n"], + [["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"], ")PY[#G\\.@PZ'#\n"], + [["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"], ")HL.CPZ3#I<.F\n"], + [["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"], ")PZ?#J,.IPZK#\n"], + [["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"], ")J\\.LPZW#KL.O\n"], + [["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"], ")P[##L<.RP[/#\n"], + [["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"], ")M,.UP[;#M\\.X\n"], + [["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"], ")P[G#NL.[P[S#\n"], + [["\xbd\xc3\xbe\xc3\xbf"], "%O<.^P[\\`\n"] + ].should be_computed_by(:pack, "u") + end + + it "calls #to_str to convert an object to a String" do + obj = mock("pack m string") + obj.should_receive(:to_str).and_return("abc") + [obj].pack("u").should == "#86)C\n" + end + + it "raises a TypeError if #to_str does not return a String" do + obj = mock("pack m non-string") + lambda { [obj].pack("u") }.should raise_error(TypeError) + end + + it "raises a TypeError if passed nil" do + lambda { [nil].pack("u") }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an Integer" do + lambda { [0].pack("u") }.should raise_error(TypeError) + lambda { [bignum_value].pack("u") }.should raise_error(TypeError) + end + + it "sets the output string to US-ASCII encoding" do + ["abcd"].pack("u").encoding.should == Encoding::US_ASCII + end +end diff --git a/spec/rubyspec/core/array/pack/v_spec.rb b/spec/rubyspec/core/array/pack/v_spec.rb new file mode 100644 index 0000000000..8ebb863686 --- /dev/null +++ b/spec/rubyspec/core/array/pack/v_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "Array#pack with format 'V'" do + it_behaves_like :array_pack_basic, 'V' + it_behaves_like :array_pack_basic_non_float, 'V' + it_behaves_like :array_pack_arguments, 'V' + it_behaves_like :array_pack_numeric_basic, 'V' + it_behaves_like :array_pack_integer, 'V' + it_behaves_like :array_pack_no_platform, 'V' + it_behaves_like :array_pack_32bit_le, 'V' +end + +describe "Array#pack with format 'v'" do + it_behaves_like :array_pack_basic, 'v' + it_behaves_like :array_pack_basic_non_float, 'v' + it_behaves_like :array_pack_arguments, 'v' + it_behaves_like :array_pack_numeric_basic, 'v' + it_behaves_like :array_pack_integer, 'v' + it_behaves_like :array_pack_no_platform, 'v' + it_behaves_like :array_pack_16bit_le, 'v' +end diff --git a/spec/rubyspec/core/array/pack/w_spec.rb b/spec/rubyspec/core/array/pack/w_spec.rb new file mode 100644 index 0000000000..9ada3e84cb --- /dev/null +++ b/spec/rubyspec/core/array/pack/w_spec.rb @@ -0,0 +1,42 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/numeric_basic', __FILE__) + +describe "Array#pack with format 'w'" do + it_behaves_like :array_pack_basic, 'w' + it_behaves_like :array_pack_basic_non_float, 'w' + it_behaves_like :array_pack_arguments, 'w' + it_behaves_like :array_pack_numeric_basic, 'w' + + it "encodes a BER-compressed integer" do + [ [[0], "\x00"], + [[1], "\x01"], + [[9999], "\xce\x0f"], + [[2**65], "\x84\x80\x80\x80\x80\x80\x80\x80\x80\x00"] + ].should be_computed_by(:pack, "w") + end + + it "calls #to_int to convert the pack argument to an Integer" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(5) + [obj].pack("w").should == "\x05" + end + + it "ignores NULL bytes between directives" do + [1, 2, 3].pack("w\x00w").should == "\x01\x02" + end + + it "ignores spaces between directives" do + [1, 2, 3].pack("w w").should == "\x01\x02" + end + + it "raises an ArgumentError when passed a negative value" do + lambda { [-1].pack("w") }.should raise_error(ArgumentError) + end + + it "returns an ASCII-8BIT string" do + [1].pack('w').encoding.should == Encoding::ASCII_8BIT + end +end diff --git a/spec/rubyspec/core/array/pack/x_spec.rb b/spec/rubyspec/core/array/pack/x_spec.rb new file mode 100644 index 0000000000..8d54ab84ee --- /dev/null +++ b/spec/rubyspec/core/array/pack/x_spec.rb @@ -0,0 +1,64 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "Array#pack with format 'x'" do + it_behaves_like :array_pack_basic, 'x' + it_behaves_like :array_pack_basic_non_float, 'x' + it_behaves_like :array_pack_no_platform, 'x' + + it "adds a NULL byte with an empty array" do + [].pack("x").should == "\x00" + end + + it "adds a NULL byte without consuming an element" do + [1, 2].pack("CxC").should == "\x01\x00\x02" + end + + it "is not affected by a previous count modifier" do + [].pack("x3x").should == "\x00\x00\x00\x00" + end + + it "adds multiple NULL bytes when passed a count modifier" do + [].pack("x3").should == "\x00\x00\x00" + end + + it "does not add a NULL byte if the count modifier is zero" do + [].pack("x0").should == "" + end + + it "does not add a NULL byte when passed the '*' modifier" do + [].pack("x*").should == "" + end +end + +describe "Array#pack with format 'X'" do + it_behaves_like :array_pack_basic, 'X' + it_behaves_like :array_pack_basic_non_float, 'X' + it_behaves_like :array_pack_no_platform, 'X' + + it "reduces the output string by one byte at the point it is encountered" do + [1, 2, 3].pack("C2XC").should == "\x01\x03" + end + + it "does not consume any elements" do + [1, 2, 3].pack("CXC").should == "\x02" + end + + it "reduces the output string by multiple bytes when passed a count modifier" do + [1, 2, 3, 4, 5].pack("C2X2C").should == "\x03" + end + + it "has no affect when passed the '*' modifier" do + [1, 2, 3].pack("C2X*C").should == "\x01\x02\x03" + end + + it "raises an ArgumentError if the output string is empty" do + lambda { [1, 2, 3].pack("XC") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the count modifier is greater than the bytes in the string" do + lambda { [1, 2, 3].pack("C2X3") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/array/pack/z_spec.rb b/spec/rubyspec/core/array/pack/z_spec.rb new file mode 100644 index 0000000000..b28a460a8e --- /dev/null +++ b/spec/rubyspec/core/array/pack/z_spec.rb @@ -0,0 +1,32 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/string', __FILE__) + +describe "Array#pack with format 'Z'" do + it_behaves_like :array_pack_basic, 'Z' + it_behaves_like :array_pack_basic_non_float, 'Z' + it_behaves_like :array_pack_no_platform, 'Z' + it_behaves_like :array_pack_string, 'Z' + + it "adds all the bytes and appends a NULL byte when passed the '*' modifier" do + ["abc"].pack("Z*").should == "abc\x00" + end + + it "padds the output with NULL bytes when the count exceeds the size of the String" do + ["abc"].pack("Z6").should == "abc\x00\x00\x00" + end + + it "adds a NULL byte when the value is nil" do + [nil].pack("Z").should == "\x00" + end + + it "pads the output with NULL bytes when the value is nil" do + [nil].pack("Z3").should == "\x00\x00\x00" + end + + it "does not append a NULL byte when passed the '*' modifier and the value is nil" do + [nil].pack("Z*").should == "\x00" + end +end diff --git a/spec/rubyspec/core/array/partition_spec.rb b/spec/rubyspec/core/array/partition_spec.rb new file mode 100644 index 0000000000..787b574c28 --- /dev/null +++ b/spec/rubyspec/core/array/partition_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#partition" do + it "returns two arrays" do + [].partition {}.should == [[], []] + end + + it "returns in the left array values for which the block evaluates to true" do + ary = [0, 1, 2, 3, 4, 5] + + ary.partition { |i| true }.should == [ary, []] + ary.partition { |i| 5 }.should == [ary, []] + ary.partition { |i| false }.should == [[], ary] + ary.partition { |i| nil }.should == [[], ary] + ary.partition { |i| i % 2 == 0 }.should == [[0, 2, 4], [1, 3, 5]] + ary.partition { |i| i / 3 == 0 }.should == [[0, 1, 2], [3, 4, 5]] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.partition { true }.should == [[empty], []] + empty.partition { false }.should == [[], [empty]] + + array = ArraySpecs.recursive_array + array.partition { true }.should == [ + [1, 'two', 3.0, array, array, array, array, array], + [] + ] + condition = true + array.partition { condition = !condition }.should == [ + ['two', array, array, array], + [1, 3.0, array, array] + ] + end + + it "does not return subclass instances on Array subclasses" do + result = ArraySpecs::MyArray[1, 2, 3].partition { |x| x % 2 == 0 } + result.should be_an_instance_of(Array) + result[0].should be_an_instance_of(Array) + result[1].should be_an_instance_of(Array) + end +end diff --git a/spec/rubyspec/core/array/permutation_spec.rb b/spec/rubyspec/core/array/permutation_spec.rb new file mode 100644 index 0000000000..8a80b93c3b --- /dev/null +++ b/spec/rubyspec/core/array/permutation_spec.rb @@ -0,0 +1,138 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + + +describe "Array#permutation" do + + before :each do + @numbers = (1..3).to_a + @yielded = [] + end + + it "returns an Enumerator of all permutations when called without a block or arguments" do + enum = @numbers.permutation + enum.should be_an_instance_of(Enumerator) + enum.to_a.sort.should == [ + [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] + ].sort + end + + it "returns an Enumerator of permutations of given length when called with an argument but no block" do + enum = @numbers.permutation(1) + enum.should be_an_instance_of(Enumerator) + enum.to_a.sort.should == [[1],[2],[3]] + end + + it "yields all permutations to the block then returns self when called with block but no arguments" do + array = @numbers.permutation {|n| @yielded << n} + array.should be_an_instance_of(Array) + array.sort.should == @numbers.sort + @yielded.sort.should == [ + [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] + ].sort + end + + it "yields all permutations of given length to the block then returns self when called with block and argument" do + array = @numbers.permutation(2) {|n| @yielded << n} + array.should be_an_instance_of(Array) + array.sort.should == @numbers.sort + @yielded.sort.should == [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]].sort + end + + it "returns the empty permutation ([[]]) when the given length is 0" do + @numbers.permutation(0).to_a.should == [[]] + @numbers.permutation(0) { |n| @yielded << n } + @yielded.should == [[]] + end + + it "returns the empty permutation([]) when called on an empty Array" do + [].permutation.to_a.should == [[]] + [].permutation { |n| @yielded << n } + @yielded.should == [[]] + end + + it "returns no permutations when the given length has no permutations" do + @numbers.permutation(9).entries.size == 0 + @numbers.permutation(9) { |n| @yielded << n } + @yielded.should == [] + end + + it "handles duplicate elements correctly" do + @numbers << 1 + @numbers.permutation(2).sort.should == [ + [1,1],[1,1],[1,2],[1,2],[1,3],[1,3], + [2,1],[2,1],[2,3], + [3,1],[3,1],[3,2] + ].sort + end + + it "handles nested Arrays correctly" do + # The ugliness is due to the order of permutations returned by + # permutation being undefined combined with #sort croaking on Arrays of + # Arrays. + @numbers << [4,5] + got = @numbers.permutation(2).to_a + expected = [ + [1, 2], [1, 3], [1, [4, 5]], + [2, 1], [2, 3], [2, [4, 5]], + [3, 1], [3, 2], [3, [4, 5]], + [[4, 5], 1], [[4, 5], 2], [[4, 5], 3] + ] + expected.each {|e| got.include?(e).should be_true} + got.size.should == expected.size + end + + it "truncates Float arguments" do + @numbers.permutation(3.7).to_a.sort.should == + @numbers.permutation(3).to_a.sort + end + + it "returns an Enumerator which works as expected even when the array was modified" do + @numbers = [1, 2] + enum = @numbers.permutation + @numbers << 3 + enum.to_a.sort.should == [ + [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] + ].sort + end + + it "generates from a defensive copy, ignoring mutations" do + accum = [] + ary = [1,2,3] + ary.permutation(3) do |x| + accum << x + ary[0] = 5 + end + + accum.should == [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + describe "with an array size greater than 0" do + it "returns the descending factorial of array size and given length" do + @numbers.permutation(4).size.should == 0 + @numbers.permutation(3).size.should == 6 + @numbers.permutation(2).size.should == 6 + @numbers.permutation(1).size.should == 3 + @numbers.permutation(0).size.should == 1 + end + it "returns the descending factorial of array size with array size when there's no param" do + @numbers.permutation.size.should == 6 + [1,2,3,4].permutation.size.should == 24 + [1].permutation.size.should == 1 + end + end + describe "with an empty array" do + it "returns 1 when the given length is 0" do + [].permutation(0).size.should == 1 + end + it "returns 1 when there's param" do + [].permutation.size.should == 1 + end + end + end + end + end +end diff --git a/spec/rubyspec/core/array/plus_spec.rb b/spec/rubyspec/core/array/plus_spec.rb new file mode 100644 index 0000000000..4517087550 --- /dev/null +++ b/spec/rubyspec/core/array/plus_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#+" do + it "concatenates two arrays" do + ([ 1, 2, 3 ] + [ 3, 4, 5 ]).should == [1, 2, 3, 3, 4, 5] + ([ 1, 2, 3 ] + []).should == [1, 2, 3] + ([] + [ 1, 2, 3 ]).should == [1, 2, 3] + ([] + []).should == [] + end + + it "can concatenate an array with itself" do + ary = [1, 2, 3] + (ary + ary).should == [1, 2, 3, 1, 2, 3] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('["x", "y"]') + obj.should_receive(:to_ary).and_return(["x", "y"]) + ([1, 2, 3] + obj).should == [1, 2, 3, "x", "y"] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + (empty + empty).should == [empty, empty] + + array = ArraySpecs.recursive_array + (empty + array).should == [empty, 1, 'two', 3.0, array, array, array, array, array] + (array + array).should == [ + 1, 'two', 3.0, array, array, array, array, array, + 1, 'two', 3.0, array, array, array, array, array] + end + + it "does return subclass instances with Array subclasses" do + (ArraySpecs::MyArray[1, 2, 3] + []).should be_an_instance_of(Array) + (ArraySpecs::MyArray[1, 2, 3] + ArraySpecs::MyArray[]).should be_an_instance_of(Array) + ([1, 2, 3] + ArraySpecs::MyArray[]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + ([5, 6] + ArraySpecs::ToAryArray[1, 2]).should == [5, 6, 1, 2] + end + + it "does not get infected even if an original array is tainted" do + ([1, 2] + [3, 4]).tainted?.should be_false + ([1, 2].taint + [3, 4]).tainted?.should be_false + ([1, 2] + [3, 4].taint).tainted?.should be_false + ([1, 2].taint + [3, 4].taint).tainted?.should be_false + end + + it "does not infected even if an original array is untrusted" do + ([1, 2] + [3, 4]).untrusted?.should be_false + ([1, 2].untrust + [3, 4]).untrusted?.should be_false + ([1, 2] + [3, 4].untrust).untrusted?.should be_false + ([1, 2].untrust + [3, 4].untrust).untrusted?.should be_false + end +end diff --git a/spec/rubyspec/core/array/pop_spec.rb b/spec/rubyspec/core/array/pop_spec.rb new file mode 100644 index 0000000000..313dc4189e --- /dev/null +++ b/spec/rubyspec/core/array/pop_spec.rb @@ -0,0 +1,168 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#pop" do + it "removes and returns the last element of the array" do + a = ["a", 1, nil, true] + + a.pop.should == true + a.should == ["a", 1, nil] + + a.pop.should == nil + a.should == ["a", 1] + + a.pop.should == 1 + a.should == ["a"] + + a.pop.should == "a" + a.should == [] + end + + it "returns nil if there are no more elements" do + [].pop.should == nil + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.pop.should == [] + + array = ArraySpecs.recursive_array + array.pop.should == [1, 'two', 3.0, array, array, array, array] + end + + it "keeps taint status" do + a = [1, 2].taint + a.pop + a.tainted?.should be_true + a.pop + a.tainted?.should be_true + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.pop }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError on an empty frozen array" do + lambda { ArraySpecs.empty_frozen_array.pop }.should raise_error(RuntimeError) + end + + it "keeps untrusted status" do + a = [1, 2].untrust + a.pop + a.untrusted?.should be_true + a.pop + a.untrusted?.should be_true + end + + describe "passed a number n as an argument" do + it "removes and returns an array with the last n elements of the array" do + a = [1, 2, 3, 4, 5, 6] + + a.pop(0).should == [] + a.should == [1, 2, 3, 4, 5, 6] + + a.pop(1).should == [6] + a.should == [1, 2, 3, 4, 5] + + a.pop(2).should == [4, 5] + a.should == [1, 2, 3] + + a.pop(3).should == [1, 2, 3] + a.should == [] + end + + it "returns an array with the last n elements even if shift was invoked" do + a = [1, 2, 3, 4] + a.shift + a.pop(3).should == [2, 3, 4] + end + + it "returns a new empty array if there are no more elements" do + a = [] + popped1 = a.pop(1) + popped1.should == [] + a.should == [] + + popped2 = a.pop(2) + popped2.should == [] + a.should == [] + + popped1.should_not equal(popped2) + end + + it "returns whole elements if n exceeds size of the array" do + a = [1, 2, 3, 4, 5] + a.pop(6).should == [1, 2, 3, 4, 5] + a.should == [] + end + + it "does not return self even when it returns whole elements" do + a = [1, 2, 3, 4, 5] + a.pop(5).should_not equal(a) + + a = [1, 2, 3, 4, 5] + a.pop(6).should_not equal(a) + end + + it "raises an ArgumentError if n is negative" do + lambda{ [1, 2, 3].pop(-1) }.should raise_error(ArgumentError) + end + + it "tries to convert n to an Integer using #to_int" do + a = [1, 2, 3, 4] + a.pop(2.3).should == [3, 4] + + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + a.should == [1, 2] + a.pop(obj).should == [1, 2] + a.should == [] + end + + it "raises a TypeError when the passed n can be coerced to Integer" do + lambda{ [1, 2].pop("cat") }.should raise_error(TypeError) + lambda{ [1, 2].pop(nil) }.should raise_error(TypeError) + end + + it "raises an ArgumentError if more arguments are passed" do + lambda{ [1, 2].pop(1, 2) }.should raise_error(ArgumentError) + end + + it "does not return subclass instances with Array subclass" do + ArraySpecs::MyArray[1, 2, 3].pop(2).should be_an_instance_of(Array) + end + + it "returns an untainted array even if the array is tainted" do + ary = [1, 2].taint + ary.pop(2).tainted?.should be_false + ary.pop(0).tainted?.should be_false + end + + it "keeps taint status" do + a = [1, 2].taint + a.pop(2) + a.tainted?.should be_true + a.pop(2) + a.tainted?.should be_true + end + + it "returns a trusted array even if the array is untrusted" do + ary = [1, 2].untrust + ary.pop(2).untrusted?.should be_false + ary.pop(0).untrusted?.should be_false + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.pop(2) }.should raise_error(RuntimeError) + lambda { ArraySpecs.frozen_array.pop(0) }.should raise_error(RuntimeError) + end + + it "keeps untrusted status" do + a = [1, 2].untrust + a.pop(2) + a.untrusted?.should be_true + a.pop(2) + a.untrusted?.should be_true + end + end +end diff --git a/spec/rubyspec/core/array/product_spec.rb b/spec/rubyspec/core/array/product_spec.rb new file mode 100644 index 0000000000..1ab38e34be --- /dev/null +++ b/spec/rubyspec/core/array/product_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#product" do + it "returns converted arguments using :to_ary" do + lambda{ [1].product(2..3) }.should raise_error(TypeError) + ar = ArraySpecs::ArrayConvertable.new(2,3) + [1].product(ar).should == [[1,2],[1,3]] + ar.called.should == :to_ary + end + + it "returns the expected result" do + [1,2].product([3,4,5],[6,8]).should == [[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8], + [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]] + end + + it "has no required argument" do + [1,2].product.should == [[1],[2]] + end + + it "returns an empty array when the argument is an empty array" do + [1, 2].product([]).should == [] + end + + it "does not attempt to produce an unreasonable number of products" do + a = (0..100).to_a + lambda do + a.product(a, a, a, a, a, a, a, a, a, a) + end.should raise_error(RangeError) + end + + describe "when given a block" do + it "yields all combinations in turn" do + acc = [] + [1,2].product([3,4,5],[6,8]){|array| acc << array} + acc.should == [[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8], + [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]] + + acc = [] + [1,2].product([3,4,5],[],[6,8]){|array| acc << array} + acc.should be_empty + end + + it "returns self" do + a = [1, 2, 3].freeze + + a.product([1, 2]) { |p| p.first }.should == a + end + + it "will ignore unreasonable numbers of products and yield anyway" do + a = (0..100).to_a + lambda do + a.product(a, a, a, a, a, a, a, a, a, a) + end.should raise_error(RangeError) + end + end + + describe "when given an empty block" do + it "returns self" do + arr = [1,2] + arr.product([3,4,5],[6,8]){}.should equal(arr) + arr = [] + arr.product([3,4,5],[6,8]){}.should equal(arr) + arr = [1,2] + arr.product([]){}.should equal(arr) + end + end +end diff --git a/spec/rubyspec/core/array/push_spec.rb b/spec/rubyspec/core/array/push_spec.rb new file mode 100644 index 0000000000..562a6888dd --- /dev/null +++ b/spec/rubyspec/core/array/push_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#push" do + it "appends the arguments to the array" do + a = [ "a", "b", "c" ] + a.push("d", "e", "f").should equal(a) + a.push().should == ["a", "b", "c", "d", "e", "f"] + a.push(5) + a.should == ["a", "b", "c", "d", "e", "f", 5] + + a = [0, 1] + a.push(2) + a.should == [0, 1, 2] + end + + it "isn't confused by previous shift" do + a = [ "a", "b", "c" ] + a.shift + a.push("foo") + a.should == ["b", "c", "foo"] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.push(:last).should == [empty, :last] + + array = ArraySpecs.recursive_array + array.push(:last).should == [1, 'two', 3.0, array, array, array, array, array, :last] + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.push(1) }.should raise_error(RuntimeError) + lambda { ArraySpecs.frozen_array.push }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/rassoc_spec.rb b/spec/rubyspec/core/array/rassoc_spec.rb new file mode 100644 index 0000000000..cf3daccfc9 --- /dev/null +++ b/spec/rubyspec/core/array/rassoc_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#rassoc" do + it "returns the first contained array whose second element is == object" do + ary = [[1, "a", 0.5], [2, "b"], [3, "b"], [4, "c"], [], [5], [6, "d"]] + ary.rassoc("a").should == [1, "a", 0.5] + ary.rassoc("b").should == [2, "b"] + ary.rassoc("d").should == [6, "d"] + ary.rassoc("z").should == nil + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.rassoc([]).should be_nil + [[empty, empty]].rassoc(empty).should == [empty, empty] + + array = ArraySpecs.recursive_array + array.rassoc(array).should be_nil + [[empty, array]].rassoc(array).should == [empty, array] + end + + it "calls elem == obj on the second element of each contained array" do + key = 'foobar' + o = mock('foobar') + def o.==(other); other == 'foobar'; end + + [[1, :foobar], [2, o], [3, mock('foo')]].rassoc(key).should == [2, o] + end + + it "does not check the last element in each contained but speficically the second" do + key = 'foobar' + o = mock('foobar') + def o.==(other); other == 'foobar'; end + + [[1, :foobar, o], [2, o, 1], [3, mock('foo')]].rassoc(key).should == [2, o, 1] + end +end diff --git a/spec/rubyspec/core/array/reject_spec.rb b/spec/rubyspec/core/array/reject_spec.rb new file mode 100644 index 0000000000..857cbf6a4d --- /dev/null +++ b/spec/rubyspec/core/array/reject_spec.rb @@ -0,0 +1,117 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorize', __FILE__) +require File.expand_path('../shared/delete_if', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Array#reject" do + it "returns a new array without elements for which block is true" do + ary = [1, 2, 3, 4, 5] + ary.reject { true }.should == [] + ary.reject { false }.should == ary + ary.reject { false }.object_id.should_not == ary.object_id + ary.reject { nil }.should == ary + ary.reject { nil }.object_id.should_not == ary.object_id + ary.reject { 5 }.should == [] + ary.reject { |i| i < 3 }.should == [3, 4, 5] + ary.reject { |i| i % 2 == 0 }.should == [1, 3, 5] + end + + it "returns self when called on an Array emptied with #shift" do + array = [1] + array.shift + array.reject { |x| true }.should == [] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.reject { false }.should == [empty] + empty.reject { true }.should == [] + + array = ArraySpecs.recursive_array + array.reject { false }.should == [1, 'two', 3.0, array, array, array, array, array] + array.reject { true }.should == [] + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].reject { |x| x % 2 == 0 }.should be_an_instance_of(Array) + end + + it "does not retain instance variables" do + array = [] + array.instance_variable_set("@variable", "value") + array.reject { false }.instance_variable_get("@variable").should == nil + end + + it_behaves_like :enumeratorize, :reject + it_behaves_like :enumeratorized_with_origin_size, :reject, [1,2,3] +end + +describe "Array#reject!" do + it "removes elements for which block is true" do + a = [3, 4, 5, 6, 7, 8, 9, 10, 11] + a.reject! { |i| i % 2 == 0 }.should equal(a) + a.should == [3, 5, 7, 9, 11] + a.reject! { |i| i > 8 } + a.should == [3, 5, 7] + a.reject! { |i| i < 4 } + a.should == [5, 7] + a.reject! { |i| i == 5 } + a.should == [7] + a.reject! { true } + a.should == [] + a.reject! { true } + a.should == [] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty_dup = empty.dup + empty.reject! { false }.should == nil + empty.should == empty_dup + + empty = ArraySpecs.empty_recursive_array + empty.reject! { true }.should == [] + empty.should == [] + + array = ArraySpecs.recursive_array + array_dup = array.dup + array.reject! { false }.should == nil + array.should == array_dup + + array = ArraySpecs.recursive_array + array.reject! { true }.should == [] + array.should == [] + end + + it "returns nil when called on an Array emptied with #shift" do + array = [1] + array.shift + array.reject! { |x| true }.should == nil + end + + it "returns nil if no changes are made" do + a = [1, 2, 3] + + a.reject! { |i| i < 0 }.should == nil + + a.reject! { true } + a.reject! { true }.should == nil + end + + it "returns an Enumerator if no block given, and the array is frozen" do + ArraySpecs.frozen_array.reject!.should be_an_instance_of(Enumerator) + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.reject! {} }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError on an empty frozen array" do + lambda { ArraySpecs.empty_frozen_array.reject! {} }.should raise_error(RuntimeError) + end + + it_behaves_like :enumeratorize, :reject! + it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3] + it_behaves_like :delete_if, :reject! +end diff --git a/spec/rubyspec/core/array/repeated_combination_spec.rb b/spec/rubyspec/core/array/repeated_combination_spec.rb new file mode 100644 index 0000000000..e79c34a520 --- /dev/null +++ b/spec/rubyspec/core/array/repeated_combination_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#repeated_combination" do + before :each do + @array = [10, 11, 12] + end + + it "returns an enumerator when no block is provided" do + @array.repeated_combination(2).should be_an_instance_of(Enumerator) + end + + it "returns self when a block is given" do + @array.repeated_combination(2){}.should equal(@array) + end + + it "yields nothing for negative length and return self" do + @array.repeated_combination(-1){ fail }.should equal(@array) + @array.repeated_combination(-10){ fail }.should equal(@array) + end + + it "yields the expected repeated_combinations" do + @array.repeated_combination(2).to_a.sort.should == [[10, 10], [10, 11], [10, 12], [11, 11], [11, 12], [12, 12]] + @array.repeated_combination(3).to_a.sort.should == [[10, 10, 10], [10, 10, 11], [10, 10, 12], [10, 11, 11], [10, 11, 12], + [10, 12, 12], [11, 11, 11], [11, 11, 12], [11, 12, 12], [12, 12, 12]] + end + + it "yields [] when length is 0" do + @array.repeated_combination(0).to_a.should == [[]] # one repeated_combination of length 0 + [].repeated_combination(0).to_a.should == [[]] # one repeated_combination of length 0 + end + + it "yields nothing when the array is empty and num is non zero" do + [].repeated_combination(5).to_a.should == [] # one repeated_combination of length 0 + end + + it "yields a partition consisting of only singletons" do + @array.repeated_combination(1).sort.to_a.should == [[10],[11],[12]] + end + + it "accepts sizes larger than the original array" do + @array.repeated_combination(4).to_a.sort.should == + [[10, 10, 10, 10], [10, 10, 10, 11], [10, 10, 10, 12], + [10, 10, 11, 11], [10, 10, 11, 12], [10, 10, 12, 12], + [10, 11, 11, 11], [10, 11, 11, 12], [10, 11, 12, 12], + [10, 12, 12, 12], [11, 11, 11, 11], [11, 11, 11, 12], + [11, 11, 12, 12], [11, 12, 12, 12], [12, 12, 12, 12]] + end + + it "generates from a defensive copy, ignoring mutations" do + accum = [] + @array.repeated_combination(2) do |x| + accum << x + @array[0] = 1 + end + accum.sort.should == [[10, 10], [10, 11], [10, 12], [11, 11], [11, 12], [12, 12]] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "returns 0 when the combination_size is < 0" do + @array.repeated_combination(-1).size.should == 0 + [].repeated_combination(-2).size.should == 0 + end + + it "returns 1 when the combination_size is 0" do + @array.repeated_combination(0).size.should == 1 + [].repeated_combination(0).size.should == 1 + end + + it "returns the binomial coeficient between combination_size and array size + combination_size -1" do + @array.repeated_combination(5).size.should == 21 + @array.repeated_combination(4).size.should == 15 + @array.repeated_combination(3).size.should == 10 + @array.repeated_combination(2).size.should == 6 + @array.repeated_combination(1).size.should == 3 + @array.repeated_combination(0).size.should == 1 + [].repeated_combination(0).size.should == 1 + [].repeated_combination(1).size.should == 0 + end + end + end + end +end diff --git a/spec/rubyspec/core/array/repeated_permutation_spec.rb b/spec/rubyspec/core/array/repeated_permutation_spec.rb new file mode 100644 index 0000000000..9038d49560 --- /dev/null +++ b/spec/rubyspec/core/array/repeated_permutation_spec.rb @@ -0,0 +1,94 @@ +require File.expand_path('../../../spec_helper', __FILE__) + + +describe "Array#repeated_permutation" do + + before :each do + @numbers = [10, 11, 12] + @permutations = [[10, 10], [10, 11], [10, 12], [11, 10], [11, 11], [11, 12], [12, 10], [12, 11], [12, 12]] + end + + it "returns an Enumerator of all repeated permutations of given length when called without a block" do + enum = @numbers.repeated_permutation(2) + enum.should be_an_instance_of(Enumerator) + enum.to_a.sort.should == @permutations + end + + it "yields all repeated_permutations to the block then returns self when called with block but no arguments" do + yielded = [] + @numbers.repeated_permutation(2) {|n| yielded << n}.should equal(@numbers) + yielded.sort.should == @permutations + end + + it "yields the empty repeated_permutation ([[]]) when the given length is 0" do + @numbers.repeated_permutation(0).to_a.should == [[]] + [].repeated_permutation(0).to_a.should == [[]] + end + + it "does not yield when called on an empty Array with a nonzero argument" do + [].repeated_permutation(10).to_a.should == [] + end + + it "handles duplicate elements correctly" do + @numbers[-1] = 10 + @numbers.repeated_permutation(2).sort.should == + [[10, 10], [10, 10], [10, 10], [10, 10], [10, 11], [10, 11], [11, 10], [11, 10], [11, 11]] + end + + it "truncates Float arguments" do + @numbers.repeated_permutation(3.7).to_a.sort.should == + @numbers.repeated_permutation(3).to_a.sort + end + + it "returns an Enumerator which works as expected even when the array was modified" do + @numbers.shift + enum = @numbers.repeated_permutation(2) + @numbers.unshift 10 + enum.to_a.sort.should == @permutations + end + + it "allows permutations larger than the number of elements" do + [1,2].repeated_permutation(3).sort.should == + [[1, 1, 1], [1, 1, 2], [1, 2, 1], + [1, 2, 2], [2, 1, 1], [2, 1, 2], + [2, 2, 1], [2, 2, 2]] + end + + it "generates from a defensive copy, ignoring mutations" do + accum = [] + ary = [1,2] + ary.repeated_permutation(3) do |x| + accum << x + ary[0] = 5 + end + + accum.sort.should == + [[1, 1, 1], [1, 1, 2], [1, 2, 1], + [1, 2, 2], [2, 1, 1], [2, 1, 2], + [2, 2, 1], [2, 2, 2]] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "returns 0 when combination_size is < 0" do + @numbers.repeated_permutation(-1).size.should == 0 + [].repeated_permutation(-1).size.should == 0 + end + + it "returns array size ** combination_size" do + @numbers.repeated_permutation(4).size.should == 81 + @numbers.repeated_permutation(3).size.should == 27 + @numbers.repeated_permutation(2).size.should == 9 + @numbers.repeated_permutation(1).size.should == 3 + @numbers.repeated_permutation(0).size.should == 1 + [].repeated_permutation(4).size.should == 0 + [].repeated_permutation(3).size.should == 0 + [].repeated_permutation(2).size.should == 0 + [].repeated_permutation(1).size.should == 0 + [].repeated_permutation(0).size.should == 1 + end + end + end + end +end diff --git a/spec/rubyspec/core/array/replace_spec.rb b/spec/rubyspec/core/array/replace_spec.rb new file mode 100644 index 0000000000..e8b0d53e04 --- /dev/null +++ b/spec/rubyspec/core/array/replace_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/replace', __FILE__) + +describe "Array#replace" do + it_behaves_like(:array_replace, :replace) +end diff --git a/spec/rubyspec/core/array/reverse_each_spec.rb b/spec/rubyspec/core/array/reverse_each_spec.rb new file mode 100644 index 0000000000..1bc0ed5ac5 --- /dev/null +++ b/spec/rubyspec/core/array/reverse_each_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorize', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +# Modifying a collection while the contents are being iterated +# gives undefined behavior. See +# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 + +describe "Array#reverse_each" do + before :each do + ScratchPad.record [] + end + + it "traverses array in reverse order and pass each element to block" do + [1, 3, 4, 6].reverse_each { |i| ScratchPad << i } + ScratchPad.recorded.should == [6, 4, 3, 1] + end + + it "returns self" do + a = [:a, :b, :c] + a.reverse_each { |x| }.should equal(a) + end + + it "yields only the top level element of an empty recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.reverse_each { |i| ScratchPad << i } + ScratchPad.recorded.should == [empty] + end + + it "yields only the top level element of a recursive array" do + array = ArraySpecs.recursive_array + array.reverse_each { |i| ScratchPad << i } + ScratchPad.recorded.should == [array, array, array, array, array, 3.0, 'two', 1] + end + + it "returns the correct size when no block is given" do + [1, 2, 3].reverse_each.size.should == 3 + end + + it_behaves_like :enumeratorize, :reverse_each + it_behaves_like :enumeratorized_with_origin_size, :reverse_each, [1,2,3] +end diff --git a/spec/rubyspec/core/array/reverse_spec.rb b/spec/rubyspec/core/array/reverse_spec.rb new file mode 100644 index 0000000000..a3a6db9506 --- /dev/null +++ b/spec/rubyspec/core/array/reverse_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#reverse" do + it "returns a new array with the elements in reverse order" do + [].reverse.should == [] + [1, 3, 5, 2].reverse.should == [2, 5, 3, 1] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.reverse.should == empty + + array = ArraySpecs.recursive_array + array.reverse.should == [array, array, array, array, array, 3.0, 'two', 1] + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].reverse.should be_an_instance_of(Array) + end +end + +describe "Array#reverse!" do + it "reverses the elements in place" do + a = [6, 3, 4, 2, 1] + a.reverse!.should equal(a) + a.should == [1, 2, 4, 3, 6] + [].reverse!.should == [] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.reverse!.should == [empty] + + array = ArraySpecs.recursive_array + array.reverse!.should == [array, array, array, array, array, 3.0, 'two', 1] + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.reverse! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/rindex_spec.rb b/spec/rubyspec/core/array/rindex_spec.rb new file mode 100644 index 0000000000..19a6f04c85 --- /dev/null +++ b/spec/rubyspec/core/array/rindex_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +# Modifying a collection while the contents are being iterated +# gives undefined behavior. See +# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/23633 + +describe "Array#rindex" do + it "returns the first index backwards from the end where element == to object" do + key = 3 + uno = mock('one') + dos = mock('two') + tres = mock('three') + tres.should_receive(:==).any_number_of_times.and_return(false) + dos.should_receive(:==).any_number_of_times.and_return(true) + uno.should_not_receive(:==) + ary = [uno, dos, tres] + + ary.rindex(key).should == 1 + end + + it "returns size-1 if last element == to object" do + [2, 1, 3, 2, 5].rindex(5).should == 4 + end + + it "returns 0 if only first element == to object" do + [2, 1, 3, 1, 5].rindex(2).should == 0 + end + + it "returns nil if no element == to object" do + [1, 1, 3, 2, 1, 3].rindex(4).should == nil + end + + it "returns correct index even after delete_at" do + array = ["fish", "bird", "lion", "cat"] + array.delete_at(0) + array.rindex("lion").should == 1 + end + + it "properly handles empty recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.rindex(empty).should == 0 + empty.rindex(1).should be_nil + end + + it "properly handles recursive arrays" do + array = ArraySpecs.recursive_array + array.rindex(1).should == 0 + array.rindex(array).should == 7 + end + + it "accepts a block instead of an argument" do + [4, 2, 1, 5, 1, 3].rindex { |x| x < 2 }.should == 4 + end + + it "ignores the block if there is an argument" do + -> { + [4, 2, 1, 5, 1, 3].rindex(5) { |x| x < 2 }.should == 3 + }.should complain(/given block not used/) + end + + it "rechecks the array size during iteration" do + ary = [4, 2, 1, 5, 1, 3] + seen = [] + ary.rindex { |x| seen << x; ary.clear; false } + + seen.should == [3] + end + + describe "given no argument and no block" do + it "produces an Enumerator" do + enum = [4, 2, 1, 5, 1, 3].rindex + enum.should be_an_instance_of(Enumerator) + enum.each { |x| x < 2 }.should == 4 + end + end + + it_behaves_like :enumeratorized_with_unknown_size, :bsearch, [1,2,3] +end diff --git a/spec/rubyspec/core/array/rotate_spec.rb b/spec/rubyspec/core/array/rotate_spec.rb new file mode 100644 index 0000000000..270bfeb446 --- /dev/null +++ b/spec/rubyspec/core/array/rotate_spec.rb @@ -0,0 +1,129 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#rotate" do + describe "when passed no argument" do + it "returns a copy of the array with the first element moved at the end" do + [1, 2, 3, 4, 5].rotate.should == [2, 3, 4, 5, 1] + end + end + + describe "with an argument n" do + it "returns a copy of the array with the first (n % size) elements moved at the end" do + a = [1, 2, 3, 4, 5] + a.rotate( 2).should == [3, 4, 5, 1, 2] + a.rotate( -1).should == [5, 1, 2, 3, 4] + a.rotate(-21).should == [5, 1, 2, 3, 4] + a.rotate( 13).should == [4, 5, 1, 2, 3] + a.rotate( 0).should == a + end + + it "coerces the argument using to_int" do + [1, 2, 3].rotate(2.6).should == [3, 1, 2] + + obj = mock('integer_like') + obj.should_receive(:to_int).and_return(2) + [1, 2, 3].rotate(obj).should == [3, 1, 2] + end + + it "raises a TypeError if not passed an integer-like argument" do + lambda { + [1, 2].rotate(nil) + }.should raise_error(TypeError) + lambda { + [1, 2].rotate("4") + }.should raise_error(TypeError) + end + end + + it "returns a copy of the array when its length is one or zero" do + [1].rotate.should == [1] + [1].rotate(2).should == [1] + [1].rotate(-42).should == [1] + [ ].rotate.should == [] + [ ].rotate(2).should == [] + [ ].rotate(-42).should == [] + end + + it "does not mutate the receiver" do + lambda { + [].freeze.rotate + [2].freeze.rotate(2) + [1,2,3].freeze.rotate(-3) + }.should_not raise_error + end + + it "does not return self" do + a = [1, 2, 3] + a.rotate.should_not equal(a) + a = [] + a.rotate(0).should_not equal(a) + end + + it "does not return subclass instance for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].rotate.should be_an_instance_of(Array) + end +end + +describe "Array#rotate!" do + describe "when passed no argument" do + it "moves the first element to the end and returns self" do + a = [1, 2, 3, 4, 5] + a.rotate!.should equal(a) + a.should == [2, 3, 4, 5, 1] + end + end + + describe "with an argument n" do + it "moves the first (n % size) elements at the end and returns self" do + a = [1, 2, 3, 4, 5] + a.rotate!(2).should equal(a) + a.should == [3, 4, 5, 1, 2] + a.rotate!(-12).should equal(a) + a.should == [1, 2, 3, 4, 5] + a.rotate!(13).should equal(a) + a.should == [4, 5, 1, 2, 3] + end + + it "coerces the argument using to_int" do + [1, 2, 3].rotate!(2.6).should == [3, 1, 2] + + obj = mock('integer_like') + obj.should_receive(:to_int).and_return(2) + [1, 2, 3].rotate!(obj).should == [3, 1, 2] + end + + it "raises a TypeError if not passed an integer-like argument" do + lambda { + [1, 2].rotate!(nil) + }.should raise_error(TypeError) + lambda { + [1, 2].rotate!("4") + }.should raise_error(TypeError) + end + end + + it "does nothing and returns self when the length is zero or one" do + a = [1] + a.rotate!.should equal(a) + a.should == [1] + a.rotate!(2).should equal(a) + a.should == [1] + a.rotate!(-21).should equal(a) + a.should == [1] + + a = [] + a.rotate!.should equal(a) + a.should == [] + a.rotate!(2).should equal(a) + a.should == [] + a.rotate!(-21).should equal(a) + a.should == [] + end + + it "raises a RuntimeError on a frozen array" do + lambda { [1, 2, 3].freeze.rotate!(0) }.should raise_error(RuntimeError) + lambda { [1].freeze.rotate!(42) }.should raise_error(RuntimeError) + lambda { [].freeze.rotate! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/sample_spec.rb b/spec/rubyspec/core/array/sample_spec.rb new file mode 100644 index 0000000000..53601dd5c4 --- /dev/null +++ b/spec/rubyspec/core/array/sample_spec.rb @@ -0,0 +1,155 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#sample" do + it "samples evenly" do + ary = [0, 1, 2, 3] + 3.times do |i| + counts = [0, 0, 0, 0] + 4000.times do + counts[ary.sample(3)[i]] += 1 + end + counts.each do |count| + (800..1200).should include(count) + end + end + end + + it "returns nil for an empty Array" do + [].sample.should be_nil + end + + it "returns a single value when not passed a count" do + [4].sample.should equal(4) + end + + it "returns an empty Array when passed zero" do + [4].sample(0).should == [] + end + + it "returns an Array of elements when passed a count" do + [1, 2, 3, 4].sample(3).should be_an_instance_of(Array) + end + + it "returns elements from the Array" do + array = [1, 2, 3, 4] + array.sample(3).all? { |x| array.should include(x) } + end + + it "returns at most the number of elements in the Array" do + array = [1, 2, 3, 4] + result = array.sample(20) + result.size.should == 4 + end + + it "does not return the same value if the Array has unique values" do + array = [1, 2, 3, 4] + result = array.sample(20) + result.sort.should == array + end + + it "may return the same value if the array is not unique" do + [4, 4].sample(2).should == [4,4] + end + + it "calls #to_int to convert the count when passed an Object" do + [1, 2, 3, 4].sample(mock_int(2)).size.should == 2 + end + + it "raises ArgumentError when passed a negative count" do + lambda { [1, 2].sample(-1) }.should raise_error(ArgumentError) + end + + it "does not return subclass instances with Array subclass" do + ArraySpecs::MyArray[1, 2, 3].sample(2).should be_an_instance_of(Array) + end + + describe "with options" do + it "calls #to_hash to convert the passed Object" do + obj = mock("array_sample") + obj.should_receive(:to_hash).and_return({}) + obj.should_not_receive(:to_int) + + [1, 2].sample(obj).should be_an_instance_of(Fixnum) + end + + it "calls #to_int on the first argument and #to_hash on the second when passed Objects" do + count = mock("array_sample_count") + count.should_receive(:to_int).and_return(2) + options = mock("array_sample_options") + options.should_receive(:to_hash).and_return({}) + + [1, 2].sample(count, options).size.should == 2 + end + + it "calls #rand on the Object passed by the :random key in the arguments Hash" do + obj = mock("array_sample_random") + obj.should_receive(:rand).and_return(0.5) + + [1, 2].sample(random: obj).should be_an_instance_of(Fixnum) + end + + it "raises a NoMethodError if an object passed for the RNG does not define #rand" do + obj = BasicObject.new + + lambda { [1, 2].sample(random: obj) }.should raise_error(NoMethodError) + end + + describe "when the object returned by #rand is a Fixnum" do + it "uses the fixnum as index" do + random = mock("array_sample_random_ret") + random.should_receive(:rand).and_return(0) + + [1, 2].sample(random: random).should == 1 + + random = mock("array_sample_random_ret") + random.should_receive(:rand).and_return(1) + + [1, 2].sample(random: random).should == 2 + end + + it "raises a RangeError if the value is less than zero" do + random = mock("array_sample_random") + random.should_receive(:rand).and_return(-1) + + lambda { [1, 2].sample(random: random) }.should raise_error(RangeError) + end + + it "raises a RangeError if the value is equal to the Array size" do + random = mock("array_sample_random") + random.should_receive(:rand).and_return(2) + + lambda { [1, 2].sample(random: random) }.should raise_error(RangeError) + end + end + end + + describe "when the object returned by #rand is not a Fixnum but responds to #to_int" do + it "calls #to_int on the Object" do + value = mock("array_sample_random_value") + value.should_receive(:to_int).and_return(1) + random = mock("array_sample_random") + random.should_receive(:rand).and_return(value) + + [1, 2].sample(random: random).should == 2 + end + + it "raises a RangeError if the value is less than zero" do + value = mock("array_sample_random_value") + value.should_receive(:to_int).and_return(-1) + random = mock("array_sample_random") + random.should_receive(:rand).and_return(value) + + lambda { [1, 2].sample(random: random) }.should raise_error(RangeError) + end + + it "raises a RangeError if the value is equal to the Array size" do + value = mock("array_sample_random_value") + value.should_receive(:to_int).and_return(2) + random = mock("array_sample_random") + random.should_receive(:rand).and_return(value) + + lambda { [1, 2].sample(random: random) }.should raise_error(RangeError) + end + end +end diff --git a/spec/rubyspec/core/array/select_spec.rb b/spec/rubyspec/core/array/select_spec.rb new file mode 100644 index 0000000000..8b83acaa5f --- /dev/null +++ b/spec/rubyspec/core/array/select_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorize', __FILE__) +require File.expand_path('../shared/keep_if', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Array#select" do + it_behaves_like :enumeratorize, :select + it_behaves_like :enumeratorized_with_origin_size, :select, [1,2,3] + + it "returns a new array of elements for which block is true" do + [1, 3, 4, 5, 6, 9].select { |i| i % ((i + 1) / 2) == 0}.should == [1, 4, 6] + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].select { true }.should be_an_instance_of(Array) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.select { true }.should == empty + empty.select { false }.should == [] + + array = ArraySpecs.recursive_array + array.select { true }.should == [1, 'two', 3.0, array, array, array, array, array] + array.select { false }.should == [] + end +end + +describe "Array#select!" do + it "returns nil if no changes were made in the array" do + [1, 2, 3].select! { true }.should be_nil + end + + it_behaves_like :keep_if, :select! +end diff --git a/spec/rubyspec/core/array/shared/clone.rb b/spec/rubyspec/core/array/shared/clone.rb new file mode 100644 index 0000000000..6fc7ae31eb --- /dev/null +++ b/spec/rubyspec/core/array/shared/clone.rb @@ -0,0 +1,42 @@ +describe :array_clone, shared: true do + it "returns an Array or a subclass instance" do + [].send(@method).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2].send(@method).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "produces a shallow copy where the references are directly copied" do + a = [mock('1'), mock('2')] + b = a.send @method + b.first.object_id.should == a.first.object_id + b.last.object_id.should == a.last.object_id + end + + it "creates a new array containing all elements or the original" do + a = [1, 2, 3, 4] + b = a.send @method + b.should == a + b.__id__.should_not == a.__id__ + end + + it "copies taint status from the original" do + a = [1, 2, 3, 4] + b = [1, 2, 3, 4] + a.taint + aa = a.send @method + bb = b.send @method + + aa.tainted?.should == true + bb.tainted?.should == false + end + + it "copies untrusted status from the original" do + a = [1, 2, 3, 4] + b = [1, 2, 3, 4] + a.untrust + aa = a.send @method + bb = b.send @method + + aa.untrusted?.should == true + bb.untrusted?.should == false + end +end diff --git a/spec/rubyspec/core/array/shared/collect.rb b/spec/rubyspec/core/array/shared/collect.rb new file mode 100644 index 0000000000..f6bcfd8904 --- /dev/null +++ b/spec/rubyspec/core/array/shared/collect.rb @@ -0,0 +1,136 @@ +require File.expand_path('../../../enumerable/shared/enumeratorized', __FILE__) + +describe :array_collect, shared: true do + it "returns a copy of array with each element replaced by the value returned by block" do + a = ['a', 'b', 'c', 'd'] + b = a.send(@method) { |i| i + '!' } + b.should == ["a!", "b!", "c!", "d!"] + b.object_id.should_not == a.object_id + end + + it "does not return subclass instance" do + ArraySpecs::MyArray[1, 2, 3].send(@method) { |x| x + 1 }.should be_an_instance_of(Array) + end + + it "does not change self" do + a = ['a', 'b', 'c', 'd'] + a.send(@method) { |i| i + '!' } + a.should == ['a', 'b', 'c', 'd'] + end + + it "returns the evaluated value of block if it broke in the block" do + a = ['a', 'b', 'c', 'd'] + b = a.send(@method) {|i| + if i == 'c' + break 0 + else + i + '!' + end + } + b.should == 0 + end + + it "returns an Enumerator when no block given" do + a = [1, 2, 3] + a.send(@method).should be_an_instance_of(Enumerator) + end + + it "raises an ArgumentError when no block and with arguments" do + a = [1, 2, 3] + lambda { + a.send(@method, :foo) + }.should raise_error(ArgumentError) + end + + it "does not copy tainted status" do + a = [1, 2, 3] + a.taint + a.send(@method){|x| x}.tainted?.should be_false + end + + it "does not copy untrusted status" do + a = [1, 2, 3] + a.untrust + a.send(@method){|x| x}.untrusted?.should be_false + end + + before :all do + @object = [1, 2, 3, 4] + end + it_should_behave_like :enumeratorized_with_origin_size +end + +describe :array_collect_b, shared: true do + it "replaces each element with the value returned by block" do + a = [7, 9, 3, 5] + a.send(@method) { |i| i - 1 }.should equal(a) + a.should == [6, 8, 2, 4] + end + + it "returns self" do + a = [1, 2, 3, 4, 5] + b = a.send(@method) {|i| i+1 } + a.object_id.should == b.object_id + end + + it "returns the evaluated value of block but its contents is partially modified, if it broke in the block" do + a = ['a', 'b', 'c', 'd'] + b = a.send(@method) {|i| + if i == 'c' + break 0 + else + i + '!' + end + } + b.should == 0 + a.should == ['a!', 'b!', 'c', 'd'] + end + + it "returns an Enumerator when no block given, and the enumerator can modify the original array" do + a = [1, 2, 3] + enum = a.send(@method) + enum.should be_an_instance_of(Enumerator) + enum.each{|i| "#{i}!" } + a.should == ["1!", "2!", "3!"] + end + + it "keeps tainted status" do + a = [1, 2, 3] + a.taint + a.tainted?.should be_true + a.send(@method){|x| x} + a.tainted?.should be_true + end + + it "keeps untrusted status" do + a = [1, 2, 3] + a.untrust + a.send(@method){|x| x} + a.untrusted?.should be_true + end + + describe "when frozen" do + it "raises a RuntimeError" do + lambda { ArraySpecs.frozen_array.send(@method) {} }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when empty" do + lambda { ArraySpecs.empty_frozen_array.send(@method) {} }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when calling #each on the returned Enumerator" do + enumerator = ArraySpecs.frozen_array.send(@method) + lambda { enumerator.each {|x| x } }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when calling #each on the returned Enumerator when empty" do + enumerator = ArraySpecs.empty_frozen_array.send(@method) + lambda { enumerator.each {|x| x } }.should raise_error(RuntimeError) + end + end + + before :all do + @object = [1, 2, 3, 4] + end + it_should_behave_like :enumeratorized_with_origin_size +end diff --git a/spec/rubyspec/core/array/shared/delete_if.rb b/spec/rubyspec/core/array/shared/delete_if.rb new file mode 100644 index 0000000000..a9fb57e0d9 --- /dev/null +++ b/spec/rubyspec/core/array/shared/delete_if.rb @@ -0,0 +1,27 @@ +describe :delete_if, shared: true do + before :each do + @object = [1,2,3] + end + + ruby_version_is "2.3" do + it "updates the receiver after all blocks" do + @object.send(@method) do |e| + @object.length.should == 3 + true + end + @object.length.should == 0 + end + end + + ruby_version_is ""..."2.3" do + it "updates the receiver after each true block" do + count = 0 + @object.send(@method) do |e| + @object.length.should == (3 - count) + count += 1 + true + end + @object.length.should == 0 + end + end +end diff --git a/spec/rubyspec/core/array/shared/enumeratorize.rb b/spec/rubyspec/core/array/shared/enumeratorize.rb new file mode 100644 index 0000000000..a19a5d3b9b --- /dev/null +++ b/spec/rubyspec/core/array/shared/enumeratorize.rb @@ -0,0 +1,5 @@ +describe :enumeratorize, shared: true do + it "returns an Enumerator if no block given" do + [1,2].send(@method).should be_an_instance_of(Enumerator) + end +end diff --git a/spec/rubyspec/core/array/shared/eql.rb b/spec/rubyspec/core/array/shared/eql.rb new file mode 100644 index 0000000000..b5d9128434 --- /dev/null +++ b/spec/rubyspec/core/array/shared/eql.rb @@ -0,0 +1,92 @@ +describe :array_eql, shared: true do + it "returns true if other is the same array" do + a = [1] + a.send(@method, a).should be_true + end + + it "returns true if corresponding elements are #eql?" do + [].send(@method, []).should be_true + [1, 2, 3, 4].send(@method, [1, 2, 3, 4]).should be_true + end + + it "returns false if other is shorter than self" do + [1, 2, 3, 4].send(@method, [1, 2, 3]).should be_false + end + + it "returns false if other is longer than self" do + [1, 2, 3, 4].send(@method, [1, 2, 3, 4, 5]).should be_false + end + + it "returns false immediately when sizes of the arrays differ" do + obj = mock('1') + obj.should_not_receive(@method) + + [] .send(@method, [obj] ).should be_false + [obj] .send(@method, [] ).should be_false + end + + it "handles well recursive arrays" do + a = ArraySpecs.empty_recursive_array + a .send(@method, [a] ).should be_true + a .send(@method, [[a]] ).should be_true + [a] .send(@method, a ).should be_true + [[a]] .send(@method, a ).should be_true + # These may be surprising, but no difference can be + # found between these arrays, so they are ==. + # There is no "path" that will lead to a difference + # (contrary to other examples below) + + a2 = ArraySpecs.empty_recursive_array + a .send(@method, a2 ).should be_true + a .send(@method, [a2] ).should be_true + a .send(@method, [[a2]] ).should be_true + [a] .send(@method, a2 ).should be_true + [[a]] .send(@method, a2 ).should be_true + + back = [] + forth = [back]; back << forth; + back .send(@method, a ).should be_true + + x = []; x << x << x + x .send(@method, a ).should be_false # since x.size != a.size + x .send(@method, [a, a] ).should be_false # since x[0].size != [a, a][0].size + x .send(@method, [x, a] ).should be_false # since x[1].size != [x, a][1].size + [x, a] .send(@method, [a, x] ).should be_false # etc... + x .send(@method, [x, x] ).should be_true + x .send(@method, [[x, x], [x, x]] ).should be_true + + tree = []; + branch = []; branch << tree << tree; tree << branch + tree2 = []; + branch2 = []; branch2 << tree2 << tree2; tree2 << branch2 + forest = [tree, branch, :bird, a]; forest << forest + forest2 = [tree2, branch2, :bird, a2]; forest2 << forest2 + + forest .send(@method, forest2 ).should be_true + forest .send(@method, [tree2, branch, :bird, a, forest2]).should be_true + + diffforest = [branch2, tree2, :bird, a2]; diffforest << forest2 + forest .send(@method, diffforest ).should be_false # since forest[0].size == 1 != 3 == diffforest[0] + forest .send(@method, [nil] ).should be_false + forest .send(@method, [forest] ).should be_false + end + + it "does not call #to_ary on its argument" do + obj = mock('to_ary') + obj.should_not_receive(:to_ary) + + [1, 2, 3].send(@method, obj).should be_false + end + + it "does not call #to_ary on Array subclasses" do + ary = ArraySpecs::ToAryArray[5, 6, 7] + ary.should_not_receive(:to_ary) + [5, 6, 7].send(@method, ary).should be_true + end + + it "ignores array class differences" do + ArraySpecs::MyArray[1, 2, 3].send(@method, [1, 2, 3]).should be_true + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true + [1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_true + end +end diff --git a/spec/rubyspec/core/array/shared/index.rb b/spec/rubyspec/core/array/shared/index.rb new file mode 100644 index 0000000000..a9896554f2 --- /dev/null +++ b/spec/rubyspec/core/array/shared/index.rb @@ -0,0 +1,37 @@ +describe :array_index, shared: true do + it "returns the index of the first element == to object" do + x = mock('3') + def x.==(obj) 3 == obj; end + + [2, x, 3, 1, 3, 1].send(@method, 3).should == 1 + [2, 3.0, 3, x, 1, 3, 1].send(@method, x).should == 1 + end + + it "returns 0 if first element == to object" do + [2, 1, 3, 2, 5].send(@method, 2).should == 0 + end + + it "returns size-1 if only last element == to object" do + [2, 1, 3, 1, 5].send(@method, 5).should == 4 + end + + it "returns nil if no element == to object" do + [2, 1, 1, 1, 1].send(@method, 3).should == nil + end + + it "accepts a block instead of an argument" do + [4, 2, 1, 5, 1, 3].send(@method) {|x| x < 2}.should == 2 + end + + it "ignores the block if there is an argument" do + -> { + [4, 2, 1, 5, 1, 3].send(@method, 5) {|x| x < 2}.should == 3 + }.should complain(/given block not used/) + end + + describe "given no argument and no block" do + it "produces an Enumerator" do + [].send(@method).should be_an_instance_of(Enumerator) + end + end +end diff --git a/spec/rubyspec/core/array/shared/inspect.rb b/spec/rubyspec/core/array/shared/inspect.rb new file mode 100644 index 0000000000..823dd40e7e --- /dev/null +++ b/spec/rubyspec/core/array/shared/inspect.rb @@ -0,0 +1,104 @@ +require File.expand_path('../../fixtures/encoded_strings', __FILE__) + +describe :array_inspect, shared: true do + it "returns a string" do + [1, 2, 3].send(@method).should be_an_instance_of(String) + end + + it "returns '[]' for an empty Array" do + [].send(@method).should == "[]" + end + + it "calls inspect on its elements and joins the results with commas" do + items = Array.new(3) do |i| + obj = mock(i.to_s) + obj.should_receive(:inspect).and_return(i.to_s) + obj + end + items.send(@method).should == "[0, 1, 2]" + end + + it "represents a recursive element with '[...]'" do + ArraySpecs.recursive_array.send(@method).should == "[1, \"two\", 3.0, [...], [...], [...], [...], [...]]" + ArraySpecs.head_recursive_array.send(@method).should == "[[...], [...], [...], [...], [...], 1, \"two\", 3.0]" + ArraySpecs.empty_recursive_array.send(@method).should == "[[...]]" + end + + it "taints the result if the Array is non-empty and tainted" do + [1, 2].taint.send(@method).tainted?.should be_true + end + + it "does not taint the result if the Array is tainted but empty" do + [].taint.send(@method).tainted?.should be_false + end + + it "taints the result if an element is tainted" do + ["str".taint].send(@method).tainted?.should be_true + end + + it "untrusts the result if the Array is untrusted" do + [1, 2].untrust.send(@method).untrusted?.should be_true + end + + it "does not untrust the result if the Array is untrusted but empty" do + [].untrust.send(@method).untrusted?.should be_false + end + + it "untrusts the result if an element is untrusted" do + ["str".untrust].send(@method).untrusted?.should be_true + end + + describe "with encoding" do + before :each do + @default_external_encoding = Encoding.default_external + end + + after :each do + Encoding.default_external = @default_external_encoding + end + + it "returns a US-ASCII string for an empty Array" do + [].send(@method).encoding.should == Encoding::US_ASCII + end + + it "use the default external encoding if it is ascii compatible" do + Encoding.default_external = Encoding.find('UTF-8') + + utf8 = "utf8".encode("UTF-8") + jp = "jp".encode("EUC-JP") + array = [jp, utf8] + + array.send(@method).encoding.name.should == "UTF-8" + end + + it "use US-ASCII encoding if the default external encoding is not ascii compatible" do + Encoding.default_external = Encoding.find('UTF-32') + + utf8 = "utf8".encode("UTF-8") + jp = "jp".encode("EUC-JP") + array = [jp, utf8] + + array.send(@method).encoding.name.should == "US-ASCII" + end + + ruby_version_is ''...'2.3' do + it "raises if inspected result is not default external encoding" do + utf_16be = mock("utf_16be") + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE)) + + lambda { + [utf_16be].send(@method) + }.should raise_error(Encoding::CompatibilityError) + end + end + + ruby_version_is '2.3' do + it "does not raise if inspected result is not default external encoding" do + utf_16be = mock("utf_16be") + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE)) + + [utf_16be].send(@method).should == '["utf_16be \u3042"]' + end + end + end +end diff --git a/spec/rubyspec/core/array/shared/join.rb b/spec/rubyspec/core/array/shared/join.rb new file mode 100644 index 0000000000..fa66588b47 --- /dev/null +++ b/spec/rubyspec/core/array/shared/join.rb @@ -0,0 +1,161 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../fixtures/encoded_strings', __FILE__) + +describe :array_join_with_default_separator, shared: true do + before :each do + @separator = $, + end + + after :each do + $, = @separator + end + + it "returns an empty string if the Array is empty" do + [].send(@method).should == '' + end + + it "returns a US-ASCII string for an empty Array" do + [].send(@method).encoding.should == Encoding::US_ASCII + end + + it "returns a string formed by concatenating each String element separated by $," do + $, = " | " + ["1", "2", "3"].send(@method).should == "1 | 2 | 3" + end + + it "attempts coercion via #to_str first" do + obj = mock('foo') + obj.should_receive(:to_str).any_number_of_times.and_return("foo") + [obj].send(@method).should == "foo" + end + + it "attempts coercion via #to_ary second" do + obj = mock('foo') + obj.should_receive(:to_str).any_number_of_times.and_return(nil) + obj.should_receive(:to_ary).any_number_of_times.and_return(["foo"]) + [obj].send(@method).should == "foo" + end + + it "attempts coercion via #to_s third" do + obj = mock('foo') + obj.should_receive(:to_str).any_number_of_times.and_return(nil) + obj.should_receive(:to_ary).any_number_of_times.and_return(nil) + obj.should_receive(:to_s).any_number_of_times.and_return("foo") + [obj].send(@method).should == "foo" + end + + it "raises a NoMethodError if an element does not respond to #to_str, #to_ary, or #to_s" do + obj = mock('o') + class << obj; undef :to_s; end + lambda { [1, obj].send(@method) }.should raise_error(NoMethodError) + end + + it "raises an ArgumentError when the Array is recursive" do + lambda { ArraySpecs.recursive_array.send(@method) }.should raise_error(ArgumentError) + lambda { ArraySpecs.head_recursive_array.send(@method) }.should raise_error(ArgumentError) + lambda { ArraySpecs.empty_recursive_array.send(@method) }.should raise_error(ArgumentError) + end + + it "taints the result if the Array is tainted and non-empty" do + [1, 2].taint.send(@method).tainted?.should be_true + end + + it "does not taint the result if the Array is tainted but empty" do + [].taint.send(@method).tainted?.should be_false + end + + it "taints the result if the result of coercing an element is tainted" do + s = mock("taint") + s.should_receive(:to_s).and_return("str".taint) + [s].send(@method).tainted?.should be_true + end + + it "untrusts the result if the Array is untrusted and non-empty" do + [1, 2].untrust.send(@method).untrusted?.should be_true + end + + it "does not untrust the result if the Array is untrusted but empty" do + [].untrust.send(@method).untrusted?.should be_false + end + + it "untrusts the result if the result of coercing an element is untrusted" do + s = mock("untrust") + s.should_receive(:to_s).and_return("str".untrust) + [s].send(@method).untrusted?.should be_true + end + + it "uses the first encoding when other strings are compatible" do + ary1 = ArraySpecs.array_with_7bit_utf8_and_usascii_strings + ary2 = ArraySpecs.array_with_usascii_and_7bit_utf8_strings + ary3 = ArraySpecs.array_with_utf8_and_7bit_ascii8bit_strings + ary4 = ArraySpecs.array_with_usascii_and_7bit_ascii8bit_strings + + ary1.send(@method).encoding.should == Encoding::UTF_8 + ary2.send(@method).encoding.should == Encoding::US_ASCII + ary3.send(@method).encoding.should == Encoding::UTF_8 + ary4.send(@method).encoding.should == Encoding::US_ASCII + end + + it "uses the widest common encoding when other strings are incompatible" do + ary1 = ArraySpecs.array_with_utf8_and_usascii_strings + ary2 = ArraySpecs.array_with_usascii_and_utf8_strings + + ary1.send(@method).encoding.should == Encoding::UTF_8 + ary2.send(@method).encoding.should == Encoding::UTF_8 + end + + it "fails for arrays with incompatibly-encoded strings" do + ary_utf8_bad_ascii8bit = ArraySpecs.array_with_utf8_and_ascii8bit_strings + + lambda { ary_utf8_bad_ascii8bit.send(@method) }.should raise_error(EncodingError) + end +end + +describe :array_join_with_string_separator, shared: true do + it "returns a string formed by concatenating each element.to_str separated by separator" do + obj = mock('foo') + obj.should_receive(:to_str).and_return("foo") + [1, 2, 3, 4, obj].send(@method, ' | ').should == '1 | 2 | 3 | 4 | foo' + end + + it "uses the same separator with nested arrays" do + [1, [2, [3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6" + [1, [2, ArraySpecs::MyArray[3, 4], 5], 6].send(@method, ":").should == "1:2:3:4:5:6" + end + + describe "with a tainted separator" do + before :each do + @sep = ":".taint + end + + it "does not taint the result if the array is empty" do + [].send(@method, @sep).tainted?.should be_false + end + + it "does not taint the result if the array has only one element" do + [1].send(@method, @sep).tainted?.should be_false + end + + it "taints the result if the array has two or more elements" do + [1, 2].send(@method, @sep).tainted?.should be_true + end + end + + describe "with an untrusted separator" do + before :each do + @sep = ":".untrust + end + + it "does not untrust the result if the array is empty" do + [].send(@method, @sep).untrusted?.should be_false + end + + it "does not untrust the result if the array has only one element" do + [1].send(@method, @sep).untrusted?.should be_false + end + + it "untrusts the result if the array has two or more elements" do + [1, 2].send(@method, @sep).untrusted?.should be_true + end + end +end diff --git a/spec/rubyspec/core/array/shared/keep_if.rb b/spec/rubyspec/core/array/shared/keep_if.rb new file mode 100644 index 0000000000..581ba31d1b --- /dev/null +++ b/spec/rubyspec/core/array/shared/keep_if.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../enumerable/shared/enumeratorized', __FILE__) + +describe :keep_if, shared: true do + it "deletes elements for which the block returns a false value" do + array = [1, 2, 3, 4, 5] + array.send(@method) {|item| item > 3 }.should equal(array) + array.should == [4, 5] + end + + it "returns an enumerator if no block is given" do + [1, 2, 3].send(@method).should be_an_instance_of(Enumerator) + end + + it "updates the receiver after all blocks" do + a = [1, 2, 3] + a.send(@method) do |e| + a.length.should == 3 + false + end + a.length.should == 0 + end + + before :all do + @object = [1,2,3] + end + it_should_behave_like :enumeratorized_with_origin_size + + describe "on frozen objects" do + before :each do + @origin = [true, false] + @frozen = @origin.dup.freeze + end + + it "returns an Enumerator if no block is given" do + @frozen.send(@method).should be_an_instance_of(Enumerator) + end + + describe "with truthy block" do + it "keeps elements after any exception" do + lambda { @frozen.send(@method) { true } }.should raise_error(Exception) + @frozen.should == @origin + end + + it "raises a RuntimeError" do + lambda { @frozen.send(@method) { true } }.should raise_error(RuntimeError) + end + end + + describe "with falsy block" do + it "keeps elements after any exception" do + lambda { @frozen.send(@method) { false } }.should raise_error(Exception) + @frozen.should == @origin + end + + it "raises a RuntimeError" do + lambda { @frozen.send(@method) { false } }.should raise_error(RuntimeError) + end + end + end +end diff --git a/spec/rubyspec/core/array/shared/length.rb b/spec/rubyspec/core/array/shared/length.rb new file mode 100644 index 0000000000..f84966d0ba --- /dev/null +++ b/spec/rubyspec/core/array/shared/length.rb @@ -0,0 +1,11 @@ +describe :array_length, shared: true do + it "returns the number of elements" do + [].send(@method).should == 0 + [1, 2, 3].send(@method).should == 3 + end + + it "properly handles recursive arrays" do + ArraySpecs.empty_recursive_array.send(@method).should == 1 + ArraySpecs.recursive_array.send(@method).should == 8 + end +end diff --git a/spec/rubyspec/core/array/shared/replace.rb b/spec/rubyspec/core/array/shared/replace.rb new file mode 100644 index 0000000000..8442d9a841 --- /dev/null +++ b/spec/rubyspec/core/array/shared/replace.rb @@ -0,0 +1,60 @@ +describe :array_replace, shared: true do + it "replaces the elements with elements from other array" do + a = [1, 2, 3, 4, 5] + b = ['a', 'b', 'c'] + a.send(@method, b).should equal(a) + a.should == b + a.should_not equal(b) + + a.send(@method, [4] * 10) + a.should == [4] * 10 + + a.send(@method, []) + a.should == [] + end + + it "properly handles recursive arrays" do + orig = [1, 2, 3] + empty = ArraySpecs.empty_recursive_array + orig.send(@method, empty) + orig.should == empty + + array = ArraySpecs.recursive_array + orig.send(@method, array) + orig.should == array + end + + it "returns self" do + ary = [1, 2, 3] + other = [:a, :b, :c] + ary.send(@method, other).should equal(ary) + end + + it "does not make self dependent to the original array" do + ary = [1, 2, 3] + other = [:a, :b, :c] + ary.send(@method, other) + ary.should == [:a, :b, :c] + ary << :d + ary.should == [:a, :b, :c, :d] + other.should == [:a, :b, :c] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('to_ary') + obj.stub!(:to_ary).and_return([1, 2, 3]) + [].send(@method, obj).should == [1, 2, 3] + end + + it "does not call #to_ary on Array subclasses" do + obj = ArraySpecs::ToAryArray[5, 6, 7] + obj.should_not_receive(:to_ary) + [].send(@method, ArraySpecs::ToAryArray[5, 6, 7]).should == [5, 6, 7] + end + + it "raises a RuntimeError on a frozen array" do + lambda { + ArraySpecs.frozen_array.send(@method, ArraySpecs.frozen_array) + }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/shared/slice.rb b/spec/rubyspec/core/array/shared/slice.rb new file mode 100644 index 0000000000..b3f4ccb9a6 --- /dev/null +++ b/spec/rubyspec/core/array/shared/slice.rb @@ -0,0 +1,459 @@ +describe :array_slice, shared: true do + it "returns the element at index with [index]" do + [ "a", "b", "c", "d", "e" ].send(@method, 1).should == "b" + + a = [1, 2, 3, 4] + + a.send(@method, 0).should == 1 + a.send(@method, 1).should == 2 + a.send(@method, 2).should == 3 + a.send(@method, 3).should == 4 + a.send(@method, 4).should == nil + a.send(@method, 10).should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns the element at index from the end of the array with [-index]" do + [ "a", "b", "c", "d", "e" ].send(@method, -2).should == "d" + + a = [1, 2, 3, 4] + + a.send(@method, -1).should == 4 + a.send(@method, -2).should == 3 + a.send(@method, -3).should == 2 + a.send(@method, -4).should == 1 + a.send(@method, -5).should == nil + a.send(@method, -10).should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns count elements starting from index with [index, count]" do + [ "a", "b", "c", "d", "e" ].send(@method, 2, 3).should == ["c", "d", "e"] + + a = [1, 2, 3, 4] + + a.send(@method, 0, 0).should == [] + a.send(@method, 0, 1).should == [1] + a.send(@method, 0, 2).should == [1, 2] + a.send(@method, 0, 4).should == [1, 2, 3, 4] + a.send(@method, 0, 6).should == [1, 2, 3, 4] + a.send(@method, 0, -1).should == nil + a.send(@method, 0, -2).should == nil + a.send(@method, 0, -4).should == nil + + a.send(@method, 2, 0).should == [] + a.send(@method, 2, 1).should == [3] + a.send(@method, 2, 2).should == [3, 4] + a.send(@method, 2, 4).should == [3, 4] + a.send(@method, 2, -1).should == nil + + a.send(@method, 4, 0).should == [] + a.send(@method, 4, 2).should == [] + a.send(@method, 4, -1).should == nil + + a.send(@method, 5, 0).should == nil + a.send(@method, 5, 2).should == nil + a.send(@method, 5, -1).should == nil + + a.send(@method, 6, 0).should == nil + a.send(@method, 6, 2).should == nil + a.send(@method, 6, -1).should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns count elements starting at index from the end of array with [-index, count]" do + [ "a", "b", "c", "d", "e" ].send(@method, -2, 2).should == ["d", "e"] + + a = [1, 2, 3, 4] + + a.send(@method, -1, 0).should == [] + a.send(@method, -1, 1).should == [4] + a.send(@method, -1, 2).should == [4] + a.send(@method, -1, -1).should == nil + + a.send(@method, -2, 0).should == [] + a.send(@method, -2, 1).should == [3] + a.send(@method, -2, 2).should == [3, 4] + a.send(@method, -2, 4).should == [3, 4] + a.send(@method, -2, -1).should == nil + + a.send(@method, -4, 0).should == [] + a.send(@method, -4, 1).should == [1] + a.send(@method, -4, 2).should == [1, 2] + a.send(@method, -4, 4).should == [1, 2, 3, 4] + a.send(@method, -4, 6).should == [1, 2, 3, 4] + a.send(@method, -4, -1).should == nil + + a.send(@method, -5, 0).should == nil + a.send(@method, -5, 1).should == nil + a.send(@method, -5, 10).should == nil + a.send(@method, -5, -1).should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns the first count elements with [0, count]" do + [ "a", "b", "c", "d", "e" ].send(@method, 0, 3).should == ["a", "b", "c"] + end + + it "returns the subarray which is independent to self with [index,count]" do + a = [1, 2, 3] + sub = a.send(@method, 1,2) + sub.replace([:a, :b]) + a.should == [1, 2, 3] + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.stub!(:to_int).and_return(2) + + a = [1, 2, 3, 4] + a.send(@method, obj).should == 3 + a.send(@method, obj, 1).should == [3] + a.send(@method, obj, obj).should == [3, 4] + a.send(@method, 0, obj).should == [1, 2] + end + + it "returns the elements specified by Range indexes with [m..n]" do + [ "a", "b", "c", "d", "e" ].send(@method, 1..3).should == ["b", "c", "d"] + [ "a", "b", "c", "d", "e" ].send(@method, 4..-1).should == ['e'] + [ "a", "b", "c", "d", "e" ].send(@method, 3..3).should == ['d'] + [ "a", "b", "c", "d", "e" ].send(@method, 3..-2).should == ['d'] + ['a'].send(@method, 0..-1).should == ['a'] + + a = [1, 2, 3, 4] + + a.send(@method, 0..-10).should == [] + a.send(@method, 0..0).should == [1] + a.send(@method, 0..1).should == [1, 2] + a.send(@method, 0..2).should == [1, 2, 3] + a.send(@method, 0..3).should == [1, 2, 3, 4] + a.send(@method, 0..4).should == [1, 2, 3, 4] + a.send(@method, 0..10).should == [1, 2, 3, 4] + + a.send(@method, 2..-10).should == [] + a.send(@method, 2..0).should == [] + a.send(@method, 2..2).should == [3] + a.send(@method, 2..3).should == [3, 4] + a.send(@method, 2..4).should == [3, 4] + + a.send(@method, 3..0).should == [] + a.send(@method, 3..3).should == [4] + a.send(@method, 3..4).should == [4] + + a.send(@method, 4..0).should == [] + a.send(@method, 4..4).should == [] + a.send(@method, 4..5).should == [] + + a.send(@method, 5..0).should == nil + a.send(@method, 5..5).should == nil + a.send(@method, 5..6).should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns elements specified by Range indexes except the element at index n with [m...n]" do + [ "a", "b", "c", "d", "e" ].send(@method, 1...3).should == ["b", "c"] + + a = [1, 2, 3, 4] + + a.send(@method, 0...-10).should == [] + a.send(@method, 0...0).should == [] + a.send(@method, 0...1).should == [1] + a.send(@method, 0...2).should == [1, 2] + a.send(@method, 0...3).should == [1, 2, 3] + a.send(@method, 0...4).should == [1, 2, 3, 4] + a.send(@method, 0...10).should == [1, 2, 3, 4] + + a.send(@method, 2...-10).should == [] + a.send(@method, 2...0).should == [] + a.send(@method, 2...2).should == [] + a.send(@method, 2...3).should == [3] + a.send(@method, 2...4).should == [3, 4] + + a.send(@method, 3...0).should == [] + a.send(@method, 3...3).should == [] + a.send(@method, 3...4).should == [4] + + a.send(@method, 4...0).should == [] + a.send(@method, 4...4).should == [] + a.send(@method, 4...5).should == [] + + a.send(@method, 5...0).should == nil + a.send(@method, 5...5).should == nil + a.send(@method, 5...6).should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns elements that exist if range start is in the array but range end is not with [m..n]" do + [ "a", "b", "c", "d", "e" ].send(@method, 4..7).should == ["e"] + end + + it "accepts Range instances having a negative m and both signs for n with [m..n] and [m...n]" do + a = [1, 2, 3, 4] + + a.send(@method, -1..-1).should == [4] + a.send(@method, -1...-1).should == [] + a.send(@method, -1..3).should == [4] + a.send(@method, -1...3).should == [] + a.send(@method, -1..4).should == [4] + a.send(@method, -1...4).should == [4] + a.send(@method, -1..10).should == [4] + a.send(@method, -1...10).should == [4] + a.send(@method, -1..0).should == [] + a.send(@method, -1..-4).should == [] + a.send(@method, -1...-4).should == [] + a.send(@method, -1..-6).should == [] + a.send(@method, -1...-6).should == [] + + a.send(@method, -2..-2).should == [3] + a.send(@method, -2...-2).should == [] + a.send(@method, -2..-1).should == [3, 4] + a.send(@method, -2...-1).should == [3] + a.send(@method, -2..10).should == [3, 4] + a.send(@method, -2...10).should == [3, 4] + + a.send(@method, -4..-4).should == [1] + a.send(@method, -4..-2).should == [1, 2, 3] + a.send(@method, -4...-2).should == [1, 2] + a.send(@method, -4..-1).should == [1, 2, 3, 4] + a.send(@method, -4...-1).should == [1, 2, 3] + a.send(@method, -4..3).should == [1, 2, 3, 4] + a.send(@method, -4...3).should == [1, 2, 3] + a.send(@method, -4..4).should == [1, 2, 3, 4] + a.send(@method, -4...4).should == [1, 2, 3, 4] + a.send(@method, -4...4).should == [1, 2, 3, 4] + a.send(@method, -4..0).should == [1] + a.send(@method, -4...0).should == [] + a.send(@method, -4..1).should == [1, 2] + a.send(@method, -4...1).should == [1] + + a.send(@method, -5..-5).should == nil + a.send(@method, -5...-5).should == nil + a.send(@method, -5..-4).should == nil + a.send(@method, -5..-1).should == nil + a.send(@method, -5..10).should == nil + + a.should == [1, 2, 3, 4] + end + + it "returns the subarray which is independent to self with [m..n]" do + a = [1, 2, 3] + sub = a.send(@method, 1..2) + sub.replace([:a, :b]) + a.should == [1, 2, 3] + end + + it "tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + a = [1, 2, 3, 4] + + a.send(@method, from..to).should == [2, 3] + a.send(@method, from...to).should == [2] + a.send(@method, 1..0).should == [] + a.send(@method, 1...0).should == [] + + lambda { a.send(@method, "a" .. "b") }.should raise_error(TypeError) + lambda { a.send(@method, "a" ... "b") }.should raise_error(TypeError) + lambda { a.send(@method, from .. "b") }.should raise_error(TypeError) + lambda { a.send(@method, from ... "b") }.should raise_error(TypeError) + end + + it "returns the same elements as [m..n] and [m...n] with Range subclasses" do + a = [1, 2, 3, 4] + range_incl = ArraySpecs::MyRange.new(1, 2) + range_excl = ArraySpecs::MyRange.new(-3, -1, true) + + a.send(@method, range_incl).should == [2, 3] + a.send(@method, range_excl).should == [2, 3] + end + + it "returns nil for a requested index not in the array with [index]" do + [ "a", "b", "c", "d", "e" ].send(@method, 5).should == nil + end + + it "returns [] if the index is valid but length is zero with [index, length]" do + [ "a", "b", "c", "d", "e" ].send(@method, 0, 0).should == [] + [ "a", "b", "c", "d", "e" ].send(@method, 2, 0).should == [] + end + + it "returns nil if length is zero but index is invalid with [index, length]" do + [ "a", "b", "c", "d", "e" ].send(@method, 100, 0).should == nil + [ "a", "b", "c", "d", "e" ].send(@method, -50, 0).should == nil + end + + # This is by design. It is in the official documentation. + it "returns [] if index == array.size with [index, length]" do + %w|a b c d e|.send(@method, 5, 2).should == [] + end + + it "returns nil if index > array.size with [index, length]" do + %w|a b c d e|.send(@method, 6, 2).should == nil + end + + it "returns nil if length is negative with [index, length]" do + %w|a b c d e|.send(@method, 3, -1).should == nil + %w|a b c d e|.send(@method, 2, -2).should == nil + %w|a b c d e|.send(@method, 1, -100).should == nil + end + + it "returns nil if no requested index is in the array with [m..n]" do + [ "a", "b", "c", "d", "e" ].send(@method, 6..10).should == nil + end + + it "returns nil if range start is not in the array with [m..n]" do + [ "a", "b", "c", "d", "e" ].send(@method, -10..2).should == nil + [ "a", "b", "c", "d", "e" ].send(@method, 10..12).should == nil + end + + it "returns an empty array when m == n with [m...n]" do + [1, 2, 3, 4, 5].send(@method, 1...1).should == [] + end + + it "returns an empty array with [0...0]" do + [1, 2, 3, 4, 5].send(@method, 0...0).should == [] + end + + it "returns a subarray where m, n negatives and m < n with [m..n]" do + [ "a", "b", "c", "d", "e" ].send(@method, -3..-2).should == ["c", "d"] + end + + it "returns an array containing the first element with [0..0]" do + [1, 2, 3, 4, 5].send(@method, 0..0).should == [1] + end + + it "returns the entire array with [0..-1]" do + [1, 2, 3, 4, 5].send(@method, 0..-1).should == [1, 2, 3, 4, 5] + end + + it "returns all but the last element with [0...-1]" do + [1, 2, 3, 4, 5].send(@method, 0...-1).should == [1, 2, 3, 4] + end + + it "returns [3] for [2..-1] out of [1, 2, 3]" do + [1,2,3].send(@method, 2..-1).should == [3] + end + + it "returns an empty array when m > n and m, n are positive with [m..n]" do + [1, 2, 3, 4, 5].send(@method, 3..2).should == [] + end + + it "returns an empty array when m > n and m, n are negative with [m..n]" do + [1, 2, 3, 4, 5].send(@method, -2..-3).should == [] + end + + it "does not expand array when the indices are outside of the array bounds" do + a = [1, 2] + a.send(@method, 4).should == nil + a.should == [1, 2] + a.send(@method, 4, 0).should == nil + a.should == [1, 2] + a.send(@method, 6, 1).should == nil + a.should == [1, 2] + a.send(@method, 8...8).should == nil + a.should == [1, 2] + a.send(@method, 10..10).should == nil + a.should == [1, 2] + end + + describe "with a subclass of Array" do + before :each do + ScratchPad.clear + + @array = ArraySpecs::MyArray[1, 2, 3, 4, 5] + end + + it "returns a subclass instance with [n, m]" do + @array.send(@method, 0, 2).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "returns a subclass instance with [-n, m]" do + @array.send(@method, -3, 2).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "returns a subclass instance with [n..m]" do + @array.send(@method, 1..3).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "returns a subclass instance with [n...m]" do + @array.send(@method, 1...3).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "returns a subclass instance with [-n..-m]" do + @array.send(@method, -3..-1).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "returns a subclass instance with [-n...-m]" do + @array.send(@method, -3...-1).should be_an_instance_of(ArraySpecs::MyArray) + end + + it "returns an empty array when m == n with [m...n]" do + @array.send(@method, 1...1).should == [] + ScratchPad.recorded.should be_nil + end + + it "returns an empty array with [0...0]" do + @array.send(@method, 0...0).should == [] + ScratchPad.recorded.should be_nil + end + + it "returns an empty array when m > n and m, n are positive with [m..n]" do + @array.send(@method, 3..2).should == [] + ScratchPad.recorded.should be_nil + end + + it "returns an empty array when m > n and m, n are negative with [m..n]" do + @array.send(@method, -2..-3).should == [] + ScratchPad.recorded.should be_nil + end + + it "returns [] if index == array.size with [index, length]" do + @array.send(@method, 5, 2).should == [] + ScratchPad.recorded.should be_nil + end + + it "returns [] if the index is valid but length is zero with [index, length]" do + @array.send(@method, 0, 0).should == [] + @array.send(@method, 2, 0).should == [] + ScratchPad.recorded.should be_nil + end + + it "does not call #initialize on the subclass instance" do + @array.send(@method, 0, 3).should == [1, 2, 3] + ScratchPad.recorded.should be_nil + end + end + + it "raises a RangeError when the start index is out of range of Fixnum" do + array = [1, 2, 3, 4, 5, 6] + obj = mock('large value') + obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000) + lambda { array.send(@method, obj) }.should raise_error(RangeError) + + obj = 8e19 + lambda { array.send(@method, obj) }.should raise_error(RangeError) + end + + it "raises a RangeError when the length is out of range of Fixnum" do + array = [1, 2, 3, 4, 5, 6] + obj = mock('large value') + obj.should_receive(:to_int).and_return(0x8000_0000_0000_0000_0000) + lambda { array.send(@method, 1, obj) }.should raise_error(RangeError) + + obj = 8e19 + lambda { array.send(@method, 1, obj) }.should raise_error(RangeError) + end +end diff --git a/spec/rubyspec/core/array/shift_spec.rb b/spec/rubyspec/core/array/shift_spec.rb new file mode 100644 index 0000000000..e5d25e2050 --- /dev/null +++ b/spec/rubyspec/core/array/shift_spec.rb @@ -0,0 +1,134 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#shift" do + it "removes and returns the first element" do + a = [5, 1, 1, 5, 4] + a.shift.should == 5 + a.should == [1, 1, 5, 4] + a.shift.should == 1 + a.should == [1, 5, 4] + a.shift.should == 1 + a.should == [5, 4] + a.shift.should == 5 + a.should == [4] + a.shift.should == 4 + a.should == [] + end + + it "returns nil when the array is empty" do + [].shift.should == nil + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.shift.should == [] + empty.should == [] + + array = ArraySpecs.recursive_array + array.shift.should == 1 + array[0..2].should == ['two', 3.0, array] + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.shift }.should raise_error(RuntimeError) + end + it "raises a RuntimeError on an empty frozen array" do + lambda { ArraySpecs.empty_frozen_array.shift }.should raise_error(RuntimeError) + end + + describe "passed a number n as an argument" do + it "removes and returns an array with the first n element of the array" do + a = [1, 2, 3, 4, 5, 6] + + a.shift(0).should == [] + a.should == [1, 2, 3, 4, 5, 6] + + a.shift(1).should == [1] + a.should == [2, 3, 4, 5, 6] + + a.shift(2).should == [2, 3] + a.should == [4, 5, 6] + + a.shift(3).should == [4, 5, 6] + a.should == [] + end + + it "does not corrupt the array when shift without arguments is followed by shift with an argument" do + a = [1, 2, 3, 4, 5] + + a.shift.should == 1 + a.shift(3).should == [2, 3, 4] + a.should == [5] + end + + it "returns a new empty array if there are no more elements" do + a = [] + popped1 = a.shift(1) + popped1.should == [] + a.should == [] + + popped2 = a.shift(2) + popped2.should == [] + a.should == [] + + popped1.should_not equal(popped2) + end + + it "returns whole elements if n exceeds size of the array" do + a = [1, 2, 3, 4, 5] + a.shift(6).should == [1, 2, 3, 4, 5] + a.should == [] + end + + it "does not return self even when it returns whole elements" do + a = [1, 2, 3, 4, 5] + a.shift(5).should_not equal(a) + + a = [1, 2, 3, 4, 5] + a.shift(6).should_not equal(a) + end + + it "raises an ArgumentError if n is negative" do + lambda{ [1, 2, 3].shift(-1) }.should raise_error(ArgumentError) + end + + it "tries to convert n to an Integer using #to_int" do + a = [1, 2, 3, 4] + a.shift(2.3).should == [1, 2] + + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + a.should == [3, 4] + a.shift(obj).should == [3, 4] + a.should == [] + end + + it "raises a TypeError when the passed n can be coerced to Integer" do + lambda{ [1, 2].shift("cat") }.should raise_error(TypeError) + lambda{ [1, 2].shift(nil) }.should raise_error(TypeError) + end + + it "raises an ArgumentError if more arguments are passed" do + lambda{ [1, 2].shift(1, 2) }.should raise_error(ArgumentError) + end + + it "does not return subclass instances with Array subclass" do + ArraySpecs::MyArray[1, 2, 3].shift(2).should be_an_instance_of(Array) + end + + it "returns an untainted array even if the array is tainted" do + ary = [1, 2].taint + ary.shift(2).tainted?.should be_false + ary.shift(0).tainted?.should be_false + end + + it "keeps taint status" do + a = [1, 2].taint + a.shift(2) + a.tainted?.should be_true + a.shift(2) + a.tainted?.should be_true + end + end +end diff --git a/spec/rubyspec/core/array/shuffle_spec.rb b/spec/rubyspec/core/array/shuffle_spec.rb new file mode 100644 index 0000000000..4c3b820186 --- /dev/null +++ b/spec/rubyspec/core/array/shuffle_spec.rb @@ -0,0 +1,102 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#shuffle" do + it "returns the same values, in a usually different order" do + a = [1, 2, 3, 4] + different = false + 10.times do + s = a.shuffle + s.sort.should == a + different ||= (a != s) + end + different.should be_true # Will fail once in a blue moon (4!^10) + end + + it "is not destructive" do + a = [1, 2, 3] + 10.times do + a.shuffle + a.should == [1, 2, 3] + end + end + + it "does not return subclass instances with Array subclass" do + ArraySpecs::MyArray[1, 2, 3].shuffle.should be_an_instance_of(Array) + end + + it "attempts coercion via #to_hash" do + obj = mock('hash') + obj.should_receive(:to_hash).once.and_return({}) + [2, 3].shuffle(obj) + end + + it "calls #rand on the Object passed by the :random key in the arguments Hash" do + obj = mock("array_shuffle_random") + obj.should_receive(:rand).at_least(1).times.and_return(0.5) + + result = [1, 2].shuffle(random: obj) + result.size.should == 2 + result.should include(1, 2) + end + + it "raises a NoMethodError if an object passed for the RNG does not define #rand" do + obj = BasicObject.new + + lambda { [1, 2].shuffle(random: obj) }.should raise_error(NoMethodError) + end + + it "accepts a Float for the value returned by #rand" do + random = mock("array_shuffle_random") + random.should_receive(:rand).at_least(1).times.and_return(0.3) + + [1, 2].shuffle(random: random).should be_an_instance_of(Array) + end + + it "calls #to_int on the Object returned by #rand" do + value = mock("array_shuffle_random_value") + value.should_receive(:to_int).at_least(1).times.and_return(0) + random = mock("array_shuffle_random") + random.should_receive(:rand).at_least(1).times.and_return(value) + + [1, 2].shuffle(random: random).should be_an_instance_of(Array) + end + + it "raises a RangeError if the value is less than zero" do + value = mock("array_shuffle_random_value") + value.should_receive(:to_int).and_return(-1) + random = mock("array_shuffle_random") + random.should_receive(:rand).and_return(value) + + lambda { [1, 2].shuffle(random: random) }.should raise_error(RangeError) + end + + it "raises a RangeError if the value is equal to one" do + value = mock("array_shuffle_random_value") + value.should_receive(:to_int).at_least(1).times.and_return(1) + random = mock("array_shuffle_random") + random.should_receive(:rand).at_least(1).times.and_return(value) + + lambda { [1, 2].shuffle(random: random) }.should raise_error(RangeError) + end +end + +describe "Array#shuffle!" do + it "returns the same values, in a usually different order" do + a = [1, 2, 3, 4] + original = a + different = false + 10.times do + a = a.shuffle! + a.sort.should == [1, 2, 3, 4] + different ||= (a != [1, 2, 3, 4]) + end + different.should be_true # Will fail once in a blue moon (4!^10) + a.should equal(original) + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.shuffle! }.should raise_error(RuntimeError) + lambda { ArraySpecs.empty_frozen_array.shuffle! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/size_spec.rb b/spec/rubyspec/core/array/size_spec.rb new file mode 100644 index 0000000000..2c8a18ade6 --- /dev/null +++ b/spec/rubyspec/core/array/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Array#size" do + it_behaves_like(:array_length, :size) +end diff --git a/spec/rubyspec/core/array/slice_spec.rb b/spec/rubyspec/core/array/slice_spec.rb new file mode 100644 index 0000000000..f6cbd1bcc4 --- /dev/null +++ b/spec/rubyspec/core/array/slice_spec.rb @@ -0,0 +1,160 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/slice', __FILE__) + +describe "Array#slice!" do + it "removes and return the element at index" do + a = [1, 2, 3, 4] + a.slice!(10).should == nil + a.should == [1, 2, 3, 4] + a.slice!(-10).should == nil + a.should == [1, 2, 3, 4] + a.slice!(2).should == 3 + a.should == [1, 2, 4] + a.slice!(-1).should == 4 + a.should == [1, 2] + a.slice!(1).should == 2 + a.should == [1] + a.slice!(-1).should == 1 + a.should == [] + a.slice!(-1).should == nil + a.should == [] + a.slice!(0).should == nil + a.should == [] + end + + it "removes and returns length elements beginning at start" do + a = [1, 2, 3, 4, 5, 6] + a.slice!(2, 3).should == [3, 4, 5] + a.should == [1, 2, 6] + a.slice!(1, 1).should == [2] + a.should == [1, 6] + a.slice!(1, 0).should == [] + a.should == [1, 6] + a.slice!(2, 0).should == [] + a.should == [1, 6] + a.slice!(0, 4).should == [1, 6] + a.should == [] + a.slice!(0, 4).should == [] + a.should == [] + + a = [1] + a.slice!(0, 1).should == [1] + a.should == [] + a[-1].should == nil + + a = [1, 2, 3] + a.slice!(0,1).should == [1] + a.should == [2, 3] + end + + it "returns nil if length is negative" do + a = [1, 2, 3] + a.slice!(2, -1).should == nil + a.should == [1, 2, 3] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.slice(0).should == empty + + array = ArraySpecs.recursive_array + array.slice(4).should == array + array.slice(0..3).should == [1, 'two', 3.0, array] + end + + it "calls to_int on start and length arguments" do + obj = mock('2') + def obj.to_int() 2 end + + a = [1, 2, 3, 4, 5] + a.slice!(obj).should == 3 + a.should == [1, 2, 4, 5] + a.slice!(obj, obj).should == [4, 5] + a.should == [1, 2] + a.slice!(0, obj).should == [1, 2] + a.should == [] + end + + it "removes and return elements in range" do + a = [1, 2, 3, 4, 5, 6, 7, 8] + a.slice!(1..4).should == [2, 3, 4, 5] + a.should == [1, 6, 7, 8] + a.slice!(1...3).should == [6, 7] + a.should == [1, 8] + a.slice!(-1..-1).should == [8] + a.should == [1] + a.slice!(0...0).should == [] + a.should == [1] + a.slice!(0..0).should == [1] + a.should == [] + + a = [1,2,3] + a.slice!(0..3).should == [1,2,3] + a.should == [] + end + + it "removes and returns elements in end-exclusive ranges" do + a = [1, 2, 3, 4, 5, 6, 7, 8] + a.slice!(4...a.length).should == [5, 6, 7, 8] + a.should == [1, 2, 3, 4] + end + + it "calls to_int on range arguments" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + a = [1, 2, 3, 4, 5] + + a.slice!(from .. to).should == [2, 3, 4] + a.should == [1, 5] + + lambda { a.slice!("a" .. "b") }.should raise_error(TypeError) + lambda { a.slice!(from .. "b") }.should raise_error(TypeError) + end + + it "returns last element for consecutive calls at zero index" do + a = [ 1, 2, 3 ] + a.slice!(0).should == 1 + a.slice!(0).should == 2 + a.slice!(0).should == 3 + a.should == [] + end + + it "does not expand array with indices out of bounds" do + a = [1, 2] + a.slice!(4).should == nil + a.should == [1, 2] + a.slice!(4, 0).should == nil + a.should == [1, 2] + a.slice!(6, 1).should == nil + a.should == [1, 2] + a.slice!(8...8).should == nil + a.should == [1, 2] + a.slice!(10..10).should == nil + a.should == [1, 2] + end + + it "does not expand array with negative indices out of bounds" do + a = [1, 2] + a.slice!(-3, 1).should == nil + a.should == [1, 2] + a.slice!(-3..2).should == nil + a.should == [1, 2] + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.slice!(0, 0) }.should raise_error(RuntimeError) + end +end + +describe "Array#slice" do + it_behaves_like(:array_slice, :slice) +end diff --git a/spec/rubyspec/core/array/sort_by_spec.rb b/spec/rubyspec/core/array/sort_by_spec.rb new file mode 100644 index 0000000000..9f45f3ef4d --- /dev/null +++ b/spec/rubyspec/core/array/sort_by_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Array#sort_by!" do + it "sorts array in place by passing each element to the given block" do + a = [-100, -2, 1, 200, 30000] + a.sort_by!{ |e| e.to_s.size } + a.should == [1, -2, 200, -100, 30000] + end + + it "returns an Enumerator if not given a block" do + (1..10).to_a.sort_by!.should be_an_instance_of(Enumerator) + end + + it "completes when supplied a block that always returns the same result" do + a = [2, 3, 5, 1, 4] + a.sort_by!{ 1 } + a.should be_an_instance_of(Array) + a.sort_by!{ 0 } + a.should be_an_instance_of(Array) + a.sort_by!{ -1 } + a.should be_an_instance_of(Array) + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.sort_by! {}}.should raise_error(RuntimeError) + end + + it "raises a RuntimeError on an empty frozen array" do + lambda { ArraySpecs.empty_frozen_array.sort_by! {}}.should raise_error(RuntimeError) + end + + it "returns the specified value when it would break in the given block" do + [1, 2, 3].sort_by!{ break :a }.should == :a + end + + it "makes some modification even if finished sorting when it would break in the given block" do + partially_sorted = (1..5).map{|i| + ary = [5, 4, 3, 2, 1] + ary.sort_by!{|x,y| break if x==i; x<=>y} + ary + } + partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should be_true + end + + it "changes nothing when called on a single element array" do + [1].sort_by!(&:to_s).should == [1] + end + + it_behaves_like :enumeratorized_with_origin_size, :sort_by!, [1,2,3] +end diff --git a/spec/rubyspec/core/array/sort_spec.rb b/spec/rubyspec/core/array/sort_spec.rb new file mode 100644 index 0000000000..584b818caf --- /dev/null +++ b/spec/rubyspec/core/array/sort_spec.rb @@ -0,0 +1,250 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#sort" do + it "returns a new array sorted based on comparing elements with <=>" do + a = [1, -2, 3, 9, 1, 5, -5, 1000, -5, 2, -10, 14, 6, 23, 0] + a.sort.should == [-10, -5, -5, -2, 0, 1, 1, 2, 3, 5, 6, 9, 14, 23, 1000] + end + + it "does not affect the original Array" do + a = [3, 1, 2] + a.sort.should == [1, 2, 3] + a.should == [3, 1, 2] + + a = [0, 15, 2, 3, 4, 6, 14, 5, 7, 12, 8, 9, 1, 10, 11, 13] + b = a.sort + a.should == [0, 15, 2, 3, 4, 6, 14, 5, 7, 12, 8, 9, 1, 10, 11, 13] + b.should == (0..15).to_a + end + + it "sorts already-sorted Arrays" do + (0..15).to_a.sort.should == (0..15).to_a + end + + it "sorts reverse-sorted Arrays" do + (0..15).to_a.reverse.sort.should == (0..15).to_a + end + + it "sorts Arrays that consist entirely of equal elements" do + a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + a.sort.should == a + b = Array.new(15).map { ArraySpecs::SortSame.new } + b.sort.should == b + end + + it "sorts Arrays that consist mostly of equal elements" do + a = [1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1] + a.sort.should == [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + end + + it "does not return self even if the array would be already sorted" do + a = [1, 2, 3] + sorted = a.sort + sorted.should == a + sorted.should_not equal(a) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.sort.should == empty + + array = [[]]; array << array + array.sort.should == [[], array] + end + + it "uses #<=> of elements in order to sort" do + a = ArraySpecs::MockForCompared.new + b = ArraySpecs::MockForCompared.new + c = ArraySpecs::MockForCompared.new + + ArraySpecs::MockForCompared.compared?.should == false + [a, b, c].sort.should == [c, b, a] + ArraySpecs::MockForCompared.compared?.should == true + end + + it "does not deal with exceptions raised by unimplemented or incorrect #<=>" do + o = Object.new + + lambda { [o, 1].sort }.should raise_error + end + + it "may take a block which is used to determine the order of objects a and b described as -1, 0 or +1" do + a = [5, 1, 4, 3, 2] + a.sort.should == [1, 2, 3, 4, 5] + a.sort {|x, y| y <=> x}.should == [5, 4, 3, 2, 1] + end + + it "raises an error when a given block returns nil" do + lambda { [1, 2].sort {} }.should raise_error(ArgumentError) + end + + it "does not call #<=> on contained objects when invoked with a block" do + a = Array.new(25) + (0...25).each {|i| a[i] = ArraySpecs::UFOSceptic.new } + + a.sort { -1 }.should be_an_instance_of(Array) + end + + it "does not call #<=> on elements when invoked with a block even if Array is large (Rubinius #412)" do + a = Array.new(1500) + (0...1500).each {|i| a[i] = ArraySpecs::UFOSceptic.new } + + a.sort { -1 }.should be_an_instance_of(Array) + end + + it "completes when supplied a block that always returns the same result" do + a = [2, 3, 5, 1, 4] + a.sort { 1 }.should be_an_instance_of(Array) + a.sort { 0 }.should be_an_instance_of(Array) + a.sort { -1 }.should be_an_instance_of(Array) + end + + it "does not freezes self during being sorted" do + a = [1, 2, 3] + a.sort { |x,y| a.frozen?.should == false; x <=> y } + end + + it "returns the specified value when it would break in the given block" do + [1, 2, 3].sort{ break :a }.should == :a + end + + it "uses the sign of Bignum block results as the sort result" do + a = [1, 2, 5, 10, 7, -4, 12] + begin + class Bignum; + alias old_spaceship <=> + def <=>(other) + raise + end + end + a.sort {|n, m| (n - m) * (2 ** 200)}.should == [-4, 1, 2, 5, 7, 10, 12] + ensure + class Bignum + alias <=> old_spaceship + end + end + end + + it "compares values returned by block with 0" do + a = [1, 2, 5, 10, 7, -4, 12] + a.sort { |n, m| n - m }.should == [-4, 1, 2, 5, 7, 10, 12] + a.sort { |n, m| + ArraySpecs::ComparableWithFixnum.new(n-m) + }.should == [-4, 1, 2, 5, 7, 10, 12] + lambda { + a.sort { |n, m| (n - m).to_s } + }.should raise_error(ArgumentError) + end + + it "sorts an array that has a value shifted off without a block" do + a = Array.new(20, 1) + a.shift + a[0] = 2 + a.sort.last.should == 2 + end + + it "sorts an array that has a value shifted off with a block" do + a = Array.new(20, 1) + a.shift + a[0] = 2 + a.sort {|x, y| x <=> y }.last.should == 2 + end + + it "raises an error if objects can't be compared" do + a=[ArraySpecs::Uncomparable.new, ArraySpecs::Uncomparable.new] + lambda {a.sort}.should raise_error(ArgumentError) + end + + # From a strange Rubinius bug + it "handles a large array that has been pruned" do + pruned = ArraySpecs::LargeArray.dup.delete_if { |n| n !~ /^test./ } + pruned.sort.should == ArraySpecs::LargeTestArraySorted + end + + it "does not return subclass instance on Array subclasses" do + ary = ArraySpecs::MyArray[1, 2, 3] + ary.sort.should be_an_instance_of(Array) + end +end + +describe "Array#sort!" do + it "sorts array in place using <=>" do + a = [1, -2, 3, 9, 1, 5, -5, 1000, -5, 2, -10, 14, 6, 23, 0] + a.sort! + a.should == [-10, -5, -5, -2, 0, 1, 1, 2, 3, 5, 6, 9, 14, 23, 1000] + end + + it "sorts array in place using block value if a block given" do + a = [0, 15, 2, 3, 4, 6, 14, 5, 7, 12, 8, 9, 1, 10, 11, 13] + a.sort! { |x, y| y <=> x }.should == (0..15).to_a.reverse + end + + it "returns self if the order of elements changed" do + a = [6, 7, 2, 3, 7] + a.sort!.should equal(a) + a.should == [2, 3, 6, 7, 7] + end + + it "returns self even if makes no modification" do + a = [1, 2, 3, 4, 5] + a.sort!.should equal(a) + a.should == [1, 2, 3, 4, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.sort!.should == empty + + array = [[]]; array << array + array.sort!.should == array + end + + it "uses #<=> of elements in order to sort" do + a = ArraySpecs::MockForCompared.new + b = ArraySpecs::MockForCompared.new + c = ArraySpecs::MockForCompared.new + + ArraySpecs::MockForCompared.compared?.should == false + [a, b, c].sort!.should == [c, b, a] + ArraySpecs::MockForCompared.compared?.should == true + end + + it "does not call #<=> on contained objects when invoked with a block" do + a = Array.new(25) + (0...25).each {|i| a[i] = ArraySpecs::UFOSceptic.new } + + a.sort! { -1 }.should be_an_instance_of(Array) + end + + it "does not call #<=> on elements when invoked with a block even if Array is large (Rubinius #412)" do + a = Array.new(1500) + (0...1500).each {|i| a[i] = ArraySpecs::UFOSceptic.new } + + a.sort! { -1 }.should be_an_instance_of(Array) + end + + it "completes when supplied a block that always returns the same result" do + a = [2, 3, 5, 1, 4] + a.sort!{ 1 }.should be_an_instance_of(Array) + a.sort!{ 0 }.should be_an_instance_of(Array) + a.sort!{ -1 }.should be_an_instance_of(Array) + end + + it "raises a RuntimeError on a frozen array" do + lambda { ArraySpecs.frozen_array.sort! }.should raise_error(RuntimeError) + end + + it "returns the specified value when it would break in the given block" do + [1, 2, 3].sort{ break :a }.should == :a + end + + it "makes some modification even if finished sorting when it would break in the given block" do + partially_sorted = (1..5).map{|i| + ary = [5, 4, 3, 2, 1] + ary.sort!{|x,y| break if x==i; x<=>y} + ary + } + partially_sorted.any?{|ary| ary != [1, 2, 3, 4, 5]}.should be_true + end +end diff --git a/spec/rubyspec/core/array/take_spec.rb b/spec/rubyspec/core/array/take_spec.rb new file mode 100644 index 0000000000..6ba87706cf --- /dev/null +++ b/spec/rubyspec/core/array/take_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#take" do + it "returns the first specified number of elements" do + [1, 2, 3].take(2).should == [1, 2] + end + + it "returns all elements when the argument is greater than the Array size" do + [1, 2].take(99).should == [1, 2] + end + + it "returns all elements when the argument is less than the Array size" do + [1, 2].take(4).should == [1, 2] + end + + it "returns an empty Array when passed zero" do + [1].take(0).should == [] + end + + it "returns an empty Array when called on an empty Array" do + [].take(3).should == [] + end + + it "raises an ArgumentError when the argument is negative" do + lambda{ [1].take(-3) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/array/take_while_spec.rb b/spec/rubyspec/core/array/take_while_spec.rb new file mode 100644 index 0000000000..a97f304490 --- /dev/null +++ b/spec/rubyspec/core/array/take_while_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Array#take_while" do + it "returns all elements until the block returns false" do + [1, 2, 3].take_while{ |element| element < 3 }.should == [1, 2] + end + + it "returns all elements until the block returns nil" do + [1, 2, nil, 4].take_while{ |element| element }.should == [1, 2] + end + + it "returns all elements until the block returns false" do + [1, 2, false, 4].take_while{ |element| element }.should == [1, 2] + end +end diff --git a/spec/rubyspec/core/array/to_a_spec.rb b/spec/rubyspec/core/array/to_a_spec.rb new file mode 100644 index 0000000000..f3761a275e --- /dev/null +++ b/spec/rubyspec/core/array/to_a_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#to_a" do + it "returns self" do + a = [1, 2, 3] + a.to_a.should == [1, 2, 3] + a.should equal(a.to_a) + end + + it "does not return subclass instance on Array subclasses" do + e = ArraySpecs::MyArray.new(1, 2) + e.to_a.should be_an_instance_of(Array) + e.to_a.should == [1, 2] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.to_a.should == empty + + array = ArraySpecs.recursive_array + array.to_a.should == array + end +end diff --git a/spec/rubyspec/core/array/to_ary_spec.rb b/spec/rubyspec/core/array/to_ary_spec.rb new file mode 100644 index 0000000000..ab4dfdb5ed --- /dev/null +++ b/spec/rubyspec/core/array/to_ary_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#to_ary" do + it "returns self" do + a = [1, 2, 3] + a.should equal(a.to_ary) + a = ArraySpecs::MyArray[1, 2, 3] + a.should equal(a.to_ary) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.to_ary.should == empty + + array = ArraySpecs.recursive_array + array.to_ary.should == array + end + +end diff --git a/spec/rubyspec/core/array/to_h_spec.rb b/spec/rubyspec/core/array/to_h_spec.rb new file mode 100644 index 0000000000..17783914aa --- /dev/null +++ b/spec/rubyspec/core/array/to_h_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#to_h" do + it "converts empty array to empty hash" do + [].to_h.should == {} + end + + it "converts [key, value] pairs to a hash" do + hash = [[:a, 1], [:b, 2]].to_h + hash.should == { a: 1, b: 2 } + end + + it "uses the last value of a duplicated key" do + hash = [[:a, 1], [:b, 2], [:a, 3]].to_h + hash.should == { a: 3, b: 2 } + end + + it "calls #to_ary on contents" do + pair = mock('to_ary') + pair.should_receive(:to_ary).and_return([:b, 2]) + hash = [[:a, 1], pair].to_h + hash.should == { a: 1, b: 2 } + end + + it "raises TypeError if an element is not an array" do + lambda { [:x].to_h }.should raise_error(TypeError) + end + + it "raises ArgumentError if an element is not a [key, value] pair" do + lambda { [[:x]].to_h }.should raise_error(ArgumentError) + end + + it "does not accept arguments" do + lambda { [].to_h(:a, :b) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/array/to_s_spec.rb b/spec/rubyspec/core/array/to_s_spec.rb new file mode 100644 index 0000000000..3a34d5ee0e --- /dev/null +++ b/spec/rubyspec/core/array/to_s_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/join', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "Array#to_s" do + it_behaves_like :array_inspect, :to_s +end diff --git a/spec/rubyspec/core/array/transpose_spec.rb b/spec/rubyspec/core/array/transpose_spec.rb new file mode 100644 index 0000000000..a8edad7bab --- /dev/null +++ b/spec/rubyspec/core/array/transpose_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#transpose" do + it "assumes an array of arrays and returns the result of transposing rows and columns" do + [[1, 'a'], [2, 'b'], [3, 'c']].transpose.should == [[1, 2, 3], ["a", "b", "c"]] + [[1, 2, 3], ["a", "b", "c"]].transpose.should == [[1, 'a'], [2, 'b'], [3, 'c']] + [].transpose.should == [] + [[]].transpose.should == [] + [[], []].transpose.should == [] + [[0]].transpose.should == [[0]] + [[0], [1]].transpose.should == [[0, 1]] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('[1,2]') + obj.should_receive(:to_ary).and_return([1, 2]) + [obj, [:a, :b]].transpose.should == [[1, :a], [2, :b]] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.transpose.should == empty + + a = []; a << a + b = []; b << b + [a, b].transpose.should == [[a, b]] + + a = [1]; a << a + b = [2]; b << b + [a, b].transpose.should == [ [1, 2], [a, b] ] + end + + it "raises a TypeError if the passed Argument does not respond to #to_ary" do + lambda { [Object.new, [:a, :b]].transpose }.should raise_error(TypeError) + end + + it "does not call to_ary on array subclass elements" do + ary = [ArraySpecs::ToAryArray[1, 2], ArraySpecs::ToAryArray[4, 6]] + ary.transpose.should == [[1, 4], [2, 6]] + end + + it "raises an IndexError if the arrays are not of the same length" do + lambda { [[1, 2], [:a]].transpose }.should raise_error(IndexError) + end + + it "does not return subclass instance on Array subclasses" do + result = ArraySpecs::MyArray[ArraySpecs::MyArray[1, 2, 3], ArraySpecs::MyArray[4, 5, 6]].transpose + result.should be_an_instance_of(Array) + result[0].should be_an_instance_of(Array) + result[1].should be_an_instance_of(Array) + end +end diff --git a/spec/rubyspec/core/array/try_convert_spec.rb b/spec/rubyspec/core/array/try_convert_spec.rb new file mode 100644 index 0000000000..5d8f9f100f --- /dev/null +++ b/spec/rubyspec/core/array/try_convert_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array.try_convert" do + it "returns the argument if it's an Array" do + x = Array.new + Array.try_convert(x).should equal(x) + end + + it "returns the argument if it's a kind of Array" do + x = ArraySpecs::MyArray[] + Array.try_convert(x).should equal(x) + end + + it "returns nil when the argument does not respond to #to_ary" do + Array.try_convert(Object.new).should be_nil + end + + it "sends #to_ary to the argument and returns the result if it's nil" do + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(nil) + Array.try_convert(obj).should be_nil + end + + it "sends #to_ary to the argument and returns the result if it's an Array" do + x = Array.new + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(x) + Array.try_convert(obj).should equal(x) + end + + it "sends #to_ary to the argument and returns the result if it's a kind of Array" do + x = ArraySpecs::MyArray[] + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(x) + Array.try_convert(obj).should equal(x) + end + + it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(Object.new) + lambda { Array.try_convert obj }.should raise_error(TypeError) + end + + it "does not rescue exceptions raised by #to_ary" do + obj = mock("to_ary") + obj.should_receive(:to_ary).and_raise(RuntimeError) + lambda { Array.try_convert obj }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/union_spec.rb b/spec/rubyspec/core/array/union_spec.rb new file mode 100644 index 0000000000..f7fd5c43ac --- /dev/null +++ b/spec/rubyspec/core/array/union_spec.rb @@ -0,0 +1,82 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#|" do + it "returns an array of elements that appear in either array (union)" do + ([] | []).should == [] + ([1, 2] | []).should == [1, 2] + ([] | [1, 2]).should == [1, 2] + ([ 1, 2, 3, 4 ] | [ 3, 4, 5 ]).should == [1, 2, 3, 4, 5] + end + + it "creates an array with no duplicates" do + ([ 1, 2, 3, 1, 4, 5 ] | [ 1, 3, 4, 5, 3, 6 ]).should == [1, 2, 3, 4, 5, 6] + end + + it "creates an array with elements in order they are first encountered" do + ([ 1, 2, 3, 1 ] | [ 1, 3, 4, 5 ]).should == [1, 2, 3, 4, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + (empty | empty).should == empty + + array = ArraySpecs.recursive_array + (array | []).should == [1, 'two', 3.0, array] + ([] | array).should == [1, 'two', 3.0, array] + (array | array).should == [1, 'two', 3.0, array] + (array | empty).should == [1, 'two', 3.0, array, empty] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('[1,2,3]') + obj.should_receive(:to_ary).and_return([1, 2, 3]) + ([0] | obj).should == ([0] | [1, 2, 3]) + end + + # MRI follows hashing semantics here, so doesn't actually call eql?/hash for Fixnum/Symbol + it "acts as if using an intermediate hash to collect values" do + not_supported_on :opal do + ([5.0, 4.0] | [5, 4]).should == [5.0, 4.0, 5, 4] + end + + str = "x" + ([str] | [str.dup]).should == [str] + + obj1 = mock('1') + obj2 = mock('2') + obj1.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:eql?).at_least(1).and_return(true) + + ([obj1] | [obj2]).should == [obj1] + ([obj1, obj1, obj2, obj2] | [obj2]).should == [obj1] + + obj1 = mock('3') + obj2 = mock('4') + obj1.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:hash).at_least(1).and_return(0) + obj2.should_receive(:eql?).at_least(1).and_return(false) + + ([obj1] | [obj2]).should == [obj1, obj2] + ([obj1, obj1, obj2, obj2] | [obj2]).should == [obj1, obj2] + end + + it "does not return subclass instances for Array subclasses" do + (ArraySpecs::MyArray[1, 2, 3] | []).should be_an_instance_of(Array) + (ArraySpecs::MyArray[1, 2, 3] | ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + ([] | ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + ([1, 2] | ArraySpecs::ToAryArray[5, 6]).should == [1, 2, 5, 6] + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + ([x] | [x]).should == [x] + end +end diff --git a/spec/rubyspec/core/array/uniq_spec.rb b/spec/rubyspec/core/array/uniq_spec.rb new file mode 100644 index 0000000000..199b084376 --- /dev/null +++ b/spec/rubyspec/core/array/uniq_spec.rb @@ -0,0 +1,221 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#uniq" do + it "returns an array with no duplicates" do + ["a", "a", "b", "b", "c"].uniq.should == ["a", "b", "c"] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.uniq.should == [empty] + + array = ArraySpecs.recursive_array + array.uniq.should == [1, 'two', 3.0, array] + end + + it "uses eql? semantics" do + [1.0, 1].uniq.should == [1.0, 1] + end + + it "compares elements first with hash" do + x = mock('0') + x.should_receive(:hash).at_least(1).and_return(0) + y = mock('0') + y.should_receive(:hash).at_least(1).and_return(0) + + [x, y].uniq.should == [x, y] + end + + it "does not compare elements with different hash codes via eql?" do + x = mock('0') + x.should_not_receive(:eql?) + y = mock('1') + y.should_not_receive(:eql?) + + x.should_receive(:hash).at_least(1).and_return(0) + y.should_receive(:hash).at_least(1).and_return(1) + + [x, y].uniq.should == [x, y] + end + + it "compares elements with matching hash codes with #eql?" do + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + def obj.eql?(o) + # It's undefined whether the impl does a[0].eql?(a[1]) or + # a[1].eql?(a[0]) so we taint both. + taint + o.taint + false + end + + obj + end + + a.uniq.should == a + a[0].tainted?.should == true + a[1].tainted?.should == true + + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + def obj.eql?(o) + # It's undefined whether the impl does a[0].eql?(a[1]) or + # a[1].eql?(a[0]) so we taint both. + taint + o.taint + true + end + + obj + end + + a.uniq.size.should == 1 + a[0].tainted?.should == true + a[1].tainted?.should == true + end + + it "compares elements based on the value returned from the block" do + a = [1, 2, 3, 4] + a.uniq { |x| x >= 2 ? 1 : 0 }.should == [1, 2] + end + + it "yields items in order" do + a = [1, 2, 3] + yielded = [] + a.uniq { |v| yielded << v } + yielded.should == a + end + + it "handles nil and false like any other values" do + [nil, false, 42].uniq { :foo }.should == [nil] + [false, nil, 42].uniq { :bar }.should == [false] + end + + it "returns subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].uniq.should be_an_instance_of(ArraySpecs::MyArray) + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + [x, x].uniq.should == [x] + end + + describe "given an array of BasicObject subclasses that define ==, eql?, and hash" do + # jruby/jruby#3227 + it "filters equivalent elements using those definitions" do + + basic = Class.new(BasicObject) do + attr_reader :x + + def initialize(x) + @x = x + end + + def ==(rhs) + @x == rhs.x + end + alias_method :eql?, :== + + def hash + @x.hash + end + end + + a = [basic.new(3), basic.new(2), basic.new(1), basic.new(4), basic.new(1), basic.new(2), basic.new(3)] + a.uniq.should == [basic.new(3), basic.new(2), basic.new(1), basic.new(4)] + end + end +end + +describe "Array#uniq!" do + it "modifies the array in place" do + a = [ "a", "a", "b", "b", "c" ] + a.uniq! + a.should == ["a", "b", "c"] + end + + it "returns self" do + a = [ "a", "a", "b", "b", "c" ] + a.should equal(a.uniq!) + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty_dup = empty.dup + empty.uniq! + empty.should == empty_dup + + array = ArraySpecs.recursive_array + expected = array[0..3] + array.uniq! + array.should == expected + end + + it "compares elements first with hash" do + x = mock('0') + x.should_receive(:hash).at_least(1).and_return(0) + y = mock('0') + y.should_receive(:hash).at_least(1).and_return(0) + + a = [x, y] + a.uniq! + a.should == [x, y] + end + + it "does not compare elements with different hash codes via eql?" do + x = mock('0') + x.should_not_receive(:eql?) + y = mock('1') + y.should_not_receive(:eql?) + + x.should_receive(:hash).at_least(1).and_return(0) + y.should_receive(:hash).at_least(1).and_return(1) + + a = [x, y] + a.uniq! + a.should == [x, y] + end + + it "returns nil if no changes are made to the array" do + [ "a", "b", "c" ].uniq!.should == nil + end + + it "raises a RuntimeError on a frozen array when the array is modified" do + dup_ary = [1, 1, 2] + dup_ary.freeze + lambda { dup_ary.uniq! }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on a frozen array when the array would not be modified" do + lambda { ArraySpecs.frozen_array.uniq!}.should raise_error(RuntimeError) + lambda { ArraySpecs.empty_frozen_array.uniq!}.should raise_error(RuntimeError) + end + + it "doesn't yield to the block on a frozen array" do + lambda { ArraySpecs.frozen_array.uniq!{ raise RangeError, "shouldn't yield"}}.should raise_error(RuntimeError) + end + + it "compares elements based on the value returned from the block" do + a = [1, 2, 3, 4] + a.uniq! { |x| x >= 2 ? 1 : 0 }.should == [1, 2] + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + a = [x, x] + a.uniq! + a.should == [x] + end +end diff --git a/spec/rubyspec/core/array/unshift_spec.rb b/spec/rubyspec/core/array/unshift_spec.rb new file mode 100644 index 0000000000..9467a1a6df --- /dev/null +++ b/spec/rubyspec/core/array/unshift_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#unshift" do + it "prepends object to the original array" do + a = [1, 2, 3] + a.unshift("a").should equal(a) + a.should == ['a', 1, 2, 3] + a.unshift().should equal(a) + a.should == ['a', 1, 2, 3] + a.unshift(5, 4, 3) + a.should == [5, 4, 3, 'a', 1, 2, 3] + + # shift all but one element + a = [1, 2] + a.shift + a.unshift(3, 4) + a.should == [3, 4, 2] + + # now shift all elements + a.shift + a.shift + a.shift + a.unshift(3, 4) + a.should == [3, 4] + end + + it "quietly ignores unshifting nothing" do + [].unshift().should == [] + [].unshift(*[]).should == [] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.unshift(:new).should == [:new, empty] + + array = ArraySpecs.recursive_array + array.unshift(:new) + array[0..5].should == [:new, 1, 'two', 3.0, array, array] + end + + it "raises a RuntimeError on a frozen array when the array is modified" do + lambda { ArraySpecs.frozen_array.unshift(1) }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on a frozen array when the array would not be modified" do + lambda { ArraySpecs.frozen_array.unshift }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/array/values_at_spec.rb b/spec/rubyspec/core/array/values_at_spec.rb new file mode 100644 index 0000000000..f36356f0d3 --- /dev/null +++ b/spec/rubyspec/core/array/values_at_spec.rb @@ -0,0 +1,63 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#values_at" do + it "returns an array of elements at the indexes when passed indexes" do + [1, 2, 3, 4, 5].values_at().should == [] + [1, 2, 3, 4, 5].values_at(1, 0, 5, -1, -8, 10).should == [2, 1, nil, 5, nil, nil] + end + + it "calls to_int on its indices" do + obj = mock('1') + def obj.to_int() 1 end + [1, 2].values_at(obj, obj, obj).should == [2, 2, 2] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.values_at(0, 1, 2).should == [empty, nil, nil] + + array = ArraySpecs.recursive_array + array.values_at(0, 1, 2, 3).should == [1, 'two', 3.0, array] + end + + describe "when passed ranges" do + it "returns an array of elements in the ranges" do + [1, 2, 3, 4, 5].values_at(0..2, 1...3, 2..-2).should == [1, 2, 3, 2, 3, 3, 4] + [1, 2, 3, 4, 5].values_at(6..4).should == [] + end + + it "calls to_int on arguments of ranges" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + ary = [1, 2, 3, 4, 5] + ary.values_at(from .. to, from ... to, to .. from).should == [2, 3, 4, 2, 3] + end + end + + describe "when passed a range" do + it "fills with nil if the index is out of the range" do + [0, 1].values_at(0..3).should == [0, 1, nil, nil] + [0, 1].values_at(2..4).should == [nil, nil, nil] + end + + describe "on an empty array" do + it "fills with nils if the index is out of the range" do + [].values_at(0..2).should == [nil, nil, nil] + [].values_at(1..3).should == [nil, nil, nil] + end + end + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].values_at(0, 1..2, 1).should be_an_instance_of(Array) + end +end diff --git a/spec/rubyspec/core/array/zip_spec.rb b/spec/rubyspec/core/array/zip_spec.rb new file mode 100644 index 0000000000..7aac13536b --- /dev/null +++ b/spec/rubyspec/core/array/zip_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Array#zip" do + it "returns an array of arrays containing corresponding elements of each array" do + [1, 2, 3, 4].zip(["a", "b", "c", "d", "e"]).should == + [[1, "a"], [2, "b"], [3, "c"], [4, "d"]] + end + + it "fills in missing values with nil" do + [1, 2, 3, 4, 5].zip(["a", "b", "c", "d"]).should == + [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, nil]] + end + + it "properly handles recursive arrays" do + a = []; a << a + b = [1]; b << b + + a.zip(a).should == [ [a[0], a[0]] ] + a.zip(b).should == [ [a[0], b[0]] ] + b.zip(a).should == [ [b[0], a[0]], [b[1], a[1]] ] + b.zip(b).should == [ [b[0], b[0]], [b[1], b[1]] ] + end + + it "calls #to_ary to convert the argument to an Array" do + obj = mock('[3,4]') + obj.should_receive(:to_ary).and_return([3, 4]) + [1, 2].zip(obj).should == [[1, 3], [2, 4]] + end + + it "uses #each to extract arguments' elements when #to_ary fails" do + obj = Class.new do + def each(&b) + [3,4].each(&b) + end + end.new + + [1, 2].zip(obj).should == [[1, 3], [2, 4]] + end + + it "stops at own size when given an infinite enumerator" do + [1, 2].zip(10.upto(Float::INFINITY)).should == [[1, 10], [2, 11]] + end + + it "fills nil when the given enumereator is shorter than self" do + obj = Object.new + def obj.each + yield 10 + end + [1, 2].zip(obj).should == [[1, 10], [2, nil]] + end + + it "calls block if supplied" do + values = [] + [1, 2, 3, 4].zip(["a", "b", "c", "d", "e"]) { |value| + values << value + }.should == nil + + values.should == [[1, "a"], [2, "b"], [3, "c"], [4, "d"]] + end + + it "does not return subclass instance on Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].zip(["a", "b"]).should be_an_instance_of(Array) + end +end diff --git a/spec/rubyspec/core/basicobject/__id__spec.rb b/spec/rubyspec/core/basicobject/__id__spec.rb new file mode 100644 index 0000000000..fba9ed3b34 --- /dev/null +++ b/spec/rubyspec/core/basicobject/__id__spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/kernel/object_id', __FILE__) + +describe "BasicObject#__id__" do + it_behaves_like :object_id, :__id__, BasicObject +end diff --git a/spec/rubyspec/core/basicobject/__send___spec.rb b/spec/rubyspec/core/basicobject/__send___spec.rb new file mode 100644 index 0000000000..f25339fac7 --- /dev/null +++ b/spec/rubyspec/core/basicobject/__send___spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/basicobject/send', __FILE__) + +describe "BasicObject#__send__" do + it "is a public instance method" do + BasicObject.should have_public_instance_method(:__send__) + end + + it_behaves_like(:basicobject_send, :__send__) +end diff --git a/spec/rubyspec/core/basicobject/basicobject_spec.rb b/spec/rubyspec/core/basicobject/basicobject_spec.rb new file mode 100644 index 0000000000..f58c17a0c0 --- /dev/null +++ b/spec/rubyspec/core/basicobject/basicobject_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "BasicObject" do + it "raises NoMethodError for nonexistent methods after #method_missing is removed" do + script = fixture __FILE__, "remove_method_missing.rb" + ruby_exe(script).chomp.should == "NoMethodError" + end + + it "raises NameError when referencing built-in constants" do + lambda { class BasicObjectSpecs::BOSubclass; Kernel; end }.should raise_error(NameError) + end + + it "does not define built-in constants (according to const_defined?)" do + BasicObject.const_defined?(:Kernel).should be_false + end + + it "does not define built-in constants (according to defined?)" do + BasicObjectSpecs::BOSubclass.kernel_defined?.should be_nil + end + + it "includes itself in its list of constants" do + BasicObject.constants.should include(:BasicObject) + end +end + +describe "BasicObject metaclass" do + before :each do + @meta = class << BasicObject; self; end + end + + it "is an instance of Class" do + @meta.should be_an_instance_of(Class) + end + + it "has Class as superclass" do + @meta.superclass.should equal(Class) + end + + it "contains methods for the BasicObject class" do + @meta.class_eval do + def rubyspec_test_method() :test end + end + + BasicObject.rubyspec_test_method.should == :test + end +end + +describe "BasicObject instance metaclass" do + before :each do + @object = BasicObject.new + @meta = class << @object; self; end + end + + it "is an instance of Class" do + @meta.should be_an_instance_of(Class) + end + + it "has BasicObject as superclass" do + @meta.superclass.should equal(BasicObject) + end + + it "contains methods defined for the BasicObject instance" do + @meta.class_eval do + def test_method() :test end + end + + @object.test_method.should == :test + end +end + +describe "BasicObject subclass" do + it "contains Kernel methods when including Kernel" do + obj = BasicObjectSpecs::BOSubclass.new + + obj.instance_variable_set(:@test, :value) + obj.instance_variable_get(:@test).should == :value + + obj.respond_to?(:hash).should == true + end + + describe "BasicObject references" do + it "can refer to BasicObject from within itself" do + lambda { BasicObject::BasicObject }.should_not raise_error + end + end +end diff --git a/spec/rubyspec/core/basicobject/equal_spec.rb b/spec/rubyspec/core/basicobject/equal_spec.rb new file mode 100644 index 0000000000..8142efa6bd --- /dev/null +++ b/spec/rubyspec/core/basicobject/equal_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/kernel/equal', __FILE__) + +describe "BasicObject#equal?" do + it "is a public instance method" do + BasicObject.should have_public_instance_method(:equal?) + end + + it_behaves_like :object_equal, :equal? + + it "is unaffected by overriding __id__" do + o1 = mock("object") + o1.stub!(:__id__).and_return(10) + o2 = mock("object") + o2.stub!(:__id__).and_return(10) + o1.equal?(o2).should be_false + end + + it "is unaffected by overriding object_id" do + o1 = mock("object") + o1.stub!(:object_id).and_return(10) + o2 = mock("object") + o2.stub!(:object_id).and_return(10) + o1.equal?(o2).should be_false + end + + it "is unaffected by overriding ==" do + # different objects, overriding == to return true + o1 = mock("object") + o1.stub!(:==).and_return(true) + o2 = mock("object") + o1.equal?(o2).should be_false + + # same objects, overriding == to return false + o3 = mock("object") + o3.stub!(:==).and_return(false) + o3.equal?(o3).should be_true + end + + it "is unaffected by overriding eql?" do + # different objects, overriding eql? to return true + o1 = mock("object") + o1.stub!(:eql?).and_return(true) + o2 = mock("object") + o1.equal?(o2).should be_false + + # same objects, overriding eql? to return false + o3 = mock("object") + o3.stub!(:eql?).and_return(false) + o3.equal?(o3).should be_true + end +end diff --git a/spec/rubyspec/core/basicobject/equal_value_spec.rb b/spec/rubyspec/core/basicobject/equal_value_spec.rb new file mode 100644 index 0000000000..7d67634884 --- /dev/null +++ b/spec/rubyspec/core/basicobject/equal_value_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/kernel/equal', __FILE__) + +describe "BasicObject#==" do + it "is a public instance method" do + BasicObject.should have_public_instance_method(:==) + end + + it_behaves_like :object_equal, :== +end diff --git a/spec/rubyspec/core/basicobject/fixtures/classes.rb b/spec/rubyspec/core/basicobject/fixtures/classes.rb new file mode 100644 index 0000000000..d1785afe31 --- /dev/null +++ b/spec/rubyspec/core/basicobject/fixtures/classes.rb @@ -0,0 +1,33 @@ +module BasicObjectSpecs + class IVars + def initialize + @secret = 99 + end + end + + module InstExec + def self.included(base) + base.instance_exec { @@count = 2 } + end + end + + module InstExecIncluded + include InstExec + end + + module InstEvalCVar + instance_eval { @@count = 2 } + end + + class InstEvalConst + INST_EVAL_CONST_X = 2 + end + + module InstEvalOuter + module Inner + obj = InstEvalConst.new + X_BY_STR = obj.instance_eval("INST_EVAL_CONST_X") rescue nil + X_BY_BLOCK = obj.instance_eval { INST_EVAL_CONST_X } rescue nil + end + end +end diff --git a/spec/rubyspec/core/basicobject/fixtures/common.rb b/spec/rubyspec/core/basicobject/fixtures/common.rb new file mode 100644 index 0000000000..3447a3a5e7 --- /dev/null +++ b/spec/rubyspec/core/basicobject/fixtures/common.rb @@ -0,0 +1,9 @@ +module BasicObjectSpecs + class BOSubclass < BasicObject + def self.kernel_defined? + defined?(Kernel) + end + + include ::Kernel + end +end diff --git a/spec/rubyspec/core/basicobject/fixtures/remove_method_missing.rb b/spec/rubyspec/core/basicobject/fixtures/remove_method_missing.rb new file mode 100644 index 0000000000..095b982d3a --- /dev/null +++ b/spec/rubyspec/core/basicobject/fixtures/remove_method_missing.rb @@ -0,0 +1,9 @@ +class BasicObject + remove_method :method_missing +end + +begin + Object.new.test_method +rescue NoMethodError => e + puts e.class.name +end diff --git a/spec/rubyspec/core/basicobject/fixtures/singleton_method.rb b/spec/rubyspec/core/basicobject/fixtures/singleton_method.rb new file mode 100644 index 0000000000..0e00e035fa --- /dev/null +++ b/spec/rubyspec/core/basicobject/fixtures/singleton_method.rb @@ -0,0 +1,10 @@ +module BasicObjectSpecs + class SingletonMethod + def self.singleton_method_added name + ScratchPad.record [:singleton_method_added, name] + end + + def self.singleton_method_to_alias + end + end +end diff --git a/spec/rubyspec/core/basicobject/initialize_spec.rb b/spec/rubyspec/core/basicobject/initialize_spec.rb new file mode 100644 index 0000000000..7e6680df61 --- /dev/null +++ b/spec/rubyspec/core/basicobject/initialize_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "BasicObject#initialize" do + it "is a private instance method" do + BasicObject.should have_private_instance_method(:initialize) + end + + it "does not accept arguments" do + lambda { + BasicObject.new("This", "makes it easier", "to call super", "from other constructors") + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/basicobject/instance_eval_spec.rb b/spec/rubyspec/core/basicobject/instance_eval_spec.rb new file mode 100644 index 0000000000..3898e96b8b --- /dev/null +++ b/spec/rubyspec/core/basicobject/instance_eval_spec.rb @@ -0,0 +1,180 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "BasicObject#instance_eval" do + before :each do + ScratchPad.clear + end + + it "is a public instance method" do + BasicObject.should have_public_instance_method(:instance_eval) + end + + it "sets self to the receiver in the context of the passed block" do + a = BasicObject.new + a.instance_eval { self }.equal?(a).should be_true + end + + it "evaluates strings" do + a = BasicObject.new + a.instance_eval('self').equal?(a).should be_true + end + + it "expects a block with no arguments" do + lambda { "hola".instance_eval }.should raise_error(ArgumentError) + end + + it "takes no arguments with a block" do + lambda { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError) + end + + it "yields the object to the block" do + "hola".instance_eval {|o| ScratchPad.record o } + ScratchPad.recorded.should == "hola" + end + + it "returns the result of the block" do + "hola".instance_eval { :result }.should == :result + end + + it "only binds the eval to the receiver" do + f = Object.new + f.instance_eval do + def foo + 1 + end + end + f.foo.should == 1 + lambda { Object.new.foo }.should raise_error(NoMethodError) + end + + it "preserves self in the original block when passed a block argument" do + prc = proc { self } + + old_self = prc.call + + new_self = Object.new + new_self.instance_eval(&prc).should == new_self + + prc.call.should == old_self + end + + # TODO: This should probably be replaced with a "should behave like" that uses + # the many scoping/binding specs from kernel/eval_spec, since most of those + # behaviors are the same for instance_eval. See also module_eval/class_eval. + + it "binds self to the receiver" do + s = "hola" + (s == s.instance_eval { self }).should be_true + o = mock('o') + (o == o.instance_eval("self")).should be_true + end + + it "executes in the context of the receiver" do + "Ruby-fu".instance_eval { size }.should == 7 + "hola".instance_eval("size").should == 4 + Object.class_eval { "hola".instance_eval("to_s") }.should == "hola" + Object.class_eval { "Ruby-fu".instance_eval{ to_s } }.should == "Ruby-fu" + + end + + it "has access to receiver's instance variables" do + BasicObjectSpecs::IVars.new.instance_eval { @secret }.should == 99 + BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99 + end + + it "treats block-local variables as local to the block" do + prc = instance_eval <<-CODE + proc do |x, prc| + if x + n = 2 + else + n = 1 + prc.call(true, prc) + n + end + end + CODE + + prc.call(false, prc).should == 1 + end + + it "sets class variables in the receiver" do + BasicObjectSpecs::InstEvalCVar.class_variables.should include(:@@count) + BasicObjectSpecs::InstEvalCVar.send(:class_variable_get, :@@count).should == 2 + end + + it "makes the receiver metaclass the scoped class when used with a string" do + obj = Object.new + obj.instance_eval %{ + class B; end + B + } + obj.singleton_class.const_get(:B).should be_an_instance_of(Class) + end + + it "gets constants in the receiver if a string given" do + BasicObjectSpecs::InstEvalOuter::Inner::X_BY_STR.should == 2 + end + + it "doesn't get constants in the receiver if a block given" do + BasicObjectSpecs::InstEvalOuter::Inner::X_BY_BLOCK.should be_nil + end + + it "raises a TypeError when defining methods on an immediate" do + lambda do + 1.instance_eval { def foo; end } + end.should raise_error(TypeError) + lambda do + :foo.instance_eval { def foo; end } + end.should raise_error(TypeError) + end + +quarantine! do # Not clean, leaves cvars lying around to break other specs + it "scopes class var accesses in the caller when called on a Fixnum" do + # Fixnum can take instance vars + Fixnum.class_eval "@@__tmp_instance_eval_spec = 1" + (defined? @@__tmp_instance_eval_spec).should be_nil + + @@__tmp_instance_eval_spec = 2 + 1.instance_eval { @@__tmp_instance_eval_spec }.should == 2 + Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_eval_spec) + end +end + + it "raises a TypeError when defining methods on numerics" do + lambda do + (1.0).instance_eval { def foo; end } + end.should raise_error(TypeError) + lambda do + (1 << 64).instance_eval { def foo; end } + end.should raise_error(TypeError) + end + + it "evaluates procs originating from methods" do + def meth(arg); arg; end + + m = method(:meth) + obj = Object.new + + obj.instance_eval(&m).should == obj + end + + it "evaluates string with given filename and linenumber" do + err = begin + Object.new.instance_eval("raise", "a_file", 10) + rescue => e + e + end + err.backtrace.first.split(":")[0..1].should == ["a_file", "10"] + end + + it "evaluates string with given filename and negative linenumber" do + err = begin + Object.new.instance_eval("\n\nraise\n", "b_file", -100) + rescue => e + e + end + err.backtrace.first.split(":")[0..1].should == ["b_file", "-98"] + end +end diff --git a/spec/rubyspec/core/basicobject/instance_exec_spec.rb b/spec/rubyspec/core/basicobject/instance_exec_spec.rb new file mode 100644 index 0000000000..f41af6f64f --- /dev/null +++ b/spec/rubyspec/core/basicobject/instance_exec_spec.rb @@ -0,0 +1,107 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "BasicObject#instance_exec" do + it "is a public instance method" do + BasicObject.should have_public_instance_method(:instance_exec) + end + + it "sets self to the receiver in the context of the passed block" do + a = BasicObject.new + a.instance_exec { self }.equal?(a).should be_true + end + + it "passes arguments to the block" do + a = BasicObject.new + a.instance_exec(1) { |b| b }.should equal(1) + end + + it "raises a LocalJumpError unless given a block" do + lambda { "hola".instance_exec }.should raise_error(LocalJumpError) + end + + it "has an arity of -1" do + Object.new.method(:instance_exec).arity.should == -1 + end + + it "accepts arguments with a block" do + lambda { "hola".instance_exec(4, 5) { |a,b| a + b } }.should_not raise_error + end + + it "doesn't pass self to the block as an argument" do + "hola".instance_exec { |o| o }.should be_nil + end + + it "passes any arguments to the block" do + Object.new.instance_exec(1,2) {|one, two| one + two}.should == 3 + end + + it "only binds the exec to the receiver" do + f = Object.new + f.instance_exec do + def foo + 1 + end + end + f.foo.should == 1 + lambda { Object.new.foo }.should raise_error(NoMethodError) + end + + # TODO: This should probably be replaced with a "should behave like" that uses + # the many scoping/binding specs from kernel/eval_spec, since most of those + # behaviors are the same for instance_exec. See also module_eval/class_eval. + + it "binds self to the receiver" do + s = "hola" + (s == s.instance_exec { self }).should == true + end + + it "binds the block's binding self to the receiver" do + s = "hola" + (s == s.instance_exec { eval "self", binding }).should == true + end + + it "executes in the context of the receiver" do + "Ruby-fu".instance_exec { size }.should == 7 + Object.class_eval { "Ruby-fu".instance_exec{ to_s } }.should == "Ruby-fu" + end + + it "has access to receiver's instance variables" do + BasicObjectSpecs::IVars.new.instance_exec { @secret }.should == 99 + end + + it "sets class variables in the receiver" do + BasicObjectSpecs::InstExec.class_variables.should include(:@@count) + BasicObjectSpecs::InstExec.send(:class_variable_get, :@@count).should == 2 + end + + it "raises a TypeError when defining methods on an immediate" do + lambda do + 1.instance_exec { def foo; end } + end.should raise_error(TypeError) + lambda do + :foo.instance_exec { def foo; end } + end.should raise_error(TypeError) + end + +quarantine! do # Not clean, leaves cvars lying around to break other specs + it "scopes class var accesses in the caller when called on a Fixnum" do + # Fixnum can take instance vars + Fixnum.class_eval "@@__tmp_instance_exec_spec = 1" + (defined? @@__tmp_instance_exec_spec).should == nil + + @@__tmp_instance_exec_spec = 2 + 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2 + Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec) + end +end + + it "raises a TypeError when defining methods on numerics" do + lambda do + (1.0).instance_exec { def foo; end } + end.should raise_error(TypeError) + lambda do + (1 << 64).instance_exec { def foo; end } + end.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/basicobject/method_missing_spec.rb b/spec/rubyspec/core/basicobject/method_missing_spec.rb new file mode 100644 index 0000000000..eea45a8ddc --- /dev/null +++ b/spec/rubyspec/core/basicobject/method_missing_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../shared/basicobject/method_missing', __FILE__) + +describe "BasicObject#method_missing" do + it "is a private method" do + BasicObject.should have_private_instance_method(:method_missing) + end +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_class, nil, BasicObject +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_instance, nil, BasicObject +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_defined_module, nil, KernelSpecs::ModuleMM +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_module, nil, KernelSpecs::ModuleNoMM +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_defined_class, nil, KernelSpecs::ClassMM +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_class, nil, KernelSpecs::ClassNoMM +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_defined_instance, nil, KernelSpecs::ClassMM +end + +describe "BasicObject#method_missing" do + it_behaves_like :method_missing_instance, nil, KernelSpecs::ClassNoMM +end diff --git a/spec/rubyspec/core/basicobject/not_equal_spec.rb b/spec/rubyspec/core/basicobject/not_equal_spec.rb new file mode 100644 index 0000000000..9177380154 --- /dev/null +++ b/spec/rubyspec/core/basicobject/not_equal_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "BasicObject#!=" do + it "is a public instance method" do + BasicObject.should have_public_instance_method(:'!=') + end + + it "returns true if other is not identical to self" do + a = BasicObject.new + b = BasicObject.new + (a != b).should be_true + end + + it "returns true if other is an Object" do + a = BasicObject.new + b = Object.new + (a != b).should be_true + end + + it "returns false if other is identical to self" do + a = BasicObject.new + (a != a).should be_false + end + + it "dispatches to #==" do + a = mock("not_equal") + b = BasicObject.new + a.should_receive(:==).and_return(true) + + (a != b).should be_false + end + + describe "when invoked using Kernel#send" do + it "returns true if other is not identical to self" do + a = Object.new + b = Object.new + a.send(:!=, b).should be_true + end + + it "returns false if other is identical to self" do + a = Object.new + a.send(:!=, a).should be_false + end + + it "dispatches to #==" do + a = mock("not_equal") + b = Object.new + a.should_receive(:==).and_return(true) + + a.send(:!=, b).should be_false + end + end +end diff --git a/spec/rubyspec/core/basicobject/not_spec.rb b/spec/rubyspec/core/basicobject/not_spec.rb new file mode 100644 index 0000000000..f02b31edb2 --- /dev/null +++ b/spec/rubyspec/core/basicobject/not_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "BasicObject#!" do + it "is a public instance method" do + BasicObject.should have_public_instance_method(:'!') + end + + it "returns false" do + (!BasicObject.new).should be_false + end +end diff --git a/spec/rubyspec/core/basicobject/singleton_method_added_spec.rb b/spec/rubyspec/core/basicobject/singleton_method_added_spec.rb new file mode 100644 index 0000000000..7622798dee --- /dev/null +++ b/spec/rubyspec/core/basicobject/singleton_method_added_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/singleton_method', __FILE__) + +describe "BasicObject#singleton_method_added" do + before :each do + ScratchPad.clear + end + + it "is a private method" do + BasicObject.should have_private_instance_method(:singleton_method_added) + end + + it "is called when a singleton method is defined on an object" do + obj = BasicObject.new + + def obj.singleton_method_added(name) + ScratchPad.record [:singleton_method_added, name] + end + + def obj.new_singleton_method + end + + ScratchPad.recorded.should == [:singleton_method_added, :new_singleton_method] + end + + it "is not called for instance methods" do + ScratchPad.record [] + + Module.new do + def self.singleton_method_added(name) + ScratchPad << name + end + + def new_instance_method + end + end + + ScratchPad.recorded.should_not include(:new_instance_method) + end + + it "is called when a singleton method is defined on a module" do + class BasicObjectSpecs::SingletonMethod + def self.new_method_on_self + end + end + ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_self] + end + + it "is called when a method is defined in the singleton class" do + class BasicObjectSpecs::SingletonMethod + class << self + def new_method_on_singleton + end + end + end + ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton] + end + + it "is called when a method is defined with alias_method in the singleton class" do + class BasicObjectSpecs::SingletonMethod + class << self + alias_method :new_method_on_singleton_with_alias_method, :singleton_method_to_alias + end + end + ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton_with_alias_method] + end + + it "is called when a method is defined with syntax alias in the singleton class" do + class BasicObjectSpecs::SingletonMethod + class << self + alias new_method_on_singleton_with_syntax_alias singleton_method_to_alias + end + end + ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton_with_syntax_alias] + end + + it "is called when define_method is used in the singleton class" do + class BasicObjectSpecs::SingletonMethod + class << self + define_method :new_method_with_define_method do + end + end + end + ScratchPad.recorded.should == [:singleton_method_added, :new_method_with_define_method] + end +end diff --git a/spec/rubyspec/core/basicobject/singleton_method_removed_spec.rb b/spec/rubyspec/core/basicobject/singleton_method_removed_spec.rb new file mode 100644 index 0000000000..406f4a888e --- /dev/null +++ b/spec/rubyspec/core/basicobject/singleton_method_removed_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "BasicObject#singleton_method_removed" do + before :each do + ScratchPad.clear + end + + it "is a private method" do + BasicObject.should have_private_instance_method(:singleton_method_removed) + end + + it "is called when a method is removed on self" do + klass = Class.new + def klass.singleton_method_removed(name) + ScratchPad.record [:singleton_method_removed, name] + end + def klass.singleton_method_to_remove + end + class << klass + remove_method :singleton_method_to_remove + end + ScratchPad.recorded.should == [:singleton_method_removed, :singleton_method_to_remove] + end +end diff --git a/spec/rubyspec/core/basicobject/singleton_method_undefined_spec.rb b/spec/rubyspec/core/basicobject/singleton_method_undefined_spec.rb new file mode 100644 index 0000000000..4f33cc5dbe --- /dev/null +++ b/spec/rubyspec/core/basicobject/singleton_method_undefined_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "BasicObject#singleton_method_undefined" do + before :each do + ScratchPad.clear + end + + it "is a private method" do + BasicObject.should have_private_instance_method(:singleton_method_undefined) + end + + it "is called when a method is removed on self" do + klass = Class.new + def klass.singleton_method_undefined(name) + ScratchPad.record [:singleton_method_undefined, name] + end + def klass.singleton_method_to_undefine + end + class << klass + undef_method :singleton_method_to_undefine + end + ScratchPad.recorded.should == [:singleton_method_undefined, :singleton_method_to_undefine] + end +end diff --git a/spec/rubyspec/core/bignum/abs_spec.rb b/spec/rubyspec/core/bignum/abs_spec.rb new file mode 100644 index 0000000000..b551dd95ad --- /dev/null +++ b/spec/rubyspec/core/bignum/abs_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/abs', __FILE__) + +describe "Bignum#abs" do + it_behaves_like(:bignum_abs, :abs) +end + diff --git a/spec/rubyspec/core/bignum/bignum_spec.rb b/spec/rubyspec/core/bignum/bignum_spec.rb new file mode 100644 index 0000000000..b723718f69 --- /dev/null +++ b/spec/rubyspec/core/bignum/bignum_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum" do + it "includes Comparable" do + Bignum.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/bignum/bit_and_spec.rb b/spec/rubyspec/core/bignum/bit_and_spec.rb new file mode 100644 index 0000000000..6eca0e56f0 --- /dev/null +++ b/spec/rubyspec/core/bignum/bit_and_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#&" do + before :each do + @bignum = bignum_value(5) + end + + it "returns self bitwise AND other" do + @bignum = bignum_value(5) + (@bignum & 3).should == 1 + (@bignum & 52).should == 4 + (@bignum & bignum_value(9921)).should == 9223372036854775809 + + ((2*bignum_value) & 1).should == 0 + ((2*bignum_value) & (2*bignum_value)).should == 18446744073709551616 + end + + it "returns self bitwise AND other when one operand is negative" do + ((2*bignum_value) & -1).should == 18446744073709551616 + ((4*bignum_value) & -1).should == 36893488147419103232 + (@bignum & -0xffffffffffffff5).should == 9223372036854775809 + (@bignum & -@bignum).should == 1 + (@bignum & -0x8000000000000000).should == 9223372036854775808 + end + + it "returns self bitwise AND other when both operands are negative" do + (-@bignum & -0x4000000000000005).should == -13835058055282163717 + (-@bignum & -@bignum).should == -9223372036854775813 + (-@bignum & -0x4000000000000000).should == -13835058055282163712 + end + + it "returns self bitwise AND other when both are negative and a multiple in bitsize of Fixnum::MIN" do + val = - ((1 << 93) - 1) + (val & val).should == val + + val = - ((1 << 126) - 1) + (val & val).should == val + end + + it "raises a TypeError when passed a Float" do + lambda { (@bignum & 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("bignum bit and") + obj.should_not_receive(:to_int) + + lambda { @bignum & obj }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/bit_length_spec.rb b/spec/rubyspec/core/bignum/bit_length_spec.rb new file mode 100644 index 0000000000..1c4c518345 --- /dev/null +++ b/spec/rubyspec/core/bignum/bit_length_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#bit_length" do + it "returns the position of the leftmost bit of a positive number" do + (2**1000-1).bit_length.should == 1000 + (2**1000).bit_length.should == 1001 + (2**1000+1).bit_length.should == 1001 + + (2**10000-1).bit_length.should == 10000 + (2**10000).bit_length.should == 10001 + (2**10000+1).bit_length.should == 10001 + + (1 << 100).bit_length.should == 101 + (1 << 100).succ.bit_length.should == 101 + (1 << 100).pred.bit_length.should == 100 + (1 << 10000).bit_length.should == 10001 + end + + it "returns the position of the leftmost 0 bit of a negative number" do + (-2**10000-1).bit_length.should == 10001 + (-2**10000).bit_length.should == 10000 + (-2**10000+1).bit_length.should == 10000 + + (-2**1000-1).bit_length.should == 1001 + (-2**1000).bit_length.should == 1000 + (-2**1000+1).bit_length.should == 1000 + + ((-1 << 100)-1).bit_length.should == 101 + ((-1 << 100)-1).succ.bit_length.should == 100 + ((-1 << 100)-1).pred.bit_length.should == 101 + ((-1 << 10000)-1).bit_length.should == 10001 + end +end diff --git a/spec/rubyspec/core/bignum/bit_or_spec.rb b/spec/rubyspec/core/bignum/bit_or_spec.rb new file mode 100644 index 0000000000..6bcb6ead6b --- /dev/null +++ b/spec/rubyspec/core/bignum/bit_or_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#|" do + before :each do + @bignum = bignum_value(11) + end + + it "returns self bitwise OR other" do + (@bignum | 2).should == 9223372036854775819 + (@bignum | 9).should == 9223372036854775819 + (@bignum | bignum_value).should == 9223372036854775819 + end + + it "returns self bitwise OR other when one operand is negative" do + (@bignum | -0x40000000000000000).should == -64563604257983430645 + (@bignum | -@bignum).should == -1 + (@bignum | -0x8000000000000000).should == -9223372036854775797 + end + + it "returns self bitwise OR other when both operands are negative" do + (-@bignum | -0x4000000000000005).should == -1 + (-@bignum | -@bignum).should == -9223372036854775819 + (-@bignum | -0x4000000000000000).should == -11 + end + + it "raises a TypeError when passed a Float" do + not_supported_on :opal do + lambda { + bignum_value | bignum_value(0xffff).to_f + }.should raise_error(TypeError) + end + lambda { @bignum | 9.9 }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("bignum bit or") + obj.should_not_receive(:to_int) + + lambda { @bignum | obj }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/bit_xor_spec.rb b/spec/rubyspec/core/bignum/bit_xor_spec.rb new file mode 100644 index 0000000000..ef4b4e6ae3 --- /dev/null +++ b/spec/rubyspec/core/bignum/bit_xor_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#^" do + before :each do + @bignum = bignum_value(18) + end + + it "returns self bitwise EXCLUSIVE OR other" do + (@bignum ^ 2).should == 9223372036854775824 + (@bignum ^ @bignum).should == 0 + (@bignum ^ 14).should == 9223372036854775836 + end + + it "returns self bitwise EXCLUSIVE OR other when one operand is negative" do + (@bignum ^ -0x40000000000000000).should == -64563604257983430638 + (@bignum ^ -@bignum).should == -4 + (@bignum ^ -0x8000000000000000).should == -18446744073709551598 + end + + it "returns self bitwise EXCLUSIVE OR other when both operands are negative" do + (-@bignum ^ -0x40000000000000000).should == 64563604257983430638 + (-@bignum ^ -@bignum).should == 0 + (-@bignum ^ -0x4000000000000000).should == 13835058055282163694 + end + + it "returns self bitwise EXCLUSIVE OR other when all bits are 1 and other value is negative" do + (9903520314283042199192993791 ^ -1).should == -9903520314283042199192993792 + (784637716923335095479473677900958302012794430558004314111 ^ -1).should == + -784637716923335095479473677900958302012794430558004314112 + end + + it "raises a TypeError when passed a Float" do + not_supported_on :opal do + lambda { + bignum_value ^ bignum_value(0xffff).to_f + }.should raise_error(TypeError) + end + lambda { @bignum ^ 14.5 }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("bignum bit xor") + obj.should_not_receive(:to_int) + + lambda { @bignum ^ obj }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/case_compare_spec.rb b/spec/rubyspec/core/bignum/case_compare_spec.rb new file mode 100644 index 0000000000..d7e0a89487 --- /dev/null +++ b/spec/rubyspec/core/bignum/case_compare_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Bignum#===" do + it_behaves_like :bignum_equal, :=== +end diff --git a/spec/rubyspec/core/bignum/coerce_spec.rb b/spec/rubyspec/core/bignum/coerce_spec.rb new file mode 100644 index 0000000000..40decaf51a --- /dev/null +++ b/spec/rubyspec/core/bignum/coerce_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#coerce" do + it "coerces other to a Bignum and returns [other, self] when passed a Fixnum" do + a = bignum_value + ary = a.coerce(2) + + ary[0].should be_kind_of(Bignum) + ary[1].should be_kind_of(Bignum) + ary.should == [2, a] + end + + it "returns [other, self] when passed a Bignum" do + a = bignum_value + b = bignum_value + ary = a.coerce(b) + + ary[0].should be_kind_of(Bignum) + ary[1].should be_kind_of(Bignum) + ary.should == [b, a] + end + + it "raises a TypeError when not passed a Fixnum or Bignum" do + a = bignum_value + + lambda { a.coerce(nil) }.should raise_error(TypeError) + lambda { a.coerce(mock('str')) }.should raise_error(TypeError) + lambda { a.coerce(1..4) }.should raise_error(TypeError) + lambda { a.coerce(:test) }.should raise_error(TypeError) + end + + ruby_version_is ""..."2.4" do + it "raises a TypeError when passed a String" do + a = bignum_value + lambda { a.coerce("123") }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a Float" do + a = bignum_value + lambda { a.coerce(12.3) }.should raise_error(TypeError) + end + end + + ruby_version_is "2.4" do + it "coerces both values to Floats and returns [other, self] when passed a Float" do + a = bignum_value + a.coerce(1.2).should == [1.2, a.to_f] + end + + it "coerces both values to Floats and returns [other, self] when passed a String" do + a = bignum_value + a.coerce("123").should == [123.0, a.to_f] + end + + it "calls #to_f to coerce other to a Float" do + b = mock("bignum value") + b.should_receive(:to_f).and_return(1.2) + + a = bignum_value + ary = a.coerce(b) + + ary.should == [1.2, a.to_f] + end + end +end diff --git a/spec/rubyspec/core/bignum/comparison_spec.rb b/spec/rubyspec/core/bignum/comparison_spec.rb new file mode 100644 index 0000000000..435cc9aea2 --- /dev/null +++ b/spec/rubyspec/core/bignum/comparison_spec.rb @@ -0,0 +1,162 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#<=>" do + describe "with a Fixnum" do + it "returns -1 when other is larger" do + (-bignum_value <=> 2).should == -1 + end + + it "returns 1 when other is smaller" do + (bignum_value <=> 2).should == 1 + end + end + + describe "with a Bignum" do + describe "when other is negative" do + it "returns -1 when self is negative and other is larger" do + (-bignum_value(42) <=> -bignum_value).should == -1 + end + + it "returns 0 when other is equal" do + (-bignum_value <=> -bignum_value).should == 0 + end + + it "returns 1 when self is negative and other is smaller" do + (-bignum_value <=> -bignum_value(94)).should == 1 + end + + it "returns 1 when self is positive" do + (bignum_value <=> -bignum_value).should == 1 + end + end + + describe "when other is positive" do + it "returns -1 when self is negative" do + (-bignum_value <=> bignum_value).should == -1 + end + + it "returns -1 when self is positive and other is larger" do + (bignum_value <=> bignum_value(38)).should == -1 + end + + it "returns 0 when other is equal" do + (bignum_value <=> bignum_value).should == 0 + end + + it "returns 1 when other is smaller" do + (bignum_value(56) <=> bignum_value).should == 1 + end + end + end + + describe "with a Float" do + describe "when other is negative" do + it "returns -1 when self is negative and other is larger" do + (-bignum_value(0xffff) <=> -bignum_value.to_f).should == -1 + end + + it "returns 0 when other is equal" do + (-bignum_value <=> -bignum_value.to_f).should == 0 + end + + it "returns 1 when self is negative and other is smaller" do + (-bignum_value <=> -bignum_value(0xffef).to_f).should == 1 + end + + it "returns 1 when self is positive" do + (bignum_value <=> -bignum_value.to_f).should == 1 + end + end + + describe "when other is positive" do + it "returns -1 when self is negative" do + (-bignum_value <=> bignum_value.to_f).should == -1 + end + + it "returns -1 when self is positive and other is larger" do + (bignum_value <=> bignum_value(0xfffe).to_f).should == -1 + end + + it "returns 0 when other is equal" do + (bignum_value <=> bignum_value.to_f).should == 0 + end + + it "returns 1 when other is smaller" do + (bignum_value(0xfeff) <=> bignum_value.to_f).should == 1 + end + end + end + + describe "with an Object" do + before :each do + @big = bignum_value + @num = mock("value for Bignum#<=>") + end + + it "calls #coerce on other" do + @num.should_receive(:coerce).with(@big).and_return([@big.to_f, 2.5]) + @big <=> @num + end + + ruby_version_is ""..."2.5" do + it "returns nil if #coerce raises an exception" do + @num.should_receive(:coerce).with(@big).and_raise(RuntimeError) + lambda { + @result = (@big <=> @num) + }.should complain(/Numerical comparison operators will no more rescue exceptions/) + @result.should be_nil + end + end + + ruby_version_is "2.5" do + it "lets the exception go through if #coerce raises an exception" do + @num.should_receive(:coerce).with(@big).and_raise(RuntimeError.new("my error")) + lambda { + @big <=> @num + }.should raise_error(RuntimeError, "my error") + end + end + + it "raises an exception if #coerce raises a non-StandardError exception" do + @num.should_receive(:coerce).with(@big).and_raise(Exception) + lambda { @big <=> @num }.should raise_error(Exception) + end + + it "returns nil if #coerce does not return an Array" do + @num.should_receive(:coerce).with(@big).and_return(nil) + (@big <=> @num).should be_nil + end + + it "returns -1 if the coerced value is larger" do + @num.should_receive(:coerce).with(@big).and_return([@big, bignum_value(10)]) + (@big <=> @num).should == -1 + end + + it "returns 0 if the coerced value is equal" do + @num.should_receive(:coerce).with(@big).and_return([@big, bignum_value]) + (@big <=> @num).should == 0 + end + + it "returns 1 if the coerced value is smaller" do + @num.should_receive(:coerce).with(@big).and_return([@big, 22]) + (@big <=> @num).should == 1 + end + end + + # The tests below are taken from matz's revision 23730 for Ruby trunk + it "returns 1 when self is Infinity and other is a Bignum" do + (infinity_value <=> Float::MAX.to_i*2).should == 1 + end + + it "returns -1 when self is negative and other is Infinty" do + (-Float::MAX.to_i*2 <=> infinity_value).should == -1 + end + + it "returns 1 when self is negative and other is -Infinity" do + (-Float::MAX.to_i*2 <=> -infinity_value).should == 1 + end + + it "returns -1 when self is -Infinity and other is negative" do + (-infinity_value <=> -Float::MAX.to_i*2).should == -1 + end +end diff --git a/spec/rubyspec/core/bignum/complement_spec.rb b/spec/rubyspec/core/bignum/complement_spec.rb new file mode 100644 index 0000000000..be6bc21b19 --- /dev/null +++ b/spec/rubyspec/core/bignum/complement_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#~" do + it "returns self with each bit flipped" do + (~bignum_value(48)).should == -9223372036854775857 + (~(-bignum_value(21))).should == 9223372036854775828 + (~bignum_value(1)).should == -9223372036854775810 + end +end diff --git a/spec/rubyspec/core/bignum/div_spec.rb b/spec/rubyspec/core/bignum/div_spec.rb new file mode 100644 index 0000000000..6c165289e8 --- /dev/null +++ b/spec/rubyspec/core/bignum/div_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/divide', __FILE__) + +describe "Bignum#div" do + it_behaves_like(:bignum_divide, :div) + + it "returns a result of integer division of self by a float argument" do + bignum_value(88).div(4294967295.5).should eql(2147483648) + not_supported_on :opal do + bignum_value(88).div(4294967295.0).should eql(2147483648) + bignum_value(88).div(bignum_value(88).to_f).should eql(1) + bignum_value(88).div(-bignum_value(88).to_f).should eql(-1) + end + end + + # #5490 + it "raises ZeroDivisionError if the argument is Float zero" do + lambda { bignum_value(88).div(0.0) }.should raise_error(ZeroDivisionError) + lambda { bignum_value(88).div(-0.0) }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/core/bignum/divide_spec.rb b/spec/rubyspec/core/bignum/divide_spec.rb new file mode 100644 index 0000000000..b81938b707 --- /dev/null +++ b/spec/rubyspec/core/bignum/divide_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/divide', __FILE__) + +describe "Bignum#/" do + it_behaves_like(:bignum_divide, :/) + + it "returns self divided by float" do + not_supported_on :opal do + (bignum_value(88) / 4294967295.0).should be_close(2147483648.5, TOLERANCE) + end + (bignum_value(88) / 4294967295.5).should be_close(2147483648.25, TOLERANCE) + end + + it "does NOT raise ZeroDivisionError if other is zero and is a Float" do + (bignum_value / 0.0).to_s.should == 'Infinity' + (bignum_value / -0.0).to_s.should == '-Infinity' + end +end diff --git a/spec/rubyspec/core/bignum/divmod_spec.rb b/spec/rubyspec/core/bignum/divmod_spec.rb new file mode 100644 index 0000000000..656f23482b --- /dev/null +++ b/spec/rubyspec/core/bignum/divmod_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#divmod" do + before :each do + @bignum = bignum_value(55) + end + + # Based on MRI's test/test_integer.rb (test_divmod), + # MRI maintains the following property: + # if q, r = a.divmod(b) ==> + # assert(0 < b ? (0 <= r && r < b) : (b < r && r <= 0)) + # So, r is always between 0 and b. + it "returns an Array containing quotient and modulus obtained from dividing self by the given argument" do + @bignum.divmod(4).should == [2305843009213693965, 3] + @bignum.divmod(13).should == [709490156681136604, 11] + + @bignum.divmod(4.5).should == [2049638230412172288, 3.5] + + not_supported_on :opal do + @bignum.divmod(4.0).should == [2305843009213693952, 0.0] + @bignum.divmod(13.0).should == [709490156681136640, 8.0] + + @bignum.divmod(2.0).should == [4611686018427387904, 0.0] + end + + @bignum.divmod(bignum_value).should == [1, 55] + + (-(10**50)).divmod(-(10**40 + 1)).should == [9999999999, -9999999999999999999999999999990000000001] + (10**50).divmod(10**40 + 1).should == [9999999999, 9999999999999999999999999999990000000001] + + (-10**50).divmod(10**40 + 1).should == [-10000000000, 10000000000] + (10**50).divmod(-(10**40 + 1)).should == [-10000000000, -10000000000] + end + + describe "with q = floor(x/y), a = q*b + r," do + it "returns [q,r] when a < 0, b > 0 and |a| < b" do + a = -@bignum + 1 + b = @bignum + a.divmod(b).should == [-1, 1] + end + + it "returns [q,r] when a > 0, b < 0 and a > |b|" do + b = -@bignum + 1 + a = @bignum + a.divmod(b).should == [-2, -@bignum + 2] + end + + it "returns [q,r] when a > 0, b < 0 and a < |b|" do + a = @bignum - 1 + b = -@bignum + a.divmod(b).should == [-1, -1] + end + + it "returns [q,r] when a < 0, b < 0 and |a| < |b|" do + a = -@bignum + 1 + b = -@bignum + a.divmod(b).should == [0, -@bignum + 1] + end + end + + it "raises a ZeroDivisionError when the given argument is 0" do + lambda { @bignum.divmod(0) }.should raise_error(ZeroDivisionError) + lambda { (-@bignum).divmod(0) }.should raise_error(ZeroDivisionError) + end + + # Behaviour established as correct in r23953 + it "raises a FloatDomainError if other is NaN" do + lambda { @bignum.divmod(nan_value) }.should raise_error(FloatDomainError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + lambda { @bignum.divmod(0.0) }.should raise_error(ZeroDivisionError) + lambda { (-@bignum).divmod(0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when the given argument is not an Integer" do + lambda { @bignum.divmod(mock('10')) }.should raise_error(TypeError) + lambda { @bignum.divmod("10") }.should raise_error(TypeError) + lambda { @bignum.divmod(:symbol) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/element_reference_spec.rb b/spec/rubyspec/core/bignum/element_reference_spec.rb new file mode 100644 index 0000000000..e5ee9e15ac --- /dev/null +++ b/spec/rubyspec/core/bignum/element_reference_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#[]" do + before :each do + @bignum = bignum_value(4996) + end + + it "returns the nth bit in the binary representation of self" do + @bignum[2].should == 1 + @bignum[9.2].should == 1 + @bignum[21].should == 0 + @bignum[0xffffffff].should == 0 + @bignum[-0xffffffff].should == 0 + end + + it "tries to convert the given argument to an Integer using #to_int" do + @bignum[1.3].should == @bignum[1] + + (obj = mock('2')).should_receive(:to_int).at_least(1).and_return(2) + @bignum[obj].should == 1 + end + + it "raises a TypeError when the given argument can't be converted to Integer" do + obj = mock('asdf') + lambda { @bignum[obj] }.should raise_error(TypeError) + + obj.should_receive(:to_int).and_return("asdf") + lambda { @bignum[obj] }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/eql_spec.rb b/spec/rubyspec/core/bignum/eql_spec.rb new file mode 100644 index 0000000000..c9eff9ef08 --- /dev/null +++ b/spec/rubyspec/core/bignum/eql_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#eql? when given a Bignum" do + it "returns true if the given argument has the same value" do + a = bignum_value(13) + a.should eql(bignum_value(13)) + (-a).should eql(-bignum_value(13)) + end +end + +describe "Bignum#eql? when given a non-Bignum" do + it "returns false" do + a = bignum_value(13) + a.should_not eql(a.to_f) + + a.should_not eql(2) + a.should_not eql(3.14) + a.should_not eql(:symbol) + a.should_not eql("String") + a.should_not eql(mock('str')) + end +end diff --git a/spec/rubyspec/core/bignum/equal_value_spec.rb b/spec/rubyspec/core/bignum/equal_value_spec.rb new file mode 100644 index 0000000000..0117d58683 --- /dev/null +++ b/spec/rubyspec/core/bignum/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Bignum#==" do + it_behaves_like :bignum_equal, :== +end diff --git a/spec/rubyspec/core/bignum/even_spec.rb b/spec/rubyspec/core/bignum/even_spec.rb new file mode 100644 index 0000000000..a84ea80075 --- /dev/null +++ b/spec/rubyspec/core/bignum/even_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#even?" do + it "returns true if self is even and positive" do + (10000**10).even?.should be_true + end + + it "returns true if self is even and negative" do + (-10000**10).even?.should be_true + end + + it "returns false if self is odd and positive" do + (9879**976).even?.should be_false + end + + it "returns false if self is odd and negative" do + (-9879**976).even?.should be_false + end +end diff --git a/spec/rubyspec/core/bignum/exponent_spec.rb b/spec/rubyspec/core/bignum/exponent_spec.rb new file mode 100644 index 0000000000..f69da833fd --- /dev/null +++ b/spec/rubyspec/core/bignum/exponent_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#**" do + before :each do + @bignum = bignum_value(47) + end + + it "returns self raised to other power" do + (@bignum ** 4).should == 7237005577332262361485077344629993318496048279512298547155833600056910050625 + (@bignum ** 1.2).should be_close(57262152889751597425762.57804, TOLERANCE) + end + + it "raises a TypeError when given a non-Integer" do + lambda { @bignum ** mock('10') }.should raise_error + lambda { @bignum ** "10" }.should raise_error + lambda { @bignum ** :symbol }.should raise_error + end + + it "switch to a Float when the values is too big" do + flt = (@bignum ** @bignum) + flt.should be_kind_of(Float) + flt.infinite?.should == 1 + end + + it "returns a complex number when negative and raised to a fractional power" do + ((-@bignum) ** (1.0/3)) .should be_close(Complex(1048576,1816186.907597341), TOLERANCE) + ((-@bignum) ** Rational(1,3)).should be_close(Complex(1048576,1816186.907597341), TOLERANCE) + end +end diff --git a/spec/rubyspec/core/bignum/fdiv_spec.rb b/spec/rubyspec/core/bignum/fdiv_spec.rb new file mode 100644 index 0000000000..35f3ede010 --- /dev/null +++ b/spec/rubyspec/core/bignum/fdiv_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#fdiv" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/bignum/gt_spec.rb b/spec/rubyspec/core/bignum/gt_spec.rb new file mode 100644 index 0000000000..5c814eedd1 --- /dev/null +++ b/spec/rubyspec/core/bignum/gt_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#>" do + before :each do + @bignum = bignum_value(732) + end + + it "returns true if self is greater than the given argument" do + (@bignum > (@bignum - 1)).should == true + (@bignum > 14.6).should == true + (@bignum > 10).should == true + + (@bignum > (@bignum + 500)).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { @bignum > "4" }.should raise_error(ArgumentError) + lambda { @bignum > mock('str') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/bignum/gte_spec.rb b/spec/rubyspec/core/bignum/gte_spec.rb new file mode 100644 index 0000000000..e32ce19e0f --- /dev/null +++ b/spec/rubyspec/core/bignum/gte_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#>=" do + before :each do + @bignum = bignum_value(14) + end + + it "returns true if self is greater than or equal to other" do + (@bignum >= @bignum).should == true + (@bignum >= (@bignum + 2)).should == false + (@bignum >= 5664.2).should == true + (@bignum >= 4).should == true + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { @bignum >= "4" }.should raise_error(ArgumentError) + lambda { @bignum >= mock('str') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/bignum/hash_spec.rb b/spec/rubyspec/core/bignum/hash_spec.rb new file mode 100644 index 0000000000..bdc85c3fdc --- /dev/null +++ b/spec/rubyspec/core/bignum/hash_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#hash" do + it "is provided" do + bignum_value.respond_to?(:hash).should == true + end + + it "is stable" do + bignum_value.hash.should == bignum_value.hash + bignum_value.hash.should_not == bignum_value(1).hash + end +end diff --git a/spec/rubyspec/core/bignum/left_shift_spec.rb b/spec/rubyspec/core/bignum/left_shift_spec.rb new file mode 100644 index 0000000000..364f51b708 --- /dev/null +++ b/spec/rubyspec/core/bignum/left_shift_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#<< with n << m" do + before :each do + @bignum = bignum_value * 16 + end + + it "returns n shifted left m bits when n > 0, m > 0" do + (@bignum << 4).should == 2361183241434822606848 + end + + it "returns n shifted left m bits when n < 0, m > 0" do + (-@bignum << 9).should == -75557863725914323419136 + end + + it "returns n shifted right m bits when n > 0, m < 0" do + (@bignum << -1).should == 73786976294838206464 + end + + it "returns n shifted right m bits when n < 0, m < 0" do + (-@bignum << -2).should == -36893488147419103232 + end + + it "returns n when n > 0, m == 0" do + (@bignum << 0).should == @bignum + end + + it "returns n when n < 0, m == 0" do + (-@bignum << 0).should == -@bignum + end + + it "returns 0 when m < 0 and m == p where 2**p > n >= 2**(p-1)" do + (@bignum << -68).should == 0 + end + + it "returns 0 when m < 0 and m is a Bignum" do + (@bignum << -bignum_value).should == 0 + end + + it "returns a Fixnum == fixnum_max when (fixnum_max * 2) << -1 and n > 0" do + result = (fixnum_max * 2) << -1 + result.should be_an_instance_of(Fixnum) + result.should == fixnum_max + end + + it "returns a Fixnum == fixnum_min when (fixnum_min * 2) << -1 and n < 0" do + result = (fixnum_min * 2) << -1 + result.should be_an_instance_of(Fixnum) + result.should == fixnum_min + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("4") + obj.should_receive(:to_int).and_return(4) + + (@bignum << obj).should == 2361183241434822606848 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + lambda { @bignum << obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + lambda { @bignum << nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { @bignum << "4" }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/lt_spec.rb b/spec/rubyspec/core/bignum/lt_spec.rb new file mode 100644 index 0000000000..802c68a58b --- /dev/null +++ b/spec/rubyspec/core/bignum/lt_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#<" do + before :each do + @bignum = bignum_value(32) + end + + it "returns true if self is less than the given argument" do + (@bignum < @bignum + 1).should == true + (-@bignum < -(@bignum - 1)).should == true + + (@bignum < 1).should == false + (@bignum < 5).should == false + + (@bignum < 4.999).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { @bignum < "4" }.should raise_error(ArgumentError) + lambda { @bignum < mock('str') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/bignum/lte_spec.rb b/spec/rubyspec/core/bignum/lte_spec.rb new file mode 100644 index 0000000000..9a1d22d3be --- /dev/null +++ b/spec/rubyspec/core/bignum/lte_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#<=" do + before :each do + @bignum = bignum_value(39) + end + + it "returns true if self is less than or equal to other" do + (@bignum <= @bignum).should == true + (-@bignum <= -(@bignum - 1)).should == true + + (@bignum <= 4.999).should == false + end + + it "returns false if compares with near float" do + (@bignum <= (@bignum + 0.0)).should == false + (@bignum <= (@bignum + 0.5)).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { @bignum <= "4" }.should raise_error(ArgumentError) + lambda { @bignum <= mock('str') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/bignum/magnitude_spec.rb b/spec/rubyspec/core/bignum/magnitude_spec.rb new file mode 100644 index 0000000000..35b9ba6f1e --- /dev/null +++ b/spec/rubyspec/core/bignum/magnitude_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/abs', __FILE__) + +describe "Bignum#magnitude" do + it_behaves_like(:bignum_abs, :magnitude) +end diff --git a/spec/rubyspec/core/bignum/minus_spec.rb b/spec/rubyspec/core/bignum/minus_spec.rb new file mode 100644 index 0000000000..754ef7fa42 --- /dev/null +++ b/spec/rubyspec/core/bignum/minus_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#-" do + before :each do + @bignum = bignum_value(314) + end + + it "returns self minus the given Integer" do + (@bignum - 9).should == 9223372036854776113 + (@bignum - 12.57).should be_close(9223372036854776109.43, TOLERANCE) + (@bignum - bignum_value(42)).should == 272 + end + + it "raises a TypeError when given a non-Integer" do + lambda { @bignum - mock('10') }.should raise_error(TypeError) + lambda { @bignum - "10" }.should raise_error(TypeError) + lambda { @bignum - :symbol }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/modulo_spec.rb b/spec/rubyspec/core/bignum/modulo_spec.rb new file mode 100644 index 0000000000..eee1dc76a6 --- /dev/null +++ b/spec/rubyspec/core/bignum/modulo_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/modulo', __FILE__) + +describe "Bignum#%" do + it_behaves_like(:bignum_modulo, :%) +end + +describe "Bignum#modulo" do + it_behaves_like(:bignum_modulo, :modulo) +end diff --git a/spec/rubyspec/core/bignum/multiply_spec.rb b/spec/rubyspec/core/bignum/multiply_spec.rb new file mode 100644 index 0000000000..486e36ecbc --- /dev/null +++ b/spec/rubyspec/core/bignum/multiply_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#*" do + before :each do + @bignum = bignum_value(772) + end + + it "returns self multiplied by the given Integer" do + (@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE) + (@bignum * (1/bignum_value(0xffff).to_f)).should be_close(1.0, TOLERANCE) + (@bignum * 10).should == 92233720368547765800 + (@bignum * (@bignum - 40)).should == 85070591730234629737795195287525433200 + end + + it "raises a TypeError when given a non-Integer" do + lambda { @bignum * mock('10') }.should raise_error(TypeError) + lambda { @bignum * "10" }.should raise_error(TypeError) + lambda { @bignum * :symbol }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/odd_spec.rb b/spec/rubyspec/core/bignum/odd_spec.rb new file mode 100644 index 0000000000..9d4c1191f6 --- /dev/null +++ b/spec/rubyspec/core/bignum/odd_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#odd?" do + it "returns true if self is odd and positive" do + (987279**19).odd?.should be_true + end + + it "returns true if self is odd and negative" do + (-9873389**97).odd?.should be_true + end + + it "returns false if self is even and positive" do + (10000000**10).odd?.should be_false + end + + it "returns false if self is even and negative" do + (-1000000**100).odd?.should be_false + end +end diff --git a/spec/rubyspec/core/bignum/plus_spec.rb b/spec/rubyspec/core/bignum/plus_spec.rb new file mode 100644 index 0000000000..411e226649 --- /dev/null +++ b/spec/rubyspec/core/bignum/plus_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#+" do + before :each do + @bignum = bignum_value(76) + end + + it "returns self plus the given Integer" do + (@bignum + 4).should == 9223372036854775888 + (@bignum + 4.2).should be_close(9223372036854775888.2, TOLERANCE) + (@bignum + bignum_value(3)).should == 18446744073709551695 + end + + it "raises a TypeError when given a non-Integer" do + lambda { @bignum + mock('10') }.should raise_error(TypeError) + lambda { @bignum + "10" }.should raise_error(TypeError) + lambda { @bignum + :symbol}.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/remainder_spec.rb b/spec/rubyspec/core/bignum/remainder_spec.rb new file mode 100644 index 0000000000..59f7eb4326 --- /dev/null +++ b/spec/rubyspec/core/bignum/remainder_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#remainder" do + it "returns the remainder of dividing self by other" do + a = bignum_value(79) + a.remainder(2).should == 1 + a.remainder(97.345).should be_close(46.5674996147722, TOLERANCE) + a.remainder(bignum_value).should == 79 + end + + it "raises a ZeroDivisionError if other is zero and not a Float" do + lambda { bignum_value(66).remainder(0) }.should raise_error(ZeroDivisionError) + end + + it "does raises ZeroDivisionError if other is zero and a Float" do + a = bignum_value(7) + b = bignum_value(32) + lambda { a.remainder(0.0) }.should raise_error(ZeroDivisionError) + lambda { b.remainder(-0.0) }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/core/bignum/right_shift_spec.rb b/spec/rubyspec/core/bignum/right_shift_spec.rb new file mode 100644 index 0000000000..d65f7c00a9 --- /dev/null +++ b/spec/rubyspec/core/bignum/right_shift_spec.rb @@ -0,0 +1,99 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#>> with n >> m" do + before :each do + @bignum = bignum_value * 16 + end + + it "returns n shifted right m bits when n > 0, m > 0" do + (@bignum >> 1).should == 73786976294838206464 + end + + it "returns n shifted right m bits when n < 0, m > 0" do + (-@bignum >> 2).should == -36893488147419103232 + end + + it "respects twos complement signed shifting" do + # This explicit left hand value is important because it is the + # exact bit pattern that matters, so it's important it's right + # here to show the significance. + # + + (-42949672980000000000000 >> 14).should == -2621440001220703125 + (-42949672980000000000001 >> 14).should == -2621440001220703126 + # Note the off by one -------------------- ^^^^^^^^^^^^^^^^^^^^ + # This is because even though we discard the lowest bit, in twos + # complement it would influence the bits to the left of it. + + (-42949672980000000000000 >> 15).should == -1310720000610351563 + (-42949672980000000000001 >> 15).should == -1310720000610351563 + + (-0xfffffffffffffffff >> 32).should == -68719476736 + end + + it "respects twos complement signed shifting for very large values" do + giant = 42949672980000000000000000000000000000000000000000000000000000000000000000000000000000000000 + neg = -giant + + (giant >> 84).should == 2220446050284288846538547929770901490087453566957265138626098632812 + (neg >> 84).should == -2220446050284288846538547929770901490087453566957265138626098632813 + end + + it "returns n shifted left m bits when n > 0, m < 0" do + (@bignum >> -2).should == 590295810358705651712 + end + + it "returns n shifted left m bits when n < 0, m < 0" do + (-@bignum >> -3).should == -1180591620717411303424 + end + + it "returns n when n > 0, m == 0" do + (@bignum >> 0).should == @bignum + end + + it "returns n when n < 0, m == 0" do + (-@bignum >> 0).should == -@bignum + end + + it "returns 0 when m > 0 and m == p where 2**p > n >= 2**(p-1)" do + (@bignum >> 68).should == 0 + end + + it "returns 0 when m is a Bignum" do + (@bignum >> bignum_value).should == 0 + end + + it "returns a Fixnum == fixnum_max when (fixnum_max * 2) >> 1 and n > 0" do + result = (fixnum_max * 2) >> 1 + result.should be_an_instance_of(Fixnum) + result.should == fixnum_max + end + + it "returns a Fixnum == fixnum_min when (fixnum_min * 2) >> 1 and n < 0" do + result = (fixnum_min * 2) >> 1 + result.should be_an_instance_of(Fixnum) + result.should == fixnum_min + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("2") + obj.should_receive(:to_int).and_return(2) + + (@bignum >> obj).should == 36893488147419103232 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + lambda { @bignum >> obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + lambda { @bignum >> nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { @bignum >> "4" }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/shared/abs.rb b/spec/rubyspec/core/bignum/shared/abs.rb new file mode 100644 index 0000000000..35fd85060c --- /dev/null +++ b/spec/rubyspec/core/bignum/shared/abs.rb @@ -0,0 +1,6 @@ +describe :bignum_abs, shared: true do + it "returns the absolute value" do + bignum_value(39).send(@method).should == 9223372036854775847 + (-bignum_value(18)).send(@method).should == 9223372036854775826 + end +end diff --git a/spec/rubyspec/core/bignum/shared/divide.rb b/spec/rubyspec/core/bignum/shared/divide.rb new file mode 100644 index 0000000000..cbde69bbb2 --- /dev/null +++ b/spec/rubyspec/core/bignum/shared/divide.rb @@ -0,0 +1,27 @@ +describe :bignum_divide, shared: true do + before :each do + @bignum = bignum_value(88) + end + + it "returns self divided by other" do + @bignum.send(@method, 4).should == 2305843009213693974 + + @bignum.send(@method, bignum_value(2)).should be_close(1, TOLERANCE) + + (-(10**50)).send(@method, -(10**40 + 1)).should == 9999999999 + (10**50).send(@method, 10**40 + 1).should == 9999999999 + + (-10**50).send(@method, 10**40 + 1).should == -10000000000 + (10**50).send(@method, -(10**40 + 1)).should == -10000000000 + end + + it "raises a ZeroDivisionError if other is zero and not a Float" do + lambda { @bignum.send(@method, 0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + lambda { @bignum.send(@method, mock('10')) }.should raise_error(TypeError) + lambda { @bignum.send(@method, "2") }.should raise_error(TypeError) + lambda { @bignum.send(@method, :symbol) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/shared/equal.rb b/spec/rubyspec/core/bignum/shared/equal.rb new file mode 100644 index 0000000000..ffe4daf4f1 --- /dev/null +++ b/spec/rubyspec/core/bignum/shared/equal.rb @@ -0,0 +1,31 @@ +describe :bignum_equal, shared: true do + before :each do + @bignum = bignum_value + end + + it "returns true if self has the same value as the given argument" do + @bignum.send(@method, @bignum).should == true + @bignum.send(@method, @bignum.to_f).should == true + + @bignum.send(@method, @bignum + 1).should == false + (@bignum + 1).send(@method, @bignum).should == false + + @bignum.send(@method, 9).should == false + @bignum.send(@method, 9.01).should == false + + @bignum.send(@method, bignum_value(10)).should == false + end + + it "calls 'other == self' if the given argument is not an Integer" do + obj = mock('not integer') + obj.should_receive(:==).and_return(true) + @bignum.send(@method, obj).should == true + end + + it "returns the result of 'other == self' as a boolean" do + obj = mock('not integer') + obj.should_receive(:==).exactly(2).times.and_return("woot", nil) + @bignum.send(@method, obj).should == true + @bignum.send(@method, obj).should == false + end +end diff --git a/spec/rubyspec/core/bignum/shared/modulo.rb b/spec/rubyspec/core/bignum/shared/modulo.rb new file mode 100644 index 0000000000..9814e22f3b --- /dev/null +++ b/spec/rubyspec/core/bignum/shared/modulo.rb @@ -0,0 +1,29 @@ +describe :bignum_modulo, shared: true do + before :each do + @bignum = bignum_value + end + + it "returns the modulus obtained from dividing self by the given argument" do + @bignum.send(@method, 5).should == 3 + @bignum.send(@method, -5).should == -2 + @bignum.send(@method, -100).should == -92 + @bignum.send(@method, 2.22).should be_close(0.780180180180252, TOLERANCE) + @bignum.send(@method, bignum_value(10)).should == 9223372036854775808 + end + + it "raises a ZeroDivisionError when the given argument is 0" do + lambda { @bignum.send(@method, 0) }.should raise_error(ZeroDivisionError) + lambda { (-@bignum).send(@method, 0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + lambda { @bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + lambda { -@bignum.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + lambda { @bignum.send(@method, mock('10')) }.should raise_error(TypeError) + lambda { @bignum.send(@method, "10") }.should raise_error(TypeError) + lambda { @bignum.send(@method, :symbol) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/bignum/size_spec.rb b/spec/rubyspec/core/bignum/size_spec.rb new file mode 100644 index 0000000000..8629cba972 --- /dev/null +++ b/spec/rubyspec/core/bignum/size_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#size" do + it "returns the number of bytes required to hold the unsigned bignum data" do + # that is, n such that 256 * n <= val.abs < 256 * (n+1) + (256**7).size.should == 8 + (256**8).size.should == 9 + (256**9).size.should == 10 + (256**10).size.should == 11 + (256**10-1).size.should == 10 + (256**11).size.should == 12 + (256**12).size.should == 13 + (256**20-1).size.should == 20 + (256**40-1).size.should == 40 + end +end diff --git a/spec/rubyspec/core/bignum/to_f_spec.rb b/spec/rubyspec/core/bignum/to_f_spec.rb new file mode 100644 index 0000000000..8d99045c95 --- /dev/null +++ b/spec/rubyspec/core/bignum/to_f_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#to_f" do + it "returns self converted to a Float" do + bignum_value(0x4000_0aa0_0bb0_0000).to_f.should eql(13_835_069_737_789_292_544.00) + bignum_value(0x8000_0000_0000_0ccc).to_f.should eql(18_446_744_073_709_555_712.00) + (-bignum_value(99)).to_f.should eql(-9_223_372_036_854_775_808.00) + end + + it "converts number close to Float::MAX without exceeding MAX or producing NaN" do + (10**308).to_f.should == 10.0 ** 308 + end +end diff --git a/spec/rubyspec/core/bignum/to_s_spec.rb b/spec/rubyspec/core/bignum/to_s_spec.rb new file mode 100644 index 0000000000..524639adb6 --- /dev/null +++ b/spec/rubyspec/core/bignum/to_s_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#to_s when given a base" do + it "returns self converted to a String using the given base" do + a = 2**64 + a.to_s(2).should == "10000000000000000000000000000000000000000000000000000000000000000" + a.to_s(8).should == "2000000000000000000000" + a.to_s(16).should == "10000000000000000" + a.to_s(32).should == "g000000000000" + end + + it "raises an ArgumentError if the base is less than 2 or higher than 36" do + lambda { 123.to_s(-1) }.should raise_error(ArgumentError) + lambda { 123.to_s(0) }.should raise_error(ArgumentError) + lambda { 123.to_s(1) }.should raise_error(ArgumentError) + lambda { 123.to_s(37) }.should raise_error(ArgumentError) + end +end + +describe "Bignum#to_s when given no base" do + it "returns self converted to a String using base 10" do + bignum_value(9).to_s.should == "9223372036854775817" + bignum_value.to_s.should == "9223372036854775808" + (-bignum_value(675)).to_s.should == "-9223372036854776483" + end +end + +with_feature :encoding do + describe "Bignum#to_s" do + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + bignum_value.to_s.encoding.should equal(Encoding::US_ASCII) + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do + Encoding.default_internal = Encoding::IBM437 + bignum_value.to_s.encoding.should equal(Encoding::US_ASCII) + end + end +end diff --git a/spec/rubyspec/core/bignum/uminus_spec.rb b/spec/rubyspec/core/bignum/uminus_spec.rb new file mode 100644 index 0000000000..7ec432ac71 --- /dev/null +++ b/spec/rubyspec/core/bignum/uminus_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Bignum#-@" do + it "returns self as a negative value" do + bignum_value.send(:-@).should == -9223372036854775808 + (-bignum_value).send(:-@).should == 9223372036854775808 + + bignum_value(921).send(:-@).should == -9223372036854776729 + (-bignum_value(921).send(:-@)).should == 9223372036854776729 + end +end diff --git a/spec/rubyspec/core/binding/clone_spec.rb b/spec/rubyspec/core/binding/clone_spec.rb new file mode 100644 index 0000000000..d607ae2a9e --- /dev/null +++ b/spec/rubyspec/core/binding/clone_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/clone', __FILE__) + +describe "Binding#clone" do + it_behaves_like(:binding_clone, :clone) +end diff --git a/spec/rubyspec/core/binding/dup_spec.rb b/spec/rubyspec/core/binding/dup_spec.rb new file mode 100644 index 0000000000..7f242626d7 --- /dev/null +++ b/spec/rubyspec/core/binding/dup_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/clone', __FILE__) + +describe "Binding#dup" do + it_behaves_like(:binding_clone, :dup) +end diff --git a/spec/rubyspec/core/binding/eval_spec.rb b/spec/rubyspec/core/binding/eval_spec.rb new file mode 100644 index 0000000000..cedd5fcdf9 --- /dev/null +++ b/spec/rubyspec/core/binding/eval_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Binding#eval" do + it "behaves like Kernel.eval(..., self)" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_binding + + bind.eval("@secret += square(3)").should == 10 + bind.eval("a").should be_true + + bind.eval("class Inside; end") + bind.eval("Inside.name").should == "BindingSpecs::Demo::Inside" + end + + describe "with a file given" do + it "does not store the filename permanently" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_binding + + bind.eval("__FILE__", "test.rb").should == "test.rb" + bind.eval("__FILE__").should_not == "test.rb" + end + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/binding/fixtures/classes.rb b/spec/rubyspec/core/binding/fixtures/classes.rb new file mode 100644 index 0000000000..b7b7618411 --- /dev/null +++ b/spec/rubyspec/core/binding/fixtures/classes.rb @@ -0,0 +1,32 @@ +module BindingSpecs + class Demo + def initialize(n) + @secret = n + end + + def square(n) + n * n + end + + def get_binding_and_line + a = true + [binding, __LINE__] + end + + def get_binding + get_binding_and_line[0] + end + + def get_line_of_binding + get_binding_and_line[1] + end + + def get_file_of_binding + __FILE__ + end + + def get_empty_binding + binding + end + end +end diff --git a/spec/rubyspec/core/binding/local_variable_defined_spec.rb b/spec/rubyspec/core/binding/local_variable_defined_spec.rb new file mode 100644 index 0000000000..5fa79b19e2 --- /dev/null +++ b/spec/rubyspec/core/binding/local_variable_defined_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe 'Binding#local_variable_defined?' do + it 'returns false when a variable is not defined' do + binding.local_variable_defined?(:foo).should == false + end + + it 'returns true when a regular local variable is defined' do + foo = 10 + binding.local_variable_defined?(:foo).should == true + end + + it 'returns true when a local variable is defined using eval()' do + bind = binding + bind.eval('foo = 10') + + bind.local_variable_defined?(:foo).should == true + end + + it 'returns true when a local variable is defined using Binding#local_variable_set' do + bind = binding + bind.local_variable_set(:foo, 10) + + bind.local_variable_defined?(:foo).should == true + end + + it 'returns true when a local variable is defined in a parent scope' do + foo = 10 + lambda { + binding.local_variable_defined?(:foo) + }.call.should == true + end + + it 'allows usage of a String as the variable name' do + foo = 10 + binding.local_variable_defined?('foo').should == true + end + + it 'allows usage of an object responding to #to_str as the variable name' do + foo = 10 + name = mock(:obj) + name.stub!(:to_str).and_return('foo') + + binding.local_variable_defined?(name).should == true + end +end diff --git a/spec/rubyspec/core/binding/local_variable_get_spec.rb b/spec/rubyspec/core/binding/local_variable_get_spec.rb new file mode 100644 index 0000000000..b7b0b14594 --- /dev/null +++ b/spec/rubyspec/core/binding/local_variable_get_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Binding#local_variable_get" do + it "reads local variables captured in the binding" do + a = 42 + bind = binding + bind.local_variable_get(:a).should == 42 + end + + it "raises a NameError for missing variables" do + bind = BindingSpecs::Demo.new(1).get_empty_binding + + lambda { + bind.local_variable_get(:no_such_variable) + }.should raise_error(NameError) + end + + it "reads variables added later to the binding" do + bind = BindingSpecs::Demo.new(1).get_empty_binding + + lambda { + bind.local_variable_get(:a) + }.should raise_error(NameError) + + bind.local_variable_set(:a, 42) + + bind.local_variable_get(:a).should == 42 + end + + it 'gets a local variable defined in a parent scope' do + number = 10 + + lambda { + binding.local_variable_get(:number) + }.call.should == 10 + end + + it 'gets a local variable defined using eval()' do + bind = binding + bind.eval('number = 10') + + bind.local_variable_get(:number).should == 10 + end +end diff --git a/spec/rubyspec/core/binding/local_variable_set_spec.rb b/spec/rubyspec/core/binding/local_variable_set_spec.rb new file mode 100644 index 0000000000..4b14d891b3 --- /dev/null +++ b/spec/rubyspec/core/binding/local_variable_set_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Binding#local_variable_set" do + it "adds nonexistent variables to the binding's eval scope" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_empty_binding + bind.eval('local_variables').should == [] + bind.local_variable_set :foo, 1 + bind.eval('local_variables').should == [:foo] + bind.eval('foo').should == 1 + end + + it 'sets a new local variable' do + bind = binding + + bind.local_variable_set(:number, 10) + bind.local_variable_get(:number).should == 10 + end + + it 'sets a local variable using a String as the variable name' do + bind = binding + + bind.local_variable_set('number', 10) + bind.local_variable_get('number').should == 10 + end + + it 'sets a local variable using an object responding to #to_str as the variable name' do + bind = binding + name = mock(:obj) + name.stub!(:to_str).and_return('number') + + bind.local_variable_set(name, 10) + bind.local_variable_get(name).should == 10 + end + + it 'scopes new local variables to the receiving Binding' do + bind = binding + bind.local_variable_set(:number, 10) + + lambda { number }.should raise_error(NameError) + end + + it 'overwrites an existing local variable defined before a Binding' do + number = 10 + bind = binding + + bind.local_variable_set(:number, 20) + number.should == 20 + end + + it 'overwrites a local variable defined using eval()' do + bind = binding + bind.eval('number = 10') + + bind.local_variable_set(:number, 20) + bind.local_variable_get(:number).should == 20 + end +end diff --git a/spec/rubyspec/core/binding/local_variables_spec.rb b/spec/rubyspec/core/binding/local_variables_spec.rb new file mode 100644 index 0000000000..bc6ea71f10 --- /dev/null +++ b/spec/rubyspec/core/binding/local_variables_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Binding#local_variables" do + it "returns an Array" do + binding.local_variables.should be_kind_of(Array) + end + + it "includes local variables in the current scope" do + a = 1 + b = nil + binding.local_variables.should == [:a, :b] + end + + it "includes local variables defined after calling binding.local_variables" do + binding.local_variables.should == [:a, :b] + a = 1 + b = 2 + end + + it "includes local variables of inherited scopes and eval'ed context" do + p = proc { |a| b = 1; eval("c = 2; binding.local_variables") } + p.call.should == [:c, :a, :b, :p] + end + + it "includes shadowed local variables only once" do + a = 1 + proc { |a| binding.local_variables }.call(2).should == [:a] + end + + it "includes new variables defined in the binding" do + b = binding + b.local_variable_set :a, 42 + b.local_variables.should == [:a, :b] + end +end diff --git a/spec/rubyspec/core/binding/location_spec.rb b/spec/rubyspec/core/binding/location_spec.rb new file mode 100644 index 0000000000..b4a066038d --- /dev/null +++ b/spec/rubyspec/core/binding/location_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Binding#eval" do + it "inherits __LINE__ from the enclosing scope" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_binding + bind.eval("__LINE__").should == obj.get_line_of_binding + end + + it "preserves __LINE__ across multiple calls to eval" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_binding + bind.eval("__LINE__").should == obj.get_line_of_binding + bind.eval("__LINE__").should == obj.get_line_of_binding + end + + it "increments __LINE__ on each line of a multiline eval" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_binding + bind.eval("#foo\n__LINE__").should == obj.get_line_of_binding + 1 + end + + it "starts with a __LINE__ of 1 if a filename is passed" do + bind = BindingSpecs::Demo.new(1).get_binding + bind.eval("__LINE__", "(test)").should == 1 + bind.eval("#foo\n__LINE__", "(test)").should == 2 + end + + it "starts with a __LINE__ from the third argument if passed" do + bind = BindingSpecs::Demo.new(1).get_binding + bind.eval("__LINE__", "(test)", 88).should == 88 + bind.eval("#foo\n__LINE__", "(test)", 88).should == 89 + end + + it "inherits __FILE__ from the enclosing scope" do + obj = BindingSpecs::Demo.new(1) + bind = obj.get_binding + bind.eval("__FILE__").should == obj.get_file_of_binding + end + + it "uses the __FILE__ that is passed in" do + bind = BindingSpecs::Demo.new(1).get_binding + bind.eval("__FILE__", "(test)").should == "(test)" + end +end diff --git a/spec/rubyspec/core/binding/receiver_spec.rb b/spec/rubyspec/core/binding/receiver_spec.rb new file mode 100644 index 0000000000..8784ab0e38 --- /dev/null +++ b/spec/rubyspec/core/binding/receiver_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Binding#receiver" do + it "returns the object to which binding is bound" do + obj = BindingSpecs::Demo.new(1) + obj.get_binding.receiver.should == obj + + binding.receiver.should == self + end +end diff --git a/spec/rubyspec/core/binding/shared/clone.rb b/spec/rubyspec/core/binding/shared/clone.rb new file mode 100644 index 0000000000..723e0490c3 --- /dev/null +++ b/spec/rubyspec/core/binding/shared/clone.rb @@ -0,0 +1,20 @@ +describe :binding_clone, shared: true do + before :each do + @b1 = BindingSpecs::Demo.new(99).get_binding + @b2 = @b1.send(@method) + end + + it "returns a copy of the Binding object" do + @b1.should_not == @b2 + + eval("@secret", @b1).should == eval("@secret", @b2) + eval("square(2)", @b1).should == eval("square(2)", @b2) + eval("self.square(2)", @b1).should == eval("self.square(2)", @b2) + eval("a", @b1).should == eval("a", @b2) + end + + it "is a shallow copy of the Binding object" do + eval("a = false", @b1) + eval("a", @b2).should == false + end +end diff --git a/spec/rubyspec/core/builtin_constants/builtin_constants_spec.rb b/spec/rubyspec/core/builtin_constants/builtin_constants_spec.rb new file mode 100644 index 0000000000..9019b127c2 --- /dev/null +++ b/spec/rubyspec/core/builtin_constants/builtin_constants_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "RUBY_VERSION" do + it "is a String" do + RUBY_VERSION.should be_kind_of(String) + end +end + +describe "RUBY_PATCHLEVEL" do + it "is a Fixnum" do + RUBY_PATCHLEVEL.should be_kind_of(Fixnum) + end +end + +describe "RUBY_COPYRIGHT" do + it "is a String" do + RUBY_COPYRIGHT.should be_kind_of(String) + end +end + +describe "RUBY_DESCRIPTION" do + it "is a String" do + RUBY_DESCRIPTION.should be_kind_of(String) + end +end + +describe "RUBY_ENGINE" do + it "is a String" do + RUBY_ENGINE.should be_kind_of(String) + end +end + +describe "RUBY_PLATFORM" do + it "is a String" do + RUBY_PLATFORM.should be_kind_of(String) + end +end + +describe "RUBY_RELEASE_DATE" do + it "is a String" do + RUBY_RELEASE_DATE.should be_kind_of(String) + end +end + +describe "RUBY_REVISION" do + it "is a Fixnum" do + RUBY_REVISION.should be_kind_of(Fixnum) + end +end diff --git a/spec/rubyspec/core/class/allocate_spec.rb b/spec/rubyspec/core/class/allocate_spec.rb new file mode 100644 index 0000000000..015db292eb --- /dev/null +++ b/spec/rubyspec/core/class/allocate_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Class#allocate" do + it "returns an instance of self" do + klass = Class.new + klass.allocate.should be_an_instance_of(klass) + end + + it "returns a fully-formed instance of Module" do + klass = Class.allocate + klass.constants.should_not == nil + klass.methods.should_not == nil + end + + it "throws an exception when calling a method on a new instance" do + klass = Class.allocate + lambda do + klass.new + end.should raise_error(Exception) + end + + it "does not call initialize on the new instance" do + klass = Class.new do + def initialize(*args) + @initialized = true + end + + def initialized? + @initialized || false + end + end + + klass.allocate.initialized?.should == false + end + + it "raises TypeError for #superclass" do + lambda do + Class.allocate.superclass + end.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/class/dup_spec.rb b/spec/rubyspec/core/class/dup_spec.rb new file mode 100644 index 0000000000..0548536bd6 --- /dev/null +++ b/spec/rubyspec/core/class/dup_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# NOTE: This is actually implemented by Module#initialize_copy +describe "Class#dup" do + it "duplicates both the class and the singleton class" do + klass = Class.new do + def hello + "hello" + end + + def self.message + "text" + end + end + + klass_dup = klass.dup + + klass_dup.new.hello.should == "hello" + klass_dup.message.should == "text" + end + + it "retains an included module in the ancestor chain for the singleton class" do + klass = Class.new + mod = Module.new do + def hello + "hello" + end + end + + klass.extend(mod) + klass_dup = klass.dup + klass_dup.hello.should == "hello" + end + + it "retains the correct ancestor chain for the singleton class" do + super_klass = Class.new do + def hello + "hello" + end + + def self.message + "text" + end + end + + klass = Class.new(super_klass) + klass_dup = klass.dup + + klass_dup.new.hello.should == "hello" + klass_dup.message.should == "text" + end + + it "sets the name from the class to nil if not assigned to a constant" do + copy = CoreClassSpecs::Record.dup + copy.name.should be_nil + end + + it "stores the new name if assigned to a constant" do + CoreClassSpecs::RecordCopy = CoreClassSpecs::Record.dup + CoreClassSpecs::RecordCopy.name.should == "CoreClassSpecs::RecordCopy" + end + +end diff --git a/spec/rubyspec/core/class/fixtures/classes.rb b/spec/rubyspec/core/class/fixtures/classes.rb new file mode 100644 index 0000000000..f96db90795 --- /dev/null +++ b/spec/rubyspec/core/class/fixtures/classes.rb @@ -0,0 +1,47 @@ +module CoreClassSpecs + class Record + end + + module M + def inherited(klass) + ScratchPad.record klass + super + end + end + + class F; end + class << F + include M + end + + class A + def self.inherited(klass) + ScratchPad.record klass + end + end + + class H < A + def self.inherited(klass) + super + end + end + + module Inherited + class A + SUBCLASSES = [] + def self.inherited(subclass) + SUBCLASSES << [self, subclass] + end + end + + class B < A; end + class B < A; end # reopen + class C < B; end + + class D + def self.inherited(subclass) + ScratchPad << self + end + end + end +end diff --git a/spec/rubyspec/core/class/inherited_spec.rb b/spec/rubyspec/core/class/inherited_spec.rb new file mode 100644 index 0000000000..356e46be7c --- /dev/null +++ b/spec/rubyspec/core/class/inherited_spec.rb @@ -0,0 +1,102 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Class.inherited" do + + before :each do + ScratchPad.record nil + end + + it "is invoked with the child Class when self is subclassed" do + begin + top = Class.new do + def self.inherited(cls) + $child_class = cls + end + end + + child = Class.new(top) + $child_class.should == child + + other_child = Class.new(top) + $child_class.should == other_child + ensure + $child_class = nil + end + end + + it "is invoked only once per subclass" do + expected = [ + [CoreClassSpecs::Inherited::A, CoreClassSpecs::Inherited::B], + [CoreClassSpecs::Inherited::B, CoreClassSpecs::Inherited::C], + ] + + CoreClassSpecs::Inherited::A::SUBCLASSES.should == expected + end + + it "is called when marked as a private class method" do + a = Class.new do + def self.inherited(klass) + ScratchPad.record klass + end + end + a.private_class_method :inherited + ScratchPad.recorded.should == nil + b = Class.new(a) + ScratchPad.recorded.should == b + end + + it "is called when marked as a protected class method" do + a = Class.new + class << a + def inherited(klass) + ScratchPad.record klass + end + protected :inherited + end + ScratchPad.recorded.should == nil + b = Class.new(a) + ScratchPad.recorded.should == b + end + + it "is called when marked as a public class method" do + a = Class.new do + def self.inherited(klass) + ScratchPad.record klass + end + end + a.public_class_method :inherited + ScratchPad.recorded.should == nil + b = Class.new(a) + ScratchPad.recorded.should == b + end + + it "is called by super from a method provided by an included module" do + ScratchPad.recorded.should == nil + e = Class.new(CoreClassSpecs::F) + ScratchPad.recorded.should == e + end + + it "is called by super even when marked as a private class method" do + ScratchPad.recorded.should == nil + CoreClassSpecs::H.private_class_method :inherited + i = Class.new(CoreClassSpecs::H) + ScratchPad.recorded.should == i + end + + it "will be invoked by child class regardless of visibility" do + top = Class.new do + class << self + def inherited(cls); end + end + end + + class << top; private :inherited; end + lambda { Class.new(top) }.should_not raise_error + + class << top; protected :inherited; end + lambda { Class.new(top) }.should_not raise_error + end + +end + diff --git a/spec/rubyspec/core/class/initialize_spec.rb b/spec/rubyspec/core/class/initialize_spec.rb new file mode 100644 index 0000000000..d268596dfe --- /dev/null +++ b/spec/rubyspec/core/class/initialize_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Class#initialize" do + it "is private" do + Class.should have_private_method(:initialize) + end + + it "raises a TypeError when called on already initialized classes" do + lambda{ + Fixnum.send :initialize + }.should raise_error(TypeError) + + lambda{ + Object.send :initialize + }.should raise_error(TypeError) + end + + # See [redmine:2601] + it "raises a TypeError when called on BasicObject" do + lambda{ + BasicObject.send :initialize + }.should raise_error(TypeError) + end + + describe "when given the Class" do + before :each do + @uninitialized = Class.allocate + end + + it "raises a TypeError" do + lambda{@uninitialized.send(:initialize, Class)}.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/core/class/new_spec.rb b/spec/rubyspec/core/class/new_spec.rb new file mode 100644 index 0000000000..86323b1575 --- /dev/null +++ b/spec/rubyspec/core/class/new_spec.rb @@ -0,0 +1,154 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Class.new with a block given" do + it "yields the new class as self in the block" do + self_in_block = nil + klass = Class.new do + self_in_block = self + end + self_in_block.should equal klass + end + + it "uses the given block as the class' body" do + klass = Class.new do + def self.message + "text" + end + + def hello + "hello again" + end + end + + klass.message.should == "text" + klass.new.hello.should == "hello again" + end + + it "creates a subclass of the given superclass" do + sc = Class.new do + def self.body + @body + end + @body = self + def message; "text"; end + end + klass = Class.new(sc) do + def self.body + @body + end + @body = self + def message2; "hello"; end + end + + klass.body.should == klass + sc.body.should == sc + klass.superclass.should == sc + klass.new.message.should == "text" + klass.new.message2.should == "hello" + end + + it "runs the inherited hook after yielding the block" do + ScratchPad.record [] + klass = Class.new(CoreClassSpecs::Inherited::D) do + ScratchPad << self + end + + ScratchPad.recorded.should == [CoreClassSpecs::Inherited::D, klass] + end +end + +describe "Class.new" do + it "creates a new anonymous class" do + klass = Class.new + klass.is_a?(Class).should == true + + klass_instance = klass.new + klass_instance.is_a?(klass).should == true + end + + it "raises a TypeError if passed a metaclass" do + obj = mock("Class.new metaclass") + meta = obj.singleton_class + lambda { Class.new meta }.should raise_error(TypeError) + end + + it "creates a class without a name" do + Class.new.name.should be_nil + end + + it "creates a class that can be given a name by assigning it to a constant" do + ::MyClass = Class.new + ::MyClass.name.should == "MyClass" + a = Class.new + MyClass::NestedClass = a + MyClass::NestedClass.name.should == "MyClass::NestedClass" + end + + it "sets the new class' superclass to the given class" do + top = Class.new + Class.new(top).superclass.should == top + end + + it "sets the new class' superclass to Object when no class given" do + Class.new.superclass.should == Object + end + + it "raises a TypeError when given a non-Class" do + error_msg = /superclass must be a Class/ + lambda { Class.new("") }.should raise_error(TypeError, error_msg) + lambda { Class.new(1) }.should raise_error(TypeError, error_msg) + lambda { Class.new(:symbol) }.should raise_error(TypeError, error_msg) + lambda { Class.new(mock('o')) }.should raise_error(TypeError, error_msg) + lambda { Class.new(Module.new) }.should raise_error(TypeError, error_msg) + end +end + +describe "Class#new" do + it "returns a new instance of self" do + klass = Class.new + klass.new.is_a?(klass).should == true + end + + it "invokes #initialize on the new instance with the given args" do + klass = Class.new do + def initialize(*args) + @initialized = true + @args = args + end + + def args + @args + end + + def initialized? + @initialized || false + end + end + + klass.new.initialized?.should == true + klass.new(1, 2, 3).args.should == [1, 2, 3] + end + + it "uses the internal allocator and does not call #allocate" do + klass = Class.new do + def self.allocate + raise "allocate should not be called" + end + end + + instance = klass.new + instance.should be_kind_of klass + instance.class.should equal klass + end + + it "passes the block to #initialize" do + klass = Class.new do + def initialize + yield + end + end + + klass.new { break 42 }.should == 42 + end +end diff --git a/spec/rubyspec/core/class/superclass_spec.rb b/spec/rubyspec/core/class/superclass_spec.rb new file mode 100644 index 0000000000..18b7ce5bde --- /dev/null +++ b/spec/rubyspec/core/class/superclass_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Class#superclass" do + it "returns the superclass of self" do + BasicObject.superclass.should be_nil + Object.superclass.should == BasicObject + Class.superclass.should == Module + Class.new.superclass.should == Object + Class.new(String).superclass.should == String + Class.new(Fixnum).superclass.should == Fixnum + end + + # redmine:567 + describe "for a singleton class" do + it "of an object returns the class of the object" do + a = CoreClassSpecs::A.new + sc = class << a; self; end + sc.superclass.should == CoreClassSpecs::A + end + + it "of a class returns the singleton class of its superclass" do # sorry, can't find a simpler way to express this... + sc = class << CoreClassSpecs::H; self; end + sc.superclass.should == class << CoreClassSpecs::A; self; end + end + end +end diff --git a/spec/rubyspec/core/class/to_s_spec.rb b/spec/rubyspec/core/class/to_s_spec.rb new file mode 100644 index 0000000000..693517f0da --- /dev/null +++ b/spec/rubyspec/core/class/to_s_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Class#to_s" do + it 'regular class returns same name as Module#to_s' do + String.to_s.should == 'String' + end + + describe 'singleton class' do + it 'for modules includes module name' do + CoreClassSpecs.singleton_class.to_s.should == '#' + end + + it 'for classes includes class name' do + CoreClassSpecs::Record.singleton_class.to_s.should == '#' + end + + it 'for objects includes class name and object ID' do + obj = CoreClassSpecs::Record.new + obj.singleton_class.to_s.should =~ /#>/ + end + end +end diff --git a/spec/rubyspec/core/comparable/between_spec.rb b/spec/rubyspec/core/comparable/between_spec.rb new file mode 100644 index 0000000000..ebeadb7569 --- /dev/null +++ b/spec/rubyspec/core/comparable/between_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Comparable#between?" do + it "returns true if self is greater than or equal to the first and less than or equal to the second argument" do + a = ComparableSpecs::Weird.new(-1) + b = ComparableSpecs::Weird.new(0) + c = ComparableSpecs::Weird.new(1) + d = ComparableSpecs::Weird.new(2) + + a.between?(a, a).should == true + a.between?(a, b).should == true + a.between?(a, c).should == true + a.between?(a, d).should == true + c.between?(c, d).should == true + d.between?(d, d).should == true + c.between?(a, d).should == true + + a.between?(b, b).should == false + a.between?(b, c).should == false + a.between?(b, d).should == false + c.between?(a, a).should == false + c.between?(a, b).should == false + end +end diff --git a/spec/rubyspec/core/comparable/clamp_spec.rb b/spec/rubyspec/core/comparable/clamp_spec.rb new file mode 100644 index 0000000000..75868258b5 --- /dev/null +++ b/spec/rubyspec/core/comparable/clamp_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is '2.4' do + describe 'Comparable#clamp' do + it 'raises an Argument error unless given 2 parameters' do + c = ComparableSpecs::Weird.new(0) + lambda { c.clamp(c) }.should raise_error(ArgumentError) + lambda { c.clamp(c, c, c) }.should raise_error(ArgumentError) + end + + it 'raises an Argument error unless the 2 parameters are correctly ordered' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + c = ComparableSpecs::Weird.new(3) + + lambda { c.clamp(two, one) }.should raise_error(ArgumentError) + one.should_receive(:<=>).any_number_of_times.and_return(nil) + lambda { c.clamp(one, two) }.should raise_error(ArgumentError) + end + + it 'returns self if within the given parameters' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + three = ComparableSpecs::WithOnlyCompareDefined.new(3) + c = ComparableSpecs::Weird.new(2) + + c.clamp(one, two).should equal(c) + c.clamp(two, two).should equal(c) + c.clamp(one, three).should equal(c) + c.clamp(two, three).should equal(c) + end + + it 'returns the min parameter if smaller than it' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + c = ComparableSpecs::Weird.new(0) + + c.clamp(one, two).should equal(one) + end + + it 'returns the max parameter if greater than it' do + one = ComparableSpecs::WithOnlyCompareDefined.new(1) + two = ComparableSpecs::WithOnlyCompareDefined.new(2) + c = ComparableSpecs::Weird.new(3) + + c.clamp(one, two).should equal(two) + end + end +end diff --git a/spec/rubyspec/core/comparable/equal_value_spec.rb b/spec/rubyspec/core/comparable/equal_value_spec.rb new file mode 100644 index 0000000000..2bc22771b4 --- /dev/null +++ b/spec/rubyspec/core/comparable/equal_value_spec.rb @@ -0,0 +1,139 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Comparable#==" do + a = b = nil + before :each do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(10) + end + + it "returns true if other is the same as self" do + (a == a).should == true + (b == b).should == true + end + + it "calls #<=> on self with other and returns true if #<=> returns 0" do + a.should_receive(:<=>).once.and_return(0) + (a == b).should == true + end + + it "calls #<=> on self with other and returns true if #<=> returns 0.0" do + a.should_receive(:<=>).once.and_return(0.0) + (a == b).should == true + end + + it "returns false if calling #<=> on self returns a positive Integer" do + a.should_receive(:<=>).once.and_return(1) + (a == b).should == false + end + + it "returns false if calling #<=> on self returns a negative Integer" do + a.should_receive(:<=>).once.and_return(-1) + (a == b).should == false + end + + context "when #<=> returns nil" do + before :each do + a.should_receive(:<=>).once.and_return(nil) + end + + it "returns false" do + (a == b).should be_false + end + end + + context "when #<=> returns nor nil neither an Integer" do + before :each do + a.should_receive(:<=>).once.and_return("abc") + end + + ruby_version_is ""..."2.3" do + it "returns false" do + (a == b).should be_false + end + end + + ruby_version_is "2.3" do + it "raises an ArgumentError" do + lambda { (a == b) }.should raise_error(ArgumentError) + end + end + end + + context "when #<=> raises an exception" do + context "if it is a StandardError" do + before :each do + a.should_receive(:<=>).once.and_raise(StandardError) + end + + ruby_version_is ""..."2.3" do + # Behaviour confirmed by MRI test suite + it "returns false" do + (a == b).should be_false + end + end + + ruby_version_is "2.3" do + it "lets it go through" do + lambda { (a == b) }.should raise_error(StandardError) + end + end + end + + context "if it is a subclass of StandardError" do + # TypeError < StandardError + before :each do + a.should_receive(:<=>).once.and_raise(TypeError) + end + + ruby_version_is ""..."2.3" do + it "returns false" do + (a == b).should be_false + end + end + + ruby_version_is "2.3" do + it "lets it go through" do + lambda { (a == b) }.should raise_error(TypeError) + end + end + end + + it "lets it go through if it is not a StandardError" do + a.should_receive(:<=>).once.and_raise(Exception) + lambda { (a == b) }.should raise_error(Exception) + end + end + + context "when #<=> is not defined" do + before :each do + @a = ComparableSpecs::WithoutCompareDefined.new + @b = ComparableSpecs::WithoutCompareDefined.new + end + + it "returns true for identical objects" do + @a.should == @a + end + + it "returns false and does not recurse infinitely" do + @a.should_not == @b + end + end + + context "when #<=> calls super" do + before :each do + @a = ComparableSpecs::CompareCallingSuper.new + @b = ComparableSpecs::CompareCallingSuper.new + end + + it "returns true for identical objects" do + @a.should == @a + end + + it "calls the defined #<=> only once for different objects" do + @a.should_not == @b + @a.calls.should == 1 + end + end +end diff --git a/spec/rubyspec/core/comparable/fixtures/classes.rb b/spec/rubyspec/core/comparable/fixtures/classes.rb new file mode 100644 index 0000000000..4239a47d2f --- /dev/null +++ b/spec/rubyspec/core/comparable/fixtures/classes.rb @@ -0,0 +1,36 @@ +module ComparableSpecs + class WithOnlyCompareDefined + attr_reader :value + + def initialize(value) + @value = value + end + + def <=>(other) + self.value <=> other.value + end + end + + class Weird < WithOnlyCompareDefined + include Comparable + end + + class WithoutCompareDefined + include Comparable + end + + class CompareCallingSuper + include Comparable + + attr_reader :calls + + def initialize + @calls = 0 + end + + def <=>(other) + @calls += 1 + super(other) + end + end +end diff --git a/spec/rubyspec/core/comparable/gt_spec.rb b/spec/rubyspec/core/comparable/gt_spec.rb new file mode 100644 index 0000000000..c4739bcf2a --- /dev/null +++ b/spec/rubyspec/core/comparable/gt_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Comparable#>" do + it "calls #<=> on self with other and returns true if #<=> returns any Integer greater than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(1) + (a > b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(0.1) + (a > b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(10000000) + (a > b).should == true + end + + it "returns false if calling #<=> on self returns 0 or any Integer less than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(10) + + a.should_receive(:<=>).any_number_of_times.and_return(0) + (a > b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(0.0) + (a > b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(-1.0) + (a > b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(-10000000) + (a > b).should == false + end + + it "raises an ArgumentError if calling #<=> on self returns nil" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(nil) + lambda { (a > b) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/comparable/gte_spec.rb b/spec/rubyspec/core/comparable/gte_spec.rb new file mode 100644 index 0000000000..c9d8264ee6 --- /dev/null +++ b/spec/rubyspec/core/comparable/gte_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Comparable#>=" do + it "calls #<=> on self with other and returns true if #<=> returns 0 or any Integer greater than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(0) + (a >= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(0.0) + (a >= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(1) + (a >= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(0.1) + (a >= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(10000000) + (a >= b).should == true + end + + it "returns false if calling #<=> on self returns any Integer less than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(10) + + + a.should_receive(:<=>).any_number_of_times.and_return(-0.1) + (a >= b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(-1.0) + (a >= b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(-10000000) + (a >= b).should == false + end + + it "raises an ArgumentError if calling #<=> on self returns nil" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(nil) + lambda { (a >= b) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/comparable/lt_spec.rb b/spec/rubyspec/core/comparable/lt_spec.rb new file mode 100644 index 0000000000..e9e76360b9 --- /dev/null +++ b/spec/rubyspec/core/comparable/lt_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Comparable#<" do + it "calls #<=> on self with other and returns true if #<=> returns any Integer less than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(-1) + (a < b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(-0.1) + (a < b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(-10000000) + (a < b).should == true + end + + it "returns false if calling #<=> on self returns 0 or any Integer greater than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(10) + + a.should_receive(:<=>).any_number_of_times.and_return(0) + (a < b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(0.0) + (a < b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(1.0) + (a < b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(10000000) + (a < b).should == false + end + + it "raises an ArgumentError if calling #<=> on self returns nil" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(nil) + lambda { (a < b) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/comparable/lte_spec.rb b/spec/rubyspec/core/comparable/lte_spec.rb new file mode 100644 index 0000000000..96ed38ecd5 --- /dev/null +++ b/spec/rubyspec/core/comparable/lte_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Comparable#<=" do + it "calls #<=> on self with other and returns true if #<=> returns 0 or any Integer less than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(0) + (a <= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(0.0) + (a <= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(-1) + (a <= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(-0.1) + (a <= b).should == true + + a.should_receive(:<=>).any_number_of_times.and_return(-10000000) + (a <= b).should == true + end + + it "returns false if calling #<=> on self returns any Integer greater than 0" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(10) + + a.should_receive(:<=>).any_number_of_times.and_return(0.1) + (a <= b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(1.0) + (a <= b).should == false + + a.should_receive(:<=>).any_number_of_times.and_return(10000000) + (a <= b).should == false + end + + it "raises an ArgumentError if calling #<=> on self returns nil" do + a = ComparableSpecs::Weird.new(0) + b = ComparableSpecs::Weird.new(20) + + a.should_receive(:<=>).any_number_of_times.and_return(nil) + lambda { (a <= b) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/complex/abs2_spec.rb b/spec/rubyspec/core/complex/abs2_spec.rb new file mode 100644 index 0000000000..debfade075 --- /dev/null +++ b/spec/rubyspec/core/complex/abs2_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/abs2', __FILE__) + +describe "Complex#abs2" do + it_behaves_like(:complex_abs2, :abs2) +end diff --git a/spec/rubyspec/core/complex/abs_spec.rb b/spec/rubyspec/core/complex/abs_spec.rb new file mode 100644 index 0000000000..a00d161ee9 --- /dev/null +++ b/spec/rubyspec/core/complex/abs_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/abs', __FILE__) + +describe "Complex#abs" do + it_behaves_like(:complex_abs, :abs) +end diff --git a/spec/rubyspec/core/complex/angle_spec.rb b/spec/rubyspec/core/complex/angle_spec.rb new file mode 100644 index 0000000000..f0c46bfd03 --- /dev/null +++ b/spec/rubyspec/core/complex/angle_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require File.expand_path('../../../shared/complex/arg', __FILE__) + +describe "Complex#angle" do + it_behaves_like(:complex_arg, :angle) +end diff --git a/spec/rubyspec/core/complex/arg_spec.rb b/spec/rubyspec/core/complex/arg_spec.rb new file mode 100644 index 0000000000..48f8a94cf5 --- /dev/null +++ b/spec/rubyspec/core/complex/arg_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require File.expand_path('../../../shared/complex/arg', __FILE__) + +describe "Complex#arg" do + it_behaves_like(:complex_arg, :arg) +end diff --git a/spec/rubyspec/core/complex/coerce_spec.rb b/spec/rubyspec/core/complex/coerce_spec.rb new file mode 100644 index 0000000000..7c01170fde --- /dev/null +++ b/spec/rubyspec/core/complex/coerce_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/coerce', __FILE__) + +describe "Complex#coerce" do + it_behaves_like(:complex_coerce, :coerce) +end diff --git a/spec/rubyspec/core/complex/conj_spec.rb b/spec/rubyspec/core/complex/conj_spec.rb new file mode 100644 index 0000000000..ad2c885b3b --- /dev/null +++ b/spec/rubyspec/core/complex/conj_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/conjugate', __FILE__) + +describe "Complex#conj" do + it_behaves_like(:complex_conjugate, :conj) +end diff --git a/spec/rubyspec/core/complex/conjugate_spec.rb b/spec/rubyspec/core/complex/conjugate_spec.rb new file mode 100644 index 0000000000..7fc2ddb430 --- /dev/null +++ b/spec/rubyspec/core/complex/conjugate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/conjugate', __FILE__) + +describe "Complex#conjugate" do + it_behaves_like(:complex_conjugate, :conjugate) +end diff --git a/spec/rubyspec/core/complex/constants_spec.rb b/spec/rubyspec/core/complex/constants_spec.rb new file mode 100644 index 0000000000..a8fcebbd31 --- /dev/null +++ b/spec/rubyspec/core/complex/constants_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/constants', __FILE__) + +describe "Complex::I" do + it_behaves_like :complex_I, :I +end diff --git a/spec/rubyspec/core/complex/denominator_spec.rb b/spec/rubyspec/core/complex/denominator_spec.rb new file mode 100644 index 0000000000..2568967968 --- /dev/null +++ b/spec/rubyspec/core/complex/denominator_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/denominator', __FILE__) + +describe "Complex#denominator" do + it_behaves_like(:complex_denominator, :denominator) +end diff --git a/spec/rubyspec/core/complex/divide_spec.rb b/spec/rubyspec/core/complex/divide_spec.rb new file mode 100644 index 0000000000..71614c76e1 --- /dev/null +++ b/spec/rubyspec/core/complex/divide_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/divide', __FILE__) + +describe "Complex#/" do + it_behaves_like :complex_divide, :/ +end diff --git a/spec/rubyspec/core/complex/eql_spec.rb b/spec/rubyspec/core/complex/eql_spec.rb new file mode 100644 index 0000000000..c8e432029f --- /dev/null +++ b/spec/rubyspec/core/complex/eql_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Complex#eql?" do + it "returns false if other is not Complex" do + Complex(1).eql?(1).should be_false + end + + it "returns true when the respective parts are of the same classes and self == other" do + Complex(1, 2).eql?(Complex(1, 2)).should be_true + end + + it "returns false when the real parts are of different classes" do + Complex(1).eql?(Complex(1.0)).should be_false + end + + it "returns false when the imaginary parts are of different classes" do + Complex(1, 2).eql?(Complex(1, 2.0)).should be_false + end + + it "returns false when self == other is false" do + Complex(1, 2).eql?(Complex(2, 3)).should be_false + end + + it "does NOT send #eql? to real or imaginary parts" do + real = mock_numeric('real') + imag = mock_numeric('imag') + real.should_not_receive(:eql?) + imag.should_not_receive(:eql?) + Complex(real, imag).eql?(Complex(real, imag)).should be_true + end +end diff --git a/spec/rubyspec/core/complex/equal_value_spec.rb b/spec/rubyspec/core/complex/equal_value_spec.rb new file mode 100644 index 0000000000..b3d93911bd --- /dev/null +++ b/spec/rubyspec/core/complex/equal_value_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/equal_value', __FILE__) + +describe "Complex#==" do + it_behaves_like :complex_equal_value, :== +end diff --git a/spec/rubyspec/core/complex/exponent_spec.rb b/spec/rubyspec/core/complex/exponent_spec.rb new file mode 100644 index 0000000000..62f61a2bf3 --- /dev/null +++ b/spec/rubyspec/core/complex/exponent_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/exponent', __FILE__) + +describe "Complex#**" do + it_behaves_like :complex_exponent, :** +end diff --git a/spec/rubyspec/core/complex/fdiv_spec.rb b/spec/rubyspec/core/complex/fdiv_spec.rb new file mode 100644 index 0000000000..8211dfc9de --- /dev/null +++ b/spec/rubyspec/core/complex/fdiv_spec.rb @@ -0,0 +1,129 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Complex#fdiv" do + it "accepts a numeric argument" do + lambda { Complex(20).fdiv(2) }.should_not raise_error(TypeError) + lambda { Complex(20).fdiv(2.0) }.should_not raise_error(TypeError) + lambda { Complex(20).fdiv(bignum_value) }.should_not raise_error(TypeError) + end + + it "accepts a negative numeric argument" do + lambda { Complex(20).fdiv(-2) }.should_not raise_error(TypeError) + lambda { Complex(20).fdiv(-2.0) }.should_not raise_error(TypeError) + lambda { Complex(20).fdiv(-bignum_value) }.should_not raise_error(TypeError) + end + + it "raises a TypeError if passed a non-numeric argument" do + lambda { Complex(20).fdiv([]) }.should raise_error(TypeError) + lambda { Complex(20).fdiv(:sym) }.should raise_error(TypeError) + lambda { Complex(20).fdiv('s') }.should raise_error(TypeError) + end + + it "sets the real part to NaN if self's real part is NaN" do + Complex(nan_value).fdiv(2).real.nan?.should be_true + end + + it "sets the imaginary part to NaN if self's imaginary part is NaN" do + Complex(2, nan_value).fdiv(2).imag.nan?.should be_true + end + + it "sets the real and imaginary part to NaN if self's real and imaginary parts are NaN" do + Complex(nan_value, nan_value).fdiv(2).imag.nan?.should be_true + Complex(nan_value, nan_value).fdiv(2).real.nan?.should be_true + end + + it "sets the real and imaginary part to NaN if self's real part and the argument are both NaN" do + Complex(nan_value, 2).fdiv(nan_value).imag.nan?.should be_true + Complex(nan_value, 2).fdiv(nan_value).real.nan?.should be_true + end + + it "sets the real and imaginary part to NaN if self's real part, self's imaginary part, and the argument are NaN" do + Complex(nan_value, nan_value).fdiv(nan_value).imag.nan?.should be_true + Complex(nan_value, nan_value).fdiv(nan_value).real.nan?.should be_true + end + + it "sets the real part to Infinity if self's real part is Infinity" do + Complex(infinity_value).fdiv(2).real.infinite?.should == 1 + Complex(infinity_value,2).fdiv(2).real.infinite?.should == 1 + end + + it "sets the imaginary part to Infinity if self's imaginary part is Infinity" do + Complex(2, infinity_value).fdiv(2).imag.infinite?.should == 1 + Complex(2, infinity_value).fdiv(2).imag.infinite?.should == 1 + end + + it "sets the imaginary and real part to Infinity if self's imaginary and real parts are Infinity" do + Complex(infinity_value, infinity_value).fdiv(2).real.infinite?.should == 1 + Complex(infinity_value, infinity_value).fdiv(2).imag.infinite?.should == 1 + end + + it "sets the real part to NaN and the imaginary part to NaN if self's imaginary part, self's real part, and the argument are Infinity" do + Complex(infinity_value, infinity_value).fdiv(infinity_value).real.nan?.should be_true + Complex(infinity_value, infinity_value).fdiv(infinity_value).imag.nan?.should be_true + end +end + +describe "Complex#fdiv with no imaginary part" do + before :each do + @numbers = [1, 5.43, 10, bignum_value, 99872.2918710].map{|n| [n,-n]}.flatten + end + + it "returns a Complex number" do + @numbers.each do |real| + @numbers.each do |other| + Complex(real).fdiv(other).should be_an_instance_of(Complex) + end + end + end + + it "sets the real part to self's real part fdiv'd with the argument" do + @numbers.each do |real| + @numbers.each do |other| + Complex(real).fdiv(other).real.should == real.fdiv(other) + end + end + end + + it "sets the imaginary part to 0.0" do + @numbers.each do |real| + @numbers.each do |other| + Complex(real).fdiv(other).imaginary.should == 0.0 + end + end + end +end + +describe "Complex#fdiv with an imaginary part" do + before :each do + @numbers = [1, 5.43, 10, bignum_value, 99872.2918710].map{|n| [n,-n]}.flatten + end + + it "returns a Complex number" do + @numbers.each do |real| + @numbers.each_with_index do |other,idx| + Complex( + real,@numbers[idx == 0 ? -1 : idx-1] + ).fdiv(other).should be_an_instance_of(Complex) + end + end + end + + it "sets the real part to self's real part fdiv'd with the argument" do + @numbers.each do |real| + @numbers.each_with_index do |other,idx| + Complex( + real,@numbers[idx == 0 ? -1 : idx-1] + ).fdiv(other).real.should == real.fdiv(other) + end + end + end + + it "sets the imaginary part to the imaginary part fdiv'd with the argument" do + @numbers.each do |real| + @numbers.each_with_index do |other,idx| + im = @numbers[idx == 0 ? -1 : idx-1] + Complex(real, im).fdiv(other).imag.should == im.fdiv(other) + end + end + end +end diff --git a/spec/rubyspec/core/complex/hash_spec.rb b/spec/rubyspec/core/complex/hash_spec.rb new file mode 100644 index 0000000000..db4b3590df --- /dev/null +++ b/spec/rubyspec/core/complex/hash_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/hash', __FILE__) + +describe "Complex#hash" do + it_behaves_like(:complex_hash, :hash) +end diff --git a/spec/rubyspec/core/complex/imag_spec.rb b/spec/rubyspec/core/complex/imag_spec.rb new file mode 100644 index 0000000000..6aa8803f5d --- /dev/null +++ b/spec/rubyspec/core/complex/imag_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/image', __FILE__) + +describe "Complex#imag" do + it_behaves_like(:complex_image, :imag) +end diff --git a/spec/rubyspec/core/complex/imaginary_spec.rb b/spec/rubyspec/core/complex/imaginary_spec.rb new file mode 100644 index 0000000000..ecae19283b --- /dev/null +++ b/spec/rubyspec/core/complex/imaginary_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/image', __FILE__) + +describe "Complex#imaginary" do + it_behaves_like :complex_image, :imaginary +end diff --git a/spec/rubyspec/core/complex/inspect_spec.rb b/spec/rubyspec/core/complex/inspect_spec.rb new file mode 100644 index 0000000000..b766e7f730 --- /dev/null +++ b/spec/rubyspec/core/complex/inspect_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/inspect', __FILE__) + +describe "Complex#inspect" do + it_behaves_like(:complex_inspect, :inspect) +end diff --git a/spec/rubyspec/core/complex/integer_spec.rb b/spec/rubyspec/core/complex/integer_spec.rb new file mode 100644 index 0000000000..54b420e62c --- /dev/null +++ b/spec/rubyspec/core/complex/integer_spec.rb @@ -0,0 +1,9 @@ +describe "Complex#integer?" do + it "returns false for a Complex with no imaginary part" do + Complex(20).integer?.should be_false + end + + it "returns false for a Complex with an imaginary part" do + Complex(20,3).integer?.should be_false + end +end diff --git a/spec/rubyspec/core/complex/magnitude_spec.rb b/spec/rubyspec/core/complex/magnitude_spec.rb new file mode 100644 index 0000000000..e9175d763e --- /dev/null +++ b/spec/rubyspec/core/complex/magnitude_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/abs', __FILE__) + +describe "Complex#magnitude" do + it_behaves_like(:complex_abs, :magnitude) +end diff --git a/spec/rubyspec/core/complex/marshal_dump_spec.rb b/spec/rubyspec/core/complex/marshal_dump_spec.rb new file mode 100644 index 0000000000..8d37929f54 --- /dev/null +++ b/spec/rubyspec/core/complex/marshal_dump_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Complex#marshal_dump" do + it "is a private method" do + Complex.should have_private_instance_method(:marshal_dump, false) + end + + it "dumps real and imaginary parts" do + Complex(1, 2).send(:marshal_dump).should == [1, 2] + end +end diff --git a/spec/rubyspec/core/complex/minus_spec.rb b/spec/rubyspec/core/complex/minus_spec.rb new file mode 100644 index 0000000000..2b7b8bb270 --- /dev/null +++ b/spec/rubyspec/core/complex/minus_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/minus', __FILE__) + +describe "Complex#-" do + it_behaves_like :complex_minus, :- +end diff --git a/spec/rubyspec/core/complex/multiply_spec.rb b/spec/rubyspec/core/complex/multiply_spec.rb new file mode 100644 index 0000000000..7f600fc1ab --- /dev/null +++ b/spec/rubyspec/core/complex/multiply_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/multiply', __FILE__) + +describe "Complex#*" do + it_behaves_like :complex_multiply, :* +end diff --git a/spec/rubyspec/core/complex/negative_spec.rb b/spec/rubyspec/core/complex/negative_spec.rb new file mode 100644 index 0000000000..2016968efd --- /dev/null +++ b/spec/rubyspec/core/complex/negative_spec.rb @@ -0,0 +1,11 @@ +describe "Complex#negative?" do + it "is undefined" do + c = Complex(1) + + c.methods.should_not include(:negative?) + + lambda { + c.negative? + }.should raise_error(NoMethodError) + end +end \ No newline at end of file diff --git a/spec/rubyspec/core/complex/numerator_spec.rb b/spec/rubyspec/core/complex/numerator_spec.rb new file mode 100644 index 0000000000..8c0e8761bd --- /dev/null +++ b/spec/rubyspec/core/complex/numerator_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/numerator', __FILE__) + +describe "Complex#numerator" do + it_behaves_like(:complex_numerator, :numerator) +end diff --git a/spec/rubyspec/core/complex/phase_spec.rb b/spec/rubyspec/core/complex/phase_spec.rb new file mode 100644 index 0000000000..c17f922c7f --- /dev/null +++ b/spec/rubyspec/core/complex/phase_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/arg', __FILE__) + +describe "Complex#phase" do + it_behaves_like :complex_arg, :phase +end diff --git a/spec/rubyspec/core/complex/plus_spec.rb b/spec/rubyspec/core/complex/plus_spec.rb new file mode 100644 index 0000000000..076582681f --- /dev/null +++ b/spec/rubyspec/core/complex/plus_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/plus', __FILE__) + +describe "Complex#+" do + it_behaves_like :complex_plus, :+ +end diff --git a/spec/rubyspec/core/complex/polar_spec.rb b/spec/rubyspec/core/complex/polar_spec.rb new file mode 100644 index 0000000000..d847e916ff --- /dev/null +++ b/spec/rubyspec/core/complex/polar_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../shared/complex/polar', __FILE__) + +describe "Complex.polar" do + it_behaves_like(:complex_polar_class, :polar) + + it "raises a TypeError when given non real arguments" do + lambda{ Complex.polar(nil) }.should raise_error(TypeError) + lambda{ Complex.polar(nil, nil) }.should raise_error(TypeError) + end +end + +describe "Complex#polar" do + it_behaves_like(:complex_polar, :polar) +end diff --git a/spec/rubyspec/core/complex/positive_spec.rb b/spec/rubyspec/core/complex/positive_spec.rb new file mode 100644 index 0000000000..3171cbe9b2 --- /dev/null +++ b/spec/rubyspec/core/complex/positive_spec.rb @@ -0,0 +1,11 @@ +describe "Complex#positive?" do + it "is undefined" do + c = Complex(1) + + c.methods.should_not include(:positive?) + + lambda { + c.positive? + }.should raise_error(NoMethodError) + end +end \ No newline at end of file diff --git a/spec/rubyspec/core/complex/quo_spec.rb b/spec/rubyspec/core/complex/quo_spec.rb new file mode 100644 index 0000000000..cb3f1203b4 --- /dev/null +++ b/spec/rubyspec/core/complex/quo_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/divide', __FILE__) + +describe "Complex#quo" do + it_behaves_like :complex_divide, :quo +end diff --git a/spec/rubyspec/core/complex/rationalize_spec.rb b/spec/rubyspec/core/complex/rationalize_spec.rb new file mode 100644 index 0000000000..2dd1e9b122 --- /dev/null +++ b/spec/rubyspec/core/complex/rationalize_spec.rb @@ -0,0 +1,29 @@ +describe "Complex#rationalize" do + it "raises RangeError if self has non-zero imaginary part" do + lambda { Complex(1,5).rationalize }.should raise_error(RangeError) + end + + it "raises RangeError if self has 0.0 imaginary part" do + lambda { Complex(1,0.0).rationalize }.should raise_error(RangeError) + end + + it "returns a Rational if self has zero imaginary part" do + Complex(1,0).rationalize.should == Rational(1,1) + Complex(2<<63+5).rationalize.should == Rational(2<<63+5,1) + end + + it "sends #rationalize to the real part" do + real = mock_numeric('real') + real.should_receive(:rationalize).with(0.1).and_return(:result) + Complex(real, 0).rationalize(0.1).should == :result + end + + it "ignores a single argument" do + Complex(1,0).rationalize(0.1).should == Rational(1,1) + end + + it "raises ArgumentError when passed more than one argument" do + lambda { Complex(1,0).rationalize(0.1, 0.1) }.should raise_error(ArgumentError) + lambda { Complex(1,0).rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/complex/real_spec.rb b/spec/rubyspec/core/complex/real_spec.rb new file mode 100644 index 0000000000..1293e02d3c --- /dev/null +++ b/spec/rubyspec/core/complex/real_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../shared/complex/real', __FILE__) + +describe "Complex#real" do + it_behaves_like(:complex_real, :real) +end + +describe "Complex#real?" do + it "returns false if there is an imaginary part" do + Complex(2,3).real?.should be_false + end + + it "returns false if there is not an imaginary part" do + Complex(2).real?.should be_false + end + + it "returns false if the real part is Infinity" do + Complex(infinity_value).real?.should be_false + end + + it "returns false if the real part is NaN" do + Complex(nan_value).real?.should be_false + end +end diff --git a/spec/rubyspec/core/complex/rect_spec.rb b/spec/rubyspec/core/complex/rect_spec.rb new file mode 100644 index 0000000000..cf2ff9e83b --- /dev/null +++ b/spec/rubyspec/core/complex/rect_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../shared/complex/rect', __FILE__) + +describe "Complex#rect" do + it_behaves_like(:complex_rect, :rect) +end + +describe "Complex.rect" do + it_behaves_like(:complex_rect_class, :rect) +end diff --git a/spec/rubyspec/core/complex/rectangular_spec.rb b/spec/rubyspec/core/complex/rectangular_spec.rb new file mode 100644 index 0000000000..0eb29c3500 --- /dev/null +++ b/spec/rubyspec/core/complex/rectangular_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../shared/complex/rect', __FILE__) + +describe "Complex#rectangular" do + it_behaves_like(:complex_rect, :rectangular) +end + +describe "Complex.rectangular" do + it_behaves_like(:complex_rect_class, :rectangular) +end diff --git a/spec/rubyspec/core/complex/to_f_spec.rb b/spec/rubyspec/core/complex/to_f_spec.rb new file mode 100644 index 0000000000..33342e61cc --- /dev/null +++ b/spec/rubyspec/core/complex/to_f_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Complex#to_f" do + describe "when the imaginary part is Fixnum 0" do + it "returns the result of sending #to_f to the real part" do + real = mock_numeric('real') + real.should_receive(:to_f).and_return(:f) + Complex(real, 0).to_f.should == :f + end + end + + describe "when the imaginary part is Rational 0" do + it "returns the result of sending #to_f to the real part" do + real = mock_numeric('real') + real.should_receive(:to_f).and_return(:f) + Complex(real, Rational(0)).to_f.should == :f + end + end + + describe "when the imaginary part responds to #== 0 with true" do + it "returns the result of sending #to_f to the real part" do + real = mock_numeric('real') + real.should_receive(:to_f).and_return(:f) + imag = mock_numeric('imag') + imag.should_receive(:==).with(0).any_number_of_times.and_return(true) + Complex(real, imag).to_f.should == :f + end + end + + describe "when the imaginary part is non-zero" do + it "raises RangeError" do + lambda { Complex(0, 1).to_f }.should raise_error(RangeError) + end + end + + describe "when the imaginary part is Float 0.0" do + it "raises RangeError" do + lambda { Complex(0, 0.0).to_f }.should raise_error(RangeError) + end + end +end diff --git a/spec/rubyspec/core/complex/to_i_spec.rb b/spec/rubyspec/core/complex/to_i_spec.rb new file mode 100644 index 0000000000..ea8b199b2e --- /dev/null +++ b/spec/rubyspec/core/complex/to_i_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Complex#to_i" do + describe "when the imaginary part is Fixnum 0" do + it "returns the result of sending #to_i to the real part" do + real = mock_numeric('real') + real.should_receive(:to_i).and_return(:i) + Complex(real, 0).to_i.should == :i + end + end + + describe "when the imaginary part is Rational 0" do + it "returns the result of sending #to_i to the real part" do + real = mock_numeric('real') + real.should_receive(:to_i).and_return(:i) + Complex(real, Rational(0)).to_i.should == :i + end + end + + describe "when the imaginary part responds to #== 0 with true" do + it "returns the result of sending #to_i to the real part" do + real = mock_numeric('real') + real.should_receive(:to_i).and_return(:i) + imag = mock_numeric('imag') + imag.should_receive(:==).with(0).any_number_of_times.and_return(true) + Complex(real, imag).to_i.should == :i + end + end + + describe "when the imaginary part is non-zero" do + it "raises RangeError" do + lambda { Complex(0, 1).to_i }.should raise_error(RangeError) + end + end + + describe "when the imaginary part is Float 0.0" do + it "raises RangeError" do + lambda { Complex(0, 0.0).to_i }.should raise_error(RangeError) + end + end +end diff --git a/spec/rubyspec/core/complex/to_r_spec.rb b/spec/rubyspec/core/complex/to_r_spec.rb new file mode 100644 index 0000000000..92fcdd3862 --- /dev/null +++ b/spec/rubyspec/core/complex/to_r_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Complex#to_r" do + describe "when the imaginary part is Fixnum 0" do + it "returns the result of sending #to_r to the real part" do + real = mock_numeric('real') + real.should_receive(:to_r).and_return(:r) + Complex(real, 0).to_r.should == :r + end + end + + describe "when the imaginary part is Rational 0" do + it "returns the result of sending #to_r to the real part" do + real = mock_numeric('real') + real.should_receive(:to_r).and_return(:r) + Complex(real, Rational(0)).to_r.should == :r + end + end + + describe "when the imaginary part responds to #== 0 with true" do + it "returns the result of sending #to_r to the real part" do + real = mock_numeric('real') + real.should_receive(:to_r).and_return(:r) + imag = mock_numeric('imag') + imag.should_receive(:==).with(0).any_number_of_times.and_return(true) + Complex(real, imag).to_r.should == :r + end + end + + describe "when the imaginary part is non-zero" do + it "raises RangeError" do + lambda { Complex(0, 1).to_r }.should raise_error(RangeError) + end + end + + describe "when the imaginary part is Float 0.0" do + it "raises RangeError" do + lambda { Complex(0, 0.0).to_r }.should raise_error(RangeError) + end + end +end diff --git a/spec/rubyspec/core/complex/to_s_spec.rb b/spec/rubyspec/core/complex/to_s_spec.rb new file mode 100644 index 0000000000..c398bb000e --- /dev/null +++ b/spec/rubyspec/core/complex/to_s_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/to_s', __FILE__) + +describe "Complex#to_s" do + it_behaves_like(:complex_to_s, :to_s) +end diff --git a/spec/rubyspec/core/complex/uminus_spec.rb b/spec/rubyspec/core/complex/uminus_spec.rb new file mode 100644 index 0000000000..1bf56e770b --- /dev/null +++ b/spec/rubyspec/core/complex/uminus_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Complex#-@" do + it "sends #-@ to the real and imaginary parts and returns a Complex with the resulting respective parts" do + real = mock_numeric('real') + imag = mock_numeric('imag') + real.should_receive(:-@).and_return(-1) + imag.should_receive(:-@).and_return(-2) + Complex(real, imag).send(:-@).should == Complex(-1, -2) + end +end diff --git a/spec/rubyspec/core/dir/chdir_spec.rb b/spec/rubyspec/core/dir/chdir_spec.rb new file mode 100644 index 0000000000..f5b0b80d1c --- /dev/null +++ b/spec/rubyspec/core/dir/chdir_spec.rb @@ -0,0 +1,124 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Dir.chdir" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @original = Dir.pwd + end + + after :each do + Dir.chdir(@original) + end + + it "defaults to $HOME with no arguments" do + if ENV['HOME'] + Dir.chdir + current_dir = Dir.pwd + + Dir.chdir(ENV['HOME']) + home = Dir.pwd + current_dir.should == home + end + end + + it "changes to the specified directory" do + Dir.chdir DirSpecs.mock_dir + Dir.pwd.should == DirSpecs.mock_dir + end + + it "returns 0 when successfully changing directory" do + Dir.chdir(@original).should == 0 + end + + it "calls #to_str on the argument if it's not a String" do + obj = mock('path') + obj.should_receive(:to_str).and_return(Dir.pwd) + Dir.chdir(obj) + end + + it "calls #to_str on the argument if it's not a String and a block is given" do + obj = mock('path') + obj.should_receive(:to_str).and_return(Dir.pwd) + Dir.chdir(obj) { } + end + + it "calls #to_path on the argument if it's not a String" do + obj = mock('path') + obj.should_receive(:to_path).and_return(Dir.pwd) + Dir.chdir(obj) + end + + it "prefers #to_path over #to_str" do + obj = Class.new do + def to_path; Dir.pwd; end + def to_str; DirSpecs.mock_dir; end + end + Dir.chdir(obj.new) + Dir.pwd.should == @original + end + + it "returns the value of the block when a block is given" do + Dir.chdir(@original) { :block_value }.should == :block_value + end + + it "defaults to the home directory when given a block but no argument" do + # Windows will return a path with forward slashes for ENV["HOME"] so we have + # to compare the route representations returned by Dir.chdir. + current_dir = "" + Dir.chdir { current_dir = Dir.pwd } + + Dir.chdir(ENV['HOME']) + home = Dir.pwd + current_dir.should == home + end + + it "changes to the specified directory for the duration of the block" do + ar = Dir.chdir(DirSpecs.mock_dir) { |dir| [dir, Dir.pwd] } + ar.should == [DirSpecs.mock_dir, DirSpecs.mock_dir] + + Dir.pwd.should == @original + end + + it "raises an Errno::ENOENT if the directory does not exist" do + lambda { Dir.chdir DirSpecs.nonexistent }.should raise_error(Errno::ENOENT) + lambda { Dir.chdir(DirSpecs.nonexistent) { } }.should raise_error(Errno::ENOENT) + end + + it "raises an Errno::ENOENT if the original directory no longer exists" do + dir1 = tmp('/testdir1') + dir2 = tmp('/testdir2') + File.exist?(dir1).should == false + File.exist?(dir2).should == false + Dir.mkdir dir1 + Dir.mkdir dir2 + begin + lambda { + Dir.chdir dir1 do + Dir.chdir(dir2) { Dir.unlink dir1 } + end + }.should raise_error(Errno::ENOENT) + ensure + Dir.unlink dir1 if File.exist?(dir1) + Dir.unlink dir2 if File.exist?(dir2) + end + end + + it "always returns to the original directory when given a block" do + begin + Dir.chdir(DirSpecs.mock_dir) do + raise StandardError, "something bad happened" + end + rescue StandardError + end + + Dir.pwd.should == @original + end +end diff --git a/spec/rubyspec/core/dir/chroot_spec.rb b/spec/rubyspec/core/dir/chroot_spec.rb new file mode 100644 index 0000000000..1afe254957 --- /dev/null +++ b/spec/rubyspec/core/dir/chroot_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/chroot', __FILE__) + +platform_is_not :windows do + as_superuser do + describe "Dir.chroot as root" do + it_behaves_like :dir_chroot_as_root, :chroot + end + end + + platform_is_not :cygwin do + as_user do + describe "Dir.chroot as regular user" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it "raises an Errno::EPERM exception if the directory exists" do + lambda { Dir.chroot('.') }.should raise_error(Errno::EPERM) + end + + it "raises a SystemCallError if the directory doesn't exist" do + lambda { Dir.chroot('xgwhwhsjai2222jg') }.should raise_error(SystemCallError) + end + + it "calls #to_path on non-String argument" do + p = mock('path') + p.should_receive(:to_path).and_return('.') + lambda { Dir.chroot(p) }.should raise_error + end + end + end + end + + platform_is :cygwin do + as_user do + describe "Dir.chroot as regular user" do + it_behaves_like :dir_chroot_as_root, :chroot + end + end + end +end diff --git a/spec/rubyspec/core/dir/close_spec.rb b/spec/rubyspec/core/dir/close_spec.rb new file mode 100644 index 0000000000..7b08ec5ee8 --- /dev/null +++ b/spec/rubyspec/core/dir/close_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +ruby_version_is ''...'2.3' do + require File.expand_path('../shared/closed', __FILE__) +end + +describe "Dir#close" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + ruby_version_is ''...'2.3' do + it_behaves_like :dir_closed, :close + end + + ruby_version_is '2.3' do + it "does not raise an IOError even if the Dir instance is closed" do + dir = Dir.open DirSpecs.mock_dir + dir.close + lambda { + dir.close + }.should_not raise_error(IOError) + end + end +end diff --git a/spec/rubyspec/core/dir/delete_spec.rb b/spec/rubyspec/core/dir/delete_spec.rb new file mode 100644 index 0000000000..5f36956839 --- /dev/null +++ b/spec/rubyspec/core/dir/delete_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/delete', __FILE__) + +describe "Dir.delete" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_delete, :delete +end diff --git a/spec/rubyspec/core/dir/dir_spec.rb b/spec/rubyspec/core/dir/dir_spec.rb new file mode 100644 index 0000000000..4923445bed --- /dev/null +++ b/spec/rubyspec/core/dir/dir_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Dir" do + it "includes Enumerable" do + Dir.include?(Enumerable).should == true + end +end diff --git a/spec/rubyspec/core/dir/each_spec.rb b/spec/rubyspec/core/dir/each_spec.rb new file mode 100644 index 0000000000..534691ff58 --- /dev/null +++ b/spec/rubyspec/core/dir/each_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/closed', __FILE__) + +describe "Dir#each" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @dir = Dir.open DirSpecs.mock_dir + end + + after :each do + @dir.close + end + + it "yields each directory entry in succession" do + a = [] + @dir.each {|dir| a << dir} + + a.sort.should == DirSpecs.expected_paths + end + + it "returns the directory which remains open" do + # an FS does not necessarily impose order + ls = Dir.entries(DirSpecs.mock_dir) + @dir.each {}.should == @dir + @dir.read.should == nil + @dir.rewind + ls.should include(@dir.read) + end + + describe "when no block is given" do + it "returns an Enumerator" do + @dir.each.should be_an_instance_of(Enumerator) + @dir.each.to_a.sort.should == DirSpecs.expected_paths + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @dir.each.size.should == nil + end + end + end + end +end + +describe "Dir#each" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_closed, :each +end diff --git a/spec/rubyspec/core/dir/element_reference_spec.rb b/spec/rubyspec/core/dir/element_reference_spec.rb new file mode 100644 index 0000000000..de379d75ac --- /dev/null +++ b/spec/rubyspec/core/dir/element_reference_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/glob', __FILE__) + +describe "Dir.[]" do + it_behaves_like :dir_glob, :[] +end + +describe "Dir.[]" do + it_behaves_like :dir_glob_recursive, :[] +end + +describe "Dir.[]" do + before :all do + DirSpecs.create_mock_dirs + @cwd = Dir.pwd + Dir.chdir DirSpecs.mock_dir + end + + after :all do + Dir.chdir @cwd + DirSpecs.delete_mock_dirs + end + + it "calls #to_path to convert multiple patterns" do + pat1 = mock('file_one.ext') + pat1.should_receive(:to_path).and_return('file_one.ext') + pat2 = mock('file_two.ext') + pat2.should_receive(:to_path).and_return('file_two.ext') + + Dir[pat1, pat2].should == %w[file_one.ext file_two.ext] + end +end diff --git a/spec/rubyspec/core/dir/entries_spec.rb b/spec/rubyspec/core/dir/entries_spec.rb new file mode 100644 index 0000000000..8a31ab4b4a --- /dev/null +++ b/spec/rubyspec/core/dir/entries_spec.rb @@ -0,0 +1,70 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Dir.entries" do + before :all do + DirSpecs.create_mock_dirs + end + + before :each do + @internal = Encoding.default_internal + end + + after :all do + DirSpecs.delete_mock_dirs + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns an Array of filenames in an existing directory including dotfiles" do + a = Dir.entries(DirSpecs.mock_dir).sort + + a.should == DirSpecs.expected_paths + + a = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested").sort + a.should == %w|. .. .dotfile.ext directory| + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_dir) + Dir.entries(p) + end + + it "accepts an options Hash" do + a = Dir.entries("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").sort + a.should == %w|. .. .dotfile.ext directory| + end + + it "returns entries encoded with the filesystem encoding by default" do + # This spec depends on the locale not being US-ASCII because if it is, the + # entries that are not ascii_only? will be ASCII-8BIT encoded. + entries = Dir.entries(File.join(DirSpecs.mock_dir, 'special')).sort + encoding = Encoding.find("filesystem") + encoding = Encoding::ASCII_8BIT if encoding == Encoding::US_ASCII + platform_is_not :windows do + entries.should include("こんにちは.txt".force_encoding(encoding)) + end + entries.first.encoding.should equal(Encoding.find("filesystem")) + end + + it "returns entries encoded with the specified encoding" do + dir = File.join(DirSpecs.mock_dir, 'special') + entries = Dir.entries(dir, encoding: "euc-jp").sort + entries.first.encoding.should equal(Encoding::EUC_JP) + end + + it "returns entries transcoded to the default internal encoding" do + Encoding.default_internal = Encoding::EUC_KR + entries = Dir.entries(File.join(DirSpecs.mock_dir, 'special')).sort + entries.first.encoding.should equal(Encoding::EUC_KR) + end + + it "raises a SystemCallError if called with a nonexistent diretory" do + lambda { Dir.entries DirSpecs.nonexistent }.should raise_error(SystemCallError) + end +end diff --git a/spec/rubyspec/core/dir/exist_spec.rb b/spec/rubyspec/core/dir/exist_spec.rb new file mode 100644 index 0000000000..194284b5a0 --- /dev/null +++ b/spec/rubyspec/core/dir/exist_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/exist', __FILE__) + +describe "Dir.exist?" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like(:dir_exist, :exist?) +end diff --git a/spec/rubyspec/core/dir/exists_spec.rb b/spec/rubyspec/core/dir/exists_spec.rb new file mode 100644 index 0000000000..002506a22f --- /dev/null +++ b/spec/rubyspec/core/dir/exists_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/exist', __FILE__) + +describe "Dir.exists?" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like(:dir_exist, :exists?) +end diff --git a/spec/rubyspec/core/dir/fileno_spec.rb b/spec/rubyspec/core/dir/fileno_spec.rb new file mode 100644 index 0000000000..cf8b811e3b --- /dev/null +++ b/spec/rubyspec/core/dir/fileno_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +has_dir_fileno = begin + dir = Dir.new('.') + dir.fileno + true +rescue NotImplementedError + false +rescue Exception + true +ensure + dir.close +end + +describe "Dir#fileno" do + before :each do + @name = tmp("fileno") + mkdir_p @name + @dir = Dir.new(@name) + end + + after :each do + @dir.close + rm_r @name + end + + if has_dir_fileno + it "returns the file descriptor of the dir" do + @dir.fileno.should be_kind_of(Fixnum) + end + else + it "raises an error when not implemented on the platform" do + lambda { @dir.fileno }.should raise_error(NotImplementedError) + end + end +end diff --git a/spec/rubyspec/core/dir/fixtures/common.rb b/spec/rubyspec/core/dir/fixtures/common.rb new file mode 100644 index 0000000000..f6708b04f7 --- /dev/null +++ b/spec/rubyspec/core/dir/fixtures/common.rb @@ -0,0 +1,169 @@ +# encoding: utf-8 + +module DirSpecs + def self.mock_dir(dirs = ['dir_specs_mock']) + @mock_dir ||= tmp("") + File.join @mock_dir, dirs + end + + def self.nonexistent + name = File.join mock_dir, "nonexistent00" + name = name.next while File.exist? name + name + end + + # TODO: make these relative to the mock_dir + def self.clear_dirs + [ 'nonexisting', + 'default_perms', + 'reduced', + 'always_returns_0', + '???', + [0xe9].pack('U') + ].each do |dir| + begin + Dir.rmdir dir + rescue + end + end + end + + # The names of the fixture directories and files used by + # various Dir specs. + def self.mock_dir_files + unless @mock_dir_files + @mock_dir_files = %w[ + .dotfile + .dotsubdir/.dotfile + .dotsubdir/nondotfile + + deeply/.dotfile + deeply/nested/.dotfile.ext + deeply/nested/directory/structure/.ext + deeply/nested/directory/structure/bar + deeply/nested/directory/structure/baz + deeply/nested/directory/structure/file_one + deeply/nested/directory/structure/file_one.ext + deeply/nested/directory/structure/foo + deeply/nondotfile + + file_one.ext + file_two.ext + + dir_filename_ordering + dir/filename_ordering + + nondotfile + + subdir_one/.dotfile + subdir_one/nondotfile + subdir_two/nondotfile + subdir_two/nondotfile.ext + + brace/a + brace/a.js + brace/a.erb + brace/a.js.rjs + brace/a.html.erb + + special/+ + + special/^ + special/$ + + special/( + special/) + special/[ + special/] + special/{ + special/} + + special/test{1}/file[1] + ] + + platform_is_not :windows do + @mock_dir_files += %w[ + special/* + special/? + + special/| + + special/こんにちは.txt + ] + end + end + + @mock_dir_files + end + + def self.create_mock_dirs + mock_dir_files.each do |name| + file = File.join mock_dir, name + mkdir_p File.dirname(file) + touch file + end + end + + def self.delete_mock_dirs + begin + rm_r mock_dir + rescue Errno::ENOTEMPTY => e + puts Dir["#{mock_dir}/**/*"] + raise e + end + end + + def self.mock_rmdir(*dirs) + mock_dir ['rmdir_dirs'].concat(dirs) + end + + def self.rmdir_dirs(create = true) + dirs = %w[ + empty + nonempty + nonempty/child + noperm + noperm/child + ] + + base_dir = mock_dir ['rmdir_dirs'] + + dirs.reverse_each do |d| + dir = File.join base_dir, d + if File.exist? dir + File.chmod 0777, dir + rm_r dir + end + end + rm_r base_dir + + if create + dirs.each do |d| + dir = File.join base_dir, d + unless File.exist? dir + mkdir_p dir + File.chmod 0777, dir + end + end + end + end + + def self.expected_paths + %w[ + . + .. + .dotfile + .dotsubdir + brace + deeply + dir + dir_filename_ordering + file_one.ext + file_two.ext + nondotfile + special + subdir_one + subdir_two + ] + end +end diff --git a/spec/rubyspec/core/dir/foreach_spec.rb b/spec/rubyspec/core/dir/foreach_spec.rb new file mode 100644 index 0000000000..e606b4f65c --- /dev/null +++ b/spec/rubyspec/core/dir/foreach_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Dir.foreach" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it "yields all names in an existing directory to the provided block" do + a, b = [], [] + + Dir.foreach(DirSpecs.mock_dir) {|f| a << f} + Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested") {|f| b << f} + + a.sort.should == DirSpecs.expected_paths + b.sort.should == %w|. .. .dotfile.ext directory| + end + + it "returns nil when successful" do + Dir.foreach(DirSpecs.mock_dir) {|f| f}.should == nil + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_dir) + Dir.foreach(p).to_a + end + + it "raises a SystemCallError if passed a nonexistent directory" do + lambda { Dir.foreach(DirSpecs.nonexistent) {} }.should raise_error(SystemCallError) + end + + it "returns an Enumerator if no block given" do + Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator) + Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths + end + + describe "when no block is given" do + it "returns an Enumerator" do + Dir.foreach(DirSpecs.mock_dir).should be_an_instance_of(Enumerator) + Dir.foreach(DirSpecs.mock_dir).to_a.sort.should == DirSpecs.expected_paths + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + Dir.foreach(DirSpecs.mock_dir).size.should == nil + end + end + end + end +end diff --git a/spec/rubyspec/core/dir/getwd_spec.rb b/spec/rubyspec/core/dir/getwd_spec.rb new file mode 100644 index 0000000000..26659ddec7 --- /dev/null +++ b/spec/rubyspec/core/dir/getwd_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/pwd', __FILE__) + +describe "Dir.getwd" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_pwd, :getwd +end diff --git a/spec/rubyspec/core/dir/glob_spec.rb b/spec/rubyspec/core/dir/glob_spec.rb new file mode 100644 index 0000000000..b65b738b61 --- /dev/null +++ b/spec/rubyspec/core/dir/glob_spec.rb @@ -0,0 +1,156 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/glob', __FILE__) + +describe "Dir.glob" do + it_behaves_like :dir_glob, :glob +end + +describe "Dir.glob" do + it_behaves_like :dir_glob_recursive, :glob +end + +describe "Dir.glob" do + before :each do + DirSpecs.create_mock_dirs + + @cwd = Dir.pwd + Dir.chdir DirSpecs.mock_dir + end + + after :each do + Dir.chdir @cwd + + DirSpecs.delete_mock_dirs + end + + it "can take an array of patterns" do + Dir.glob(["file_o*", "file_t*"]).should == + %w!file_one.ext file_two.ext! + end + + it "calls #to_path to convert multiple patterns" do + pat1 = mock('file_one.ext') + pat1.should_receive(:to_path).and_return('file_one.ext') + pat2 = mock('file_two.ext') + pat2.should_receive(:to_path).and_return('file_two.ext') + + Dir.glob([pat1, pat2]).should == %w[file_one.ext file_two.ext] + end + + it "matches both dot and non-dotfiles with '*' and option File::FNM_DOTMATCH" do + Dir.glob('*', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_paths + end + + it "matches files with any beginning with '*' and option File::FNM_DOTMATCH" do + Dir.glob('*file', File::FNM_DOTMATCH).sort.should == %w|.dotfile nondotfile|.sort + end + + it "matches any files in the current directory with '**' and option File::FNM_DOTMATCH" do + Dir.glob('**', File::FNM_DOTMATCH).sort.should == DirSpecs.expected_paths + end + + it "recursively matches any subdirectories except './' or '../' with '**/' from the current directory and option File::FNM_DOTMATCH" do + expected = %w[ + .dotsubdir/ + brace/ + deeply/ + deeply/nested/ + deeply/nested/directory/ + deeply/nested/directory/structure/ + dir/ + special/ + special/test{1}/ + subdir_one/ + subdir_two/ + ] + + Dir.glob('**/', File::FNM_DOTMATCH).sort.should == expected + end + + # This is a seperate case to check **/ coming after a constant + # directory as well. + it "recursively matches any subdirectories except './' or '../' with '**/' and option File::FNM_DOTMATCH" do + expected = %w[ + ./ + ./.dotsubdir/ + ./brace/ + ./deeply/ + ./deeply/nested/ + ./deeply/nested/directory/ + ./deeply/nested/directory/structure/ + ./dir/ + ./special/ + ./special/test{1}/ + ./subdir_one/ + ./subdir_two/ + ] + + Dir.glob('./**/', File::FNM_DOTMATCH).sort.should == expected + end + + it "matches a list of paths by concatenating their individual results" do + expected = %w[ + deeply/ + deeply/nested/ + deeply/nested/directory/ + deeply/nested/directory/structure/ + subdir_two/nondotfile + subdir_two/nondotfile.ext + ] + + Dir.glob('{deeply/**/,subdir_two/*}').sort.should == expected + end + + it "accepts a block and yields it with each elements" do + ary = [] + ret = Dir.glob(["file_o*", "file_t*"]) { |t| ary << t } + ret.should be_nil + ary.should == %w!file_one.ext file_two.ext! + end + + it "ignores non-dirs when traversing recursively" do + touch "spec" + Dir.glob("spec/**/*.rb").should == [] + end + + it "matches nothing when given an empty list of paths" do + Dir.glob('{}').should == [] + end + + it "handles infinite directory wildcards" do + Dir.glob('**/**/**').empty?.should == false + end + + platform_is_not(:windows) do + it "matches the literal character '\\' with option File::FNM_NOESCAPE" do + Dir.mkdir 'foo?bar' + + begin + Dir.glob('foo?bar', File::FNM_NOESCAPE).should == %w|foo?bar| + Dir.glob('foo\?bar', File::FNM_NOESCAPE).should == [] + ensure + Dir.rmdir 'foo?bar' + end + + Dir.mkdir 'foo\?bar' + + begin + Dir.glob('foo\?bar', File::FNM_NOESCAPE).should == %w|foo\\?bar| + ensure + Dir.rmdir 'foo\?bar' + end + end + + it "returns nil for directories current user has no permission to read" do + Dir.mkdir('no_permission') + File.chmod(0, 'no_permission') + + begin + Dir.glob('no_permission/*').should == [] + ensure + Dir.rmdir('no_permission') + end + end + end +end diff --git a/spec/rubyspec/core/dir/home_spec.rb b/spec/rubyspec/core/dir/home_spec.rb new file mode 100644 index 0000000000..e1ebc3032e --- /dev/null +++ b/spec/rubyspec/core/dir/home_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Dir.home" do + it "returns the current user's home directory as a string if called without arguments" do + home_directory = ENV['HOME'] + platform_is :windows do + path = ENV['HOMEDRIVE'] + ENV['HOMEPATH'] + home_directory = path.tr('\\', '/').chomp('/') + end + + Dir.home.should == home_directory + end + + platform_is_not :windows do + it "returns the named user's home directory as a string if called with an argument" do + Dir.home(ENV['USER']).should == ENV['HOME'] + end + end + + it "raises an ArgumentError if the named user doesn't exist" do + lambda { Dir.home('geuw2n288dh2k') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/dir/initialize_spec.rb b/spec/rubyspec/core/dir/initialize_spec.rb new file mode 100644 index 0000000000..b9420647d1 --- /dev/null +++ b/spec/rubyspec/core/dir/initialize_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Dir#initialize" do + before :each do + DirSpecs.create_mock_dirs + end + + after :each do + DirSpecs.delete_mock_dirs + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.stub!(:to_path).and_return(DirSpecs.mock_dir) + dir = Dir.new(p) + begin + dir.path.should == DirSpecs.mock_dir + ensure + dir.close + end + end +end diff --git a/spec/rubyspec/core/dir/inspect_spec.rb b/spec/rubyspec/core/dir/inspect_spec.rb new file mode 100644 index 0000000000..01bde8a862 --- /dev/null +++ b/spec/rubyspec/core/dir/inspect_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Dir#inspect" do + before :each do + @dir = Dir.new(Dir.getwd) + end + + after :each do + @dir.close + end + + it "returns a String" do + @dir.inspect.should be_an_instance_of(String) + end + + it "includes the class name" do + @dir.inspect.should =~ /Dir/ + end + + it "includes the directory name" do + @dir.inspect.should include(Dir.getwd) + end +end diff --git a/spec/rubyspec/core/dir/mkdir_spec.rb b/spec/rubyspec/core/dir/mkdir_spec.rb new file mode 100644 index 0000000000..ba2f12af4f --- /dev/null +++ b/spec/rubyspec/core/dir/mkdir_spec.rb @@ -0,0 +1,85 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Dir.mkdir" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it "creates the named directory with the given permissions" do + DirSpecs.clear_dirs + + begin + File.exist?('nonexisting').should == false + Dir.mkdir 'nonexisting' + File.exist?('nonexisting').should == true + platform_is_not :windows do + Dir.mkdir 'default_perms' + a = File.stat('default_perms').mode + Dir.mkdir 'reduced', (a - 1) + File.stat('reduced').mode.should_not == a + end + platform_is :windows do + Dir.mkdir 'default_perms', 0666 + a = File.stat('default_perms').mode + Dir.mkdir 'reduced', 0444 + File.stat('reduced').mode.should_not == a + end + + Dir.mkdir('always_returns_0').should == 0 + platform_is_not(:windows) do + File.chmod(0777, "nonexisting","default_perms","reduced","always_returns_0") + end + platform_is_not(:windows) do + File.chmod(0644, "nonexisting","default_perms","reduced","always_returns_0") + end + ensure + DirSpecs.clear_dirs + end + end + + it "calls #to_path on non-String arguments" do + DirSpecs.clear_dirs + p = mock('path') + p.should_receive(:to_path).and_return('nonexisting') + Dir.mkdir(p) + DirSpecs.clear_dirs + end + + it "raises a SystemCallError if any of the directories in the path before the last does not exist" do + lambda { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should raise_error(SystemCallError) + end + + it "raises Errno::EEXIST if the specified directory already exists" do + lambda { Dir.mkdir(File.dirname(__FILE__)) }.should raise_error(Errno::EEXIST) + end + + it "raises Errno::EEXIST if the argument points to the existing file" do + lambda { Dir.mkdir(__FILE__) }.should raise_error(Errno::EEXIST) + end +end + +# The permissions flag are not supported on Windows as stated in documentation: +# The permissions may be modified by the value of File.umask, and are ignored on NT. +platform_is_not :windows do + describe "Dir.mkdir" do + before :each do + @dir = tmp "noperms" + end + + after :each do + File.chmod 0777, @dir + rm_r @dir + end + + it "raises a SystemCallError when lacking adequate permissions in the parent dir" do + Dir.mkdir @dir, 0000 + + lambda { Dir.mkdir "#{@dir}/subdir" }.should raise_error(SystemCallError) + end + end +end diff --git a/spec/rubyspec/core/dir/open_spec.rb b/spec/rubyspec/core/dir/open_spec.rb new file mode 100644 index 0000000000..b3deed47b7 --- /dev/null +++ b/spec/rubyspec/core/dir/open_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/open', __FILE__) + +describe "Dir.open" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_open, :open +end diff --git a/spec/rubyspec/core/dir/path_spec.rb b/spec/rubyspec/core/dir/path_spec.rb new file mode 100644 index 0000000000..1601220636 --- /dev/null +++ b/spec/rubyspec/core/dir/path_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/path', __FILE__) + +describe "Dir#path" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like(:dir_path, :path) +end diff --git a/spec/rubyspec/core/dir/pos_spec.rb b/spec/rubyspec/core/dir/pos_spec.rb new file mode 100644 index 0000000000..9f05fab250 --- /dev/null +++ b/spec/rubyspec/core/dir/pos_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/closed', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "Dir#pos" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_pos, :pos +end + +describe "Dir#pos" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_closed, :pos +end + +describe "Dir#pos=" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_pos_set, :pos= +end diff --git a/spec/rubyspec/core/dir/pwd_spec.rb b/spec/rubyspec/core/dir/pwd_spec.rb new file mode 100644 index 0000000000..4fa86dd6b9 --- /dev/null +++ b/spec/rubyspec/core/dir/pwd_spec.rb @@ -0,0 +1,39 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/pwd', __FILE__) + +describe "Dir.pwd" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_pwd, :pwd +end + +describe "Dir.pwd" do + before :each do + @name = tmp("あ").force_encoding('binary') + @fs_encoding = Encoding.find('filesystem') + end + + after :each do + rm_r @name + end + + platform_is_not :windows do + it "correctly handles dirs with unicode characters in them" do + Dir.mkdir @name + Dir.chdir @name do + if @fs_encoding == Encoding::UTF_8 + Dir.pwd.encoding.should == Encoding::UTF_8 + end + Dir.pwd.force_encoding('binary').should == @name + end + end + end +end diff --git a/spec/rubyspec/core/dir/read_spec.rb b/spec/rubyspec/core/dir/read_spec.rb new file mode 100644 index 0000000000..79ed9b8058 --- /dev/null +++ b/spec/rubyspec/core/dir/read_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/closed', __FILE__) + +describe "Dir#read" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it "returns the file name in the current seek position" do + # an FS does not necessarily impose order + ls = Dir.entries DirSpecs.mock_dir + dir = Dir.open DirSpecs.mock_dir + ls.should include(dir.read) + dir.close + end + + it "returns nil when there are no more entries" do + dir = Dir.open DirSpecs.mock_dir + DirSpecs.expected_paths.size.times do + dir.read.should_not == nil + end + dir.read.should == nil + dir.close + end + + it "returns each entry successively" do + dir = Dir.open DirSpecs.mock_dir + entries = [] + while entry = dir.read + entries << entry + end + dir.close + + entries.sort.should == DirSpecs.expected_paths + end + + it_behaves_like :dir_closed, :read +end diff --git a/spec/rubyspec/core/dir/rewind_spec.rb b/spec/rubyspec/core/dir/rewind_spec.rb new file mode 100644 index 0000000000..65ffcdf1c3 --- /dev/null +++ b/spec/rubyspec/core/dir/rewind_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/closed', __FILE__) + +describe "Dir#rewind" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + before :each do + @dir = Dir.open DirSpecs.mock_dir + end + + after :each do + @dir.close + end + + it "resets the next read to start from the first entry" do + a = @dir.read + b = @dir.read + a.should_not == b + @dir.rewind + c = @dir.read + c.should == a + end + + it "returns the Dir instance" do + @dir.rewind.should == @dir + end + + it_behaves_like :dir_closed, :rewind +end diff --git a/spec/rubyspec/core/dir/rmdir_spec.rb b/spec/rubyspec/core/dir/rmdir_spec.rb new file mode 100644 index 0000000000..09499572c0 --- /dev/null +++ b/spec/rubyspec/core/dir/rmdir_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/delete', __FILE__) + +describe "Dir.rmdir" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_delete, :rmdir +end diff --git a/spec/rubyspec/core/dir/seek_spec.rb b/spec/rubyspec/core/dir/seek_spec.rb new file mode 100644 index 0000000000..b51e554441 --- /dev/null +++ b/spec/rubyspec/core/dir/seek_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "Dir#seek" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it "returns the Dir instance" do + @dir.seek(@dir.pos).should == @dir + end + + it_behaves_like :dir_pos_set, :seek +end diff --git a/spec/rubyspec/core/dir/shared/chroot.rb b/spec/rubyspec/core/dir/shared/chroot.rb new file mode 100644 index 0000000000..2ed033dfed --- /dev/null +++ b/spec/rubyspec/core/dir/shared/chroot.rb @@ -0,0 +1,41 @@ +describe :dir_chroot_as_root, shared: true do + before :all do + DirSpecs.create_mock_dirs + + @real_root = "../" * (File.dirname(__FILE__).count('/') - 1) + @ref_dir = File.join("/", Dir.new('/').entries.first) + end + + after :all do + until File.exist?(@ref_dir) + Dir.send(@method, "../") or break + end + + DirSpecs.delete_mock_dirs + end + + it "can be used to change the process' root directory" do + lambda { Dir.send(@method, File.dirname(__FILE__)) }.should_not raise_error + File.exist?("/#{File.basename(__FILE__)}").should be_true + end + + it "returns 0 if successful" do + Dir.send(@method, '/').should == 0 + end + + it "raises an Errno::ENOENT exception if the directory doesn't exist" do + lambda { Dir.send(@method, 'xgwhwhsjai2222jg') }.should raise_error(Errno::ENOENT) + end + + it "can be escaped from with ../" do + Dir.send(@method, @real_root) + File.exist?(@ref_dir).should be_true + File.exist?("/#{File.basename(__FILE__)}").should be_false + end + + it "calls #to_path on non-String argument" do + p = mock('path') + p.should_receive(:to_path).and_return(@real_root) + Dir.send(@method, p) + end +end diff --git a/spec/rubyspec/core/dir/shared/closed.rb b/spec/rubyspec/core/dir/shared/closed.rb new file mode 100644 index 0000000000..a1bce06a08 --- /dev/null +++ b/spec/rubyspec/core/dir/shared/closed.rb @@ -0,0 +1,9 @@ +describe :dir_closed, shared: true do + it "raises an IOError when called on a closed Dir instance" do + lambda { + dir = Dir.open DirSpecs.mock_dir + dir.close + dir.send(@method) {} + }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/dir/shared/delete.rb b/spec/rubyspec/core/dir/shared/delete.rb new file mode 100644 index 0000000000..86af65592b --- /dev/null +++ b/spec/rubyspec/core/dir/shared/delete.rb @@ -0,0 +1,59 @@ +describe :dir_delete, shared: true do + before :each do + DirSpecs.rmdir_dirs true + end + + after :each do + DirSpecs.rmdir_dirs false + end + + it "removes empty directories" do + Dir.send(@method, DirSpecs.mock_rmdir("empty")).should == 0 + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_rmdir("empty")) + Dir.send(@method, p) + end + + platform_is_not :solaris do + it "raises an Errno::ENOTEMPTY when trying to remove a nonempty directory" do + lambda do + Dir.send @method, DirSpecs.mock_rmdir("nonempty") + end.should raise_error(Errno::ENOTEMPTY) + end + end + + platform_is :solaris do + it "raises an Errno::EEXIST when trying to remove a nonempty directory" do + lambda do + Dir.send @method, DirSpecs.mock_rmdir("nonempty") + end.should raise_error(Errno::EEXIST) + end + end + + it "raises an Errno::ENOENT when trying to remove a non-existing directory" do + lambda do + Dir.send @method, DirSpecs.nonexistent + end.should raise_error(Errno::ENOENT) + end + + it "raises an Errno::ENOTDIR when trying to remove a non-directory" do + lambda do + Dir.send @method, __FILE__ + end.should raise_error(Errno::ENOTDIR) + end + + # this won't work on Windows, since chmod(0000) does not remove all permissions + platform_is_not :windows do + it "raises an Errno::EACCES if lacking adequate permissions to remove the directory" do + parent = DirSpecs.mock_rmdir("noperm") + child = DirSpecs.mock_rmdir("noperm", "child") + File.chmod(0000, parent) + lambda do + Dir.send @method, child + end.should raise_error(Errno::EACCES) + end + end +end diff --git a/spec/rubyspec/core/dir/shared/exist.rb b/spec/rubyspec/core/dir/shared/exist.rb new file mode 100644 index 0000000000..fbd2c9862d --- /dev/null +++ b/spec/rubyspec/core/dir/shared/exist.rb @@ -0,0 +1,56 @@ +describe :dir_exist, shared: true do + it "returns true if the given directory exists" do + Dir.send(@method, File.dirname(__FILE__)).should be_true + end + + it "returns true for '.'" do + Dir.send(@method, '.').should be_true + end + + it "returns true for '..'" do + Dir.send(@method, '..').should be_true + end + + it "understands non-ASCII paths" do + subdir = File.join(tmp("\u{9876}\u{665}")) + Dir.send(@method, subdir).should be_false + Dir.mkdir(subdir) + Dir.send(@method, subdir).should be_true + Dir.rmdir(subdir) + end + + it "understands relative paths" do + Dir.send(@method, File.dirname(__FILE__) + '/../').should be_true + end + + it "returns false if the given directory doesn't exist" do + Dir.send(@method, 'y26dg27n2nwjs8a/').should be_false + end + + it "doesn't require the name to have a trailing slash" do + dir = File.dirname(__FILE__) + dir.sub!(/\/$/,'') + Dir.send(@method, dir).should be_true + end + + it "doesn't expand paths" do + Dir.send(@method, File.expand_path('~')).should be_true + Dir.send(@method, '~').should be_false + end + + it "returns false if the argument exists but is a file" do + File.exist?(__FILE__).should be_true + Dir.send(@method, __FILE__).should be_false + end + + it "doesn't set $! when file doesn't exist" do + Dir.send(@method, "/path/to/non/existent/dir") + $!.should be_nil + end + + it "calls #to_path on non String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(File.dirname(__FILE__)) + Dir.send(@method, p) + end +end diff --git a/spec/rubyspec/core/dir/shared/glob.rb b/spec/rubyspec/core/dir/shared/glob.rb new file mode 100644 index 0000000000..d2201cd6cd --- /dev/null +++ b/spec/rubyspec/core/dir/shared/glob.rb @@ -0,0 +1,328 @@ +# -*- encoding: utf-8 -*- +describe :dir_glob, shared: true do + before :all do + DirSpecs.create_mock_dirs + @cwd = Dir.pwd + Dir.chdir DirSpecs.mock_dir + end + + after :all do + Dir.chdir @cwd + DirSpecs.delete_mock_dirs + end + + with_feature :encoding do + it "raises an Encoding::CompatibilityError if the argument encoding is not compatible with US-ASCII" do + pattern = "file*".force_encoding Encoding::UTF_16BE + lambda { Dir.send(@method, pattern) }.should raise_error(Encoding::CompatibilityError) + end + end + + it "calls #to_path to convert a pattern" do + obj = mock('file_one.ext') + obj.should_receive(:to_path).and_return('file_one.ext') + + Dir.send(@method, obj).should == %w[file_one.ext] + end + + it "splits the string on \\0 if there is only one string given" do + Dir.send(@method, "file_o*\0file_t*").should == + %w!file_one.ext file_two.ext! + end + + it "matches non-dotfiles with '*'" do + expected = %w[ + brace + deeply + dir + dir_filename_ordering + file_one.ext + file_two.ext + nondotfile + special + subdir_one + subdir_two + ] + + Dir.send(@method,'*').sort.should == expected + end + + it "returns empty array when empty pattern provided" do + Dir.send(@method, '').should == [] + end + + it "matches regexp special +" do + Dir.send(@method, 'special/+').should == ['special/+'] + end + + platform_is_not :windows do + it "matches regexp special *" do + Dir.send(@method, 'special/\*').should == ['special/*'] + end + + it "matches regexp special ?" do + Dir.send(@method, 'special/\?').should == ['special/?'] + end + + it "matches regexp special |" do + Dir.send(@method, 'special/|').should == ['special/|'] + end + end + + it "matches regexp special ^" do + Dir.send(@method, 'special/^').should == ['special/^'] + end + + it "matches regexp special $" do + Dir.send(@method, 'special/$').should == ['special/$'] + end + + it "matches regexp special (" do + Dir.send(@method, 'special/(').should == ['special/('] + end + + it "matches regexp special )" do + Dir.send(@method, 'special/)').should == ['special/)'] + end + + it "matches regexp special [" do + Dir.send(@method, 'special/\[').should == ['special/['] + end + + it "matches regexp special ]" do + Dir.send(@method, 'special/]').should == ['special/]'] + end + + it "matches regexp special {" do + Dir.send(@method, 'special/\{').should == ['special/{'] + end + + it "matches regexp special }" do + Dir.send(@method, 'special/\}').should == ['special/}'] + end + + it "matches paths with glob patterns" do + Dir.send(@method, 'special/test\{1\}/*').should == ['special/test{1}/file[1]'] + end + + it "matches dotfiles with '.*'" do + Dir.send(@method, '.*').sort.should == %w|. .. .dotfile .dotsubdir|.sort + end + + it "matches non-dotfiles with '*'" do + Dir.send(@method, '*file').sort.should == %w|nondotfile|.sort + end + + it "matches dotfiles with '.*'" do + Dir.send(@method, '.*file').sort.should == %w|.dotfile|.sort + end + + it "matches files with any ending with '*'" do + Dir.send(@method, 'file*').sort.should == %w|file_one.ext file_two.ext|.sort + end + + it "matches files with any middle with '*'" do + Dir.send(@method, 'sub*_one').sort.should == %w|subdir_one|.sort + end + + it "handles directories with globs" do + Dir.send(@method, 'sub*/*').sort.should == %w!subdir_one/nondotfile subdir_two/nondotfile subdir_two/nondotfile.ext! + end + + it "matches files with multiple '*' special characters" do + Dir.send(@method, '*fi*e*').sort.should == %w|dir_filename_ordering nondotfile file_one.ext file_two.ext|.sort + end + + it "matches non-dotfiles in the current directory with '**'" do + expected = %w[ + brace + deeply + dir + dir_filename_ordering + file_one.ext + file_two.ext + nondotfile + special + subdir_one + subdir_two + ] + + Dir.send(@method, '**').sort.should == expected + end + + it "matches dotfiles in the current directory with '.**'" do + Dir.send(@method, '.**').sort.should == %w|. .. .dotsubdir .dotfile|.sort + end + + it "recursively matches any nondot subdirectories with '**/'" do + expected = %w[ + brace/ + deeply/ + deeply/nested/ + deeply/nested/directory/ + deeply/nested/directory/structure/ + dir/ + special/ + special/test{1}/ + subdir_one/ + subdir_two/ + ] + + Dir.send(@method, '**/').sort.should == expected + end + + it "recursively matches any subdirectories including ./ and ../ with '.**/'" do + Dir.chdir("#{DirSpecs.mock_dir}/subdir_one") do + Dir.send(@method, '.**/').sort.should == %w|./ ../|.sort + end + end + + it "matches a single character except leading '.' with '?'" do + Dir.send(@method, '?ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "accepts multiple '?' characters in a pattern" do + Dir.send(@method, 'subdir_???').sort.should == %w|subdir_one subdir_two|.sort + end + + it "matches any characters in a set with '[]'" do + Dir.send(@method, '[stfu]ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "matches any characters in a range with '[-]'" do + Dir.send(@method, '[a-zA-Z]ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "matches any characters except those in a set with '[^]'" do + Dir.send(@method, '[^wtf]ubdir_one').sort.should == %w|subdir_one|.sort + end + + it "matches any characters except those in a range with '[^-,,...}'" do + Dir.send(@method, 'subdir_{one,two,three}').sort.should == %w|subdir_one subdir_two|.sort + end + + it "matches a set '{,,...}' which also uses a glob" do + Dir.send(@method, 'sub*_{one,two,three}').sort.should == %w|subdir_one subdir_two|.sort + end + + it "accepts string sets with empty strings with {,,}" do + a = Dir.send(@method, 'deeply/nested/directory/structure/file_one{.ext,}').sort + a.should == %w|deeply/nested/directory/structure/file_one.ext + deeply/nested/directory/structure/file_one|.sort + end + + it "matches dot or non-dotfiles with '{,.}*'" do + Dir.send(@method, '{,.}*').sort.should == DirSpecs.expected_paths + end + + it "respects the order of {} expressions, expanding left most first" do + files = Dir.send(@method, "brace/a{.js,.html}{.erb,.rjs}") + files.should == %w!brace/a.js.rjs brace/a.html.erb! + end + + it "respects the optional nested {} expressions" do + files = Dir.send(@method, "brace/a{.{js,html},}{.{erb,rjs},}") + files.should == %w!brace/a.js.rjs brace/a.js brace/a.html.erb brace/a.erb brace/a! + end + + it "matches special characters by escaping with a backslash with '\\'" do + Dir.mkdir 'foo^bar' + + begin + Dir.send(@method, 'foo?bar').should == %w|foo^bar| + Dir.send(@method, 'foo\?bar').should == [] + Dir.send(@method, 'nond\otfile').should == %w|nondotfile| + ensure + Dir.rmdir 'foo^bar' + end + end + + it "recursively matches directories with '**/'" do + Dir.send(@method, '**/*fil?{,.}*').uniq.sort.should == + %w[deeply/nested/directory/structure/file_one + deeply/nested/directory/structure/file_one.ext + deeply/nondotfile + + dir/filename_ordering + dir_filename_ordering + + file_one.ext + file_two.ext + + nondotfile + + special/test{1}/file[1] + + subdir_one/nondotfile + subdir_two/nondotfile + subdir_two/nondotfile.ext] + end + + it "ignores matching through directories that doen't exist" do + Dir.send(@method, "deeply/notthere/blah*/whatever").should == [] + end + + it "ignores matching only directories under an nonexistant path" do + Dir.send(@method, "deeply/notthere/blah/").should == [] + end + + platform_is_not :windows do + it "matches UTF-8 paths" do + Dir.send(@method, "special/こんにちは{,.txt}").should == ["special/こんにちは.txt"] + end + end +end + +describe :dir_glob_recursive, shared: true do + before :each do + @cwd = Dir.pwd + @mock_dir = File.expand_path tmp('dir_glob_mock') + + %w[ + a/x/b/y/e + a/x/b/y/b/z/e + ].each do |path| + file = File.join @mock_dir, path + mkdir_p File.dirname(file) + touch file + end + + Dir.chdir @mock_dir + end + + after :each do + Dir.chdir @cwd + rm_r @mock_dir + end + + it "matches multiple recursives" do + expected = %w[ + a/x/b/y/b/z/e + a/x/b/y/e + ] + + Dir.send(@method, 'a/**/b/**/e').uniq.sort.should == expected + end + + platform_is_not :windows do + it "ignores symlinks" do + file = File.join @mock_dir, 'b/z/e' + link = File.join @mock_dir, 'a/y' + + mkdir_p File.dirname(file) + touch file + File.symlink(File.dirname(file), link) + + expected = %w[ + a/x/b/y/b/z/e + a/x/b/y/e + ] + + Dir.send(@method, 'a/**/e').uniq.sort.should == expected + end + end +end diff --git a/spec/rubyspec/core/dir/shared/open.rb b/spec/rubyspec/core/dir/shared/open.rb new file mode 100644 index 0000000000..3c2b63b6fa --- /dev/null +++ b/spec/rubyspec/core/dir/shared/open.rb @@ -0,0 +1,63 @@ +describe :dir_open, shared: true do + it "returns a Dir instance representing the specified directory" do + dir = Dir.send(@method, DirSpecs.mock_dir) + dir.should be_kind_of(Dir) + dir.close + end + + it "raises a SystemCallError if the directory does not exist" do + lambda do + Dir.send @method, DirSpecs.nonexistent + end.should raise_error(SystemCallError) + end + + it "may take a block which is yielded to with the Dir instance" do + Dir.send(@method, DirSpecs.mock_dir) {|dir| dir.should be_kind_of(Dir)} + end + + it "returns the value of the block if a block is given" do + Dir.send(@method, DirSpecs.mock_dir) {|dir| :value }.should == :value + end + + it "closes the Dir instance when the block exits if given a block" do + closed_dir = Dir.send(@method, DirSpecs.mock_dir) { |dir| dir } + lambda { closed_dir.read }.should raise_error(IOError) + end + + it "closes the Dir instance when the block exits the block even due to an exception" do + closed_dir = nil + + lambda do + Dir.send(@method, DirSpecs.mock_dir) do |dir| + closed_dir = dir + raise + end + end.should raise_error + + lambda { closed_dir.read }.should raise_error(IOError) + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(DirSpecs.mock_dir) + Dir.send(@method, p) { true } + end + + it "accepts an options Hash" do + dir = Dir.send(@method, DirSpecs.mock_dir, encoding: "utf-8") {|d| d } + dir.should be_kind_of(Dir) + end + + it "calls #to_hash to convert the options object" do + options = mock("dir_open") + options.should_receive(:to_hash).and_return({ encoding: Encoding::UTF_8 }) + + dir = Dir.send(@method, DirSpecs.mock_dir, options) {|d| d } + dir.should be_kind_of(Dir) + end + + it "ignores the :encoding option if it is nil" do + dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d } + dir.should be_kind_of(Dir) + end +end diff --git a/spec/rubyspec/core/dir/shared/path.rb b/spec/rubyspec/core/dir/shared/path.rb new file mode 100644 index 0000000000..829eeb04c2 --- /dev/null +++ b/spec/rubyspec/core/dir/shared/path.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) +require File.expand_path('../closed', __FILE__) + +describe :dir_path, shared: true do + it "returns the path that was supplied to .new or .open" do + dir = Dir.open DirSpecs.mock_dir + begin + dir.send(@method).should == DirSpecs.mock_dir + ensure + dir.close rescue nil + end + end + + it "returns the path even when called on a closed Dir instance" do + dir = Dir.open DirSpecs.mock_dir + dir.close + dir.send(@method).should == DirSpecs.mock_dir + end + + with_feature :encoding do + it "returns a String with the same encoding as the argument to .open" do + path = DirSpecs.mock_dir.force_encoding Encoding::IBM866 + dir = Dir.open path + begin + dir.send(@method).encoding.should equal(Encoding::IBM866) + ensure + dir.close + end + end + end +end diff --git a/spec/rubyspec/core/dir/shared/pos.rb b/spec/rubyspec/core/dir/shared/pos.rb new file mode 100644 index 0000000000..2165932d99 --- /dev/null +++ b/spec/rubyspec/core/dir/shared/pos.rb @@ -0,0 +1,51 @@ +describe :dir_pos, shared: true do + before :each do + @dir = Dir.open DirSpecs.mock_dir + end + + after :each do + @dir.close rescue nil + end + + it "returns an Integer representing the current position in the directory" do + @dir.send(@method).should be_kind_of(Integer) + @dir.send(@method).should be_kind_of(Integer) + @dir.send(@method).should be_kind_of(Integer) + end + + it "returns a different Integer if moved from previous position" do + a = @dir.send(@method) + @dir.read + b = @dir.send(@method) + + a.should be_kind_of(Integer) + b.should be_kind_of(Integer) + + a.should_not == b + end +end + +describe :dir_pos_set, shared: true do + before :each do + @dir = Dir.open DirSpecs.mock_dir + end + + after :each do + @dir.close + end + + # NOTE: #seek/#pos= to a position not returned by #tell/#pos is undefined + # and should not be spec'd. + + it "moves the read position to a previously obtained position" do + pos = @dir.pos + a = @dir.read + b = @dir.read + @dir.send @method, pos + c = @dir.read + + a.should_not == b + b.should_not == c + c.should == a + end +end diff --git a/spec/rubyspec/core/dir/shared/pwd.rb b/spec/rubyspec/core/dir/shared/pwd.rb new file mode 100644 index 0000000000..5f041a9d41 --- /dev/null +++ b/spec/rubyspec/core/dir/shared/pwd.rb @@ -0,0 +1,49 @@ +describe :dir_pwd, shared: true do + with_feature :encoding do + before :each do + @fs_encoding = Encoding.find('filesystem') + end + end + + it "returns the current working directory" do + pwd = Dir.send(@method) + + File.directory?(pwd).should == true + + # On ubuntu gutsy, for example, /bin/pwd does not + # understand -P. With just `pwd -P`, /bin/pwd is run. + + # The following uses inode rather than file names to account for + # case insensitive file systems like default OS/X file systems + platform_is_not :windows do + File.stat(pwd).ino.should == File.stat(`/bin/sh -c "pwd -P"`.chomp).ino + end + platform_is :windows do + File.stat(pwd).ino.should == File.stat(File.expand_path(`cd`.chomp)).ino + end + end + + it "returns an absolute path" do + pwd = Dir.send(@method) + pwd.should == File.expand_path(pwd) + end + + it "returns an absolute path even when chdir to a relative path" do + Dir.chdir(".") do + pwd = Dir.send(@method) + File.directory?(pwd).should == true + pwd.should == File.expand_path(pwd) + end + end + + with_feature :encoding do + it "returns a String with the filesystem encoding" do + enc = Dir.send(@method).encoding + if @fs_encoding == Encoding::US_ASCII + [Encoding::US_ASCII, Encoding::ASCII_8BIT].should include(enc) + else + enc.should equal(@fs_encoding) + end + end + end +end diff --git a/spec/rubyspec/core/dir/tell_spec.rb b/spec/rubyspec/core/dir/tell_spec.rb new file mode 100644 index 0000000000..fb9848153d --- /dev/null +++ b/spec/rubyspec/core/dir/tell_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/closed', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "Dir#tell" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_pos, :tell + + it_behaves_like :dir_closed, :tell +end diff --git a/spec/rubyspec/core/dir/to_path_spec.rb b/spec/rubyspec/core/dir/to_path_spec.rb new file mode 100644 index 0000000000..85609fbfff --- /dev/null +++ b/spec/rubyspec/core/dir/to_path_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/path', __FILE__) + +describe "Dir#to_path" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like(:dir_path, :to_path) +end diff --git a/spec/rubyspec/core/dir/unlink_spec.rb b/spec/rubyspec/core/dir/unlink_spec.rb new file mode 100644 index 0000000000..4459bef56c --- /dev/null +++ b/spec/rubyspec/core/dir/unlink_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/delete', __FILE__) + +describe "Dir.unlink" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + it_behaves_like :dir_delete, :unlink +end diff --git a/spec/rubyspec/core/encoding/_dump_spec.rb b/spec/rubyspec/core/encoding/_dump_spec.rb new file mode 100644 index 0000000000..4e8305712e --- /dev/null +++ b/spec/rubyspec/core/encoding/_dump_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding#_dump" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/encoding/_load_spec.rb b/spec/rubyspec/core/encoding/_load_spec.rb new file mode 100644 index 0000000000..b8cdbaa32b --- /dev/null +++ b/spec/rubyspec/core/encoding/_load_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding._load" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/encoding/aliases_spec.rb b/spec/rubyspec/core/encoding/aliases_spec.rb new file mode 100644 index 0000000000..327c8fa641 --- /dev/null +++ b/spec/rubyspec/core/encoding/aliases_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding.aliases" do + it "returns a Hash" do + Encoding.aliases.should be_an_instance_of(Hash) + end + + it "has Strings as keys" do + Encoding.aliases.keys.each do |key| + key.should be_an_instance_of(String) + end + end + + it "has Strings as values" do + Encoding.aliases.values.each do |value| + value.should be_an_instance_of(String) + end + end + + it "has alias names as its keys" do + Encoding.aliases.key?('BINARY').should be_true + Encoding.aliases.key?('ASCII').should be_true + end + + it "has the names of the aliased encoding as its values" do + Encoding.aliases['BINARY'].should == 'ASCII-8BIT' + Encoding.aliases['ASCII'].should == 'US-ASCII' + end + + it "has an 'external' key with the external default encoding as its value" do + Encoding.aliases['external'].should == Encoding.default_external.name + end + + it "has a 'locale' key and its value equals to the name of the encoding finded by the locale charmap" do + Encoding.aliases['locale'].should == Encoding.find(Encoding.locale_charmap).name + end + + it "only contains valid aliased encodings" do + Encoding.aliases.each do |aliased, original| + Encoding.find(aliased).should == Encoding.find(original) + end + end + end +end diff --git a/spec/rubyspec/core/encoding/ascii_compatible_spec.rb b/spec/rubyspec/core/encoding/ascii_compatible_spec.rb new file mode 100644 index 0000000000..db3c31c9fb --- /dev/null +++ b/spec/rubyspec/core/encoding/ascii_compatible_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding#ascii_compatible?" do + it "returns true if self represents an ASCII-compatible encoding" do + Encoding::UTF_8.ascii_compatible?.should be_true + end + + it "returns false if self does not represent an ASCII-compatible encoding" do + Encoding::UTF_16LE.ascii_compatible?.should be_false + end + end +end diff --git a/spec/rubyspec/core/encoding/compatible_spec.rb b/spec/rubyspec/core/encoding/compatible_spec.rb new file mode 100644 index 0000000000..55e3d0fb4d --- /dev/null +++ b/spec/rubyspec/core/encoding/compatible_spec.rb @@ -0,0 +1,381 @@ +# -*- encoding: ascii-8bit -*- + +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + # TODO: add IO + + describe "Encoding.compatible? String, String" do + describe "when the first's Encoding is valid US-ASCII" do + before :each do + @str = "abc".force_encoding Encoding::US_ASCII + end + + it "returns US-ASCII when the second's is US-ASCII" do + Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::US_ASCII + end + + it "returns US-ASCII if the second String is ASCII-8BIT and ASCII only" do + Encoding.compatible?(@str, "\x7f").should == Encoding::US_ASCII + end + + it "returns ASCII-8BIT if the second String is ASCII-8BIT but not ASCII only" do + Encoding.compatible?(@str, "\xff").should == Encoding::ASCII_8BIT + end + + it "returns US-ASCII if the second String is UTF-8 and ASCII only" do + Encoding.compatible?(@str, "\x7f".encode("utf-8")).should == Encoding::US_ASCII + end + + it "returns UTF-8 if the second String is UTF-8 but not ASCII only" do + Encoding.compatible?(@str, "\u3042".encode("utf-8")).should == Encoding::UTF_8 + end + end + + describe "when the first's Encoding is ASCII compatible and ASCII only" do + it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do + [ [Encoding, "abc".force_encoding("UTF-8"), "123".force_encoding("Shift_JIS"), Encoding::UTF_8], + [Encoding, "123".force_encoding("Shift_JIS"), "abc".force_encoding("UTF-8"), Encoding::Shift_JIS] + ].should be_computed_by(:compatible?) + end + + it "returns the first's Encoding if the second is ASCII compatible and ASCII only" do + [ [Encoding, "abc".force_encoding("ASCII-8BIT"), "123".force_encoding("US-ASCII"), Encoding::ASCII_8BIT], + [Encoding, "123".force_encoding("US-ASCII"), "abc".force_encoding("ASCII-8BIT"), Encoding::US_ASCII] + ].should be_computed_by(:compatible?) + end + + it "returns the second's Encoding if the second is ASCII compatible but not ASCII only" do + [ [Encoding, "abc".force_encoding("UTF-8"), "\xff".force_encoding("Shift_JIS"), Encoding::Shift_JIS], + [Encoding, "123".force_encoding("Shift_JIS"), "\xff".force_encoding("UTF-8"), Encoding::UTF_8], + [Encoding, "abc".force_encoding("ASCII-8BIT"), "\xff".force_encoding("US-ASCII"), Encoding::US_ASCII], + [Encoding, "123".force_encoding("US-ASCII"), "\xff".force_encoding("ASCII-8BIT"), Encoding::ASCII_8BIT], + ].should be_computed_by(:compatible?) + end + + it "returns nil if the second's Encoding is not ASCII compatible" do + a = "abc".force_encoding("UTF-8") + b = "123".force_encoding("UTF-16LE") + Encoding.compatible?(a, b).should be_nil + end + end + + describe "when the first's Encoding is ASCII compatible but not ASCII only" do + it "returns the first's Encoding if the second's is valid US-ASCII" do + Encoding.compatible?("\xff", "def".encode("us-ascii")).should == Encoding::ASCII_8BIT + end + + it "returns the first's Encoding if the second's is UTF-8 and ASCII only" do + Encoding.compatible?("\xff", "\u{7f}".encode("utf-8")).should == Encoding::ASCII_8BIT + end + + it "returns nil if the second encoding is ASCII compatible but neither String's encoding is ASCII only" do + Encoding.compatible?("\xff", "\u3042".encode("utf-8")).should be_nil + end + end + + describe "when the first's Encoding is not ASCII compatible" do + before :each do + @str = "abc".force_encoding Encoding::UTF_7 + end + + it "returns nil when the second String is US-ASCII" do + Encoding.compatible?(@str, "def".encode("us-ascii")).should be_nil + end + + it "returns nil when the second String is ASCII-8BIT and ASCII only" do + Encoding.compatible?(@str, "\x7f").should be_nil + end + + it "returns nil when the second String is ASCII-8BIT but not ASCII only" do + Encoding.compatible?(@str, "\xff").should be_nil + end + + it "returns the Encoding when the second's Encoding is not ASCII compatible but the same as the first's Encoding" do + encoding = Encoding.compatible?(@str, "def".force_encoding("utf-7")) + encoding.should == Encoding::UTF_7 + end + end + + describe "when the first's Encoding is invalid" do + before :each do + @str = "\xff".force_encoding Encoding::UTF_8 + end + + it "returns the first's Encoding when the second's Encoding is US-ASCII" do + Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::UTF_8 + end + + it "returns the first's Encoding when the second String is ASCII only" do + Encoding.compatible?(@str, "\x7f").should == Encoding::UTF_8 + end + + it "returns nil when the second's Encoding is ASCII-8BIT but not ASCII only" do + Encoding.compatible?(@str, "\xff").should be_nil + end + + it "returns nil when the second's Encoding is invalid and ASCII only" do + Encoding.compatible?(@str, "\x7f".force_encoding("utf-16be")).should be_nil + end + + it "returns nil when the second's Encoding is invalid and not ASCII only" do + Encoding.compatible?(@str, "\xff".force_encoding("utf-16be")).should be_nil + end + + it "returns the Encoding when the second's Encoding is invalid but the same as the first" do + Encoding.compatible?(@str, @str).should == Encoding::UTF_8 + end + end + + describe "when the first String is empty and the second is not" do + describe "and the first's Encoding is ASCII compatible" do + before :each do + @str = "".force_encoding("utf-8") + end + + it "returns the first's encoding when the second String is ASCII only" do + Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::UTF_8 + end + + it "returns the second's encoding when the second String is not ASCII only" do + Encoding.compatible?(@str, "def".encode("utf-32le")).should == Encoding::UTF_32LE + end + end + + describe "when the first's Encoding is not ASCII compatible" do + before :each do + @str = "".force_encoding Encoding::UTF_7 + end + + it "returns the second string's encoding" do + Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::US_ASCII + end + end + end + + describe "when the second String is empty" do + before :each do + @str = "abc".force_encoding("utf-7") + end + + it "returns the first Encoding" do + Encoding.compatible?(@str, "").should == Encoding::UTF_7 + end + end + end + + describe "Encoding.compatible? String, Regexp" do + it "returns US-ASCII if both are US-ASCII" do + str = "abc".force_encoding("us-ascii") + Encoding.compatible?(str, /abc/).should == Encoding::US_ASCII + end + + it "returns the String's Encoding if it is not US-ASCII but both are ASCII only" do + [ [Encoding, "abc", Encoding::ASCII_8BIT], + [Encoding, "abc".encode("utf-8"), Encoding::UTF_8], + [Encoding, "abc".encode("euc-jp"), Encoding::EUC_JP], + [Encoding, "abc".encode("shift_jis"), Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, /abc/) + end + + it "returns the String's Encoding if the String is not ASCII only" do + [ [Encoding, "\xff", Encoding::ASCII_8BIT], + [Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8], + [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP], + [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, /abc/) + end + end + + describe "Encoding.compatible? String, Symbol" do + it "returns US-ASCII if both are ASCII only" do + str = "abc".force_encoding("us-ascii") + Encoding.compatible?(str, :abc).should == Encoding::US_ASCII + end + + it "returns the String's Encoding if it is not US-ASCII but both are ASCII only" do + [ [Encoding, "abc", Encoding::ASCII_8BIT], + [Encoding, "abc".encode("utf-8"), Encoding::UTF_8], + [Encoding, "abc".encode("euc-jp"), Encoding::EUC_JP], + [Encoding, "abc".encode("shift_jis"), Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, :abc) + end + + it "returns the String's Encoding if the String is not ASCII only" do + [ [Encoding, "\xff", Encoding::ASCII_8BIT], + [Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8], + [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP], + [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, :abc) + end + end + + describe "Encoding.compatible? String, Encoding" do + it "returns nil if the String's encoding is not ASCII compatible" do + Encoding.compatible?("abc".encode("utf-32le"), Encoding::US_ASCII).should be_nil + end + + it "returns nil if the Encoding is not ASCII compatible" do + Encoding.compatible?("abc".encode("us-ascii"), Encoding::UTF_32LE).should be_nil + end + + it "returns the String's encoding if the Encoding is US-ASCII" do + [ [Encoding, "\xff", Encoding::ASCII_8BIT], + [Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8], + [Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP], + [Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, Encoding::US_ASCII) + end + + it "returns the Encoding if the String's encoding is ASCII compatible and the String is ASCII only" do + str = "abc".encode("utf-8") + + Encoding.compatible?(str, Encoding::ASCII_8BIT).should == Encoding::ASCII_8BIT + Encoding.compatible?(str, Encoding::UTF_8).should == Encoding::UTF_8 + Encoding.compatible?(str, Encoding::EUC_JP).should == Encoding::EUC_JP + Encoding.compatible?(str, Encoding::Shift_JIS).should == Encoding::Shift_JIS + end + + it "returns nil if the String's encoding is ASCII compatible but the string is not ASCII only" do + Encoding.compatible?("\u3042".encode("utf-8"), Encoding::ASCII_8BIT).should be_nil + end + end + + describe "Encoding.compatible? Regexp, String" do + it "returns US-ASCII if both are US-ASCII" do + str = "abc".force_encoding("us-ascii") + Encoding.compatible?(/abc/, str).should == Encoding::US_ASCII + end + + end + + describe "Encoding.compatible? Regexp, Regexp" do + it "returns US-ASCII if both are US-ASCII" do + Encoding.compatible?(/abc/, /def/).should == Encoding::US_ASCII + end + + it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do + [ [Encoding, Regexp.new("\xff"), Encoding::ASCII_8BIT], + [Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8], + [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP], + [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, /abc/) + end + end + + describe "Encoding.compatible? Regexp, Symbol" do + it "returns US-ASCII if both are US-ASCII" do + Encoding.compatible?(/abc/, :def).should == Encoding::US_ASCII + end + + it "returns the first's Encoding if it is not US-ASCII and not ASCII only" do + [ [Encoding, Regexp.new("\xff"), Encoding::ASCII_8BIT], + [Encoding, Regexp.new("\u3042".encode("utf-8")), Encoding::UTF_8], + [Encoding, Regexp.new("\xa4\xa2".force_encoding("euc-jp")), Encoding::EUC_JP], + [Encoding, Regexp.new("\x82\xa0".force_encoding("shift_jis")), Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, /abc/) + end + end + + describe "Encoding.compatible? Symbol, String" do + it "returns US-ASCII if both are ASCII only" do + str = "abc".force_encoding("us-ascii") + Encoding.compatible?(str, :abc).should == Encoding::US_ASCII + end + end + + describe "Encoding.compatible? Symbol, Regexp" do + it "returns US-ASCII if both are US-ASCII" do + Encoding.compatible?(:abc, /def/).should == Encoding::US_ASCII + end + + it "returns the Regexp's Encoding if it is not US-ASCII and not ASCII only" do + a = Regexp.new("\xff") + b = Regexp.new("\u3042".encode("utf-8")) + c = Regexp.new("\xa4\xa2".force_encoding("euc-jp")) + d = Regexp.new("\x82\xa0".force_encoding("shift_jis")) + + [ [Encoding, :abc, a, Encoding::ASCII_8BIT], + [Encoding, :abc, b, Encoding::UTF_8], + [Encoding, :abc, c, Encoding::EUC_JP], + [Encoding, :abc, d, Encoding::Shift_JIS], + ].should be_computed_by(:compatible?) + end + end + + describe "Encoding.compatible? Symbol, Symbol" do + it "returns US-ASCII if both are US-ASCII" do + Encoding.compatible?(:abc, :def).should == Encoding::US_ASCII + end + + it "returns the first's Encoding if it is not ASCII only" do + [ [Encoding, "\xff".to_sym, Encoding::ASCII_8BIT], + [Encoding, "\u3042".encode("utf-8").to_sym, Encoding::UTF_8], + [Encoding, "\xa4\xa2".force_encoding("euc-jp").to_sym, Encoding::EUC_JP], + [Encoding, "\x82\xa0".force_encoding("shift_jis").to_sym, Encoding::Shift_JIS], + ].should be_computed_by(:compatible?, :abc) + end + end + + describe "Encoding.compatible? Encoding, Encoding" do + it "returns nil if one of the encodings is a dummy encoding" do + [ [Encoding, Encoding::UTF_7, Encoding::US_ASCII, nil], + [Encoding, Encoding::US_ASCII, Encoding::UTF_7, nil], + [Encoding, Encoding::EUC_JP, Encoding::UTF_7, nil], + [Encoding, Encoding::UTF_7, Encoding::EUC_JP, nil], + [Encoding, Encoding::UTF_7, Encoding::ASCII_8BIT, nil], + [Encoding, Encoding::ASCII_8BIT, Encoding::UTF_7, nil], + ].should be_computed_by(:compatible?) + end + + it "returns nil if one of the encodings is not US-ASCII" do + [ [Encoding, Encoding::UTF_8, Encoding::ASCII_8BIT, nil], + [Encoding, Encoding::ASCII_8BIT, Encoding::UTF_8, nil], + [Encoding, Encoding::ASCII_8BIT, Encoding::EUC_JP, nil], + [Encoding, Encoding::Shift_JIS, Encoding::EUC_JP, nil], + ].should be_computed_by(:compatible?) + end + + it "returns the first if the second is US-ASCII" do + [ [Encoding, Encoding::UTF_8, Encoding::US_ASCII, Encoding::UTF_8], + [Encoding, Encoding::EUC_JP, Encoding::US_ASCII, Encoding::EUC_JP], + [Encoding, Encoding::Shift_JIS, Encoding::US_ASCII, Encoding::Shift_JIS], + [Encoding, Encoding::ASCII_8BIT, Encoding::US_ASCII, Encoding::ASCII_8BIT], + ].should be_computed_by(:compatible?) + end + + it "returns the Encoding if both are the same" do + [ [Encoding, Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8], + [Encoding, Encoding::US_ASCII, Encoding::US_ASCII, Encoding::US_ASCII], + [Encoding, Encoding::ASCII_8BIT, Encoding::ASCII_8BIT, Encoding::ASCII_8BIT], + [Encoding, Encoding::UTF_7, Encoding::UTF_7, Encoding::UTF_7], + ].should be_computed_by(:compatible?) + end + end + + describe "Encoding.compatible? Object, Object" do + it "returns nil for Object, String" do + Encoding.compatible?(Object.new, "abc").should be_nil + end + + it "returns nil for Object, Regexp" do + Encoding.compatible?(Object.new, /./).should be_nil + end + + it "returns nil for Object, Symbol" do + Encoding.compatible?(Object.new, :sym).should be_nil + end + + it "returns nil for String, Object" do + Encoding.compatible?("abc", Object.new).should be_nil + end + + it "returns nil for Regexp, Object" do + Encoding.compatible?(/./, Object.new).should be_nil + end + + it "returns nil for Symbol, Object" do + Encoding.compatible?(:sym, Object.new).should be_nil + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/asciicompat_encoding_spec.rb b/spec/rubyspec/core/encoding/converter/asciicompat_encoding_spec.rb new file mode 100644 index 0000000000..329e09cade --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/asciicompat_encoding_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter.asciicompat_encoding" do + it "accepts an encoding name as a String argument" do + lambda { Encoding::Converter.asciicompat_encoding('UTF-8') }. + should_not raise_error + end + + it "coerces non-String/Encoding objects with #to_str" do + str = mock('string') + str.should_receive(:to_str).at_least(1).times.and_return('string') + Encoding::Converter.asciicompat_encoding(str) + end + + it "accepts an Encoding object as an argument" do + Encoding::Converter. + asciicompat_encoding(Encoding.find("ISO-2022-JP")). + should == Encoding::Converter.asciicompat_encoding("ISO-2022-JP") + end + + it "returns a corresponding ASCII compatible encoding for ASCII-incompatible encodings" do + Encoding::Converter.asciicompat_encoding('UTF-16BE').should == Encoding::UTF_8 + Encoding::Converter.asciicompat_encoding("ISO-2022-JP").should == Encoding.find("stateless-ISO-2022-JP") + end + + it "returns nil when the given encoding is ASCII compatible" do + Encoding::Converter.asciicompat_encoding('ASCII').should be_nil + Encoding::Converter.asciicompat_encoding('UTF-8').should be_nil + end + + it "handles encoding names who resolve to nil encodings" do + internal = Encoding.default_internal + Encoding.default_internal = nil + Encoding::Converter.asciicompat_encoding('internal').should be_nil + Encoding.default_internal = internal + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/constants_spec.rb b/spec/rubyspec/core/encoding/converter/constants_spec.rb new file mode 100644 index 0000000000..16eb60b4ab --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/constants_spec.rb @@ -0,0 +1,133 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter::INVALID_MASK" do + it "exists" do + Encoding::Converter.should have_constant(:INVALID_MASK) + end + + it "has a Fixnum value" do + Encoding::Converter::INVALID_MASK.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::INVALID_REPLACE" do + it "exists" do + Encoding::Converter.should have_constant(:INVALID_REPLACE) + end + + it "has a Fixnum value" do + Encoding::Converter::INVALID_REPLACE.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::UNDEF_MASK" do + it "exists" do + Encoding::Converter.should have_constant(:UNDEF_MASK) + end + + it "has a Fixnum value" do + Encoding::Converter::UNDEF_MASK.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::UNDEF_REPLACE" do + it "exists" do + Encoding::Converter.should have_constant(:UNDEF_REPLACE) + end + + it "has a Fixnum value" do + Encoding::Converter::UNDEF_REPLACE.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::UNDEF_HEX_CHARREF" do + it "exists" do + Encoding::Converter.should have_constant(:UNDEF_HEX_CHARREF) + end + + it "has a Fixnum value" do + Encoding::Converter::UNDEF_HEX_CHARREF.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::PARTIAL_INPUT" do + it "exists" do + Encoding::Converter.should have_constant(:PARTIAL_INPUT) + end + + it "has a Fixnum value" do + Encoding::Converter::PARTIAL_INPUT.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::AFTER_OUTPUT" do + it "exists" do + Encoding::Converter.should have_constant(:AFTER_OUTPUT) + end + + it "has a Fixnum value" do + Encoding::Converter::AFTER_OUTPUT.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR" do + it "exists" do + Encoding::Converter.should have_constant(:UNIVERSAL_NEWLINE_DECORATOR) + end + + it "has a Fixnum value" do + Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::CRLF_NEWLINE_DECORATOR" do + it "exists" do + Encoding::Converter.should have_constant(:CRLF_NEWLINE_DECORATOR) + end + + it "has a Fixnum value" do + Encoding::Converter::CRLF_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::CR_NEWLINE_DECORATOR" do + it "exists" do + Encoding::Converter.should have_constant(:CR_NEWLINE_DECORATOR) + end + + it "has a Fixnum value" do + Encoding::Converter::CR_NEWLINE_DECORATOR.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::XML_TEXT_DECORATOR" do + it "exists" do + Encoding::Converter.should have_constant(:XML_TEXT_DECORATOR) + end + + it "has a Fixnum value" do + Encoding::Converter::XML_TEXT_DECORATOR.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::XML_ATTR_CONTENT_DECORATOR" do + it "exists" do + Encoding::Converter.should have_constant(:XML_ATTR_CONTENT_DECORATOR) + end + + it "has a Fixnum value" do + Encoding::Converter::XML_ATTR_CONTENT_DECORATOR.should be_an_instance_of(Fixnum) + end + end + + describe "Encoding::Converter::XML_ATTR_QUOTE_DECORATOR" do + it "exists" do + Encoding::Converter.should have_constant(:XML_ATTR_QUOTE_DECORATOR) + end + + it "has a Fixnum value" do + Encoding::Converter::XML_ATTR_QUOTE_DECORATOR.should be_an_instance_of(Fixnum) + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/convert_spec.rb b/spec/rubyspec/core/encoding/converter/convert_spec.rb new file mode 100644 index 0000000000..588d659ceb --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/convert_spec.rb @@ -0,0 +1,47 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#convert" do + it "returns a String" do + ec = Encoding::Converter.new('ascii', 'utf-8') + ec.convert('glark').should be_an_instance_of(String) + end + + it "sets the encoding of the result to the target encoding" do + ec = Encoding::Converter.new('ascii', 'utf-8') + str = 'glark'.force_encoding('ascii') + ec.convert(str).encoding.should == Encoding::UTF_8 + end + + it "transcodes the given String to the target encoding" do + ec = Encoding::Converter.new("utf-8", "euc-jp") + ec.convert("\u3042".force_encoding('UTF-8')).should == \ + "\xA4\xA2".force_encoding('EUC-JP') + end + + it "allows Strings of different encodings to the source encoding" do + ec = Encoding::Converter.new('ascii', 'utf-8') + str = 'glark'.force_encoding('SJIS') + ec.convert(str).encoding.should == Encoding::UTF_8 + end + + it "reuses the given encoding pair if called multiple times" do + ec = Encoding::Converter.new('ascii', 'SJIS') + ec.convert('a'.force_encoding('ASCII')).should == 'a'.force_encoding('SJIS') + ec.convert('b'.force_encoding('ASCII')).should == 'b'.force_encoding('SJIS') + end + + it "raises UndefinedConversionError if the String contains characters invalid for the target encoding" do + ec = Encoding::Converter.new('UTF-8', Encoding.find('macCyrillic')) + lambda { ec.convert("\u{6543}".force_encoding('UTF-8')) }.should \ + raise_error(Encoding::UndefinedConversionError) + end + + it "raises an ArgumentError if called on a finished stream" do + ec = Encoding::Converter.new('UTF-8', Encoding.find('macCyrillic')) + ec.finish + lambda { ec.convert("\u{65}") }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/convpath_spec.rb b/spec/rubyspec/core/encoding/converter/convpath_spec.rb new file mode 100644 index 0000000000..679b894f58 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/convpath_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#convpath" do + before :all do + @perms = Encoding.name_list.permutation(2).map do |pair| + Encoding::Converter.new(pair.first, pair.last) rescue nil + end.compact.map{|ec| ec.convpath} + end + + it "returns an Array" do + ec = Encoding::Converter.new('ASCII', 'EUC-JP') + ec.convpath.should be_an_instance_of(Array) + end + + it "returns each encoding pair as a sub-Array" do + ec = Encoding::Converter.new('ASCII', 'EUC-JP') + ec.convpath.first.should be_an_instance_of(Array) + ec.convpath.first.size.should == 2 + end + + it "returns each encoding as an Encoding object" do + ec = Encoding::Converter.new('ASCII', 'EUC-JP') + ec.convpath.first.first.should be_an_instance_of(Encoding) + ec.convpath.first.last.should be_an_instance_of(Encoding) + end + + it "returns multiple encoding pairs when direct conversion is impossible" do + ec = Encoding::Converter.new('ascii','Big5') + ec.convpath.size.should == 2 + ec.convpath.first.first.should == Encoding::US_ASCII + ec.convpath.first.last.should == ec.convpath.last.first + ec.convpath.last.last.should == Encoding::Big5 + end + + it "sets the last element of each pair to the first element of the next" do + @perms.each do |convpath| + next if convpath.size == 1 + convpath.each_with_index do |pair, idx| + break if idx == convpath.size - 1 + pair.last.should == convpath[idx+1].first + end + end + end + + it "only lists a source encoding once" do + @perms.each do |convpath| + next if convpath.size < 2 + seen = Hash.new(false) + convpath.each_with_index do |pair, idx| + seen.key?(pair.first).should be_false if idx > 0 + seen[pair.first] = true + end + end + end + + it "indicates if crlf_newline conversion would occur" do + ec = Encoding::Converter.new("ISo-8859-1", "EUC-JP", {crlf_newline: true}) + ec.convpath.last.should == "crlf_newline" + + ec = Encoding::Converter.new("ASCII", "UTF-8", {crlf_newline: false}) + ec.convpath.last.should_not == "crlf_newline" + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/destination_encoding_spec.rb b/spec/rubyspec/core/encoding/converter/destination_encoding_spec.rb new file mode 100644 index 0000000000..830e6d2178 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/destination_encoding_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#destination_encoding" do + it "returns the destination encoding as an Encoding object" do + ec = Encoding::Converter.new('ASCII','Big5') + ec.destination_encoding.should == Encoding::BIG5 + + ec = Encoding::Converter.new('SJIS','EUC-JP') + ec.destination_encoding.should == Encoding::EUC_JP + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/finish_spec.rb b/spec/rubyspec/core/encoding/converter/finish_spec.rb new file mode 100644 index 0000000000..86097357f4 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/finish_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#finish" do + before :each do + @ec = Encoding::Converter.new("utf-8", "iso-2022-jp") + end + + it "returns a String" do + @ec.convert('foo') + @ec.finish.should be_an_instance_of(String) + end + + it "returns an empty String if there is nothing more to convert" do + @ec.convert("glark") + @ec.finish.should == "" + end + + it "returns the last part of the converted String if it hasn't already" do + @ec.convert("\u{9999}").should == "\e$B9a".force_encoding('iso-2022-jp') + @ec.finish.should == "\e(B".force_encoding('iso-2022-jp') + end + + it "returns a String in the destination encoding" do + @ec.convert("glark") + @ec.finish.encoding.should == Encoding::ISO2022_JP + end + + it "returns an empty String if self was not given anything to convert" do + @ec.finish.should == "" + end + + it "returns an empty String on subsequent invocations" do + @ec.finish.should == "" + @ec.finish.should == "" + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/insert_output_spec.rb b/spec/rubyspec/core/encoding/converter/insert_output_spec.rb new file mode 100644 index 0000000000..bc9a56ba45 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/insert_output_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Encoding::Converter#insert_output" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/encoding/converter/inspect_spec.rb b/spec/rubyspec/core/encoding/converter/inspect_spec.rb new file mode 100644 index 0000000000..b8216176cf --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/inspect_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Encoding::Converter#inspect" do + it "includes the source and destination encodings in the return value" do + source = Encoding::UTF_8 + destination = Encoding::UTF_16LE + + output = "#" + + x = Encoding::Converter.new(source, destination) + x.inspect.should == output + end +end diff --git a/spec/rubyspec/core/encoding/converter/last_error_spec.rb b/spec/rubyspec/core/encoding/converter/last_error_spec.rb new file mode 100644 index 0000000000..8465935368 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/last_error_spec.rb @@ -0,0 +1,85 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#last_error" do + it "returns nil when the no conversion has been attempted" do + ec = Encoding::Converter.new('ascii','utf-8') + ec.last_error.should be_nil + end + + it "returns nil when the last conversion did not produce an error" do + ec = Encoding::Converter.new('ascii','utf-8') + ec.convert('a'.force_encoding('ascii')) + ec.last_error.should be_nil + end + + it "returns nil when #primitive_convert last returned :destination_buffer_full" do + ec = Encoding::Converter.new("utf-8", "iso-2022-jp") + ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false) \ + .should == :destination_buffer_full + ec.last_error.should be_nil + end + + it "returns nil when #primitive_convert last returned :finished" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished + ec.last_error.should be_nil + end + + it "returns nil if the last conversion succeeded but the penultimate failed" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence + ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished + ec.last_error.should be_nil + end + + it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :invalid_byte_sequence" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence + ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError) + end + + it "returns an Encoding::UndefinedConversionError when #primitive_convert last returned :undefined_conversion" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + ec.primitive_convert("\u{9876}","").should == :undefined_conversion + ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError) + end + + it "returns an Encoding::InvalidByteSequenceError when #primitive_convert last returned :incomplete_input" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + ec.primitive_convert("\xa4", "", nil, 10).should == :incomplete_input + ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError) + end + + it "returns an Encoding::InvalidByteSequenceError when the last call to #convert produced one" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + exception = nil + lambda do + begin + ec.convert("\xf1abcd") + rescue Encoding::InvalidByteSequenceError => e + exception = e + raise e + end + end.should raise_error(Encoding::InvalidByteSequenceError) + ec.last_error.should be_an_instance_of(Encoding::InvalidByteSequenceError) + ec.last_error.message.should == exception.message + end + + it "returns an Encoding::UndefinedConversionError when the last call to #convert produced one" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + exception = nil + lambda do + begin + ec.convert("\u{9899}") + rescue Encoding::UndefinedConversionError => e + exception = e + raise e + end + end.should raise_error(Encoding::UndefinedConversionError) + ec.last_error.should be_an_instance_of(Encoding::UndefinedConversionError) + ec.last_error.message.should == exception.message + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/new_spec.rb b/spec/rubyspec/core/encoding/converter/new_spec.rb new file mode 100644 index 0000000000..d228c80a18 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/new_spec.rb @@ -0,0 +1,121 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter.new" do + it "accepts a String for the source encoding" do + conv = Encoding::Converter.new("us-ascii", "utf-8") + conv.source_encoding.should == Encoding::US_ASCII + end + + it "accepts a String for the destination encoding" do + conv = Encoding::Converter.new("us-ascii", "utf-8") + conv.destination_encoding.should == Encoding::UTF_8 + end + + it "accepts an Encoding object for the source encoding" do + conv = Encoding::Converter.new(Encoding::US_ASCII, "utf-8") + conv.source_encoding.should == Encoding::US_ASCII + end + + it "accepts an Encoding object for the destination encoding" do + conv = Encoding::Converter.new("us-ascii", Encoding::UTF_8) + conv.destination_encoding.should == Encoding::UTF_8 + end + + it "raises an Encoding::ConverterNotFoundError if both encodings are the same" do + lambda do + Encoding::Converter.new "utf-8", "utf-8" + end.should raise_error(Encoding::ConverterNotFoundError) + end + + it "calls #to_str to convert the source encoding argument to an encoding name" do + enc = mock("us-ascii") + enc.should_receive(:to_str).and_return("us-ascii") + conv = Encoding::Converter.new(enc, "utf-8") + conv.source_encoding.should == Encoding::US_ASCII + end + + it "calls #to_str to convert the destination encoding argument to an encoding name" do + enc = mock("utf-8") + enc.should_receive(:to_str).and_return("utf-8") + conv = Encoding::Converter.new("us-ascii", enc) + conv.destination_encoding.should == Encoding::UTF_8 + end + + it "sets replacement from the options Hash" do + conv = Encoding::Converter.new("us-ascii", "utf-8", replace: "fubar") + conv.replacement.should == "fubar" + end + + it "calls #to_hash to convert the options argument to a Hash if not a Fixnum" do + opts = mock("encoding converter options") + opts.should_receive(:to_hash).and_return({ replace: "fubar" }) + conv = Encoding::Converter.new("us-ascii", "utf-8", opts) + conv.replacement.should == "fubar" + end + + it "calls #to_str to convert the replacement object to a String" do + obj = mock("encoding converter replacement") + obj.should_receive(:to_str).and_return("fubar") + conv = Encoding::Converter.new("us-ascii", "utf-8", replace: obj) + conv.replacement.should == "fubar" + end + + it "raises a TypeError if #to_str does not return a String" do + obj = mock("encoding converter replacement") + obj.should_receive(:to_str).and_return(1) + + lambda do + Encoding::Converter.new("us-ascii", "utf-8", replace: obj) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed true for the replacement object" do + lambda do + Encoding::Converter.new("us-ascii", "utf-8", replace: true) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed false for the replacement object" do + lambda do + Encoding::Converter.new("us-ascii", "utf-8", replace: false) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed a Fixnum for the replacement object" do + lambda do + Encoding::Converter.new("us-ascii", "utf-8", replace: 1) + end.should raise_error(TypeError) + end + + it "accepts an empty String for the replacement object" do + conv = Encoding::Converter.new("us-ascii", "utf-8", replace: "") + conv.replacement.should == "" + end + + describe "when passed nil for the replacement object" do + describe "when the destination encoding is not UTF-8" do + it "sets the replacement String to '?'" do + conv = Encoding::Converter.new("us-ascii", "ascii-8bit", replace: nil) + conv.replacement.should == "?" + end + + it "sets the replacement String encoding to US-ASCII" do + conv = Encoding::Converter.new("us-ascii", "ascii-8bit", replace: nil) + conv.replacement.encoding.should == Encoding::US_ASCII + end + + it "sets the replacement String to '\\uFFFD'" do + conv = Encoding::Converter.new("us-ascii", "utf-8", replace: nil) + conv.replacement.should == "\u{fffd}".force_encoding("utf-8") + end + + it "sets the replacement String encoding to UTF-8" do + conv = Encoding::Converter.new("us-ascii", "utf-8", replace: nil) + conv.replacement.encoding.should == Encoding::UTF_8 + end + end + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/primitive_convert_spec.rb b/spec/rubyspec/core/encoding/converter/primitive_convert_spec.rb new file mode 100644 index 0000000000..b9d6288bb2 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/primitive_convert_spec.rb @@ -0,0 +1,213 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#primitive_convert" do + before :each do + @ec = Encoding::Converter.new("utf-8", "iso-8859-1") + end + + it "accepts a nil source buffer" do + lambda { @ec.primitive_convert(nil,"") }.should_not raise_error + end + + it "accepts a String as the source buffer" do + lambda { @ec.primitive_convert("","") }.should_not raise_error + end + + it "accepts nil for the destination byte offset" do + lambda { @ec.primitive_convert("","", nil) }.should_not raise_error + end + + it "accepts an integer for the destination byte offset" do + lambda { @ec.primitive_convert("","a", 1) }.should_not raise_error + end + + it "calls #to_int to convert the destination byte offset" do + offset = mock("encoding primitive_convert destination byte offset") + offset.should_receive(:to_int).and_return(2) + @ec.primitive_convert("abc", result = " ", offset).should == :finished + result.should == " abc" + end + + it "raises an ArgumentError if the destination byte offset is greater than the bytesize of the destination buffer" do + lambda { @ec.primitive_convert("","am", 0) }.should_not raise_error + lambda { @ec.primitive_convert("","am", 1) }.should_not raise_error + lambda { @ec.primitive_convert("","am", 2) }.should_not raise_error + lambda { @ec.primitive_convert("","am", 3) }.should raise_error(ArgumentError) + end + + it "uses the destination byte offset to determine where to write the result in the destination buffer" do + dest = "aa" + @ec.primitive_convert("b",dest, nil, 0) + dest.should == "aa" + + @ec.primitive_convert("b",dest, nil, 1) + dest.should == "aab" + + @ec.primitive_convert("b",dest, nil, 2) + dest.should == "aabbb" + end + + it "accepts nil for the destination bytesize" do + lambda { @ec.primitive_convert("","", nil, nil) }.should_not raise_error + end + + it "accepts an integer for the destination bytesize" do + lambda { @ec.primitive_convert("","", nil, 0) }.should_not raise_error + end + + it "allows a destination bytesize value greater than the bytesize of the source buffer" do + lambda { @ec.primitive_convert("am","", nil, 3) }.should_not raise_error + end + + it "allows a destination bytesize value less than the bytesize of the source buffer" do + lambda { @ec.primitive_convert("am","", nil, 1) }.should_not raise_error + end + + it "calls #to_int to convert the destination byte size" do + size = mock("encoding primitive_convert destination byte size") + size.should_receive(:to_int).and_return(2) + @ec.primitive_convert("abc", result = " ", 0, size).should == :destination_buffer_full + result.should == "ab" + end + + it "uses destination bytesize as the maximum bytesize of the destination buffer" do + dest = "" + @ec.primitive_convert("glark", dest, nil, 1) + dest.bytesize.should == 1 + end + + it "allows a destination buffer of unlimited size if destination bytesize is nil" do + source = "glark".force_encoding('utf-8') + dest = "" + @ec.primitive_convert("glark", dest, nil, nil) + dest.bytesize.should == source.bytesize + end + + it "accepts an options hash" do + @ec.primitive_convert("","",nil,nil, {after_output: true}).should == :finished + end + + it "sets the destination buffer's encoding to the destination encoding if the conversion suceeded" do + dest = "".force_encoding('utf-8') + dest.encoding.should == Encoding::UTF_8 + @ec.primitive_convert("\u{98}",dest).should == :finished + dest.encoding.should == Encoding::ISO_8859_1 + end + + it "sets the destination buffer's encoding to the destination encoding if the conversion failed" do + dest = "".force_encoding('utf-8') + dest.encoding.should == Encoding::UTF_8 + @ec.primitive_convert("\u{9878}",dest).should == :undefined_conversion + dest.encoding.should == Encoding::ISO_8859_1 + end + + it "removes the undefined part from the source buffer when returning :undefined_conversion" do + dest = "".force_encoding('utf-8') + s = "\u{9878}abcd" + @ec.primitive_convert(s, dest).should == :undefined_conversion + + s.should == "abcd" + end + + it "returns :incomplete_input when source buffer ends unexpectedly and :partial_input isn't specified" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + ec.primitive_convert("\xa4", "", nil, nil, partial_input: false).should == :incomplete_input + end + + it "clears the source buffer when returning :incomplete_input" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + s = "\xa4" + ec.primitive_convert(s, "").should == :incomplete_input + + s.should == "" + end + + it "returns :source_buffer_empty when source buffer ends unexpectedly and :partial_input is true" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + ec.primitive_convert("\xa4", "", nil, nil, partial_input: true).should == :source_buffer_empty + end + + it "clears the source buffer when returning :source_buffer_empty" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + s = "\xa4" + ec.primitive_convert(s, "", nil, nil, partial_input: true).should == :source_buffer_empty + + s.should == "" + end + + it "returns :undefined_conversion when a character in the source buffer is not representable in the output encoding" do + @ec.primitive_convert("\u{9876}","").should == :undefined_conversion + end + + it "returns :invalid_byte_sequence when an invalid byte sequence was found in the source buffer" do + @ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence + end + + it "removes consumed and erroneous bytes from the source buffer when returning :invalid_byte_sequence" do + ec = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC) + s = "\xC3\xA1\x80\x80\xC3\xA1".force_encoding("utf-8") + dest = "".force_encoding("utf-8") + ec.primitive_convert(s, dest) + + s.should == "\x80\xC3\xA1".force_encoding("utf-8") + end + + it "returns :finished when the conversion succeeded" do + @ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished + end + + it "clears the source buffer when returning :finished" do + s = "glark".force_encoding('utf-8') + @ec.primitive_convert(s, "").should == :finished + + s.should == "" + end + + it "returns :destination_buffer_full when the destination buffer is too small" do + ec = Encoding::Converter.new("utf-8", "iso-2022-jp") + source = "\u{9999}" + destination_bytesize = source.bytesize - 1 + ec.primitive_convert(source, "", 0, destination_bytesize) \ + .should == :destination_buffer_full + source.should == "" + end + + it "clears the source buffer when returning :destination_buffer_full" do + ec = Encoding::Converter.new("utf-8", "iso-2022-jp") + s = "\u{9999}" + destination_bytesize = s.bytesize - 1 + ec.primitive_convert(s, "", 0, destination_bytesize).should == :destination_buffer_full + + s.should == "" + end + + it "keeps removing invalid bytes from the source buffer" do + ec = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC) + s = "\x80\x80\x80" + dest = "".force_encoding(Encoding::UTF_8_MAC) + + ec.primitive_convert(s, dest) + s.should == "\x80\x80" + ec.primitive_convert(s, dest) + s.should == "\x80" + ec.primitive_convert(s, dest) + s.should == "" + end + + it "reuses read-again bytes after the first error" do + s = "\xf1abcd" + dest = "" + + @ec.primitive_convert(s, dest).should == :invalid_byte_sequence + s.should == "bcd" + @ec.primitive_errinfo[4].should == "a" + + @ec.primitive_convert(s, dest).should == :finished + s.should == "" + + dest.should == "abcd" + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/primitive_errinfo_spec.rb b/spec/rubyspec/core/encoding/converter/primitive_errinfo_spec.rb new file mode 100644 index 0000000000..f92c95c6d5 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/primitive_errinfo_spec.rb @@ -0,0 +1,72 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#primitive_errinfo" do + it "returns [:source_buffer_empty,nil,nil,nil,nil] when no conversion has been attempted" do + ec = Encoding::Converter.new('ascii','utf-8') + ec.primitive_errinfo.should == [:source_buffer_empty, nil, nil, nil, nil] + end + + it "returns [:finished,nil,nil,nil,nil] when #primitive_convert last returned :finished" do + ec = Encoding::Converter.new('ascii','utf-8') + ec.primitive_convert("a","").should == :finished + ec.primitive_errinfo.should == [:finished, nil, nil, nil, nil] + end + + it "returns [:source_buffer_empty,nil,nil,nil, nil] when #convert last succeeded" do + ec = Encoding::Converter.new('ascii','utf-8') + ec.convert("a".force_encoding('ascii')).should == "a".\ + force_encoding('utf-8') + ec.primitive_errinfo.should == [:source_buffer_empty, nil, nil, nil, nil] + end + + it "returns [:destination_buffer_full,nil,nil,nil,nil] when #primitive_convert last returned :destination_buffer_full" do + ec = Encoding::Converter.new("utf-8", "iso-2022-jp") + ec.primitive_convert("\u{9999}", "", 0, 0, partial_input: false) \ + .should == :destination_buffer_full + ec.primitive_errinfo.should == [:destination_buffer_full, nil, nil, nil, nil] + end + + it "returns the status of the last primitive conversion, even if it was successful and the previous one wasn't" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence + ec.primitive_convert("glark".force_encoding('utf-8'),"").should == :finished + ec.primitive_errinfo.should == [:finished, nil, nil, nil, nil] + end + + it "returns the state, source encoding, target encoding, and the erroneous bytes when #primitive_convert last returned :undefined_conversion" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + ec.primitive_convert("\u{9876}","").should == :undefined_conversion + ec.primitive_errinfo.should == + [:undefined_conversion, "UTF-8", "ISO-8859-1", "\xE9\xA1\xB6", ""] + end + + it "returns the state, source encoding, target encoding, and erroneous bytes when #primitive_convert last returned :incomplete_input" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + ec.primitive_convert("\xa4", "", nil, 10).should == :incomplete_input + ec.primitive_errinfo.should == [:incomplete_input, "EUC-JP", "UTF-8", "\xA4", ""] + end + + it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #primitive_convert last returned :invalid_byte_sequence" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + ec.primitive_convert("\xf1abcd","").should == :invalid_byte_sequence + ec.primitive_errinfo.should == + [:invalid_byte_sequence, "UTF-8", "ISO-8859-1", "\xF1", "a"] + end + + it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #convert last raised InvalidByteSequenceError" do + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + lambda { ec.convert("\xf1abcd") }.should raise_error(Encoding::InvalidByteSequenceError) + ec.primitive_errinfo.should == + [:invalid_byte_sequence, "UTF-8", "ISO-8859-1", "\xF1", "a"] + end + + it "returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #finish last raised InvalidByteSequenceError" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + ec.convert("\xa4") + lambda { ec.finish }.should raise_error(Encoding::InvalidByteSequenceError) + ec.primitive_errinfo.should == [:incomplete_input, "EUC-JP", "UTF-8", "\xA4", ""] + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/putback_spec.rb b/spec/rubyspec/core/encoding/converter/putback_spec.rb new file mode 100644 index 0000000000..69ce59e89b --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/putback_spec.rb @@ -0,0 +1,50 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#putback" do + before :each do + @ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + @ret = @ec.primitive_convert(@src="abc\xa1def", @dst="", nil, 10) + end + + it "returns a String" do + @ec.putback.should be_an_instance_of(String) + end + + it "returns a String in the source encoding" do + @ec.putback.encoding.should == Encoding::EUC_JP + end + + it "returns the bytes buffered due to an :invalid_byte_sequence error" do + @ret.should == :invalid_byte_sequence + @ec.putback.should == 'd' + @ec.primitive_errinfo.last.should == 'd' + end + + it "allows conversion to be resumed after an :invalid_byte_sequence" do + @src = @ec.putback + @src + @ret = @ec.primitive_convert(@src, @dst, nil, 10) + @ret.should == :finished + @dst.should == "abcdef" + @src.should == "" + end + + it "returns an empty String when there are no more bytes to put back" do + @ec.putback + @ec.putback.should == "" + end + + it "accepts an integer argument corresponding to the number of bytes to be put back" do + ec = Encoding::Converter.new("utf-16le", "iso-8859-1") + src = "\x00\xd8\x61\x00" + dst = "" + ec.primitive_convert(src, dst).should == :invalid_byte_sequence + ec.primitive_errinfo.should == + [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"] + ec.putback(1).should == "\x00".force_encoding("utf-16le") + ec.putback.should == "a".force_encoding("utf-16le") + ec.putback.should == "" + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/replacement_spec.rb b/spec/rubyspec/core/encoding/converter/replacement_spec.rb new file mode 100644 index 0000000000..9c25887cd7 --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/replacement_spec.rb @@ -0,0 +1,74 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#replacement" do + it "returns '?' in US-ASCII when the destination encoding is not UTF-8" do + ec = Encoding::Converter.new("utf-8", "us-ascii") + ec.replacement.should == "?" + ec.replacement.encoding.should == Encoding::US_ASCII + + ec = Encoding::Converter.new("utf-8", "sjis") + ec.replacement.should == "?" + ec.replacement.encoding.should == Encoding::US_ASCII + end + + it "returns \\uFFFD when the destination encoding is UTF-8" do + ec = Encoding::Converter.new("us-ascii", "utf-8") + ec.replacement.should == "\u{fffd}".force_encoding('utf-8') + ec.replacement.encoding.should == Encoding::UTF_8 + end + end + + describe "Encoding::Converter#replacement=" do + it "accepts a String argument" do + ec = Encoding::Converter.new("utf-8", "us-ascii") + ec.replacement = "!" + ec.replacement.should == "!" + end + + it "accepts a String argument of arbitrary length" do + ec = Encoding::Converter.new("utf-8", "us-ascii") + ec.replacement = "?!?" * 9999 + ec.replacement.should == "?!?" * 9999 + end + + it "raises a TypeError if assigned a non-String argument" do + ec = Encoding::Converter.new("utf-8", "us-ascii") + lambda { ec.replacement = nil }.should raise_error(TypeError) + end + + it "sets #replacement" do + ec = Encoding::Converter.new("us-ascii", "utf-8") + ec.replacement.should == "\u{fffd}".force_encoding('utf-8') + ec.replacement = '?'.encode('utf-8') + ec.replacement.should == '?'.force_encoding('utf-8') + end + + it "raises an UndefinedConversionError is the argument cannot be converted into the destination encoding" do + ec = Encoding::Converter.new("sjis", "ascii") + utf8_q = "\u{986}".force_encoding('utf-8') + ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion + lambda { ec.replacement = utf8_q }.should \ + raise_error(Encoding::UndefinedConversionError) + end + + it "does not change the replacement character if the argument cannot be converted into the destination encoding" do + ec = Encoding::Converter.new("sjis", "ascii") + utf8_q = "\u{986}".force_encoding('utf-8') + ec.primitive_convert(utf8_q.dup, "").should == :undefined_conversion + lambda { ec.replacement = utf8_q }.should \ + raise_error(Encoding::UndefinedConversionError) + ec.replacement.should == "?".force_encoding('us-ascii') + end + + it "uses the replacement character" do + ec = Encoding::Converter.new("utf-8", "us-ascii", :invalid => :replace, :undef => :replace) + ec.replacement = "!" + dest = "" + status = ec.primitive_convert "中文123", dest + + status.should == :finished + dest.should == "!!123" + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/search_convpath_spec.rb b/spec/rubyspec/core/encoding/converter/search_convpath_spec.rb new file mode 100644 index 0000000000..c04eeb98ad --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/search_convpath_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter.search_convpath" do + before :all do + @perms = Encoding.name_list.permutation(2).map do |pair| + Encoding::Converter.search_convpath(pair.first, pair.last) rescue [] + end + end + + it "returns an Array" do + Encoding::Converter.search_convpath('ASCII', 'EUC-JP').\ + should be_an_instance_of(Array) + end + + it "returns each encoding pair as a sub-Array" do + cp = Encoding::Converter.search_convpath('ASCII', 'EUC-JP') + cp.first.should be_an_instance_of(Array) + cp.first.size.should == 2 + end + + it "returns each encoding as an Encoding object" do + cp = Encoding::Converter.search_convpath('ASCII', 'EUC-JP') + cp.first.first.should be_an_instance_of(Encoding) + cp.first.last.should be_an_instance_of(Encoding) + end + + it "returns multiple encoding pairs when direct conversion is impossible" do + cp = Encoding::Converter.search_convpath('ascii','Big5') + cp.size.should == 2 + cp.first.should == [Encoding::US_ASCII, Encoding::UTF_8] + cp.last.should == [Encoding::UTF_8, Encoding::Big5] + end + + it "sets the last element of each pair to the first element of the next" do + @perms.each do |convpath| + next if convpath.size == 1 + convpath.each_with_index do |pair, idx| + break if idx == convpath.size - 1 + pair.last.should == convpath[idx+1].first + end + end + end + + it "only lists a source encoding once" do + @perms.each do |convpath| + next if convpath.size < 2 + seen = Hash.new(false) + convpath.each_with_index do |pair, idx| + seen.key?(pair.first).should be_false if idx > 0 + seen[pair.first] = true + end + end + end + + it "indicates if crlf_newline conversion would occur" do + cp = Encoding::Converter.search_convpath( + "ISo-8859-1", "EUC-JP", {crlf_newline: true}) + cp.last.should == "crlf_newline" + + cp = Encoding::Converter.search_convpath( + "ASCII", "UTF-8", {crlf_newline: false}) + cp.last.should_not == "crlf_newline" + end + + it "raises an Encoding::ConverterNotFoundError if no conversion path exists" do + lambda do + Encoding::Converter.search_convpath( + Encoding::ASCII_8BIT, Encoding::Emacs_Mule) + end.should raise_error(Encoding::ConverterNotFoundError) + end + end +end diff --git a/spec/rubyspec/core/encoding/converter/source_encoding_spec.rb b/spec/rubyspec/core/encoding/converter/source_encoding_spec.rb new file mode 100644 index 0000000000..acec01502d --- /dev/null +++ b/spec/rubyspec/core/encoding/converter/source_encoding_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::Converter#source_encoding" do + it "returns the source encoding as an Encoding object" do + ec = Encoding::Converter.new('ASCII','Big5') + ec.source_encoding.should == Encoding::US_ASCII + + ec = Encoding::Converter.new('Shift_JIS','EUC-JP') + ec.source_encoding.should == Encoding::SHIFT_JIS + end + end +end diff --git a/spec/rubyspec/core/encoding/default_external_spec.rb b/spec/rubyspec/core/encoding/default_external_spec.rb new file mode 100644 index 0000000000..2b026c793f --- /dev/null +++ b/spec/rubyspec/core/encoding/default_external_spec.rb @@ -0,0 +1,74 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding.default_external" do + before :each do + @original_encoding = Encoding.default_external + end + + after :each do + Encoding.default_external = @original_encoding + end + + it "returns an Encoding object" do + Encoding.default_external.should be_an_instance_of(Encoding) + end + + it "returns the default external encoding" do + Encoding.default_external = Encoding::UTF_8 + Encoding.default_external.should == Encoding::UTF_8 + end + + describe "with command line options" do + it "is not changed by the -U option" do + result = ruby_exe("print Encoding.default_external", options: '-U') + result.should == Encoding.default_external.name + end + + it "returns the encoding specified by '-E external'" do + result = ruby_exe("print Encoding.default_external", options: '-E euc-jp') + result.should == "EUC-JP" + end + + it "returns the encoding specified by '-E external:'" do + result = ruby_exe("print Encoding.default_external", options: '-E Shift_JIS:') + result.should == "Shift_JIS" + end + end + end + + describe "Encoding.default_external=" do + before :each do + @original_encoding = Encoding.default_external + end + + after :each do + Encoding.default_external = @original_encoding + end + + it "sets the default external encoding" do + Encoding.default_external = Encoding::UTF_8 + Encoding.default_external.should == Encoding::UTF_8 + end + + it "can accept a name of an encoding as a String" do + Encoding.default_external = 'Shift_JIS' + Encoding.default_external.should == Encoding::SHIFT_JIS + end + + it "calls #to_s on arguments that are neither Strings nor Encodings" do + string = mock('string') + string.should_receive(:to_str).at_least(1).and_return('US-ASCII') + Encoding.default_external = string + Encoding.default_external.should == Encoding::ASCII + end + + it "raises a TypeError unless the argument is an Encoding or convertible to a String" do + lambda { Encoding.default_external = [] }.should raise_error(TypeError) + end + + it "raises an ArgumentError if the argument is nil" do + lambda { Encoding.default_external = nil }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/encoding/default_internal_spec.rb b/spec/rubyspec/core/encoding/default_internal_spec.rb new file mode 100644 index 0000000000..3234929eec --- /dev/null +++ b/spec/rubyspec/core/encoding/default_internal_spec.rb @@ -0,0 +1,93 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding.default_internal" do + before :each do + @original_encoding = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @original_encoding + end + + it "is nil by default" do + Encoding.default_internal.should be_nil + end + + it "returns an Encoding object if a default internal encoding is set" do + Encoding.default_internal = Encoding::ASCII + Encoding.default_internal.should be_an_instance_of(Encoding) + end + + it "returns nil if no default internal encoding is set" do + Encoding.default_internal = nil + Encoding.default_internal.should be_nil + end + + it "returns the default internal encoding" do + Encoding.default_internal = Encoding::ASCII_8BIT + Encoding.default_internal.should == Encoding::ASCII_8BIT + end + + describe "with command line options" do + it "returns Encoding::UTF_8 if ruby was invoked with -U" do + ruby_exe("print Encoding.default_internal", options: '-U'). + should == 'UTF-8' + end + + it "uses the encoding specified when ruby is invoked with an '-E :internal' argument" do + ruby_exe("print Encoding.default_internal", options: '-E :SHIFT_JIS'). + should == 'Shift_JIS' + end + + it "uses the encoding specified when ruby is invoked with an '-E external:internal' argument" do + ruby_exe("print Encoding.default_internal", options: '-E UTF-8:SHIFT_JIS'). + should == 'Shift_JIS' + end + end + end + + describe "Encoding.default_internal=" do + before :each do + @original_encoding = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @original_encoding + end + + it "sets the default internal encoding" do + Encoding.default_internal = Encoding::SHIFT_JIS + Encoding.default_internal.should == Encoding::SHIFT_JIS + end + + it "can accept a name of an encoding as a String" do + Encoding.default_internal = 'Shift_JIS' + Encoding.default_internal.should == Encoding::SHIFT_JIS + end + + it "calls #to_str to convert an object to a String" do + obj = mock('string') + obj.should_receive(:to_str).at_least(1).times.and_return('ascii') + + Encoding.default_internal = obj + Encoding.default_internal.should == Encoding::ASCII + end + + it "raises a TypeError if #to_str does not return a String" do + obj = mock('string') + obj.should_receive(:to_str).at_least(1).times.and_return(1) + + lambda { Encoding.default_internal = obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed an object not providing #to_str" do + lambda { Encoding.default_internal = mock("encoding") }.should raise_error(TypeError) + end + + it "accepts an argument of nil to unset the default internal encoding" do + Encoding.default_internal = nil + Encoding.default_internal.should be_nil + end + end +end diff --git a/spec/rubyspec/core/encoding/dummy_spec.rb b/spec/rubyspec/core/encoding/dummy_spec.rb new file mode 100644 index 0000000000..7917c71e47 --- /dev/null +++ b/spec/rubyspec/core/encoding/dummy_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding#dummy?" do + it "returns false for proper encodings" do + Encoding::UTF_8.dummy?.should be_false + Encoding::ASCII.dummy?.should be_false + end + + it "returns true for dummy encodings" do + Encoding::ISO_2022_JP.dummy?.should be_true + Encoding::CP50221.dummy?.should be_true + Encoding::UTF_7.dummy?.should be_true + end + end +end diff --git a/spec/rubyspec/core/encoding/find_spec.rb b/spec/rubyspec/core/encoding/find_spec.rb new file mode 100644 index 0000000000..bd195f6a1a --- /dev/null +++ b/spec/rubyspec/core/encoding/find_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding.find" do + before :all do + @encodings = Encoding.aliases.to_a.flatten.uniq + end + + it "returns the corresponding Encoding object if given a valid encoding name" do + @encodings.each do |enc| + Encoding.find(enc).should be_an_instance_of(Encoding) + end + end + + it "returns the corresponding Encoding object if given a valid alias name" do + Encoding.aliases.keys.each do |enc_alias| + Encoding.find(enc_alias).should be_an_instance_of(Encoding) + end + end + + it "raises a TypeError if passed a Symbol" do + lambda { Encoding.find(:"utf-8") }.should raise_error(TypeError) + end + + it "returns the passed Encoding object" do + Encoding.find(Encoding::UTF_8).should == Encoding::UTF_8 + end + + it "accepts encoding names as Strings" do + Encoding.list.each do |enc| + Encoding.find(enc.name).should == enc + end + end + + it "accepts any object as encoding name, if it responds to #to_str" do + obj = Class.new do + attr_writer :encoding_name + def to_str; @encoding_name; end + end.new + + Encoding.list.each do |enc| + obj.encoding_name = enc.name + Encoding.find(obj).should == enc + end + end + + it "is case insensitive" do + @encodings.each do |enc| + Encoding.find(enc.upcase).should == Encoding.find(enc) + end + end + + it "raises an ArgumentError if the given encoding does not exist" do + lambda { Encoding.find('dh2dh278d') }.should raise_error(ArgumentError) + end + + # Not sure how to do a better test, since locale depends on weird platform-specific stuff + it "supports the 'locale' encoding alias" do + enc = Encoding.find('locale') + enc.should_not == nil + end + + it "returns default external encoding for the 'external' encoding alias" do + enc = Encoding.find('external') + enc.should == Encoding.default_external + end + + it "returns default internal encoding for the 'internal' encoding alias" do + enc = Encoding.find('internal') + enc.should == Encoding.default_internal + end + + platform_is_not :windows do + it "uses default external encoding for the 'filesystem' encoding alias" do + enc = Encoding.find('filesystem') + enc.should == Encoding.default_external + end + end + + platform_is :windows do + it "needs to be reviewed for spec completeness" + end + end +end diff --git a/spec/rubyspec/core/encoding/fixtures/classes.rb b/spec/rubyspec/core/encoding/fixtures/classes.rb new file mode 100644 index 0000000000..12e9a4f348 --- /dev/null +++ b/spec/rubyspec/core/encoding/fixtures/classes.rb @@ -0,0 +1,49 @@ +# -*- encoding: binary -*- +module EncodingSpecs + class UndefinedConversionError + def self.exception + ec = Encoding::Converter.new('utf-8','ascii') + begin + ec.convert("\u{8765}") + rescue Encoding::UndefinedConversionError => e + e + end + end + end + + class UndefinedConversionErrorIndirect + def self.exception + ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP") + begin + ec.convert("\xA0") + rescue Encoding::UndefinedConversionError => e + e + end + end + end + + class InvalidByteSequenceError + def self.exception + ec = Encoding::Converter.new("utf-8", "iso-8859-1") + begin + ec.convert("\xf1abcd") + rescue Encoding::InvalidByteSequenceError => e + # Return the exception object and the primitive_errinfo Array + [e, ec.primitive_errinfo] + end + end + end + + class InvalidByteSequenceErrorIndirect + def self.exception + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + begin + ec.convert("abc\xA1\xFFdef") + rescue Encoding::InvalidByteSequenceError => e + # Return the exception object and the discarded bytes reported by + # #primitive_errinfo + [e, ec.primitive_errinfo] + end + end + end +end diff --git a/spec/rubyspec/core/encoding/inspect_spec.rb b/spec/rubyspec/core/encoding/inspect_spec.rb new file mode 100644 index 0000000000..771232e433 --- /dev/null +++ b/spec/rubyspec/core/encoding/inspect_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding#inspect" do + it "returns a String" do + Encoding::UTF_8.inspect.should be_an_instance_of(String) + end + + it "returns # for a non-dummy encoding named 'name'" do + Encoding.list.to_a.reject {|e| e.dummy? }.each do |enc| + enc.inspect.should =~ /#/ + end + end + + it "returns # for a dummy encoding named 'name'" do + Encoding.list.to_a.select {|e| e.dummy? }.each do |enc| + enc.inspect.should =~ /#/ + end + end + end +end diff --git a/spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb new file mode 100644 index 0000000000..790dd18655 --- /dev/null +++ b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_name_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do + before :each do + @exception, = EncodingSpecs::InvalidByteSequenceError.exception + @exception2, = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception + end + + it "returns a String" do + @exception.destination_encoding_name.should be_an_instance_of(String) + @exception2.destination_encoding_name.should be_an_instance_of(String) + end + + it "is equal to the destination encoding name of the object that raised it" do + @exception.destination_encoding_name.should == "ISO-8859-1" + @exception2.destination_encoding_name.should == "UTF-8" + end + end +end diff --git a/spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb new file mode 100644 index 0000000000..981a62424e --- /dev/null +++ b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/destination_encoding_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::InvalidByteSequenceError#destination_encoding" do + before :each do + @exception, = EncodingSpecs::InvalidByteSequenceError.exception + @exception2, = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception + end + + it "returns an Encoding object" do + @exception.destination_encoding.should be_an_instance_of(Encoding) + @exception2.destination_encoding.should be_an_instance_of(Encoding) + end + + it "is equal to the destination encoding of the object that raised it" do + @exception.destination_encoding.should == Encoding::ISO_8859_1 + @exception2.destination_encoding.should == Encoding::UTF_8 + end + end +end diff --git a/spec/rubyspec/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb new file mode 100644 index 0000000000..633ad2e1f7 --- /dev/null +++ b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb @@ -0,0 +1,32 @@ +# -*- encoding: binary -*- +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::InvalidByteSequenceError#error_bytes" do + before :each do + @exception, @errinfo = EncodingSpecs::InvalidByteSequenceError.exception + @exception2, @errinfo2 = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception + end + + it "returns a String" do + @exception.error_bytes.should be_an_instance_of(String) + @exception2.error_bytes.should be_an_instance_of(String) + end + + it "returns the bytes that caused the exception" do + @exception.error_bytes.size.should == 1 + @exception.error_bytes.should == "\xF1" + @exception.error_bytes.should == @errinfo[-2] + + @exception2.error_bytes.size.should == 1 + @exception2.error_bytes.should == "\xA1" + @exception2.error_bytes.should == @errinfo2[-2] + end + + it "uses ASCII-8BIT as the encoding" do + @exception.error_bytes.encoding.should == Encoding::ASCII_8BIT + + @exception2.error_bytes.encoding.should == Encoding::ASCII_8BIT + end + end +end diff --git a/spec/rubyspec/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb new file mode 100644 index 0000000000..c79a6663e2 --- /dev/null +++ b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb @@ -0,0 +1,31 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding::InvalidByteSequenceError#incomplete_input?" do + + it "returns nil by default" do + Encoding::InvalidByteSequenceError.new.incomplete_input?.should be_nil + end + + it "returns true if #primitive_convert returned :incomplete_input for the same data" do + ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1") + ec.primitive_convert("\xA1",'').should == :incomplete_input + begin + ec.convert("\xA1") + rescue Encoding::InvalidByteSequenceError => e + e.incomplete_input?.should be_true + end + end + + it "returns false if #primitive_convert returned :invalid_byte_sequence for the same data" do + ec = Encoding::Converter.new("ascii", "utf-8") + ec.primitive_convert("\xfffffffff",'').should == :invalid_byte_sequence + begin + ec.convert("\xfffffffff") + rescue Encoding::InvalidByteSequenceError => e + e.incomplete_input?.should be_false + end + end + end +end diff --git a/spec/rubyspec/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb new file mode 100644 index 0000000000..31408a4320 --- /dev/null +++ b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb @@ -0,0 +1,32 @@ +# -*- encoding: binary -*- +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::InvalidByteSequenceError#readagain_bytes" do + before :each do + @exception, @errinfo = EncodingSpecs::InvalidByteSequenceError.exception + @exception2, @errinfo2 = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception + end + + it "returns a String" do + @exception.readagain_bytes.should be_an_instance_of(String) + @exception2.readagain_bytes.should be_an_instance_of(String) + end + + it "returns the bytes to be read again" do + @exception.readagain_bytes.size.should == 1 + @exception.readagain_bytes.should == "a".force_encoding('binary') + @exception.readagain_bytes.should == @errinfo[-1] + + @exception2.readagain_bytes.size.should == 1 + @exception2.readagain_bytes.should == "\xFF".force_encoding('binary') + @exception2.readagain_bytes.should == @errinfo2[-1] + end + + it "uses ASCII-8BIT as the encoding" do + @exception.readagain_bytes.encoding.should == Encoding::ASCII_8BIT + + @exception2.readagain_bytes.encoding.should == Encoding::ASCII_8BIT + end + end +end diff --git a/spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb new file mode 100644 index 0000000000..41320c5207 --- /dev/null +++ b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_name_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::UndefinedConversionError#source_encoding_name" do + before :each do + @exception, = EncodingSpecs::UndefinedConversionError.exception + @exception2, = EncodingSpecs::UndefinedConversionErrorIndirect.exception + end + + it "returns a String" do + @exception.source_encoding_name.should be_an_instance_of(String) + end + + it "is equal to the source encoding name of the object that raised it" do + @exception.source_encoding_name.should == "UTF-8" + end + + # The source encoding specified in the Encoding::Converter constructor may + # differ from the source encoding returned here. What seems to happen is + # that when transcoding along a path with multiple pairs of encodings, the + # last one encountered when the error occurred is returned. So in this + # case, the conversion path is ISO-8859-1 -> UTF-8 -> EUC-JP. The + # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from + # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was + # UTF-8, so UTF-8 is regarded as the source encoding. + it "is equal to the source encoding at the stage of the conversion path where the error occured" do + @exception2.source_encoding_name.should == 'UTF-8' + end + end +end diff --git a/spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb new file mode 100644 index 0000000000..75514e5229 --- /dev/null +++ b/spec/rubyspec/core/encoding/invalid_byte_sequence_error/source_encoding_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::InvalidByteSequenceError#source_encoding" do + before :each do + @exception, = EncodingSpecs::InvalidByteSequenceError.exception + @exception2, = EncodingSpecs::InvalidByteSequenceErrorIndirect.exception + end + + it "returns an Encoding object" do + @exception.source_encoding.should be_an_instance_of(Encoding) + @exception2.source_encoding.should be_an_instance_of(Encoding) + end + + it "is equal to the source encoding of the object that raised it" do + @exception.source_encoding.should == Encoding::UTF_8 + end + + # The source encoding specified in the Encoding::Converter constructor may + # differ from the source encoding returned here. What seems to happen is + # that when transcoding along a path with multiple pairs of encodings, the + # last one encountered when the error occurred is returned. So in this + # case, the conversion path is EUC-JP -> UTF-8 -> ISO-8859-1. The + # conversions failed with the first pair of encodings (i.e. transcoding + # from EUC-JP to UTF-8, so UTF-8 is regarded as the source encoding; if + # the error had occurred when converting from UTF-8 to ISO-8859-1, UTF-8 + # would have been the source encoding. + + # FIXME: Derive example where the failure occurs at the UTF-8 -> + # ISO-8859-1 case so as to better illustrate the issue + it "is equal to the source encoding at the stage of the conversion path where the error occured" do + @exception2.source_encoding.should == Encoding::EUC_JP + end + end +end diff --git a/spec/rubyspec/core/encoding/list_spec.rb b/spec/rubyspec/core/encoding/list_spec.rb new file mode 100644 index 0000000000..18488607e5 --- /dev/null +++ b/spec/rubyspec/core/encoding/list_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding.list" do + it "returns an Array" do + Encoding.list.should be_an_instance_of(Array) + end + + it "returns an Array of Encoding objects" do + Encoding.list.each do |enc| + enc.should be_an_instance_of(Encoding) + end + end + + it "returns each encoding only once" do + orig = Encoding.list.map {|e| e.name} + orig.should == orig.uniq + end + + it "includes the default external encoding" do + Encoding.list.include?(Encoding.default_external).should be_true + end + + it "does not include any alias names" do + Encoding.aliases.keys.each do |enc_alias| + Encoding.list.include?(enc_alias).should be_false + end + end + + it "includes all aliased encodings" do + Encoding.aliases.values.each do |enc_alias| + Encoding.list.include?(Encoding.find(enc_alias)).should be_true + end + end + + it "includes dummy encodings" do + Encoding.list.select {|e| e.dummy?}.should_not == [] + end + + # TODO: Find example that illustrates this + it "updates the list when #find is used to load a new encoding" + end +end diff --git a/spec/rubyspec/core/encoding/locale_charmap_spec.rb b/spec/rubyspec/core/encoding/locale_charmap_spec.rb new file mode 100644 index 0000000000..12b93c2562 --- /dev/null +++ b/spec/rubyspec/core/encoding/locale_charmap_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding.locale_charmap" do + it "returns a String" do + Encoding.locale_charmap.should be_an_instance_of(String) + end + + # FIXME: Get this working on Windows + platform_is :linux do + it "returns a value based on the LC_ALL environment variable" do + old_lc_all = ENV['LC_ALL'] + ENV['LC_ALL'] = 'C' + ruby_exe("print Encoding.locale_charmap").should == 'ANSI_X3.4-1968' + ENV['LC_ALL'] = old_lc_all + end + end + + platform_is :freebsd, :darwin do + it "returns a value based on the LC_ALL environment variable" do + old_lc_all = ENV['LC_ALL'] + ENV['LC_ALL'] = 'C' + ruby_exe("print Encoding.locale_charmap").should == 'US-ASCII' + ENV['LC_ALL'] = old_lc_all + end + end + + platform_is :netbsd, :openbsd do + it "returns a value based on the LC_ALL environment variable" do + old_lc_all = ENV['LC_ALL'] + ENV['LC_ALL'] = 'C' + ruby_exe("print Encoding.locale_charmap").should == '646' + ENV['LC_ALL'] = old_lc_all + end + end + + platform_is :bsd, :darwin, :linux do + it "is unaffected by assigning to ENV['LC_ALL'] in the same process" do + old_charmap = Encoding.locale_charmap + old_lc_all = ENV['LC_ALL'] + ENV['LC_ALL'] = 'C' + Encoding.locale_charmap.should == old_charmap + ENV['LC_ALL'] = old_lc_all + end + end + end +end diff --git a/spec/rubyspec/core/encoding/name_list_spec.rb b/spec/rubyspec/core/encoding/name_list_spec.rb new file mode 100644 index 0000000000..c06ec410ec --- /dev/null +++ b/spec/rubyspec/core/encoding/name_list_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding.name_list" do + it "returns an Array" do + Encoding.name_list.should be_an_instance_of(Array) + end + + it "returns encoding names as Strings" do + Encoding.name_list.each {|e| e.should be_an_instance_of(String) } + end + + it "includes all aliases" do + Encoding.aliases.keys.each do |enc_alias| + Encoding.name_list.include?(enc_alias).should be_true + end + end + + it "includes all non-dummy encodings" do + Encoding.list.each do |enc| + Encoding.name_list.include?(enc.name).should be_true + end + end + end +end diff --git a/spec/rubyspec/core/encoding/name_spec.rb b/spec/rubyspec/core/encoding/name_spec.rb new file mode 100644 index 0000000000..4ea89a563a --- /dev/null +++ b/spec/rubyspec/core/encoding/name_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../shared/name', __FILE__) + +with_feature :encoding do + describe "Encoding#name" do + it_behaves_like(:encoding_name, :name) + end +end diff --git a/spec/rubyspec/core/encoding/names_spec.rb b/spec/rubyspec/core/encoding/names_spec.rb new file mode 100644 index 0000000000..3cd741e513 --- /dev/null +++ b/spec/rubyspec/core/encoding/names_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding#names" do + it "returns an Array" do + Encoding.name_list.each do |name| + e = Encoding.find(name) or next + e.names.should be_an_instance_of(Array) + end + end + + it "returns names as Strings" do + Encoding.name_list.each do |name| + e = Encoding.find(name) or next + e.names.each do |this_name| + this_name.should be_an_instance_of(String) + end + end + end + + it "returns #name as the first value" do + Encoding.name_list.each do |name| + e = Encoding.find(name) or next + e.names.first.should == e.name + end + end + + it "includes any aliases the encoding has" do + Encoding.name_list.each do |name| + e = Encoding.find(name) or next + aliases = Encoding.aliases.select{|a,n| n == name}.keys + names = e.names + aliases.each {|a| names.include?(a).should be_true} + end + end + end +end diff --git a/spec/rubyspec/core/encoding/replicate_spec.rb b/spec/rubyspec/core/encoding/replicate_spec.rb new file mode 100644 index 0000000000..5d007dd827 --- /dev/null +++ b/spec/rubyspec/core/encoding/replicate_spec.rb @@ -0,0 +1,48 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "Encoding#replicate" do + before :all do + @i = 0 + end + + before :each do + @i += 1 + @prefix = "RS#{@i}" + end + + it "returns a replica of ASCII" do + name = @prefix + '-ASCII' + e = Encoding::ASCII.replicate(name) + e.name.should == name + "a".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end + + it "returns a replica of UTF-8" do + name = @prefix + 'UTF-8' + e = Encoding::UTF_8.replicate(name) + e.name.should == name + "a".force_encoding(e).valid_encoding?.should be_true + "\u3042".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end + + it "returns a replica of UTF-16BE" do + name = @prefix + 'UTF-16-BE' + e = Encoding::UTF_16BE.replicate(name) + e.name.should == name + "a".force_encoding(e).valid_encoding?.should be_false + "\x30\x42".force_encoding(e).valid_encoding?.should be_true + "\x80".force_encoding(e).valid_encoding?.should be_false + end + + it "returns a replica of ISO-2022-JP" do + name = @prefix + 'ISO-2022-JP' + e = Encoding::ISO_2022_JP.replicate(name) + e.name.should == name + e.dummy?.should be_true + end + end +end diff --git a/spec/rubyspec/core/encoding/shared/name.rb b/spec/rubyspec/core/encoding/shared/name.rb new file mode 100644 index 0000000000..7f85c46764 --- /dev/null +++ b/spec/rubyspec/core/encoding/shared/name.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :encoding_name, shared: true do + it "returns a String" do + Encoding.list.each do |e| + e.send(@method).should be_an_instance_of(String) + end + end + + it "uniquely identifies an encoding" do + Encoding.list.each do |e| + e.should == Encoding.find(e.send(@method)) + end + end +end diff --git a/spec/rubyspec/core/encoding/to_s_spec.rb b/spec/rubyspec/core/encoding/to_s_spec.rb new file mode 100644 index 0000000000..ddc57e321e --- /dev/null +++ b/spec/rubyspec/core/encoding/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../shared/name', __FILE__) + +with_feature :encoding do + describe "Encoding#to_s" do + it_behaves_like(:encoding_name, :to_s) + end +end diff --git a/spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb b/spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb new file mode 100644 index 0000000000..2f7f33e45e --- /dev/null +++ b/spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_name_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::UndefinedConversionError#destination_encoding_name" do + before :each do + @exception = EncodingSpecs::UndefinedConversionError.exception + end + + it "returns a String" do + @exception.destination_encoding_name.should be_an_instance_of(String) + end + + it "is equal to the destination encoding name of the object that raised it" do + @exception.destination_encoding_name.should == "US-ASCII" + end + end +end diff --git a/spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_spec.rb b/spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_spec.rb new file mode 100644 index 0000000000..6d4f42c0d8 --- /dev/null +++ b/spec/rubyspec/core/encoding/undefined_conversion_error/destination_encoding_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::UndefinedConversionError#destination_encoding" do + before :each do + @exception = EncodingSpecs::UndefinedConversionError.exception + end + + it "returns an Encoding object" do + @exception.destination_encoding.should be_an_instance_of(Encoding) + end + + it "is equal to the destination encoding of the object that raised it" do + @exception.destination_encoding.should == Encoding::US_ASCII + end + end +end diff --git a/spec/rubyspec/core/encoding/undefined_conversion_error/error_char_spec.rb b/spec/rubyspec/core/encoding/undefined_conversion_error/error_char_spec.rb new file mode 100644 index 0000000000..7f538a60d6 --- /dev/null +++ b/spec/rubyspec/core/encoding/undefined_conversion_error/error_char_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::UndefinedConversionError#error_char" do + before :each do + @exception = EncodingSpecs::UndefinedConversionError.exception + @exception2 = EncodingSpecs::UndefinedConversionErrorIndirect.exception + end + + it "returns a String" do + @exception.error_char.should be_an_instance_of(String) + @exception2.error_char.should be_an_instance_of(String) + end + + it "returns the one-character String that caused the exception" do + @exception.error_char.size.should == 1 + @exception.error_char.should == "\u{8765}" + + @exception2.error_char.size.should == 1 + @exception2.error_char.should == "\u{A0}" + end + + it "uses the source encoding" do + @exception.error_char.encoding.should == @exception.source_encoding + + @exception2.error_char.encoding.should == @exception2.source_encoding + end + end +end diff --git a/spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb b/spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb new file mode 100644 index 0000000000..4a7aba2044 --- /dev/null +++ b/spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_name_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::UndefinedConversionError#source_encoding_name" do + before :each do + @exception = EncodingSpecs::UndefinedConversionError.exception + @exception2 = EncodingSpecs::UndefinedConversionErrorIndirect.exception + end + + it "returns a String" do + @exception.source_encoding_name.should be_an_instance_of(String) + end + + it "is equal to the source encoding name of the object that raised it" do + @exception.source_encoding_name.should == "UTF-8" + end + + # The source encoding specified in the Encoding::Converter constructor may + # differ from the source encoding returned here. What seems to happen is + # that when transcoding along a path with multiple pairs of encodings, the + # last one encountered when the error occurred is returned. So in this + # case, the conversion path is ISO-8859-1 -> UTF-8 -> EUC-JP. The + # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from + # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was + # UTF-8, so UTF-8 is regarded as the source encoding. + it "is equal to the source encoding at the stage of the conversion path where the error occured" do + @exception2.source_encoding_name.should == 'UTF-8' + end + end +end diff --git a/spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_spec.rb b/spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_spec.rb new file mode 100644 index 0000000000..984862646c --- /dev/null +++ b/spec/rubyspec/core/encoding/undefined_conversion_error/source_encoding_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +with_feature :encoding do + describe "Encoding::UndefinedConversionError#source_encoding" do + before :each do + @exception = EncodingSpecs::UndefinedConversionError.exception + @exception2 = EncodingSpecs::UndefinedConversionErrorIndirect.exception + end + + it "returns an Encoding object" do + @exception.source_encoding.should be_an_instance_of(Encoding) + @exception2.source_encoding.should be_an_instance_of(Encoding) + end + + it "is equal to the source encoding of the object that raised it" do + @exception.source_encoding.should == Encoding::UTF_8 + end + + # The source encoding specified in the Encoding::Converter constructor may + # differ from the source encoding returned here. What seems to happen is + # that when transcoding along a path with multiple pairs of encodings, the + # last one encountered when the error occurred is returned. So in this + # case, the conversion path is ISO-8859-1 -> UTF-8 -> EUC-JP. The + # conversion from ISO-8859-1 -> UTF-8 succeeded, but the conversion from + # UTF-8 to EUC-JP failed. IOW, it failed when the source encoding was + # UTF-8, so UTF-8 is regarded as the source encoding. + it "is equal to the source encoding at the stage of the conversion path where the error occured" do + @exception2.source_encoding.should == Encoding::UTF_8 + end + end +end diff --git a/spec/rubyspec/core/enumerable/all_spec.rb b/spec/rubyspec/core/enumerable/all_spec.rb new file mode 100644 index 0000000000..bfde584260 --- /dev/null +++ b/spec/rubyspec/core/enumerable/all_spec.rb @@ -0,0 +1,121 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#all?" do + + before :each do + @enum = EnumerableSpecs::Numerous.new + @empty = EnumerableSpecs::Empty.new() + @enum1 = [0, 1, 2, -1] + @enum2 = [nil, false, true] + end + + it "always returns true on empty enumeration" do + @empty.all?.should == true + @empty.all? { nil }.should == true + + [].all?.should == true + [].all? { false }.should == true + + {}.all?.should == true + {}.all? { nil }.should == true + end + + it "does not hide exceptions out of #each" do + lambda { + EnumerableSpecs::ThrowingEach.new.all? + }.should raise_error(RuntimeError) + + lambda { + EnumerableSpecs::ThrowingEach.new.all? { false } + }.should raise_error(RuntimeError) + end + + describe "with no block" do + it "returns true if no elements are false or nil" do + @enum.all?.should == true + @enum1.all?.should == true + @enum2.all?.should == false + + EnumerableSpecs::Numerous.new('a','b','c').all?.should == true + EnumerableSpecs::Numerous.new(0, "x", true).all?.should == true + end + + it "returns false if there are false or nil elements" do + EnumerableSpecs::Numerous.new(false).all?.should == false + EnumerableSpecs::Numerous.new(false, false).all?.should == false + + EnumerableSpecs::Numerous.new(nil).all?.should == false + EnumerableSpecs::Numerous.new(nil, nil).all?.should == false + + EnumerableSpecs::Numerous.new(1, nil, 2).all?.should == false + EnumerableSpecs::Numerous.new(0, "x", false, true).all?.should == false + @enum2.all?.should == false + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMultiWithFalse.new + multi.all?.should be_true + end + + end + + describe "with block" do + it "returns true if the block never returns false or nil" do + @enum.all? { true }.should == true + @enum1.all?{ |o| o < 5 }.should == true + @enum1.all?{ |o| 5 }.should == true + end + + it "returns false if the block ever returns false or nil" do + @enum.all? { false }.should == false + @enum.all? { nil }.should == false + @enum1.all?{ |o| o > 2 }.should == false + + EnumerableSpecs::Numerous.new.all? { |i| i > 5 }.should == false + EnumerableSpecs::Numerous.new.all? { |i| i == 3 ? nil : true }.should == false + end + + it "stops iterating once the return value is determined" do + yielded = [] + EnumerableSpecs::Numerous.new(:one, :two, :three).all? do |e| + yielded << e + false + end.should == false + yielded.should == [:one] + + yielded = [] + EnumerableSpecs::Numerous.new(true, true, false, true).all? do |e| + yielded << e + e + end.should == false + yielded.should == [true, true, false] + + yielded = [] + EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).all? do |e| + yielded << e + e + end.should == true + yielded.should == [1, 2, 3, 4, 5] + end + + it "does not hide exceptions out of the block" do + lambda { + @enum.all? { raise "from block" } + }.should raise_error(RuntimeError) + end + + it "gathers initial args as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.all? {|e| !(Array === e) }.should be_true + end + + it "yields multiple arguments when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + yielded = [] + multi.all? {|e, i| yielded << [e, i] } + yielded.should == [[1, 2], [3, 4], [6, 7]] + end + + end +end diff --git a/spec/rubyspec/core/enumerable/any_spec.rb b/spec/rubyspec/core/enumerable/any_spec.rb new file mode 100644 index 0000000000..4f7af68b07 --- /dev/null +++ b/spec/rubyspec/core/enumerable/any_spec.rb @@ -0,0 +1,141 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#any?" do + before :each do + @enum = EnumerableSpecs::Numerous.new + @empty = EnumerableSpecs::Empty.new() + @enum1 = [0, 1, 2, -1] + @enum2 = [nil, false, true] + end + + it "always returns false on empty enumeration" do + @empty.any?.should == false + @empty.any? { nil }.should == false + + [].any?.should == false + [].any? { false }.should == false + + {}.any?.should == false + {}.any? { nil }.should == false + end + + it "raises an ArgumentError when any arguments provided" do + lambda { @enum.any?(Proc.new {}) }.should raise_error(ArgumentError) + lambda { @enum.any?(nil) }.should raise_error(ArgumentError) + lambda { @empty.any?(1) }.should raise_error(ArgumentError) + lambda { @enum1.any?(1) {} }.should raise_error(ArgumentError) + lambda { @enum2.any?(1, 2, 3) {} }.should raise_error(ArgumentError) + end + + it "does not hide exceptions out of #each" do + lambda { + EnumerableSpecs::ThrowingEach.new.any? + }.should raise_error(RuntimeError) + + lambda { + EnumerableSpecs::ThrowingEach.new.any? { false } + }.should raise_error(RuntimeError) + end + + describe "with no block" do + it "returns true if any element is not false or nil" do + @enum.any?.should == true + @enum1.any?.should == true + @enum2.any?.should == true + EnumerableSpecs::Numerous.new(true).any?.should == true + EnumerableSpecs::Numerous.new('a','b','c').any?.should == true + EnumerableSpecs::Numerous.new('a','b','c', nil).any?.should == true + EnumerableSpecs::Numerous.new(1, nil, 2).any?.should == true + EnumerableSpecs::Numerous.new(1, false).any?.should == true + EnumerableSpecs::Numerous.new(false, nil, 1, false).any?.should == true + EnumerableSpecs::Numerous.new(false, 0, nil).any?.should == true + end + + it "returns false if all elements are false or nil" do + EnumerableSpecs::Numerous.new(false).any?.should == false + EnumerableSpecs::Numerous.new(false, false).any?.should == false + EnumerableSpecs::Numerous.new(nil).any?.should == false + EnumerableSpecs::Numerous.new(nil, nil).any?.should == false + EnumerableSpecs::Numerous.new(nil, false, nil).any?.should == false + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMultiWithFalse.new + multi.any?.should be_true + end + end + + describe "with block" do + it "returns true if the block ever returns other than false or nil" do + @enum.any? { true } == true + @enum.any? { 0 } == true + @enum.any? { 1 } == true + + @enum1.any? { Object.new } == true + @enum1.any?{ |o| o < 1 }.should == true + @enum1.any?{ |o| 5 }.should == true + + @enum2.any? { |i| i == nil }.should == true + end + + it "any? should return false if the block never returns other than false or nil" do + @enum.any? { false }.should == false + @enum.any? { nil }.should == false + + @enum1.any?{ |o| o < -10 }.should == false + @enum1.any?{ |o| nil }.should == false + + @enum2.any? { |i| i == :stuff }.should == false + end + + it "stops iterating once the return value is determined" do + yielded = [] + EnumerableSpecs::Numerous.new(:one, :two, :three).any? do |e| + yielded << e + false + end.should == false + yielded.should == [:one, :two, :three] + + yielded = [] + EnumerableSpecs::Numerous.new(true, true, false, true).any? do |e| + yielded << e + e + end.should == true + yielded.should == [true] + + yielded = [] + EnumerableSpecs::Numerous.new(false, nil, false, true, false).any? do |e| + yielded << e + e + end.should == true + yielded.should == [false, nil, false, true] + + yielded = [] + EnumerableSpecs::Numerous.new(1, 2, 3, 4, 5).any? do |e| + yielded << e + e + end.should == true + yielded.should == [1] + end + + it "does not hide exceptions out of the block" do + lambda { + @enum.any? { raise "from block" } + }.should raise_error(RuntimeError) + end + + it "gathers initial args as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.any? {|e| e == 1 }.should be_true + end + + it "yields multiple arguments when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + yielded = [] + multi.any? {|e, i| yielded << [e, i] } + yielded.should == [[1, 2]] + end + + end +end diff --git a/spec/rubyspec/core/enumerable/chunk_spec.rb b/spec/rubyspec/core/enumerable/chunk_spec.rb new file mode 100644 index 0000000000..9d658010e1 --- /dev/null +++ b/spec/rubyspec/core/enumerable/chunk_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#chunk" do + before do + ScratchPad.record [] + end + + ruby_version_is ""..."2.4" do + it "raises an ArgumentError if called without a block" do + lambda do + EnumerableSpecs::Numerous.new.chunk + end.should raise_error(ArgumentError) + end + end + + ruby_version_is "2.4" do + it "returns an Enumerator if called without a block" do + chunk = EnumerableSpecs::Numerous.new(1, 2, 3, 1, 2).chunk + chunk.should be_an_instance_of(Enumerator) + result = chunk.with_index {|elt, i| elt - i }.to_a + result.should == [[1, [1, 2, 3]], [-2, [1, 2]]] + end + end + + it "returns an Enumerator if given a block" do + EnumerableSpecs::Numerous.new.chunk {}.should be_an_instance_of(Enumerator) + end + + it "yields the current element and the current chunk to the block" do + e = EnumerableSpecs::Numerous.new(1, 2, 3) + e.chunk { |x| ScratchPad << x }.to_a + ScratchPad.recorded.should == [1, 2, 3] + end + + it "returns elements of the Enumerable in an Array of Arrays, [v, ary], where 'ary' contains the consecutive elements for which the block returned the value 'v'" do + e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 3, 2, 1) + result = e.chunk { |x| x < 3 && 1 || 0 }.to_a + result.should == [[1, [1, 2]], [0, [3]], [1, [2]], [0, [3]], [1, [2, 1]]] + end + + it "returns elements for which the block returns :_alone in separate Arrays" do + e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1) + result = e.chunk { |x| x < 2 && :_alone }.to_a + result.should == [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]] + end + + it "does not return elements for which the block returns :_separator" do + e = EnumerableSpecs::Numerous.new(1, 2, 3, 3, 2, 1) + result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a + result.should == [[1, [1]], [1, [3, 3]], [1, [1]]] + end + + it "does not return elements for which the block returns nil" do + e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1) + result = e.chunk { |x| x == 2 ? nil : 1 }.to_a + result.should == [[1, [1]], [1, [3]], [1, [1]]] + end + + it "raises a RuntimeError if the block returns a Symbol starting with an underscore other than :_alone or :_separator" do + e = EnumerableSpecs::Numerous.new(1, 2, 3, 2, 1) + lambda { e.chunk { |x| :_arbitrary }.to_a }.should raise_error(RuntimeError) + end + + ruby_version_is ""..."2.3" do + describe "with [initial_state]" do + it "yields an element and an object value-equal but not identical to the object passed to #chunk" do + e = EnumerableSpecs::Numerous.new(1) + value = "value" + + e.chunk(value) do |x, v| + x.should == 1 + v.should == value + v.should_not equal(value) + end.to_a + end + + it "does not yield the object passed to #chunk if it is nil" do + e = EnumerableSpecs::Numerous.new(1) + e.chunk(nil) { |*x| ScratchPad << x }.to_a + ScratchPad.recorded.should == [[1]] + end + end + end + + ruby_version_is "2.3" do + it "does not accept arguments" do + e = EnumerableSpecs::Numerous.new(1, 2, 3) + lambda { + e.chunk(1) {} + }.should raise_error(ArgumentError) + end + end + + it 'returned Enumerator size returns nil' do + e = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 2, 1) + enum = e.chunk { |x| true } + enum.size.should == nil + end +end diff --git a/spec/rubyspec/core/enumerable/chunk_while_spec.rb b/spec/rubyspec/core/enumerable/chunk_while_spec.rb new file mode 100644 index 0000000000..a5cbdc3348 --- /dev/null +++ b/spec/rubyspec/core/enumerable/chunk_while_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.3" do + describe "Enumerable#chunk_while" do + before :each do + ary = [10, 9, 7, 6, 4, 3, 2, 1] + @enum = EnumerableSpecs::Numerous.new(*ary) + @result = @enum.chunk_while { |i, j| i - 1 == j } + @enum_length = ary.length + end + + context "when given a block" do + it "returns an enumerator" do + @result.should be_an_instance_of(Enumerator) + end + + it "splits chunks between adjacent elements i and j where the block returns false" do + @result.to_a.should == [[10, 9], [7, 6], [4, 3, 2, 1]] + end + + it "calls the block for length of the receiver enumerable minus one times" do + times_called = 0 + @enum.chunk_while do |i, j| + times_called += 1 + i - 1 == j + end.to_a + times_called.should == (@enum_length - 1) + end + end + + context "when not given a block" do + it "raises an ArgumentError" do + lambda { @enum.chunk_while }.should raise_error(ArgumentError) + end + end + + context "on a single-element array" do + it "ignores the block and returns an enumerator that yields [element]" do + [1].chunk_while {|x| x.even?}.to_a.should == [[1]] + end + end + end +end diff --git a/spec/rubyspec/core/enumerable/collect_concat_spec.rb b/spec/rubyspec/core/enumerable/collect_concat_spec.rb new file mode 100644 index 0000000000..6f21012060 --- /dev/null +++ b/spec/rubyspec/core/enumerable/collect_concat_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/collect_concat', __FILE__) + +describe "Enumerable#collect_concat" do + it_behaves_like(:enumerable_collect_concat , :collect_concat) +end diff --git a/spec/rubyspec/core/enumerable/collect_spec.rb b/spec/rubyspec/core/enumerable/collect_spec.rb new file mode 100644 index 0000000000..a830eef9f7 --- /dev/null +++ b/spec/rubyspec/core/enumerable/collect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Enumerable#collect" do + it_behaves_like(:enumerable_collect , :collect) +end diff --git a/spec/rubyspec/core/enumerable/count_spec.rb b/spec/rubyspec/core/enumerable/count_spec.rb new file mode 100644 index 0000000000..9d1e08f3a9 --- /dev/null +++ b/spec/rubyspec/core/enumerable/count_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#count" do + before :each do + @elements = [1, 2, 4, 2] + @numerous = EnumerableSpecs::Numerous.new(*@elements) + end + + describe "when no argument or a block" do + it "returns size" do + @numerous.count.should == 4 + end + + describe "with a custom size method" do + before :each do + class << @numerous + def size + :any_object + end + end + end + + it "ignores the custom size method" do + @numerous.count.should == 4 + end + end + end + + it "counts nils if given nil as an argument" do + EnumerableSpecs::Numerous.new(nil, nil, nil, false).count(nil).should == 3 + end + + it "accepts an argument for comparison using ==" do + @numerous.count(2).should == 2 + end + + it "uses a block for comparison" do + @numerous.count{|x| x%2==0 }.should == 3 + end + + it "ignores the block when given an argument" do + -> { + @numerous.count(4){|x| x%2==0 }.should == 1 + }.should complain(/given block not used/) + end + + describe "when each yields multiple values" do + it "gathers initial args as elements" do + multi = EnumerableSpecs::YieldsMulti.new + multi.count {|e| e == 1 }.should == 1 + end + + it "accepts an argument for comparison using ==" do + multi = EnumerableSpecs::YieldsMulti.new + multi.count([1, 2]).should == 1 + end + end +end diff --git a/spec/rubyspec/core/enumerable/cycle_spec.rb b/spec/rubyspec/core/enumerable/cycle_spec.rb new file mode 100644 index 0000000000..2f5760992d --- /dev/null +++ b/spec/rubyspec/core/enumerable/cycle_spec.rb @@ -0,0 +1,104 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorized', __FILE__) + +describe "Enumerable#cycle" do + describe "passed no argument or nil" do + it "loops indefinitely" do + [[],[nil]].each do |args| + bomb = 10 + EnumerableSpecs::Numerous.new.cycle(*args) do + bomb -= 1 + break 42 if bomb <= 0 + end.should == 42 + bomb.should == 0 + end + end + + it "returns nil if there are no elements" do + out = EnumerableSpecs::Empty.new.cycle { break :nope } + out.should be_nil + end + + it "yields successive elements of the array repeatedly" do + b = [] + EnumerableSpecs::Numerous.new(1,2,3).cycle do |elem| + b << elem + break if b.size == 7 + end + b.should == [1,2,3,1,2,3,1] + end + + it "calls each at most once" do + enum = EnumerableSpecs::EachCounter.new(1, 2) + enum.cycle.first(6).should == [1,2,1,2,1,2] + enum.times_called.should == 1 + end + + it "yields only when necessary" do + enum = EnumerableSpecs::EachCounter.new(10, 20, 30) + enum.cycle { |x| break if x == 20} + enum.times_yielded.should == 2 + end + end + + describe "passed a number n as an argument" do + it "returns nil and does nothing for non positive n" do + EnumerableSpecs::ThrowingEach.new.cycle(0) {}.should be_nil + EnumerableSpecs::NoEach.new.cycle(-22) {}.should be_nil + end + + it "calls each at most once" do + enum = EnumerableSpecs::EachCounter.new(1, 2) + enum.cycle(3).to_a.should == [1,2,1,2,1,2] + enum.times_called.should == 1 + end + + it "yields only when necessary" do + enum = EnumerableSpecs::EachCounter.new(10, 20, 30) + enum.cycle(3) { |x| break if x == 20} + enum.times_yielded.should == 2 + end + + it "tries to convert n to an Integer using #to_int" do + enum = EnumerableSpecs::Numerous.new(3, 2, 1) + enum.cycle(2.3).to_a.should == [3, 2, 1, 3, 2, 1] + + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + enum.cycle(obj).to_a.should == [3, 2, 1, 3, 2, 1] + end + + it "raises a TypeError when the passed n can be coerced to Integer" do + enum = EnumerableSpecs::Numerous.new + lambda{ enum.cycle("cat"){} }.should raise_error(TypeError) + end + + it "raises an ArgumentError if more arguments are passed" do + enum = EnumerableSpecs::Numerous.new + lambda{ enum.cycle(1, 2) {} }.should raise_error(ArgumentError) + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.cycle(2).to_a.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9], [1, 2], [3, 4, 5], [6, 7, 8, 9]] + end + end + + describe "Enumerable with size" do + before :all do + @object = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4) + @empty_object = EnumerableSpecs::EmptyWithSize.new + end + it_should_behave_like :enumeratorized_with_cycle_size + end + + describe "Enumerable with no size" do + before :all do + @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4) + @method = :cycle + end + it_should_behave_like :enumeratorized_with_unknown_size + end + +end diff --git a/spec/rubyspec/core/enumerable/detect_spec.rb b/spec/rubyspec/core/enumerable/detect_spec.rb new file mode 100644 index 0000000000..f69e456052 --- /dev/null +++ b/spec/rubyspec/core/enumerable/detect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/find', __FILE__) + +describe "Enumerable#detect" do + it_behaves_like(:enumerable_find , :detect) +end diff --git a/spec/rubyspec/core/enumerable/drop_spec.rb b/spec/rubyspec/core/enumerable/drop_spec.rb new file mode 100644 index 0000000000..1bcdc0ee9a --- /dev/null +++ b/spec/rubyspec/core/enumerable/drop_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#drop" do + before :each do + @enum = EnumerableSpecs::Numerous.new(3, 2, 1, :go) + end + + it "requires exactly one argument" do + lambda{ @enum.drop{} }.should raise_error(ArgumentError) + lambda{ @enum.drop(1, 2){} }.should raise_error(ArgumentError) + end + + describe "passed a number n as an argument" do + it "raises ArgumentError if n < 0" do + lambda{ @enum.drop(-1) }.should raise_error(ArgumentError) + end + + it "tries to convert n to an Integer using #to_int" do + @enum.drop(2.3).should == [1, :go] + + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + @enum.drop(obj).should == [1, :go] + end + + it "returns [] for empty enumerables" do + EnumerableSpecs::Empty.new.drop(0).should == [] + EnumerableSpecs::Empty.new.drop(2).should == [] + end + + it "returns [] if dropping all" do + @enum.drop(5).should == [] + EnumerableSpecs::Numerous.new(3, 2, 1, :go).drop(4).should == [] + end + + it "raises a TypeError when the passed n can be coerced to Integer" do + lambda{ @enum.drop("hat") }.should raise_error(TypeError) + lambda{ @enum.drop(nil) }.should raise_error(TypeError) + end + + end +end diff --git a/spec/rubyspec/core/enumerable/drop_while_spec.rb b/spec/rubyspec/core/enumerable/drop_while_spec.rb new file mode 100644 index 0000000000..731b9588e4 --- /dev/null +++ b/spec/rubyspec/core/enumerable/drop_while_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#drop_while" do + before :each do + @enum = EnumerableSpecs::Numerous.new(3, 2, 1, :go) + end + + it "returns an Enumerator if no block given" do + @enum.drop_while.should be_an_instance_of(Enumerator) + end + + it "returns no/all elements for {true/false} block" do + @enum.drop_while{true}.should == [] + @enum.drop_while{false}.should == @enum.to_a + end + + it "accepts returns other than true/false" do + @enum.drop_while{1}.should == [] + @enum.drop_while{nil}.should == @enum.to_a + end + + it "passes elements to the block until the first false" do + a = [] + @enum.drop_while{|obj| (a << obj).size < 3}.should == [1, :go] + a.should == [3, 2, 1] + end + + it "will only go through what's needed" do + enum = EnumerableSpecs::EachCounter.new(1,2,3,4) + enum.drop_while { |x| + break 42 if x == 3 + true + }.should == 42 + enum.times_yielded.should == 3 + end + + it "doesn't return self when it could" do + a = [1,2,3] + a.drop_while{false}.should_not equal(a) + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.drop_while {|e| e != [6, 7, 8, 9] }.should == [[6, 7, 8, 9]] + end + + it_behaves_like :enumerable_enumeratorized_with_unknown_size, :drop_while +end diff --git a/spec/rubyspec/core/enumerable/each_cons_spec.rb b/spec/rubyspec/core/enumerable/each_cons_spec.rb new file mode 100644 index 0000000000..6720199bc3 --- /dev/null +++ b/spec/rubyspec/core/enumerable/each_cons_spec.rb @@ -0,0 +1,99 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorized', __FILE__) + +describe "Enumerable#each_cons" do + before :each do + @enum = EnumerableSpecs::Numerous.new(4,3,2,1) + @in_threes = [[4,3,2],[3,2,1]] + end + + it "passes element groups to the block" do + acc = [] + @enum.each_cons(3){|g| acc << g}.should be_nil + acc.should == @in_threes + end + + it "raises an ArgumentError if there is not a single parameter > 0" do + lambda{ @enum.each_cons(0){} }.should raise_error(ArgumentError) + lambda{ @enum.each_cons(-2){} }.should raise_error(ArgumentError) + lambda{ @enum.each_cons{} }.should raise_error(ArgumentError) + lambda{ @enum.each_cons(2,2){} }.should raise_error(ArgumentError) + lambda{ @enum.each_cons(0) }.should raise_error(ArgumentError) + lambda{ @enum.each_cons(-2) }.should raise_error(ArgumentError) + lambda{ @enum.each_cons }.should raise_error(ArgumentError) + lambda{ @enum.each_cons(2,2) }.should raise_error(ArgumentError) + end + + it "tries to convert n to an Integer using #to_int" do + acc = [] + @enum.each_cons(3.3){|g| acc << g}.should == nil + acc.should == @in_threes + + obj = mock('to_int') + obj.should_receive(:to_int).and_return(3) + @enum.each_cons(obj){|g| break g.length}.should == 3 + end + + it "works when n is >= full length" do + full = @enum.to_a + acc = [] + @enum.each_cons(full.length){|g| acc << g} + acc.should == [full] + acc = [] + @enum.each_cons(full.length+1){|g| acc << g} + acc.should == [] + end + + it "yields only as much as needed" do + cnt = EnumerableSpecs::EachCounter.new(1, 2, :stop, "I said stop!", :got_it) + cnt.each_cons(2) {|g| break 42 if g[-1] == :stop }.should == 42 + cnt.times_yielded.should == 3 + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.each_cons(2).to_a.should == [[[1, 2], [3, 4, 5]], [[3, 4, 5], [6, 7, 8, 9]]] + end + + describe "when no block is given" do + it "returns an enumerator" do + e = @enum.each_cons(3) + e.should be_an_instance_of(Enumerator) + e.to_a.should == @in_threes + end + + describe "Enumerable with size" do + describe "returned Enumerator" do + describe "size" do + it "returns enum size - each_cons argument + 1" do + enum = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + enum.each_cons(10).size.should == 1 + enum.each_cons(9).size.should == 2 + enum.each_cons(3).size.should == 8 + enum.each_cons(2).size.should == 9 + enum.each_cons(1).size.should == 10 + end + + it "returns 0 when the argument is larger than self" do + enum = EnumerableSpecs::NumerousWithSize.new(1, 2, 3) + enum.each_cons(20).size.should == 0 + end + + it "returns 0 when the enum is empty" do + enum = EnumerableSpecs::EmptyWithSize.new + enum.each_cons(10).size.should == 0 + end + end + end + end + + describe "Enumerable with no size" do + before :all do + @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4) + @method = [:each_cons, 8] + end + it_should_behave_like :enumeratorized_with_unknown_size + end + end +end diff --git a/spec/rubyspec/core/enumerable/each_entry_spec.rb b/spec/rubyspec/core/enumerable/each_entry_spec.rb new file mode 100644 index 0000000000..05d181a998 --- /dev/null +++ b/spec/rubyspec/core/enumerable/each_entry_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#each_entry" do + before :each do + ScratchPad.record [] + @enum = EnumerableSpecs::YieldsMixed.new + @entries = [1, [2], [3,4], [5,6,7], [8,9], nil, []] + end + + it "yields multiple arguments as an array" do + acc = [] + @enum.each_entry {|g| acc << g}.should equal(@enum) + acc.should == @entries + end + + it "returns an enumerator if no block" do + e = @enum.each_entry + e.should be_an_instance_of(Enumerator) + e.to_a.should == @entries + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.each_entry { |x, i| ScratchPad << [x, i] } + ScratchPad.recorded.should == [[:a, 0], [:b, 1]] + end + + it "raises an ArgumentError when extra arguments" do + lambda { @enum.each_entry("one").to_a }.should raise_error(ArgumentError) + lambda { @enum.each_entry("one"){}.to_a }.should raise_error(ArgumentError) + end + + it "passes extra arguments to #each" do + enum = EnumerableSpecs::EachCounter.new(1, 2) + enum.each_entry(:foo, "bar").to_a.should == [1,2] + enum.arguments_passed.should == [:foo, "bar"] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :each_entry +end diff --git a/spec/rubyspec/core/enumerable/each_slice_spec.rb b/spec/rubyspec/core/enumerable/each_slice_spec.rb new file mode 100644 index 0000000000..62503fe206 --- /dev/null +++ b/spec/rubyspec/core/enumerable/each_slice_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumeratorized', __FILE__) + +describe "Enumerable#each_slice" do + before :each do + @enum = EnumerableSpecs::Numerous.new(7,6,5,4,3,2,1) + @sliced = [[7,6,5],[4,3,2],[1]] + end + + it "passes element groups to the block" do + acc = [] + @enum.each_slice(3){|g| acc << g}.should be_nil + acc.should == @sliced + end + + it "raises an ArgumentError if there is not a single parameter > 0" do + lambda{ @enum.each_slice(0){} }.should raise_error(ArgumentError) + lambda{ @enum.each_slice(-2){} }.should raise_error(ArgumentError) + lambda{ @enum.each_slice{} }.should raise_error(ArgumentError) + lambda{ @enum.each_slice(2,2){} }.should raise_error(ArgumentError) + lambda{ @enum.each_slice(0) }.should raise_error(ArgumentError) + lambda{ @enum.each_slice(-2) }.should raise_error(ArgumentError) + lambda{ @enum.each_slice }.should raise_error(ArgumentError) + lambda{ @enum.each_slice(2,2) }.should raise_error(ArgumentError) + end + + it "tries to convert n to an Integer using #to_int" do + acc = [] + @enum.each_slice(3.3){|g| acc << g}.should == nil + acc.should == @sliced + + obj = mock('to_int') + obj.should_receive(:to_int).and_return(3) + @enum.each_slice(obj){|g| break g.length}.should == 3 + end + + it "works when n is >= full length" do + full = @enum.to_a + acc = [] + @enum.each_slice(full.length){|g| acc << g} + acc.should == [full] + acc = [] + @enum.each_slice(full.length+1){|g| acc << g} + acc.should == [full] + end + + it "yields only as much as needed" do + cnt = EnumerableSpecs::EachCounter.new(1, 2, :stop, "I said stop!", :got_it) + cnt.each_slice(2) {|g| break 42 if g[0] == :stop }.should == 42 + cnt.times_yielded.should == 4 + end + + it "returns an enumerator if no block" do + e = @enum.each_slice(3) + e.should be_an_instance_of(Enumerator) + e.to_a.should == @sliced + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.each_slice(2).to_a.should == [[[1, 2], [3, 4, 5]], [[6, 7, 8, 9]]] + end + + describe "when no block is given" do + it "returns an enumerator" do + e = @enum.each_slice(3) + e.should be_an_instance_of(Enumerator) + e.to_a.should == @sliced + end + + describe "Enumerable with size" do + describe "returned Enumerator" do + describe "size" do + it "returns the ceil of Enumerable size divided by the argument value" do + enum = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + enum.each_slice(10).size.should == 1 + enum.each_slice(9).size.should == 2 + enum.each_slice(3).size.should == 4 + enum.each_slice(2).size.should == 5 + enum.each_slice(1).size.should == 10 + end + + it "returns 0 when the Enumerable is empty" do + enum = EnumerableSpecs::EmptyWithSize.new + enum.each_slice(10).size.should == 0 + end + end + end + end + + describe "Enumerable with no size" do + before :all do + @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4) + @method = [:each_slice, 8] + end + it_should_behave_like :enumeratorized_with_unknown_size + end + end + +end diff --git a/spec/rubyspec/core/enumerable/each_with_index_spec.rb b/spec/rubyspec/core/enumerable/each_with_index_spec.rb new file mode 100644 index 0000000000..9884e71167 --- /dev/null +++ b/spec/rubyspec/core/enumerable/each_with_index_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#each_with_index" do + + before :each do + @b = EnumerableSpecs::Numerous.new(2, 5, 3, 6, 1, 4) + end + + it "passes each element and its index to block" do + @a = [] + @b.each_with_index { |o, i| @a << [o, i] } + @a.should == [[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]] + end + + it "provides each element to the block" do + acc = [] + obj = EnumerableSpecs::EachDefiner.new() + res = obj.each_with_index {|a,i| acc << [a,i]} + acc.should == [] + obj.should == res + end + + it "provides each element to the block and its index" do + acc = [] + res = @b.each_with_index {|a,i| acc << [a,i]} + [[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]].should == acc + res.should eql(@b) + end + + it "binds splat arguments properly" do + acc = [] + res = @b.each_with_index { |*b| c,d = b; acc << c; acc << d } + [2, 0, 5, 1, 3, 2, 6, 3, 1, 4, 4, 5].should == acc + res.should eql(@b) + end + + it "returns an enumerator if no block" do + e = @b.each_with_index + e.should be_an_instance_of(Enumerator) + e.to_a.should == [[2, 0], [5, 1], [3, 2], [6, 3], [1, 4], [4, 5]] + end + + it "passes extra parameters to each" do + count = EnumerableSpecs::EachCounter.new(:apple) + e = count.each_with_index(:foo, :bar) + e.to_a.should == [[:apple, 0]] + count.arguments_passed.should == [:foo, :bar] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :each_with_index +end diff --git a/spec/rubyspec/core/enumerable/each_with_object_spec.rb b/spec/rubyspec/core/enumerable/each_with_object_spec.rb new file mode 100644 index 0000000000..13a7c1c66d --- /dev/null +++ b/spec/rubyspec/core/enumerable/each_with_object_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#each_with_object" do + before :each do + @values = [2, 5, 3, 6, 1, 4] + @enum = EnumerableSpecs::Numerous.new(*@values) + @initial = "memo" + end + + it "passes each element and its argument to the block" do + acc = [] + @enum.each_with_object(@initial) do |elem, obj| + obj.should equal(@initial) + obj = 42 + acc << elem + end.should equal(@initial) + acc.should == @values + end + + it "returns an enumerator if no block" do + acc = [] + e = @enum.each_with_object(@initial) + e.each do |elem, obj| + obj.should equal(@initial) + obj = 42 + acc << elem + end.should equal(@initial) + acc.should == @values + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + array = [] + multi.each_with_object(array) { |elem, obj| obj << elem } + array.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, [:each_with_object, []] +end diff --git a/spec/rubyspec/core/enumerable/entries_spec.rb b/spec/rubyspec/core/enumerable/entries_spec.rb new file mode 100644 index 0000000000..94eceee713 --- /dev/null +++ b/spec/rubyspec/core/enumerable/entries_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/entries', __FILE__) + +describe "Enumerable#entries" do + it_behaves_like(:enumerable_entries , :entries) +end diff --git a/spec/rubyspec/core/enumerable/find_all_spec.rb b/spec/rubyspec/core/enumerable/find_all_spec.rb new file mode 100644 index 0000000000..3d587d7709 --- /dev/null +++ b/spec/rubyspec/core/enumerable/find_all_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/find_all', __FILE__) + +describe "Enumerable#find_all" do + it_behaves_like(:enumerable_find_all , :find_all) +end diff --git a/spec/rubyspec/core/enumerable/find_index_spec.rb b/spec/rubyspec/core/enumerable/find_index_spec.rb new file mode 100644 index 0000000000..c118a61fcf --- /dev/null +++ b/spec/rubyspec/core/enumerable/find_index_spec.rb @@ -0,0 +1,89 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#find_index" do + before :each do + @elements = [2, 4, 6, 8, 10] + @numerous = EnumerableSpecs::Numerous.new(*@elements) + @yieldsmixed = EnumerableSpecs::YieldsMixed2.new + end + + it "passes each entry in enum to block while block when block is false" do + visited_elements = [] + @numerous.find_index do |element| + visited_elements << element + false + end + visited_elements.should == @elements + end + + it "returns nil when the block is false" do + @numerous.find_index {|e| false }.should == nil + end + + it "returns the first index for which the block is not false" do + @elements.each_with_index do |element, index| + @numerous.find_index {|e| e > element - 1 }.should == index + end + end + + it "returns the first index found" do + repeated = [10, 11, 11, 13, 11, 13, 10, 10, 13, 11] + numerous_repeat = EnumerableSpecs::Numerous.new(*repeated) + repeated.each do |element| + numerous_repeat.find_index(element).should == element - 10 + end + end + + it "returns nil when the element not found" do + @numerous.find_index(-1).should == nil + end + + it "ignores the block if an argument is given" do + -> { + @numerous.find_index(-1) {|e| true }.should == nil + }.should complain(/given block not used/) + end + + it "returns an Enumerator if no block given" do + @numerous.find_index.should be_an_instance_of(Enumerator) + end + + it "uses #== for testing equality" do + [2].to_enum.find_index(2.0).should == 0 + [2.0].to_enum.find_index(2).should == 0 + end + + describe "without block" do + it "gathers whole arrays as elements when each yields multiple" do + @yieldsmixed.find_index([0, 1, 2]).should == 3 + end + end + + describe "with block" do + before :each do + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + describe "given a single yield parameter" do + it "passes first element to the parameter" do + @yieldsmixed.find_index {|a| ScratchPad << a; false } + ScratchPad.recorded.should == EnumerableSpecs::YieldsMixed2.first_yields + end + end + + describe "given a greedy yield parameter" do + it "passes a gathered array to the parameter" do + @yieldsmixed.find_index {|*args| ScratchPad << args; false } + ScratchPad.recorded.should == EnumerableSpecs::YieldsMixed2.greedy_yields + end + end + end + + it_behaves_like :enumerable_enumeratorized_with_unknown_size, :find_index +end diff --git a/spec/rubyspec/core/enumerable/find_spec.rb b/spec/rubyspec/core/enumerable/find_spec.rb new file mode 100644 index 0000000000..62e1194537 --- /dev/null +++ b/spec/rubyspec/core/enumerable/find_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/find', __FILE__) + +describe "Enumerable#find" do + it_behaves_like(:enumerable_find , :find) +end diff --git a/spec/rubyspec/core/enumerable/first_spec.rb b/spec/rubyspec/core/enumerable/first_spec.rb new file mode 100644 index 0000000000..a85550ee3b --- /dev/null +++ b/spec/rubyspec/core/enumerable/first_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/take', __FILE__) + +describe "Enumerable#first" do + it "returns the first element" do + EnumerableSpecs::Numerous.new.first.should == 2 + EnumerableSpecs::Empty.new.first.should == nil + end + + it "returns nil if self is empty" do + EnumerableSpecs::Empty.new.first.should == nil + end + + it 'returns a gathered array from yield parameters' do + EnumerableSpecs::YieldsMulti.new.to_enum.first.should == [1, 2] + EnumerableSpecs::YieldsMixed2.new.to_enum.first.should == nil + end + + it "raises a RangeError when passed a Bignum" do + enum = EnumerableSpecs::Empty.new + lambda { enum.first(bignum_value) }.should raise_error(RangeError) + end + + describe "when passed an argument" do + it_behaves_like :enumerable_take, :first + end +end diff --git a/spec/rubyspec/core/enumerable/fixtures/classes.rb b/spec/rubyspec/core/enumerable/fixtures/classes.rb new file mode 100644 index 0000000000..26a8aff8e2 --- /dev/null +++ b/spec/rubyspec/core/enumerable/fixtures/classes.rb @@ -0,0 +1,331 @@ +module EnumerableSpecs + + class Numerous + include Enumerable + def initialize(*list) + @list = list.empty? ? [2, 5, 3, 6, 1, 4] : list + end + + def each + @list.each { |i| yield i } + end + end + + class NumerousWithSize < Numerous + def size + @list.size + end + end + + class EachCounter < Numerous + attr_reader :times_called, :times_yielded, :arguments_passed + def initialize(*list) + super(*list) + @times_yielded = @times_called = 0 + end + + def each(*arg) + @times_called += 1 + @times_yielded = 0 + @arguments_passed = arg + @list.each do |i| + @times_yielded +=1 + yield i + end + end + end + + class Empty + include Enumerable + def each + end + end + + class EmptyWithSize + include Enumerable + def each + end + def size + 0 + end + end + + class ThrowingEach + include Enumerable + def each + raise "from each" + end + end + + class NoEach + include Enumerable + end + + # (Legacy form rubycon) + class EachDefiner + + include Enumerable + + attr_reader :arr + + def initialize(*arr) + @arr = arr + end + + def each + i = 0 + loop do + break if i == @arr.size + yield @arr[i] + i += 1 + end + end + + end + + class SortByDummy + def initialize(s) + @s = s + end + + def s + @s + end + end + + class ComparesByVowelCount + + attr_accessor :value, :vowels + + def self.wrap(*args) + args.map {|element| ComparesByVowelCount.new(element)} + end + + def initialize(string) + self.value = string + self.vowels = string.gsub(/[^aeiou]/, '').size + end + + def <=>(other) + self.vowels <=> other.vowels + end + + end + + class InvalidComparable + def <=>(other) + "Not Valid" + end + end + + class ArrayConvertable + attr_accessor :called + def initialize(*values) + @values = values + end + + def to_a + self.called = :to_a + @values + end + + def to_ary + self.called = :to_ary + @values + end + end + + class EnumConvertable + attr_accessor :called + attr_accessor :sym + def initialize(delegate) + @delegate = delegate + end + + def to_enum(sym) + self.called = :to_enum + self.sym = sym + @delegate.to_enum(sym) + end + + def respond_to_missing?(*args) + @delegate.respond_to?(*args) + end + end + + class Equals + def initialize(obj) + @obj = obj + end + def ==(other) + @obj == other + end + end + + class YieldsMulti + include Enumerable + def each + yield 1,2 + yield 3,4,5 + yield 6,7,8,9 + end + end + + class YieldsMultiWithFalse + include Enumerable + def each + yield false,2 + yield false,4,5 + yield false,7,8,9 + end + end + + class YieldsMultiWithSingleTrue + include Enumerable + def each + yield false,2 + yield true,4,5 + yield false,7,8,9 + end + end + + class YieldsMixed + include Enumerable + def each + yield 1 + yield [2] + yield 3,4 + yield 5,6,7 + yield [8,9] + yield nil + yield [] + end + end + + class YieldsMixed2 + include Enumerable + + def self.first_yields + [nil, 0, 0, 0, 0, nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]] + end + + def self.gathered_yields + [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]] + end + + def self.gathered_yields_with_args(arg, *args) + [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, arg, args, [], [0], [0, 1], [0, 1, 2]] + end + + def self.greedy_yields + [[], [0], [0, 1], [0, 1, 2], [0, 1, 2], [nil], [:default_arg], [[]], [[]], [[0]], [[0, 1]], [[0, 1, 2]]] + end + + def each(arg=:default_arg, *args) + yield + yield 0 + yield 0, 1 + yield 0, 1, 2 + yield(*[0, 1, 2]) + yield nil + yield arg + yield args + yield [] + yield [0] + yield [0, 1] + yield [0, 1, 2] + end + end + + class ReverseComparable + include Comparable + def initialize(num) + @num = num + end + + attr_accessor :num + + # Reverse comparison + def <=>(other) + other.num <=> @num + end + end + + class ComparableWithFixnum + include Comparable + def initialize(num) + @num = num + end + + def <=>(fixnum) + @num <=> fixnum + end + end + + class Uncomparable + def <=>(obj) + nil + end + end + + class Undupable + attr_reader :initialize_called, :initialize_dup_called + def dup + raise "Can't, sorry" + end + + def clone + raise "Can't, either, sorry" + end + + def initialize + @initialize_dup = true + end + + def initialize_dup(arg) + @initialize_dup_called = true + end + end + + class Freezy + include Enumerable + + def each + yield 1 + yield 2 + end + + def to_a + super.freeze + end + end + + class MapReturnsEnumerable + include Enumerable + + class EnumerableMapping + include Enumerable + + def initialize(items, block) + @items = items + @block = block + end + + def each + @items.each do |i| + yield @block.call(i) + end + end + end + + def each + yield 1 + yield 2 + yield 3 + end + + def map(&block) + EnumerableMapping.new(self, block) + end + end +end # EnumerableSpecs utility classes diff --git a/spec/rubyspec/core/enumerable/flat_map_spec.rb b/spec/rubyspec/core/enumerable/flat_map_spec.rb new file mode 100644 index 0000000000..aaddeed05d --- /dev/null +++ b/spec/rubyspec/core/enumerable/flat_map_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/collect_concat', __FILE__) + +describe "Enumerable#flat_map" do + it_behaves_like(:enumerable_collect_concat , :flat_map) +end diff --git a/spec/rubyspec/core/enumerable/grep_spec.rb b/spec/rubyspec/core/enumerable/grep_spec.rb new file mode 100644 index 0000000000..777d5e538e --- /dev/null +++ b/spec/rubyspec/core/enumerable/grep_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#grep" do + before :each do + @a = EnumerableSpecs::EachDefiner.new( 2, 4, 6, 8, 10) + end + + it "grep without a block should return an array of all elements === pattern" do + class EnumerableSpecGrep; def ===(obj); obj == '2'; end; end + + EnumerableSpecs::Numerous.new('2', 'a', 'nil', '3', false).grep(EnumerableSpecGrep.new).should == ['2'] + end + + it "grep with a block should return an array of elements === pattern passed through block" do + class EnumerableSpecGrep2; def ===(obj); /^ca/ =~ obj; end; end + + EnumerableSpecs::Numerous.new("cat", "coat", "car", "cadr", "cost").grep(EnumerableSpecGrep2.new) { |i| i.upcase }.should == ["CAT", "CAR", "CADR"] + end + + it "grep the enumerable (rubycon legacy)" do + EnumerableSpecs::EachDefiner.new().grep(1).should == [] + @a.grep(3..7).should == [4,6] + @a.grep(3..7) {|a| a+1}.should == [5,7] + end + + it "can use $~ in the block when used with a Regexp" do + ary = ["aba", "aba"] + ary.grep(/a(b)a/) { $1 }.should == ["b", "b"] + end + + describe "with a block" do + before :each do + @numerous = EnumerableSpecs::Numerous.new(*(0..9).to_a) + def (@odd_matcher = BasicObject.new).===(obj) + obj.odd? + end + end + + it "returns an Array of matched elements that mapped by the block" do + @numerous.grep(@odd_matcher) { |n| n * 2 }.should == [2, 6, 10, 14, 18] + end + + it "calls the block with gathered array when yielded with multiple arguments" do + EnumerableSpecs::YieldsMixed2.new.grep(Object){ |e| e }.should == EnumerableSpecs::YieldsMixed2.gathered_yields + end + + it "raises an ArgumentError when not given a pattern" do + -> { @numerous.grep { |e| e } }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/enumerable/grep_v_spec.rb b/spec/rubyspec/core/enumerable/grep_v_spec.rb new file mode 100644 index 0000000000..05c43a43ef --- /dev/null +++ b/spec/rubyspec/core/enumerable/grep_v_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.3" do + describe "Enumerable#grep_v" do + before :each do + @numerous = EnumerableSpecs::Numerous.new(*(0..9).to_a) + def (@odd_matcher = BasicObject.new).===(obj) + obj.odd? + end + end + + describe "without block" do + it "returns an Array of matched elements" do + @numerous.grep_v(@odd_matcher).should == [0, 2, 4, 6, 8] + end + + it "compares pattern with gathered array when yielded with multiple arguments" do + (unmatcher = Object.new).stub!(:===).and_return(false) + EnumerableSpecs::YieldsMixed2.new.grep_v(unmatcher).should == EnumerableSpecs::YieldsMixed2.gathered_yields + end + + it "raises an ArgumentError when not given a pattern" do + -> { @numerous.grep_v }.should raise_error(ArgumentError) + end + end + + describe "with block" do + it "returns an Array of matched elements that mapped by the block" do + @numerous.grep_v(@odd_matcher) { |n| n * 2 }.should == [0, 4, 8, 12, 16] + end + + it "calls the block with gathered array when yielded with multiple arguments" do + (unmatcher = Object.new).stub!(:===).and_return(false) + EnumerableSpecs::YieldsMixed2.new.grep_v(unmatcher){ |e| e }.should == EnumerableSpecs::YieldsMixed2.gathered_yields + end + + it "raises an ArgumentError when not given a pattern" do + -> { @numerous.grep_v { |e| e } }.should raise_error(ArgumentError) + end + end + end +end diff --git a/spec/rubyspec/core/enumerable/group_by_spec.rb b/spec/rubyspec/core/enumerable/group_by_spec.rb new file mode 100644 index 0000000000..3513512ebf --- /dev/null +++ b/spec/rubyspec/core/enumerable/group_by_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#group_by" do + it "returns a hash with values grouped according to the block" do + e = EnumerableSpecs::Numerous.new("foo", "bar", "baz") + h = e.group_by { |word| word[0..0].to_sym } + h.should == { f: ["foo"], b: ["bar", "baz"]} + end + + it "returns an empty hash for empty enumerables" do + EnumerableSpecs::Empty.new.group_by { |x| x}.should == {} + end + + it "returns a hash without default_proc" do + e = EnumerableSpecs::Numerous.new("foo", "bar", "baz") + h = e.group_by { |word| word[0..0].to_sym } + h[:some].should be_nil + h.default_proc.should be_nil + h.default.should be_nil + end + + it "returns an Enumerator if called without a block" do + EnumerableSpecs::Numerous.new.group_by.should be_an_instance_of(Enumerator) + end + + it "gathers whole arrays as elements when each yields multiple" do + e = EnumerableSpecs::YieldsMulti.new + h = e.group_by { |i| i } + h.should == { [1, 2] => [[1, 2]], + [6, 7, 8, 9] => [[6, 7, 8, 9]], + [3, 4, 5] => [[3, 4, 5]] } + end + + it "returns a tainted hash if self is tainted" do + EnumerableSpecs::Empty.new.taint.group_by {}.tainted?.should be_true + end + + it "returns an untrusted hash if self is untrusted" do + EnumerableSpecs::Empty.new.untrust.group_by {}.untrusted?.should be_true + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :group_by +end diff --git a/spec/rubyspec/core/enumerable/include_spec.rb b/spec/rubyspec/core/enumerable/include_spec.rb new file mode 100644 index 0000000000..2cc0b6e83a --- /dev/null +++ b/spec/rubyspec/core/enumerable/include_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/include', __FILE__) + +describe "Enumerable#include?" do + it_behaves_like(:enumerable_include, :include?) +end diff --git a/spec/rubyspec/core/enumerable/inject_spec.rb b/spec/rubyspec/core/enumerable/inject_spec.rb new file mode 100644 index 0000000000..289a451552 --- /dev/null +++ b/spec/rubyspec/core/enumerable/inject_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/inject', __FILE__) + +describe "Enumerable#inject" do + it_behaves_like :enumerable_inject, :inject +end diff --git a/spec/rubyspec/core/enumerable/lazy_spec.rb b/spec/rubyspec/core/enumerable/lazy_spec.rb new file mode 100644 index 0000000000..f989fb947e --- /dev/null +++ b/spec/rubyspec/core/enumerable/lazy_spec.rb @@ -0,0 +1,10 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#lazy" do + it "returns an instance of Enumerator::Lazy" do + EnumerableSpecs::Numerous.new.lazy.should be_an_instance_of(Enumerator::Lazy) + end +end diff --git a/spec/rubyspec/core/enumerable/map_spec.rb b/spec/rubyspec/core/enumerable/map_spec.rb new file mode 100644 index 0000000000..b2ddf1eb9d --- /dev/null +++ b/spec/rubyspec/core/enumerable/map_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Enumerable#map" do + it_behaves_like(:enumerable_collect , :map) +end diff --git a/spec/rubyspec/core/enumerable/max_by_spec.rb b/spec/rubyspec/core/enumerable/max_by_spec.rb new file mode 100644 index 0000000000..4058cf0a40 --- /dev/null +++ b/spec/rubyspec/core/enumerable/max_by_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#max_by" do + it "returns an enumerator if no block" do + EnumerableSpecs::Numerous.new(42).max_by.should be_an_instance_of(Enumerator) + end + + it "returns nil if #each yields no objects" do + EnumerableSpecs::Empty.new.max_by {|o| o.nonesuch }.should == nil + end + + it "returns the object for whom the value returned by block is the largest" do + EnumerableSpecs::Numerous.new(*%w[1 2 3]).max_by {|obj| obj.to_i }.should == '3' + EnumerableSpecs::Numerous.new(*%w[three five]).max_by {|obj| obj.length }.should == 'three' + end + + it "returns the object that appears first in #each in case of a tie" do + a, b, c = '1', '2', '2' + EnumerableSpecs::Numerous.new(a, b, c).max_by {|obj| obj.to_i }.should equal(b) + end + + it "uses max.<=>(current) to determine order" do + a, b, c = (1..3).map{|n| EnumerableSpecs::ReverseComparable.new(n)} + + # Just using self here to avoid additional complexity + EnumerableSpecs::Numerous.new(a, b, c).max_by {|obj| obj }.should == a + end + + it "is able to return the maximum for enums that contain nils" do + enum = EnumerableSpecs::Numerous.new(nil, nil, true) + enum.max_by {|o| o.nil? ? 0 : 1 }.should == true + enum.max_by {|o| o.nil? ? 1 : 0 }.should == nil + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.max_by {|e| e.size}.should == [6, 7, 8, 9] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :max_by + + context "when called with an argument n" do + before :each do + @enum = EnumerableSpecs::Numerous.new(101, 55, 1, 20, 33, 500, 60) + end + + context "without a block" do + it "returns an enumerator" do + @enum.max_by(2).should be_an_instance_of(Enumerator) + end + end + + context "with a block" do + it "returns an array containing the maximum n elements based on the block's value" do + result = @enum.max_by(3) { |i| i.to_s } + result.should == [60, 55, 500] + end + + context "on a enumerable of length x where x < n" do + it "returns an array containing the maximum n elements of length n" do + result = @enum.max_by(500) { |i| i.to_s } + result.length.should == 7 + end + end + + context "when n is negative" do + it "raises an ArgumentError" do + lambda { @enum.max_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError) + end + end + end + + context "when n is nil" do + it "returns the maximum element" do + @enum.max_by(nil) { |i| i.to_s }.should == 60 + end + end + end +end diff --git a/spec/rubyspec/core/enumerable/max_spec.rb b/spec/rubyspec/core/enumerable/max_spec.rb new file mode 100644 index 0000000000..e283a5d0e2 --- /dev/null +++ b/spec/rubyspec/core/enumerable/max_spec.rb @@ -0,0 +1,119 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#max" do + before :each do + @e_strs = EnumerableSpecs::EachDefiner.new("333", "22", "666666", "1", "55555", "1010101010") + @e_ints = EnumerableSpecs::EachDefiner.new( 333, 22, 666666, 55555, 1010101010) + end + + it "returns the maximum element" do + EnumerableSpecs::Numerous.new.max.should == 6 + end + + it "returns the maximum element (basics cases)" do + EnumerableSpecs::EachDefiner.new(55).max.should == 55 + + EnumerableSpecs::EachDefiner.new(11,99).max.should == 99 + EnumerableSpecs::EachDefiner.new(99,11).max.should == 99 + EnumerableSpecs::EachDefiner.new(2, 33, 4, 11).max.should == 33 + + EnumerableSpecs::EachDefiner.new(1,2,3,4,5).max.should == 5 + EnumerableSpecs::EachDefiner.new(5,4,3,2,1).max.should == 5 + EnumerableSpecs::EachDefiner.new(1,4,3,5,2).max.should == 5 + EnumerableSpecs::EachDefiner.new(5,5,5,5,5).max.should == 5 + + EnumerableSpecs::EachDefiner.new("aa","tt").max.should == "tt" + EnumerableSpecs::EachDefiner.new("tt","aa").max.should == "tt" + EnumerableSpecs::EachDefiner.new("2","33","4","11").max.should == "4" + + @e_strs.max.should == "666666" + @e_ints.max.should == 1010101010 + end + + it "returns nil for an empty Enumerable" do + EnumerableSpecs::EachDefiner.new.max.should == nil + end + + it "raises a NoMethodError for elements without #<=>" do + lambda do + EnumerableSpecs::EachDefiner.new(BasicObject.new, BasicObject.new).max + end.should raise_error(NoMethodError) + end + + it "raises an ArgumentError for incomparable elements" do + lambda do + EnumerableSpecs::EachDefiner.new(11,"22").max + end.should raise_error(ArgumentError) + lambda do + EnumerableSpecs::EachDefiner.new(11,12,22,33).max{|a, b| nil} + end.should raise_error(ArgumentError) + end + + context "when passed a block" do + it "returns the maximum element" do + EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| a <=> b }.should == "4" + EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| a <=> b }.should == 33 + + EnumerableSpecs::EachDefiner.new("2","33","4","11").max {|a,b| b <=> a }.should == "11" + EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).max {|a,b| b <=> a }.should == 2 + + @e_strs.max {|a,b| a.length <=> b.length }.should == "1010101010" + + @e_strs.max {|a,b| a <=> b }.should == "666666" + @e_strs.max {|a,b| a.to_i <=> b.to_i }.should == "1010101010" + + @e_ints.max {|a,b| a <=> b }.should == 1010101010 + @e_ints.max {|a,b| a.to_s <=> b.to_s }.should == 666666 + end + end + + it "returns the maximum for enumerables that contain nils" do + arr = EnumerableSpecs::Numerous.new(nil, nil, true) + arr.max { |a, b| + x = a.nil? ? 1 : a ? 0 : -1 + y = b.nil? ? 1 : b ? 0 : -1 + x <=> y + }.should == nil + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.max.should == [6, 7, 8, 9] + end + + context "when called with an argument n" do + context "without a block" do + it "returns an array containing the maximum n elements" do + result = @e_ints.max(2) + result.should == [1010101010, 666666] + end + end + + context "with a block" do + it "returns an array containing the maximum n elements" do + result = @e_ints.max(2) { |a, b| a * 2 <=> b * 2 } + result.should == [1010101010, 666666] + end + end + + context "on a enumerable of length x where x < n" do + it "returns an array containing the maximum n elements of length x" do + result = @e_ints.max(500) + result.length.should == 5 + end + end + + context "that is negative" do + it "raises an ArgumentError" do + lambda { @e_ints.max(-1) }.should raise_error(ArgumentError) + end + end + end + + context "that is nil" do + it "returns the maximum element" do + @e_ints.max(nil).should == 1010101010 + end + end +end diff --git a/spec/rubyspec/core/enumerable/member_spec.rb b/spec/rubyspec/core/enumerable/member_spec.rb new file mode 100644 index 0000000000..862c949817 --- /dev/null +++ b/spec/rubyspec/core/enumerable/member_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/include', __FILE__) + +describe "Enumerable#member?" do + it_behaves_like(:enumerable_include, :member?) +end diff --git a/spec/rubyspec/core/enumerable/min_by_spec.rb b/spec/rubyspec/core/enumerable/min_by_spec.rb new file mode 100644 index 0000000000..24fe995f09 --- /dev/null +++ b/spec/rubyspec/core/enumerable/min_by_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#min_by" do + it "returns an enumerator if no block" do + EnumerableSpecs::Numerous.new(42).min_by.should be_an_instance_of(Enumerator) + end + + it "returns nil if #each yields no objects" do + EnumerableSpecs::Empty.new.min_by {|o| o.nonesuch }.should == nil + end + + it "returns the object for whom the value returned by block is the smallest" do + EnumerableSpecs::Numerous.new(*%w[3 2 1]).min_by {|obj| obj.to_i }.should == '1' + EnumerableSpecs::Numerous.new(*%w[five three]).min_by {|obj| obj.length }.should == 'five' + end + + it "returns the object that appears first in #each in case of a tie" do + a, b, c = '2', '1', '1' + EnumerableSpecs::Numerous.new(a, b, c).min_by {|obj| obj.to_i }.should equal(b) + end + + it "uses min.<=>(current) to determine order" do + a, b, c = (1..3).map{|n| EnumerableSpecs::ReverseComparable.new(n)} + + # Just using self here to avoid additional complexity + EnumerableSpecs::Numerous.new(a, b, c).min_by {|obj| obj }.should == c + end + + it "is able to return the minimum for enums that contain nils" do + enum = EnumerableSpecs::Numerous.new(nil, nil, true) + enum.min_by {|o| o.nil? ? 0 : 1 }.should == nil + enum.min_by {|o| o.nil? ? 1 : 0 }.should == true + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.min_by {|e| e.size}.should == [1, 2] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :min_by + + context "when called with an argument n" do + before :each do + @enum = EnumerableSpecs::Numerous.new(101, 55, 1, 20, 33, 500, 60) + end + + context "without a block" do + it "returns an enumerator" do + @enum.min_by(2).should be_an_instance_of(Enumerator) + end + end + + context "with a block" do + it "returns an array containing the minimum n elements based on the block's value" do + result = @enum.min_by(3) { |i| i.to_s } + result.should == [1, 101, 20] + end + + context "on a enumerable of length x where x < n" do + it "returns an array containing the minimum n elements of length n" do + result = @enum.min_by(500) { |i| i.to_s } + result.length.should == 7 + end + end + + context "when n is negative" do + it "raises an ArgumentError" do + lambda { @enum.min_by(-1) { |i| i.to_s } }.should raise_error(ArgumentError) + end + end + end + + context "when n is nil" do + it "returns the minimum element" do + @enum.min_by(nil) { |i| i.to_s }.should == 1 + end + end + end +end diff --git a/spec/rubyspec/core/enumerable/min_spec.rb b/spec/rubyspec/core/enumerable/min_spec.rb new file mode 100644 index 0000000000..f56d0420c9 --- /dev/null +++ b/spec/rubyspec/core/enumerable/min_spec.rb @@ -0,0 +1,123 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#min" do + before :each do + @e_strs = EnumerableSpecs::EachDefiner.new("333", "22", "666666", "1", "55555", "1010101010") + @e_ints = EnumerableSpecs::EachDefiner.new( 333, 22, 666666, 55555, 1010101010) + end + + it "min should return the minimum element" do + EnumerableSpecs::Numerous.new.min.should == 1 + end + + it "returns the minimum (basic cases)" do + EnumerableSpecs::EachDefiner.new(55).min.should == 55 + + EnumerableSpecs::EachDefiner.new(11,99).min.should == 11 + EnumerableSpecs::EachDefiner.new(99,11).min.should == 11 + EnumerableSpecs::EachDefiner.new(2, 33, 4, 11).min.should == 2 + + EnumerableSpecs::EachDefiner.new(1,2,3,4,5).min.should == 1 + EnumerableSpecs::EachDefiner.new(5,4,3,2,1).min.should == 1 + EnumerableSpecs::EachDefiner.new(4,1,3,5,2).min.should == 1 + EnumerableSpecs::EachDefiner.new(5,5,5,5,5).min.should == 5 + + EnumerableSpecs::EachDefiner.new("aa","tt").min.should == "aa" + EnumerableSpecs::EachDefiner.new("tt","aa").min.should == "aa" + EnumerableSpecs::EachDefiner.new("2","33","4","11").min.should == "11" + + @e_strs.min.should == "1" + @e_ints.min.should == 22 + end + + it "returns nil for an empty Enumerable" do + EnumerableSpecs::EachDefiner.new.min.should be_nil + end + + it "raises a NoMethodError for elements without #<=>" do + lambda do + EnumerableSpecs::EachDefiner.new(BasicObject.new, BasicObject.new).min + end.should raise_error(NoMethodError) + end + + it "raises an ArgumentError for incomparable elements" do + lambda do + EnumerableSpecs::EachDefiner.new(11,"22").min + end.should raise_error(ArgumentError) + lambda do + EnumerableSpecs::EachDefiner.new(11,12,22,33).min{|a, b| nil} + end.should raise_error(ArgumentError) + end + + it "returns the minimum when using a block rule" do + EnumerableSpecs::EachDefiner.new("2","33","4","11").min {|a,b| a <=> b }.should == "11" + EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).min {|a,b| a <=> b }.should == 2 + + EnumerableSpecs::EachDefiner.new("2","33","4","11").min {|a,b| b <=> a }.should == "4" + EnumerableSpecs::EachDefiner.new( 2 , 33 , 4 , 11 ).min {|a,b| b <=> a }.should == 33 + + EnumerableSpecs::EachDefiner.new( 1, 2, 3, 4 ).min {|a,b| 15 }.should == 1 + + EnumerableSpecs::EachDefiner.new(11,12,22,33).min{|a, b| 2 }.should == 11 + @i = -2 + EnumerableSpecs::EachDefiner.new(11,12,22,33).min{|a, b| @i += 1 }.should == 12 + + @e_strs.min {|a,b| a.length <=> b.length }.should == "1" + + @e_strs.min {|a,b| a <=> b }.should == "1" + @e_strs.min {|a,b| a.to_i <=> b.to_i }.should == "1" + + @e_ints.min {|a,b| a <=> b }.should == 22 + @e_ints.min {|a,b| a.to_s <=> b.to_s }.should == 1010101010 + end + + it "returns the minimum for enumerables that contain nils" do + arr = EnumerableSpecs::Numerous.new(nil, nil, true) + arr.min { |a, b| + x = a.nil? ? -1 : a ? 0 : 1 + y = b.nil? ? -1 : b ? 0 : 1 + x <=> y + }.should == nil + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.min.should == [1, 2] + end + + context "when called with an argument n" do + context "without a block" do + it "returns an array containing the minimum n elements" do + result = @e_ints.min(2) + result.should == [22, 333] + end + end + + context "with a block" do + it "returns an array containing the minimum n elements" do + result = @e_ints.min(2) { |a, b| a * 2 <=> b * 2 } + result.should == [22, 333] + end + end + + context "on a enumerable of length x where x < n" do + it "returns an array containing the minimum n elements of length x" do + result = @e_ints.min(500) + result.length.should == 5 + end + end + + context "that is negative" do + it "raises an ArgumentError" do + lambda { @e_ints.min(-1) }.should raise_error(ArgumentError) + end + end + end + + context "that is nil" do + it "returns the minimum element" do + @e_ints.min(nil).should == 22 + end + end +end diff --git a/spec/rubyspec/core/enumerable/minmax_by_spec.rb b/spec/rubyspec/core/enumerable/minmax_by_spec.rb new file mode 100644 index 0000000000..c92eb381a4 --- /dev/null +++ b/spec/rubyspec/core/enumerable/minmax_by_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#minmax_by" do + it "returns an enumerator if no block" do + EnumerableSpecs::Numerous.new(42).minmax_by.should be_an_instance_of(Enumerator) + end + + it "returns nil if #each yields no objects" do + EnumerableSpecs::Empty.new.minmax_by {|o| o.nonesuch }.should == [nil, nil] + end + + it "returns the object for whom the value returned by block is the largest" do + EnumerableSpecs::Numerous.new(*%w[1 2 3]).minmax_by {|obj| obj.to_i }.should == ['1', '3'] + EnumerableSpecs::Numerous.new(*%w[three five]).minmax_by {|obj| obj.length }.should == ['five', 'three'] + end + + it "returns the object that appears first in #each in case of a tie" do + a, b, c, d = '1', '1', '2', '2' + mm = EnumerableSpecs::Numerous.new(a, b, c, d).minmax_by {|obj| obj.to_i } + mm[0].should equal(a) + mm[1].should equal(c) + end + + it "uses min/max.<=>(current) to determine order" do + a, b, c = (1..3).map{|n| EnumerableSpecs::ReverseComparable.new(n)} + + # Just using self here to avoid additional complexity + EnumerableSpecs::Numerous.new(a, b, c).minmax_by {|obj| obj }.should == [c, a] + end + + it "is able to return the maximum for enums that contain nils" do + enum = EnumerableSpecs::Numerous.new(nil, nil, true) + enum.minmax_by {|o| o.nil? ? 0 : 1 }.should == [nil, true] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.minmax_by {|e| e.size}.should == [[1, 2], [6, 7, 8, 9]] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :minmax_by +end diff --git a/spec/rubyspec/core/enumerable/minmax_spec.rb b/spec/rubyspec/core/enumerable/minmax_spec.rb new file mode 100644 index 0000000000..10bc9b68e4 --- /dev/null +++ b/spec/rubyspec/core/enumerable/minmax_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#minmax" do + before :each do + @enum = EnumerableSpecs::Numerous.new(6, 4, 5, 10, 8) + + @strs = EnumerableSpecs::Numerous.new("333", "2", "60", "55555", "1010", "111") + end + + it "min should return the minimum element" do + @enum.minmax.should == [4, 10] + @strs.minmax.should == ["1010", "60" ] + end + + it "returns [nil, nil] for an empty Enumerable" do + EnumerableSpecs::Empty.new.minmax.should == [nil, nil] + end + + it "raises an ArgumentError when elements are incomparable" do + lambda do + EnumerableSpecs::Numerous.new(11,"22").minmax + end.should raise_error(ArgumentError) + lambda do + EnumerableSpecs::Numerous.new(11,12,22,33).minmax{|a, b| nil} + end.should raise_error(ArgumentError) + end + + it "raises a NoMethodError for elements without #<=>" do + lambda do + EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new).minmax + end.should raise_error(NoMethodError) + end + + it "returns the minimum when using a block rule" do + @enum.minmax {|a,b| b <=> a }.should == [10, 4] + @strs.minmax {|a,b| a.length <=> b.length }.should == ["2", "55555"] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.minmax.should == [[1, 2], [6, 7, 8, 9]] + end +end diff --git a/spec/rubyspec/core/enumerable/none_spec.rb b/spec/rubyspec/core/enumerable/none_spec.rb new file mode 100644 index 0000000000..0646c13b34 --- /dev/null +++ b/spec/rubyspec/core/enumerable/none_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#none?" do + it "returns true if none of the elements in self are true" do + e = EnumerableSpecs::Numerous.new(false, nil, false) + e.none?.should be_true + end + + it "returns false if at least one of the elements in self are true" do + e = EnumerableSpecs::Numerous.new(false, nil, true, false) + e.none?.should be_false + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMultiWithFalse.new + multi.none?.should be_false + end +end + +describe "Enumerable#none? with a block" do + before :each do + @e = EnumerableSpecs::Numerous.new(1,1,2,3,4) + end + + it "passes each element to the block in turn until it returns true" do + acc = [] + @e.none? {|e| acc << e; false } + acc.should == [1,1,2,3,4] + end + + it "stops passing elements to the block when it returns true" do + acc = [] + @e.none? {|e| acc << e; e == 3 ? true : false } + acc.should == [1,1,2,3] + end + + it "returns true if the block never returns true" do + @e.none? {|e| false }.should be_true + end + + it "returns false if the block ever returns true" do + @e.none? {|e| e == 3 ? true : false }.should be_false + end + + it "gathers initial args as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.none? {|e| e == [1, 2] }.should be_true + end + + it "yields multiple arguments when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + yielded = [] + multi.none? {|e, i| yielded << [e, i] } + yielded.should == [[1, 2]] + end +end diff --git a/spec/rubyspec/core/enumerable/one_spec.rb b/spec/rubyspec/core/enumerable/one_spec.rb new file mode 100644 index 0000000000..818d4663a4 --- /dev/null +++ b/spec/rubyspec/core/enumerable/one_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#one?" do + describe "when passed a block" do + it "returns true if block returns true once" do + [:a, :b, :c].one? { |s| s == :a }.should be_true + end + + it "returns false if the block returns true more than once" do + [:a, :b, :c].one? { |s| s == :a || s == :b }.should be_false + end + + it "returns false if the block only returns false" do + [:a, :b, :c].one? { |s| s == :d }.should be_false + end + + it "gathers initial args as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.one? {|e| e == 1 }.should be_true + end + + it "yields multiple arguments when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + yielded = [] + multi.one? {|e, i| yielded << [e, i] } + yielded.should == [[1, 2], [3, 4]] + end + end + + describe "when not passed a block" do + it "returns true if only one element evaluates to true" do + [false, nil, true].one?.should be_true + end + + it "returns false if two elements evaluate to true" do + [false, :value, nil, true].one?.should be_false + end + + it "returns false if all elements evaluate to false" do + [false, nil, false].one?.should be_false + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMultiWithSingleTrue.new + multi.one?.should be_false + end + end +end diff --git a/spec/rubyspec/core/enumerable/partition_spec.rb b/spec/rubyspec/core/enumerable/partition_spec.rb new file mode 100644 index 0000000000..4319a9328f --- /dev/null +++ b/spec/rubyspec/core/enumerable/partition_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#partition" do + it "returns two arrays, the first containing elements for which the block is true, the second containing the rest" do + EnumerableSpecs::Numerous.new.partition { |i| i % 2 == 0 }.should == [[2, 6, 4], [5, 3, 1]] + end + + it "returns an Enumerator if called without a block" do + EnumerableSpecs::Numerous.new.partition.should be_an_instance_of(Enumerator) + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.partition {|e| e == [3, 4, 5] }.should == [[[3, 4, 5]], [[1, 2], [6, 7, 8, 9]]] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :partition +end diff --git a/spec/rubyspec/core/enumerable/reduce_spec.rb b/spec/rubyspec/core/enumerable/reduce_spec.rb new file mode 100644 index 0000000000..8afecb2a8e --- /dev/null +++ b/spec/rubyspec/core/enumerable/reduce_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/inject', __FILE__) + +describe "Enumerable#reduce" do + it_behaves_like :enumerable_inject, :reduce +end diff --git a/spec/rubyspec/core/enumerable/reject_spec.rb b/spec/rubyspec/core/enumerable/reject_spec.rb new file mode 100644 index 0000000000..3dbfb07067 --- /dev/null +++ b/spec/rubyspec/core/enumerable/reject_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#reject" do + it "returns an array of the elements for which block is false" do + EnumerableSpecs::Numerous.new.reject { |i| i > 3 }.should == [2, 3, 1] + entries = (1..10).to_a + numerous = EnumerableSpecs::Numerous.new(*entries) + numerous.reject {|i| i % 2 == 0 }.should == [1,3,5,7,9] + numerous.reject {|i| true }.should == [] + numerous.reject {|i| false }.should == entries + end + + it "returns an Enumerator if called without a block" do + EnumerableSpecs::Numerous.new.reject.should be_an_instance_of(Enumerator) + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.reject {|e| e == [3, 4, 5] }.should == [[1, 2], [6, 7, 8, 9]] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :reject +end diff --git a/spec/rubyspec/core/enumerable/reverse_each_spec.rb b/spec/rubyspec/core/enumerable/reverse_each_spec.rb new file mode 100644 index 0000000000..62c3c0daef --- /dev/null +++ b/spec/rubyspec/core/enumerable/reverse_each_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#reverse_each" do + it "traverses enum in reverse order and pass each element to block" do + a=[] + EnumerableSpecs::Numerous.new.reverse_each { |i| a << i } + a.should == [4, 1, 6, 3, 5, 2] + end + + it "returns an Enumerator if no block given" do + enum = EnumerableSpecs::Numerous.new.reverse_each + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == [4, 1, 6, 3, 5, 2] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + yielded = [] + multi.reverse_each {|e| yielded << e } + yielded.should == [[6, 7, 8, 9], [3, 4, 5], [1, 2]] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :reverse_each +end diff --git a/spec/rubyspec/core/enumerable/select_spec.rb b/spec/rubyspec/core/enumerable/select_spec.rb new file mode 100644 index 0000000000..b4da35c754 --- /dev/null +++ b/spec/rubyspec/core/enumerable/select_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/find_all', __FILE__) + +describe "Enumerable#select" do + it_behaves_like(:enumerable_find_all , :select) +end diff --git a/spec/rubyspec/core/enumerable/shared/collect.rb b/spec/rubyspec/core/enumerable/shared/collect.rb new file mode 100644 index 0000000000..f66c539904 --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/collect.rb @@ -0,0 +1,32 @@ +require File.expand_path('../enumerable_enumeratorized', __FILE__) + +describe :enumerable_collect, shared: true do + before :each do + ScratchPad.record [] + end + + it "returns a new array with the results of passing each element to block" do + entries = [0, 1, 3, 4, 5, 6] + numerous = EnumerableSpecs::Numerous.new(*entries) + numerous.send(@method) { |i| i % 2 }.should == [0, 1, 1, 0, 1, 0] + numerous.send(@method) { |i| i }.should == entries + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i]; nil } + ScratchPad.recorded.should == [[:a, 0], [:b, 1]] + end + + it "gathers initial args as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.send(@method) {|e| e}.should == [1,3,6] + end + + it "returns an enumerator when no block given" do + enum = EnumerableSpecs::Numerous.new.send(@method) + enum.should be_an_instance_of(Enumerator) + enum.each { |i| -i }.should == [-2, -5, -3, -6, -1, -4] + end + + it_should_behave_like :enumerable_enumeratorized_with_origin_size +end diff --git a/spec/rubyspec/core/enumerable/shared/collect_concat.rb b/spec/rubyspec/core/enumerable/shared/collect_concat.rb new file mode 100644 index 0000000000..54e10692eb --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/collect_concat.rb @@ -0,0 +1,54 @@ +require File.expand_path('../enumerable_enumeratorized', __FILE__) + +describe :enumerable_collect_concat, shared: true do + it "yields elements to the block and flattens one level" do + numerous = EnumerableSpecs::Numerous.new(1, [2, 3], [4, [5, 6]], {foo: :bar}) + numerous.send(@method) { |i| i }.should == [1, 2, 3, 4, [5, 6], {foo: :bar}] + end + + it "appends non-Array elements that do not define #to_ary" do + obj = mock("to_ary undefined") + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.send(@method) { |i| i }.should == [1, obj, 2] + end + + it "concatenates the result of calling #to_ary if it returns an Array" do + obj = mock("to_ary defined") + obj.should_receive(:to_ary).and_return([:a, :b]) + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.send(@method) { |i| i }.should == [1, :a, :b, 2] + end + + it "does not call #to_a" do + obj = mock("to_ary undefined") + obj.should_not_receive(:to_a) + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.send(@method) { |i| i }.should == [1, obj, 2] + end + + it "appends an element that defines #to_ary that returns nil" do + obj = mock("to_ary defined") + obj.should_receive(:to_ary).and_return(nil) + + numerous = EnumerableSpecs::Numerous.new(1, obj, 2) + numerous.send(@method) { |i| i }.should == [1, obj, 2] + end + + it "raises a TypeError if an element defining #to_ary does not return an Array or nil" do + obj = mock("to_ary defined") + obj.should_receive(:to_ary).and_return("array") + + lambda { [1, obj, 3].send(@method) { |i| i } }.should raise_error(TypeError) + end + + it "returns an enumerator when no block given" do + enum = EnumerableSpecs::Numerous.new(1, 2).send(@method) + enum.should be_an_instance_of(Enumerator) + enum.each{ |i| [i] * i }.should == [1, 2, 2] + end + + it_should_behave_like :enumerable_enumeratorized_with_origin_size +end diff --git a/spec/rubyspec/core/enumerable/shared/entries.rb b/spec/rubyspec/core/enumerable/shared/entries.rb new file mode 100644 index 0000000000..f52844cb45 --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/entries.rb @@ -0,0 +1,24 @@ +describe :enumerable_entries, shared: true do + it "returns an array containing the elements" do + numerous = EnumerableSpecs::Numerous.new(1, nil, 'a', 2, false, true) + numerous.send(@method).should == [1, nil, "a", 2, false, true] + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.send(@method).should == [[:a, 0], [:b, 1]] + end + + it "passes arguments to each" do + count = EnumerableSpecs::EachCounter.new(1, 2, 3) + count.send(@method, :hello, "world").should == [1, 2, 3] + count.arguments_passed.should == [:hello, "world"] + end + + it "returns a tainted array if self is tainted" do + EnumerableSpecs::Empty.new.taint.send(@method).tainted?.should be_true + end + + it "returns an untrusted array if self is untrusted" do + EnumerableSpecs::Empty.new.untrust.send(@method).untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/enumerable/shared/enumerable_enumeratorized.rb b/spec/rubyspec/core/enumerable/shared/enumerable_enumeratorized.rb new file mode 100644 index 0000000000..b03ce9ed4e --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/enumerable_enumeratorized.rb @@ -0,0 +1,33 @@ +require File.expand_path('../enumeratorized', __FILE__) + +describe :enumerable_enumeratorized_with_unknown_size, shared: true do + describe "Enumerable with size" do + before :all do + @object = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4) + end + it_should_behave_like :enumeratorized_with_unknown_size + end + + describe "Enumerable with no size" do + before :all do + @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4) + end + it_should_behave_like :enumeratorized_with_unknown_size + end +end + +describe :enumerable_enumeratorized_with_origin_size, shared: true do + describe "Enumerable with size" do + before :all do + @object = EnumerableSpecs::NumerousWithSize.new(1, 2, 3, 4) + end + it_should_behave_like :enumeratorized_with_origin_size + end + + describe "Enumerable with no size" do + before :all do + @object = EnumerableSpecs::Numerous.new(1, 2, 3, 4) + end + it_should_behave_like :enumeratorized_with_unknown_size + end +end diff --git a/spec/rubyspec/core/enumerable/shared/enumeratorized.rb b/spec/rubyspec/core/enumerable/shared/enumeratorized.rb new file mode 100644 index 0000000000..05d27b5783 --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/enumeratorized.rb @@ -0,0 +1,42 @@ +describe :enumeratorized_with_unknown_size, shared: true do + describe "when no block is given" do + describe "returned Enumerator" do + it "size returns nil" do + @object.send(*@method).size.should == nil + end + end + end +end + +describe :enumeratorized_with_origin_size, shared: true do + describe "when no block is given" do + describe "returned Enumerator" do + it "size returns the enumerable size" do + @object.send(*@method).size.should == @object.size + end + end + end +end + +describe :enumeratorized_with_cycle_size, shared: true do + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "should be the result of multiplying the enumerable size by the argument passed" do + @object.cycle(2).size.should == @object.size * 2 + @object.cycle(7).size.should == @object.size * 7 + @object.cycle(0).size.should == 0 + @empty_object.cycle(2).size.should == 0 + end + + it "should be zero when the argument passed is 0 or less" do + @object.cycle(-1).size.should == 0 + end + + it "should be Float::INFINITY when no argument is passed" do + @object.cycle.size.should == Float::INFINITY + end + end + end + end +end diff --git a/spec/rubyspec/core/enumerable/shared/find.rb b/spec/rubyspec/core/enumerable/shared/find.rb new file mode 100644 index 0000000000..4cbbf07be0 --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/find.rb @@ -0,0 +1,73 @@ +require File.expand_path('../enumerable_enumeratorized', __FILE__) + +describe :enumerable_find, shared: true do + # #detect and #find are aliases, so we only need one function + before :each do + ScratchPad.record [] + @elements = [2, 4, 6, 8, 10] + @numerous = EnumerableSpecs::Numerous.new(*@elements) + @empty = [] + end + + it "passes each entry in enum to block while block when block is false" do + visited_elements = [] + @numerous.send(@method) do |element| + visited_elements << element + false + end + visited_elements.should == @elements + end + + it "returns nil when the block is false and there is no ifnone proc given" do + @numerous.send(@method) {|e| false }.should == nil + end + + it "returns the first element for which the block is not false" do + @elements.each do |element| + @numerous.send(@method) {|e| e > element - 1 }.should == element + end + end + + it "returns the value of the ifnone proc if the block is false" do + fail_proc = lambda { "cheeseburgers" } + @numerous.send(@method, fail_proc) {|e| false }.should == "cheeseburgers" + end + + it "doesn't call the ifnone proc if an element is found" do + fail_proc = lambda { raise "This shouldn't have been called" } + @numerous.send(@method, fail_proc) {|e| e == @elements.first }.should == 2 + end + + it "calls the ifnone proc only once when the block is false" do + times = 0 + fail_proc = lambda { times += 1; raise if times > 1; "cheeseburgers" } + @numerous.send(@method, fail_proc) {|e| false }.should == "cheeseburgers" + end + + it "calls the ifnone proc when there are no elements" do + fail_proc = lambda { "yay" } + @empty.send(@method, fail_proc) {|e| true}.should == "yay" + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i]; nil } + ScratchPad.recorded.should == [[:a, 0], [:b, 1]] + end + + it "returns an enumerator when no block given" do + @numerous.send(@method).should be_an_instance_of(Enumerator) + end + + it "passes the ifnone proc to the enumerator" do + times = 0 + fail_proc = lambda { times += 1; raise if times > 1; "cheeseburgers" } + @numerous.send(@method, fail_proc).each {|e| false }.should == "cheeseburgers" + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.send(@method) {|e| e == [1, 2] }.should == [1, 2] + end + + it_should_behave_like :enumerable_enumeratorized_with_unknown_size +end diff --git a/spec/rubyspec/core/enumerable/shared/find_all.rb b/spec/rubyspec/core/enumerable/shared/find_all.rb new file mode 100644 index 0000000000..3e15c68e9f --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/find_all.rb @@ -0,0 +1,31 @@ +require File.expand_path('../enumerable_enumeratorized', __FILE__) + +describe :enumerable_find_all, shared: true do + before :each do + ScratchPad.record [] + @elements = (1..10).to_a + @numerous = EnumerableSpecs::Numerous.new(*@elements) + end + + it "returns all elements for which the block is not false" do + @numerous.send(@method) {|i| i % 3 == 0 }.should == [3, 6, 9] + @numerous.send(@method) {|i| true }.should == @elements + @numerous.send(@method) {|i| false }.should == [] + end + + it "returns an enumerator when no block given" do + @numerous.send(@method).should be_an_instance_of(Enumerator) + end + + it "passes through the values yielded by #each_with_index" do + [:a, :b].each_with_index.send(@method) { |x, i| ScratchPad << [x, i] } + ScratchPad.recorded.should == [[:a, 0], [:b, 1]] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.send(@method) {|e| e == [3, 4, 5] }.should == [[3, 4, 5]] + end + + it_should_behave_like :enumerable_enumeratorized_with_origin_size +end diff --git a/spec/rubyspec/core/enumerable/shared/include.rb b/spec/rubyspec/core/enumerable/shared/include.rb new file mode 100644 index 0000000000..569f350fd5 --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/include.rb @@ -0,0 +1,34 @@ +describe :enumerable_include, shared: true do + it "returns true if any element == argument for numbers" do + class EnumerableSpecIncludeP; def ==(obj) obj == 5; end; end + + elements = (0..5).to_a + EnumerableSpecs::Numerous.new(*elements).send(@method,5).should == true + EnumerableSpecs::Numerous.new(*elements).send(@method,10).should == false + EnumerableSpecs::Numerous.new(*elements).send(@method,EnumerableSpecIncludeP.new).should == true + end + + it "returns true if any element == argument for other objects" do + class EnumerableSpecIncludeP11; def ==(obj); obj == '11'; end; end + + elements = ('0'..'5').to_a + [EnumerableSpecIncludeP11.new] + EnumerableSpecs::Numerous.new(*elements).send(@method,'5').should == true + EnumerableSpecs::Numerous.new(*elements).send(@method,'10').should == false + EnumerableSpecs::Numerous.new(*elements).send(@method,EnumerableSpecIncludeP11.new).should == true + EnumerableSpecs::Numerous.new(*elements).send(@method,'11').should == true + end + + + it "returns true if any member of enum equals obj when == compare different classes (legacy rubycon)" do + # equality is tested with == + EnumerableSpecs::Numerous.new(2,4,6,8,10).send(@method, 2.0).should == true + EnumerableSpecs::Numerous.new(2,4,[6,8],10).send(@method, [6, 8]).should == true + EnumerableSpecs::Numerous.new(2,4,[6,8],10).send(@method, [6.0, 8.0]).should == true + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.send(@method, [1,2]).should be_true + end + +end diff --git a/spec/rubyspec/core/enumerable/shared/inject.rb b/spec/rubyspec/core/enumerable/shared/inject.rb new file mode 100644 index 0000000000..12e0665dda --- /dev/null +++ b/spec/rubyspec/core/enumerable/shared/inject.rb @@ -0,0 +1,69 @@ +describe :enumerable_inject, shared: true do + it "with argument takes a block with an accumulator (with argument as initial value) and the current element. Value of block becomes new accumulator" do + a = [] + EnumerableSpecs::Numerous.new.send(@method, 0) { |memo, i| a << [memo, i]; i } + a.should == [[0, 2], [2, 5], [5, 3], [3, 6], [6, 1], [1, 4]] + EnumerableSpecs::EachDefiner.new(true, true, true).send(@method, nil) {|result, i| i && result}.should == nil + end + + it "produces an array of the accumulator and the argument when given a block with a *arg" do + a = [] + [1,2].send(@method, 0) {|*args| a << args; args[0] + args[1]} + a.should == [[0, 1], [1, 2]] + end + + it "can take two argument" do + EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-).should == 4 + end + + it "ignores the block if two arguments" do + EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-){ raise "we never get here"}.should == 4 + end + + it "can take a symbol argument" do + EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, :-).should == 4 + end + + it "without argument takes a block with an accumulator (with first element as initial value) and the current element. Value of block becomes new accumulator" do + a = [] + EnumerableSpecs::Numerous.new.send(@method) { |memo, i| a << [memo, i]; i } + a.should == [[2, 5], [5, 3], [3, 6], [6, 1], [1, 4]] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.send(@method, []) {|acc, e| acc << e }.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]] + end + + it "with inject arguments(legacy rubycon)" do + # with inject argument + EnumerableSpecs::EachDefiner.new().send(@method, 1) {|acc,x| 999 }.should == 1 + EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| 999 }.should == 999 + EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| acc }.should == 1 + EnumerableSpecs::EachDefiner.new(2).send(@method, 1) {|acc,x| x }.should == 2 + + EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method, 100) {|acc,x| acc + x }.should == 110 + EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method, 100) {|acc,x| acc * x }.should == 2400 + + EnumerableSpecs::EachDefiner.new('a','b','c').send(@method, "z") {|result, i| i+result}.should == "cbaz" + end + + it "without inject arguments(legacy rubycon)" do + # no inject argument + EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| 999 } .should == 2 + EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| acc }.should == 2 + EnumerableSpecs::EachDefiner.new(2).send(@method) {|acc,x| x }.should == 2 + + EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method) {|acc,x| acc + x }.should == 10 + EnumerableSpecs::EachDefiner.new(1,2,3,4).send(@method) {|acc,x| acc * x }.should == 24 + + EnumerableSpecs::EachDefiner.new('a','b','c').send(@method) {|result, i| i+result}.should == "cba" + EnumerableSpecs::EachDefiner.new(3, 4, 5).send(@method) {|result, i| result*i}.should == 60 + EnumerableSpecs::EachDefiner.new([1], 2, 'a','b').send(@method){|r,i| r< length" do + @enum.send(@method, 100).should == @values + @enum.send(@method, 8).should == @values # See redmine #1686 ! + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(3).at_most(:twice) # called twice, no apparent reason. See redmine #1554 + @enum.send(@method, obj).should == [4, 3, 2] + end + + it "raises a TypeError if the passed argument is not numeric" do + lambda { @enum.send(@method, nil) }.should raise_error(TypeError) + lambda { @enum.send(@method, "a") }.should raise_error(TypeError) + + obj = mock("nonnumeric") + lambda { @enum.send(@method, obj) }.should raise_error(TypeError) + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.send(@method, 1).should == [[1, 2]] + end + + it "consumes only what is needed" do + thrower = EnumerableSpecs::ThrowingEach.new + thrower.send(@method, 0).should == [] + counter = EnumerableSpecs::EachCounter.new(1,2,3,4) + counter.send(@method, 2).should == [1,2] + counter.times_called.should == 1 + counter.times_yielded.should == 2 + end +end diff --git a/spec/rubyspec/core/enumerable/slice_after_spec.rb b/spec/rubyspec/core/enumerable/slice_after_spec.rb new file mode 100644 index 0000000000..a199b9f1ed --- /dev/null +++ b/spec/rubyspec/core/enumerable/slice_after_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#slice_after" do + before :each do + @enum = EnumerableSpecs::Numerous.new(7, 6, 5, 4, 3, 2, 1) + end + + describe "when given an argument and no block" do + it "calls === on the argument to determine when to yield" do + arg = mock("filter") + arg.should_receive(:===).and_return(false, true, false, false, false, true, false) + e = @enum.slice_after(arg) + e.should be_an_instance_of(Enumerator) + e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]] + end + + it "doesn't yield an empty array if the filter matches the first entry or the last entry" do + arg = mock("filter") + arg.should_receive(:===).and_return(true).exactly(7) + e = @enum.slice_after(arg) + e.to_a.should == [[7], [6], [5], [4], [3], [2], [1]] + end + + it "uses standard boolean as a test" do + arg = mock("filter") + arg.should_receive(:===).and_return(false, :foo, nil, false, false, 42, false) + e = @enum.slice_after(arg) + e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]] + end + end + + describe "when given a block" do + describe "and no argument" do + it "calls the block to determine when to yield" do + e = @enum.slice_after{ |i| i == 6 || i == 2 } + e.should be_an_instance_of(Enumerator) + e.to_a.should == [[7, 6], [5, 4, 3, 2], [1]] + end + end + + describe "and an argument" do + it "raises an ArgumentError" do + lambda { @enum.slice_after(42) { |i| i == 6 } }.should raise_error(ArgumentError) + end + end + end + + it "raises an ArgumentError when given an incorrect number of arguments" do + lambda { @enum.slice_after("one", "two") }.should raise_error(ArgumentError) + lambda { @enum.slice_after }.should raise_error(ArgumentError) + end +end + +describe "when an iterator method yields more than one value" do + it "processes all yielded values" do + enum = EnumerableSpecs::YieldsMulti.new + result = enum.slice_after { |i| i == [3, 4, 5] }.to_a + result.should == [[[1, 2], [3, 4, 5]], [[6, 7, 8, 9]]] + end +end diff --git a/spec/rubyspec/core/enumerable/slice_before_spec.rb b/spec/rubyspec/core/enumerable/slice_before_spec.rb new file mode 100644 index 0000000000..1594372d32 --- /dev/null +++ b/spec/rubyspec/core/enumerable/slice_before_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#slice_before" do + before :each do + @enum = EnumerableSpecs::Numerous.new(7,6,5,4,3,2,1) + end + + describe "when given an argument and no block" do + it "calls === on the argument to determine when to yield" do + arg = mock "filter" + arg.should_receive(:===).and_return(false, true, false, false, false, true, false) + e = @enum.slice_before(arg) + e.should be_an_instance_of(Enumerator) + e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]] + end + + it "doesn't yield an empty array if the filter matches the first entry or the last entry" do + arg = mock "filter" + arg.should_receive(:===).and_return(true).exactly(7) + e = @enum.slice_before(arg) + e.to_a.should == [[7], [6], [5], [4], [3], [2], [1]] + end + + it "uses standard boolean as a test" do + arg = mock "filter" + arg.should_receive(:===).and_return(false, :foo, nil, false, false, 42, false) + e = @enum.slice_before(arg) + e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]] + end + end + + describe "when given a block" do + describe "and no argument" do + it "calls the block to determine when to yield" do + e = @enum.slice_before{|i| i == 6 || i == 2} + e.should be_an_instance_of(Enumerator) + e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]] + end + end + + ruby_version_is ""..."2.3" do + describe "and an argument" do + it "calls the block with a copy of that argument" do + arg = [:foo] + first = nil + e = @enum.slice_before(arg) do |i, init| + init.should == arg + init.should_not equal(arg) + first = init + i == 6 || i == 2 + end + e.should be_an_instance_of(Enumerator) + e.to_a.should == [[7], [6, 5, 4, 3], [2, 1]] + e = @enum.slice_before(arg) do |i, init| + init.should_not equal(first) + end + e.to_a + end + end + end + + ruby_version_is "2.3" do + it "does not accept arguments" do + lambda { + @enum.slice_before(1) {} + }.should raise_error(ArgumentError) + end + end + end + + it "raises an ArgumentError when given an incorrect number of arguments" do + lambda { @enum.slice_before("one", "two") }.should raise_error(ArgumentError) + lambda { @enum.slice_before }.should raise_error(ArgumentError) + end + + describe "when an iterator method yields more than one value" do + it "processes all yielded values" do + enum = EnumerableSpecs::YieldsMulti.new + result = enum.slice_before { |i| i == [3, 4, 5] }.to_a + result.should == [[[1, 2]], [[3, 4, 5], [6, 7, 8, 9]]] + end + end + + it_behaves_like :enumerable_enumeratorized_with_unknown_size, [:slice_before, 3] +end diff --git a/spec/rubyspec/core/enumerable/slice_when_spec.rb b/spec/rubyspec/core/enumerable/slice_when_spec.rb new file mode 100644 index 0000000000..593e623b1b --- /dev/null +++ b/spec/rubyspec/core/enumerable/slice_when_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#slice_when" do + before :each do + ary = [10, 9, 7, 6, 4, 3, 2, 1] + @enum = EnumerableSpecs::Numerous.new(*ary) + @result = @enum.slice_when { |i, j| i - 1 != j } + @enum_length = ary.length + end + + context "when given a block" do + it "returns an enumerator" do + @result.should be_an_instance_of(Enumerator) + end + + it "splits chunks between adjacent elements i and j where the block returns true" do + @result.to_a.should == [[10, 9], [7, 6], [4, 3, 2, 1]] + end + + it "calls the block for length of the receiver enumerable minus one times" do + times_called = 0 + @enum.slice_when do |i, j| + times_called += 1 + i - 1 != j + end.to_a + times_called.should == (@enum_length - 1) + end + + it "doesn't yield an empty array if the block matches the first or the last time" do + @enum.slice_when { true }.to_a.should == [[10], [9], [7], [6], [4], [3], [2], [1]] + end + + it "doesn't yield an empty array on a small enumerable" do + EnumerableSpecs::Empty.new.slice_when { raise }.to_a.should == [] + EnumerableSpecs::Numerous.new(42).slice_when { raise }.to_a.should == [[42]] + end + end + + context "when not given a block" do + it "raises an ArgumentError" do + lambda { @enum.slice_when }.should raise_error(ArgumentError) + end + end + + describe "when an iterator method yields more than one value" do + it "processes all yielded values" do + def foo + yield 1, 2 + end + to_enum(:foo).slice_when { true }.to_a.should == [[[1, 2]]] + end + end +end diff --git a/spec/rubyspec/core/enumerable/sort_by_spec.rb b/spec/rubyspec/core/enumerable/sort_by_spec.rb new file mode 100644 index 0000000000..f7df8659a8 --- /dev/null +++ b/spec/rubyspec/core/enumerable/sort_by_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#sort_by" do + it "returns an array of elements ordered by the result of block" do + a = EnumerableSpecs::Numerous.new("once", "upon", "a", "time") + a.sort_by { |i| i[0] }.should == ["a", "once", "time", "upon"] + end + + it "sorts the object by the given attribute" do + a = EnumerableSpecs::SortByDummy.new("fooo") + b = EnumerableSpecs::SortByDummy.new("bar") + + ar = [a, b].sort_by { |d| d.s } + ar.should == [b, a] + end + + it "returns an Enumerator when a block is not supplied" do + a = EnumerableSpecs::Numerous.new("a","b") + a.sort_by.should be_an_instance_of(Enumerator) + a.to_a.should == ["a", "b"] + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.sort_by {|e| e.size}.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]] + end + + it "returns an array of elements when a block is supplied and #map returns an enumerable" do + b = EnumerableSpecs::MapReturnsEnumerable.new + b.sort_by{ |x| -x }.should == [3, 2, 1] + end + + it_behaves_like :enumerable_enumeratorized_with_origin_size, :sort_by +end diff --git a/spec/rubyspec/core/enumerable/sort_spec.rb b/spec/rubyspec/core/enumerable/sort_spec.rb new file mode 100644 index 0000000000..a39fa7ed34 --- /dev/null +++ b/spec/rubyspec/core/enumerable/sort_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#sort" do + it "sorts by the natural order as defined by <=>" do + EnumerableSpecs::Numerous.new.sort.should == [1, 2, 3, 4, 5, 6] + sorted = EnumerableSpecs::ComparesByVowelCount.wrap("a" * 1, "a" * 2, "a"*3, "a"*4, "a"*5) + EnumerableSpecs::Numerous.new(sorted[2],sorted[0],sorted[1],sorted[3],sorted[4]).sort.should == sorted + end + + it "yields elements to the provided block" do + EnumerableSpecs::Numerous.new.sort { |a, b| b <=> a }.should == [6, 5, 4, 3, 2, 1] + EnumerableSpecs::Numerous.new(2,0,1,3,4).sort { |n, m| -(n <=> m) }.should == [4,3,2,1,0] + end + + it "raises a NoMethodError if elements do not define <=>" do + lambda do + EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new, BasicObject.new).sort + end.should raise_error(NoMethodError) + end + + it "sorts enumerables that contain nils" do + arr = EnumerableSpecs::Numerous.new(nil, true, nil, false, nil, true, nil, false, nil) + arr.sort { |a, b| + x = a ? -1 : a.nil? ? 0 : 1 + y = b ? -1 : b.nil? ? 0 : 1 + x <=> y + }.should == [true, true, nil, nil, nil, nil, nil, false, false] + end + + it "compare values returned by block with 0" do + EnumerableSpecs::Numerous.new.sort { |n, m| -(n+m) * (n <=> m) }.should == [6, 5, 4, 3, 2, 1] + EnumerableSpecs::Numerous.new.sort { |n, m| + EnumerableSpecs::ComparableWithFixnum.new(-(n+m) * (n <=> m)) + }.should == [6, 5, 4, 3, 2, 1] + lambda { + EnumerableSpecs::Numerous.new.sort { |n, m| (n <=> m).to_s } + }.should raise_error(ArgumentError) + end + + it "raises an error if objects can't be compared" do + a=EnumerableSpecs::Numerous.new(EnumerableSpecs::Uncomparable.new, EnumerableSpecs::Uncomparable.new) + lambda {a.sort}.should raise_error(ArgumentError) + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.sort {|a, b| a.first <=> b.first}.should == [[1, 2], [3, 4, 5], [6, 7, 8, 9]] + end + + it "doesn't raise an error if #to_a returns a frozen Array" do + EnumerableSpecs::Freezy.new.sort.should == [1,2] + end +end diff --git a/spec/rubyspec/core/enumerable/take_spec.rb b/spec/rubyspec/core/enumerable/take_spec.rb new file mode 100644 index 0000000000..71bf77050c --- /dev/null +++ b/spec/rubyspec/core/enumerable/take_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/take', __FILE__) + +describe "Enumerable#take" do + it "requires an argument" do + lambda{ EnumerableSpecs::Numerous.new.take}.should raise_error(ArgumentError) + end + + describe "when passed an argument" do + it_behaves_like :enumerable_take, :take + end +end diff --git a/spec/rubyspec/core/enumerable/take_while_spec.rb b/spec/rubyspec/core/enumerable/take_while_spec.rb new file mode 100644 index 0000000000..990d16209a --- /dev/null +++ b/spec/rubyspec/core/enumerable/take_while_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/enumerable_enumeratorized', __FILE__) + +describe "Enumerable#take_while" do + before :each do + @enum = EnumerableSpecs::Numerous.new(3, 2, 1, :go) + end + + it "returns an Enumerator if no block given" do + @enum.take_while.should be_an_instance_of(Enumerator) + end + + it "returns no/all elements for {true/false} block" do + @enum.take_while{true}.should == @enum.to_a + @enum.take_while{false}.should == [] + end + + it "accepts returns other than true/false" do + @enum.take_while{1}.should == @enum.to_a + @enum.take_while{nil}.should == [] + end + + it "passes elements to the block until the first false" do + a = [] + @enum.take_while{|obj| (a << obj).size < 3}.should == [3, 2] + a.should == [3, 2, 1] + end + + it "will only go through what's needed" do + enum = EnumerableSpecs::EachCounter.new(4, 3, 2, 1, :stop) + enum.take_while { |x| + break 42 if x == 3 + true + }.should == 42 + enum.times_yielded.should == 2 + end + + it "doesn't return self when it could" do + a = [1,2,3] + a.take_while{true}.should_not equal(a) + end + + it "calls the block with initial args when yielded with multiple arguments" do + yields = [] + EnumerableSpecs::YieldsMixed.new.take_while{ |v| yields << v } + yields.should == [1, [2], 3, 5, [8, 9], nil, []] + end + + it_behaves_like :enumerable_enumeratorized_with_unknown_size, :take_while +end diff --git a/spec/rubyspec/core/enumerable/to_a_spec.rb b/spec/rubyspec/core/enumerable/to_a_spec.rb new file mode 100644 index 0000000000..b14a3c7a1a --- /dev/null +++ b/spec/rubyspec/core/enumerable/to_a_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/entries', __FILE__) + +describe "Enumerable#to_a" do + it_behaves_like(:enumerable_entries , :to_a) +end diff --git a/spec/rubyspec/core/enumerable/to_h_spec.rb b/spec/rubyspec/core/enumerable/to_h_spec.rb new file mode 100644 index 0000000000..b5b301b882 --- /dev/null +++ b/spec/rubyspec/core/enumerable/to_h_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#to_h" do + it "converts empty enumerable to empty hash" do + enum = EnumerableSpecs::EachDefiner.new + enum.to_h.should == {} + end + + it "converts yielded [key, value] pairs to a hash" do + enum = EnumerableSpecs::EachDefiner.new([:a, 1], [:b, 2]) + enum.to_h.should == { a: 1, b: 2 } + end + + it "uses the last value of a duplicated key" do + enum = EnumerableSpecs::EachDefiner.new([:a, 1], [:b, 2], [:a, 3]) + enum.to_h.should == { a: 3, b: 2 } + end + + it "calls #to_ary on contents" do + pair = mock('to_ary') + pair.should_receive(:to_ary).and_return([:b, 2]) + enum = EnumerableSpecs::EachDefiner.new([:a, 1], pair) + enum.to_h.should == { a: 1, b: 2 } + end + + it "forwards arguments to #each" do + enum = Object.new + def enum.each(*args) + yield(*args) + yield([:b, 2]) + end + enum.extend Enumerable + enum.to_h(:a, 1).should == { a: 1, b: 2 } + end + + it "raises TypeError if an element is not an array" do + enum = EnumerableSpecs::EachDefiner.new(:x) + lambda { enum.to_h }.should raise_error(TypeError) + end + + it "raises ArgumentError if an element is not a [key, value] pair" do + enum = EnumerableSpecs::EachDefiner.new([:x]) + lambda { enum.to_h }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/enumerable/zip_spec.rb b/spec/rubyspec/core/enumerable/zip_spec.rb new file mode 100644 index 0000000000..2d090f335c --- /dev/null +++ b/spec/rubyspec/core/enumerable/zip_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerable#zip" do + + it "combines each element of the receiver with the element of the same index in arrays given as arguments" do + EnumerableSpecs::Numerous.new(1,2,3).zip([4,5,6],[7,8,9]).should == [[1,4,7],[2,5,8],[3,6,9]] + EnumerableSpecs::Numerous.new(1,2,3).zip.should == [[1],[2],[3]] + end + + it "passes each element of the result array to a block and return nil if a block is given" do + expected = [[1,4,7],[2,5,8],[3,6,9]] + EnumerableSpecs::Numerous.new(1,2,3).zip([4,5,6],[7,8,9]) do |result_component| + result_component.should == expected.shift + end.should == nil + expected.size.should == 0 + end + + it "fills resulting array with nils if an argument array is too short" do + EnumerableSpecs::Numerous.new(1,2,3).zip([4,5,6], [7,8]).should == [[1,4,7],[2,5,8],[3,6,nil]] + end + + it "converts arguments to arrays using #to_ary" do + convertable = EnumerableSpecs::ArrayConvertable.new(4,5,6) + EnumerableSpecs::Numerous.new(1,2,3).zip(convertable).should == [[1,4],[2,5],[3,6]] + convertable.called.should == :to_ary + end + + it "converts arguments to enums using #to_enum" do + convertable = EnumerableSpecs::EnumConvertable.new(4..6) + EnumerableSpecs::Numerous.new(1,2,3).zip(convertable).should == [[1,4],[2,5],[3,6]] + convertable.called.should == :to_enum + convertable.sym.should == :each + end + + it "gathers whole arrays as elements when each yields multiple" do + multi = EnumerableSpecs::YieldsMulti.new + multi.zip(multi).should == [[[1, 2], [1, 2]], [[3, 4, 5], [3, 4, 5]], [[6, 7, 8, 9], [6, 7, 8, 9]]] + end + +end + diff --git a/spec/rubyspec/core/enumerator/each_spec.rb b/spec/rubyspec/core/enumerator/each_spec.rb new file mode 100644 index 0000000000..a6ecf2af2d --- /dev/null +++ b/spec/rubyspec/core/enumerator/each_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/enumerator/each', __FILE__) + +describe "Enumerator#each" do + it_behaves_like(:enum_each, :each) +end diff --git a/spec/rubyspec/core/enumerator/each_with_index_spec.rb b/spec/rubyspec/core/enumerator/each_with_index_spec.rb new file mode 100644 index 0000000000..c8cb0bd496 --- /dev/null +++ b/spec/rubyspec/core/enumerator/each_with_index_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/with_index', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Enumerator#each_with_index" do + it_behaves_like(:enum_with_index, :each_with_index) + it_behaves_like(:enumeratorized_with_origin_size, :each_with_index, [1,2,3].select) + + it "returns a new Enumerator when no block is given" do + enum1 = [1,2,3].select + enum2 = enum1.each_with_index + enum2.should be_an_instance_of(Enumerator) + enum1.should_not === enum2 + end + + it "raises an ArgumentError if passed extra arguments" do + lambda do + [1].to_enum.each_with_index(:glark) + end.should raise_error(ArgumentError) + end + + it "passes on the given block's return value" do + arr = [1,2,3] + arr.delete_if.with_index { |a,b| false } + arr.should == [1,2,3] + end + + it "returns the iterator's return value" do + [1,2,3].select.with_index { |a,b| false }.should == [] + end +end + +describe "Enumerator#each_with_index" do + it "returns the correct value if chained with itself" do + [:a].each_with_index.each_with_index.to_a.should == [[[:a,0],0]] + [:a].each.with_index.with_index.to_a.should == [[[:a,0],0]] + end +end diff --git a/spec/rubyspec/core/enumerator/each_with_object_spec.rb b/spec/rubyspec/core/enumerator/each_with_object_spec.rb new file mode 100644 index 0000000000..ec461e2425 --- /dev/null +++ b/spec/rubyspec/core/enumerator/each_with_object_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/with_object', __FILE__) + +describe "Enumerator#each_with_object" do + it_behaves_like :enum_with_object, :each_with_object +end diff --git a/spec/rubyspec/core/enumerator/enum_for_spec.rb b/spec/rubyspec/core/enumerator/enum_for_spec.rb new file mode 100644 index 0000000000..43c11e5a39 --- /dev/null +++ b/spec/rubyspec/core/enumerator/enum_for_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/enum_for', __FILE__) + +describe "Enumerator#enum_for" do + it_behaves_like :enum_for, :enum_for +end diff --git a/spec/rubyspec/core/enumerator/enumerator_spec.rb b/spec/rubyspec/core/enumerator/enumerator_spec.rb new file mode 100644 index 0000000000..2d5213edd9 --- /dev/null +++ b/spec/rubyspec/core/enumerator/enumerator_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator" do + it "includes Enumerable" do + Enumerator.include?(Enumerable).should == true + end +end diff --git a/spec/rubyspec/core/enumerator/feed_spec.rb b/spec/rubyspec/core/enumerator/feed_spec.rb new file mode 100644 index 0000000000..32ea77a30d --- /dev/null +++ b/spec/rubyspec/core/enumerator/feed_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Enumerator#feed" do + before :each do + ScratchPad.record [] + @enum = EnumeratorSpecs::Feed.new.to_enum(:each) + end + + it "sets the future return value of yield if called before advancing the iterator" do + @enum.feed :a + @enum.next + @enum.next + @enum.next + ScratchPad.recorded.should == [:a, nil] + end + + it "causes yield to return the value if called during iteration" do + @enum.next + @enum.feed :a + @enum.next + @enum.next + ScratchPad.recorded.should == [:a, nil] + end + + it "can be called for each iteration" do + @enum.next + @enum.feed :a + @enum.next + @enum.feed :b + @enum.next + ScratchPad.recorded.should == [:a, :b] + end + + it "returns nil" do + @enum.feed(:a).should be_nil + end + + it "raises a TypeError if called more than once without advancing the enumerator" do + @enum.feed :a + @enum.next + lambda { @enum.feed :b }.should raise_error(TypeError) + end + + it "sets the return value of Yielder#yield" do + enum = Enumerator.new { |y| ScratchPad << y.yield } + enum.next + enum.feed :a + lambda { enum.next }.should raise_error(StopIteration) + ScratchPad.recorded.should == [:a] + end +end diff --git a/spec/rubyspec/core/enumerator/first_spec.rb b/spec/rubyspec/core/enumerator/first_spec.rb new file mode 100644 index 0000000000..ba3b0df492 --- /dev/null +++ b/spec/rubyspec/core/enumerator/first_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator#first" do + it "returns arrays correctly when calling #first (2376)" do + Enumerator.new {|y| y << [42] }.first.should == [42] + end +end diff --git a/spec/rubyspec/core/enumerator/fixtures/common.rb b/spec/rubyspec/core/enumerator/fixtures/common.rb new file mode 100644 index 0000000000..e332e3195b --- /dev/null +++ b/spec/rubyspec/core/enumerator/fixtures/common.rb @@ -0,0 +1,9 @@ +module EnumeratorSpecs + class Feed + def each + ScratchPad << yield + ScratchPad << yield + ScratchPad << yield + end + end +end diff --git a/spec/rubyspec/core/enumerator/generator/each_spec.rb b/spec/rubyspec/core/enumerator/generator/each_spec.rb new file mode 100644 index 0000000000..06395d0aa0 --- /dev/null +++ b/spec/rubyspec/core/enumerator/generator/each_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Enumerator::Generator#each" do + before :each do + @generator = Enumerator::Generator.new do |y, *args| + y << 3 << 2 << 1 + y << args unless args.empty? + :block_returned + end + end + + it "is an enumerable" do + @generator.should be_kind_of(Enumerable) + end + + it "supports enumeration with a block" do + r = [] + @generator.each { |v| r << v } + + r.should == [3, 2, 1] + end + + it "raises a LocalJumpError if no block given" do + lambda { @generator.each }.should raise_error(LocalJumpError) + end + + it "returns the block returned value" do + @generator.each {}.should equal(:block_returned) + end + + it "requires multiple arguments" do + Enumerator::Generator.instance_method(:each).arity.should < 0 + end + + it "appends given arguments to receiver.each" do + yields = [] + @generator.each(:each0, :each1) { |yielded| yields << yielded } + yields.should == [3, 2, 1, [:each0, :each1]] + end +end diff --git a/spec/rubyspec/core/enumerator/generator/initialize_spec.rb b/spec/rubyspec/core/enumerator/generator/initialize_spec.rb new file mode 100644 index 0000000000..85b0e04354 --- /dev/null +++ b/spec/rubyspec/core/enumerator/generator/initialize_spec.rb @@ -0,0 +1,26 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Enumerator::Generator#initialize" do + before :each do + @class = Enumerator::Generator + @uninitialized = @class.allocate + end + + it "is a private method" do + @class.should have_private_instance_method(:initialize, false) + end + + it "returns self when given a block" do + @uninitialized.send(:initialize) {}.should equal(@uninitialized) + end + + describe "on frozen instance" do + it "raises a RuntimeError" do + lambda { + @uninitialized.freeze.send(:initialize) {} + }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/enumerator/initialize_spec.rb b/spec/rubyspec/core/enumerator/initialize_spec.rb new file mode 100644 index 0000000000..58f8a5e865 --- /dev/null +++ b/spec/rubyspec/core/enumerator/initialize_spec.rb @@ -0,0 +1,61 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator#initialize" do + before :each do + @uninitialized = Enumerator.allocate + end + + it "is a private method" do + Enumerator.should have_private_instance_method(:initialize, false) + end + + it "returns self when given an object" do + @uninitialized.send(:initialize, Object.new).should equal(@uninitialized) + end + + it "returns self when given a block" do + @uninitialized.send(:initialize) {}.should equal(@uninitialized) + end + + # Maybe spec should be broken up? + it "accepts a block" do + @uninitialized.send(:initialize) do |yielder| + r = yielder.yield 3 + yielder << r << 2 << 1 + end + @uninitialized.should be_an_instance_of(Enumerator) + r = [] + @uninitialized.each{|x| r << x; x * 2} + r.should == [3, 6, 2, 1] + end + + it "sets size to nil if size is not given" do + @uninitialized.send(:initialize) {}.size.should be_nil + end + + it "sets size to nil if the given size is nil" do + @uninitialized.send(:initialize, nil) {}.size.should be_nil + end + + it "sets size to the given size if the given size is Float::INFINITY" do + @uninitialized.send(:initialize, Float::INFINITY) {}.size.should equal(Float::INFINITY) + end + + it "sets size to the given size if the given size is a Fixnum" do + @uninitialized.send(:initialize, 100) {}.size.should == 100 + end + + it "sets size to the given size if the given size is a Proc" do + @uninitialized.send(:initialize, lambda { 200 }) {}.size.should == 200 + end + + describe "on frozen instance" do + it "raises a RuntimeError" do + lambda { + @uninitialized.freeze.send(:initialize) {} + }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/enumerator/inject_spec.rb b/spec/rubyspec/core/enumerator/inject_spec.rb new file mode 100644 index 0000000000..64085a03c5 --- /dev/null +++ b/spec/rubyspec/core/enumerator/inject_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../shared/enumerator/each', __FILE__) + +describe "Enumerator#inject" do + it_behaves_like(:enum_each, :each) + + it "works when chained against each_with_index" do + passed_values = [] + [:a].each_with_index.inject(0) do |accumulator,value| + passed_values << value + accumulator + 1 + end.should == 1 + passed_values.should == [[:a,0]] + end + +end diff --git a/spec/rubyspec/core/enumerator/inspect_spec.rb b/spec/rubyspec/core/enumerator/inspect_spec.rb new file mode 100644 index 0000000000..e89d3b7f3a --- /dev/null +++ b/spec/rubyspec/core/enumerator/inspect_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator#inspect" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/enumerator/lazy/collect_concat_spec.rb b/spec/rubyspec/core/enumerator/lazy/collect_concat_spec.rb new file mode 100644 index 0000000000..8c19dcbbf9 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/collect_concat_spec.rb @@ -0,0 +1,8 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/collect_concat', __FILE__) + +describe "Enumerator::Lazy#collect_concat" do + it_behaves_like :enumerator_lazy_collect_concat, :collect_concat +end diff --git a/spec/rubyspec/core/enumerator/lazy/collect_spec.rb b/spec/rubyspec/core/enumerator/lazy/collect_spec.rb new file mode 100644 index 0000000000..764b8af36d --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/collect_spec.rb @@ -0,0 +1,8 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Enumerator::Lazy#collect" do + it_behaves_like :enumerator_lazy_collect, :collect +end diff --git a/spec/rubyspec/core/enumerator/lazy/drop_spec.rb b/spec/rubyspec/core/enumerator/lazy/drop_spec.rb new file mode 100644 index 0000000000..eb65bb246b --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/drop_spec.rb @@ -0,0 +1,52 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#drop" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.drop(1) + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets difference of given count with old size to new size" do + Enumerator::Lazy.new(Object.new, 100) {}.drop(20).size.should == 80 + Enumerator::Lazy.new(Object.new, 100) {}.drop(200).size.should == 0 + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.drop(2).first(2).should == [2, 3] + + @eventsmixed.drop(0).first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + describe "on a nested Lazy" do + it "sets difference of given count with old size to new size" do + Enumerator::Lazy.new(Object.new, 100) {}.drop(20).drop(50).size.should == 30 + Enumerator::Lazy.new(Object.new, 100) {}.drop(50).drop(20).size.should == 30 + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.drop(2).drop(2).first(2).should == [4, 5] + + @eventsmixed.drop(0).drop(0).first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/drop_while_spec.rb b/spec/rubyspec/core/enumerator/lazy/drop_while_spec.rb new file mode 100644 index 0000000000..a08644a20c --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/drop_while_spec.rb @@ -0,0 +1,60 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#drop_while" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.drop_while {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.drop_while { |v| v }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.drop_while { |n| n < 5 }.first(2).should == [5, 6] + + @eventsmixed.drop_while { false }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with initial values when yield with multiple arguments" do + yields = [] + @yieldsmixed.drop_while { |v| yields << v; true }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields + end + + it "raises an ArgumentError when not given a block" do + lambda { @yieldsmixed.drop_while }.should raise_error(ArgumentError) + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take(20).drop_while { |v| v }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.drop_while { |n| n < 5 }.drop_while { |n| n.odd? }.first(2).should == [6, 7] + + @eventsmixed.drop_while { false }.drop_while { false }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/enum_for_spec.rb b/spec/rubyspec/core/enumerator/lazy/enum_for_spec.rb new file mode 100644 index 0000000000..b2ef2c881e --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/enum_for_spec.rb @@ -0,0 +1,8 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_enum', __FILE__) + +describe "Enumerator::Lazy#enum_for" do + it_behaves_like :enumerator_lazy_to_enum, :enum_for +end diff --git a/spec/rubyspec/core/enumerator/lazy/find_all_spec.rb b/spec/rubyspec/core/enumerator/lazy/find_all_spec.rb new file mode 100644 index 0000000000..ab2e69c857 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/find_all_spec.rb @@ -0,0 +1,8 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/select', __FILE__) + +describe "Enumerator::Lazy#find_all" do + it_behaves_like :enumerator_lazy_select, :find_all +end diff --git a/spec/rubyspec/core/enumerator/lazy/fixtures/classes.rb b/spec/rubyspec/core/enumerator/lazy/fixtures/classes.rb new file mode 100644 index 0000000000..e35592ba1c --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/fixtures/classes.rb @@ -0,0 +1,54 @@ +# -*- encoding: us-ascii -*- + +module EnumeratorLazySpecs + class SpecificError < Exception; end + + class YieldsMixed + def self.initial_yields + [nil, 0, 0, 0, 0, nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]] + end + + def self.gathered_yields + [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, :default_arg, [], [], [0], [0, 1], [0, 1, 2]] + end + + def self.gathered_non_array_yields + [nil, 0, nil, :default_arg] + end + + def self.gathered_yields_with_args(arg, *args) + [nil, 0, [0, 1], [0, 1, 2], [0, 1, 2], nil, arg, args, [], [0], [0, 1], [0, 1, 2]] + end + + def each(arg=:default_arg, *args) + yield + yield 0 + yield 0, 1 + yield 0, 1, 2 + yield(*[0, 1, 2]) + yield nil + yield arg + yield args + yield [] + yield [0] + yield [0, 1] + yield [0, 1, 2] + end + end + + class EventsMixed + def each + ScratchPad << :before_yield + + yield 0 + + ScratchPad << :after_yield + + raise SpecificError + + ScratchPad << :after_error + + :should_not_reach_here + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/flat_map_spec.rb b/spec/rubyspec/core/enumerator/lazy/flat_map_spec.rb new file mode 100644 index 0000000000..b7fba5e81c --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/flat_map_spec.rb @@ -0,0 +1,8 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/collect_concat', __FILE__) + +describe "Enumerator::Lazy#flat_map" do + it_behaves_like :enumerator_lazy_collect_concat, :flat_map +end diff --git a/spec/rubyspec/core/enumerator/lazy/force_spec.rb b/spec/rubyspec/core/enumerator/lazy/force_spec.rb new file mode 100644 index 0000000000..03ff9a0fb6 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/force_spec.rb @@ -0,0 +1,30 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#force" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "passes given arguments to receiver.each" do + @yieldsmixed.force(:arg1, :arg2, :arg3).should == + EnumeratorLazySpecs::YieldsMixed.gathered_yields_with_args(:arg1, :arg2, :arg3) + end + + describe "on a nested Lazy" do + it "calls all block and returns an Array" do + (0..Float::INFINITY).lazy.map(&:succ).take(2).force.should == [1, 2] + + @eventsmixed.take(1).map(&:succ).force.should == [1] + ScratchPad.recorded == [:after_yields] + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/grep_spec.rb b/spec/rubyspec/core/enumerator/lazy/grep_spec.rb new file mode 100644 index 0000000000..c5fbfcf1dd --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/grep_spec.rb @@ -0,0 +1,82 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#grep" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "requires an argument" do + Enumerator::Lazy.instance_method(:grep).arity.should == 1 + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.grep(Object) {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + + ret = @yieldsmixed.grep(Object) + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.grep(Object) {}.size.should == nil + Enumerator::Lazy.new(Object.new, 100) {}.grep(Object).size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times when not given a block" do + (0..Float::INFINITY).lazy.grep(Integer).first(3).should == [0, 1, 2] + + @eventsmixed.grep(BasicObject).first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "stops after specified times when given a block" do + (0..Float::INFINITY).lazy.grep(Integer, &:succ).first(3).should == [1, 2, 3] + + @eventsmixed.grep(BasicObject) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with a gathered array when yield with multiple arguments" do + yields = [] + @yieldsmixed.grep(BasicObject) { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields + + @yieldsmixed.grep(BasicObject).force.should == yields + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.grep(Object) {}.size.should == nil + Enumerator::Lazy.new(Object.new, 100) {}.grep(Object).size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times when not given a block" do + (0..Float::INFINITY).lazy.grep(Integer).grep(Object).first(3).should == [0, 1, 2] + + @eventsmixed.grep(BasicObject).grep(Object).first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "stops after specified times when given a block" do + (0..Float::INFINITY).lazy.grep(Integer) { |n| n > 3 ? n : false }.grep(Integer) { |n| n.even? ? n : false }.first(3).should == [4, false, 6] + + @eventsmixed.grep(BasicObject) {}.grep(Object) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/grep_v_spec.rb b/spec/rubyspec/core/enumerator/lazy/grep_v_spec.rb new file mode 100644 index 0000000000..06dd42213c --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/grep_v_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.3" do + describe "Enumerator::Lazy#grep_v" do + before(:each) do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after(:each) do + ScratchPad.clear + end + + it "requires an argument" do + Enumerator::Lazy.instance_method(:grep_v).arity.should == 1 + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.grep_v(Object) {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + + ret = @yieldsmixed.grep_v(Object) + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object) {}.size.should == nil + Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object).size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times when not given a block" do + (0..Float::INFINITY).lazy.grep_v(3..5).first(3).should == [0, 1, 2] + + @eventsmixed.grep_v(Symbol).first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "stops after specified times when given a block" do + (0..Float::INFINITY).lazy.grep_v(4..8, &:succ).first(3).should == [1, 2, 3] + + @eventsmixed.grep_v(Symbol) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with a gathered array when yield with multiple arguments" do + yields = [] + @yieldsmixed.grep_v(Array) { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_non_array_yields + + @yieldsmixed.grep_v(Array).force.should == yields + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object).grep_v(Object) {}.size.should == nil + Enumerator::Lazy.new(Object.new, 100) {}.grep_v(Object).grep_v(Object).size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times when not given a block" do + (0..Float::INFINITY).lazy.grep_v(3..5).grep_v(6..10).first(3).should == [0, 1, 2] + + @eventsmixed.grep_v(Symbol).grep_v(String).first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "stops after specified times when given a block" do + (0..Float::INFINITY).lazy + .grep_v(1..2) { |n| n > 3 ? n : false } + .grep_v(false) { |n| n.even? ? n : false } + .first(3) + .should == [4, false, 6] + + @eventsmixed.grep_v(Symbol) {}.grep_v(String) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/initialize_spec.rb b/spec/rubyspec/core/enumerator/lazy/initialize_spec.rb new file mode 100644 index 0000000000..47eeafb5cf --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/initialize_spec.rb @@ -0,0 +1,63 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Enumerator::Lazy#initialize" do + before :each do + @receiver = receiver = Object.new + + def receiver.each + yield 0 + yield 1 + yield 2 + end + + @uninitialized = Enumerator::Lazy.allocate + end + + it "is a private method" do + Enumerator::Lazy.should have_private_instance_method(:initialize, false) + end + + it "returns self" do + @uninitialized.send(:initialize, @receiver) {}.should equal(@uninitialized) + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + @uninitialized.send(:initialize, @receiver) do |yielder, *values| + yielder.<<(*values) + end.first(2).should == [0, 1] + end + end + + it "sets #size to nil if not given a size" do + @uninitialized.send(:initialize, @receiver) {}.size.should be_nil + end + + it "sets #size to nil if given size is nil" do + @uninitialized.send(:initialize, @receiver, nil) {}.size.should be_nil + end + + it "sets given size to own size if the given size is Float::INFINITY" do + @uninitialized.send(:initialize, @receiver, Float::INFINITY) {}.size.should equal(Float::INFINITY) + end + + it "sets given size to own size if the given size is a Fixnum" do + @uninitialized.send(:initialize, @receiver, 100) {}.size.should == 100 + end + + it "sets given size to own size if the given size is a Proc" do + @uninitialized.send(:initialize, @receiver, lambda { 200 }) {}.size.should == 200 + end + + it "raises an ArgumentError when block is not given" do + lambda { @uninitialized.send :initialize, @receiver }.should raise_error(ArgumentError) + end + + describe "on frozen instance" do + it "raises a RuntimeError" do + lambda { @uninitialized.freeze.send(:initialize, @receiver) {} }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/lazy_spec.rb b/spec/rubyspec/core/enumerator/lazy/lazy_spec.rb new file mode 100644 index 0000000000..a82a1af271 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/lazy_spec.rb @@ -0,0 +1,16 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Enumerator::Lazy" do + it "is a subclass of Enumerator" do + Enumerator::Lazy.superclass.should equal(Enumerator) + end +end + +describe "Enumerator::Lazy#lazy" do + it "returns self" do + lazy = (1..3).to_enum.lazy + lazy.lazy.should equal(lazy) + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/map_spec.rb b/spec/rubyspec/core/enumerator/lazy/map_spec.rb new file mode 100644 index 0000000000..8ff2573fe5 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/map_spec.rb @@ -0,0 +1,12 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Enumerator::Lazy#map" do + it_behaves_like :enumerator_lazy_collect, :map + + it "doesn't unwrap Arrays" do + Enumerator.new {|y| y.yield([1])}.lazy.to_a.should == [[1]] + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/reject_spec.rb b/spec/rubyspec/core/enumerator/lazy/reject_spec.rb new file mode 100644 index 0000000000..317e927f02 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/reject_spec.rb @@ -0,0 +1,60 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#reject" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.reject {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.reject {}.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.reject(&:even?).first(3).should == [1, 3, 5] + + @eventsmixed.reject { false }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with a gathered array when yield with multiple arguments" do + yields = [] + @yieldsmixed.reject { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields + end + + it "raises an ArgumentError when not given a block" do + lambda { @yieldsmixed.reject }.should raise_error(ArgumentError) + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take(20).reject {}.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.reject { |n| n < 4 }.reject(&:even?).first(3).should == [5, 7, 9] + + @eventsmixed.reject { false }.reject { false }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/select_spec.rb b/spec/rubyspec/core/enumerator/lazy/select_spec.rb new file mode 100644 index 0000000000..ba5823c7ad --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/select_spec.rb @@ -0,0 +1,8 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/select', __FILE__) + +describe "Enumerator::Lazy#select" do + it_behaves_like :enumerator_lazy_select, :select +end diff --git a/spec/rubyspec/core/enumerator/lazy/shared/collect.rb b/spec/rubyspec/core/enumerator/lazy/shared/collect.rb new file mode 100644 index 0000000000..c892784bc2 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/shared/collect.rb @@ -0,0 +1,56 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :enumerator_lazy_collect, shared: true do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.send(@method) {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "keeps size" do + Enumerator::Lazy.new(Object.new, 100) {}.send(@method) {}.size.should == 100 + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.send(@method, &:succ).first(3).should == [1, 2, 3] + + @eventsmixed.send(@method) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with initial values when yield with multiple arguments" do + yields = [] + @yieldsmixed.send(@method) { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields + end + + describe "on a nested Lazy" do + it "keeps size" do + Enumerator::Lazy.new(Object.new, 100) {}.send(@method) {}.send(@method) {}.size.should == 100 + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.send(@method, &:succ).send(@method, &:succ).first(3).should == [2, 3, 4] + + @eventsmixed.send(@method) {}.send(@method) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/shared/collect_concat.rb b/spec/rubyspec/core/enumerator/lazy/shared/collect_concat.rb new file mode 100644 index 0000000000..69bc10c1a4 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/shared/collect_concat.rb @@ -0,0 +1,72 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :enumerator_lazy_collect_concat, shared: true do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.send(@method) {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s }.first(6).should == %w[0 10 20 30 40 50] + + @eventsmixed.send(@method) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "flattens elements when the given block returned an array or responding to .each and .force" do + (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.chars }.first(6).should == %w[0 1 0 2 0 3] + (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should be_true + (0..Float::INFINITY).lazy.send(@method) { |n| (n * 10).to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3] + end + end + + it "calls the block with initial values when yield with multiple arguments" do + yields = [] + @yieldsmixed.send(@method) { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields + end + + it "raises an ArgumentError when not given a block" do + lambda { @yieldsmixed.send(@method) }.should raise_error(ArgumentError) + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.send(@method) {}.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s }.first(6).should == %w[0 10 20 30 40 50] + + @eventsmixed.send(@method) {}.send(@method) {}.first(1) + ScratchPad.recorded.should == [:before_yield] + end + + it "flattens elements when the given block returned an array or responding to .each and .force" do + (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.chars }.first(6).should == %w[0 1 0 2 0 3] + (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char }.first(6).all? { |o| o.instance_of? Enumerator }.should be_true + (0..Float::INFINITY).lazy.map {|n| n * 10 }.send(@method) { |n| n.to_s.each_char.lazy }.first(6).should == %w[0 1 0 2 0 3] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/shared/select.rb b/spec/rubyspec/core/enumerator/lazy/shared/select.rb new file mode 100644 index 0000000000..546256360e --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/shared/select.rb @@ -0,0 +1,60 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :enumerator_lazy_select, shared: true do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.send(@method) {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.send(@method, &:even?).first(3).should == [0, 2, 4] + + @eventsmixed.send(@method) { true }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with a gathered array when yield with multiple arguments" do + yields = [] + @yieldsmixed.send(@method) { |v| yields << v }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.gathered_yields + end + + it "raises an ArgumentError when not given a block" do + lambda { @yieldsmixed.send(@method) }.should raise_error(ArgumentError) + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take(50) {}.send(@method) { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.send(@method) { |n| n > 5 }.send(@method, &:even?).first(3).should == [6, 8, 10] + + @eventsmixed.send(@method) { true }.send(@method) { true }.first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/shared/to_enum.rb b/spec/rubyspec/core/enumerator/lazy/shared/to_enum.rb new file mode 100644 index 0000000000..5e6935b45a --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/shared/to_enum.rb @@ -0,0 +1,50 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../../spec_helper', __FILE__) + +describe :enumerator_lazy_to_enum, shared: true do + before :each do + @infinite = (0..Float::INFINITY).lazy + end + + it "requires multiple arguments" do + Enumerator::Lazy.instance_method(@method).arity.should < 0 + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @infinite.send @method + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@infinite) + end + + it "sets #size to nil when not given a block" do + Enumerator::Lazy.new(Object.new, 100) {}.send(@method).size.should == nil + end + + it "sets given block to size when given a block" do + Enumerator::Lazy.new(Object.new, 100) {}.send(@method) { 30 }.size.should == 30 + end + + it "generates a lazy enumerator from the given name" do + @infinite.send(@method, :with_index, 10).first(3).should == [[0, 10], [1, 11], [2, 12]] + end + + it "passes given arguments to wrapped method" do + @infinite.send(@method, :each_slice, 2).map { |assoc| assoc.first * assoc.last }.first(4).should == [0, 6, 20, 42] + end + + it "used by some parent's methods though returning Lazy" do + { each_with_index: [], + with_index: [], + cycle: [1], + each_with_object: [Object.new], + with_object: [Object.new], + each_slice: [2], + each_entry: [], + each_cons: [2] + }.each_pair do |method, args| + @infinite.method(method).owner.should_not equal(Enumerator::Lazy) + @infinite.send(method, *args).should be_an_instance_of(Enumerator::Lazy) + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/take_spec.rb b/spec/rubyspec/core/enumerator/lazy/take_spec.rb new file mode 100644 index 0000000000..5ef732237d --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/take_spec.rb @@ -0,0 +1,66 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#take" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.take(1) + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets given count to size if the given count is less than old size" do + Enumerator::Lazy.new(Object.new, 100) {}.take(20).size.should == 20 + Enumerator::Lazy.new(Object.new, 100) {}.take(200).size.should == 100 + end + + it "sets given count to size if the old size is Infinity" do + loop.lazy.take(20).size.should == 20 + end + + describe "when the returned lazy enumerator is evaluated by .force" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.take(2).force.should == [0, 1] + + @eventsmixed.take(1).force + ScratchPad.recorded.should == [:before_yield] + end + + it "stops without iterations if the given argument is 0" do + @eventsmixed.take(0).force + ScratchPad.recorded.should == [] + end + end + + describe "on a nested Lazy" do + it "sets given count to size if the given count is less than old size" do + Enumerator::Lazy.new(Object.new, 100) {}.take(20).take(50).size.should == 20 + Enumerator::Lazy.new(Object.new, 100) {}.take(50).take(20).size.should == 20 + end + + describe "when the returned lazy enumerator is evaluated by .force" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.map(&:succ).take(2).force.should == [1, 2] + + @eventsmixed.take(10).take(1).force + ScratchPad.recorded.should == [:before_yield] + end + + it "stops without iterations if the given argument is 0" do + @eventsmixed.take(10).take(0).force + ScratchPad.recorded.should == [] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/take_while_spec.rb b/spec/rubyspec/core/enumerator/lazy/take_while_spec.rb new file mode 100644 index 0000000000..8647dfcaf0 --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/take_while_spec.rb @@ -0,0 +1,60 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#take_while" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.take_while {} + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take_while { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by .force" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.take_while { |n| n < 3 }.force.should == [0, 1, 2] + + @eventsmixed.take_while { false }.force + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with initial values when yield with multiple arguments" do + yields = [] + @yieldsmixed.take_while { |v| yields << v; true }.force + yields.should == EnumeratorLazySpecs::YieldsMixed.initial_yields + end + + it "raises an ArgumentError when not given a block" do + lambda { @yieldsmixed.take_while }.should raise_error(ArgumentError) + end + + describe "on a nested Lazy" do + it "sets #size to nil" do + Enumerator::Lazy.new(Object.new, 100) {}.take(20).take_while { true }.size.should == nil + end + + describe "when the returned lazy enumerator is evaluated by .force" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.take_while { |n| n < 3 }.take_while(&:even?).force.should == [0] + + @eventsmixed.take_while { true }.take_while { false }.force + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/lazy/to_enum_spec.rb b/spec/rubyspec/core/enumerator/lazy/to_enum_spec.rb new file mode 100644 index 0000000000..e0966037ab --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/to_enum_spec.rb @@ -0,0 +1,8 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_enum', __FILE__) + +describe "Enumerator::Lazy#to_enum" do + it_behaves_like :enumerator_lazy_to_enum, :to_enum +end diff --git a/spec/rubyspec/core/enumerator/lazy/zip_spec.rb b/spec/rubyspec/core/enumerator/lazy/zip_spec.rb new file mode 100644 index 0000000000..9c728364ce --- /dev/null +++ b/spec/rubyspec/core/enumerator/lazy/zip_spec.rb @@ -0,0 +1,74 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Enumerator::Lazy#zip" do + before :each do + @yieldsmixed = EnumeratorLazySpecs::YieldsMixed.new.to_enum.lazy + @eventsmixed = EnumeratorLazySpecs::EventsMixed.new.to_enum.lazy + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "returns a new instance of Enumerator::Lazy" do + ret = @yieldsmixed.zip [] + ret.should be_an_instance_of(Enumerator::Lazy) + ret.should_not equal(@yieldsmixed) + end + + it "keeps size" do + Enumerator::Lazy.new(Object.new, 100) {}.zip([], []).size.should == 100 + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.zip([4, 5], [8]).first(2).should == [[0, 4, 8], [1, 5, nil]] + + @eventsmixed.zip([0, 1]).first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + + it "calls the block with a gathered array when yield with multiple arguments" do + yields = @yieldsmixed.zip(EnumeratorLazySpecs::YieldsMixed.new.to_enum).force + yields.should == [EnumeratorLazySpecs::YieldsMixed.gathered_yields, + EnumeratorLazySpecs::YieldsMixed.gathered_yields].transpose + end + + it "returns a Lazy when no arguments given" do + @yieldsmixed.zip.should be_an_instance_of(Enumerator::Lazy) + end + + it "raises a TypeError if arguments contain non-list object" do + lambda { @yieldsmixed.zip [], Object.new, [] }.should raise_error(TypeError) + end + + describe "on a nested Lazy" do + it "keeps size" do + Enumerator::Lazy.new(Object.new, 100) {}.map {}.zip([], []).size.should == 100 + end + + it "behaves as Enumerable#zip when given a block" do + lazy_yields = [] + lazy_ret = @yieldsmixed.zip(EnumeratorLazySpecs::YieldsMixed.new.to_enum) { |lists| lazy_yields << lists } + enum_yields = [] + enum_ret = EnumeratorLazySpecs::YieldsMixed.new.to_enum.zip(EnumeratorLazySpecs::YieldsMixed.new.to_enum) { |lists| enum_yields << lists } + + lazy_yields.should == enum_yields + lazy_ret.should == enum_ret + end + + describe "when the returned lazy enumerator is evaluated by Enumerable#first" do + it "stops after specified times" do + (0..Float::INFINITY).lazy.map(&:succ).zip([4, 5], [8]).first(2).should == [[1, 4, 8], [2, 5, nil]] + + @eventsmixed.zip([0, 1]).zip([0, 1]).first(1) + ScratchPad.recorded.should == [:before_yield] + end + end + end +end diff --git a/spec/rubyspec/core/enumerator/new_spec.rb b/spec/rubyspec/core/enumerator/new_spec.rb new file mode 100644 index 0000000000..e8e0572a40 --- /dev/null +++ b/spec/rubyspec/core/enumerator/new_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/new', __FILE__) + +describe "Enumerator.new" do + it_behaves_like(:enum_new, :new) +end diff --git a/spec/rubyspec/core/enumerator/next_spec.rb b/spec/rubyspec/core/enumerator/next_spec.rb new file mode 100644 index 0000000000..6b3309a2bc --- /dev/null +++ b/spec/rubyspec/core/enumerator/next_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/next', __FILE__) + +describe "Enumerator#next" do + it_behaves_like(:enum_next,:next) +end diff --git a/spec/rubyspec/core/enumerator/next_values_spec.rb b/spec/rubyspec/core/enumerator/next_values_spec.rb new file mode 100644 index 0000000000..2c4b23dc8d --- /dev/null +++ b/spec/rubyspec/core/enumerator/next_values_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator#next_values" do + before :each do + o = Object.new + def o.each + yield :a + yield :b1, :b2 + yield :c + yield :d1, :d2 + yield :e1, :e2, :e3 + yield nil + yield + end + + @e = o.to_enum + end + + it "returns the next element in self" do + @e.next_values.should == [:a] + end + + it "advances the position of the current element" do + @e.next.should == :a + @e.next_values.should == [:b1, :b2] + @e.next.should == :c + end + + it "advances the position of the enumerator each time when called multiple times" do + 2.times { @e.next_values } + @e.next_values.should == [:c] + @e.next.should == [:d1, :d2] + end + + it "works in concert with #rewind" do + 2.times { @e.next } + @e.rewind + @e.next_values.should == [:a] + end + + it "returns an array with only nil if yield is called with nil" do + 5.times { @e.next } + @e.next_values.should == [nil] + end + + it "returns an empty array if yield is called without arguments" do + 6.times { @e.next } + @e.next_values.should == [] + end + + it "raises StopIteration if called on a finished enumerator" do + 7.times { @e.next } + lambda { @e.next_values }.should raise_error(StopIteration) + end +end diff --git a/spec/rubyspec/core/enumerator/peek_spec.rb b/spec/rubyspec/core/enumerator/peek_spec.rb new file mode 100644 index 0000000000..26ac85161b --- /dev/null +++ b/spec/rubyspec/core/enumerator/peek_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator#peek" do + before :each do + @e = (1..5).to_a.to_enum + end + + it "returns the next element in self" do + @e.peek.should == 1 + end + + it "does not advance the position of the current element" do + @e.next.should == 1 + @e.peek.should == 2 + @e.next.should == 2 + end + + it "can be called repeatedly without advancing the position of the current element" do + @e.peek + @e.peek + @e.peek.should == 1 + @e.next.should == 1 + end + + it "works in concert with #rewind" do + @e.next + @e.next + @e.rewind + @e.peek.should == 1 + end + + it "raises StopIteration if called on a finished enumerator" do + 5.times { @e.next } + lambda { @e.peek }.should raise_error(StopIteration) + end +end diff --git a/spec/rubyspec/core/enumerator/peek_values_spec.rb b/spec/rubyspec/core/enumerator/peek_values_spec.rb new file mode 100644 index 0000000000..ecc4758854 --- /dev/null +++ b/spec/rubyspec/core/enumerator/peek_values_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator#peek_values" do + before :each do + o = Object.new + def o.each + yield :a + yield :b1, :b2 + yield :c + yield :d1, :d2 + yield :e1, :e2, :e3 + yield nil + yield + end + + @e = o.to_enum + end + + it "returns the next element in self" do + @e.peek_values.should == [:a] + end + + it "does not advance the position of the current element" do + @e.next.should == :a + @e.peek_values.should == [:b1, :b2] + @e.next.should == [:b1, :b2] + end + + it "can be called repeatedly without advancing the position of the current element" do + @e.peek_values + @e.peek_values + @e.peek_values.should == [:a] + @e.next.should == :a + end + + it "works in concert with #rewind" do + @e.next + @e.next + @e.rewind + @e.peek_values.should == [:a] + end + + it "returns an array with only nil if yield is called with nil" do + 5.times { @e.next } + @e.peek_values.should == [nil] + end + + it "returns an empty array if yield is called without arguments" do + 6.times { @e.next } + @e.peek_values.should == [] + end + + it "raises StopIteration if called on a finished enumerator" do + 7.times { @e.next } + lambda { @e.peek_values }.should raise_error(StopIteration) + end +end diff --git a/spec/rubyspec/core/enumerator/rewind_spec.rb b/spec/rubyspec/core/enumerator/rewind_spec.rb new file mode 100644 index 0000000000..666136d74a --- /dev/null +++ b/spec/rubyspec/core/enumerator/rewind_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/rewind', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Enumerator#rewind" do + it_behaves_like(:enum_rewind, :rewind) + + it "calls the enclosed object's rewind method if one exists" do + obj = mock('rewinder') + enum = obj.to_enum + obj.should_receive(:each).at_most(1) + obj.should_receive(:rewind) + enum.rewind + end + + it "does nothing if the object doesn't have a #rewind method" do + obj = mock('rewinder') + enum = obj.to_enum + obj.should_receive(:each).at_most(1) + lambda { enum.rewind.should == enum }.should_not raise_error + end +end + +describe "Enumerator#rewind" do + before :each do + ScratchPad.record [] + @enum = EnumeratorSpecs::Feed.new.to_enum(:each) + end + + it "clears a pending #feed value" do + @enum.next + @enum.feed :a + @enum.rewind + @enum.next + @enum.next + ScratchPad.recorded.should == [nil] + end +end diff --git a/spec/rubyspec/core/enumerator/size_spec.rb b/spec/rubyspec/core/enumerator/size_spec.rb new file mode 100644 index 0000000000..ef7940dc16 --- /dev/null +++ b/spec/rubyspec/core/enumerator/size_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Enumerator#size" do + it "returns same value if set size is an Integer" do + Enumerator.new(100) {}.size.should == 100 + end + + it "returns nil if set size is nil" do + Enumerator.new(nil) {}.size.should be_nil + end + + it "returns returning value from size.call if set size is a Proc" do + base_size = 100 + enum = Enumerator.new(lambda { base_size + 1 }) {} + base_size = 200 + enum.size.should == 201 + base_size = 300 + enum.size.should == 301 + end + + it "returns the result from size.call if the size respond to call" do + obj = mock('call') + obj.should_receive(:call).and_return(42) + Enumerator.new(obj) {}.size.should == 42 + end +end diff --git a/spec/rubyspec/core/enumerator/to_enum_spec.rb b/spec/rubyspec/core/enumerator/to_enum_spec.rb new file mode 100644 index 0000000000..a719e62212 --- /dev/null +++ b/spec/rubyspec/core/enumerator/to_enum_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/enum_for', __FILE__) + +describe "Enumerator#to_enum" do + it_behaves_like :enum_for, :enum_for +end diff --git a/spec/rubyspec/core/enumerator/with_index_spec.rb b/spec/rubyspec/core/enumerator/with_index_spec.rb new file mode 100644 index 0000000000..3d0ec0a298 --- /dev/null +++ b/spec/rubyspec/core/enumerator/with_index_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/with_index', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Enumerator#with_index" do + it_behaves_like(:enum_with_index, :with_index) + it_behaves_like(:enumeratorized_with_origin_size, :with_index, [1,2,3].select) + + it "returns a new Enumerator when no block is given" do + enum1 = [1,2,3].select + enum2 = enum1.with_index + enum2.should be_an_instance_of(Enumerator) + enum1.should_not === enum2 + end + + it "accepts an optional argument when given a block" do + lambda do + @enum.with_index(1) { |f| f} + end.should_not raise_error(ArgumentError) + end + + it "accepts an optional argument when not given a block" do + lambda do + @enum.with_index(1) + end.should_not raise_error(ArgumentError) + end + + it "numbers indices from the given index when given an offset but no block" do + @enum.with_index(1).to_a.should == [[1,1], [2,2], [3,3], [4,4]] + end + + it "numbers indices from the given index when given an offset and block" do + acc = [] + @enum.with_index(1) {|e,i| acc << [e,i] } + acc.should == [[1,1], [2,2], [3,3], [4,4]] + end + + it "raises a TypeError when the argument cannot be converted to numeric" do + lambda do + @enum.with_index('1') {|*i| i} + end.should raise_error(TypeError) + end + + it "converts non-numeric arguments to Integer via #to_int" do + (o = mock('1')).should_receive(:to_int).and_return(1) + @enum.with_index(o).to_a.should == [[1,1], [2,2], [3,3], [4,4]] + end + + it "coerces the given numeric argument to an Integer" do + @enum.with_index(1.678).to_a.should == [[1,1], [2,2], [3,3], [4,4]] + + res = [] + @enum.with_index(1.001) { |*x| res << x} + res.should == [[1,1], [2,2], [3,3], [4,4]] + end + + it "treats nil argument as no argument" do + @enum.with_index(nil).to_a.should == [[1,0], [2,1], [3,2], [4,3]] + + res = [] + @enum.with_index(nil) { |*x| res << x} + res.should == [[1,0], [2,1], [3,2], [4,3]] + end + + it "accepts negative argument" do + @enum.with_index(-1).to_a.should == [[1,-1], [2,0], [3,1], [4,2]] + + res = [] + @enum.with_index(-1) { |*x| res << x} + res.should == [[1,-1], [2,0], [3,1], [4,2]] + end +end diff --git a/spec/rubyspec/core/enumerator/with_object_spec.rb b/spec/rubyspec/core/enumerator/with_object_spec.rb new file mode 100644 index 0000000000..a7bd74220c --- /dev/null +++ b/spec/rubyspec/core/enumerator/with_object_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/enumerator/with_object', __FILE__) + +describe "Enumerator#with_object" do + it_behaves_like :enum_with_object, :with_object +end diff --git a/spec/rubyspec/core/enumerator/yielder/append_spec.rb b/spec/rubyspec/core/enumerator/yielder/append_spec.rb new file mode 100644 index 0000000000..d2313b01f4 --- /dev/null +++ b/spec/rubyspec/core/enumerator/yielder/append_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Enumerator::Yielder#<<" do + # TODO: There's some common behavior between yield and <<; move to a shared spec + it "yields the value to the block" do + ary = [] + y = Enumerator::Yielder.new {|x| ary << x} + y << 1 + + ary.should == [1] + end + + it "doesn't double-wrap Arrays" do + yields = [] + y = Enumerator::Yielder.new {|args| yields << args } + y << [1] + yields.should == [[1]] + end + + it "returns self" do + y = Enumerator::Yielder.new {|x| x + 1} + (y << 1).should equal(y) + end + + it "requires multiple arguments" do + Enumerator::Yielder.instance_method(:<<).arity.should < 0 + end + + it "yields with passed arguments" do + yields = [] + y = Enumerator::Yielder.new {|*args| yields << args } + y.<<(1, 2) + yields.should == [[1, 2]] + end +end diff --git a/spec/rubyspec/core/enumerator/yielder/initialize_spec.rb b/spec/rubyspec/core/enumerator/yielder/initialize_spec.rb new file mode 100644 index 0000000000..095b6a64c6 --- /dev/null +++ b/spec/rubyspec/core/enumerator/yielder/initialize_spec.rb @@ -0,0 +1,18 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Enumerator::Yielder#initialize" do + before :each do + @class = Enumerator::Yielder + @uninitialized = @class.allocate + end + + it "is a private method" do + @class.should have_private_instance_method(:initialize, false) + end + + it "returns self when given a block" do + @uninitialized.send(:initialize) {}.should equal(@uninitialized) + end +end diff --git a/spec/rubyspec/core/enumerator/yielder/yield_spec.rb b/spec/rubyspec/core/enumerator/yielder/yield_spec.rb new file mode 100644 index 0000000000..be904afef1 --- /dev/null +++ b/spec/rubyspec/core/enumerator/yielder/yield_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Enumerator::Yielder#yield" do + it "yields the value to the block" do + ary = [] + y = Enumerator::Yielder.new {|x| ary << x} + y.yield 1 + + ary.should == [1] + end + + it "returns the result of the block for the given value" do + y = Enumerator::Yielder.new {|x| x + 1} + y.yield(1).should == 2 + end +end diff --git a/spec/rubyspec/core/env/assoc_spec.rb b/spec/rubyspec/core/env/assoc_spec.rb new file mode 100644 index 0000000000..fb10a52b3c --- /dev/null +++ b/spec/rubyspec/core/env/assoc_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.assoc" do + after :each do + ENV.delete("foo") + end + + it "returns an array of the key and value of the environment variable with the given key" do + ENV["foo"] = "bar" + ENV.assoc("foo").should == ["foo", "bar"] + end + + it "returns nil if no environment variable with the given key exists" do + ENV.assoc("foo").should == nil + end + + it "returns the key element coerced with #to_str" do + ENV["foo"] = "bar" + k = mock('key') + k.should_receive(:to_str).and_return("foo") + ENV.assoc(k).should == ["foo", "bar"] + end +end diff --git a/spec/rubyspec/core/env/clear_spec.rb b/spec/rubyspec/core/env/clear_spec.rb new file mode 100644 index 0000000000..c184926cc2 --- /dev/null +++ b/spec/rubyspec/core/env/clear_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.clear" do + it "deletes all environment variables" do + orig = ENV.to_hash + begin + ENV.clear + + # This used 'env' the helper before. That shells out to 'env' which + # itself sets up certain environment variables before it runs, because + # the shell sets them up before it runs any command. + # + # Thusly, you can ONLY test this by asking through ENV itself. + ENV.size.should == 0 + ensure + ENV.replace orig + end + end + +end diff --git a/spec/rubyspec/core/env/delete_if_spec.rb b/spec/rubyspec/core/env/delete_if_spec.rb new file mode 100644 index 0000000000..9a8220ae7d --- /dev/null +++ b/spec/rubyspec/core/env/delete_if_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "ENV.delete_if" do + it "deletes pairs if the block returns true" do + ENV["foo"] = "bar" + ENV.delete_if { |k, v| k == "foo" } + ENV["foo"].should == nil + end + + it "returns ENV even if nothing deleted" do + ENV.delete_if { false }.should_not == nil + end + + it "returns an Enumerator if no block given" do + ENV.delete_if.should be_an_instance_of(Enumerator) + end + + it "deletes pairs through enumerator" do + ENV["foo"] = "bar" + enum = ENV.delete_if + enum.each { |k, v| k == "foo" } + ENV["foo"].should == nil + end + + it_behaves_like :enumeratorized_with_origin_size, :delete_if, ENV +end diff --git a/spec/rubyspec/core/env/delete_spec.rb b/spec/rubyspec/core/env/delete_spec.rb new file mode 100644 index 0000000000..e02adf963f --- /dev/null +++ b/spec/rubyspec/core/env/delete_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.delete" do + after :each do + ENV.delete("foo") + end + + it "removes the variable from the environment" do + ENV["foo"] = "bar" + ENV.delete("foo") + ENV["foo"].should == nil + end + + it "returns the previous value" do + ENV["foo"] = "bar" + ENV.delete("foo").should == "bar" + end + + it "yields the name to the given block if the named environment variable does not exist" do + ENV.delete("foo") + ENV.delete("foo") { |name| ScratchPad.record name } + ScratchPad.recorded.should == "foo" + end +end diff --git a/spec/rubyspec/core/env/each_key_spec.rb b/spec/rubyspec/core/env/each_key_spec.rb new file mode 100644 index 0000000000..82721cdb96 --- /dev/null +++ b/spec/rubyspec/core/env/each_key_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "ENV.each_key" do + + it "returns each key" do + e = [] + orig = ENV.to_hash + begin + ENV.clear + ENV["1"] = "3" + ENV["2"] = "4" + ENV.each_key { |k| e << k } + e.should include("1") + e.should include("2") + ensure + ENV.replace orig + end + end + + it "returns an Enumerator if called without a block" do + ENV.each_key.should be_an_instance_of(Enumerator) + end + + it "returns keys in the locale encoding" do + ENV.each_key do |key| + key.encoding.should == Encoding.find('locale') + end + end + + it_behaves_like :enumeratorized_with_origin_size, :each_key, ENV +end diff --git a/spec/rubyspec/core/env/each_pair_spec.rb b/spec/rubyspec/core/env/each_pair_spec.rb new file mode 100644 index 0000000000..255ccd86c5 --- /dev/null +++ b/spec/rubyspec/core/env/each_pair_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each.rb', __FILE__) + +describe "ENV.each_pair" do + it_behaves_like(:env_each, :each_pair) +end diff --git a/spec/rubyspec/core/env/each_spec.rb b/spec/rubyspec/core/env/each_spec.rb new file mode 100644 index 0000000000..2424c5e4e0 --- /dev/null +++ b/spec/rubyspec/core/env/each_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/each.rb', __FILE__) + +describe "ENV.each" do + it_behaves_like(:env_each, :each) +end diff --git a/spec/rubyspec/core/env/each_value_spec.rb b/spec/rubyspec/core/env/each_value_spec.rb new file mode 100644 index 0000000000..070a1d2cb9 --- /dev/null +++ b/spec/rubyspec/core/env/each_value_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "ENV.each_value" do + + it "returns each value" do + e = [] + orig = ENV.to_hash + begin + ENV.clear + ENV["1"] = "3" + ENV["2"] = "4" + ENV.each_value { |v| e << v } + e.should include("3") + e.should include("4") + ensure + ENV.replace orig + end + end + + it "returns an Enumerator if called without a block" do + ENV.each_value.should be_an_instance_of(Enumerator) + end + + it "uses the locale encoding" do + ENV.each_value do |value| + value.encoding.should == Encoding.find('locale') + end + end + + it_behaves_like :enumeratorized_with_origin_size, :each_value, ENV +end diff --git a/spec/rubyspec/core/env/element_reference_spec.rb b/spec/rubyspec/core/env/element_reference_spec.rb new file mode 100644 index 0000000000..2e6402dd28 --- /dev/null +++ b/spec/rubyspec/core/env/element_reference_spec.rb @@ -0,0 +1,66 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.[]" do + before :each do + @variable = "returns_only_frozen_values" + end + + after :each do + ENV.delete @variable + end + + it "returns nil if the variable isn't found" do + ENV["this_var_is_never_set"].should == nil + end + + it "returns only frozen values" do + ENV[@variable] = "a non-frozen string" + ENV[@variable].frozen?.should == true + end + + platform_is :windows do + it "looks up values case-insensitively" do + ENV[@variable] = "bar" + ENV[@variable.upcase].should == "bar" + end + end +end + +with_feature :encoding do + describe "ENV.[]" do + before :each do + @variable = "env_element_reference_encoding_specs" + + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::ASCII_8BIT + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + + ENV.delete @variable + end + + it "uses the locale encoding if Encoding.default_internal is nil" do + Encoding.default_internal = nil + + locale = Encoding.find('locale') + locale = Encoding::ASCII_8BIT if locale == Encoding::US_ASCII + ENV[@variable] = "\xC3\xB8" + ENV[@variable].encoding.should == locale + end + + it "transcodes from the locale encoding to Encoding.default_internal if set" do + # We cannot reliably know the locale encoding, so we merely check that + # the result string has the expected encoding. + ENV[@variable] = "" + Encoding.default_internal = Encoding::IBM437 + + ENV[@variable].encoding.should equal(Encoding::IBM437) + end + end +end diff --git a/spec/rubyspec/core/env/element_set_spec.rb b/spec/rubyspec/core/env/element_set_spec.rb new file mode 100644 index 0000000000..a80cd0c51e --- /dev/null +++ b/spec/rubyspec/core/env/element_set_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/store.rb', __FILE__) + +describe "ENV.[]=" do + it_behaves_like(:env_store, :[]=) +end diff --git a/spec/rubyspec/core/env/empty_spec.rb b/spec/rubyspec/core/env/empty_spec.rb new file mode 100644 index 0000000000..fa02985a6e --- /dev/null +++ b/spec/rubyspec/core/env/empty_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.empty?" do + + it "returns true if the Environment is empty" do + if ENV.keys.size > 0 + ENV.empty?.should == false + end + orig = ENV.to_hash + begin + ENV.clear + ENV.empty?.should == true + ensure + ENV.replace orig + end + end + + it "returns false if not empty" do + if ENV.keys.size > 0 + ENV.empty?.should == false + end + end +end diff --git a/spec/rubyspec/core/env/fetch_spec.rb b/spec/rubyspec/core/env/fetch_spec.rb new file mode 100644 index 0000000000..708ee91c39 --- /dev/null +++ b/spec/rubyspec/core/env/fetch_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.fetch" do + it "returns a value" do + ENV["foo"] = "bar" + ENV.fetch("foo").should == "bar" + ENV.delete "foo" + end + + it "raises a TypeError if the key is not a String" do + lambda { ENV.fetch :should_never_be_set }.should raise_error(TypeError) + end + + it "raises a KeyError if the key is not found" do + lambda { ENV.fetch "should_never_be_set" }.should raise_error(KeyError) + end + + it "provides the given default parameter" do + ENV.fetch("should_never_be_set", "default").should == "default" + end + + it "provides a default value from a block" do + ENV.fetch("should_never_be_set") { |k| "wanted #{k}" }.should == "wanted should_never_be_set" + end + + it "warns on block and default parameter given" do + lambda do + ENV.fetch("should_never_be_set", "default") { 1 }.should == 1 + end.should complain(/block supersedes default value argument/) + end + + it "uses the locale encoding" do + ENV.fetch(ENV.keys.first).encoding.should == Encoding.find('locale') + end +end diff --git a/spec/rubyspec/core/env/has_key_spec.rb b/spec/rubyspec/core/env/has_key_spec.rb new file mode 100644 index 0000000000..8da2d94265 --- /dev/null +++ b/spec/rubyspec/core/env/has_key_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/include.rb', __FILE__) + +describe "ENV.has_key?" do + it_behaves_like(:env_include, :has_key?) +end diff --git a/spec/rubyspec/core/env/has_value_spec.rb b/spec/rubyspec/core/env/has_value_spec.rb new file mode 100644 index 0000000000..76980a8df4 --- /dev/null +++ b/spec/rubyspec/core/env/has_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/value.rb', __FILE__) + +describe "ENV.has_value?" do + it_behaves_like(:env_value, :has_value?) +end diff --git a/spec/rubyspec/core/env/include_spec.rb b/spec/rubyspec/core/env/include_spec.rb new file mode 100644 index 0000000000..4a716fee85 --- /dev/null +++ b/spec/rubyspec/core/env/include_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/include.rb', __FILE__) + +describe "ENV.include?" do + it_behaves_like(:env_include, :include?) +end diff --git a/spec/rubyspec/core/env/index_spec.rb b/spec/rubyspec/core/env/index_spec.rb new file mode 100644 index 0000000000..95009b3558 --- /dev/null +++ b/spec/rubyspec/core/env/index_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/key.rb', __FILE__) + +describe "ENV.index" do + it_behaves_like(:env_key, :index) +end diff --git a/spec/rubyspec/core/env/indexes_spec.rb b/spec/rubyspec/core/env/indexes_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/core/env/indexes_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/core/env/indices_spec.rb b/spec/rubyspec/core/env/indices_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/core/env/indices_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/core/env/inspect_spec.rb b/spec/rubyspec/core/env/inspect_spec.rb new file mode 100644 index 0000000000..9c4273e188 --- /dev/null +++ b/spec/rubyspec/core/env/inspect_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.inspect" do + + it "returns a String that looks like a Hash with real data" do + ENV["foo"] = "bar" + ENV.inspect.should =~ /\{.*"foo"=>"bar".*\}/ + ENV.delete "foo" + end + +end diff --git a/spec/rubyspec/core/env/invert_spec.rb b/spec/rubyspec/core/env/invert_spec.rb new file mode 100644 index 0000000000..42170230db --- /dev/null +++ b/spec/rubyspec/core/env/invert_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.invert" do + before :each do + ENV["foo"] = "bar" + end + + after :each do + ENV.delete "foo" + end + + it "returns a hash with ENV.keys as the values and vice versa" do + ENV.invert["bar"].should == "foo" + ENV["foo"].should == "bar" + end +end diff --git a/spec/rubyspec/core/env/keep_if_spec.rb b/spec/rubyspec/core/env/keep_if_spec.rb new file mode 100644 index 0000000000..c5bbc3dc05 --- /dev/null +++ b/spec/rubyspec/core/env/keep_if_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "ENV.keep_if" do + before :each do + ENV["foo"] = "bar" + end + + after :each do + ENV.delete "foo" + end + + it "deletes pairs if the block returns false" do + ENV.keep_if { |k, v| k != "foo" } + ENV["foo"].should == nil + end + + it "returns ENV even if nothing deleted" do + ENV.keep_if { true }.should_not == nil + end + + it "returns an Enumerator if no block given" do + ENV.keep_if.should be_an_instance_of(Enumerator) + end + + it "deletes pairs through enumerator" do + enum = ENV.keep_if + enum.each { |k, v| k != "foo" } + ENV["foo"].should == nil + end + + it_behaves_like :enumeratorized_with_origin_size, :keep_if, ENV +end diff --git a/spec/rubyspec/core/env/key_spec.rb b/spec/rubyspec/core/env/key_spec.rb new file mode 100644 index 0000000000..b653b1b1a5 --- /dev/null +++ b/spec/rubyspec/core/env/key_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/include.rb', __FILE__) +require File.expand_path('../shared/key.rb', __FILE__) + +describe "ENV.key?" do + it_behaves_like(:env_include, :key?) +end + +describe "ENV.key" do + it_behaves_like(:env_key, :key) +end diff --git a/spec/rubyspec/core/env/keys_spec.rb b/spec/rubyspec/core/env/keys_spec.rb new file mode 100644 index 0000000000..d79919b79d --- /dev/null +++ b/spec/rubyspec/core/env/keys_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.keys" do + + it "returns all the keys" do + ENV.keys.sort.should == ENV.to_hash.keys.sort + end + + it "returns the keys in the locale encoding" do + ENV.keys.each do |key| + key.encoding.should == Encoding.find('locale') + end + end +end diff --git a/spec/rubyspec/core/env/length_spec.rb b/spec/rubyspec/core/env/length_spec.rb new file mode 100644 index 0000000000..83d1b58c74 --- /dev/null +++ b/spec/rubyspec/core/env/length_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length.rb', __FILE__) + +describe "ENV.length" do + it_behaves_like(:env_length, :length) +end diff --git a/spec/rubyspec/core/env/member_spec.rb b/spec/rubyspec/core/env/member_spec.rb new file mode 100644 index 0000000000..25aa71e973 --- /dev/null +++ b/spec/rubyspec/core/env/member_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/include.rb', __FILE__) + +describe "ENV.member?" do + it_behaves_like(:env_include, :member?) +end diff --git a/spec/rubyspec/core/env/rassoc_spec.rb b/spec/rubyspec/core/env/rassoc_spec.rb new file mode 100644 index 0000000000..3a86e7e158 --- /dev/null +++ b/spec/rubyspec/core/env/rassoc_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.rassoc" do + after :each do + ENV.delete("foo") + end + + it "returns an array of the key and value of the environment variable with the given value" do + ENV["foo"] = "bar" + ENV.rassoc("bar").should == ["foo", "bar"] + end + + it "returns nil if no environment variable with the given value exists" do + ENV.rassoc("bar").should == nil + end + + it "returns the value element coerced with #to_str" do + ENV["foo"] = "bar" + v = mock('value') + v.should_receive(:to_str).and_return("bar") + ENV.rassoc(v).should == ["foo", "bar"] + end +end diff --git a/spec/rubyspec/core/env/rehash_spec.rb b/spec/rubyspec/core/env/rehash_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/core/env/rehash_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/core/env/reject_spec.rb b/spec/rubyspec/core/env/reject_spec.rb new file mode 100644 index 0000000000..0da84425b6 --- /dev/null +++ b/spec/rubyspec/core/env/reject_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "ENV.reject!" do + it "rejects entries based on key" do + ENV["foo"] = "bar" + ENV.reject! { |k, v| k == "foo" } + ENV["foo"].should == nil + end + + it "rejects entries based on value" do + ENV["foo"] = "bar" + ENV.reject! { |k, v| v == "bar" } + ENV["foo"].should == nil + end + + it "returns itself or nil" do + ENV.reject! { false }.should == nil + ENV["foo"] = "bar" + ENV.reject! { |k, v| k == "foo" }.should == ENV + ENV["foo"].should == nil + end + + it "returns an Enumerator if called without a block" do + ENV.reject!.should be_an_instance_of(Enumerator) + end + + it "doesn't raise if empty" do + orig = ENV.to_hash + begin + ENV.clear + lambda { ENV.reject! }.should_not raise_error(LocalJumpError) + ensure + ENV.replace orig + end + end + + it_behaves_like :enumeratorized_with_origin_size, :reject!, ENV +end + +describe "ENV.reject" do + it "rejects entries based on key" do + ENV["foo"] = "bar" + e = ENV.reject { |k, v| k == "foo" } + e["foo"].should == nil + ENV["foo"].should == "bar" + ENV["foo"] = nil + end + + it "rejects entries based on value" do + ENV["foo"] = "bar" + e = ENV.reject { |k, v| v == "bar" } + e["foo"].should == nil + ENV["foo"].should == "bar" + ENV["foo"] = nil + end + + it "returns a Hash" do + ENV.reject { false }.should be_kind_of(Hash) + end + + it "returns an Enumerator if called without a block" do + ENV.reject.should be_an_instance_of(Enumerator) + end + + it "doesn't raise if empty" do + orig = ENV.to_hash + begin + ENV.clear + lambda { ENV.reject }.should_not raise_error(LocalJumpError) + ensure + ENV.replace orig + end + end + + it_behaves_like :enumeratorized_with_origin_size, :reject, ENV +end diff --git a/spec/rubyspec/core/env/replace_spec.rb b/spec/rubyspec/core/env/replace_spec.rb new file mode 100644 index 0000000000..0c11e173ac --- /dev/null +++ b/spec/rubyspec/core/env/replace_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.replace" do + + it "replaces ENV with a Hash" do + ENV["foo"] = "bar" + e = ENV.reject { |k, v| k == "foo" } + e["baz"] = "bam" + ENV.replace e + ENV["foo"].should == nil + ENV["baz"].should == "bam" + ENV.delete "baz" + end + +end diff --git a/spec/rubyspec/core/env/select_spec.rb b/spec/rubyspec/core/env/select_spec.rb new file mode 100644 index 0000000000..8261ff593a --- /dev/null +++ b/spec/rubyspec/core/env/select_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "ENV.select!" do + it "removes environment variables for which the block returns true" do + ENV["foo"] = "bar" + ENV.select! { |k, v| k != "foo" } + ENV["foo"].should == nil + end + + it "returns self if any changes were made" do + ENV["foo"] = "bar" + ENV.select! { |k, v| k != "foo" }.should == ENV + end + + it "returns nil if no changes were made" do + ENV.select! { true }.should == nil + end + + it "returns an Enumerator if called without a block" do + ENV.select!.should be_an_instance_of(Enumerator) + end + + it_behaves_like :enumeratorized_with_origin_size, :select!, ENV +end + +describe "ENV.select" do + it "returns a Hash of names and values for which block return true" do + ENV["foo"] = "bar" + ENV.select { |k, v| k == "foo" }.should == {"foo" => "bar"} + ENV.delete "foo" + end + + it "returns an Enumerator when no block is given" do + ENV.select.should be_an_instance_of(Enumerator) + end + + it_behaves_like :enumeratorized_with_origin_size, :select, ENV +end diff --git a/spec/rubyspec/core/env/shared/each.rb b/spec/rubyspec/core/env/shared/each.rb new file mode 100644 index 0000000000..494fd5cee1 --- /dev/null +++ b/spec/rubyspec/core/env/shared/each.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../enumerable/shared/enumeratorized', __FILE__) + +describe :env_each, shared: true do + it "returns each pair" do + orig = ENV.to_hash + e = [] + begin + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "boo" + ENV.send(@method) { |k, v| e << [k, v] } + e.should include(["foo", "bar"]) + e.should include(["baz", "boo"]) + ensure + ENV.replace orig + end + end + + it "returns an Enumerator if called without a block" do + ENV.send(@method).should be_an_instance_of(Enumerator) + end + + before :all do + @object = ENV + end + it_should_behave_like :enumeratorized_with_origin_size + + with_feature :encoding do + describe "with encoding" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::ASCII_8BIT + + @locale_encoding = Encoding.find "locale" + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "uses the locale encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + + ENV.send(@method) do |key, value| + key.encoding.should equal(@locale_encoding) + value.encoding.should equal(@locale_encoding) + end + end + + it "transcodes from the locale encoding to Encoding.default_internal if set" do + Encoding.default_internal = internal = Encoding::IBM437 + + ENV.send(@method) do |key, value| + key.encoding.should equal(internal) + if value.ascii_only? + value.encoding.should equal(internal) + end + end + end + end + end +end diff --git a/spec/rubyspec/core/env/shared/include.rb b/spec/rubyspec/core/env/shared/include.rb new file mode 100644 index 0000000000..8d8311dcf2 --- /dev/null +++ b/spec/rubyspec/core/env/shared/include.rb @@ -0,0 +1,11 @@ +describe :env_include, shared: true do + it "returns true if ENV has the key" do + ENV["foo"] = "bar" + ENV.send(@method, "foo").should == true + ENV.delete "foo" + end + + it "returns false if ENV doesn't include the key" do + ENV.send(@method, "should_never_be_set").should == false + end +end diff --git a/spec/rubyspec/core/env/shared/key.rb b/spec/rubyspec/core/env/shared/key.rb new file mode 100644 index 0000000000..5e6c21840f --- /dev/null +++ b/spec/rubyspec/core/env/shared/key.rb @@ -0,0 +1,15 @@ +describe :env_key, shared: true do + it "needs to be reviewed for completeness" + + it "returns the index associated with the passed value" do + ENV["foo"] = "bar" + ENV.send(@method, "bar").should == "foo" + ENV.delete "foo" + end + + it "returns nil if the passed value is not found" do + ENV.send(@method, "should_never_be_set").should be_nil + end +end + + diff --git a/spec/rubyspec/core/env/shared/length.rb b/spec/rubyspec/core/env/shared/length.rb new file mode 100644 index 0000000000..6d788a3f4a --- /dev/null +++ b/spec/rubyspec/core/env/shared/length.rb @@ -0,0 +1,13 @@ +describe :env_length, shared: true do + it "returns the number of ENV entries" do + orig = ENV.to_hash + begin + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "boo" + ENV.send(@method).should == 2 + ensure + ENV.replace orig + end + end +end diff --git a/spec/rubyspec/core/env/shared/store.rb b/spec/rubyspec/core/env/shared/store.rb new file mode 100644 index 0000000000..4949ca8c73 --- /dev/null +++ b/spec/rubyspec/core/env/shared/store.rb @@ -0,0 +1,56 @@ +describe :env_store, shared: true do + after :each do + ENV.delete("foo") + end + + it "sets the environment variable to the given value" do + ENV.send(@method, "foo", "bar") + ENV["foo"].should == "bar" + end + + it "returns the value" do + value = "bar" + ENV.send(@method, "foo", value).should equal(value) + end + + it "deletes the environment variable when the value is nil" do + ENV["foo"] = "bar" + ENV.send(@method, "foo", nil) + ENV.key?("foo").should be_false + end + + it "coerces the key argument with #to_str" do + k = mock("key") + k.should_receive(:to_str).and_return("foo") + ENV.send(@method, k, "bar") + ENV["foo"].should == "bar" + end + + it "coerces the value argument with #to_str" do + v = mock("value") + v.should_receive(:to_str).and_return("bar") + ENV.send(@method, "foo", v) + ENV["foo"].should == "bar" + end + + it "raises TypeError when the key is not coercible to String" do + lambda { ENV.send(@method, Object.new, "bar") }.should raise_error(TypeError) + end + + it "raises TypeError when the value is not coercible to String" do + lambda { ENV.send(@method, "foo", Object.new) }.should raise_error(TypeError) + end + + it "raises Errno::EINVAL when the key contains the '=' character" do + lambda { ENV.send(@method, "foo=", "bar") }.should raise_error(Errno::EINVAL) + end + + it "raises Errno::EINVAL when the key is an empty string" do + lambda { ENV.send(@method, "", "bar") }.should raise_error(Errno::EINVAL) + end + + it "does nothing when the key is not a valid environment variable key and the value is nil" do + ENV.send(@method, "foo=", nil) + ENV.key?("foo=").should be_false + end +end diff --git a/spec/rubyspec/core/env/shared/to_hash.rb b/spec/rubyspec/core/env/shared/to_hash.rb new file mode 100644 index 0000000000..3bfbc415f7 --- /dev/null +++ b/spec/rubyspec/core/env/shared/to_hash.rb @@ -0,0 +1,22 @@ +describe :env_to_hash, shared: true do + it "returns the ENV as a hash" do + ENV["foo"] = "bar" + h = ENV.send(@method) + h.should be_an_instance_of(Hash) + h["foo"].should == "bar" + ENV.delete "foo" + end + + it "uses the locale encoding for keys" do + ENV.send(@method).keys.all? {|k| k.encoding == Encoding.find('locale') }.should be_true + end + + it "uses the locale encoding for values" do + ENV.send(@method).values.all? {|v| v.encoding == Encoding.find('locale') }.should be_true + end + + it "duplicates the ENV when converting to a Hash" do + h = ENV.send(@method) + h.object_id.should_not == ENV.object_id + end +end diff --git a/spec/rubyspec/core/env/shared/value.rb b/spec/rubyspec/core/env/shared/value.rb new file mode 100644 index 0000000000..d9ee90f12d --- /dev/null +++ b/spec/rubyspec/core/env/shared/value.rb @@ -0,0 +1,11 @@ +describe :env_value, shared: true do + it "returns true if ENV has the value" do + ENV["foo"] = "bar" + ENV.send(@method, "bar").should == true + ENV["foo"] = nil + end + + it "returns false if ENV doesn't have the value" do + ENV.send(@method, "this_value_should_never_exist").should == false + end +end diff --git a/spec/rubyspec/core/env/shift_spec.rb b/spec/rubyspec/core/env/shift_spec.rb new file mode 100644 index 0000000000..bae6a17ba0 --- /dev/null +++ b/spec/rubyspec/core/env/shift_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.shift" do + it "returns a pair and deletes it" do + ENV.empty?.should == false + orig = ENV.to_hash + begin + pair = ENV.shift + ENV.has_key?(pair.first).should == false + ensure + ENV.replace orig + end + ENV.has_key?(pair.first).should == true + end + + it "returns nil if ENV.empty?" do + orig = ENV.to_hash + begin + ENV.clear + ENV.shift.should == nil + ensure + ENV.replace orig + end + end +end + +with_feature :encoding do + describe "ENV.shift" do + before :each do + @orig = ENV.to_hash + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::ASCII_8BIT + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + ENV.replace @orig + end + + it "uses the locale encoding if Encoding.default_internal is nil" do + Encoding.default_internal = nil + + pair = ENV.shift + pair.first.encoding.should equal(Encoding.find("locale")) + pair.last.encoding.should equal(Encoding.find("locale")) + end + + it "transcodes from the locale encoding to Encoding.default_internal if set" do + Encoding.default_internal = Encoding::IBM437 + + pair = ENV.shift + pair.first.encoding.should equal(Encoding::IBM437) + pair.last.encoding.should equal(Encoding::IBM437) + end + end +end diff --git a/spec/rubyspec/core/env/size_spec.rb b/spec/rubyspec/core/env/size_spec.rb new file mode 100644 index 0000000000..882ceac485 --- /dev/null +++ b/spec/rubyspec/core/env/size_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length.rb', __FILE__) + +describe "ENV.size" do + it_behaves_like(:env_length, :size) +end diff --git a/spec/rubyspec/core/env/store_spec.rb b/spec/rubyspec/core/env/store_spec.rb new file mode 100644 index 0000000000..1ee5ce020e --- /dev/null +++ b/spec/rubyspec/core/env/store_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/store.rb', __FILE__) + +describe "ENV.store" do + it_behaves_like(:env_store, :store) +end diff --git a/spec/rubyspec/core/env/to_a_spec.rb b/spec/rubyspec/core/env/to_a_spec.rb new file mode 100644 index 0000000000..ffb66b8767 --- /dev/null +++ b/spec/rubyspec/core/env/to_a_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.to_a" do + + it "returns the ENV as an array" do + ENV["foo"] = "bar" + a = ENV.to_a + a.is_a?(Array).should == true + a.find { |e| e.first == "foo" }.should == ["foo", "bar"] + ENV.delete "foo" + end + + it "returns the entries in the locale encoding" do + ENV.to_a.each do |key, value| + key.encoding.should == Encoding.find('locale') + value.encoding.should == Encoding.find('locale') + end + end +end diff --git a/spec/rubyspec/core/env/to_h_spec.rb b/spec/rubyspec/core/env/to_h_spec.rb new file mode 100644 index 0000000000..d0fef5382b --- /dev/null +++ b/spec/rubyspec/core/env/to_h_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_hash.rb', __FILE__) + +describe "ENV.to_hash" do + it_behaves_like(:env_to_hash, :to_h) +end diff --git a/spec/rubyspec/core/env/to_hash_spec.rb b/spec/rubyspec/core/env/to_hash_spec.rb new file mode 100644 index 0000000000..3362fa9307 --- /dev/null +++ b/spec/rubyspec/core/env/to_hash_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_hash.rb', __FILE__) + +describe "ENV.to_hash" do + it_behaves_like(:env_to_hash, :to_hash) +end diff --git a/spec/rubyspec/core/env/to_s_spec.rb b/spec/rubyspec/core/env/to_s_spec.rb new file mode 100644 index 0000000000..10aca09723 --- /dev/null +++ b/spec/rubyspec/core/env/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.to_s" do + it "returns \"ENV\"" do + ENV.to_s.should == "ENV" + end +end diff --git a/spec/rubyspec/core/env/update_spec.rb b/spec/rubyspec/core/env/update_spec.rb new file mode 100644 index 0000000000..7ebfbb313d --- /dev/null +++ b/spec/rubyspec/core/env/update_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.update" do + + it "adds the parameter hash to ENV" do + ENV["foo"].should == nil + ENV.update "foo" => "bar" + ENV["foo"].should == "bar" + ENV.delete "foo" + end + + it "yields key, the old value and the new value when replacing entries" do + ENV.update "foo" => "bar" + ENV["foo"].should == "bar" + ENV.update("foo" => "boo") do |key, old, new| + key.should == "foo" + old.should == "bar" + new.should == "boo" + "rab" + end + ENV["foo"].should == "rab" + ENV.delete "foo" + end + +end diff --git a/spec/rubyspec/core/env/value_spec.rb b/spec/rubyspec/core/env/value_spec.rb new file mode 100644 index 0000000000..c7eb7c5376 --- /dev/null +++ b/spec/rubyspec/core/env/value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/value.rb', __FILE__) + +describe "ENV.value?" do + it_behaves_like(:env_value, :value?) +end diff --git a/spec/rubyspec/core/env/values_at_spec.rb b/spec/rubyspec/core/env/values_at_spec.rb new file mode 100644 index 0000000000..efc7de2a05 --- /dev/null +++ b/spec/rubyspec/core/env/values_at_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.values_at" do + + it "returns an array of the values referenced by the parameters as keys" do + ENV["foo"] = "oof" + ENV["bar"] = "rab" + ENV.values_at.should == [] + ENV.values_at("bar", "foo").should == ["rab", "oof"] + ENV.delete "foo" + ENV.delete "bar" + end + + it "uses the locale encoding" do + ENV.values_at(ENV.keys.first).first.encoding.should == Encoding.find('locale') + end +end diff --git a/spec/rubyspec/core/env/values_spec.rb b/spec/rubyspec/core/env/values_spec.rb new file mode 100644 index 0000000000..3a620bdb8b --- /dev/null +++ b/spec/rubyspec/core/env/values_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ENV.values" do + + it "returns an array of the values" do + orig = ENV.to_hash + begin + ENV.replace "a" => "b", "c" => "d" + a = ENV.values + a.sort.should == ["b", "d"] + ensure + ENV.replace orig + end + end + + it "uses the locale encoding" do + ENV.values.each do |value| + value.encoding.should == Encoding.find('locale') + end + end +end diff --git a/spec/rubyspec/core/exception/args_spec.rb b/spec/rubyspec/core/exception/args_spec.rb new file mode 100644 index 0000000000..410e21edfb --- /dev/null +++ b/spec/rubyspec/core/exception/args_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NoMethodError#args" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/arguments_spec.rb b/spec/rubyspec/core/exception/arguments_spec.rb new file mode 100644 index 0000000000..47c4339e2d --- /dev/null +++ b/spec/rubyspec/core/exception/arguments_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ArgumentError" do + it "is a subclass of StandardError" do + StandardError.should be_ancestor_of(ArgumentError) + end + + it "gives its own class name as message if it has no message" do + ArgumentError.new.message.should == "ArgumentError" + end +end diff --git a/spec/rubyspec/core/exception/backtrace_spec.rb b/spec/rubyspec/core/exception/backtrace_spec.rb new file mode 100644 index 0000000000..2d115e9b2f --- /dev/null +++ b/spec/rubyspec/core/exception/backtrace_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Exception#backtrace" do + before :each do + @backtrace = ExceptionSpecs::Backtrace.backtrace + end + + it "returns nil if no backtrace was set" do + Exception.new.backtrace.should be_nil + end + + it "returns an Array" do + @backtrace.should be_an_instance_of(Array) + end + + it "sets each element to a String" do + @backtrace.each {|l| l.should be_an_instance_of(String)} + end + + it "includes the filename of the location where self raised in the first element" do + @backtrace.first.should =~ /common\.rb/ + end + + it "includes the line number of the location where self raised in the first element" do + @backtrace.first.should =~ /:7:in / + end + + it "includes the name of the method from where self raised in the first element" do + @backtrace.first.should =~ /in `backtrace'/ + end + + it "includes the filename of the location immediately prior to where self raised in the second element" do + @backtrace[1].should =~ /backtrace_spec\.rb/ + end + + it "includes the line number of the location immediately prior to where self raised in the second element" do + @backtrace[1].should =~ /:6(:in )?/ + end + + it "contains lines of the same format for each prior position in the stack" do + @backtrace[2..-1].each do |line| + # This regexp is deliberately imprecise to account for the need to abstract out + # the paths of the included mspec files and the desire to avoid specifying in any + # detail what the in `...' portion looks like. + line.should =~ /^[^ ]+\:\d+(:in `[^`]+')?$/ + end + end + + it "produces a backtrace for an exception captured using $!" do + exception = begin + raise + rescue RuntimeError + $! + end + + exception.backtrace.first.should =~ /backtrace_spec/ + end + + it "returns an Array that can be updated" do + begin + raise + rescue RuntimeError => e + e.backtrace.unshift "backtrace first" + e.backtrace[0].should == "backtrace first" + end + end +end diff --git a/spec/rubyspec/core/exception/case_compare_spec.rb b/spec/rubyspec/core/exception/case_compare_spec.rb new file mode 100644 index 0000000000..7592564855 --- /dev/null +++ b/spec/rubyspec/core/exception/case_compare_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SystemCallError.===" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/cause_spec.rb b/spec/rubyspec/core/exception/cause_spec.rb new file mode 100644 index 0000000000..a1aa39ae34 --- /dev/null +++ b/spec/rubyspec/core/exception/cause_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Exception#cause" do + it "returns the active exception when an exception is raised" do + begin + raise Exception, "the cause" + rescue Exception + begin + raise RuntimeError, "the consequence" + rescue RuntimeError => e + e.should be_an_instance_of(RuntimeError) + e.message.should == "the consequence" + + e.cause.should be_an_instance_of(Exception) + e.cause.message.should == "the cause" + end + end + end +end diff --git a/spec/rubyspec/core/exception/destination_encoding_name_spec.rb b/spec/rubyspec/core/exception/destination_encoding_name_spec.rb new file mode 100644 index 0000000000..d6a01c3220 --- /dev/null +++ b/spec/rubyspec/core/exception/destination_encoding_name_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::UndefinedConversionError#destination_encoding_name" do + it "needs to be reviewed for spec completeness" +end + +describe "Encoding::InvalidByteSequenceError#destination_encoding_name" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/destination_encoding_spec.rb b/spec/rubyspec/core/exception/destination_encoding_spec.rb new file mode 100644 index 0000000000..09064a01f3 --- /dev/null +++ b/spec/rubyspec/core/exception/destination_encoding_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::UndefinedConversionError#destination_encoding" do + it "needs to be reviewed for spec completeness" +end + +describe "Encoding::InvalidByteSequenceError#destination_encoding" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/equal_value_spec.rb b/spec/rubyspec/core/exception/equal_value_spec.rb new file mode 100644 index 0000000000..3aad809377 --- /dev/null +++ b/spec/rubyspec/core/exception/equal_value_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Exception#==" do + it "returns true if both exceptions are the same object" do + e = ArgumentError.new + e.should == e + end + + it "returns true if one exception is the dup'd copy of the other" do + e = ArgumentError.new + e.should == e.dup + end + + it "returns true if both exceptions have the same class, no message, and no backtrace" do + RuntimeError.new.should == RuntimeError.new + end + + it "returns true if both exceptions have the same class, the same message, and no backtrace" do + TypeError.new("message").should == TypeError.new("message") + end + + it "returns true if both exceptions have the same class, the same message, and the same backtrace" do + one = TypeError.new("message") + one.set_backtrace [File.dirname(__FILE__)] + two = TypeError.new("message") + two.set_backtrace [File.dirname(__FILE__)] + one.should == two + end + + it "returns false if the two exceptions inherit from Exception but have different classes" do + one = RuntimeError.new("message") + one.set_backtrace [File.dirname(__FILE__)] + one.should be_kind_of(Exception) + two = TypeError.new("message") + two.set_backtrace [File.dirname(__FILE__)] + two.should be_kind_of(Exception) + one.should_not == two + end + + it "returns true if the two objects subclass Exception and have the same message and backtrace" do + one = ExceptionSpecs::UnExceptional.new + two = ExceptionSpecs::UnExceptional.new + one.message.should == two.message + two.backtrace.should == two.backtrace + one.should == two + end + + it "returns false if the argument is not an Exception" do + ArgumentError.new.should_not == String.new + end + + it "returns false if the two exceptions differ only in their backtrace" do + one = RuntimeError.new("message") + one.set_backtrace [File.dirname(__FILE__)] + two = RuntimeError.new("message") + two.set_backtrace nil + one.should_not == two + end + + it "returns false if the two exceptions differ only in their message" do + one = RuntimeError.new("message") + one.set_backtrace [File.dirname(__FILE__)] + two = RuntimeError.new("message2") + two.set_backtrace [File.dirname(__FILE__)] + one.should_not == two + end +end diff --git a/spec/rubyspec/core/exception/errno_spec.rb b/spec/rubyspec/core/exception/errno_spec.rb new file mode 100644 index 0000000000..f7f5b45d8a --- /dev/null +++ b/spec/rubyspec/core/exception/errno_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "SystemCallError#errno" do + it "needs to be reviewed for spec completeness" +end + +describe "Errno::EINVAL.new" do + it "can be called with no arguments" do + exc = Errno::EINVAL.new + exc.should be_an_instance_of(Errno::EINVAL) + exc.errno.should == Errno::EINVAL::Errno + exc.message.should == "Invalid argument" + end + + it "accepts an optional custom message" do + exc = Errno::EINVAL.new('custom message') + exc.should be_an_instance_of(Errno::EINVAL) + exc.errno.should == Errno::EINVAL::Errno + exc.message.should == "Invalid argument - custom message" + end + + it "accepts an optional custom message and location" do + exc = Errno::EINVAL.new('custom message', 'location') + exc.should be_an_instance_of(Errno::EINVAL) + exc.errno.should == Errno::EINVAL::Errno + exc.message.should == "Invalid argument @ location - custom message" + end +end + +describe "Errno::EMFILE" do + it "can be subclassed" do + ExceptionSpecs::EMFILESub = Class.new(Errno::EMFILE) + exc = ExceptionSpecs::EMFILESub.new + exc.should be_an_instance_of(ExceptionSpecs::EMFILESub) + end +end + +describe "Errno::EAGAIN" do + # From http://jira.codehaus.org/browse/JRUBY-4747 + it "is the same class as Errno::EWOULDBLOCK if they represent the same errno value" do + if Errno::EAGAIN::Errno == Errno::EWOULDBLOCK::Errno + Errno::EAGAIN.should == Errno::EWOULDBLOCK + else + Errno::EAGAIN.should_not == Errno::EWOULDBLOCK + end + end +end diff --git a/spec/rubyspec/core/exception/error_bytes_spec.rb b/spec/rubyspec/core/exception/error_bytes_spec.rb new file mode 100644 index 0000000000..e5027d0cf3 --- /dev/null +++ b/spec/rubyspec/core/exception/error_bytes_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::InvalidByteSequenceError#error_bytes" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/error_char_spec.rb b/spec/rubyspec/core/exception/error_char_spec.rb new file mode 100644 index 0000000000..8842424e90 --- /dev/null +++ b/spec/rubyspec/core/exception/error_char_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::UndefinedConversionError#error_char" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/exception_spec.rb b/spec/rubyspec/core/exception/exception_spec.rb new file mode 100644 index 0000000000..afa482b9d7 --- /dev/null +++ b/spec/rubyspec/core/exception/exception_spec.rb @@ -0,0 +1,83 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "Exception.exception" do + it_behaves_like(:exception_new, :exception) +end + +describe "Exception" do + it "is a Class" do + Exception.should be_kind_of(Class) + end + + it "is a superclass of NoMemoryError" do + Exception.should be_ancestor_of(NoMemoryError) + end + + it "is a superclass of ScriptError" do + Exception.should be_ancestor_of(ScriptError) + end + + it "is a superclass of SignalException" do + Exception.should be_ancestor_of(SignalException) + end + + it "is a superclass of Interrupt" do + SignalException.should be_ancestor_of(Interrupt) + end + + it "is a superclass of StandardError" do + Exception.should be_ancestor_of(StandardError) + end + + it "is a superclass of SystemExit" do + Exception.should be_ancestor_of(SystemExit) + end + + it "is a superclass of SystemStackError" do + Exception.should be_ancestor_of(SystemStackError) + end + + it "is a superclass of SecurityError" do + Exception.should be_ancestor_of(SecurityError) + end + + it "is a superclass of EncodingError" do + Exception.should be_ancestor_of(EncodingError) + end +end + +describe "Exception#exception" do + it "returns self when passed no argument" do + e = RuntimeError.new + e.should == e.exception + end + + it "returns self when passed self as an argument" do + e = RuntimeError.new + e.should == e.exception(e) + end + + it "returns an exception of the same class as self with the message given as argument" do + e = RuntimeError.new + e2 = e.exception("message") + e2.should be_an_instance_of(RuntimeError) + e2.message.should == "message" + end + + class CustomArgumentError < StandardError + attr_reader :val + def initialize(val) + @val = val + end + end + + it "returns an exception of the same class as self with the message given as argument, but without reinitializing" do + e = CustomArgumentError.new(:boom) + e2 = e.exception("message") + e2.should be_an_instance_of(CustomArgumentError) + e2.val.should == :boom + e2.message.should == "message" + end +end diff --git a/spec/rubyspec/core/exception/exit_value_spec.rb b/spec/rubyspec/core/exception/exit_value_spec.rb new file mode 100644 index 0000000000..daa5eb0b94 --- /dev/null +++ b/spec/rubyspec/core/exception/exit_value_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "LocalJumpError#exit_value" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/fixtures/common.rb b/spec/rubyspec/core/exception/fixtures/common.rb new file mode 100644 index 0000000000..51dd0bf9ed --- /dev/null +++ b/spec/rubyspec/core/exception/fixtures/common.rb @@ -0,0 +1,64 @@ +module ExceptionSpecs + class Exceptional < Exception; end + + class Backtrace + def self.backtrace + begin + raise # Do not move this line or update backtrace_spec.rb + rescue RuntimeError => e + e.backtrace + end + end + end + + class UnExceptional < Exception + def backtrace + nil + end + def message + nil + end + end + + class ConstructorException < Exception + + def initialize + end + + end + + class OverrideToS < RuntimeError + def to_s + "this is from #to_s" + end + end + + class EmptyToS < RuntimeError + def to_s + "" + end + end +end + +module NoMethodErrorSpecs + class NoMethodErrorA; end + + class NoMethodErrorB; end + + class NoMethodErrorC; + protected + def a_protected_method;end + private + def a_private_method; end + end + + class NoMethodErrorD; end +end + +class NameErrorSpecs + class ReceiverClass + def call_undefined_class_variable + @@doesnt_exist + end + end +end diff --git a/spec/rubyspec/core/exception/incomplete_input_spec.rb b/spec/rubyspec/core/exception/incomplete_input_spec.rb new file mode 100644 index 0000000000..a64d4be3f3 --- /dev/null +++ b/spec/rubyspec/core/exception/incomplete_input_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::InvalidByteSequenceError#incomplete_input?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/initialize_spec.rb b/spec/rubyspec/core/exception/initialize_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/core/exception/initialize_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/core/exception/inspect_spec.rb b/spec/rubyspec/core/exception/inspect_spec.rb new file mode 100644 index 0000000000..5b06ffee71 --- /dev/null +++ b/spec/rubyspec/core/exception/inspect_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Exception#inspect" do + it "returns '#' when no message given" do + Exception.new.inspect.should == "#" + end + + it "includes #to_s when the result is non-empty" do + ExceptionSpecs::OverrideToS.new.inspect.should == "#" + end + + it "returns the class name when #to_s returns an empty string" do + ExceptionSpecs::EmptyToS.new.inspect.should == "ExceptionSpecs::EmptyToS" + end + + it "returns the derived class name with a subclassed Exception" do + ExceptionSpecs::UnExceptional.new.inspect.should == "#" + end +end diff --git a/spec/rubyspec/core/exception/interrupt_spec.rb b/spec/rubyspec/core/exception/interrupt_spec.rb new file mode 100644 index 0000000000..ef24743936 --- /dev/null +++ b/spec/rubyspec/core/exception/interrupt_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Interrupt" do + it "is a subclass of SignalException" do + Interrupt.superclass.should == SignalException + end +end + +describe "Interrupt.new" do + it "returns an instance of interrupt with no message given" do + e = Interrupt.new + e.signo.should == Signal.list["INT"] + e.signm.should == "Interrupt" + end + + it "takes an optional message argument" do + e = Interrupt.new("message") + e.signo.should == Signal.list["INT"] + e.signm.should == "message" + end +end + +describe "rescueing Interrupt" do + before do + @original_sigint_proc = Signal.trap(:INT, :SIG_DFL) + end + + after do + Signal.trap(:INT, @original_sigint_proc) + end + + it "raises an Interrupt when sent a signal SIGINT" do + begin + Process.kill :INT, Process.pid + sleep + rescue Interrupt => e + e.signo.should == Signal.list["INT"] + e.signm.should == "" + end + end +end diff --git a/spec/rubyspec/core/exception/io_error_spec.rb b/spec/rubyspec/core/exception/io_error_spec.rb new file mode 100644 index 0000000000..0971be332f --- /dev/null +++ b/spec/rubyspec/core/exception/io_error_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IOError" do + it "is a superclass of EOFError" do + IOError.should be_ancestor_of(EOFError) + end +end + +describe "IO::EAGAINWaitReadable" do + it "combines Errno::EAGAIN and IO::WaitReadable" do + IO::EAGAINWaitReadable.superclass.should == Errno::EAGAIN + IO::EAGAINWaitReadable.ancestors.should include IO::WaitReadable + end + + it "is the same as IO::EWOULDBLOCKWaitReadable if Errno::EAGAIN is the same as Errno::EWOULDBLOCK" do + if Errno::EAGAIN.equal? Errno::EWOULDBLOCK + IO::EAGAINWaitReadable.should equal IO::EWOULDBLOCKWaitReadable + else + IO::EAGAINWaitReadable.should_not equal IO::EWOULDBLOCKWaitReadable + end + end +end + +describe "IO::EWOULDBLOCKWaitReadable" do + it "combines Errno::EWOULDBLOCK and IO::WaitReadable" do + IO::EWOULDBLOCKWaitReadable.superclass.should == Errno::EWOULDBLOCK + IO::EAGAINWaitReadable.ancestors.should include IO::WaitReadable + end +end + +describe "IO::EAGAINWaitWritable" do + it "combines Errno::EAGAIN and IO::WaitWritable" do + IO::EAGAINWaitWritable.superclass.should == Errno::EAGAIN + IO::EAGAINWaitWritable.ancestors.should include IO::WaitWritable + end + + it "is the same as IO::EWOULDBLOCKWaitWritable if Errno::EAGAIN is the same as Errno::EWOULDBLOCK" do + if Errno::EAGAIN.equal? Errno::EWOULDBLOCK + IO::EAGAINWaitWritable.should equal IO::EWOULDBLOCKWaitWritable + else + IO::EAGAINWaitWritable.should_not equal IO::EWOULDBLOCKWaitWritable + end + end +end + +describe "IO::EWOULDBLOCKWaitWritable" do + it "combines Errno::EWOULDBLOCK and IO::WaitWritable" do + IO::EWOULDBLOCKWaitWritable.superclass.should == Errno::EWOULDBLOCK + IO::EAGAINWaitWritable.ancestors.should include IO::WaitWritable + end +end diff --git a/spec/rubyspec/core/exception/load_error_spec.rb b/spec/rubyspec/core/exception/load_error_spec.rb new file mode 100644 index 0000000000..2999c66117 --- /dev/null +++ b/spec/rubyspec/core/exception/load_error_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "LoadError#path" do + before :each do + @le = LoadError.new + end + + it "is nil when constructed directly" do + @le.path.should == nil + end +end + +describe "LoadError raised by load or require" do + it "provides the failing path in its #path attribute" do + begin + require 'file_that_does_not_exist' + rescue LoadError => le + le.path.should == 'file_that_does_not_exist' + end + end +end diff --git a/spec/rubyspec/core/exception/message_spec.rb b/spec/rubyspec/core/exception/message_spec.rb new file mode 100644 index 0000000000..7eee6d99de --- /dev/null +++ b/spec/rubyspec/core/exception/message_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Exception#message" do + it "returns the class name if there is no message" do + Exception.new.message.should == "Exception" + end + + it "returns the message passed to #initialize" do + Exception.new("Ouch!").message.should == "Ouch!" + end + + it "calls #to_s on self" do + exc = ExceptionSpecs::OverrideToS.new("you won't see this") + exc.message.should == "this is from #to_s" + end + + context "when #backtrace is redefined" do + it "returns the Exception message" do + e = Exception.new + e.message.should == 'Exception' + + def e.backtrace; []; end + e.message.should == 'Exception' + end + end +end diff --git a/spec/rubyspec/core/exception/name_error_spec.rb b/spec/rubyspec/core/exception/name_error_spec.rb new file mode 100644 index 0000000000..e5b19d6219 --- /dev/null +++ b/spec/rubyspec/core/exception/name_error_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NameError" do + it "is a superclass of NoMethodError" do + NameError.should be_ancestor_of(NoMethodError) + end +end + +describe "NameError.new" do + it "should take optional name argument" do + NameError.new("msg","name").name.should == "name" + end +end diff --git a/spec/rubyspec/core/exception/name_spec.rb b/spec/rubyspec/core/exception/name_spec.rb new file mode 100644 index 0000000000..e8a3c011d2 --- /dev/null +++ b/spec/rubyspec/core/exception/name_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NameError#name" do + it "returns a method name as a symbol" do + -> { + doesnt_exist + }.should raise_error(NameError) {|e| e.name.should == :doesnt_exist } + end + + it "returns a constant name as a symbol" do + -> { + DoesntExist + }.should raise_error(NameError) {|e| e.name.should == :DoesntExist } + end + + it "returns a constant name without namespace as a symbol" do + -> { + Object::DoesntExist + }.should raise_error(NameError) {|e| e.name.should == :DoesntExist } + end + + it "returns a class variable name as a symbol" do + -> { + -> { + @@doesnt_exist + }.should complain(/class variable access from toplevel/) + }.should raise_error(NameError) { |e| e.name.should == :@@doesnt_exist } + end + + ruby_version_is ""..."2.3" do + it "always returns a symbol when a NameError is raised from #instance_variable_get" do + -> { + Object.new.instance_variable_get("invalid_ivar_name") + }.should raise_error(NameError) { |e| e.name.should == :invalid_ivar_name } + end + + it "always returns a symbol when a NameError is raised from #class_variable_get" do + -> { + Object.class_variable_get("invalid_cvar_name") + }.should raise_error(NameError) { |e| e.name.should == :invalid_cvar_name } + end + end + + ruby_version_is "2.3" do + it "returns the first argument passed to the method when a NameError is raised from #instance_variable_get" do + invalid_ivar_name = "invalid_ivar_name" + + -> { + Object.new.instance_variable_get(invalid_ivar_name) + }.should raise_error(NameError) {|e| e.name.should equal(invalid_ivar_name) } + end + + it "returns the first argument passed to the method when a NameError is raised from #class_variable_get" do + invalid_cvar_name = "invalid_cvar_name" + + -> { + Object.class_variable_get(invalid_cvar_name) + }.should raise_error(NameError) {|e| e.name.should equal(invalid_cvar_name) } + end + end +end diff --git a/spec/rubyspec/core/exception/new_spec.rb b/spec/rubyspec/core/exception/new_spec.rb new file mode 100644 index 0000000000..61d35a1dfa --- /dev/null +++ b/spec/rubyspec/core/exception/new_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "Exception.new" do + it_behaves_like(:exception_new, :new) +end diff --git a/spec/rubyspec/core/exception/no_method_error_spec.rb b/spec/rubyspec/core/exception/no_method_error_spec.rb new file mode 100644 index 0000000000..cf3fe58b1d --- /dev/null +++ b/spec/rubyspec/core/exception/no_method_error_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "NoMethodError.new" do + it "allows passing method args" do + NoMethodError.new("msg","name","args").args.should == "args" + end + + it "does not require a name" do + NoMethodError.new("msg").message.should == "msg" + end +end + +describe "NoMethodError#args" do + it "returns an empty array if the caller method had no arguments" do + begin + NoMethodErrorSpecs::NoMethodErrorB.new.foo + rescue Exception => e + e.args.should == [] + end + end + + it "returns an array with the same elements as passed to the method" do + begin + a = NoMethodErrorSpecs::NoMethodErrorA.new + NoMethodErrorSpecs::NoMethodErrorB.new.foo(1,a) + rescue Exception => e + e.args.should == [1,a] + e.args[1].object_id.should == a.object_id + end + end +end + +describe "NoMethodError#message" do + it "for an undefined method match /undefined method/" do + begin + NoMethodErrorSpecs::NoMethodErrorD.new.foo + rescue Exception => e + e.should be_kind_of(NoMethodError) + end + end + + it "for an protected method match /protected method/" do + begin + NoMethodErrorSpecs::NoMethodErrorC.new.a_protected_method + rescue Exception => e + e.should be_kind_of(NoMethodError) + end + end + + it "for private method match /private method/" do + begin + NoMethodErrorSpecs::NoMethodErrorC.new.a_private_method + rescue Exception => e + e.should be_kind_of(NoMethodError) + e.message.match(/private method/).should_not == nil + end + end +end diff --git a/spec/rubyspec/core/exception/range_error_spec.rb b/spec/rubyspec/core/exception/range_error_spec.rb new file mode 100644 index 0000000000..9c0462bbf7 --- /dev/null +++ b/spec/rubyspec/core/exception/range_error_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "RangeError" do + it "is a superclass of FloatDomainError" do + RangeError.should be_ancestor_of(FloatDomainError) + end +end diff --git a/spec/rubyspec/core/exception/readagain_bytes_spec.rb b/spec/rubyspec/core/exception/readagain_bytes_spec.rb new file mode 100644 index 0000000000..30efb67686 --- /dev/null +++ b/spec/rubyspec/core/exception/readagain_bytes_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::InvalidByteSequenceError#readagain_bytes" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/reason_spec.rb b/spec/rubyspec/core/exception/reason_spec.rb new file mode 100644 index 0000000000..fad4d47c64 --- /dev/null +++ b/spec/rubyspec/core/exception/reason_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "LocalJumpError#reason" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/receiver_spec.rb b/spec/rubyspec/core/exception/receiver_spec.rb new file mode 100644 index 0000000000..83f8d5927c --- /dev/null +++ b/spec/rubyspec/core/exception/receiver_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +ruby_version_is "2.3" do + describe "NameError#receiver" do + class ::ReceiverClass + def call_undefined_class_variable; @@doesnt_exist end + end + + it "returns the object that raised the exception" do + receiver = Object.new + + -> { + receiver.doesnt_exist + }.should raise_error(NameError) {|e| e.receiver.should equal(receiver) } + end + + it "returns the Object class when an undefined constant is called without namespace" do + -> { + DoesntExist + }.should raise_error(NameError) {|e| e.receiver.should equal(Object) } + end + + it "returns a class when an undefined constant is called" do + -> { + NameErrorSpecs::ReceiverClass::DoesntExist + }.should raise_error(NameError) {|e| e.receiver.should equal(NameErrorSpecs::ReceiverClass) } + end + + it "returns the Object class when an undefined class variable is called" do + -> { + -> { + @@doesnt_exist + }.should complain(/class variable access from toplevel/) + }.should raise_error(NameError) {|e| e.receiver.should equal(Object) } + end + + it "returns a class when an undefined class variable is called in a subclass' namespace" do + -> { + NameErrorSpecs::ReceiverClass.new.call_undefined_class_variable + }.should raise_error(NameError) {|e| e.receiver.should equal(NameErrorSpecs::ReceiverClass) } + end + + it "returns the receiver when raised from #instance_variable_get" do + receiver = Object.new + + -> { + receiver.instance_variable_get("invalid_ivar_name") + }.should raise_error(NameError) {|e| e.receiver.should equal(receiver) } + end + + it "returns the receiver when raised from #class_variable_get" do + -> { + Object.class_variable_get("invalid_cvar_name") + }.should raise_error(NameError) {|e| e.receiver.should equal(Object) } + end + + it "raises an ArgumentError when the receiver is none" do + -> { NameError.new.receiver }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/exception/result_spec.rb b/spec/rubyspec/core/exception/result_spec.rb new file mode 100644 index 0000000000..350c071f60 --- /dev/null +++ b/spec/rubyspec/core/exception/result_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "StopIteration" do + it "is a subclass of IndexError" do + StopIteration.superclass.should equal(IndexError) + end +end + +describe "StopIteration#result" do + before :each do + obj = Object.new + def obj.each + yield :yield_returned_1 + yield :yield_returned_2 + :method_returned + end + @enum = obj.to_enum + end + + it "returns the method-returned-object from an Enumerator" do + @enum.next + @enum.next + lambda { @enum.next }.should( + raise_error(StopIteration) do |error| + error.result.should equal(:method_returned) + end + ) + end +end diff --git a/spec/rubyspec/core/exception/script_error_spec.rb b/spec/rubyspec/core/exception/script_error_spec.rb new file mode 100644 index 0000000000..5ca0333261 --- /dev/null +++ b/spec/rubyspec/core/exception/script_error_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ScriptError" do + it "is a superclass of LoadError" do + ScriptError.should be_ancestor_of(LoadError) + end + + it "is a superclass of NotImplementedError" do + ScriptError.should be_ancestor_of(NotImplementedError) + end + + it "is a superclass of SyntaxError" do + ScriptError.should be_ancestor_of(SyntaxError) + end +end diff --git a/spec/rubyspec/core/exception/set_backtrace_spec.rb b/spec/rubyspec/core/exception/set_backtrace_spec.rb new file mode 100644 index 0000000000..db58a193ef --- /dev/null +++ b/spec/rubyspec/core/exception/set_backtrace_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Exception#set_backtrace" do + it "accepts an Array of Strings" do + err = RuntimeError.new + err.set_backtrace ["unhappy"] + err.backtrace.should == ["unhappy"] + end + + it "allows the user to set the backtrace from a rescued exception" do + bt = ExceptionSpecs::Backtrace.backtrace + err = RuntimeError.new + + err.set_backtrace bt + err.backtrace.should == bt + end + + it "accepts an empty Array" do + err = RuntimeError.new + err.set_backtrace [] + err.backtrace.should == [] + end + + it "accepts a String" do + err = RuntimeError.new + err.set_backtrace "unhappy" + err.backtrace.should == ["unhappy"] + end + + it "accepts nil" do + err = RuntimeError.new + err.set_backtrace nil + err.backtrace.should be_nil + end + + it "raises a TypeError when passed a Symbol" do + err = RuntimeError.new + lambda { err.set_backtrace :unhappy }.should raise_error(TypeError) + end + + it "raises a TypeError when the Array contains a Symbol" do + err = RuntimeError.new + lambda { err.set_backtrace ["String", :unhappy] }.should raise_error(TypeError) + end + + it "raises a TypeError when the array contains nil" do + err = Exception.new + lambda { err.set_backtrace ["String", nil] }.should raise_error(TypeError) + end + + it "raises a TypeError when the argument is a nested array" do + err = Exception.new + lambda { err.set_backtrace ["String", ["String"]] }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/exception/shared/new.rb b/spec/rubyspec/core/exception/shared/new.rb new file mode 100644 index 0000000000..bcde8ee4b2 --- /dev/null +++ b/spec/rubyspec/core/exception/shared/new.rb @@ -0,0 +1,18 @@ +describe :exception_new, shared: true do + it "creates a new instance of Exception" do + Exception.should be_ancestor_of(Exception.send(@method).class) + end + + it "sets the message of the Exception when passes a message" do + Exception.send(@method, "I'm broken.").message.should == "I'm broken." + end + + it "returns 'Exception' for message when no message given" do + Exception.send(@method).message.should == "Exception" + end + + it "returns the exception when it has a custom constructor" do + ExceptionSpecs::ConstructorException.send(@method).should be_kind_of(ExceptionSpecs::ConstructorException) + end + +end diff --git a/spec/rubyspec/core/exception/signal_exception_spec.rb b/spec/rubyspec/core/exception/signal_exception_spec.rb new file mode 100644 index 0000000000..3b2d1aad61 --- /dev/null +++ b/spec/rubyspec/core/exception/signal_exception_spec.rb @@ -0,0 +1,74 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SignalException.new" do + it "takes a signal number as the first argument" do + exc = SignalException.new(Signal.list["INT"]) + exc.signo.should == Signal.list["INT"] + exc.signm.should == "SIGINT" + exc.message.should == "SIGINT" + end + + it "raises an exception with an invalid signal number" do + lambda { SignalException.new(100000) }.should raise_error(ArgumentError) + end + + it "takes a signal name without SIG prefix as the first argument" do + exc = SignalException.new("INT") + exc.signo.should == Signal.list["INT"] + exc.signm.should == "SIGINT" + exc.message.should == "SIGINT" + end + + it "takes a signal name with SIG prefix as the first argument" do + exc = SignalException.new("SIGINT") + exc.signo.should == Signal.list["INT"] + exc.signm.should == "SIGINT" + exc.message.should == "SIGINT" + end + + it "raises an exception with an invalid signal name" do + lambda { SignalException.new("NONEXISTANT") }.should raise_error(ArgumentError) + end + + it "takes a signal symbol without SIG prefix as the first argument" do + exc = SignalException.new(:INT) + exc.signo.should == Signal.list["INT"] + exc.signm.should == "SIGINT" + exc.message.should == "SIGINT" + end + + it "takes a signal symbol with SIG prefix as the first argument" do + exc = SignalException.new(:SIGINT) + exc.signo.should == Signal.list["INT"] + exc.signm.should == "SIGINT" + exc.message.should == "SIGINT" + end + + it "raises an exception with an invalid signal name" do + lambda { SignalException.new(:NONEXISTANT) }.should raise_error(ArgumentError) + end + + it "takes an optional message argument with a signal number" do + exc = SignalException.new(Signal.list["INT"], "name") + exc.signo.should == Signal.list["INT"] + exc.signm.should == "name" + exc.message.should == "name" + end + + it "raises an exception for an optional argument with a signal name" do + lambda { SignalException.new("INT","name") }.should raise_error(ArgumentError) + end +end + +describe "rescueing SignalException" do + it "raises a SignalException when sent a signal" do + begin + Process.kill :TERM, Process.pid + sleep + rescue SignalException => e + e.signo.should == Signal.list["TERM"] + e.signm.should == "SIGTERM" + e.message.should == "SIGTERM" + end + end +end diff --git a/spec/rubyspec/core/exception/signm_spec.rb b/spec/rubyspec/core/exception/signm_spec.rb new file mode 100644 index 0000000000..e205b79e19 --- /dev/null +++ b/spec/rubyspec/core/exception/signm_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SignalException#signm" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/signo_spec.rb b/spec/rubyspec/core/exception/signo_spec.rb new file mode 100644 index 0000000000..08c85274ca --- /dev/null +++ b/spec/rubyspec/core/exception/signo_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SignalException#signo" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/source_encoding_name_spec.rb b/spec/rubyspec/core/exception/source_encoding_name_spec.rb new file mode 100644 index 0000000000..5796072121 --- /dev/null +++ b/spec/rubyspec/core/exception/source_encoding_name_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::UndefinedConversionError#source_encoding_name" do + it "needs to be reviewed for spec completeness" +end + +describe "Encoding::InvalidByteSequenceError#source_encoding_name" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/source_encoding_spec.rb b/spec/rubyspec/core/exception/source_encoding_spec.rb new file mode 100644 index 0000000000..796bec88f6 --- /dev/null +++ b/spec/rubyspec/core/exception/source_encoding_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Encoding::UndefinedConversionError#source_encoding" do + it "needs to be reviewed for spec completeness" +end + +describe "Encoding::InvalidByteSequenceError#source_encoding" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/standard_error_spec.rb b/spec/rubyspec/core/exception/standard_error_spec.rb new file mode 100644 index 0000000000..9b3af4b322 --- /dev/null +++ b/spec/rubyspec/core/exception/standard_error_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "StandardError" do + it "is a superclass of ArgumentError" do + StandardError.should be_ancestor_of(ArgumentError) + end + + it "is a superclass of IOError" do + StandardError.should be_ancestor_of(IOError) + end + + it "is a superclass of IndexError" do + StandardError.should be_ancestor_of(IndexError) + end + + it "is a superclass of LocalJumpError" do + StandardError.should be_ancestor_of(LocalJumpError) + end + + it "is a superclass of NameError" do + StandardError.should be_ancestor_of(NameError) + end + + it "is a superclass of RangeError" do + StandardError.should be_ancestor_of(RangeError) + end + + it "is a superclass of RegexpError" do + StandardError.should be_ancestor_of(RegexpError) + end + + it "is a superclass of RuntimeError" do + StandardError.should be_ancestor_of(RuntimeError) + end + + it "is a superclass of SystemCallError" do + StandardError.should be_ancestor_of(SystemCallError.new("").class) + end + it "is a superclass of ThreadError" do + StandardError.should be_ancestor_of(ThreadError) + end + + it "is a superclass of TypeError" do + StandardError.should be_ancestor_of(TypeError) + end + + it "is a superclass of ZeroDivisionError" do + StandardError.should be_ancestor_of(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/core/exception/status_spec.rb b/spec/rubyspec/core/exception/status_spec.rb new file mode 100644 index 0000000000..e648dc0adc --- /dev/null +++ b/spec/rubyspec/core/exception/status_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SystemExit#status" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/success_spec.rb b/spec/rubyspec/core/exception/success_spec.rb new file mode 100644 index 0000000000..d9b69b4f45 --- /dev/null +++ b/spec/rubyspec/core/exception/success_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SystemExit#success?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/exception/system_call_error_spec.rb b/spec/rubyspec/core/exception/system_call_error_spec.rb new file mode 100644 index 0000000000..edcc8d3734 --- /dev/null +++ b/spec/rubyspec/core/exception/system_call_error_spec.rb @@ -0,0 +1,89 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "SystemCallError" do + before :each do + ScratchPad.clear + end + + it "can be subclassed" do + ExceptionSpecs::SCESub = Class.new(SystemCallError) do + def initialize + ScratchPad.record :initialize + end + end + + exc = ExceptionSpecs::SCESub.new + ScratchPad.recorded.should equal(:initialize) + exc.should be_an_instance_of(ExceptionSpecs::SCESub) + end +end + +describe "SystemCallError.new" do + it "requires at least one argument" do + lambda { SystemCallError.new }.should raise_error(ArgumentError) + end + + it "accepts single Fixnum argument as errno" do + SystemCallError.new(-2**24).errno.should == -2**24 + SystemCallError.new(42).errno.should == 42 + SystemCallError.new(2**24).errno.should == 2**24 + end + + it "constructs the appropriate Errno class" do + # EINVAL should be more or less mortable across the platforms, + # so let's use it then. + SystemCallError.new(22).should be_kind_of(SystemCallError) + SystemCallError.new(22).should be_an_instance_of(Errno::EINVAL) + SystemCallError.new(2**28).should be_an_instance_of(SystemCallError) + end + + it "accepts an optional custom message preceding the errno" do + exc = SystemCallError.new("custom message", 22) + exc.should be_an_instance_of(Errno::EINVAL) + exc.errno.should == 22 + exc.message.should == "Invalid argument - custom message" + end + + it "accepts an optional third argument specifying the location" do + exc = SystemCallError.new("custom message", 22, "location") + exc.should be_an_instance_of(Errno::EINVAL) + exc.errno.should == 22 + exc.message.should == "Invalid argument @ location - custom message" + end + + it "returns an arity of -1 for the initialize method" do + SystemCallError.instance_method(:initialize).arity.should == -1 + end +end + +describe "SystemCallError#errno" do + it "returns nil when no errno given" do + SystemCallError.new("message").errno.should == nil + end + + it "returns the errno given as optional argument to new" do + SystemCallError.new("message", -2**20).errno.should == -2**20 + SystemCallError.new("message", -1).errno.should == -1 + SystemCallError.new("message", 0).errno.should == 0 + SystemCallError.new("message", 1).errno.should == 1 + SystemCallError.new("message", 42).errno.should == 42 + SystemCallError.new("message", 2**20).errno.should == 2**20 + end +end + +describe "SystemCallError#message" do + it "returns the default message when no message is given" do + platform_is :aix do + SystemCallError.new(2**28).message.should =~ /Error .*occurred/i + end + platform_is_not :aix do + SystemCallError.new(2**28).message.should =~ /Unknown error/i + end + end + + it "returns the message given as an argument to new" do + SystemCallError.new("message", 1).message.should =~ /message/ + SystemCallError.new("XXX").message.should =~ /XXX/ + end +end diff --git a/spec/rubyspec/core/exception/system_stack_error_spec.rb b/spec/rubyspec/core/exception/system_stack_error_spec.rb new file mode 100644 index 0000000000..acd56c0a0f --- /dev/null +++ b/spec/rubyspec/core/exception/system_stack_error_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SystemStackError" do + it "is a subclass of Exception" do + SystemStackError.superclass.should == Exception + end +end diff --git a/spec/rubyspec/core/exception/to_s_spec.rb b/spec/rubyspec/core/exception/to_s_spec.rb new file mode 100644 index 0000000000..83234bfe23 --- /dev/null +++ b/spec/rubyspec/core/exception/to_s_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Exception#to_s" do + it "returns the self's name if no message is set" do + Exception.new.to_s.should == 'Exception' + ExceptionSpecs::Exceptional.new.to_s.should == 'ExceptionSpecs::Exceptional' + end + + it "returns self's message if set" do + ExceptionSpecs::Exceptional.new('!!').to_s.should == '!!' + end + + it "calls #to_s on the message" do + message = mock("message") + message.should_receive(:to_s).and_return("message") + ExceptionSpecs::Exceptional.new(message).to_s.should == "message" + end +end + +describe "NameError#to_s" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/false/and_spec.rb b/spec/rubyspec/core/false/and_spec.rb new file mode 100644 index 0000000000..a949c02503 --- /dev/null +++ b/spec/rubyspec/core/false/and_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "FalseClass#&" do + it "returns false" do + (false & false).should == false + (false & true).should == false + (false & nil).should == false + (false & "").should == false + (false & mock('x')).should == false + end +end diff --git a/spec/rubyspec/core/false/inspect_spec.rb b/spec/rubyspec/core/false/inspect_spec.rb new file mode 100644 index 0000000000..f3bb6645bf --- /dev/null +++ b/spec/rubyspec/core/false/inspect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "FalseClass#inspect" do + it "returns the string 'false'" do + false.inspect.should == "false" + end +end diff --git a/spec/rubyspec/core/false/or_spec.rb b/spec/rubyspec/core/false/or_spec.rb new file mode 100644 index 0000000000..e2f4dbd90b --- /dev/null +++ b/spec/rubyspec/core/false/or_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "FalseClass#|" do + it "returns false if other is nil or false, otherwise true" do + (false | false).should == false + (false | true).should == true + (false | nil).should == false + (false | "").should == true + (false | mock('x')).should == true + end +end diff --git a/spec/rubyspec/core/false/to_s_spec.rb b/spec/rubyspec/core/false/to_s_spec.rb new file mode 100644 index 0000000000..bb2bdb65af --- /dev/null +++ b/spec/rubyspec/core/false/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "FalseClass#to_s" do + it "returns the string 'false'" do + false.to_s.should == "false" + end +end diff --git a/spec/rubyspec/core/false/xor_spec.rb b/spec/rubyspec/core/false/xor_spec.rb new file mode 100644 index 0000000000..3e5c452f9b --- /dev/null +++ b/spec/rubyspec/core/false/xor_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "FalseClass#^" do + it "returns false if other is nil or false, otherwise true" do + (false ^ false).should == false + (false ^ true).should == true + (false ^ nil).should == false + (false ^ "").should == true + (false ^ mock('x')).should == true + end +end diff --git a/spec/rubyspec/core/fiber/new_spec.rb b/spec/rubyspec/core/fiber/new_spec.rb new file mode 100644 index 0000000000..a3361acc65 --- /dev/null +++ b/spec/rubyspec/core/fiber/new_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :fiber do + describe "Fiber.new" do + it "creates a fiber from the given block" do + fiber = Fiber.new {} + fiber.resume + fiber.should be_an_instance_of(Fiber) + end + + it "creates a fiber from a subclass" do + class MyFiber < Fiber + end + fiber = MyFiber.new {} + fiber.resume + fiber.should be_an_instance_of(MyFiber) + end + + it "raises an ArgumentError if called without a block" do + lambda { Fiber.new }.should raise_error(ArgumentError) + end + + it "does not invoke the block" do + invoked = false + fiber = Fiber.new { invoked = true } + invoked.should be_false + fiber.resume + end + + it "closes over lexical environments" do + o = Object.new + def o.f + a = 1 + f = Fiber.new { a = 2 } + f.resume + a + end + o.f.should == 2 + end + end +end diff --git a/spec/rubyspec/core/fiber/resume_spec.rb b/spec/rubyspec/core/fiber/resume_spec.rb new file mode 100644 index 0000000000..3fd3aed8fa --- /dev/null +++ b/spec/rubyspec/core/fiber/resume_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/fiber/resume', __FILE__) + +with_feature :fiber do + describe "Fiber#resume" do + it_behaves_like :fiber_resume, :resume + end + + describe "Fiber#resume" do + it "returns control to the calling Fiber if called from one" do + fiber1 = Fiber.new { :fiber1 } + fiber2 = Fiber.new { fiber1.resume; :fiber2 } + fiber2.resume.should == :fiber2 + end + + with_feature :fork do + # Redmine #595 + it "executes the ensure clause" do + rd, wr = IO.pipe + + pid = Kernel::fork do + rd.close + f = Fiber.new do + begin + Fiber.yield + ensure + wr.write "executed" + end + end + + # The apparent issue is that when Fiber.yield executes, control + # "leaves" the "ensure block" and so the ensure clause should run. But + # control really does NOT leave the ensure block when Fiber.yield + # executes. It merely pauses there. To require ensure to run when a + # Fiber is suspended then makes ensure-in-a-Fiber-context different + # than ensure-in-a-Thread-context and this would be very confusing. + f.resume + + # When we execute the second #resume call, the ensure block DOES exit, + # the ensure clause runs. This is Ruby behavior as of 2.3.1. + f.resume + + exit 0 + end + + wr.close + Process.waitpid pid + + rd.read.should == "executed" + rd.close + end + end + end +end diff --git a/spec/rubyspec/core/fiber/yield_spec.rb b/spec/rubyspec/core/fiber/yield_spec.rb new file mode 100644 index 0000000000..45054e9cec --- /dev/null +++ b/spec/rubyspec/core/fiber/yield_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :fiber do + describe "Fiber.yield" do + it "passes control to the Fiber's caller" do + step = 0 + fiber = Fiber.new { step = 1; Fiber.yield; step = 2; Fiber.yield; step = 3 } + fiber.resume + step.should == 1 + fiber.resume + step.should == 2 + end + + it "returns its arguments to the caller" do + fiber = Fiber.new { true; Fiber.yield :glark; true } + fiber.resume.should == :glark + fiber.resume + end + + it "returns nil to the caller if given no arguments" do + fiber = Fiber.new { true; Fiber.yield; true } + fiber.resume.should be_nil + fiber.resume + end + + it "returns to the Fiber the value of the #resume call that invoked it" do + fiber = Fiber.new { Fiber.yield.should == :caller } + fiber.resume + fiber.resume :caller + end + + it "raises a FiberError if called from the root Fiber" do + lambda{ Fiber.yield }.should raise_error(FiberError) + end + end +end diff --git a/spec/rubyspec/core/file/absolute_path_spec.rb b/spec/rubyspec/core/file/absolute_path_spec.rb new file mode 100644 index 0000000000..b1f4f05aee --- /dev/null +++ b/spec/rubyspec/core/file/absolute_path_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.absolute_path" do + before :each do + @abs = File.expand_path(__FILE__) + end + + it "returns the argument if it's an absolute pathname" do + File.absolute_path(@abs).should == @abs + end + + it "resolves paths relative to the current working directory" do + path = File.dirname(@abs) + Dir.chdir(path) do + File.absolute_path('hello.txt').should == File.join(Dir.pwd, 'hello.txt') + end + end + + it "does not expand '~' to a home directory." do + File.absolute_path('~').should_not == File.expand_path('~') + end + + it "does not expand '~user' to a home directory." do + path = File.dirname(@abs) + Dir.chdir(path) do + File.absolute_path('~user').should == File.join(Dir.pwd, '~user') + end + end + + it "accepts a second argument of a directory from which to resolve the path" do + File.absolute_path(__FILE__, File.dirname(__FILE__)).should == @abs + end + + it "calls #to_path on its argument" do + File.absolute_path(mock_to_path(@abs)).should == @abs + end +end diff --git a/spec/rubyspec/core/file/atime_spec.rb b/spec/rubyspec/core/file/atime_spec.rb new file mode 100644 index 0000000000..76e7fbd62a --- /dev/null +++ b/spec/rubyspec/core/file/atime_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.atime" do + before :each do + @file = tmp('test.txt') + touch @file + end + + after :each do + rm_r @file + end + + it "returns the last access time for the named file as a Time object" do + File.atime(@file) + File.atime(@file).should be_kind_of(Time) + end + + platform_is :linux do + ## NOTE also that some Linux systems disable atime (e.g. via mount params) for better filesystem speed. + it "returns the last access time for the named file with microseconds" do + supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10) + if supports_subseconds != 0 + expected_time = Time.at(Time.now.to_i + 0.123456) + File.utime expected_time, 0, @file + File.atime(@file).usec.should == expected_time.usec + else + File.atime(__FILE__).usec.should == 0 + end + end + end + + it "raises an Errno::ENOENT exception if the file is not found" do + lambda { File.atime('a_fake_file') }.should raise_error(Errno::ENOENT) + end + + it "accepts an object that has a #to_path method" do + File.atime(mock_to_path(@file)) + end +end + +describe "File#atime" do + before :each do + @name = File.expand_path(__FILE__) + @file = File.open(@name) + end + + after :each do + @file.close rescue nil + end + + it "returns the last access time to self" do + @file.atime + @file.atime.should be_kind_of(Time) + end +end diff --git a/spec/rubyspec/core/file/basename_spec.rb b/spec/rubyspec/core/file/basename_spec.rb new file mode 100644 index 0000000000..4cf26062d3 --- /dev/null +++ b/spec/rubyspec/core/file/basename_spec.rb @@ -0,0 +1,170 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +# TODO: Fix these +describe "File.basename" do + it "returns the basename of a path (basic cases)" do + File.basename("/Some/path/to/test.txt").should == "test.txt" + File.basename(File.join("/tmp")).should == "tmp" + File.basename(File.join(*%w( g f d s a b))).should == "b" + File.basename("/tmp", ".*").should == "tmp" + File.basename("/tmp", ".c").should == "tmp" + File.basename("/tmp.c", ".c").should == "tmp" + File.basename("/tmp.c", ".*").should == "tmp" + File.basename("/tmp.c", ".?").should == "tmp.c" + File.basename("/tmp.cpp", ".*").should == "tmp" + File.basename("/tmp.cpp", ".???").should == "tmp.cpp" + File.basename("/tmp.o", ".c").should == "tmp.o" + File.basename(File.join("/tmp/")).should == "tmp" + File.basename("/").should == "/" + File.basename("//").should == "/" + File.basename("dir///base", ".*").should == "base" + File.basename("dir///base", ".c").should == "base" + File.basename("dir///base.c", ".c").should == "base" + File.basename("dir///base.c", ".*").should == "base" + File.basename("dir///base.o", ".c").should == "base.o" + File.basename("dir///base///").should == "base" + File.basename("dir//base/", ".*").should == "base" + File.basename("dir//base/", ".c").should == "base" + File.basename("dir//base.c/", ".c").should == "base" + File.basename("dir//base.c/", ".*").should == "base" + end + + it "returns the last component of the filename" do + File.basename('a').should == 'a' + File.basename('/a').should == 'a' + File.basename('/a/b').should == 'b' + File.basename('/ab/ba/bag').should == 'bag' + File.basename('/ab/ba/bag.txt').should == 'bag.txt' + File.basename('/').should == '/' + File.basename('/foo/bar/baz.rb', '.rb').should == 'baz' + File.basename('baz.rb', 'z.rb').should == 'ba' + end + + it "returns an string" do + File.basename("foo").should be_kind_of(String) + end + + it "returns the basename for unix format" do + File.basename("/foo/bar").should == "bar" + File.basename("/foo/bar.txt").should == "bar.txt" + File.basename("bar.c").should == "bar.c" + File.basename("/bar").should == "bar" + File.basename("/bar/").should == "bar" + + # Considered UNC paths on Windows + platform_is :windows do + File.basename("baz//foo").should =="foo" + File.basename("//foo/bar/baz").should == "baz" + end + end + + it "returns the basename for edge cases" do + File.basename("").should == "" + File.basename(".").should == "." + File.basename("..").should == ".." + platform_is_not :windows do + File.basename("//foo/").should == "foo" + File.basename("//foo//").should == "foo" + end + File.basename("foo/").should == "foo" + end + + it "ignores a trailing directory separator" do + File.basename("foo.rb/", '.rb').should == "foo" + File.basename("bar.rb///", '.*').should == "bar" + end + + it "returns the basename for unix suffix" do + File.basename("bar.c", ".c").should == "bar" + File.basename("bar.txt", ".txt").should == "bar" + File.basename("/bar.txt", ".txt").should == "bar" + File.basename("/foo/bar.txt", ".txt").should == "bar" + File.basename("bar.txt", ".exe").should == "bar.txt" + File.basename("bar.txt.exe", ".exe").should == "bar.txt" + File.basename("bar.txt.exe", ".txt").should == "bar.txt.exe" + File.basename("bar.txt", ".*").should == "bar" + File.basename("bar.txt.exe", ".*").should == "bar.txt" + File.basename("bar.txt.exe", ".txt.exe").should == "bar" + end + + platform_is_not :windows do + it "takes into consideration the platform path separator(s)" do + File.basename("C:\\foo\\bar").should == "C:\\foo\\bar" + File.basename("C:/foo/bar").should == "bar" + File.basename("/foo/bar\\baz").should == "bar\\baz" + end + end + + platform_is :windows do + it "takes into consideration the platform path separator(s)" do + File.basename("C:\\foo\\bar").should == "bar" + File.basename("C:/foo/bar").should == "bar" + File.basename("/foo/bar\\baz").should == "baz" + end + end + + it "raises a TypeError if the arguments are not String types" do + lambda { File.basename(nil) }.should raise_error(TypeError) + lambda { File.basename(1) }.should raise_error(TypeError) + lambda { File.basename("bar.txt", 1) }.should raise_error(TypeError) + lambda { File.basename(true) }.should raise_error(TypeError) + end + + it "accepts an object that has a #to_path method" do + File.basename(mock_to_path("foo.txt")) + end + + it "raises an ArgumentError if passed more than two arguments" do + lambda { File.basename('bar.txt', '.txt', '.txt') }.should raise_error(ArgumentError) + end + + # specific to MS Windows + platform_is :windows do + it "returns the basename for windows" do + File.basename("C:\\foo\\bar\\baz.txt").should == "baz.txt" + File.basename("C:\\foo\\bar").should == "bar" + File.basename("C:\\foo\\bar\\").should == "bar" + File.basename("C:\\foo").should == "foo" + File.basename("C:\\").should == "\\" + end + + it "returns basename windows unc" do + File.basename("\\\\foo\\bar\\baz.txt").should == "baz.txt" + File.basename("\\\\foo\\bar\\baz").should =="baz" + end + + it "returns basename windows forward slash" do + File.basename("C:/").should == "/" + File.basename("C:/foo").should == "foo" + File.basename("C:/foo/bar").should == "bar" + File.basename("C:/foo/bar/").should == "bar" + File.basename("C:/foo/bar//").should == "bar" + end + + it "returns basename with windows suffix" do + File.basename("c:\\bar.txt", ".txt").should == "bar" + File.basename("c:\\foo\\bar.txt", ".txt").should == "bar" + File.basename("c:\\bar.txt", ".exe").should == "bar.txt" + File.basename("c:\\bar.txt.exe", ".exe").should == "bar.txt" + File.basename("c:\\bar.txt.exe", ".txt").should == "bar.txt.exe" + File.basename("c:\\bar.txt", ".*").should == "bar" + File.basename("c:\\bar.txt.exe", ".*").should == "bar.txt" + end + end + + with_feature :encoding do + + it "returns the extension for a multibyte filename" do + File.basename('/path/Офис.m4a').should == "Офис.m4a" + end + + it "returns the basename with the same encoding as the original" do + basename = File.basename('C:/Users/Scuby Pagrubý'.encode(Encoding::Windows_1250)) + basename.should == 'Scuby Pagrubý'.encode(Encoding::Windows_1250) + basename.encoding.should == Encoding::Windows_1250 + end + + end + +end diff --git a/spec/rubyspec/core/file/birthtime_spec.rb b/spec/rubyspec/core/file/birthtime_spec.rb new file mode 100644 index 0000000000..9720ede834 --- /dev/null +++ b/spec/rubyspec/core/file/birthtime_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.birthtime" do + before :each do + @file = __FILE__ + end + + after :each do + @file = nil + end + + platform_is :windows, :darwin, :freebsd, :netbsd do + it "returns the birth time for the named file as a Time object" do + File.birthtime(@file) + File.birthtime(@file).should be_kind_of(Time) + end + + it "accepts an object that has a #to_path method" do + File.birthtime(mock_to_path(@file)) + end + + it "raises an Errno::ENOENT exception if the file is not found" do + lambda { File.birthtime('bogus') }.should raise_error(Errno::ENOENT) + end + end + + platform_is :linux, :openbsd do + it "raises an NotImplementedError" do + lambda { File.birthtime(@file) }.should raise_error(NotImplementedError) + end + end +end + +describe "File#birthtime" do + before :each do + @file = File.open(__FILE__) + end + + after :each do + @file.close + @file = nil + end + + platform_is :windows, :darwin, :freebsd, :netbsd do + it "returns the birth time for self" do + @file.birthtime + @file.birthtime.should be_kind_of(Time) + end + end + + platform_is :linux, :openbsd do + it "raises an NotImplementedError" do + lambda { @file.birthtime }.should raise_error(NotImplementedError) + end + end +end diff --git a/spec/rubyspec/core/file/blockdev_spec.rb b/spec/rubyspec/core/file/blockdev_spec.rb new file mode 100644 index 0000000000..f5e03d1ade --- /dev/null +++ b/spec/rubyspec/core/file/blockdev_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/blockdev', __FILE__) + +describe "File.blockdev?" do + it_behaves_like :file_blockdev, :blockdev?, File +end diff --git a/spec/rubyspec/core/file/chardev_spec.rb b/spec/rubyspec/core/file/chardev_spec.rb new file mode 100644 index 0000000000..963823a206 --- /dev/null +++ b/spec/rubyspec/core/file/chardev_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/chardev', __FILE__) + +describe "File.chardev?" do + it_behaves_like :file_chardev, :chardev?, File +end diff --git a/spec/rubyspec/core/file/chmod_spec.rb b/spec/rubyspec/core/file/chmod_spec.rb new file mode 100644 index 0000000000..8590f3008d --- /dev/null +++ b/spec/rubyspec/core/file/chmod_spec.rb @@ -0,0 +1,239 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File#chmod" do + before :each do + @filename = tmp('i_exist.exe') + @file = File.open(@filename, 'w') + end + + after :each do + @file.close + rm_r @filename + end + + it "returns 0 if successful" do + @file.chmod(0755).should == 0 + end + + platform_is_not :freebsd, :netbsd, :openbsd do + it "always succeeds with any numeric values" do + vals = [-2**30, -2**16, -2**8, -2, -1, + -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30] + vals.each { |v| + lambda { @file.chmod(v) }.should_not raise_error + } + end + end + + # -256, -2 and -1 raise Errno::E079 on FreeBSD + # -256, -2 and -1 raise Errno::EFTYPE on NetBSD + platform_is :freebsd, :netbsd do + it "always succeeds with any numeric values" do + vals = [-2**30, -2**16, #-2**8, -2, -1, + -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30] + vals.each { |v| + lambda { @file.chmod(v) }.should_not raise_error + } + end + end + + # -256, -2 and -1 raise Errno::EINVAL on OpenBSD + platform_is :openbsd do + it "always succeeds with any numeric values" do + vals = [#-2**30, -2**16, -2**8, -2, -1, + -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8]#, 2**16, 2**30 + vals.each { |v| + lambda { @file.chmod(v) }.should_not raise_error + } + end + end + it "invokes to_int on non-integer argument" do + mode = File.stat(@filename).mode + (obj = mock('mode')).should_receive(:to_int).and_return(mode) + @file.chmod(obj) + File.stat(@filename).mode.should == mode + end + + platform_is :windows do + it "with '0444' makes file readable and executable but not writable" do + @file.chmod(0444) + File.readable?(@filename).should == true + File.writable?(@filename).should == false + File.executable?(@filename).should == true + end + + it "with '0644' makes file readable and writable and also executable" do + @file.chmod(0644) + File.readable?(@filename).should == true + File.writable?(@filename).should == true + File.executable?(@filename).should == true + end + end + + platform_is_not :windows do + it "with '0222' makes file writable but not readable or executable" do + @file.chmod(0222) + File.readable?(@filename).should == false + File.writable?(@filename).should == true + File.executable?(@filename).should == false + end + + it "with '0444' makes file readable but not writable or executable" do + @file.chmod(0444) + File.readable?(@filename).should == true + File.writable?(@filename).should == false + File.executable?(@filename).should == false + end + + it "with '0666' makes file readable and writable but not executable" do + @file.chmod(0666) + File.readable?(@filename).should == true + File.writable?(@filename).should == true + File.executable?(@filename).should == false + end + + it "with '0111' makes file executable but not readable or writable" do + @file.chmod(0111) + File.readable?(@filename).should == false + File.writable?(@filename).should == false + File.executable?(@filename).should == true + end + + it "modifies the permission bits of the files specified" do + @file.chmod(0755) + File.stat(@filename).mode.should == 33261 + end + end +end + +describe "File.chmod" do + before :each do + @file = tmp('i_exist.exe') + touch @file + @count = File.chmod(0755, @file) + end + + after :each do + rm_r @file + end + + it "returns the number of files modified" do + @count.should == 1 + end + + platform_is_not :freebsd, :netbsd, :openbsd do + it "always succeeds with any numeric values" do + vals = [-2**30, -2**16, -2**8, -2, -1, + -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30] + vals.each { |v| + lambda { File.chmod(v, @file) }.should_not raise_error + } + end + end + + # -256, -2 and -1 raise Errno::E079 on FreeBSD + # -256, -2 and -1 raise Errno::EFTYPE on NetBSD + platform_is :freebsd, :netbsd do + it "always succeeds with any numeric values" do + vals = [-2**30, -2**16, #-2**8, -2, -1, + -0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8, 2**16, 2**30] + vals.each { |v| + lambda { File.chmod(v, @file) }.should_not raise_error + } + end + end + + platform_is :openbsd do + it "succeeds with valid values" do + vals = [-0.5, 0, 1, 2, 5.555575, 16, 32, 64, 2**8] + vals.each { |v| + lambda { File.chmod(v, @file) }.should_not raise_error + } + end + + it "fails with invalid values" do + vals = [-2**30, -2**16, -2**8, -2, -1, 2**16, 2**30] + vals.each { |v| + lambda { File.chmod(v, @file) }.should raise_error(Errno::EINVAL) + } + end + end + + it "accepts an object that has a #to_path method" do + File.chmod(0, mock_to_path(@file)) + end + + it "throws a TypeError if the given path is not coercable into a string" do + lambda { File.chmod(0, []) }.should raise_error(TypeError) + end + + it "raises an error for a non existent path" do + lambda { + File.chmod(0644, "#{@file}.not.existing") + }.should raise_error(Errno::ENOENT) + end + + it "invokes to_int on non-integer argument" do + mode = File.stat(@file).mode + (obj = mock('mode')).should_receive(:to_int).and_return(mode) + File.chmod(obj, @file) + File.stat(@file).mode.should == mode + end + + it "invokes to_str on non-string file names" do + mode = File.stat(@file).mode + (obj = mock('path')).should_receive(:to_str).and_return(@file) + File.chmod(mode, obj) + File.stat(@file).mode.should == mode + end + + platform_is :windows do + it "with '0444' makes file readable and executable but not writable" do + File.chmod(0444, @file) + File.readable?(@file).should == true + File.writable?(@file).should == false + File.executable?(@file).should == true + end + + it "with '0644' makes file readable and writable and also executable" do + File.chmod(0644, @file) + File.readable?(@file).should == true + File.writable?(@file).should == true + File.executable?(@file).should == true + end + end + + platform_is_not :windows do + it "with '0222' makes file writable but not readable or executable" do + File.chmod(0222, @file) + File.readable?(@file).should == false + File.writable?(@file).should == true + File.executable?(@file).should == false + end + + it "with '0444' makes file readable but not writable or executable" do + File.chmod(0444, @file) + File.readable?(@file).should == true + File.writable?(@file).should == false + File.executable?(@file).should == false + end + + it "with '0666' makes file readable and writable but not executable" do + File.chmod(0666, @file) + File.readable?(@file).should == true + File.writable?(@file).should == true + File.executable?(@file).should == false + end + + it "with '0111' makes file executable but not readable or writable" do + File.chmod(0111, @file) + File.readable?(@file).should == false + File.writable?(@file).should == false + File.executable?(@file).should == true + end + + it "modifies the permission bits of the files specified" do + File.stat(@file).mode.should == 33261 + end + end +end diff --git a/spec/rubyspec/core/file/chown_spec.rb b/spec/rubyspec/core/file/chown_spec.rb new file mode 100644 index 0000000000..a0b46e9e39 --- /dev/null +++ b/spec/rubyspec/core/file/chown_spec.rb @@ -0,0 +1,152 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.chown" do + before :each do + @fname = tmp('file_chown_test') + touch @fname + end + + after :each do + rm_r @fname + end + + as_superuser do + platform_is :windows do + it "does not modify the owner id of the file" do + File.chown 0, nil, @fname + File.stat(@fname).uid.should == 0 + File.chown 501, nil, @fname + File.stat(@fname).uid.should == 0 + end + + it "does not modify the group id of the file" do + File.chown nil, 0, @fname + File.stat(@fname).gid.should == 0 + File.chown nil, 501, @fname + File.stat(@fname).gid.should == 0 + end + end + + platform_is_not :windows do + it "changes the owner id of the file" do + File.chown 501, nil, @fname + File.stat(@fname).uid.should == 501 + File.chown 0, nil, @fname + File.stat(@fname).uid.should == 0 + end + + it "changes the group id of the file" do + File.chown nil, 501, @fname + File.stat(@fname).gid.should == 501 + File.chown nil, 0, @fname + File.stat(@fname).uid.should == 0 + end + + it "does not modify the owner id of the file if passed nil or -1" do + File.chown 501, nil, @fname + File.chown nil, nil, @fname + File.stat(@fname).uid.should == 501 + File.chown nil, -1, @fname + File.stat(@fname).uid.should == 501 + end + + it "does not modify the group id of the file if passed nil or -1" do + File.chown nil, 501, @fname + File.chown nil, nil, @fname + File.stat(@fname).gid.should == 501 + File.chown nil, -1, @fname + File.stat(@fname).gid.should == 501 + end + end + end + + it "returns the number of files processed" do + File.chown(nil, nil, @fname, @fname).should == 2 + end + + platform_is_not :windows do + it "raises an error for a non existent path" do + lambda { + File.chown(nil, nil, "#{@fname}_not_existing") + }.should raise_error(Errno::ENOENT) + end + end + + it "accepts an object that has a #to_path method" do + File.chown(nil, nil, mock_to_path(@fname)).should == 1 + end +end + +describe "File#chown" do + before :each do + @fname = tmp('file_chown_test') + @file = File.open(@fname, 'w') + end + + after :each do + @file.close unless @file.closed? + rm_r @fname + end + + as_superuser do + platform_is :windows do + it "does not modify the owner id of the file" do + File.chown 0, nil, @fname + File.stat(@fname).uid.should == 0 + File.chown 501, nil, @fname + File.stat(@fname).uid.should == 0 + end + + it "does not modify the group id of the file" do + File.chown nil, 0, @fname + File.stat(@fname).gid.should == 0 + File.chown nil, 501, @fname + File.stat(@fname).gid.should == 0 + end + end + + platform_is_not :windows do + it "changes the owner id of the file" do + @file.chown 501, nil + @file.stat.uid.should == 501 + @file.chown 0, nil + @file.stat.uid.should == 0 + end + + it "changes the group id of the file" do + @file.chown nil, 501 + @file.stat.gid.should == 501 + @file.chown nil, 0 + @file.stat.uid.should == 0 + end + + it "does not modify the owner id of the file if passed nil or -1" do + @file.chown 501, nil + @file.chown nil, nil + @file.stat.uid.should == 501 + @file.chown nil, -1 + @file.stat.uid.should == 501 + end + + it "does not modify the group id of the file if passed nil or -1" do + @file.chown nil, 501 + @file.chown nil, nil + @file.stat.gid.should == 501 + @file.chown nil, -1 + @file.stat.gid.should == 501 + end + end + end + + it "returns 0" do + @file.chown(nil, nil).should == 0 + end +end + +describe "File.chown" do + it "needs to be reviewed for spec completeness" +end + +describe "File#chown" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/constants/constants_spec.rb b/spec/rubyspec/core/file/constants/constants_spec.rb new file mode 100644 index 0000000000..3b2f67cc30 --- /dev/null +++ b/spec/rubyspec/core/file/constants/constants_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +["APPEND", "CREAT", "EXCL", "FNM_CASEFOLD", + "FNM_DOTMATCH", "FNM_EXTGLOB", "FNM_NOESCAPE", "FNM_PATHNAME", + "FNM_SYSCASE", "LOCK_EX", "LOCK_NB", "LOCK_SH", + "LOCK_UN", "NONBLOCK", "RDONLY", + "RDWR", "TRUNC", "WRONLY"].each do |const| + describe "File::Constants::#{const}" do + it "is defined" do + File::Constants.const_defined?(const).should be_true + end + end +end + +platform_is :windows do + describe "File::Constants::BINARY" do + it "is defined" do + File::Constants.const_defined?(:BINARY).should be_true + end + end +end + +platform_is_not :windows do + ["NOCTTY", "SYNC"].each do |const| + describe "File::Constants::#{const}" do + it "is defined" do + File::Constants.const_defined?(const).should be_true + end + end + end +end diff --git a/spec/rubyspec/core/file/constants_spec.rb b/spec/rubyspec/core/file/constants_spec.rb new file mode 100644 index 0000000000..0379149a18 --- /dev/null +++ b/spec/rubyspec/core/file/constants_spec.rb @@ -0,0 +1,141 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +# TODO: migrate these to constants/constants_spec.rb + +describe "File::Constants" do + it "matches mode constants" do + File::FNM_NOESCAPE.should_not == nil + File::FNM_PATHNAME.should_not == nil + File::FNM_DOTMATCH.should_not == nil + File::FNM_CASEFOLD.should_not == nil + File::FNM_SYSCASE.should_not == nil + + platform_is :windows do #|| VMS + File::FNM_SYSCASE.should == 8 + end + end + + # Only these constants are not inherited from the IO class + it "the separator constant" do + File::SEPARATOR.should_not == nil + File::Separator.should_not == nil + File::PATH_SEPARATOR.should_not == nil + File::SEPARATOR.should == "/" + + platform_is :windows do #|| VMS + File::ALT_SEPARATOR.should_not == nil + File::PATH_SEPARATOR.should == ";" + end + + platform_is_not :windows do + File::ALT_SEPARATOR.should == nil + File::PATH_SEPARATOR.should == ":" + end + end + + it "the open mode constants" do + File::APPEND.should_not == nil + File::CREAT.should_not == nil + File::EXCL.should_not == nil + File::NONBLOCK.should_not == nil + File::RDONLY.should_not == nil + File::RDWR.should_not == nil + File::TRUNC.should_not == nil + File::WRONLY.should_not == nil + + platform_is_not :windows do # Not sure about VMS here + File::NOCTTY.should_not == nil + end + end + + it "lock mode constants" do + File::LOCK_EX.should_not == nil + File::LOCK_NB.should_not == nil + File::LOCK_SH.should_not == nil + File::LOCK_UN.should_not == nil + end +end + +describe "File::Constants" do + # These mode and permission bits are platform dependent + it "File::RDONLY" do + defined?(File::RDONLY).should == "constant" + end + + it "File::WRONLY" do + defined?(File::WRONLY).should == "constant" + end + + it "File::CREAT" do + defined?(File::CREAT).should == "constant" + end + + it "File::RDWR" do + defined?(File::RDWR).should == "constant" + end + + it "File::APPEND" do + defined?(File::APPEND).should == "constant" + end + + it "File::TRUNC" do + defined?(File::TRUNC).should == "constant" + end + + platform_is_not :windows do # Not sure about VMS here + it "File::NOCTTY" do + defined?(File::NOCTTY).should == "constant" + end + end + + it "File::NONBLOCK" do + defined?(File::NONBLOCK).should == "constant" + end + + it "File::LOCK_EX" do + defined?(File::LOCK_EX).should == "constant" + end + + it "File::LOCK_NB" do + defined?(File::LOCK_NB).should == "constant" + end + + it "File::LOCK_SH" do + defined?(File::LOCK_SH).should == "constant" + end + + it "File::LOCK_UN" do + defined?(File::LOCK_UN).should == "constant" + end + + it "File::SEPARATOR" do + defined?(File::SEPARATOR).should == "constant" + end + it "File::Separator" do + defined?(File::Separator).should == "constant" + end + + it "File::PATH_SEPARATOR" do + defined?(File::PATH_SEPARATOR).should == "constant" + end + + it "File::SEPARATOR" do + defined?(File::SEPARATOR).should == "constant" + File::SEPARATOR.should == "/" + end + + platform_is :windows do #|| VMS + it "File::ALT_SEPARATOR" do + defined?(File::ALT_SEPARATOR).should == "constant" + File::PATH_SEPARATOR.should == ";" + end + end + + platform_is_not :windows do + it "File::PATH_SEPARATOR" do + defined?(File::PATH_SEPARATOR).should == "constant" + File::PATH_SEPARATOR.should == ":" + end + end + +end diff --git a/spec/rubyspec/core/file/ctime_spec.rb b/spec/rubyspec/core/file/ctime_spec.rb new file mode 100644 index 0000000000..c39775fcdd --- /dev/null +++ b/spec/rubyspec/core/file/ctime_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.ctime" do + before :each do + @file = __FILE__ + end + + after :each do + @file = nil + end + + it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do + File.ctime(@file) + File.ctime(@file).should be_kind_of(Time) + end + + platform_is :linux do + it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself) with microseconds." do + supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d+)/, 1], 10) + if supports_subseconds != 0 + File.ctime(__FILE__).usec.should > 0 + else + File.ctime(__FILE__).usec.should == 0 + end + end + end + + it "accepts an object that has a #to_path method" do + File.ctime(mock_to_path(@file)) + end + + it "raises an Errno::ENOENT exception if the file is not found" do + lambda { File.ctime('bogus') }.should raise_error(Errno::ENOENT) + end +end + +describe "File#ctime" do + before :each do + @file = File.open(__FILE__) + end + + after :each do + @file.close + @file = nil + end + + it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself)." do + @file.ctime + @file.ctime.should be_kind_of(Time) + end +end diff --git a/spec/rubyspec/core/file/delete_spec.rb b/spec/rubyspec/core/file/delete_spec.rb new file mode 100644 index 0000000000..2e903806d7 --- /dev/null +++ b/spec/rubyspec/core/file/delete_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/unlink', __FILE__) + +describe "File.delete" do + it_behaves_like(:file_unlink, :delete) +end diff --git a/spec/rubyspec/core/file/directory_spec.rb b/spec/rubyspec/core/file/directory_spec.rb new file mode 100644 index 0000000000..d8e8b25121 --- /dev/null +++ b/spec/rubyspec/core/file/directory_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/directory', __FILE__) + +describe "File.directory?" do + it_behaves_like :file_directory, :directory?, File +end + +describe "File.directory?" do + it_behaves_like :file_directory_io, :directory?, File +end diff --git a/spec/rubyspec/core/file/dirname_spec.rb b/spec/rubyspec/core/file/dirname_spec.rb new file mode 100644 index 0000000000..f56f0806df --- /dev/null +++ b/spec/rubyspec/core/file/dirname_spec.rb @@ -0,0 +1,108 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.dirname" do + it "returns all the components of filename except the last one" do + File.dirname('/home/jason').should == '/home' + File.dirname('/home/jason/poot.txt').should == '/home/jason' + File.dirname('poot.txt').should == '.' + File.dirname('/holy///schnikies//w00t.bin').should == '/holy///schnikies' + File.dirname('').should == '.' + File.dirname('/').should == '/' + File.dirname('/foo/foo').should == '/foo' + end + + it "returns a String" do + File.dirname("foo").should be_kind_of(String) + end + + it "does not modify its argument" do + x = "/usr/bin" + File.dirname(x) + x.should == "/usr/bin" + end + + it "ignores a trailing /" do + File.dirname("/foo/bar/").should == "/foo" + end + + it "returns the return all the components of filename except the last one (unix format)" do + File.dirname("foo").should =="." + File.dirname("/foo").should =="/" + File.dirname("/foo/bar").should =="/foo" + File.dirname("/foo/bar.txt").should =="/foo" + File.dirname("/foo/bar/baz").should =="/foo/bar" + end + + it "returns all the components of filename except the last one (edge cases on all platforms)" do + File.dirname("").should == "." + File.dirname(".").should == "." + File.dirname("./").should == "." + File.dirname("./b/./").should == "./b" + File.dirname("..").should == "." + File.dirname("../").should == "." + File.dirname("/").should == "/" + File.dirname("/.").should == "/" + File.dirname("/foo/").should == "/" + File.dirname("/foo/.").should == "/foo" + File.dirname("/foo/./").should == "/foo" + File.dirname("/foo/../.").should == "/foo/.." + File.dirname("foo/../").should == "foo" + end + + platform_is_not :windows do + it "returns all the components of filename except the last one (edge cases on non-windows)" do + File.dirname('/////').should == '/' + File.dirname("//foo//").should == "/" + File.dirname('foo\bar').should == '.' + File.dirname('/foo\bar').should == '/' + File.dirname('foo/bar\baz').should == 'foo' + end + end + + platform_is :windows do + it "returns all the components of filename except the last one (edge cases on windows)" do + File.dirname("//foo").should == "//foo" + File.dirname("//foo//").should == "//foo" + File.dirname('/////').should == '//' + end + end + + it "accepts an object that has a #to_path method" do + File.dirname(mock_to_path("/")).should == "/" + end + + it "raises a TypeError if not passed a String type" do + lambda { File.dirname(nil) }.should raise_error(TypeError) + lambda { File.dirname(0) }.should raise_error(TypeError) + lambda { File.dirname(true) }.should raise_error(TypeError) + lambda { File.dirname(false) }.should raise_error(TypeError) + end + + # Windows specific tests + platform_is :windows do + it "returns the return all the components of filename except the last one (Windows format)" do + File.dirname("C:\\foo\\bar\\baz.txt").should =="C:\\foo\\bar" + File.dirname("C:\\foo\\bar").should =="C:\\foo" + File.dirname("C:\\foo\\bar\\").should == "C:\\foo" + File.dirname("C:\\foo").should == "C:\\" + File.dirname("C:\\").should =="C:\\" + end + + it "returns the return all the components of filename except the last one (windows unc)" do + File.dirname("\\\\foo\\bar\\baz.txt").should == "\\\\foo\\bar" + File.dirname("\\\\foo\\bar\\baz").should == "\\\\foo\\bar" + File.dirname("\\\\foo").should =="\\\\foo" + File.dirname("\\\\foo\\bar").should =="\\\\foo\\bar" + File.dirname("\\\\\\foo\\bar").should =="\\\\foo\\bar" + File.dirname("\\\\\\foo").should =="\\\\foo" + end + + it "returns the return all the components of filename except the last one (forward_slash)" do + File.dirname("C:/").should == "C:/" + File.dirname("C:/foo").should == "C:/" + File.dirname("C:/foo/bar").should == "C:/foo" + File.dirname("C:/foo/bar/").should == "C:/foo" + File.dirname("C:/foo/bar//").should == "C:/foo" + end + end +end diff --git a/spec/rubyspec/core/file/executable_real_spec.rb b/spec/rubyspec/core/file/executable_real_spec.rb new file mode 100644 index 0000000000..24d6824169 --- /dev/null +++ b/spec/rubyspec/core/file/executable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/executable_real', __FILE__) + +describe "File.executable_real?" do + it_behaves_like :file_executable_real, :executable_real?, File + it_behaves_like :file_executable_real_missing, :executable_real?, File +end diff --git a/spec/rubyspec/core/file/executable_spec.rb b/spec/rubyspec/core/file/executable_spec.rb new file mode 100644 index 0000000000..82d6a81a0d --- /dev/null +++ b/spec/rubyspec/core/file/executable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/executable', __FILE__) + +describe "File.executable?" do + it_behaves_like :file_executable, :executable?, File + it_behaves_like :file_executable_missing, :executable?, File +end diff --git a/spec/rubyspec/core/file/exist_spec.rb b/spec/rubyspec/core/file/exist_spec.rb new file mode 100644 index 0000000000..29a410c125 --- /dev/null +++ b/spec/rubyspec/core/file/exist_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/exist', __FILE__) + +describe "File.exist?" do + it_behaves_like(:file_exist, :exist?, File) +end diff --git a/spec/rubyspec/core/file/exists_spec.rb b/spec/rubyspec/core/file/exists_spec.rb new file mode 100644 index 0000000000..70ebd12d86 --- /dev/null +++ b/spec/rubyspec/core/file/exists_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/exist', __FILE__) + +describe "File.exists?" do + it_behaves_like(:file_exist, :exists?, File) +end diff --git a/spec/rubyspec/core/file/expand_path_spec.rb b/spec/rubyspec/core/file/expand_path_spec.rb new file mode 100644 index 0000000000..c57f323c4c --- /dev/null +++ b/spec/rubyspec/core/file/expand_path_spec.rb @@ -0,0 +1,242 @@ +# -*- encoding: utf-8 -*- + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "File.expand_path" do + before :each do + platform_is :windows do + @base = `cd`.chomp.tr '\\', '/' + @tmpdir = "c:/tmp" + @rootdir = "c:/" + end + + platform_is_not :windows do + @base = Dir.pwd + @tmpdir = "/tmp" + @rootdir = "/" + end + end + + with_feature :encoding do + before :each do + @external = Encoding.default_external + end + + after :each do + Encoding.default_external = @external + end + end + + it "converts a pathname to an absolute pathname" do + File.expand_path('').should == @base + File.expand_path('a').should == File.join(@base, 'a') + File.expand_path('a', nil).should == File.join(@base, 'a') + end + + it "converts a pathname to an absolute pathname, Ruby-Talk:18512" do + # See Ruby-Talk:18512 + File.expand_path('.a').should == File.join(@base, '.a') + File.expand_path('..a').should == File.join(@base, '..a') + File.expand_path('a../b').should == File.join(@base, 'a../b') + end + + platform_is_not :windows do + it "keeps trailing dots on absolute pathname" do + # See Ruby-Talk:18512 + File.expand_path('a.').should == File.join(@base, 'a.') + File.expand_path('a..').should == File.join(@base, 'a..') + end + end + + it "converts a pathname to an absolute pathname, using a complete path" do + File.expand_path("", "#{@tmpdir}").should == "#{@tmpdir}" + File.expand_path("a", "#{@tmpdir}").should =="#{@tmpdir}/a" + File.expand_path("../a", "#{@tmpdir}/xxx").should == "#{@tmpdir}/a" + File.expand_path(".", "#{@rootdir}").should == "#{@rootdir}" + end + + # FIXME: do not use conditionals like this around #it blocks + unless not home = ENV['HOME'] + platform_is_not :windows do + it "converts a pathname to an absolute pathname, using ~ (home) as base" do + File.expand_path('~').should == home + File.expand_path('~', '/tmp/gumby/ddd').should == home + File.expand_path('~/a', '/tmp/gumby/ddd').should == File.join(home, 'a') + end + + it "does not return a frozen string" do + File.expand_path('~').frozen?.should == false + File.expand_path('~', '/tmp/gumby/ddd').frozen?.should == false + File.expand_path('~/a', '/tmp/gumby/ddd').frozen?.should == false + end + end + platform_is :windows do + it "converts a pathname to an absolute pathname, using ~ (home) as base" do + File.expand_path('~').should == home.tr("\\", '/') + File.expand_path('~', '/tmp/gumby/ddd').should == home.tr("\\", '/') + File.expand_path('~/a', '/tmp/gumby/ddd').should == File.join(home.tr("\\", '/'), 'a') + end + + it "does not return a frozen string" do + File.expand_path('~').frozen?.should == false + File.expand_path('~', '/tmp/gumby/ddd').frozen?.should == false + File.expand_path('~/a', '/tmp/gumby/ddd').frozen?.should == false + end + end + end + + platform_is_not :windows do + before do + @home = ENV['HOME'].chomp('/') + end + + # FIXME: these are insane! + it "expand path with" do + File.expand_path("../../bin", "/tmp/x").should == "/bin" + File.expand_path("../../bin", "/tmp").should == "/bin" + File.expand_path("../../bin", "/").should == "/bin" + File.expand_path("../bin", "tmp/x").should == File.join(@base, 'tmp', 'bin') + File.expand_path("../bin", "x/../tmp").should == File.join(@base, 'bin') + end + + it "expand_path for commoms unix path give a full path" do + File.expand_path('/tmp/').should =='/tmp' + File.expand_path('/tmp/../../../tmp').should == '/tmp' + File.expand_path('').should == Dir.pwd + File.expand_path('./////').should == Dir.pwd + File.expand_path('.').should == Dir.pwd + File.expand_path(Dir.pwd).should == Dir.pwd + File.expand_path('~/').should == @home + File.expand_path('~/..badfilename').should == "#{@home}/..badfilename" + File.expand_path('..').should == Dir.pwd.split('/')[0...-1].join("/") + File.expand_path('~/a','~/b').should == "#{@home}/a" + end + + it "does not replace multiple '/' at the beginning of the path" do + File.expand_path('////some/path').should == "////some/path" + end + + it "replaces multiple '/' with a single '/'" do + File.expand_path('/some////path').should == "/some/path" + end + + it "raises an ArgumentError if the path is not valid" do + lambda { File.expand_path("~a_not_existing_user") }.should raise_error(ArgumentError) + end + + it "expands ~ENV['USER'] to the user's home directory" do + File.expand_path("~#{ENV['USER']}").should == @home + File.expand_path("~#{ENV['USER']}/a").should == "#{@home}/a" + end + + it "does not expand ~ENV['USER'] when it's not at the start" do + File.expand_path("/~#{ENV['USER']}/a").should == "/~#{ENV['USER']}/a" + end + + it "expands ../foo with ~/dir as base dir to /path/to/user/home/foo" do + File.expand_path('../foo', '~/dir').should == "#{@home}/foo" + end + end + + it "accepts objects that have a #to_path method" do + File.expand_path(mock_to_path("a"), mock_to_path("#{@tmpdir}")) + end + + it "raises a TypeError if not passed a String type" do + lambda { File.expand_path(1) }.should raise_error(TypeError) + lambda { File.expand_path(nil) }.should raise_error(TypeError) + lambda { File.expand_path(true) }.should raise_error(TypeError) + end + + platform_is_not :windows do + it "expands /./dir to /dir" do + File.expand_path("/./dir").should == "/dir" + end + end + + platform_is :windows do + it "expands C:/./dir to C:/dir" do + File.expand_path("C:/./dir").should == "C:/dir" + end + end + + with_feature :encoding do + it "returns a String in the same encoding as the argument" do + Encoding.default_external = Encoding::SHIFT_JIS + + path = "./a".force_encoding Encoding::CP1251 + File.expand_path(path).encoding.should equal(Encoding::CP1251) + + weird_path = [222, 173, 190, 175].pack('C*') + File.expand_path(weird_path).encoding.should equal(Encoding::ASCII_8BIT) + end + + platform_is_not :windows do + it "expands a path when the default external encoding is ASCII-8BIT" do + Encoding.default_external = Encoding::ASCII_8BIT + path_8bit = [222, 173, 190, 175].pack('C*') + File.expand_path( path_8bit, @rootdir).should == "#{@rootdir}" + path_8bit + end + end + + it "expands a path with multi-byte characters" do + File.expand_path("Ångström").should == "#{@base}/Ångström" + end + + platform_is_not :windows do + it "raises an Encoding::CompatibilityError if the external encoding is not compatible" do + Encoding.default_external = Encoding::UTF_16BE + lambda { File.expand_path("./a") }.should raise_error(Encoding::CompatibilityError) + end + end + end + + it "does not modify the string argument" do + str = "./a/b/../c" + File.expand_path(str, @base).should == "#{@base}/a/c" + str.should == "./a/b/../c" + end + + it "does not modify a HOME string argument" do + str = "~/a" + File.expand_path(str).should == "#{Dir.home}/a" + str.should == "~/a" + end + + it "returns a String when passed a String subclass" do + str = FileSpecs::SubString.new "./a/b/../c" + path = File.expand_path(str, @base) + path.should == "#{@base}/a/c" + path.should be_an_instance_of(String) + end +end + +platform_is_not :windows do + describe "File.expand_path when HOME is not set" do + before :each do + @home = ENV["HOME"] + end + + after :each do + ENV["HOME"] = @home + end + + ruby_version_is ''...'2.4' do + it "raises an ArgumentError when passed '~' if HOME is nil" do + ENV.delete "HOME" + lambda { File.expand_path("~") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed '~/' if HOME is nil" do + ENV.delete "HOME" + lambda { File.expand_path("~/") }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError when passed '~' if HOME == ''" do + ENV["HOME"] = "" + lambda { File.expand_path("~") }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/file/extname_spec.rb b/spec/rubyspec/core/file/extname_spec.rb new file mode 100644 index 0000000000..fedd4fc89f --- /dev/null +++ b/spec/rubyspec/core/file/extname_spec.rb @@ -0,0 +1,54 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.extname" do + it "returns the extension (the portion of file name in path after the period)." do + File.extname("foo.rb").should == ".rb" + File.extname("/foo/bar.rb").should == ".rb" + File.extname("/foo.rb/bar.c").should == ".c" + File.extname("bar").should == "" + File.extname(".bashrc").should == "" + File.extname("/foo.bar/baz").should == "" + File.extname(".app.conf").should == ".conf" + end + + it "returns the extension (the portion of file name in path after the period).(edge cases)" do + File.extname("").should == "" + File.extname(".").should == "" + File.extname("/").should == "" + File.extname("/.").should == "" + File.extname("..").should == "" + File.extname("...").should == "" + File.extname("....").should == "" + File.extname(".foo.").should == "" + File.extname("foo.").should == "" + end + + it "returns only the last extension of a file with several dots" do + File.extname("a.b.c.d.e").should == ".e" + end + + it "accepts an object that has a #to_path method" do + File.extname(mock_to_path("a.b.c.d.e")).should == ".e" + end + + it "raises a TypeError if not passed a String type" do + lambda { File.extname(nil) }.should raise_error(TypeError) + lambda { File.extname(0) }.should raise_error(TypeError) + lambda { File.extname(true) }.should raise_error(TypeError) + lambda { File.extname(false) }.should raise_error(TypeError) + end + + it "raises an ArgumentError if not passed one argument" do + lambda { File.extname }.should raise_error(ArgumentError) + lambda { File.extname("foo.bar", "foo.baz") }.should raise_error(ArgumentError) + end + + with_feature :encoding do + + it "returns the extension for a multibyte filename" do + File.extname('Имя.m4a').should == ".m4a" + end + + end +end diff --git a/spec/rubyspec/core/file/file_spec.rb b/spec/rubyspec/core/file/file_spec.rb new file mode 100644 index 0000000000..99eaacd086 --- /dev/null +++ b/spec/rubyspec/core/file/file_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/file', __FILE__) + +describe "File" do + it "includes Enumerable" do + File.include?(Enumerable).should == true + end + + it "includes File::Constants" do + File.include?(File::Constants).should == true + end +end + +describe "File.file?" do + it_behaves_like :file_file, :file?, File +end diff --git a/spec/rubyspec/core/file/fixtures/common.rb b/spec/rubyspec/core/file/fixtures/common.rb new file mode 100644 index 0000000000..50721388ad --- /dev/null +++ b/spec/rubyspec/core/file/fixtures/common.rb @@ -0,0 +1,22 @@ +module FileSpecs + class SubString < String; end + + def self.make_closer(obj, exc=nil) + ScratchPad << :file_opened + + class << obj + attr_accessor :close_exception + + alias_method :original_close, :close + + def close + original_close + ScratchPad << :file_closed + + raise @close_exception if @close_exception + end + end + + obj.close_exception = exc + end +end diff --git a/spec/rubyspec/core/file/fixtures/do_not_remove b/spec/rubyspec/core/file/fixtures/do_not_remove new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/rubyspec/core/file/fixtures/file_types.rb b/spec/rubyspec/core/file/fixtures/file_types.rb new file mode 100644 index 0000000000..1c446b33a6 --- /dev/null +++ b/spec/rubyspec/core/file/fixtures/file_types.rb @@ -0,0 +1,65 @@ +module FileSpecs + # Try to set up known locations of each filetype + def self.reconfigure() + @file = tmp("test.txt") + @dir = Dir.pwd + @fifo = tmp("test_fifo") + + platform_is_not :windows do + @block = `find /dev /devices -type b 2> /dev/null`.split("\n").first + @char = `find /dev /devices -type c 2> /dev/null`.split("\n").last + + %w[/dev /usr/bin /usr/local/bin].each do |dir| + links = `find #{dir} -type l 2> /dev/null`.split("\n") + next if links.empty? + @link = links.first + break + end + + end + end + + # TODO: Automatic reload mechanism + reconfigure + + def self.normal_file() + File.open(@file, "w") {} # 'Touch' + yield @file + ensure + rm_r @file + end + + def self.directory() + yield @dir + end + + # TODO: need a platform-independent helper here + def self.fifo() + system "mkfifo #{@fifo} 2> /dev/null" + yield @fifo + ensure + rm_r @fifo + end + + def self.block_device() + yield @block + end + + def self.character_device() + yield @char + end + + def self.symlink() + yield @link + end + + def self.socket() + require 'socket' + name = tmp("ftype_socket.socket") + rm_r name + socket = UNIXServer.new name + yield name + socket.close + rm_r name + end +end diff --git a/spec/rubyspec/core/file/flock_spec.rb b/spec/rubyspec/core/file/flock_spec.rb new file mode 100644 index 0000000000..e14d4252d4 --- /dev/null +++ b/spec/rubyspec/core/file/flock_spec.rb @@ -0,0 +1,106 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File#flock" do + before :each do + ScratchPad.record [] + + @name = tmp("flock_test") + touch(@name) + + @file = File.open @name, "w+" + end + + after :each do + @file.flock File::LOCK_UN + @file.close + + rm_r @name + end + + it "exclusively locks a file" do + @file.flock(File::LOCK_EX).should == 0 + @file.flock(File::LOCK_UN).should == 0 + end + + it "non-exclusively locks a file" do + @file.flock(File::LOCK_SH).should == 0 + @file.flock(File::LOCK_UN).should == 0 + end + + it "returns false if trying to lock an exclusively locked file" do + @file.flock File::LOCK_EX + + ruby_exe(<<-END_OF_CODE, escape: true).should == "false" + File.open('#{@name}', "w") do |f2| + print f2.flock(File::LOCK_EX | File::LOCK_NB).to_s + end + END_OF_CODE + end + + it "blocks if trying to lock an exclusively locked file" do + @file.flock File::LOCK_EX + + out = ruby_exe(<<-END_OF_CODE, escape: true) + running = false + + t = Thread.new do + File.open('#{@name}', "w") do |f2| + puts "before" + running = true + f2.flock(File::LOCK_EX) + puts "after" + end + end + + Thread.pass until running + Thread.pass while t.status and t.status != "sleep" + sleep 0.1 + + t.kill + t.join + END_OF_CODE + + out.should == "before\n" + end + + it "returns 0 if trying to lock a non-exclusively locked file" do + @file.flock File::LOCK_SH + + File.open(@name, "r") do |f2| + f2.flock(File::LOCK_SH | File::LOCK_NB).should == 0 + f2.flock(File::LOCK_UN).should == 0 + end + end +end + +platform_is :solaris do + describe "File#flock on Solaris" do + before :each do + @name = tmp("flock_test") + touch(@name) + + @read_file = File.open @name, "r" + @write_file = File.open @name, "w" + end + + after :each do + @read_file.flock File::LOCK_UN + @read_file.close + @write_file.flock File::LOCK_UN + @write_file.close + rm_r @name + end + + it "fails with EBADF acquiring exclusive lock on read-only File" do + lambda do + @read_file.flock File::LOCK_EX + end.should raise_error(Errno::EBADF) + end + + it "fails with EBADF acquiring shared lock on read-only File" do + lambda do + @write_file.flock File::LOCK_SH + end.should raise_error(Errno::EBADF) + end + end +end diff --git a/spec/rubyspec/core/file/fnmatch_spec.rb b/spec/rubyspec/core/file/fnmatch_spec.rb new file mode 100644 index 0000000000..8a4caacfb8 --- /dev/null +++ b/spec/rubyspec/core/file/fnmatch_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/fnmatch', __FILE__) + +describe "File.fnmatch" do + it_behaves_like(:file_fnmatch, :fnmatch) +end + +describe "File.fnmatch?" do + it_behaves_like(:file_fnmatch, :fnmatch?) +end diff --git a/spec/rubyspec/core/file/ftype_spec.rb b/spec/rubyspec/core/file/ftype_spec.rb new file mode 100644 index 0000000000..79ed1d5b45 --- /dev/null +++ b/spec/rubyspec/core/file/ftype_spec.rb @@ -0,0 +1,69 @@ +require "#{File.dirname(__FILE__)}/../../spec_helper" +require "#{File.dirname(__FILE__)}/fixtures/file_types.rb" + +describe "File.ftype" do + it "raises ArgumentError if not given exactly one filename" do + lambda { File.ftype }.should raise_error(ArgumentError) + lambda { File.ftype('blah', 'bleh') }.should raise_error(ArgumentError) + end + + it "raises Errno::ENOENT if the file is not valid" do + l = lambda { File.ftype("/#{$$}#{Time.now.to_f}") } + l.should raise_error(Errno::ENOENT) + end + + it "returns a String" do + FileSpecs.normal_file do |file| + File.ftype(file).should be_kind_of(String) + end + end + + it "returns 'file' when the file is a file" do + FileSpecs.normal_file do |file| + File.ftype(file).should == 'file' + end + end + + it "returns 'directory' when the file is a dir" do + FileSpecs.directory do |dir| + File.ftype(dir).should == 'directory' + end + end + + # Both FreeBSD and Windows does not have block devices + platform_is_not :freebsd, :windows do + with_block_device do + it "returns 'blockSpecial' when the file is a block" do + FileSpecs.block_device do |block| + File.ftype(block).should == 'blockSpecial' + end + end + end + end + + platform_is_not :windows do + it "returns 'characterSpecial' when the file is a char" do + FileSpecs.character_device do |char| + File.ftype(char).should == 'characterSpecial' + end + end + + it "returns 'link' when the file is a link" do + FileSpecs.symlink do |link| + File.ftype(link).should == 'link' + end + end + + it "returns fifo when the file is a fifo" do + FileSpecs.fifo do |fifo| + File.ftype(fifo).should == 'fifo' + end + end + + it "returns 'socket' when the file is a socket" do + FileSpecs.socket do |socket| + File.ftype(socket).should == 'socket' + end + end + end +end diff --git a/spec/rubyspec/core/file/grpowned_spec.rb b/spec/rubyspec/core/file/grpowned_spec.rb new file mode 100644 index 0000000000..0b5514d7ca --- /dev/null +++ b/spec/rubyspec/core/file/grpowned_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/grpowned', __FILE__) + +describe "File.grpowned?" do + it_behaves_like :file_grpowned, :grpowned?, File + + it "returns false if file the does not exist" do + File.grpowned?("i_am_a_bogus_file").should == false + end +end diff --git a/spec/rubyspec/core/file/identical_spec.rb b/spec/rubyspec/core/file/identical_spec.rb new file mode 100644 index 0000000000..303337b62d --- /dev/null +++ b/spec/rubyspec/core/file/identical_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/identical', __FILE__) + +describe "File.identical?" do + it_behaves_like :file_identical, :identical?, File +end diff --git a/spec/rubyspec/core/file/initialize_spec.rb b/spec/rubyspec/core/file/initialize_spec.rb new file mode 100644 index 0000000000..269e13b3ca --- /dev/null +++ b/spec/rubyspec/core/file/initialize_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File#initialize" do + it "needs to be reviewed for spec completeness" +end + +describe "File#initialize" do + after :each do + @io.close if @io + end + + it "accepts encoding options in mode parameter" do + @io = File.new(__FILE__, 'r:UTF-8:iso-8859-1') + @io.external_encoding.to_s.should == 'UTF-8' + @io.internal_encoding.to_s.should == 'ISO-8859-1' + end + + it "accepts encoding options as a hash parameter" do + @io = File.new(__FILE__, 'r', encoding: 'UTF-8:iso-8859-1') + @io.external_encoding.to_s.should == 'UTF-8' + @io.internal_encoding.to_s.should == 'ISO-8859-1' + end +end diff --git a/spec/rubyspec/core/file/inspect_spec.rb b/spec/rubyspec/core/file/inspect_spec.rb new file mode 100644 index 0000000000..a059fa2c48 --- /dev/null +++ b/spec/rubyspec/core/file/inspect_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File#inspect" do + before :each do + @name = tmp("file_inspect.txt") + @file = File.open @name, "w" + end + + after :each do + @file.close unless @file.closed? + rm_r @name + end + + it "returns a String" do + @file.inspect.should be_an_instance_of(String) + end +end diff --git a/spec/rubyspec/core/file/join_spec.rb b/spec/rubyspec/core/file/join_spec.rb new file mode 100644 index 0000000000..7c5955d03b --- /dev/null +++ b/spec/rubyspec/core/file/join_spec.rb @@ -0,0 +1,139 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.join" do + # see [ruby-core:46804] for the 4 following rules + it "changes only boundaries separators" do + File.join("file/\\/usr/", "/bin").should == "file/\\/usr/bin" + File.join("file://usr", "bin").should == "file://usr/bin" + end + + it "respects the given separator if only one part has a boundary separator" do + File.join("usr/", "bin").should == "usr/bin" + File.join("usr", "/bin").should == "usr/bin" + File.join("usr//", "bin").should == "usr//bin" + File.join("usr", "//bin").should == "usr//bin" + end + + it "joins parts using File::SEPARATOR if there are no boundary separators" do + File.join("usr", "bin").should == "usr/bin" + end + + it "prefers the separator of the right part if both parts have separators" do + File.join("usr/", "//bin").should == "usr//bin" + File.join("usr//", "/bin").should == "usr/bin" + end + + platform_is :windows do + it "respects given separator if only one part has a boundary separator" do + File.join("C:\\", 'windows').should == "C:\\windows" + File.join("C:", "\\windows").should == "C:\\windows" + File.join("\\\\", "usr").should == "\\\\usr" + end + + it "prefers the separator of the right part if both parts have separators" do + File.join("C:/", "\\windows").should == "C:\\windows" + File.join("C:\\", "/windows").should == "C:/windows" + end + end + + platform_is_not :windows do + it "does not treat \\ as a separator on non-Windows" do + File.join("usr\\", 'bin').should == "usr\\/bin" + File.join("usr", "\\bin").should == "usr/\\bin" + File.join("usr/", "\\bin").should == "usr/\\bin" + File.join("usr\\", "/bin").should == "usr\\/bin" + end + end + + it "returns an empty string when given no arguments" do + File.join.should == "" + end + + it "returns a duplicate string when given a single argument" do + str = "usr" + File.join(str).should == str + File.join(str).should_not equal(str) + end + + it "supports any number of arguments" do + File.join("a", "b", "c", "d").should == "a/b/c/d" + end + + it "flattens nested arrays" do + File.join(["a", "b", "c"]).should == "a/b/c" + File.join(["a", ["b", ["c"]]]).should == "a/b/c" + end + + it "inserts the separator in between empty strings and arrays" do + File.join("").should == "" + File.join("", "").should == "/" + File.join(["", ""]).should == "/" + File.join("a", "").should == "a/" + File.join("", "a").should == "/a" + + File.join([]).should == "" + File.join([], []).should == "/" + File.join([[], []]).should == "/" + File.join("a", []).should == "a/" + File.join([], "a").should == "/a" + end + + it "handles leading parts edge cases" do + File.join("/bin") .should == "/bin" + File.join("", "bin") .should == "/bin" + File.join("/", "bin") .should == "/bin" + File.join("/", "/bin").should == "/bin" + end + + it "handles trailing parts edge cases" do + File.join("bin", "") .should == "bin/" + File.join("bin/") .should == "bin/" + File.join("bin/", "") .should == "bin/" + File.join("bin", "/") .should == "bin/" + File.join("bin/", "/").should == "bin/" + end + + it "handles middle parts edge cases" do + File.join("usr", "", "bin") .should == "usr/bin" + File.join("usr/", "", "bin") .should == "usr/bin" + File.join("usr", "", "/bin").should == "usr/bin" + File.join("usr/", "", "/bin").should == "usr/bin" + end + + # TODO: See MRI svn r23306. Add patchlevel when there is a release. + it "raises an ArgumentError if passed a recursive array" do + a = ["a"] + a << a + lambda { File.join a }.should raise_error(ArgumentError) + end + + it "raises a TypeError exception when args are nil" do + lambda { File.join nil }.should raise_error(TypeError) + end + + it "calls #to_str" do + lambda { File.join(mock('x')) }.should raise_error(TypeError) + + bin = mock("bin") + bin.should_receive(:to_str).exactly(:twice).and_return("bin") + File.join(bin).should == "bin" + File.join("usr", bin).should == "usr/bin" + end + + it "doesn't mutate the object when calling #to_str" do + usr = mock("usr") + str = "usr" + usr.should_receive(:to_str).and_return(str) + File.join(usr, "bin").should == "usr/bin" + str.should == "usr" + end + + it "calls #to_path" do + lambda { File.join(mock('x')) }.should raise_error(TypeError) + + bin = mock("bin") + bin.should_receive(:to_path).exactly(:twice).and_return("bin") + File.join(bin).should == "bin" + File.join("usr", bin).should == "usr/bin" + end +end diff --git a/spec/rubyspec/core/file/lchmod_spec.rb b/spec/rubyspec/core/file/lchmod_spec.rb new file mode 100644 index 0000000000..2ce265841e --- /dev/null +++ b/spec/rubyspec/core/file/lchmod_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.lchmod" do + platform_is_not :linux, :windows, :openbsd, :solaris, :aix do + before :each do + @fname = tmp('file_chmod_test') + @lname = @fname + '.lnk' + + touch(@fname) { |f| f.write "rubinius" } + + rm_r @lname + File.symlink @fname, @lname + end + + after :each do + rm_r @lname, @fname + end + + it "changes the file mode of the link and not of the file" do + File.chmod(0222, @lname).should == 1 + File.lchmod(0755, @lname).should == 1 + + File.lstat(@lname).executable?.should == true + File.lstat(@lname).readable?.should == true + File.lstat(@lname).writable?.should == true + + File.stat(@lname).executable?.should == false + File.stat(@lname).readable?.should == false + File.stat(@lname).writable?.should == true + end + end + + platform_is :linux, :openbsd, :aix do + it "returns false from #respond_to?" do + File.respond_to?(:lchmod).should be_false + end + + it "raises a NotImplementedError when called" do + lambda { File.lchmod 0 }.should raise_error(NotImplementedError) + end + end +end diff --git a/spec/rubyspec/core/file/lchown_spec.rb b/spec/rubyspec/core/file/lchown_spec.rb new file mode 100644 index 0000000000..814b0bf534 --- /dev/null +++ b/spec/rubyspec/core/file/lchown_spec.rb @@ -0,0 +1,63 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +as_superuser do + describe "File.lchown" do + platform_is_not :windows do + before :each do + @fname = tmp('file_chown_test') + @lname = @fname + '.lnk' + + touch(@fname) { |f| f.chown 501, 501 } + + rm_r @lname + File.symlink @fname, @lname + end + + after :each do + rm_r @lname, @fname + end + + it "changes the owner id of the file" do + File.lchown 502, nil, @lname + File.stat(@fname).uid.should == 501 + File.lstat(@lname).uid.should == 502 + File.lchown 0, nil, @lname + File.stat(@fname).uid.should == 501 + File.lstat(@lname).uid.should == 0 + end + + it "changes the group id of the file" do + File.lchown nil, 502, @lname + File.stat(@fname).gid.should == 501 + File.lstat(@lname).gid.should == 502 + File.lchown nil, 0, @lname + File.stat(@fname).uid.should == 501 + File.lstat(@lname).uid.should == 0 + end + + it "does not modify the owner id of the file if passed nil or -1" do + File.lchown 502, nil, @lname + File.lchown nil, nil, @lname + File.lstat(@lname).uid.should == 502 + File.lchown nil, -1, @lname + File.lstat(@lname).uid.should == 502 + end + + it "does not modify the group id of the file if passed nil or -1" do + File.lchown nil, 502, @lname + File.lchown nil, nil, @lname + File.lstat(@lname).gid.should == 502 + File.lchown nil, -1, @lname + File.lstat(@lname).gid.should == 502 + end + + it "returns the number of files processed" do + File.lchown(nil, nil, @lname, @lname).should == 2 + end + end + end +end + +describe "File.lchown" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/link_spec.rb b/spec/rubyspec/core/file/link_spec.rb new file mode 100644 index 0000000000..69d1459672 --- /dev/null +++ b/spec/rubyspec/core/file/link_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.link" do + before :each do + @file = tmp("file_link.txt") + @link = tmp("file_link.lnk") + + rm_r @link + touch @file + end + + after :each do + rm_r @link, @file + end + + platform_is_not :windows do + it "link a file with another" do + File.link(@file, @link).should == 0 + File.exist?(@link).should == true + File.identical?(@file, @link).should == true + end + + it "raises an Errno::EEXIST if the target already exists" do + File.link(@file, @link) + lambda { File.link(@file, @link) }.should raise_error(Errno::EEXIST) + end + + it "raises an ArgumentError if not passed two arguments" do + lambda { File.link }.should raise_error(ArgumentError) + lambda { File.link(@file) }.should raise_error(ArgumentError) + lambda { File.link(@file, @link, @file) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed String types" do + lambda { File.link(@file, nil) }.should raise_error(TypeError) + lambda { File.link(@file, 1) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/core/file/lstat_spec.rb b/spec/rubyspec/core/file/lstat_spec.rb new file mode 100644 index 0000000000..6657bfa00e --- /dev/null +++ b/spec/rubyspec/core/file/lstat_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/stat', __FILE__) + +describe "File.lstat" do + it_behaves_like :file_stat, :lstat +end + +describe "File.lstat" do + + before :each do + @file = tmp('i_exist') + @link = tmp('i_am_a_symlink') + touch(@file) { |f| f.write 'rubinius' } + File.symlink(@file, @link) + end + + after :each do + rm_r @link, @file + end + + platform_is_not :windows do + it "returns a File::Stat object with symlink properties for a symlink" do + st = File.lstat(@link) + + st.symlink?.should == true + st.file?.should == false + end + end +end + +describe "File#lstat" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/mkfifo_spec.rb b/spec/rubyspec/core/file/mkfifo_spec.rb new file mode 100644 index 0000000000..ad6e804b99 --- /dev/null +++ b/spec/rubyspec/core/file/mkfifo_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.3" do + describe "File.mkfifo" do + platform_is_not :windows do + before do + @path = tmp('fifo') + end + + after do + rm_r(@path) + end + + context "when path passed responds to :to_path" do + it "creates a FIFO file at the path specified" do + File.mkfifo(@path) + File.ftype(@path).should == "fifo" + end + end + + context "when path passed is not a String value" do + it "raises a TypeError" do + lambda { File.mkfifo(:"/tmp/fifo") }.should raise_error(TypeError) + end + end + + context "when path does not exist" do + it "raises an Errno::ENOENT exception" do + lambda { File.mkfifo("/bogus/path") }.should raise_error(Errno::ENOENT) + end + end + + it "creates a FIFO file at the passed path" do + File.mkfifo(@path.to_s) + File.ftype(@path).should == "fifo" + end + + it "creates a FIFO file with passed mode & ~umask" do + File.mkfifo(@path, 0755) + File.stat(@path).mode.should == 010755 & ~File.umask + end + + it "creates a FIFO file with a default mode of 0666 & ~umask" do + File.mkfifo(@path) + File.stat(@path).mode.should == 010666 & ~File.umask + end + + it "returns 0 after creating the FIFO file" do + File.mkfifo(@path).should == 0 + end + end + end +end diff --git a/spec/rubyspec/core/file/mtime_spec.rb b/spec/rubyspec/core/file/mtime_spec.rb new file mode 100644 index 0000000000..56b7e4464e --- /dev/null +++ b/spec/rubyspec/core/file/mtime_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.mtime" do + before :each do + @filename = tmp('i_exist') + touch(@filename) { @mtime = Time.now } + end + + after :each do + rm_r @filename + end + + it "returns the modification Time of the file" do + File.mtime(@filename).should be_kind_of(Time) + File.mtime(@filename).should be_close(@mtime, 2.0) + end + + platform_is :linux do + it "returns the modification Time of the file with microseconds" do + supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10) + if supports_subseconds != 0 + expected_time = Time.at(Time.now.to_i + 0.123456) + File.utime 0, expected_time, @filename + File.mtime(@filename).usec.should == expected_time.usec + else + File.mtime(__FILE__).usec.should == 0 + end + end + end + + it "raises an Errno::ENOENT exception if the file is not found" do + lambda { File.mtime('bogus') }.should raise_error(Errno::ENOENT) + end +end + +describe "File#mtime" do + before :each do + @filename = tmp('i_exist') + @f = File.open(@filename, 'w') + end + + after :each do + @f.close + rm_r @filename + end + + it "returns the modification Time of the file" do + @f.mtime.should be_kind_of(Time) + end + +end diff --git a/spec/rubyspec/core/file/new_spec.rb b/spec/rubyspec/core/file/new_spec.rb new file mode 100644 index 0000000000..3c72ac48e5 --- /dev/null +++ b/spec/rubyspec/core/file/new_spec.rb @@ -0,0 +1,162 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/open', __FILE__) + +describe "File.new" do + before :each do + @file = tmp('test.txt') + @fh = nil + @flags = File::CREAT | File::TRUNC | File::WRONLY + touch @file + end + + after :each do + @fh.close if @fh + rm_r @file + end + + it "returns a new File with mode string" do + @fh = File.new(@file, 'w') + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "returns a new File with mode num" do + @fh = File.new(@file, @flags) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "returns a new File with modus num and permissions" do + rm_r @file + File.umask(0011) + @fh = File.new(@file, @flags, 0755) + @fh.should be_kind_of(File) + platform_is_not :windows do + File.stat(@file).mode.to_s(8).should == "100744" + end + File.exist?(@file).should == true + end + + it "creates the file and returns writable descriptor when called with 'w' mode and r-o permissions" do + # it should be possible to write to such a file via returned descriptior, + # even though the file permissions are r-r-r. + + rm_r @file + begin + f = File.new(@file, "w", 0444) + lambda { f.puts("test") }.should_not raise_error(IOError) + ensure + f.close + end + File.exist?(@file).should == true + File.read(@file).should == "test\n" + end + + platform_is_not :windows do + it "opens the existing file, does not change permissions even when they are specified" do + File.chmod(0644, @file) # r-w perms + orig_perms = File.stat(@file).mode & 0777 + begin + f = File.new(@file, "w", 0444) # r-o perms, but they should be ignored + f.puts("test") + ensure + f.close + end + perms = File.stat(@file).mode & 0777 + perms.should == orig_perms + + # it should be still possible to read from the file + File.read(@file).should == "test\n" + end + end + + it "returns a new File with modus fd" do + @fh = File.new(@file) + fh_copy = File.new(@fh.fileno) + fh_copy.autoclose = false + fh_copy.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "creates a new file when use File::EXCL mode" do + @fh = File.new(@file, File::EXCL) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "raises an Errorno::EEXIST if the file exists when create a new file with File::CREAT|File::EXCL" do + lambda { @fh = File.new(@file, File::CREAT|File::EXCL) }.should raise_error(Errno::EEXIST) + end + + it "creates a new file when use File::WRONLY|File::APPEND mode" do + @fh = File.new(@file, File::WRONLY|File::APPEND) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "returns a new File when use File::APPEND mode" do + @fh = File.new(@file, File::APPEND) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "returns a new File when use File::RDONLY|File::APPEND mode" do + @fh = File.new(@file, File::RDONLY|File::APPEND) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "returns a new File when use File::RDONLY|File::WRONLY mode" do + @fh = File.new(@file, File::RDONLY|File::WRONLY) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + + it "creates a new file when use File::WRONLY|File::TRUNC mode" do + @fh = File.new(@file, File::WRONLY|File::TRUNC) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "coerces filename using to_str" do + name = mock("file") + name.should_receive(:to_str).and_return(@file) + @fh = File.new(name, "w") + File.exist?(@file).should == true + end + + it "coerces filename using #to_path" do + name = mock("file") + name.should_receive(:to_path).and_return(@file) + @fh = File.new(name, "w") + File.exist?(@file).should == true + end + + it "raises a TypeError if the first parameter can't be coerced to a string" do + lambda { File.new(true) }.should raise_error(TypeError) + lambda { File.new(false) }.should raise_error(TypeError) + end + + it "raises a TypeError if the first parameter is nil" do + lambda { File.new(nil) }.should raise_error(TypeError) + end + + it "raises an Errno::EBADF if the first parameter is an invalid file descriptor" do + lambda { File.new(-1) }.should raise_error(Errno::EBADF) + end + + platform_is_not :windows do + it "can't alter mode or permissions when opening a file" do + @fh = File.new(@file) + lambda { + f = File.new(@fh.fileno, @flags) + f.autoclose = false + }.should raise_error(Errno::EINVAL) + end + end + + platform_is_not :windows do + it_behaves_like :open_directory, :new + end +end diff --git a/spec/rubyspec/core/file/null_spec.rb b/spec/rubyspec/core/file/null_spec.rb new file mode 100644 index 0000000000..b9dd6b658b --- /dev/null +++ b/spec/rubyspec/core/file/null_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File::NULL" do + platform_is :windows do + it "returns NUL as a string" do + File::NULL.should == 'NUL' + end + end + + platform_is_not :windows do + it "returns /dev/null as a string" do + File::NULL.should == '/dev/null' + end + end +end diff --git a/spec/rubyspec/core/file/open_spec.rb b/spec/rubyspec/core/file/open_spec.rb new file mode 100644 index 0000000000..09b1cb6add --- /dev/null +++ b/spec/rubyspec/core/file/open_spec.rb @@ -0,0 +1,676 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/open', __FILE__) + +describe "File.open" do + before :all do + @file = tmp("file_open.txt") + @unicode_path = tmp("こんにちは.txt") + @nonexistent = tmp("fake.txt") + rm_r @file, @nonexistent + end + + before :each do + ScratchPad.record [] + + @fh = @fd = nil + @flags = File::CREAT | File::TRUNC | File::WRONLY + touch @file + end + + after :each do + @fh.close if @fh and not @fh.closed? + rm_r @file, @unicode_path, @nonexistent + end + + describe "with a block" do + it "does not raise error when file is closed inside the block" do + @fh = File.open(@file) { |fh| fh.close; fh } + @fh.closed?.should == true + end + + it "invokes close on an opened file when exiting the block" do + File.open(@file, 'r') { |f| FileSpecs.make_closer f } + + ScratchPad.recorded.should == [:file_opened, :file_closed] + end + + it "propagates non-StandardErrors produced by close" do + lambda { + File.open(@file, 'r') { |f| FileSpecs.make_closer f, Exception } + }.should raise_error(Exception) + + ScratchPad.recorded.should == [:file_opened, :file_closed] + end + + it "propagates StandardErrors produced by close" do + lambda { + File.open(@file, 'r') { |f| FileSpecs.make_closer f, StandardError } + }.should raise_error(StandardError) + + ScratchPad.recorded.should == [:file_opened, :file_closed] + end + + it "does not propagate IOError with 'closed stream' message produced by close" do + File.open(@file, 'r') { |f| FileSpecs.make_closer f, IOError.new('closed stream') } + + ScratchPad.recorded.should == [:file_opened, :file_closed] + end + end + + it "opens the file (basic case)" do + @fh = File.open(@file) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens the file with unicode characters" do + @fh = File.open(@unicode_path, "w") + @fh.should be_kind_of(File) + File.exist?(@unicode_path).should == true + end + + it "opens a file when called with a block" do + File.open(@file) { |fh| } + File.exist?(@file).should == true + end + + it "opens with mode string" do + @fh = File.open(@file, 'w') + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file with mode string and block" do + File.open(@file, 'w') { |fh| } + File.exist?(@file).should == true + end + + it "opens a file with mode num" do + @fh = File.open(@file, @flags) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file with mode num and block" do + File.open(@file, 'w') { |fh| } + File.exist?(@file).should == true + end + + it "opens a file with mode and permission as nil" do + @fh = File.open(@file, nil, nil) + @fh.should be_kind_of(File) + end + + # For this test we delete the file first to reset the perms + it "opens the file when passed mode, num and permissions" do + rm_r @file + File.umask(0011) + @fh = File.open(@file, @flags, 0755) + @fh.should be_kind_of(File) + platform_is_not :windows do + @fh.lstat.mode.to_s(8).should == "100744" + end + File.exist?(@file).should == true + end + + # For this test we delete the file first to reset the perms + it "opens the file when passed mode, num, permissions and block" do + rm_r @file + File.umask(0022) + File.open(@file, "w", 0755){ |fh| } + platform_is_not :windows do + File.stat(@file).mode.to_s(8).should == "100755" + end + File.exist?(@file).should == true + end + + it "creates the file and returns writable descriptor when called with 'w' mode and r-o permissions" do + # it should be possible to write to such a file via returned descriptior, + # even though the file permissions are r-r-r. + + File.open(@file, "w", 0444) { |f| f.write("test") } + File.read(@file).should == "test" + end + + platform_is_not :windows do + it "opens the existing file, does not change permissions even when they are specified" do + File.chmod(0664, @file) + orig_perms = File.stat(@file).mode.to_s(8) + File.open(@file, "w", 0444) { |f| f.write("test") } + + File.stat(@file).mode.to_s(8).should == orig_perms + File.read(@file).should == "test" + end + end + + platform_is_not :windows do + it "creates a new write-only file when invoked with 'w' and '0222'" do + rm_r @file + File.open(@file, 'w', 0222) {} + File.readable?(@file).should == false + File.writable?(@file).should == true + end + end + + it "opens the file when call with fd" do + @fh = File.open(@file) + fh_copy = File.open(@fh.fileno) + fh_copy.autoclose = false + fh_copy.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file with a file descriptor d and a block" do + @fh = File.open(@file) + @fh.should be_kind_of(File) + + lambda { + File.open(@fh.fileno) do |fh| + @fd = fh.fileno + @fh.close + end + }.should raise_error(Errno::EBADF) + lambda { File.open(@fd) }.should raise_error(Errno::EBADF) + + File.exist?(@file).should == true + end + + it "opens a file that no exists when use File::WRONLY mode" do + lambda { File.open(@nonexistent, File::WRONLY) }.should raise_error(Errno::ENOENT) + end + + it "opens a file that no exists when use File::RDONLY mode" do + lambda { File.open(@nonexistent, File::RDONLY) }.should raise_error(Errno::ENOENT) + end + + it "opens a file that no exists when use 'r' mode" do + lambda { File.open(@nonexistent, 'r') }.should raise_error(Errno::ENOENT) + end + + it "opens a file that no exists when use File::EXCL mode" do + lambda { File.open(@nonexistent, File::EXCL) }.should raise_error(Errno::ENOENT) + end + + it "opens a file that no exists when use File::NONBLOCK mode" do + lambda { File.open(@nonexistent, File::NONBLOCK) }.should raise_error(Errno::ENOENT) + end + + platform_is_not :openbsd, :windows do + it "opens a file that no exists when use File::TRUNC mode" do + lambda { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::ENOENT) + end + end + + platform_is :openbsd, :windows do + it "does not open a file that does no exists when using File::TRUNC mode" do + lambda { File.open(@nonexistent, File::TRUNC) }.should raise_error(Errno::EINVAL) + end + end + + platform_is_not :windows do + it "opens a file that no exists when use File::NOCTTY mode" do + lambda { File.open(@nonexistent, File::NOCTTY) }.should raise_error(Errno::ENOENT) + end + end + + it "opens a file that no exists when use File::CREAT mode" do + @fh = File.open(@nonexistent, File::CREAT) { |f| f } + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file that no exists when use 'a' mode" do + @fh = File.open(@nonexistent, 'a') { |f| f } + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file that no exists when use 'w' mode" do + @fh = File.open(@nonexistent, 'w') { |f| f } + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + # Check the grants associated to the differents open modes combinations. + it "raises an ArgumentError exception when call with an unknown mode" do + lambda { File.open(@file, "q") }.should raise_error(ArgumentError) + end + + it "can read in a block when call open with RDONLY mode" do + File.open(@file, File::RDONLY) do |f| + f.gets.should == nil + end + end + + it "can read in a block when call open with 'r' mode" do + File.open(@file, "r") do |f| + f.gets.should == nil + end + end + + it "raises an IO exception when write in a block opened with RDONLY mode" do + File.open(@file, File::RDONLY) do |f| + lambda { f.puts "writing ..." }.should raise_error(IOError) + end + end + + it "raises an IO exception when write in a block opened with 'r' mode" do + File.open(@file, "r") do |f| + lambda { f.puts "writing ..." }.should raise_error(IOError) + end + end + + it "can't write in a block when call open with File::WRONLY||File::RDONLY mode" do + File.open(@file, File::WRONLY|File::RDONLY ) do |f| + f.puts("writing").should == nil + end + end + + it "can't read in a block when call open with File::WRONLY||File::RDONLY mode" do + lambda { + File.open(@file, File::WRONLY|File::RDONLY ) do |f| + f.gets.should == nil + end + }.should raise_error(IOError) + end + + it "can write in a block when call open with WRONLY mode" do + File.open(@file, File::WRONLY) do |f| + f.puts("writing").should == nil + end + end + + it "can write in a block when call open with 'w' mode" do + File.open(@file, "w") do |f| + f.puts("writing").should == nil + end + end + + it "raises an IOError when read in a block opened with WRONLY mode" do + File.open(@file, File::WRONLY) do |f| + lambda { f.gets }.should raise_error(IOError) + end + end + + it "raises an IOError when read in a block opened with 'w' mode" do + File.open(@file, "w") do |f| + lambda { f.gets }.should raise_error(IOError) + end + end + + it "raises an IOError when read in a block opened with 'a' mode" do + File.open(@file, "a") do |f| + lambda { f.gets }.should raise_error(IOError) + end + end + + it "raises an IOError when read in a block opened with 'a' mode" do + File.open(@file, "a") do |f| + f.puts("writing").should == nil + lambda { f.gets }.should raise_error(IOError) + end + end + + it "raises an IOError when read in a block opened with 'a' mode" do + File.open(@file, File::WRONLY|File::APPEND ) do |f| + lambda { f.gets }.should raise_error(IOError) + end + end + + it "raises an IOError when read in a block opened with File::WRONLY|File::APPEND mode" do + File.open(@file, File::WRONLY|File::APPEND ) do |f| + f.puts("writing").should == nil + lambda { f.gets }.should raise_error(IOError) + end + end + + it "raises an IOError when read in a block opened with File::RDONLY|File::APPEND mode" do + lambda { + File.open(@file, File::RDONLY|File::APPEND ) do |f| + f.puts("writing") + end + }.should raise_error(IOError) + end + + it "can read and write in a block when call open with RDWR mode" do + File.open(@file, File::RDWR) do |f| + f.gets.should == nil + f.puts("writing").should == nil + f.rewind + f.gets.should == "writing\n" + end + end + + it "can't read in a block when call open with File::EXCL mode" do + lambda { + File.open(@file, File::EXCL) do |f| + f.puts("writing").should == nil + end + }.should raise_error(IOError) + end + + it "can read in a block when call open with File::EXCL mode" do + File.open(@file, File::EXCL) do |f| + f.gets.should == nil + end + end + + it "can read and write in a block when call open with File::RDWR|File::EXCL mode" do + File.open(@file, File::RDWR|File::EXCL) do |f| + f.gets.should == nil + f.puts("writing").should == nil + f.rewind + f.gets.should == "writing\n" + end + end + + it "raises an Errorno::EEXIST if the file exists when open with File::CREAT|File::EXCL" do + lambda { + File.open(@file, File::CREAT|File::EXCL) do |f| + f.puts("writing") + end + }.should raise_error(Errno::EEXIST) + end + + it "creates a new file when use File::WRONLY|File::APPEND mode" do + @fh = File.open(@file, File::WRONLY|File::APPEND) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file when use File::WRONLY|File::APPEND mode" do + File.open(@file, File::WRONLY) do |f| + f.puts("hello file") + end + File.open(@file, File::RDWR|File::APPEND) do |f| + f.puts("bye file") + f.rewind + f.gets.should == "hello file\n" + f.gets.should == "bye file\n" + f.gets.should == nil + end + end + + it "raises an IOError if the file exists when open with File::RDONLY|File::APPEND" do + lambda { + File.open(@file, File::RDONLY|File::APPEND) do |f| + f.puts("writing").should == nil + end + }.should raise_error(IOError) + end + + platform_is_not :openbsd, :windows do + it "truncates the file when passed File::TRUNC mode" do + File.open(@file, File::RDWR) { |f| f.puts "hello file" } + @fh = File.open(@file, File::TRUNC) + @fh.gets.should == nil + end + + it "can't read in a block when call open with File::TRUNC mode" do + File.open(@file, File::TRUNC) do |f| + f.gets.should == nil + end + end + end + + it "opens a file when use File::WRONLY|File::TRUNC mode" do + fh1 = File.open(@file, "w") + begin + @fh = File.open(@file, File::WRONLY|File::TRUNC) + @fh.should be_kind_of(File) + File.exist?(@file).should == true + ensure + fh1.close + end + end + + platform_is_not :openbsd, :windows do + it "can't write in a block when call open with File::TRUNC mode" do + lambda { + File.open(@file, File::TRUNC) do |f| + f.puts("writing") + end + }.should raise_error(IOError) + end + + it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do + lambda { + File.open(@file, File::RDONLY|File::TRUNC) do |f| + f.puts("writing").should == nil + end + }.should raise_error(IOError) + end + end + + platform_is :openbsd, :windows do + it "can't write in a block when call open with File::TRUNC mode" do + lambda { + File.open(@file, File::TRUNC) do |f| + f.puts("writing") + end + }.should raise_error(Errno::EINVAL) + end + + it "raises an Errorno::EEXIST if the file exists when open with File::RDONLY|File::TRUNC" do + lambda { + File.open(@file, File::RDONLY|File::TRUNC) do |f| + f.puts("writing").should == nil + end + }.should raise_error(Errno::EINVAL) + end + end + + platform_is_not :windows do + it "raises an Errno::EACCES when opening non-permitted file" do + @fh = File.open(@file, "w") + @fh.chmod(000) + lambda { fh1 = File.open(@file); fh1.close }.should raise_error(Errno::EACCES) + end + end + + it "raises an Errno::EACCES when opening read-only file" do + @fh = File.open(@file, "w") + @fh.chmod(0444) + lambda { File.open(@file, "w") }.should raise_error(Errno::EACCES) + end + + it "opens a file for binary read" do + @fh = File.open(@file, "rb") + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file for binary write" do + @fh = File.open(@file, "wb") + @fh.should be_kind_of(File) + File.exist?(@file).should == true + end + + it "opens a file for read-write and truncate the file" do + File.open(@file, "w") { |f| f.puts "testing" } + File.size(@file).should > 0 + File.open(@file, "w+") do |f| + f.pos.should == 0 + f.eof?.should == true + end + File.size(@file).should == 0 + end + + it "opens a file for binary read-write starting at the beginning of the file" do + File.open(@file, "w") { |f| f.puts "testing" } + File.size(@file).should > 0 + File.open(@file, "rb+") do |f| + f.pos.should == 0 + f.eof?.should == false + end + end + + it "opens a file for binary read-write and truncate the file" do + File.open(@file, "w") { |f| f.puts "testing" } + File.size(@file).should > 0 + File.open(@file, "wb+") do |f| + f.pos.should == 0 + f.eof?.should == true + end + File.size(@file).should == 0 + end + + ruby_version_is "2.3" do + platform_is :linux do + if defined?(File::TMPFILE) + it "creates an unnamed temporary file with File::TMPFILE" do + dir = tmp("").chomp("/") + rm_r @file + Dir["#{dir}/*"].should == [] + begin + File.open(dir, "r+", flags: File::TMPFILE) do |io| + io.write("ruby") + io.flush + io.rewind + io.read.should == "ruby" + Dir["#{dir}/*"].should == [] + end + rescue Errno::EOPNOTSUPP, Errno::EINVAL + # EOPNOTSUPP: no support from the filesystem + # EINVAL: presumably bug in glibc + 1.should == 1 + end + end + end + end + end + + it "raises a TypeError if passed a filename that is not a String or Integer type" do + lambda { File.open(true) }.should raise_error(TypeError) + lambda { File.open(false) }.should raise_error(TypeError) + lambda { File.open(nil) }.should raise_error(TypeError) + end + + it "raises a SystemCallError if passed an invalid Integer type" do + lambda { File.open(-1) }.should raise_error(SystemCallError) + end + + it "raises an ArgumentError if passed the wrong number of arguments" do + lambda { File.open(@file, File::CREAT, 0755, 'test') }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed an invalid string for mode" do + lambda { File.open(@file, 'fake') }.should raise_error(ArgumentError) + end + + it "defaults external_encoding to ASCII-8BIT for binary modes" do + File.open(@file, 'rb') {|f| f.external_encoding.should == Encoding::ASCII_8BIT} + File.open(@file, 'wb+') {|f| f.external_encoding.should == Encoding::ASCII_8BIT} + end + + it "uses the second argument as an options Hash" do + @fh = File.open(@file, mode: "r") + @fh.should be_an_instance_of(File) + end + + it "calls #to_hash to convert the second argument to a Hash" do + options = mock("file open options") + options.should_receive(:to_hash).and_return({ mode: "r" }) + + @fh = File.open(@file, options) + end + + ruby_version_is "2.3" do + it "accepts extra flags as a keyword argument and combine with a string mode" do + lambda { + File.open(@file, "w", flags: File::EXCL) { } + }.should raise_error(Errno::EEXIST) + + lambda { + File.open(@file, mode: "w", flags: File::EXCL) { } + }.should raise_error(Errno::EEXIST) + end + + it "accepts extra flags as a keyword argument and combine with an integer mode" do + lambda { + File.open(@file, File::WRONLY | File::CREAT, flags: File::EXCL) { } + }.should raise_error(Errno::EEXIST) + end + end + + platform_is_not :windows do + describe "on a FIFO" do + before :each do + @fifo = tmp("File_open_fifo") + system "mkfifo #{@fifo}" + end + + after :each do + rm_r @fifo + end + + it "opens it as a normal file" do + file_w, file_r, read_bytes, written_length = nil + + # open in threads, due to blocking open and writes + writer = Thread.new do + file_w = File.open(@fifo, 'w') + written_length = file_w.syswrite('hello') + end + reader = Thread.new do + file_r = File.open(@fifo, 'r') + read_bytes = file_r.sysread(5) + end + + begin + writer.join + reader.join + + written_length.should == 5 + read_bytes.should == 'hello' + ensure + file_w.close if file_w + file_r.close if file_r + end + end + end + end + +end + +describe "File.open when passed a file descriptor" do + before do + @content = "File#open when passed a file descriptor" + @name = tmp("file_open_with_fd.txt") + @fd = new_fd @name, fmode("w:utf-8") + @file = nil + end + + after do + @file.close if @file and not @file.closed? + rm_r @name + end + + it "opens a file" do + @file = File.open(@fd, "w") + @file.should be_an_instance_of(File) + @file.fileno.should equal(@fd) + @file.write @content + @file.flush + File.read(@name).should == @content + end + + it "opens a file when passed a block" do + @file = File.open(@fd, "w") do |f| + f.should be_an_instance_of(File) + f.fileno.should equal(@fd) + f.write @content + f + end + File.read(@name).should == @content + end +end + +platform_is_not :windows do + describe "File.open" do + it_behaves_like :open_directory, :open + end +end diff --git a/spec/rubyspec/core/file/owned_spec.rb b/spec/rubyspec/core/file/owned_spec.rb new file mode 100644 index 0000000000..d19e9cb278 --- /dev/null +++ b/spec/rubyspec/core/file/owned_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/owned', __FILE__) + +describe "File.owned?" do + it_behaves_like :file_owned, :owned?, File +end + +describe "File.owned?" do + before :each do + @filename = tmp("i_exist") + touch(@filename) + end + + after :each do + rm_r @filename + end + + it "returns false if file does not exist" do + File.owned?("I_am_a_bogus_file").should == false + end + + it "returns true if the file exist and is owned by the user" do + File.owned?(@filename).should == true + end + + platform_is_not :windows do + it "returns false when the file is not owned by the user" do + system_file = '/etc/passwd' + File.owned?(system_file).should == false + end + end + +end diff --git a/spec/rubyspec/core/file/path_spec.rb b/spec/rubyspec/core/file/path_spec.rb new file mode 100644 index 0000000000..5004e128cd --- /dev/null +++ b/spec/rubyspec/core/file/path_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File#path" do + before :each do + @name = tmp("file_path") + end + + after :each do + rm_r @name + end + + it "returns the pathname used to create file as a string" do + File.open(@name,'w') { |file| file.path.should == @name } + end +end + +describe "File.path" do + before :each do + @name = tmp("file_path") + end + + after :each do + rm_r @name + end + + it "returns the full path for the given file" do + File.path(@name).should == @name + end +end diff --git a/spec/rubyspec/core/file/pipe_spec.rb b/spec/rubyspec/core/file/pipe_spec.rb new file mode 100644 index 0000000000..ca7392b8ee --- /dev/null +++ b/spec/rubyspec/core/file/pipe_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/pipe', __FILE__) + +describe "File.pipe?" do + it_behaves_like :file_pipe, :pipe?, File +end + +describe "File.pipe?" do + it "returns false if file does not exist" do + File.pipe?("I_am_a_bogus_file").should == false + end + + it "returns false if the file is not a pipe" do + filename = tmp("i_exist") + touch(filename) + + File.pipe?(filename).should == false + + rm_r filename + end + + platform_is_not :windows do + it "returns true if the file is a pipe" do + filename = tmp("i_am_a_pipe") + system "mkfifo #{filename}" + + File.pipe?(filename).should == true + + rm_r filename + end + end +end diff --git a/spec/rubyspec/core/file/read_spec.rb b/spec/rubyspec/core/file/read_spec.rb new file mode 100644 index 0000000000..fdbbf58a1c --- /dev/null +++ b/spec/rubyspec/core/file/read_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/read', __FILE__) + +describe "File.read" do + it_behaves_like :file_read_directory, :read, File +end diff --git a/spec/rubyspec/core/file/readable_real_spec.rb b/spec/rubyspec/core/file/readable_real_spec.rb new file mode 100644 index 0000000000..5fca968611 --- /dev/null +++ b/spec/rubyspec/core/file/readable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/readable_real', __FILE__) + +describe "File.readable_real?" do + it_behaves_like :file_readable_real, :readable_real?, File + it_behaves_like :file_readable_real_missing, :readable_real?, File +end diff --git a/spec/rubyspec/core/file/readable_spec.rb b/spec/rubyspec/core/file/readable_spec.rb new file mode 100644 index 0000000000..3307e5e30f --- /dev/null +++ b/spec/rubyspec/core/file/readable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/readable', __FILE__) + +describe "File.readable?" do + it_behaves_like :file_readable, :readable?, File + it_behaves_like :file_readable_missing, :readable?, File +end diff --git a/spec/rubyspec/core/file/readlink_spec.rb b/spec/rubyspec/core/file/readlink_spec.rb new file mode 100644 index 0000000000..6db2c09780 --- /dev/null +++ b/spec/rubyspec/core/file/readlink_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.readlink" do + # symlink/readlink are not supported on Windows + platform_is_not :windows do + describe "File.readlink with absolute paths" do + before :each do + @file = tmp('file_readlink.txt') + @link = tmp('file_readlink.lnk') + + File.symlink(@file, @link) + end + + after :each do + rm_r @file, @link + end + + it "returns the name of the file referenced by the given link" do + touch @file + File.readlink(@link).should == @file + end + + it "returns the name of the file referenced by the given link when the file does not exist" do + File.readlink(@link).should == @file + end + + it "raises an Errno::ENOENT if there is no such file" do + # TODO: missing_file + lambda { File.readlink("/this/surely/doesnt/exist") }.should raise_error(Errno::ENOENT) + end + + it "raises an Errno::EINVAL if called with a normal file" do + touch @file + lambda { File.readlink(@file) }.should raise_error(Errno::EINVAL) + end + end + + describe "File.readlink when changing the working directory" do + before :each do + @cwd = Dir.pwd + @tmpdir = tmp("/readlink") + Dir.mkdir @tmpdir + Dir.chdir @tmpdir + + @link = 'readlink_link' + @file = 'readlink_file' + + File.symlink(@file, @link) + end + + after :each do + rm_r @file, @link + Dir.chdir @cwd + Dir.rmdir @tmpdir + end + + it "returns the name of the file referenced by the given link" do + touch @file + File.readlink(@link).should == @file + end + + it "returns the name of the file referenced by the given link when the file does not exist" do + File.readlink(@link).should == @file + end + end + end +end diff --git a/spec/rubyspec/core/file/realdirpath_spec.rb b/spec/rubyspec/core/file/realdirpath_spec.rb new file mode 100644 index 0000000000..3d50b8813c --- /dev/null +++ b/spec/rubyspec/core/file/realdirpath_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +platform_is_not :windows do + describe "File.realdirpath" do + before :each do + @real_dir = tmp('dir_realdirpath_real') + @fake_dir = tmp('dir_realdirpath_fake') + @link_dir = tmp('dir_realdirpath_link') + + mkdir_p @real_dir + File.symlink(@real_dir, @link_dir) + + @file = File.join(@real_dir, 'file') + @link = File.join(@link_dir, 'link') + + touch @file + File.symlink(@file, @link) + + @fake_file_in_real_dir = File.join(@real_dir, 'fake_file_in_real_dir') + @fake_file_in_fake_dir = File.join(@fake_dir, 'fake_file_in_fake_dir') + @fake_link_to_real_dir = File.join(@link_dir, 'fake_link_to_real_dir') + @fake_link_to_fake_dir = File.join(@link_dir, 'fake_link_to_fake_dir') + + File.symlink(@fake_file_in_real_dir, @fake_link_to_real_dir) + File.symlink(@fake_file_in_fake_dir, @fake_link_to_fake_dir) + + @dir_for_relative_link = File.join(@real_dir, 'dir1') + mkdir_p @dir_for_relative_link + + @relative_path_to_file = File.join('..', 'file') + @relative_symlink = File.join(@dir_for_relative_link, 'link') + File.symlink(@relative_path_to_file, @relative_symlink) + end + + after :each do + rm_r @file, @link, @fake_link_to_real_dir, @fake_link_to_fake_dir, @real_dir, @link_dir + end + + it "returns '/' when passed '/'" do + File.realdirpath('/').should == '/' + end + + it "returns the real (absolute) pathname not containing symlinks" do + File.realdirpath(@link).should == @file + end + + it "uses base directory for interpreting relative pathname" do + File.realdirpath(File.basename(@link), @link_dir).should == @file + end + + it "uses current directory for interpreting relative pathname" do + Dir.chdir @link_dir do + File.realdirpath(File.basename(@link)).should == @file + end + end + + it "uses link directory for expanding relative links" do + File.realdirpath(@relative_symlink).should == @file + end + + it "raises an Errno::ELOOP if the symlink points to itself" do + File.unlink @link + File.symlink(@link, @link) + lambda { File.realdirpath(@link) }.should raise_error(Errno::ELOOP) + end + + it "returns the real (absolute) pathname if the file is absent" do + File.realdirpath(@fake_file_in_real_dir).should == @fake_file_in_real_dir + end + + it "raises Errno::ENOENT if the directory is absent" do + lambda { File.realdirpath(@fake_file_in_fake_dir) }.should raise_error(Errno::ENOENT) + end + + it "returns the real (absolute) pathname if the symlink points to an absent file" do + File.realdirpath(@fake_link_to_real_dir).should == @fake_file_in_real_dir + end + + it "raises Errno::ENOENT if the symlink points to an absent directory" do + lambda { File.realdirpath(@fake_link_to_fake_dir) }.should raise_error(Errno::ENOENT) + end + end +end + +platform_is :windows do + describe "File.realdirpath" do + before :each do + @file = tmp("realdirpath") + end + + it "returns the same path" do + file = __FILE__ + File.realdirpath(file).should == file + end + + it "returns the same path even if the last component does not exist" do + File.realdirpath(@file).should == @file + end + end +end diff --git a/spec/rubyspec/core/file/realpath_spec.rb b/spec/rubyspec/core/file/realpath_spec.rb new file mode 100644 index 0000000000..e6ddfdad7c --- /dev/null +++ b/spec/rubyspec/core/file/realpath_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +platform_is_not :windows do + describe "File.realpath" do + before :each do + @real_dir = tmp('dir_realpath_real') + @link_dir = tmp('dir_realpath_link') + + mkdir_p @real_dir + File.symlink(@real_dir, @link_dir) + + @file = File.join(@real_dir, 'file') + @link = File.join(@link_dir, 'link') + + touch @file + File.symlink(@file, @link) + + @fake_file = File.join(@real_dir, 'fake_file') + @fake_link = File.join(@link_dir, 'fake_link') + + File.symlink(@fake_file, @fake_link) + + @dir_for_relative_link = File.join(@real_dir, 'dir1') + mkdir_p @dir_for_relative_link + + @relative_path_to_file = File.join('..', 'file') + @relative_symlink = File.join(@dir_for_relative_link, 'link') + File.symlink(@relative_path_to_file, @relative_symlink) + end + + after :each do + rm_r @file, @link, @fake_link, @real_dir, @link_dir + end + + it "returns '/' when passed '/'" do + File.realpath('/').should == '/' + end + + it "returns the real (absolute) pathname not containing symlinks" do + File.realpath(@link).should == @file + end + + it "uses base directory for interpreting relative pathname" do + File.realpath(File.basename(@link), @link_dir).should == @file + end + + it "uses current directory for interpreting relative pathname" do + Dir.chdir @link_dir do + File.realpath(File.basename(@link)).should == @file + end + end + + it "uses link directory for expanding relative links" do + File.realpath(@relative_symlink).should == @file + end + + it "raises an Errno::ELOOP if the symlink points to itself" do + File.unlink @link + File.symlink(@link, @link) + lambda { File.realpath(@link) }.should raise_error(Errno::ELOOP) + end + + it "raises Errno::ENOENT if the file is absent" do + lambda { File.realpath(@fake_file) }.should raise_error(Errno::ENOENT) + end + + it "raises Errno::ENOENT if the symlink points to an absent file" do + lambda { File.realpath(@fake_link) }.should raise_error(Errno::ENOENT) + end + end +end + +platform_is :windows do + describe "File.realpath" do + it "returns the same path" do + file = __FILE__ + File.realpath(file).should == file + end + end +end diff --git a/spec/rubyspec/core/file/rename_spec.rb b/spec/rubyspec/core/file/rename_spec.rb new file mode 100644 index 0000000000..a62ba809bd --- /dev/null +++ b/spec/rubyspec/core/file/rename_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.rename" do + before :each do + @old = tmp("file_rename.txt") + @new = tmp("file_rename.new") + + rm_r @new + touch(@old) { |f| f.puts "hello" } + end + + after :each do + rm_r @old, @new + end + + it "renames a file" do + File.exist?(@old).should == true + File.exist?(@new).should == false + File.rename(@old, @new) + File.exist?(@old).should == false + File.exist?(@new).should == true + end + + it "raises an Errno::ENOENT if the source does not exist" do + rm_r @old + lambda { File.rename(@old, @new) }.should raise_error(Errno::ENOENT) + end + + it "raises an ArgumentError if not passed two arguments" do + lambda { File.rename }.should raise_error(ArgumentError) + lambda { File.rename(@file) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed String types" do + lambda { File.rename(1, 2) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/file/reopen_spec.rb b/spec/rubyspec/core/file/reopen_spec.rb new file mode 100644 index 0000000000..2493829740 --- /dev/null +++ b/spec/rubyspec/core/file/reopen_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File#reopen" do + before :each do + @name_a = tmp("file_reopen_a.txt") + @name_b = tmp("file_reopen_b.txt") + @content_a = "File#reopen a" + @content_b = "File#reopen b" + + touch(@name_a) { |f| f.write @content_a } + touch(@name_b) { |f| f.write @content_b } + + @file = nil + end + + after :each do + @file.close if @file and not @file.closed? + rm_r @name_a, @name_b + end + + it "resets the stream to a new file path" do + file = File.new @name_a, "r" + file.read.should == @content_a + @file = file.reopen(@name_b, "r") + @file.read.should == @content_b + end + + it "calls #to_path to convern an Object" do + @file = File.new(@name_a).reopen(mock_to_path(@name_b), "r") + @file.read.should == @content_b + end +end diff --git a/spec/rubyspec/core/file/setgid_spec.rb b/spec/rubyspec/core/file/setgid_spec.rb new file mode 100644 index 0000000000..dc63329cc3 --- /dev/null +++ b/spec/rubyspec/core/file/setgid_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/setgid', __FILE__) + +describe "File.setgid?" do + it_behaves_like :file_setgid, :setgid?, File +end + +describe "File.setgid?" do + before :each do + @name = tmp('test.txt') + touch @name + end + + after :each do + rm_r @name + end + + it "returns false if the file was just made" do + File.setgid?(@name).should == false + end + + it "returns false if the file does not exist" do + rm_r @name # delete it prematurely, just for this part + File.setgid?(@name).should == false + end + + as_superuser do + platform_is_not :windows do + it "returns true when the gid bit is set" do + system "chmod g+s #{@name}" + + File.setgid?(@name).should == true + end + end + end +end diff --git a/spec/rubyspec/core/file/setuid_spec.rb b/spec/rubyspec/core/file/setuid_spec.rb new file mode 100644 index 0000000000..dcd1d3aed1 --- /dev/null +++ b/spec/rubyspec/core/file/setuid_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/setuid', __FILE__) + +describe "File.setuid?" do + it_behaves_like :file_setuid, :setuid?, File +end + +describe "File.setuid?" do + before :each do + @name = tmp('test.txt') + touch @name + end + + after :each do + rm_r @name + end + + it "returns false if the file was just made" do + File.setuid?(@name).should == false + end + + it "returns false if the file does not exist" do + rm_r @name # delete it prematurely, just for this part + File.setuid?(@name).should == false + end + + platform_is_not :windows do + it "returns true when the gid bit is set" do + platform_is :solaris do + # Solaris requires execute bit before setting suid + system "chmod u+x #{@name}" + end + system "chmod u+s #{@name}" + + File.setuid?(@name).should == true + end + end +end diff --git a/spec/rubyspec/core/file/shared/fnmatch.rb b/spec/rubyspec/core/file/shared/fnmatch.rb new file mode 100644 index 0000000000..80e37b3fff --- /dev/null +++ b/spec/rubyspec/core/file/shared/fnmatch.rb @@ -0,0 +1,241 @@ +describe :file_fnmatch, shared: true do + it "matches entire strings" do + File.send(@method, 'cat', 'cat').should == true + end + + it "does not match partial strings" do + File.send(@method, 'cat', 'category').should == false + end + + it "does not support { } patterns by default" do + File.send(@method, 'c{at,ub}s', 'cats').should == false + File.send(@method, 'c{at,ub}s', 'c{at,ub}s').should == true + end + + it "supports some { } patterns when File::FNM_EXTGLOB is passed" do + File.send(@method, "{a,b}", "a", File::FNM_EXTGLOB).should == true + File.send(@method, "{a,b}", "b", File::FNM_EXTGLOB).should == true + File.send(@method, "c{at,ub}s", "cats", File::FNM_EXTGLOB).should == true + File.send(@method, "c{at,ub}s", "cubs", File::FNM_EXTGLOB).should == true + File.send(@method, "-c{at,ub}s-", "-cats-", File::FNM_EXTGLOB).should == true + File.send(@method, "-c{at,ub}s-", "-cubs-", File::FNM_EXTGLOB).should == true + File.send(@method, "{a,b,c}{d,e,f}{g,h}", "adg", File::FNM_EXTGLOB).should == true + File.send(@method, "{a,b,c}{d,e,f}{g,h}", "bdg", File::FNM_EXTGLOB).should == true + File.send(@method, "{a,b,c}{d,e,f}{g,h}", "ceh", File::FNM_EXTGLOB).should == true + File.send(@method, "{aa,bb,cc,dd}", "aa", File::FNM_EXTGLOB).should == true + File.send(@method, "{aa,bb,cc,dd}", "bb", File::FNM_EXTGLOB).should == true + File.send(@method, "{aa,bb,cc,dd}", "cc", File::FNM_EXTGLOB).should == true + File.send(@method, "{aa,bb,cc,dd}", "dd", File::FNM_EXTGLOB).should == true + File.send(@method, "{1,5{a,b{c,d}}}", "1", File::FNM_EXTGLOB).should == true + File.send(@method, "{1,5{a,b{c,d}}}", "5a", File::FNM_EXTGLOB).should == true + File.send(@method, "{1,5{a,b{c,d}}}", "5bc", File::FNM_EXTGLOB).should == true + File.send(@method, "{1,5{a,b{c,d}}}", "5bd", File::FNM_EXTGLOB).should == true + File.send(@method, "\\\\{a\\,b,b\\}c}", "\\a,b", File::FNM_EXTGLOB).should == true + File.send(@method, "\\\\{a\\,b,b\\}c}", "\\b}c", File::FNM_EXTGLOB).should == true + end + + it "doesn't support some { } patterns even when File::FNM_EXTGLOB is passed" do + File.send(@method, "a{0..3}b", "a0b", File::FNM_EXTGLOB).should == false + File.send(@method, "a{0..3}b", "a1b", File::FNM_EXTGLOB).should == false + File.send(@method, "a{0..3}b", "a2b", File::FNM_EXTGLOB).should == false + File.send(@method, "a{0..3}b", "a3b", File::FNM_EXTGLOB).should == false + File.send(@method, "{0..12}", "0", File::FNM_EXTGLOB).should == false + File.send(@method, "{0..12}", "6", File::FNM_EXTGLOB).should == false + File.send(@method, "{0..12}", "12", File::FNM_EXTGLOB).should == false + File.send(@method, "{3..-2}", "3", File::FNM_EXTGLOB).should == false + File.send(@method, "{3..-2}", "0", File::FNM_EXTGLOB).should == false + File.send(@method, "{3..-2}", "-2", File::FNM_EXTGLOB).should == false + File.send(@method, "{a..g}", "a", File::FNM_EXTGLOB).should == false + File.send(@method, "{a..g}", "d", File::FNM_EXTGLOB).should == false + File.send(@method, "{a..g}", "g", File::FNM_EXTGLOB).should == false + File.send(@method, "{g..a}", "a", File::FNM_EXTGLOB).should == false + File.send(@method, "{g..a}", "d", File::FNM_EXTGLOB).should == false + File.send(@method, "{g..a}", "g", File::FNM_EXTGLOB).should == false + File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false + File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: ,", File::FNM_EXTGLOB).should == false + File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: }", File::FNM_EXTGLOB).should == false + File.send(@method, "escaping: {{,\\,,\\},\\{}", "escaping: {", File::FNM_EXTGLOB).should == false + end + + it "doesn't match an extra } when File::FNM_EXTGLOB is passed" do + File.send(@method, 'c{at,ub}}s', 'cats', File::FNM_EXTGLOB).should == false + end + + it "matches when both FNM_EXTGLOB and FNM_PATHNAME are passed" do + File.send(@method, "?.md", "a.md", File::FNM_EXTGLOB | File::FNM_PATHNAME).should == true + end + + it "matches a single character for each ? character" do + File.send(@method, 'c?t', 'cat').should == true + File.send(@method, 'c??t', 'cat').should == false + end + + it "matches zero or more characters for each * character" do + File.send(@method, 'c*', 'cats').should == true + File.send(@method, 'c*t', 'c/a/b/t').should == true + end + + it "matches ranges of characters using bracket expresions (e.g. [a-z])" do + File.send(@method, 'ca[a-z]', 'cat').should == true + end + + it "matches ranges of characters using bracket expresions, taking case into account" do + File.send(@method, '[a-z]', 'D').should == false + File.send(@method, '[^a-z]', 'D').should == true + File.send(@method, '[A-Z]', 'd').should == false + File.send(@method, '[^A-Z]', 'd').should == true + File.send(@method, '[a-z]', 'D', File::FNM_CASEFOLD).should == true + end + + it "does not match characters outside of the range of the bracket expresion" do + File.send(@method, 'ca[x-z]', 'cat').should == false + File.send(@method, '/ca[s][s-t]/rul[a-b]/[z]he/[x-Z]orld', '/cats/rule/the/World').should == false + end + + it "matches ranges of characters using exclusive bracket expresions (e.g. [^t] or [!t])" do + File.send(@method, 'ca[^t]', 'cat').should == false + File.send(@method, 'ca[!t]', 'cat').should == false + end + + it "matches characters with a case sensitive comparison" do + File.send(@method, 'cat', 'CAT').should == false + end + + it "matches characters with case insensitive comparison when flags includes FNM_CASEFOLD" do + File.send(@method, 'cat', 'CAT', File::FNM_CASEFOLD).should == true + end + + platform_is_not :windows do + it "doesn't match case sensitive characters on platfroms with case sensitive paths, when flags include FNM_SYSCASE" do + File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == false + end + end + + platform_is :windows do + it "matches case sensitive characters on platfroms with case insensitive paths, when flags include FNM_SYSCASE" do + File.send(@method, 'cat', 'CAT', File::FNM_SYSCASE).should == true + end + end + + it "does not match '/' characters with ? or * when flags includes FNM_PATHNAME" do + File.send(@method, '?', '/', File::FNM_PATHNAME).should == false + File.send(@method, '*', '/', File::FNM_PATHNAME).should == false + end + + it "does not match '/' characters inside bracket expressions when flags includes FNM_PATHNAME" do + File.send(@method, '[/]', '/', File::FNM_PATHNAME).should == false + end + + it "matches literal ? or * in path when pattern includes \\? or \\*" do + File.send(@method, '\?', '?').should == true + File.send(@method, '\?', 'a').should == false + + File.send(@method, '\*', '*').should == true + File.send(@method, '\*', 'a').should == false + end + + it "matches literal character (e.g. 'a') in path when pattern includes escaped character (e.g. \\a)" do + File.send(@method, '\a', 'a').should == true + File.send(@method, 'this\b', 'thisb').should == true + end + + it "matches '\\' characters in path when flags includes FNM_NOESACPE" do + File.send(@method, '\a', '\a', File::FNM_NOESCAPE).should == true + File.send(@method, '\a', 'a', File::FNM_NOESCAPE).should == false + File.send(@method, '\[foo\]\[bar\]', '[foo][bar]', File::FNM_NOESCAPE).should == false + end + + it "escapes special characters inside bracket expression" do + File.send(@method, '[\?]', '?').should == true + File.send(@method, '[\*]', '*').should == true + end + + it "does not match leading periods in filenames with wildcards by default" do + File.send(@method, '*', '.profile').should == false + File.send(@method, '*', 'home/.profile').should == true + File.send(@method, '*/*', 'home/.profile').should == true + File.send(@method, '*/*', 'dave/.profile', File::FNM_PATHNAME).should == false + end + + it "matches patterns with leading periods to dotfiles by default" do + File.send(@method, '.*', '.profile').should == true + File.send(@method, ".*file", "nondotfile").should == false + end + + it "matches leading periods in filenames when flags includes FNM_DOTMATCH" do + File.send(@method, '*', '.profile', File::FNM_DOTMATCH).should == true + File.send(@method, '*', 'home/.profile', File::FNM_DOTMATCH).should == true + end + + it "matches multiple directories with ** and *" do + files = '**/*.rb' + File.send(@method, files, 'main.rb').should == false + File.send(@method, files, './main.rb').should == false + File.send(@method, files, 'lib/song.rb').should == true + File.send(@method, '**.rb', 'main.rb').should == true + File.send(@method, '**.rb', './main.rb').should == false + File.send(@method, '**.rb', 'lib/song.rb').should == true + File.send(@method, '*', 'dave/.profile').should == true + end + + it "matches multiple directories with ** when flags includes File::FNM_PATHNAME" do + files = '**/*.rb' + flags = File::FNM_PATHNAME + + File.send(@method, files, 'main.rb', flags).should == true + File.send(@method, files, 'one/two/three/main.rb', flags).should == true + File.send(@method, files, './main.rb', flags).should == false + + flags = File::FNM_PATHNAME | File::FNM_DOTMATCH + + File.send(@method, files, './main.rb', flags).should == true + File.send(@method, files, 'one/two/.main.rb', flags).should == true + + File.send(@method, "**/best/*", 'lib/my/best/song.rb').should == true + end + + it "returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME" do + pattern = '*/*' + File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME).should be_false + + pattern = '**/foo' + File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME).should be_false + end + + it "returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME" do + pattern = '*/*' + File.send(@method, pattern, 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + + pattern = '**/foo' + File.send(@method, pattern, 'a/b/c/foo', File::FNM_PATHNAME).should be_true + File.send(@method, pattern, '/a/b/c/foo', File::FNM_PATHNAME).should be_true + File.send(@method, pattern, 'c:/a/b/c/foo', File::FNM_PATHNAME).should be_true + File.send(@method, pattern, 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH).should be_true + end + + it "accepts an object that has a #to_path method" do + File.send(@method, '\*', mock_to_path('a')).should == false + end + + it "raises a TypeError if the first and second arguments are not string-like" do + lambda { File.send(@method, nil, nil, 0, 0) }.should raise_error(ArgumentError) + lambda { File.send(@method, 1, 'some/thing') }.should raise_error(TypeError) + lambda { File.send(@method, 'some/thing', 1) }.should raise_error(TypeError) + lambda { File.send(@method, 1, 1) }.should raise_error(TypeError) + end + + it "raises a TypeError if the third argument is not an Integer" do + lambda { File.send(@method, "*/place", "path/to/file", "flags") }.should raise_error(TypeError) + lambda { File.send(@method, "*/place", "path/to/file", nil) }.should raise_error(TypeError) + end + + it "does not raise a TypeError if the third argument can be coerced to an Integer" do + flags = mock("flags") + flags.should_receive(:to_int).and_return(10) + lambda { File.send(@method, "*/place", "path/to/file", flags) }.should_not raise_error + end + + it "matches multibyte characters" do + File.fnmatch("*/ä/ø/ñ", "a/ä/ø/ñ").should == true + end +end diff --git a/spec/rubyspec/core/file/shared/open.rb b/spec/rubyspec/core/file/shared/open.rb new file mode 100644 index 0000000000..0ca1bc74db --- /dev/null +++ b/spec/rubyspec/core/file/shared/open.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../dir/fixtures/common', __FILE__) + +describe :open_directory, shared: true do + it "opens directories" do + file = File.send(@method, tmp("")) + begin + file.should be_kind_of(File) + ensure + file.close + end + end +end diff --git a/spec/rubyspec/core/file/shared/read.rb b/spec/rubyspec/core/file/shared/read.rb new file mode 100644 index 0000000000..916a6222bf --- /dev/null +++ b/spec/rubyspec/core/file/shared/read.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../dir/fixtures/common', __FILE__) + +describe :file_read_directory, shared: true do + platform_is :darwin, :linux, :windows do + it "raises an Errno::EISDIR when passed a path that is a directory" do + lambda { @object.send(@method, ".") }.should raise_error(Errno::EISDIR) + end + end + + platform_is :bsd do + it "does not raises any exception when passed a path that is a directory" do + lambda { @object.send(@method, ".") }.should_not raise_error + end + end +end diff --git a/spec/rubyspec/core/file/shared/stat.rb b/spec/rubyspec/core/file/shared/stat.rb new file mode 100644 index 0000000000..aac710dd2f --- /dev/null +++ b/spec/rubyspec/core/file/shared/stat.rb @@ -0,0 +1,32 @@ +describe :file_stat, shared: true do + before :each do + @file = tmp('i_exist') + touch(@file) + end + + after :each do + rm_r @file + end + + it "returns a File::Stat object if the given file exists" do + st = File.send(@method, @file) + st.should be_an_instance_of(File::Stat) + end + + it "returns a File::Stat object when called on an instance of File" do + File.open(@file) do |f| + st = f.send(@method) + st.should be_an_instance_of(File::Stat) + end + end + + it "accepts an object that has a #to_path method" do + File.send(@method, mock_to_path(@file)) + end + + it "raises an Errno::ENOENT if the file does not exist" do + lambda { + File.send(@method, "fake_file") + }.should raise_error(Errno::ENOENT) + end +end diff --git a/spec/rubyspec/core/file/shared/unlink.rb b/spec/rubyspec/core/file/shared/unlink.rb new file mode 100644 index 0000000000..7b0413b76b --- /dev/null +++ b/spec/rubyspec/core/file/shared/unlink.rb @@ -0,0 +1,63 @@ +describe :file_unlink, shared: true do + before :each do + @file1 = tmp('test.txt') + @file2 = tmp('test2.txt') + + touch @file1 + touch @file2 + end + + after :each do + File.send(@method, @file1) if File.exist?(@file1) + File.send(@method, @file2) if File.exist?(@file2) + + @file1 = nil + @file2 = nil + end + + it "returns 0 when called without arguments" do + File.send(@method).should == 0 + end + + it "deletes a single file" do + File.send(@method, @file1).should == 1 + File.exist?(@file1).should == false + end + + it "deletes multiple files" do + File.send(@method, @file1, @file2).should == 2 + File.exist?(@file1).should == false + File.exist?(@file2).should == false + end + + it "raises a TypeError if not passed a String type" do + lambda { File.send(@method, 1) }.should raise_error(TypeError) + end + + it "raises an Errno::ENOENT when the given file doesn't exist" do + lambda { File.send(@method, 'bogus') }.should raise_error(Errno::ENOENT) + end + + it "coerces a given parameter into a string if possible" do + mock = mock("to_str") + mock.should_receive(:to_str).and_return(@file1) + File.send(@method, mock).should == 1 + end + + it "accepts an object that has a #to_path method" do + File.send(@method, mock_to_path(@file1)).should == 1 + end + + ruby_version_is "2.3" do + platform_is :windows do + it "allows deleting an open file with File::SHARE_DELETE" do + path = tmp("share_delete.txt") + File.open(path, mode: File::CREAT | File::WRONLY | File::BINARY | File::SHARE_DELETE) do |f| + File.exist?(path).should be_true + File.send(@method, path) + end + File.exist?(path).should be_false + end + end + end +end diff --git a/spec/rubyspec/core/file/size_spec.rb b/spec/rubyspec/core/file/size_spec.rb new file mode 100644 index 0000000000..73c8192b18 --- /dev/null +++ b/spec/rubyspec/core/file/size_spec.rb @@ -0,0 +1,119 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/size', __FILE__) + +describe "File.size?" do + it_behaves_like :file_size, :size?, File +end + +describe "File.size?" do + it_behaves_like :file_size_to_io, :size?, File +end + +describe "File.size?" do + it_behaves_like :file_size_nil_when_missing, :size?, File +end + +describe "File.size?" do + it_behaves_like :file_size_nil_when_empty, :size?, File +end + +describe "File.size?" do + it_behaves_like :file_size_with_file_argument, :size?, File +end + +describe "File.size" do + it_behaves_like :file_size, :size, File +end + +describe "File.size" do + it_behaves_like :file_size_to_io, :size, File +end + +describe "File.size" do + it_behaves_like :file_size_raise_when_missing, :size, File +end + +describe "File.size" do + it_behaves_like :file_size_0_when_empty, :size, File +end + +describe "File.size" do + it_behaves_like :file_size_with_file_argument, :size, File +end + +describe "File#size" do + + before :each do + @name = tmp('i_exist') + touch(@name) { |f| f.write 'rubinius' } + @file = File.new @name + @file_org = @file + end + + after :each do + @file_org.close unless @file_org.closed? + rm_r @name + end + + it "is an instance method" do + @file.respond_to?(:size).should be_true + end + + it "returns the file's size as a Fixnum" do + @file.size.should be_an_instance_of(Fixnum) + end + + it "returns the file's size in bytes" do + @file.size.should == 8 + end + + platform_is_not :windows do # impossible to remove opened file on Windows + it "returns the cached size of the file if subsequently deleted" do + rm_r @file.path + @file.size.should == 8 + end + end + + it "returns the file's current size even if modified" do + File.open(@file.path,'a') {|f| f.write '!'} + @file.size.should == 9 + end + + it "raises an IOError on a closed file" do + @file.close + lambda { @file.size }.should raise_error(IOError) + end + + platform_is_not :windows do + it "follows symlinks if necessary" do + ln_file = tmp('i_exist_ln') + rm_r ln_file + + begin + File.symlink(@file.path, ln_file).should == 0 + file = File.new(ln_file) + file.size.should == 8 + ensure + file.close if file && !file.closed? + rm_r ln_file + end + end + end +end + +describe "File#size for an empty file" do + before :each do + @name = tmp('empty') + touch(@name) + @file = File.new @name + end + + after :each do + @file.close unless @file.closed? + rm_r @name + end + + it "returns 0" do + @file.size.should == 0 + end +end diff --git a/spec/rubyspec/core/file/socket_spec.rb b/spec/rubyspec/core/file/socket_spec.rb new file mode 100644 index 0000000000..80f33f4b19 --- /dev/null +++ b/spec/rubyspec/core/file/socket_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/socket', __FILE__) +require 'socket' + +describe "File.socket?" do + it_behaves_like :file_socket, :socket?, File +end + +describe "File.socket?" do + it "returns false if file does not exist" do + File.socket?("I_am_a_bogus_file").should == false + end + + it "returns false if the file is not a socket" do + filename = tmp("i_exist") + touch(filename) + + File.socket?(filename).should == false + + rm_r filename + end +end + +platform_is_not :windows do + describe "File.socket?" do + before :each do + # We need a really short name here. + # On Linux the path length is limited to 107, see unix(7). + @name = tmp("s") + @server = UNIXServer.new @name + end + + after :each do + @server.close + rm_r @name + end + + it "returns true if the file is a socket" do + File.socket?(@name).should == true + end + end +end diff --git a/spec/rubyspec/core/file/split_spec.rb b/spec/rubyspec/core/file/split_spec.rb new file mode 100644 index 0000000000..2479d4b949 --- /dev/null +++ b/spec/rubyspec/core/file/split_spec.rb @@ -0,0 +1,63 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.split" do + before :each do + @backslash_ext = "C:\\foo\\bar\\baz.rb" + @backslash = "C:\\foo\\bar\\baz" + end + + it "splits the string at the last '/' when the last component does not have an extension" do + File.split("/foo/bar/baz").should == ["/foo/bar", "baz"] + File.split("C:/foo/bar/baz").should == ["C:/foo/bar", "baz"] + end + + it "splits the string at the last '/' when the last component has an extension" do + File.split("/foo/bar/baz.rb").should == ["/foo/bar", "baz.rb"] + File.split("C:/foo/bar/baz.rb").should == ["C:/foo/bar", "baz.rb"] + end + + it "splits an empty string into a '.' and an empty string" do + File.split("").should == [".", ""] + end + + platform_is_not :windows do + it "collapses multiple '/' characters and strips trailing ones" do + File.split("//foo////").should == ["/", "foo"] + end + end + + platform_is_not :windows do + it "does not split a string that contains '\\'" do + File.split(@backslash).should == [".", "C:\\foo\\bar\\baz"] + File.split(@backslash_ext).should == [".", "C:\\foo\\bar\\baz.rb"] + end + end + + platform_is :windows do + it "splits the string at the last '\\' when the last component does not have an extension" do + File.split(@backslash).should == ["C:\\foo\\bar", "baz"] + end + + it "splits the string at the last '\\' when the last component has an extension" do + File.split(@backslash_ext).should == ["C:\\foo\\bar", "baz.rb"] + end + end + + it "raises an ArgumentError when not passed a single argument" do + lambda { File.split }.should raise_error(ArgumentError) + lambda { File.split('string', 'another string') }.should raise_error(ArgumentError) + end + + it "raises a TypeError if the argument is not a String type" do + lambda { File.split(1) }.should raise_error(TypeError) + end + + it "coerces the argument with to_str if it is not a String type" do + class C; def to_str; "/rubinius/better/than/ruby"; end; end + File.split(C.new).should == ["/rubinius/better/than", "ruby"] + end + + it "accepts an object that has a #to_path method" do + File.split(mock_to_path("")).should == [".", ""] + end +end diff --git a/spec/rubyspec/core/file/stat/atime_spec.rb b/spec/rubyspec/core/file/stat/atime_spec.rb new file mode 100644 index 0000000000..575c98ce44 --- /dev/null +++ b/spec/rubyspec/core/file/stat/atime_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#atime" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + it "returns the atime of a File::Stat object" do + st = File.stat(@file) + st.atime.should be_kind_of(Time) + st.atime.should <= Time.now + end +end diff --git a/spec/rubyspec/core/file/stat/birthtime_spec.rb b/spec/rubyspec/core/file/stat/birthtime_spec.rb new file mode 100644 index 0000000000..c2ccc319f1 --- /dev/null +++ b/spec/rubyspec/core/file/stat/birthtime_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#birthtime" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + platform_is :windows, :darwin, :freebsd, :netbsd do + it "returns the birthtime of a File::Stat object" do + st = File.stat(@file) + st.birthtime.should be_kind_of(Time) + st.birthtime.should <= Time.now + end + end + + platform_is :linux, :openbsd do + it "raises an NotImplementedError" do + st = File.stat(@file) + lambda { st.birthtime }.should raise_error(NotImplementedError) + end + end +end diff --git a/spec/rubyspec/core/file/stat/blksize_spec.rb b/spec/rubyspec/core/file/stat/blksize_spec.rb new file mode 100644 index 0000000000..4399e6b4bb --- /dev/null +++ b/spec/rubyspec/core/file/stat/blksize_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#blksize" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + platform_is_not :windows do + it "returns the blksize of a File::Stat object" do + st = File.stat(@file) + st.blksize.is_a?(Integer).should == true + st.blksize.should > 0 + end + end + + platform_is :windows do + it "returns nil" do + st = File.stat(@file) + st.blksize.should == nil + end + end +end diff --git a/spec/rubyspec/core/file/stat/blockdev_spec.rb b/spec/rubyspec/core/file/stat/blockdev_spec.rb new file mode 100644 index 0000000000..440291f130 --- /dev/null +++ b/spec/rubyspec/core/file/stat/blockdev_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/blockdev', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#blockdev?" do + it_behaves_like :file_blockdev, :blockdev?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/blocks_spec.rb b/spec/rubyspec/core/file/stat/blocks_spec.rb new file mode 100644 index 0000000000..ca0fd2c8a6 --- /dev/null +++ b/spec/rubyspec/core/file/stat/blocks_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#blocks" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + platform_is_not :windows do + it "returns the blocks of a File::Stat object" do + st = File.stat(@file) + st.blocks.is_a?(Integer).should == true + st.blocks.should > 0 + end + end + + platform_is :windows do + it "returns nil" do + st = File.stat(@file) + st.blocks.should be_nil + end + end +end diff --git a/spec/rubyspec/core/file/stat/chardev_spec.rb b/spec/rubyspec/core/file/stat/chardev_spec.rb new file mode 100644 index 0000000000..25c8c877f7 --- /dev/null +++ b/spec/rubyspec/core/file/stat/chardev_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/chardev', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#chardev?" do + it_behaves_like :file_chardev, :chardev?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/comparison_spec.rb b/spec/rubyspec/core/file/stat/comparison_spec.rb new file mode 100644 index 0000000000..a70a083ab2 --- /dev/null +++ b/spec/rubyspec/core/file/stat/comparison_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#<=>" do + before :each do + @name1 = tmp("i_exist") + @name2 = tmp("i_exist_too") + touch @name1 + touch @name2 + end + + after :each do + rm_r @name1, @name2 + end + + it "is able to compare files by the same modification times" do + now = Time.now - 1 # 1 second ago to avoid NFS cache issue + File.utime(now, now, @name1) + File.utime(now, now, @name2) + + File.open(@name1) { |file1| + File.open(@name2) { |file2| + (file1.stat <=> file2.stat).should == 0 + } + } + end + + it "is able to compare files by different modification times" do + now = Time.now + File.utime(now, now + 100, @name2) + + File.open(@name1) { |file1| + File.open(@name2) { |file2| + (file1.stat <=> file2.stat).should == -1 + } + } + + File.utime(now, now - 100, @name2) + + File.open(@name1) { |file1| + File.open(@name2) { |file2| + (file1.stat <=> file2.stat).should == 1 + } + } + end + + # TODO: Fix + it "includes Comparable and #== shows mtime equality between two File::Stat objects" do + File.open(@name1) { |file1| + File.open(@name2) { |file2| + (file1.stat == file1.stat).should == true + (file2.stat == file2.stat).should == true + } + } + + now = Time.now + File.utime(now, now + 100, @name2) + + File.open(@name1) { |file1| + File.open(@name2) { |file2| + (file1.stat == file2.stat).should == false + (file1.stat == file1.stat).should == true + (file2.stat == file2.stat).should == true + } + } + end +end diff --git a/spec/rubyspec/core/file/stat/ctime_spec.rb b/spec/rubyspec/core/file/stat/ctime_spec.rb new file mode 100644 index 0000000000..2f82dfdab6 --- /dev/null +++ b/spec/rubyspec/core/file/stat/ctime_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#ctime" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + it "returns the ctime of a File::Stat object" do + st = File.stat(@file) + st.ctime.should be_kind_of(Time) + st.ctime.should <= Time.now + end +end diff --git a/spec/rubyspec/core/file/stat/dev_major_spec.rb b/spec/rubyspec/core/file/stat/dev_major_spec.rb new file mode 100644 index 0000000000..0b00fc4d36 --- /dev/null +++ b/spec/rubyspec/core/file/stat/dev_major_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#dev_major" do + before :each do + @name = tmp("file.txt") + touch(@name) + end + after :each do + rm_r @name + end + + platform_is_not :windows do + it "returns the major part of File::Stat#dev" do + File.stat(@name).dev_major.should be_kind_of(Integer) + end + end + + platform_is :windows do + it "returns nil" do + File.stat(@name).dev_major.should be_nil + end + end +end diff --git a/spec/rubyspec/core/file/stat/dev_minor_spec.rb b/spec/rubyspec/core/file/stat/dev_minor_spec.rb new file mode 100644 index 0000000000..0475e3be81 --- /dev/null +++ b/spec/rubyspec/core/file/stat/dev_minor_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#dev_minor" do + before :each do + @name = tmp("file.txt") + touch(@name) + end + after :each do + rm_r @name + end + + platform_is_not :windows do + it "returns the minor part of File::Stat#dev" do + File.stat(@name).dev_minor.should be_kind_of(Integer) + end + end + + platform_is :windows do + it "returns nil" do + File.stat(@name).dev_minor.should be_nil + end + end +end diff --git a/spec/rubyspec/core/file/stat/dev_spec.rb b/spec/rubyspec/core/file/stat/dev_spec.rb new file mode 100644 index 0000000000..3cdc704fd7 --- /dev/null +++ b/spec/rubyspec/core/file/stat/dev_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#dev" do + before :each do + @name = tmp("file.txt") + touch(@name) + end + after :each do + rm_r @name + end + + it "returns the number of the device on which the file exists" do + File.stat(@name).dev.should be_kind_of(Integer) + end +end diff --git a/spec/rubyspec/core/file/stat/directory_spec.rb b/spec/rubyspec/core/file/stat/directory_spec.rb new file mode 100644 index 0000000000..5ead2dca49 --- /dev/null +++ b/spec/rubyspec/core/file/stat/directory_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/directory', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#directory?" do + it_behaves_like :file_directory, :directory?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/executable_real_spec.rb b/spec/rubyspec/core/file/stat/executable_real_spec.rb new file mode 100644 index 0000000000..11de0a5b39 --- /dev/null +++ b/spec/rubyspec/core/file/stat/executable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/executable_real', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#executable_real?" do + it_behaves_like :file_executable_real, :executable_real?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/executable_spec.rb b/spec/rubyspec/core/file/stat/executable_spec.rb new file mode 100644 index 0000000000..e3b1093056 --- /dev/null +++ b/spec/rubyspec/core/file/stat/executable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/executable', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#executable?" do + it_behaves_like :file_executable, :executable?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/file_spec.rb b/spec/rubyspec/core/file/stat/file_spec.rb new file mode 100644 index 0000000000..da79dddb00 --- /dev/null +++ b/spec/rubyspec/core/file/stat/file_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/file', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#file?" do + it_behaves_like :file_file, :file?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/fixtures/classes.rb b/spec/rubyspec/core/file/stat/fixtures/classes.rb new file mode 100644 index 0000000000..4fe9a2a30f --- /dev/null +++ b/spec/rubyspec/core/file/stat/fixtures/classes.rb @@ -0,0 +1,5 @@ +class FileStat + def self.method_missing(meth, file) + File.lstat(file).send(meth) + end +end diff --git a/spec/rubyspec/core/file/stat/ftype_spec.rb b/spec/rubyspec/core/file/stat/ftype_spec.rb new file mode 100644 index 0000000000..c20109765b --- /dev/null +++ b/spec/rubyspec/core/file/stat/ftype_spec.rb @@ -0,0 +1,65 @@ +require "#{File.dirname(__FILE__)}/../../../spec_helper" +require "#{File.dirname(__FILE__)}/../fixtures/file_types.rb" + +describe "File::Stat#ftype" do + it "returns a String" do + FileSpecs.normal_file do |file| + File.lstat(file).ftype.should be_kind_of(String) + end + end + + it "returns 'file' when the file is a file" do + FileSpecs.normal_file do |file| + File.lstat(file).ftype.should == 'file' + end + end + + it "returns 'directory' when the file is a dir" do + FileSpecs.directory do |dir| + File.lstat(dir).ftype.should == 'directory' + end + end + + platform_is_not :windows do + it "returns 'characterSpecial' when the file is a char" do + FileSpecs.character_device do |char| + File.lstat(char).ftype.should == 'characterSpecial' + end + end + end + + platform_is_not :freebsd do # FreeBSD does not have block devices + with_block_device do + it "returns 'blockSpecial' when the file is a block" do + FileSpecs.block_device do |block| + File.lstat(block).ftype.should == 'blockSpecial' + end + end + end + end + + platform_is_not :windows do + it "returns 'link' when the file is a link" do + FileSpecs.symlink do |link| + File.lstat(link).ftype.should == 'link' + end + end + + it "returns fifo when the file is a fifo" do + FileSpecs.fifo do |fifo| + File.lstat(fifo).ftype.should == 'fifo' + end + end + + # This will silently not execute the block if no socket + # can be found. However, if you are running X, there is + # a good chance that if nothing else, at least the X + # Server socket exists. + it "returns 'socket' when the file is a socket" do + FileSpecs.socket do |socket| + File.lstat(socket).ftype.should == 'socket' + end + end + end +end + diff --git a/spec/rubyspec/core/file/stat/gid_spec.rb b/spec/rubyspec/core/file/stat/gid_spec.rb new file mode 100644 index 0000000000..27356b6401 --- /dev/null +++ b/spec/rubyspec/core/file/stat/gid_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#gid" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + File.chown(nil, Process.gid, @file) + end + + after :each do + rm_r @file + end + + it "returns the group owner attribute of a File::Stat object" do + st = File.stat(@file) + st.gid.is_a?(Integer).should == true + st.gid.should == Process.gid + end +end diff --git a/spec/rubyspec/core/file/stat/grpowned_spec.rb b/spec/rubyspec/core/file/stat/grpowned_spec.rb new file mode 100644 index 0000000000..07a52876d0 --- /dev/null +++ b/spec/rubyspec/core/file/stat/grpowned_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/grpowned', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#grpowned?" do + it_behaves_like :file_grpowned, :grpowned?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/ino_spec.rb b/spec/rubyspec/core/file/stat/ino_spec.rb new file mode 100644 index 0000000000..0339dee54f --- /dev/null +++ b/spec/rubyspec/core/file/stat/ino_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#ino" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + platform_is_not :windows do + it "returns the ino of a File::Stat object" do + st = File.stat(@file) + st.ino.should be_kind_of(Integer) + st.ino.should > 0 + end + end + + platform_is :windows do + ruby_version_is ""..."2.3" do + it "returns 0" do + st = File.stat(@file) + st.ino.should be_kind_of(Integer) + st.ino.should == 0 + end + end + + ruby_version_is "2.3" do + it "returns BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low of a File::Stat object" do + st = File.stat(@file) + st.ino.should be_kind_of(Integer) + st.ino.should > 0 + end + end + end +end diff --git a/spec/rubyspec/core/file/stat/inspect_spec.rb b/spec/rubyspec/core/file/stat/inspect_spec.rb new file mode 100644 index 0000000000..dd2ad21da3 --- /dev/null +++ b/spec/rubyspec/core/file/stat/inspect_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#inspect" do + + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + it "produces a nicely formatted description of a File::Stat object" do + st = File.stat(@file) + expected = "#" + st.inspect.should == expected + end +end diff --git a/spec/rubyspec/core/file/stat/mode_spec.rb b/spec/rubyspec/core/file/stat/mode_spec.rb new file mode 100644 index 0000000000..1c895bf0ce --- /dev/null +++ b/spec/rubyspec/core/file/stat/mode_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#mode" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + File.chmod(0644, @file) + end + + after :each do + rm_r @file + end + + it "returns the mode of a File::Stat object" do + st = File.stat(@file) + st.mode.is_a?(Integer).should == true + (st.mode & 0777).should == 0644 + end +end diff --git a/spec/rubyspec/core/file/stat/mtime_spec.rb b/spec/rubyspec/core/file/stat/mtime_spec.rb new file mode 100644 index 0000000000..9dd20dfd65 --- /dev/null +++ b/spec/rubyspec/core/file/stat/mtime_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#mtime" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + it "returns the mtime of a File::Stat object" do + st = File.stat(@file) + st.mtime.should be_kind_of(Time) + st.mtime.should <= Time.now + end +end diff --git a/spec/rubyspec/core/file/stat/new_spec.rb b/spec/rubyspec/core/file/stat/new_spec.rb new file mode 100644 index 0000000000..ec7d81362f --- /dev/null +++ b/spec/rubyspec/core/file/stat/new_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#initialize" do + + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + File.chmod(0755, @file) + end + + after :each do + rm_r @file + end + + it "raises an exception if the file doesn't exist" do + lambda { File::Stat.new(tmp("i_am_a_dummy_file_that_doesnt_exist")) }.should raise_error + end + + it "creates a File::Stat object for the given file" do + st = File::Stat.new(@file) + st.should be_kind_of(File::Stat) + st.ftype.should == 'file' + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return @file + File::Stat.new p + end +end diff --git a/spec/rubyspec/core/file/stat/nlink_spec.rb b/spec/rubyspec/core/file/stat/nlink_spec.rb new file mode 100644 index 0000000000..e857b07fd1 --- /dev/null +++ b/spec/rubyspec/core/file/stat/nlink_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#nlink" do + before :each do + @file = tmp("stat_nlink") + @link = @file + ".lnk" + touch @file + end + + after :each do + rm_r @link, @file + end + + platform_is_not :windows do + it "returns the number of links to a file" do + File::Stat.new(@file).nlink.should == 1 + File.link(@file, @link) + File::Stat.new(@file).nlink.should == 2 + end + end +end diff --git a/spec/rubyspec/core/file/stat/owned_spec.rb b/spec/rubyspec/core/file/stat/owned_spec.rb new file mode 100644 index 0000000000..4c4d843bbe --- /dev/null +++ b/spec/rubyspec/core/file/stat/owned_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/owned', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#owned?" do + it_behaves_like :file_owned, :owned?, FileStat +end + +describe "File::Stat#owned?" do + before :each do + @file = tmp("i_exist") + touch(@file) + end + + after :each do + rm_r @file + end + + it "returns true if the file is owned by the user" do + st = File.stat(@file) + st.owned?.should == true + end + + platform_is_not :windows do + it "returns false if the file is not owned by the user" do + system_file = '/etc/passwd' + st = File.stat(system_file) + st.owned?.should == false + end + end +end diff --git a/spec/rubyspec/core/file/stat/pipe_spec.rb b/spec/rubyspec/core/file/stat/pipe_spec.rb new file mode 100644 index 0000000000..e4c0b559bb --- /dev/null +++ b/spec/rubyspec/core/file/stat/pipe_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/pipe', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#pipe?" do + it_behaves_like :file_pipe, :pipe?, FileStat +end + +describe "File::Stat#pipe?" do + it "returns false if the file is not a pipe" do + filename = tmp("i_exist") + touch(filename) + + st = File.stat(filename) + st.pipe?.should == false + + rm_r filename + end + + platform_is_not :windows do + it "returns true if the file is a pipe" do + filename = tmp("i_am_a_pipe") + system "mkfifo #{filename}" + + st = File.stat(filename) + st.pipe?.should == true + + rm_r filename + end + end + +end diff --git a/spec/rubyspec/core/file/stat/rdev_major_spec.rb b/spec/rubyspec/core/file/stat/rdev_major_spec.rb new file mode 100644 index 0000000000..f9d514fbc0 --- /dev/null +++ b/spec/rubyspec/core/file/stat/rdev_major_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#rdev_major" do + before :each do + platform_is :solaris do + @name = "/dev/zfs" + end + platform_is_not :solaris do + @name = tmp("file.txt") + touch(@name) + end + end + + after :each do + platform_is_not :solaris do + rm_r @name + end + end + + platform_is_not :windows do + it "returns the major part of File::Stat#rdev" do + File.stat(@name).rdev_major.should be_kind_of(Integer) + end + end + + platform_is :windows do + it "returns nil" do + File.stat(@name).rdev_major.should be_nil + end + end +end diff --git a/spec/rubyspec/core/file/stat/rdev_minor_spec.rb b/spec/rubyspec/core/file/stat/rdev_minor_spec.rb new file mode 100644 index 0000000000..67399c5e68 --- /dev/null +++ b/spec/rubyspec/core/file/stat/rdev_minor_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#rdev_minor" do + before :each do + platform_is :solaris do + @name = "/dev/zfs" + end + platform_is_not :solaris do + @name = tmp("file.txt") + touch(@name) + end + end + + after :each do + platform_is_not :solaris do + rm_r @name + end + end + + platform_is_not :windows do + it "returns the minor part of File::Stat#rdev" do + File.stat(@name).rdev_minor.should be_kind_of(Integer) + end + end + + platform_is :windows do + it "returns nil" do + File.stat(@name).rdev_minor.should be_nil + end + end +end diff --git a/spec/rubyspec/core/file/stat/rdev_spec.rb b/spec/rubyspec/core/file/stat/rdev_spec.rb new file mode 100644 index 0000000000..12f97fb044 --- /dev/null +++ b/spec/rubyspec/core/file/stat/rdev_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#rdev" do + before :each do + @name = tmp("file.txt") + touch(@name) + end + after :each do + rm_r @name + end + + it "returns the number of the device this file represents which the file exists" do + File.stat(@name).rdev.should be_kind_of(Integer) + end +end diff --git a/spec/rubyspec/core/file/stat/readable_real_spec.rb b/spec/rubyspec/core/file/stat/readable_real_spec.rb new file mode 100644 index 0000000000..49412f1df2 --- /dev/null +++ b/spec/rubyspec/core/file/stat/readable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/readable_real', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#readable_real?" do + it_behaves_like :file_readable_real, :readable_real?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/readable_spec.rb b/spec/rubyspec/core/file/stat/readable_spec.rb new file mode 100644 index 0000000000..3d81975309 --- /dev/null +++ b/spec/rubyspec/core/file/stat/readable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/readable', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#readable?" do + it_behaves_like :file_readable, :readable?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/setgid_spec.rb b/spec/rubyspec/core/file/stat/setgid_spec.rb new file mode 100644 index 0000000000..318a72b437 --- /dev/null +++ b/spec/rubyspec/core/file/stat/setgid_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/setgid', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#setgid?" do + it_behaves_like :file_setgid, :setgid?, FileStat +end + +describe "File::Stat#setgid?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/stat/setuid_spec.rb b/spec/rubyspec/core/file/stat/setuid_spec.rb new file mode 100644 index 0000000000..5057af0ccc --- /dev/null +++ b/spec/rubyspec/core/file/stat/setuid_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/setuid', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#setuid?" do + it_behaves_like :file_setuid, :setuid?, FileStat +end + +describe "File::Stat#setuid?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/stat/size_spec.rb b/spec/rubyspec/core/file/stat/size_spec.rb new file mode 100644 index 0000000000..84db12d591 --- /dev/null +++ b/spec/rubyspec/core/file/stat/size_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/size', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat.size?" do + it_behaves_like :file_size, :size?, FileStat + it_behaves_like :file_size_nil_when_empty, :size?, FileStat +end + +describe "File::Stat.size" do + it_behaves_like :file_size, :size, FileStat + it_behaves_like :file_size_0_when_empty, :size, FileStat +end + +describe "File::Stat#size" do + it "needs to be reviewed for spec completeness" +end + +describe "File::Stat#size?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/stat/socket_spec.rb b/spec/rubyspec/core/file/stat/socket_spec.rb new file mode 100644 index 0000000000..b25d9314f9 --- /dev/null +++ b/spec/rubyspec/core/file/stat/socket_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/socket', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#socket?" do + it_behaves_like :file_socket, :socket?, FileStat +end + +describe "File::Stat#socket?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/stat/sticky_spec.rb b/spec/rubyspec/core/file/stat/sticky_spec.rb new file mode 100644 index 0000000000..c2fefbe106 --- /dev/null +++ b/spec/rubyspec/core/file/stat/sticky_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/sticky', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#sticky?" do + it_behaves_like :file_sticky, :sticky?, FileStat +end + +describe "File::Stat#sticky?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/stat/symlink_spec.rb b/spec/rubyspec/core/file/stat/symlink_spec.rb new file mode 100644 index 0000000000..579c1de0ad --- /dev/null +++ b/spec/rubyspec/core/file/stat/symlink_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/symlink', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#symlink?" do + it_behaves_like :file_symlink, :symlink?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/uid_spec.rb b/spec/rubyspec/core/file/stat/uid_spec.rb new file mode 100644 index 0000000000..75be97c234 --- /dev/null +++ b/spec/rubyspec/core/file/stat/uid_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "File::Stat#uid" do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @file + end + + it "returns the owner attribute of a File::Stat object" do + st = File.stat(@file) + st.uid.is_a?(Integer).should == true + st.uid.should == Process.uid + end +end diff --git a/spec/rubyspec/core/file/stat/world_readable_spec.rb b/spec/rubyspec/core/file/stat/world_readable_spec.rb new file mode 100644 index 0000000000..178e39a1ea --- /dev/null +++ b/spec/rubyspec/core/file/stat/world_readable_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/world_readable', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat.world_readable?" do + it_behaves_like(:file_world_readable, :world_readable?, FileStat) +end + +describe "File::Stat#world_readable?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/stat/world_writable_spec.rb b/spec/rubyspec/core/file/stat/world_writable_spec.rb new file mode 100644 index 0000000000..73a7c6d3ed --- /dev/null +++ b/spec/rubyspec/core/file/stat/world_writable_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/world_writable', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat.world_writable?" do + it_behaves_like(:file_world_writable, :world_writable?, FileStat) +end + +describe "File::Stat#world_writable?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/file/stat/writable_real_spec.rb b/spec/rubyspec/core/file/stat/writable_real_spec.rb new file mode 100644 index 0000000000..e069db507b --- /dev/null +++ b/spec/rubyspec/core/file/stat/writable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/writable_real', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#writable_real?" do + it_behaves_like :file_writable_real, :writable_real?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/writable_spec.rb b/spec/rubyspec/core/file/stat/writable_spec.rb new file mode 100644 index 0000000000..b720e59f81 --- /dev/null +++ b/spec/rubyspec/core/file/stat/writable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/writable', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#writable?" do + it_behaves_like :file_writable, :writable?, FileStat +end diff --git a/spec/rubyspec/core/file/stat/zero_spec.rb b/spec/rubyspec/core/file/stat/zero_spec.rb new file mode 100644 index 0000000000..127c706b90 --- /dev/null +++ b/spec/rubyspec/core/file/stat/zero_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../shared/file/zero', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "File::Stat#zero?" do + it_behaves_like :file_zero, :zero?, FileStat +end diff --git a/spec/rubyspec/core/file/stat_spec.rb b/spec/rubyspec/core/file/stat_spec.rb new file mode 100644 index 0000000000..1ea003142e --- /dev/null +++ b/spec/rubyspec/core/file/stat_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/stat', __FILE__) + +describe "File.stat" do + it_behaves_like :file_stat, :stat +end + +platform_is_not :windows do + describe "File.stat" do + before :each do + @file = tmp('i_exist') + @link = tmp('i_am_a_symlink') + touch(@file) { |f| f.write "rubinius" } + end + + after :each do + rm_r @link, @file + end + + it "returns information for a file that has been deleted but is still open" do + File.open(@file) do |f| + rm_r @file + + st = f.stat + + st.file?.should == true + st.zero?.should == false + st.size.should == 8 + st.size?.should == 8 + st.blksize.should >= 0 + st.atime.should be_kind_of(Time) + st.ctime.should be_kind_of(Time) + st.mtime.should be_kind_of(Time) + end + end + + it "returns a File::Stat object with file properties for a symlink" do + File.symlink(@file, @link) + st = File.stat(@link) + + st.file?.should == true + st.symlink?.should == false + end + end +end diff --git a/spec/rubyspec/core/file/sticky_spec.rb b/spec/rubyspec/core/file/sticky_spec.rb new file mode 100644 index 0000000000..d01e2b6818 --- /dev/null +++ b/spec/rubyspec/core/file/sticky_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/sticky', __FILE__) + +describe "File.sticky?" do + it_behaves_like :file_sticky, :sticky?, File + it_behaves_like :file_sticky_missing, :sticky?, File +end + +describe "File.sticky?" do + platform_is_not :windows do + it "returns false if file does not exist" do + File.sticky?("I_am_a_bogus_file").should == false + end + + it "returns false if the file has not sticky bit set" do + filename = tmp("i_exist") + touch(filename) + + File.sticky?(filename).should == false + + rm_r filename + end + end + + platform_is :linux, :darwin do + it "returns true if the file has sticky bit set" do + filename = tmp("i_exist") + touch(filename) + system "chmod +t #{filename}" + + File.sticky?(filename).should == true + + rm_r filename + end + end + + platform_is :bsd do + # FreeBSD and NetBSD can't set stiky bit to a normal file + it "cannot set sticky bit to a normal file" do + filename = tmp("i_exist") + touch(filename) + stat = File.stat(filename) + mode = stat.mode + raise_error(Errno::EFTYPE){File.chmod(mode|01000, filename)} + File.sticky?(filename).should == false + + rm_r filename + end + end +end diff --git a/spec/rubyspec/core/file/symlink_spec.rb b/spec/rubyspec/core/file/symlink_spec.rb new file mode 100644 index 0000000000..2426b8c9a7 --- /dev/null +++ b/spec/rubyspec/core/file/symlink_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/symlink', __FILE__) + +describe "File.symlink" do + before :each do + @file = tmp("file_symlink.txt") + @link = tmp("file_symlink.lnk") + + rm_r @link + touch @file + end + + after :each do + rm_r @link, @file + end + + platform_is_not :windows do + it "creates a symlink between a source and target file" do + File.symlink(@file, @link).should == 0 + File.identical?(@file, @link).should == true + end + + it "creates a symbolic link" do + File.symlink(@file, @link) + File.symlink?(@link).should == true + end + + it "accepts args that have #to_path methods" do + File.symlink(mock_to_path(@file), mock_to_path(@link)) + File.symlink?(@link).should == true + end + + it "raises an Errno::EEXIST if the target already exists" do + File.symlink(@file, @link) + lambda { File.symlink(@file, @link) }.should raise_error(Errno::EEXIST) + end + + it "raises an ArgumentError if not called with two arguments" do + lambda { File.symlink }.should raise_error(ArgumentError) + lambda { File.symlink(@file) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not called with String types" do + lambda { File.symlink(@file, nil) }.should raise_error(TypeError) + lambda { File.symlink(@file, 1) }.should raise_error(TypeError) + lambda { File.symlink(1, 1) }.should raise_error(TypeError) + end + end +end + +describe "File.symlink?" do + it_behaves_like :file_symlink, :symlink?, File +end + +describe "File.symlink?" do + it_behaves_like :file_symlink_nonexistent, :symlink?, File +end diff --git a/spec/rubyspec/core/file/to_path_spec.rb b/spec/rubyspec/core/file/to_path_spec.rb new file mode 100644 index 0000000000..3dc801cc27 --- /dev/null +++ b/spec/rubyspec/core/file/to_path_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File#to_path" do + before :each do + @name = "file_to_path" + @path = tmp(@name) + touch @path + end + + after :each do + @file.close if @file and !@file.closed? + rm_r @path + end + + it "returns a String" do + @file = File.new @path + @file.to_path.should be_an_instance_of(String) + end + + it "does not normalise the path it returns" do + Dir.chdir(tmp("")) do + unorm = "./#{@name}" + @file = File.new unorm + @file.to_path.should == unorm + end + end + + it "does not canonicalize the path it returns" do + dir = File.basename tmp("") + path = "#{tmp("")}../#{dir}/#{@name}" + @file = File.new path + @file.to_path.should == path + end + + it "does not absolute-ise the path it returns" do + Dir.chdir(tmp("")) do + @file = File.new @name + @file.to_path.should == @name + end + end + + with_feature :encoding do + it "preserves the encoding of the path" do + path = @path.force_encoding("euc-jp") + @file = File.new path + @file.to_path.encoding.should == Encoding.find("euc-jp") + end + end +end diff --git a/spec/rubyspec/core/file/truncate_spec.rb b/spec/rubyspec/core/file/truncate_spec.rb new file mode 100644 index 0000000000..a120c610b8 --- /dev/null +++ b/spec/rubyspec/core/file/truncate_spec.rb @@ -0,0 +1,177 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.truncate" do + before :each do + @name = tmp("test.txt") + touch(@name) { |f| f.write("1234567890") } + end + + after :each do + rm_r @name + end + + it "truncates a file" do + File.size(@name).should == 10 + + File.truncate(@name, 5) + File.size(@name).should == 5 + + File.open(@name, "r") do |f| + f.read(99).should == "12345" + f.eof?.should == true + end + end + + it "truncate a file size to 0" do + File.truncate(@name, 0).should == 0 + IO.read(@name).should == "" + end + + it "truncate a file size to 5" do + File.size(@name).should == 10 + File.truncate(@name, 5) + File.size(@name).should == 5 + IO.read(@name).should == "12345" + end + + it "truncates to a larger file size than the original file" do + File.truncate(@name, 12) + File.size(@name).should == 12 + IO.read(@name).should == "1234567890\000\000" + end + + it "truncates to the same size as the original file" do + File.truncate(@name, File.size(@name)) + File.size(@name).should == 10 + IO.read(@name).should == "1234567890" + end + + it "raises an Errno::ENOENT if the file does not exist" do + # TODO: missing_file + not_existing_file = tmp("file-does-not-exist-for-sure.txt") + + # make sure it doesn't exist for real + rm_r not_existing_file + + begin + lambda { File.truncate(not_existing_file, 5) }.should raise_error(Errno::ENOENT) + ensure + rm_r not_existing_file + end + end + + it "raises an ArgumentError if not passed two arguments" do + lambda { File.truncate }.should raise_error(ArgumentError) + lambda { File.truncate(@name) }.should raise_error(ArgumentError) + end + + platform_is_not :netbsd, :openbsd do + it "raises an Errno::EINVAL if the length argument is not valid" do + lambda { File.truncate(@name, -1) }.should raise_error(Errno::EINVAL) # May fail + end + end + + it "raises a TypeError if not passed a String type for the first argument" do + lambda { File.truncate(1, 1) }.should raise_error(TypeError) + end + + it "raises a TypeError if not passed an Integer type for the second argument" do + lambda { File.truncate(@name, nil) }.should raise_error(TypeError) + end + + it "accepts an object that has a #to_path method" do + File.truncate(mock_to_path(@name), 0).should == 0 + end +end + + +describe "File#truncate" do + before :each do + @name = tmp("test.txt") + @file = File.open @name, 'w' + @file.write "1234567890" + @file.flush + end + + after :each do + @file.close unless @file.closed? + rm_r @name + end + + it "does not move the file write pointer to the specified byte offset" do + @file.truncate(3) + @file.write "abc" + @file.close + File.read(@name).should == "123\x00\x00\x00\x00\x00\x00\x00abc" + end + + it "does not move the file read pointer to the specified byte offset" do + File.open(@name, "r+") do |f| + f.read(1).should == "1" + f.truncate(0) + f.read(1).should == nil + end + end + + it "truncates a file" do + File.size(@name).should == 10 + + @file.truncate(5) + File.size(@name).should == 5 + File.open(@name, "r") do |f| + f.read(99).should == "12345" + f.eof?.should == true + end + end + + it "truncates a file size to 0" do + @file.truncate(0).should == 0 + IO.read(@name).should == "" + end + + it "truncates a file size to 5" do + File.size(@name).should == 10 + @file.truncate(5) + File.size(@name).should == 5 + IO.read(@name).should == "12345" + end + + it "truncates a file to a larger size than the original file" do + @file.truncate(12) + File.size(@name).should == 12 + IO.read(@name).should == "1234567890\000\000" + end + + it "truncates a file to the same size as the original file" do + @file.truncate(File.size(@name)) + File.size(@name).should == 10 + IO.read(@name).should == "1234567890" + end + + it "raises an ArgumentError if not passed one argument" do + lambda { @file.truncate }.should raise_error(ArgumentError) + lambda { @file.truncate(1) }.should_not raise_error(ArgumentError) + end + + platform_is_not :netbsd do + it "raises an Errno::EINVAL if the length argument is not valid" do + lambda { @file.truncate(-1) }.should raise_error(Errno::EINVAL) # May fail + end + end + + it "raises an IOError if file is closed" do + @file.close + @file.closed?.should == true + lambda { @file.truncate(42) }.should raise_error(IOError) + end + + it "raises an IOError if file is not opened for writing" do + File.open(@name, 'r') do |file| + lambda { file.truncate(42) }.should raise_error(IOError) + end + end + + it "raises a TypeError if not passed an Integer type for the for the argument" do + lambda { @file.truncate(nil) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/file/umask_spec.rb b/spec/rubyspec/core/file/umask_spec.rb new file mode 100644 index 0000000000..2286bf064f --- /dev/null +++ b/spec/rubyspec/core/file/umask_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.umask" do + before :each do + @orig_umask = File.umask + @file = tmp('test.txt') + touch @file + end + + after :each do + rm_r @file + File.umask(@orig_umask) + end + + it "returns a Fixnum" do + File.umask.should be_kind_of(Fixnum) + end + + platform_is_not :windows do + it "returns the current umask value for the process" do + File.umask(022) + File.umask(006).should == 022 + File.umask.should == 006 + end + + it "invokes to_int on non-integer argument" do + (obj = mock(022)).should_receive(:to_int).any_number_of_times.and_return(022) + File.umask(obj) + File.umask(obj).should == 022 + end + end + + it "always succeeds with any integer values" do + vals = [-2**30, -2**16, -2**8, -2, + -1.5, -1, 0.5, 0, 1, 2, 7.77777, 16, 32, 64, 2**8, 2**16, 2**30] + vals.each { |v| + lambda { File.umask(v) }.should_not raise_error + } + end + + it "raises ArgumentError when more than one argument is provided" do + lambda { File.umask(022, 022) }.should raise_error(ArgumentError) + end + + platform_is :windows do + it "returns the current umask value for this process (basic)" do + File.umask.should == 0 + File.umask(022).should == 0 + File.umask(044).should == 0 + end + + # The value used here is the value of _S_IWRITE. + it "returns the current umask value for this process" do + File.umask(0000200) + File.umask.should == 0000200 + File.umask(0006) + File.umask.should == 0 + end + end +end diff --git a/spec/rubyspec/core/file/unlink_spec.rb b/spec/rubyspec/core/file/unlink_spec.rb new file mode 100644 index 0000000000..a1e96aef6a --- /dev/null +++ b/spec/rubyspec/core/file/unlink_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/unlink', __FILE__) + +describe "File.unlink" do + it_behaves_like(:file_unlink, :unlink) +end diff --git a/spec/rubyspec/core/file/utime_spec.rb b/spec/rubyspec/core/file/utime_spec.rb new file mode 100644 index 0000000000..73112420d1 --- /dev/null +++ b/spec/rubyspec/core/file/utime_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "File.utime" do + before :each do + @atime = Time.now + @mtime = Time.now + @file1 = tmp("specs_file_utime1") + @file2 = tmp("specs_file_utime2") + touch @file1 + touch @file2 + end + + after :each do + rm_r @file1, @file2 + end + + it "sets the access and modification time of each file" do + File.utime(@atime, @mtime, @file1, @file2) + File.atime(@file1).to_i.should be_close(@atime.to_i, 2) + File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2) + File.atime(@file2).to_i.should be_close(@atime.to_i, 2) + File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2) + end + + it "uses the current times if two nil values are passed" do + File.utime(nil, nil, @file1, @file2) + File.atime(@file1).to_i.should be_close(Time.now.to_i, 2) + File.mtime(@file1).to_i.should be_close(Time.now.to_i, 2) + File.atime(@file2).to_i.should be_close(Time.now.to_i, 2) + File.mtime(@file2).to_i.should be_close(Time.now.to_i, 2) + end + + it "accepts an object that has a #to_path method" do + File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2)) + end +end diff --git a/spec/rubyspec/core/file/world_readable_spec.rb b/spec/rubyspec/core/file/world_readable_spec.rb new file mode 100644 index 0000000000..a130f0d115 --- /dev/null +++ b/spec/rubyspec/core/file/world_readable_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/world_readable', __FILE__) + +describe "File.world_readable?" do + it_behaves_like(:file_world_readable, :world_readable?, File) + + it "returns nil if the file does not exist" do + file = rand.to_s + $$.to_s + File.exist?(file).should be_false + File.world_readable?(file).should be_nil + end +end diff --git a/spec/rubyspec/core/file/world_writable_spec.rb b/spec/rubyspec/core/file/world_writable_spec.rb new file mode 100644 index 0000000000..5a39643ef9 --- /dev/null +++ b/spec/rubyspec/core/file/world_writable_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/world_writable', __FILE__) + +describe "File.world_writable?" do + it_behaves_like(:file_world_writable, :world_writable?, File) + + it "returns nil if the file does not exist" do + file = rand.to_s + $$.to_s + File.exist?(file).should be_false + File.world_writable?(file).should be_nil + end +end diff --git a/spec/rubyspec/core/file/writable_real_spec.rb b/spec/rubyspec/core/file/writable_real_spec.rb new file mode 100644 index 0000000000..36f576e222 --- /dev/null +++ b/spec/rubyspec/core/file/writable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/writable_real', __FILE__) + +describe "File.writable_real?" do + it_behaves_like :file_writable_real, :writable_real?, File + it_behaves_like :file_writable_real_missing, :writable_real?, File +end diff --git a/spec/rubyspec/core/file/writable_spec.rb b/spec/rubyspec/core/file/writable_spec.rb new file mode 100644 index 0000000000..4f6213ec77 --- /dev/null +++ b/spec/rubyspec/core/file/writable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/writable', __FILE__) + +describe "File.writable?" do + it_behaves_like :file_writable, :writable?, File + it_behaves_like :file_writable_missing, :writable?, File +end diff --git a/spec/rubyspec/core/file/zero_spec.rb b/spec/rubyspec/core/file/zero_spec.rb new file mode 100644 index 0000000000..0fc087faff --- /dev/null +++ b/spec/rubyspec/core/file/zero_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/zero', __FILE__) + +describe "File.zero?" do + it_behaves_like :file_zero, :zero?, File + it_behaves_like :file_zero_missing, :zero?, File + + platform_is :solaris do + it "returns false for /dev/null" do + File.zero?('/dev/null').should == true + end + end +end diff --git a/spec/rubyspec/core/filetest/blockdev_spec.rb b/spec/rubyspec/core/filetest/blockdev_spec.rb new file mode 100644 index 0000000000..13bc98b483 --- /dev/null +++ b/spec/rubyspec/core/filetest/blockdev_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/blockdev', __FILE__) + +describe "FileTest.blockdev?" do + it_behaves_like :file_blockdev, :blockdev?, FileTest +end diff --git a/spec/rubyspec/core/filetest/chardev_spec.rb b/spec/rubyspec/core/filetest/chardev_spec.rb new file mode 100644 index 0000000000..c126c81658 --- /dev/null +++ b/spec/rubyspec/core/filetest/chardev_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/chardev', __FILE__) + +describe "FileTest.chardev?" do + it_behaves_like :file_chardev, :chardev?, FileTest +end diff --git a/spec/rubyspec/core/filetest/directory_spec.rb b/spec/rubyspec/core/filetest/directory_spec.rb new file mode 100644 index 0000000000..e6f1ae51d2 --- /dev/null +++ b/spec/rubyspec/core/filetest/directory_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/directory', __FILE__) + +describe "FileTest.directory?" do + it_behaves_like :file_directory, :directory?, FileTest +end + +describe "FileTest.directory?" do + it_behaves_like :file_directory_io, :directory?, FileTest +end diff --git a/spec/rubyspec/core/filetest/executable_real_spec.rb b/spec/rubyspec/core/filetest/executable_real_spec.rb new file mode 100644 index 0000000000..37511e6c88 --- /dev/null +++ b/spec/rubyspec/core/filetest/executable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/executable_real', __FILE__) + +describe "FileTest.executable_real?" do + it_behaves_like :file_executable_real, :executable_real?, FileTest + it_behaves_like :file_executable_real_missing, :executable_real?, FileTest +end diff --git a/spec/rubyspec/core/filetest/executable_spec.rb b/spec/rubyspec/core/filetest/executable_spec.rb new file mode 100644 index 0000000000..477e989423 --- /dev/null +++ b/spec/rubyspec/core/filetest/executable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/executable', __FILE__) + +describe "FileTest.executable?" do + it_behaves_like :file_executable, :executable?, FileTest + it_behaves_like :file_executable_missing, :executable?, FileTest +end diff --git a/spec/rubyspec/core/filetest/exist_spec.rb b/spec/rubyspec/core/filetest/exist_spec.rb new file mode 100644 index 0000000000..b375d3a4b1 --- /dev/null +++ b/spec/rubyspec/core/filetest/exist_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/exist', __FILE__) + +describe "FileTest.exist?" do + it_behaves_like :file_exist, :exist?, FileTest +end diff --git a/spec/rubyspec/core/filetest/exists_spec.rb b/spec/rubyspec/core/filetest/exists_spec.rb new file mode 100644 index 0000000000..09d4ca1a83 --- /dev/null +++ b/spec/rubyspec/core/filetest/exists_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/exist', __FILE__) + +describe "FileTest.exists?" do + it_behaves_like :file_exist, :exists?, FileTest +end diff --git a/spec/rubyspec/core/filetest/file_spec.rb b/spec/rubyspec/core/filetest/file_spec.rb new file mode 100644 index 0000000000..887dc1da88 --- /dev/null +++ b/spec/rubyspec/core/filetest/file_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/file', __FILE__) + +describe "File.file?" do + it_behaves_like :file_file, :file?, File +end + +describe "FileTest.file?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/grpowned_spec.rb b/spec/rubyspec/core/filetest/grpowned_spec.rb new file mode 100644 index 0000000000..950671aae9 --- /dev/null +++ b/spec/rubyspec/core/filetest/grpowned_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/grpowned', __FILE__) + +describe "FileTest.grpowned?" do + it_behaves_like :file_grpowned, :grpowned?, FileTest + + it "returns false if the file doesn't exist" do + FileTest.grpowned?("xxx-tmp-doesnt_exist-blah").should be_false + end +end diff --git a/spec/rubyspec/core/filetest/identical_spec.rb b/spec/rubyspec/core/filetest/identical_spec.rb new file mode 100644 index 0000000000..cb4bc82873 --- /dev/null +++ b/spec/rubyspec/core/filetest/identical_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/identical', __FILE__) + +describe "FileTest.identical?" do + it_behaves_like :file_identical, :identical?, FileTest +end diff --git a/spec/rubyspec/core/filetest/owned_spec.rb b/spec/rubyspec/core/filetest/owned_spec.rb new file mode 100644 index 0000000000..8483f2af21 --- /dev/null +++ b/spec/rubyspec/core/filetest/owned_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/owned', __FILE__) + +describe "FileTest.owned?" do + it_behaves_like :file_owned, :owned?, FileTest +end + +describe "FileTest.owned?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/pipe_spec.rb b/spec/rubyspec/core/filetest/pipe_spec.rb new file mode 100644 index 0000000000..0147dae820 --- /dev/null +++ b/spec/rubyspec/core/filetest/pipe_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/pipe', __FILE__) + +describe "FileTest.pipe?" do + it_behaves_like :file_pipe, :pipe?, FileTest +end + +describe "FileTest.pipe?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/readable_real_spec.rb b/spec/rubyspec/core/filetest/readable_real_spec.rb new file mode 100644 index 0000000000..62ac972834 --- /dev/null +++ b/spec/rubyspec/core/filetest/readable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/readable_real', __FILE__) + +describe "FileTest.readable_real?" do + it_behaves_like :file_readable_real, :readable_real?, FileTest + it_behaves_like :file_readable_real_missing, :readable_real?, FileTest +end diff --git a/spec/rubyspec/core/filetest/readable_spec.rb b/spec/rubyspec/core/filetest/readable_spec.rb new file mode 100644 index 0000000000..086a9e5819 --- /dev/null +++ b/spec/rubyspec/core/filetest/readable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/readable', __FILE__) + +describe "FileTest.readable?" do + it_behaves_like :file_readable, :readable?, FileTest + it_behaves_like :file_readable_missing, :readable?, FileTest +end diff --git a/spec/rubyspec/core/filetest/setgid_spec.rb b/spec/rubyspec/core/filetest/setgid_spec.rb new file mode 100644 index 0000000000..f6e3f5b979 --- /dev/null +++ b/spec/rubyspec/core/filetest/setgid_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/setgid', __FILE__) + +describe "FileTest.setgid?" do + it_behaves_like :file_setgid, :setgid?, FileTest +end + +describe "FileTest.setgid?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/setuid_spec.rb b/spec/rubyspec/core/filetest/setuid_spec.rb new file mode 100644 index 0000000000..f077ec5b65 --- /dev/null +++ b/spec/rubyspec/core/filetest/setuid_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/setuid', __FILE__) + +describe "FileTest.setuid?" do + it_behaves_like :file_setuid, :setuid?, FileTest +end + +describe "FileTest.setuid?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/size_spec.rb b/spec/rubyspec/core/filetest/size_spec.rb new file mode 100644 index 0000000000..398a8e69b8 --- /dev/null +++ b/spec/rubyspec/core/filetest/size_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/size', __FILE__) + +describe "FileTest.size?" do + it_behaves_like :file_size, :size?, FileTest +end + +describe "FileTest.size?" do + it_behaves_like :file_size_nil_when_missing, :size?, FileTest +end + +describe "FileTest.size?" do + it_behaves_like :file_size_nil_when_empty, :size?, FileTest +end + +describe "FileTest.size?" do + it_behaves_like :file_size_with_file_argument, :size?, FileTest +end + +describe "FileTest.size" do + it_behaves_like :file_size, :size, FileTest +end + +describe "FileTest.size" do + it_behaves_like :file_size_raise_when_missing, :size, FileTest +end + +describe "FileTest.size" do + it_behaves_like :file_size_0_when_empty, :size, FileTest +end + +describe "FileTest.size" do + it_behaves_like :file_size_with_file_argument, :size, FileTest +end diff --git a/spec/rubyspec/core/filetest/socket_spec.rb b/spec/rubyspec/core/filetest/socket_spec.rb new file mode 100644 index 0000000000..debe032ada --- /dev/null +++ b/spec/rubyspec/core/filetest/socket_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/socket', __FILE__) + +describe "FileTest.socket?" do + it_behaves_like :file_socket, :socket?, FileTest +end + +describe "FileTest.socket?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/sticky_spec.rb b/spec/rubyspec/core/filetest/sticky_spec.rb new file mode 100644 index 0000000000..ca38a45c9d --- /dev/null +++ b/spec/rubyspec/core/filetest/sticky_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/sticky', __FILE__) + +describe "FileTest.sticky?" do + it_behaves_like :file_sticky, :sticky?, FileTest + it_behaves_like :file_sticky_missing, :sticky?, FileTest +end diff --git a/spec/rubyspec/core/filetest/symlink_spec.rb b/spec/rubyspec/core/filetest/symlink_spec.rb new file mode 100644 index 0000000000..0812b09f5a --- /dev/null +++ b/spec/rubyspec/core/filetest/symlink_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/symlink', __FILE__) + +describe "FileTest.symlink?" do + it_behaves_like :file_symlink, :symlink?, FileTest +end + +describe "FileTest.symlink?" do + it_behaves_like :file_symlink_nonexistent, :symlink?, File +end diff --git a/spec/rubyspec/core/filetest/world_readable_spec.rb b/spec/rubyspec/core/filetest/world_readable_spec.rb new file mode 100644 index 0000000000..ef84c1ad01 --- /dev/null +++ b/spec/rubyspec/core/filetest/world_readable_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "FileTest.world_readable?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/world_writable_spec.rb b/spec/rubyspec/core/filetest/world_writable_spec.rb new file mode 100644 index 0000000000..2803f3576a --- /dev/null +++ b/spec/rubyspec/core/filetest/world_writable_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "FileTest.world_writable?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/filetest/writable_real_spec.rb b/spec/rubyspec/core/filetest/writable_real_spec.rb new file mode 100644 index 0000000000..83194f8173 --- /dev/null +++ b/spec/rubyspec/core/filetest/writable_real_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/writable_real', __FILE__) + +describe "FileTest.writable_real?" do + it_behaves_like :file_writable_real, :writable_real?, FileTest + it_behaves_like :file_writable_real_missing, :writable_real?, FileTest +end diff --git a/spec/rubyspec/core/filetest/writable_spec.rb b/spec/rubyspec/core/filetest/writable_spec.rb new file mode 100644 index 0000000000..9064f1b16d --- /dev/null +++ b/spec/rubyspec/core/filetest/writable_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/writable', __FILE__) + +describe "FileTest.writable?" do + it_behaves_like :file_writable, :writable?, FileTest + it_behaves_like :file_writable_missing, :writable?, FileTest +end diff --git a/spec/rubyspec/core/filetest/zero_spec.rb b/spec/rubyspec/core/filetest/zero_spec.rb new file mode 100644 index 0000000000..46b1842d55 --- /dev/null +++ b/spec/rubyspec/core/filetest/zero_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/file/zero', __FILE__) + +describe "FileTest.zero?" do + it_behaves_like :file_zero, :zero?, FileTest + it_behaves_like :file_zero_missing, :zero?, FileTest + + platform_is :solaris do + it "returns false for /dev/null" do + File.zero?('/dev/null').should == true + end + end +end diff --git a/spec/rubyspec/core/fixnum/abs_spec.rb b/spec/rubyspec/core/fixnum/abs_spec.rb new file mode 100644 index 0000000000..5e6a7de891 --- /dev/null +++ b/spec/rubyspec/core/fixnum/abs_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/abs', __FILE__) + +describe "Fixnum#abs" do + it_behaves_like :fixnum_abs, :abs +end + diff --git a/spec/rubyspec/core/fixnum/bit_and_spec.rb b/spec/rubyspec/core/fixnum/bit_and_spec.rb new file mode 100644 index 0000000000..17fd8f81f5 --- /dev/null +++ b/spec/rubyspec/core/fixnum/bit_and_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#&" do + it "returns self bitwise AND other" do + (256 & 16).should == 0 + (2010 & 5).should == 0 + (65535 & 1).should == 1 + (0xffff & bignum_value + 0xffff_ffff).should == 65535 + end + + it "returns self bitwise AND a Bignum" do + (-1 & 2**64).should == 18446744073709551616 + end + + it "raises a TypeError when passed a Float" do + lambda { (3 & 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("fixnum bit and") + obj.should_not_receive(:to_int) + + lambda { 3 & obj }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/bit_length_spec.rb b/spec/rubyspec/core/fixnum/bit_length_spec.rb new file mode 100644 index 0000000000..8c9f69b7d7 --- /dev/null +++ b/spec/rubyspec/core/fixnum/bit_length_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#bit_length" do + it "returns the position of the leftmost bit of a positive number" do + 0.bit_length.should == 0 + 1.bit_length.should == 1 + 2.bit_length.should == 2 + 3.bit_length.should == 2 + 4.bit_length.should == 3 + n = fixnum_max.bit_length + fixnum_max[n].should == 0 + fixnum_max[n-1].should == 1 + + 0.bit_length.should == 0 + 1.bit_length.should == 1 + 0xff.bit_length.should == 8 + 0x100.bit_length.should == 9 + (2**12-1).bit_length.should == 12 + (2**12).bit_length.should == 13 + (2**12+1).bit_length.should == 13 + end + + it "returns the position of the leftmost 0 bit of a negative number" do + -1.bit_length.should == 0 + -2.bit_length.should == 1 + -3.bit_length.should == 2 + -4.bit_length.should == 2 + -5.bit_length.should == 3 + n = fixnum_min.bit_length + fixnum_min[n].should == 1 + fixnum_min[n-1].should == 0 + + (-2**12-1).bit_length.should == 13 + (-2**12).bit_length.should == 12 + (-2**12+1).bit_length.should == 12 + -0x101.bit_length.should == 9 + -0x100.bit_length.should == 8 + -0xff.bit_length.should == 8 + -2.bit_length.should == 1 + -1.bit_length.should == 0 + end +end diff --git a/spec/rubyspec/core/fixnum/bit_or_spec.rb b/spec/rubyspec/core/fixnum/bit_or_spec.rb new file mode 100644 index 0000000000..cd1865ffe9 --- /dev/null +++ b/spec/rubyspec/core/fixnum/bit_or_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#|" do + it "returns self bitwise OR other" do + (1 | 0).should == 1 + (5 | 4).should == 5 + (5 | 6).should == 7 + (248 | 4096).should == 4344 + (0xffff | bignum_value + 0xf0f0).should == 0x8000_0000_0000_ffff + end + + it "returns self bitwise OR a Bignum" do + (-1 | 2**64).should == -1 + end + + it "raises a TypeError when passed a Float" do + lambda { (3 | 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("fixnum bit or") + obj.should_not_receive(:to_int) + + lambda { 3 | obj }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/bit_xor_spec.rb b/spec/rubyspec/core/fixnum/bit_xor_spec.rb new file mode 100644 index 0000000000..38a90a4dfa --- /dev/null +++ b/spec/rubyspec/core/fixnum/bit_xor_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#^" do + it "returns self bitwise EXCLUSIVE OR other" do + (3 ^ 5).should == 6 + (-2 ^ -255).should == 255 + (5 ^ bignum_value + 0xffff_ffff).should == 0x8000_0000_ffff_fffa + end + + it "returns self bitwise EXCLUSIVE OR a Bignum" do + (-1 ^ 2**64).should == -18446744073709551617 + end + + it "raises a TypeError when passed a Float" do + lambda { (3 ^ 3.4) }.should raise_error(TypeError) + end + + it "raises a TypeError and does not call #to_int when defined on an object" do + obj = mock("fixnum bit xor") + obj.should_not_receive(:to_int) + + lambda { 3 ^ obj }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/case_compare_spec.rb b/spec/rubyspec/core/fixnum/case_compare_spec.rb new file mode 100644 index 0000000000..53bfc38eb8 --- /dev/null +++ b/spec/rubyspec/core/fixnum/case_compare_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Fixnum#===" do + it_behaves_like :fixnum_equal, :=== +end diff --git a/spec/rubyspec/core/fixnum/coerce_spec.rb b/spec/rubyspec/core/fixnum/coerce_spec.rb new file mode 100644 index 0000000000..3ca7cb2df4 --- /dev/null +++ b/spec/rubyspec/core/fixnum/coerce_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#coerce when given a Fixnum" do + it "returns an array containing two Fixnums" do + 1.coerce(2).should == [2, 1] + 1.coerce(2).map { |i| i.class }.should == [Fixnum, Fixnum] + end +end + +describe "Fixnum#coerce when given a String" do + it "raises an ArgumentError when trying to coerce with a non-number String" do + lambda { 1.coerce(":)") }.should raise_error(ArgumentError) + end + + it "returns an array containing two Floats" do + 1.coerce("2").should == [2.0, 1.0] + 1.coerce("-2").should == [-2.0, 1.0] + end +end + +describe "Fixnum#coerce" do + it "raises a TypeError when trying to coerce with nil" do + lambda { 1.coerce(nil) }.should raise_error(TypeError) + end + + it "tries to convert the given Object into a Float by using #to_f" do + (obj = mock('1.0')).should_receive(:to_f).and_return(1.0) + 2.coerce(obj).should == [1.0, 2.0] + + (obj = mock('0')).should_receive(:to_f).and_return('0') + lambda { 2.coerce(obj).should == [1.0, 2.0] }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Object that does not respond to #to_f" do + lambda { 1.coerce(mock('x')) }.should raise_error(TypeError) + lambda { 1.coerce(1..4) }.should raise_error(TypeError) + lambda { 1.coerce(:test) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/comparison_spec.rb b/spec/rubyspec/core/fixnum/comparison_spec.rb new file mode 100644 index 0000000000..4c932d213d --- /dev/null +++ b/spec/rubyspec/core/fixnum/comparison_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#<=>" do + it "returns -1 when self is less than the given argument" do + (-3 <=> -1).should == -1 + (-5 <=> 10).should == -1 + (-5 <=> -4.5).should == -1 + end + + it "returns 0 when self is equal to the given argument" do + (0 <=> 0).should == 0 + (954 <=> 954).should == 0 + (954 <=> 954.0).should == 0 + end + + it "returns 1 when self is greater than the given argument" do + (496 <=> 5).should == 1 + (200 <=> 100).should == 1 + (51 <=> 50.5).should == 1 + end + + it "returns nil when the given argument is not an Integer" do + (3 <=> mock('x')).should == nil + (3 <=> 'test').should == nil + end +end diff --git a/spec/rubyspec/core/fixnum/complement_spec.rb b/spec/rubyspec/core/fixnum/complement_spec.rb new file mode 100644 index 0000000000..06692579c5 --- /dev/null +++ b/spec/rubyspec/core/fixnum/complement_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#~" do + it "returns self with each bit flipped" do + (~0).should == -1 + (~1221).should == -1222 + (~-2).should == 1 + (~-599).should == 598 + end +end diff --git a/spec/rubyspec/core/fixnum/div_spec.rb b/spec/rubyspec/core/fixnum/div_spec.rb new file mode 100644 index 0000000000..be9b498508 --- /dev/null +++ b/spec/rubyspec/core/fixnum/div_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#div with a Fixnum" do + it "returns self divided by the given argument as an Integer" do + 2.div(2).should == 1 + 1.div(2).should == 0 + 5.div(2).should == 2 + end +end + +describe "Fixnum#div" do + it "rounds towards -inf" do + 8192.div(10).should == 819 + 8192.div(-10).should == -820 + (-8192).div(10).should == -820 + (-8192).div(-10).should == 819 + end + + it "coerces self and the given argument to Floats and returns self divided by other as Fixnum" do + 1.div(0.2).should == 5 + 1.div(0.16).should == 6 + 1.div(0.169).should == 5 + -1.div(50.4).should == -1 + 1.div(bignum_value).should == 0 + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + lambda { 0.div(0.0) }.should raise_error(ZeroDivisionError) + lambda { 10.div(0.0) }.should raise_error(ZeroDivisionError) + lambda { -10.div(0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0" do + lambda { 13.div(0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13.div(obj) + }.should raise_error(TypeError) + lambda { 5.div("2") }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/divide_spec.rb b/spec/rubyspec/core/fixnum/divide_spec.rb new file mode 100644 index 0000000000..1e7c17e58f --- /dev/null +++ b/spec/rubyspec/core/fixnum/divide_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#/" do + it "returns self divided by the given argument" do + (2 / 2).should == 1 + (3 / 2).should == 1 + end + + it "supports dividing negative numbers" do + (-1 / 10).should == -1 + end + + it "raises a ZeroDivisionError if the given argument is zero and not a Float" do + lambda { 1 / 0 }.should raise_error(ZeroDivisionError) + end + + it "does NOT raise ZeroDivisionError if the given argument is zero and is a Float" do + (1 / 0.0).to_s.should == 'Infinity' + (-1 / 0.0).to_s.should == '-Infinity' + end + + it "coerces fixnum and return self divided by other" do + (-1 / 50.4).should be_close(-0.0198412698412698, TOLERANCE) + (1 / bignum_value).should == 0 + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 / obj + }.should raise_error(TypeError) + lambda { 13 / "10" }.should raise_error(TypeError) + lambda { 13 / :symbol }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/divmod_spec.rb b/spec/rubyspec/core/fixnum/divmod_spec.rb new file mode 100644 index 0000000000..21c9afe315 --- /dev/null +++ b/spec/rubyspec/core/fixnum/divmod_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#divmod" do + it "returns an Array containing quotient and modulus obtained from dividing self by the given argument" do + 13.divmod(4).should == [3, 1] + 4.divmod(13).should == [0, 4] + + 13.divmod(4.0).should == [3, 1] + 4.divmod(13.0).should == [0, 4] + + 1.divmod(2.0).should == [0, 1.0] + 200.divmod(bignum_value).should == [0, 200] + end + + it "raises a ZeroDivisionError when the given argument is 0" do + lambda { 13.divmod(0) }.should raise_error(ZeroDivisionError) + lambda { 0.divmod(0) }.should raise_error(ZeroDivisionError) + lambda { -10.divmod(0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + lambda { 0.divmod(0.0) }.should raise_error(ZeroDivisionError) + lambda { 10.divmod(0.0) }.should raise_error(ZeroDivisionError) + lambda { -10.divmod(0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13.divmod(obj) + }.should raise_error(TypeError) + lambda { 13.divmod("10") }.should raise_error(TypeError) + lambda { 13.divmod(:symbol) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/element_reference_spec.rb b/spec/rubyspec/core/fixnum/element_reference_spec.rb new file mode 100644 index 0000000000..736e8a549b --- /dev/null +++ b/spec/rubyspec/core/fixnum/element_reference_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#[]" do + it "behaves like (n >> b) & 1" do + 0b101[1].should == 0 + 0b101[2].should == 1 + end + + it "returns 1 if the nth bit is set" do + 15[1].should == 1 + end + + it "returns 1 if the nth bit is set (in two's-complement representation)" do + (-1)[1].should == 1 + end + + it "returns 0 if the nth bit is not set" do + 8[2].should == 0 + end + + it "returns 0 if the nth bit is not set (in two's-complement representation)" do + (-2)[0].should == 0 + end + + it "returns 0 if the nth bit is greater than the most significant bit" do + 2[3].should == 0 + end + + it "returns 1 if self is negative and the nth bit is greater than the most significant bit" do + (-1)[3].should == 1 + end + + it "returns 0 when passed a negative argument" do + 3[-1].should == 0 + (-1)[-1].should == 0 + end + + it "calls #to_int to convert the argument to an Integer and returns 1 if the nth bit is set" do + obj = mock('1') + obj.should_receive(:to_int).and_return(1) + + 2[obj].should == 1 + end + + it "calls #to_int to convert the argument to an Integer and returns 0 if the nth bit is set" do + obj = mock('0') + obj.should_receive(:to_int).and_return(0) + + 2[obj].should == 0 + end + + it "accepts a Float argument and returns 0 if the bit at the truncated value is not set" do + 13[1.3].should == 0 + end + + it "accepts a Float argument and returns 1 if the bit at the truncated value is set" do + 13[2.1].should == 1 + end + + it "raises a TypeError when passed a String" do + lambda { 3["3"] }.should raise_error(TypeError) + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock('asdf') + obj.should_receive(:to_int).and_return("asdf") + lambda { 3[obj] }.should raise_error(TypeError) + end + + it "calls #to_int to coerce a String to a Bignum and returns 0" do + obj = mock('bignum value') + obj.should_receive(:to_int).and_return(bignum_value) + + 3[obj].should == 0 + end + + it "returns 0 when passed a Float in the range of a Bignum" do + 3[bignum_value.to_f].should == 0 + end +end diff --git a/spec/rubyspec/core/fixnum/equal_value_spec.rb b/spec/rubyspec/core/fixnum/equal_value_spec.rb new file mode 100644 index 0000000000..3a9ef93d24 --- /dev/null +++ b/spec/rubyspec/core/fixnum/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Fixnum#==" do + it_behaves_like :fixnum_equal, :== +end diff --git a/spec/rubyspec/core/fixnum/even_spec.rb b/spec/rubyspec/core/fixnum/even_spec.rb new file mode 100644 index 0000000000..1cafb7d3a0 --- /dev/null +++ b/spec/rubyspec/core/fixnum/even_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#even?" do + it "is true for zero" do + 0.even?.should be_true + end + + it "is true for even positive Fixnums" do + 4.even?.should be_true + end + + it "is true for even negative Fixnums" do + (-4).even?.should be_true + end + + it "is false for odd positive Fixnums" do + 5.even?.should be_false + end + + it "is false for odd negative Fixnums" do + (-5).even?.should be_false + end +end diff --git a/spec/rubyspec/core/fixnum/exponent_spec.rb b/spec/rubyspec/core/fixnum/exponent_spec.rb new file mode 100644 index 0000000000..f3e7349ace --- /dev/null +++ b/spec/rubyspec/core/fixnum/exponent_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#**" do + it "returns self raised to the given power" do + (2 ** 0).should eql 1 + (2 ** 1).should eql 2 + (2 ** 2).should eql 4 + + (9 ** 0.5).should eql 3.0 + (5 ** -1).to_f.to_s.should == '0.2' + + (2 ** 40).should eql 1099511627776 + end + + it "overflows the answer to a bignum transparantly" do + (2 ** 29).should eql 536870912 + (2 ** 30).should eql 1073741824 + (2 ** 31).should eql 2147483648 + (2 ** 32).should eql 4294967296 + + (2 ** 61).should eql 2305843009213693952 + (2 ** 62).should eql 4611686018427387904 + (2 ** 63).should eql 9223372036854775808 + (2 ** 64).should eql 18446744073709551616 + (8 ** 23).should eql 590295810358705651712 + end + + it "raises negative numbers to the given power" do + ((-2) ** 29).should eql(-536870912) + ((-2) ** 30).should eql(1073741824) + ((-2) ** 31).should eql(-2147483648) + ((-2) ** 32).should eql(4294967296) + + ((-2) ** 61).should eql(-2305843009213693952) + ((-2) ** 62).should eql(4611686018427387904) + ((-2) ** 63).should eql(-9223372036854775808) + ((-2) ** 64).should eql(18446744073709551616) + end + + it "can raise 1 to a Bignum safely" do + big = bignum_value(4611686018427387904) + (1 ** big).should eql 1 + end + + it "can raise -1 to a Bignum safely" do + ((-1) ** bignum_value(0)).should eql(1) + ((-1) ** bignum_value(1)).should eql(-1) + end + + it "switches to a Float when the number is too big" do + big = bignum_value(4611686018427387904) + flt = (2 ** big) + flt.should be_kind_of(Float) + flt.infinite?.should == 1 + end + + conflicts_with :Rational do + it "raises a ZeroDivisionError for 0**-1" do + lambda { (0**-1) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 ** obj + }.should raise_error(TypeError) + lambda { 13 ** "10" }.should raise_error(TypeError) + lambda { 13 ** :symbol }.should raise_error(TypeError) + end + end + + it "returns a complex number when negative and raised to a fractional power" do + ((-8) ** (1.0/3)) .should be_close(Complex(1, 1.73205), TOLERANCE) + ((-8) ** Rational(1,3)).should be_close(Complex(1, 1.73205), TOLERANCE) + end +end diff --git a/spec/rubyspec/core/fixnum/fdiv_spec.rb b/spec/rubyspec/core/fixnum/fdiv_spec.rb new file mode 100644 index 0000000000..bb5f09c333 --- /dev/null +++ b/spec/rubyspec/core/fixnum/fdiv_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#fdiv" do + it "performs floating-point division between self and a Fixnum" do + 8.fdiv(7).should be_close(1.14285714285714, TOLERANCE) + end + + it "performs floating-point division between self and a Bignum" do + 8.fdiv(bignum_value).should be_close(8.673617379884035e-19, TOLERANCE) + end + + it "performs floating-point division between self and a Float" do + 8.fdiv(9.0).should be_close(0.888888888888889, TOLERANCE) + end + + it "returns NaN when the argument is NaN" do + -1.fdiv(nan_value).nan?.should be_true + 1.fdiv(nan_value).nan?.should be_true + end + + it "returns Infinity when the argument is 0" do + 1.fdiv(0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0 and self is negative" do + -1.fdiv(0).infinite?.should == -1 + end + + it "returns Infinity when the argument is 0.0" do + 1.fdiv(0.0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0.0 and self is negative" do + -1.fdiv(0.0).infinite?.should == -1 + end + + it "raises a TypeError when argument isn't numeric" do + lambda { 1.fdiv(mock('non-numeric')) }.should raise_error(TypeError) + end + + it "raises an ArgumentError when passed multiple arguments" do + lambda { 1.fdiv(6,0.2) }.should raise_error(ArgumentError) + end + + it "follows the coercion protocol" do + (obj = mock('10')).should_receive(:coerce).with(1).and_return([1, 10]) + 1.fdiv(obj).should == 0.1 + end +end diff --git a/spec/rubyspec/core/fixnum/fixnum_spec.rb b/spec/rubyspec/core/fixnum/fixnum_spec.rb new file mode 100644 index 0000000000..d0af975c59 --- /dev/null +++ b/spec/rubyspec/core/fixnum/fixnum_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum" do + it "includes Comparable" do + Fixnum.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/fixnum/gt_spec.rb b/spec/rubyspec/core/fixnum/gt_spec.rb new file mode 100644 index 0000000000..2fe70304ae --- /dev/null +++ b/spec/rubyspec/core/fixnum/gt_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#>" do + it "returns true if self is greater than the given argument" do + (13 > 2).should == true + (-500 > -600).should == true + + (1 > 5).should == false + (5 > 5).should == false + + (900 > bignum_value).should == false + (5 > 4.999).should == true + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { 5 > "4" }.should raise_error(ArgumentError) + lambda { 5 > mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/fixnum/gte_spec.rb b/spec/rubyspec/core/fixnum/gte_spec.rb new file mode 100644 index 0000000000..1d5c2b70f8 --- /dev/null +++ b/spec/rubyspec/core/fixnum/gte_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#>=" do + it "returns true if self is greater than or equal to the given argument" do + (13 >= 2).should == true + (-500 >= -600).should == true + + (1 >= 5).should == false + (2 >= 2).should == true + (5 >= 5).should == true + + (900 >= bignum_value).should == false + (5 >= 4.999).should == true + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { 5 >= "4" }.should raise_error(ArgumentError) + lambda { 5 >= mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/fixnum/hash_spec.rb b/spec/rubyspec/core/fixnum/hash_spec.rb new file mode 100644 index 0000000000..de2996a7c6 --- /dev/null +++ b/spec/rubyspec/core/fixnum/hash_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#hash" do + it "is provided" do + 1.respond_to?(:hash).should == true + end + + it "is stable" do + 1.hash.should == 1.hash + end +end diff --git a/spec/rubyspec/core/fixnum/left_shift_spec.rb b/spec/rubyspec/core/fixnum/left_shift_spec.rb new file mode 100644 index 0000000000..8eb5e424ff --- /dev/null +++ b/spec/rubyspec/core/fixnum/left_shift_spec.rb @@ -0,0 +1,91 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#<< with n << m" do + it "returns n shifted left m bits when n > 0, m > 0" do + (1 << 1).should == 2 + end + + it "returns n shifted left m bits when n < 0, m > 0" do + (-1 << 1).should == -2 + (-7 << 1).should == -14 + (-42 << 2).should == -168 + end + + it "returns n shifted right m bits when n > 0, m < 0" do + (2 << -1).should == 1 + end + + it "returns n shifted right m bits when n < 0, m < 0" do + (-2 << -1).should == -1 + end + + it "returns 0 when n == 0" do + (0 << 1).should == 0 + end + + it "returns n when n > 0, m == 0" do + (1 << 0).should == 1 + end + + it "returns n when n < 0, m == 0" do + (-1 << 0).should == -1 + end + + it "returns 0 when n > 0, m < 0 and n < 2**-m" do + (3 << -2).should == 0 + (7 << -3).should == 0 + (127 << -7).should == 0 + + # To make sure the exponent is not truncated + (7 << -32).should == 0 + (7 << -64).should == 0 + end + + it "returns -1 when n < 0, m < 0 and n > -(2**-m)" do + (-3 << -2).should == -1 + (-7 << -3).should == -1 + (-127 << -7).should == -1 + + # To make sure the exponent is not truncated + (-7 << -32).should == -1 + (-7 << -64).should == -1 + end + + it "returns 0 when m < 0 and m is a Bignum" do + (3 << -bignum_value).should == 0 + end + + it "returns a Bignum == fixnum_max * 2 when fixnum_max << 1 and n > 0" do + result = fixnum_max << 1 + result.should be_an_instance_of(Bignum) + result.should == fixnum_max * 2 + end + + it "returns a Bignum == fixnum_min * 2 when fixnum_min << 1 and n < 0" do + result = fixnum_min << 1 + result.should be_an_instance_of(Bignum) + result.should == fixnum_min * 2 + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("4") + obj.should_receive(:to_int).and_return(4) + + (3 << obj).should == 48 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + lambda { 3 << obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + lambda { 3 << nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { 3 << "4" }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/lt_spec.rb b/spec/rubyspec/core/fixnum/lt_spec.rb new file mode 100644 index 0000000000..0bedf428b2 --- /dev/null +++ b/spec/rubyspec/core/fixnum/lt_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#<" do + it "returns true if self is less than the given argument" do + (2 < 13).should == true + (-600 < -500).should == true + + (5 < 1).should == false + (5 < 5).should == false + + (900 < bignum_value).should == true + (5 < 4.999).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { 5 < "4" }.should raise_error(ArgumentError) + lambda { 5 < mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/fixnum/lte_spec.rb b/spec/rubyspec/core/fixnum/lte_spec.rb new file mode 100644 index 0000000000..b9e5810d26 --- /dev/null +++ b/spec/rubyspec/core/fixnum/lte_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#<=" do + it "returns true if self is less than or equal to other" do + (2 <= 13).should == true + (-600 <= -500).should == true + + (5 <= 1).should == false + (5 <= 5).should == true + (-2 <= -2).should == true + + (900 <= bignum_value).should == true + (5 <= 4.999).should == false + end + + it "raises an ArgumentError when given a non-Integer" do + lambda { 5 <= "4" }.should raise_error(ArgumentError) + lambda { 5 <= mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/fixnum/magnitude_spec.rb b/spec/rubyspec/core/fixnum/magnitude_spec.rb new file mode 100644 index 0000000000..dc250eba19 --- /dev/null +++ b/spec/rubyspec/core/fixnum/magnitude_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/abs', __FILE__) + +describe "Fixnum#magnitude" do + it_behaves_like :fixnum_abs, :magnitude +end diff --git a/spec/rubyspec/core/fixnum/minus_spec.rb b/spec/rubyspec/core/fixnum/minus_spec.rb new file mode 100644 index 0000000000..de3af05179 --- /dev/null +++ b/spec/rubyspec/core/fixnum/minus_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#-" do + it "returns self minus the given Integer" do + (5 - 10).should == -5 + (9237212 - 5_280).should == 9231932 + + (781 - 0.5).should == 780.5 + (2_560_496 - bignum_value).should == -9223372036852215312 + end + + it "returns a Bignum only if the result is too large to be a Fixnum" do + (5 - 10).should be_an_instance_of Fixnum + (-1 - bignum_value).should be_an_instance_of Bignum + + bignum_zero = bignum_value.coerce(0).first + (1 - bignum_zero).should be_an_instance_of Fixnum + (fixnum_min - 1).should be_an_instance_of(Bignum) + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 - obj + }.should raise_error(TypeError) + lambda { 13 - "10" }.should raise_error(TypeError) + lambda { 13 - :symbol }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/modulo_spec.rb b/spec/rubyspec/core/fixnum/modulo_spec.rb new file mode 100644 index 0000000000..19d3291a11 --- /dev/null +++ b/spec/rubyspec/core/fixnum/modulo_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/modulo', __FILE__) + +describe "Fixnum#%" do + it_behaves_like(:fixnum_modulo, :%) +end + +describe "Fixnum#modulo" do + it_behaves_like(:fixnum_modulo, :modulo) +end diff --git a/spec/rubyspec/core/fixnum/multiply_spec.rb b/spec/rubyspec/core/fixnum/multiply_spec.rb new file mode 100644 index 0000000000..2eabd7b632 --- /dev/null +++ b/spec/rubyspec/core/fixnum/multiply_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#*" do + it "returns self multiplied by the given Integer" do + (4923 * 2).should == 9846 + (1342177 * 800).should == 1073741600 + (65536 * 65536).should == 4294967296 + + (256 * bignum_value).should == 2361183241434822606848 + (6712 * 0.25).should == 1678.0 + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 * obj + }.should raise_error(TypeError) + lambda { 13 * "10" }.should raise_error(TypeError) + lambda { 13 * :symbol }.should raise_error(TypeError) + end + + it "overflows to Bignum when the result does not fit in Fixnum" do + (fixnum_max * fixnum_max).should be_kind_of(Bignum) + (fixnum_max * fixnum_min).should be_kind_of(Bignum) + end + +end diff --git a/spec/rubyspec/core/fixnum/odd_spec.rb b/spec/rubyspec/core/fixnum/odd_spec.rb new file mode 100644 index 0000000000..3cf6bf45ed --- /dev/null +++ b/spec/rubyspec/core/fixnum/odd_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#odd?" do + it "is false for zero" do + 0.odd?.should be_false + end + + it "is false for even positive Fixnums" do + 4.odd?.should be_false + end + + it "is false for even negative Fixnums" do + (-4).odd?.should be_false + end + + it "is true for odd positive Fixnums" do + 5.odd?.should be_true + end + + it "is true for odd negative Fixnums" do + (-5).odd?.should be_true + end +end diff --git a/spec/rubyspec/core/fixnum/plus_spec.rb b/spec/rubyspec/core/fixnum/plus_spec.rb new file mode 100644 index 0000000000..4754bfeaf8 --- /dev/null +++ b/spec/rubyspec/core/fixnum/plus_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#+" do + it "returns self plus the given Integer" do + (491 + 2).should == 493 + (90210 + 10).should == 90220 + + (9 + bignum_value).should == 9223372036854775817 + (1001 + 5.219).should == 1006.219 + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13 + obj + }.should raise_error(TypeError) + lambda { 13 + "10" }.should raise_error(TypeError) + lambda { 13 + :symbol }.should raise_error(TypeError) + end + + it "overflows to Bignum when the result does not fit in Fixnum" do + (5 + 10).should be_an_instance_of Fixnum + (1 + bignum_value).should be_an_instance_of Bignum + + bignum_zero = bignum_value.coerce(0).first + (1 + bignum_zero).should be_an_instance_of Fixnum + (fixnum_max + 1).should be_an_instance_of(Bignum) + end +end diff --git a/spec/rubyspec/core/fixnum/right_shift_spec.rb b/spec/rubyspec/core/fixnum/right_shift_spec.rb new file mode 100644 index 0000000000..9a221ddbe7 --- /dev/null +++ b/spec/rubyspec/core/fixnum/right_shift_spec.rb @@ -0,0 +1,91 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#>> with n >> m" do + it "returns n shifted right m bits when n > 0, m > 0" do + (2 >> 1).should == 1 + end + + it "returns n shifted right m bits when n < 0, m > 0" do + (-2 >> 1).should == -1 + (-7 >> 1).should == -4 + (-42 >> 2).should == -11 + end + + it "returns n shifted left m bits when n > 0, m < 0" do + (1 >> -1).should == 2 + end + + it "returns n shifted left m bits when n < 0, m < 0" do + (-1 >> -1).should == -2 + end + + it "returns 0 when n == 0" do + (0 >> 1).should == 0 + end + + it "returns n when n > 0, m == 0" do + (1 >> 0).should == 1 + end + + it "returns n when n < 0, m == 0" do + (-1 >> 0).should == -1 + end + + it "returns 0 when n > 0, m > 0 and n < 2**m" do + (3 >> 2).should == 0 + (7 >> 3).should == 0 + (127 >> 7).should == 0 + + # To make sure the exponent is not truncated + (7 >> 32).should == 0 + (7 >> 64).should == 0 + end + + it "returns -1 when n < 0, m > 0 and n > -(2**m)" do + (-3 >> 2).should == -1 + (-7 >> 3).should == -1 + (-127 >> 7).should == -1 + + # To make sure the exponent is not truncated + (-7 >> 32).should == -1 + (-7 >> 64).should == -1 + end + + it "returns 0 when m is a Bignum" do + (3 >> bignum_value).should == 0 + end + + it "returns a Bignum == fixnum_max * 2 when fixnum_max >> -1 and n > 0" do + result = fixnum_max >> -1 + result.should be_an_instance_of(Bignum) + result.should == fixnum_max * 2 + end + + it "returns a Bignum == fixnum_min * 2 when fixnum_min >> -1 and n < 0" do + result = fixnum_min >> -1 + result.should be_an_instance_of(Bignum) + result.should == fixnum_min * 2 + end + + it "calls #to_int to convert the argument to an Integer" do + obj = mock("2") + obj.should_receive(:to_int).and_return(2) + + (8 >> obj).should == 2 + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("a string") + obj.should_receive(:to_int).and_return("asdf") + + lambda { 3 >> obj }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + lambda { 3 >> nil }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { 3 >> "4" }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/shared/abs.rb b/spec/rubyspec/core/fixnum/shared/abs.rb new file mode 100644 index 0000000000..511ec5221b --- /dev/null +++ b/spec/rubyspec/core/fixnum/shared/abs.rb @@ -0,0 +1,9 @@ +describe :fixnum_abs, shared: true do + it "returns self's absolute value" do + { 0 => [0, -0, +0], 2 => [2, -2, +2], 100 => [100, -100, +100] }.each do |key, values| + values.each do |value| + value.send(@method).should == key + end + end + end +end diff --git a/spec/rubyspec/core/fixnum/shared/equal.rb b/spec/rubyspec/core/fixnum/shared/equal.rb new file mode 100644 index 0000000000..01c763f316 --- /dev/null +++ b/spec/rubyspec/core/fixnum/shared/equal.rb @@ -0,0 +1,24 @@ +describe :fixnum_equal, shared: true do + it "returns true if self has the same value as other" do + 1.send(@method, 1).should == true + 9.send(@method, 5).should == false + + # Actually, these call Float#==, Bignum#== etc. + 9.send(@method, 9.0).should == true + 9.send(@method, 9.01).should == false + + 10.send(@method, bignum_value).should == false + end + + it "calls 'other == self' if the given argument is not a Fixnum" do + 1.send(@method, '*').should == false + + obj = mock('one other') + obj.should_receive(:==).any_number_of_times.and_return(false) + 1.send(@method, obj).should == false + + obj = mock('another') + obj.should_receive(:==).any_number_of_times.and_return(true) + 2.send(@method, obj).should == true + end +end diff --git a/spec/rubyspec/core/fixnum/shared/modulo.rb b/spec/rubyspec/core/fixnum/shared/modulo.rb new file mode 100644 index 0000000000..a2f9a2691d --- /dev/null +++ b/spec/rubyspec/core/fixnum/shared/modulo.rb @@ -0,0 +1,42 @@ +describe :fixnum_modulo, shared: true do + it "returns the modulus obtained from dividing self by the given argument" do + 13.send(@method, 4).should == 1 + 4.send(@method, 13).should == 4 + + 13.send(@method, 4.0).should == 1 + 4.send(@method, 13.0).should == 4 + + (-200).send(@method, 256).should == 56 + (-1000).send(@method, 512).should == 24 + + (-200).send(@method, -256).should == -200 + (-1000).send(@method, -512).should == -488 + + (200).send(@method, -256).should == -56 + (1000).send(@method, -512).should == -24 + + 1.send(@method, 2.0).should == 1.0 + 200.send(@method, bignum_value).should == 200 + end + + it "raises a ZeroDivisionError when the given argument is 0" do + lambda { 13.send(@method, 0) }.should raise_error(ZeroDivisionError) + lambda { 0.send(@method, 0) }.should raise_error(ZeroDivisionError) + lambda { -10.send(@method, 0) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the given argument is 0 and a Float" do + lambda { 0.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + lambda { 10.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + lambda { -10.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('10')).should_receive(:to_int).any_number_of_times.and_return(10) + 13.send(@method, obj) + }.should raise_error(TypeError) + lambda { 13.send(@method, "10") }.should raise_error(TypeError) + lambda { 13.send(@method, :symbol) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/fixnum/size_spec.rb b/spec/rubyspec/core/fixnum/size_spec.rb new file mode 100644 index 0000000000..f973d446ed --- /dev/null +++ b/spec/rubyspec/core/fixnum/size_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#size" do + platform_is wordsize: 32 do + it "returns the number of bytes in the machine representation of self" do + -1.size.should == 4 + 0.size.should == 4 + 4091.size.should == 4 + end + end + + platform_is wordsize: 64 do + it "returns the number of bytes in the machine representation of self" do + -1.size.should == 8 + 0.size.should == 8 + 4091.size.should == 8 + end + end +end diff --git a/spec/rubyspec/core/fixnum/succ_spec.rb b/spec/rubyspec/core/fixnum/succ_spec.rb new file mode 100644 index 0000000000..50dd8c9481 --- /dev/null +++ b/spec/rubyspec/core/fixnum/succ_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#succ" do + it "returns the next larger positive Fixnum" do + 2.succ.should == 3 + end + + it "returns the next larger negative Fixnum" do + (-2).succ.should == -1 + end + + it "overflows a Fixnum to a Bignum" do + fixnum_max.succ.should == (fixnum_max + 1) + end +end diff --git a/spec/rubyspec/core/fixnum/to_f_spec.rb b/spec/rubyspec/core/fixnum/to_f_spec.rb new file mode 100644 index 0000000000..1d66348a5a --- /dev/null +++ b/spec/rubyspec/core/fixnum/to_f_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#to_f" do + it "returns self converted to a Float" do + 0.to_f.should == 0.0 + -500.to_f.should == -500.0 + 9_641_278.to_f.should == 9641278.0 + end +end diff --git a/spec/rubyspec/core/fixnum/to_s_spec.rb b/spec/rubyspec/core/fixnum/to_s_spec.rb new file mode 100644 index 0000000000..4a6649237b --- /dev/null +++ b/spec/rubyspec/core/fixnum/to_s_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#to_s when given a base" do + it "returns self converted to a String in the given base" do + 12345.to_s(2).should == "11000000111001" + 12345.to_s(8).should == "30071" + 12345.to_s(10).should == "12345" + 12345.to_s(16).should == "3039" + 95.to_s(16).should == "5f" + 12345.to_s(36).should == "9ix" + end + + it "raises an ArgumentError if the base is less than 2 or higher than 36" do + lambda { 123.to_s(-1) }.should raise_error(ArgumentError) + lambda { 123.to_s(0) }.should raise_error(ArgumentError) + lambda { 123.to_s(1) }.should raise_error(ArgumentError) + lambda { 123.to_s(37) }.should raise_error(ArgumentError) + end +end + +describe "Fixnum#to_s when no base given" do + it "returns self converted to a String using base 10" do + 255.to_s.should == '255' + 3.to_s.should == '3' + 0.to_s.should == '0' + -9002.to_s.should == '-9002' + end +end + +with_feature :encoding do + describe "Fixnum#to_s" do + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + 1.to_s.encoding.should equal(Encoding::US_ASCII) + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do + Encoding.default_internal = Encoding::IBM437 + 1.to_s.encoding.should equal(Encoding::US_ASCII) + end + end +end diff --git a/spec/rubyspec/core/fixnum/uminus_spec.rb b/spec/rubyspec/core/fixnum/uminus_spec.rb new file mode 100644 index 0000000000..ac676400d1 --- /dev/null +++ b/spec/rubyspec/core/fixnum/uminus_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#-@" do + it "returns self as a negative value" do + 2.send(:-@).should == -2 + -2.should == -2 + -268435455.should == -268435455 + (--5).should == 5 + -8.send(:-@).should == 8 + end + + it "negates self at Fixnum/Bignum boundaries" do + fixnum_max.send(:-@).should == (0 - fixnum_max) + fixnum_min.send(:-@).should == (0 - fixnum_min) + end +end diff --git a/spec/rubyspec/core/fixnum/zero_spec.rb b/spec/rubyspec/core/fixnum/zero_spec.rb new file mode 100644 index 0000000000..e155f9f5e6 --- /dev/null +++ b/spec/rubyspec/core/fixnum/zero_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Fixnum#zero?" do + it "returns true if self is 0" do + 0.zero?.should == true + -1.zero?.should == false + 1.zero?.should == false + end +end diff --git a/spec/rubyspec/core/float/abs_spec.rb b/spec/rubyspec/core/float/abs_spec.rb new file mode 100644 index 0000000000..3ff2e4369b --- /dev/null +++ b/spec/rubyspec/core/float/abs_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/abs', __FILE__) + +describe "Float#abs" do + it_behaves_like(:float_abs, :abs) +end diff --git a/spec/rubyspec/core/float/angle_spec.rb b/spec/rubyspec/core/float/angle_spec.rb new file mode 100644 index 0000000000..2a5d40ad30 --- /dev/null +++ b/spec/rubyspec/core/float/angle_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/float/arg', __FILE__) + +describe "Float#angle" do + it_behaves_like :float_arg, :angle +end diff --git a/spec/rubyspec/core/float/arg_spec.rb b/spec/rubyspec/core/float/arg_spec.rb new file mode 100644 index 0000000000..20a8ac0a63 --- /dev/null +++ b/spec/rubyspec/core/float/arg_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/float/arg', __FILE__) + +describe "Float#arg" do + it_behaves_like :float_arg, :arg +end diff --git a/spec/rubyspec/core/float/case_compare_spec.rb b/spec/rubyspec/core/float/case_compare_spec.rb new file mode 100644 index 0000000000..c2ad2941e0 --- /dev/null +++ b/spec/rubyspec/core/float/case_compare_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Float#===" do + it_behaves_like :float_equal, :=== +end diff --git a/spec/rubyspec/core/float/ceil_spec.rb b/spec/rubyspec/core/float/ceil_spec.rb new file mode 100644 index 0000000000..8037164c68 --- /dev/null +++ b/spec/rubyspec/core/float/ceil_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#ceil" do + it "returns the smallest Integer greater than or equal to self" do + -1.2.ceil.should eql( -1) + -1.0.ceil.should eql( -1) + 0.0.ceil.should eql( 0 ) + 1.3.ceil.should eql( 2 ) + 3.0.ceil.should eql( 3 ) + -9223372036854775808.1.ceil.should eql(-9223372036854775808) + +9223372036854775808.1.ceil.should eql(+9223372036854775808) + end +end diff --git a/spec/rubyspec/core/float/coerce_spec.rb b/spec/rubyspec/core/float/coerce_spec.rb new file mode 100644 index 0000000000..90475f2680 --- /dev/null +++ b/spec/rubyspec/core/float/coerce_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#coerce" do + it "returns [other, self] both as Floats" do + 1.2.coerce(1).should == [1.0, 1.2] + 5.28.coerce(1.0).should == [1.0, 5.28] + 1.0.coerce(1).should == [1.0, 1.0] + 1.0.coerce("2.5").should == [2.5, 1.0] + 1.0.coerce(3.14).should == [3.14, 1.0] + + a, b = -0.0.coerce(bignum_value) + a.should be_close(9223372036854775808.0, TOLERANCE) + b.should be_close(-0.0, TOLERANCE) + a, b = 1.0.coerce(bignum_value) + a.should be_close(9223372036854775808.0, TOLERANCE) + b.should be_close(1.0, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/float/comparison_spec.rb b/spec/rubyspec/core/float/comparison_spec.rb new file mode 100644 index 0000000000..49c3debbe1 --- /dev/null +++ b/spec/rubyspec/core/float/comparison_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#<=>" do + it "returns -1, 0, 1 when self is less than, equal, or greater than other" do + (1.5 <=> 5).should == -1 + (2.45 <=> 2.45).should == 0 + ((bignum_value*1.1) <=> bignum_value).should == 1 + end + + it "returns nil when either argument is NaN" do + (nan_value <=> 71.2).should be_nil + (1771.176 <=> nan_value).should be_nil + end + + it "returns nil when the given argument is not a Float" do + (1.0 <=> "1").should be_nil + end + + # The 4 tests below are taken from matz's revision 23730 for Ruby trunk + # + it "returns 1 when self is Infinity and other is a Bignum" do + (infinity_value <=> Float::MAX.to_i*2).should == 1 + end + + it "returns -1 when self is negative and other is Infinty" do + (-Float::MAX.to_i*2 <=> infinity_value).should == -1 + end + + it "returns -1 when self is -Infinity and other is negative" do + (-infinity_value <=> -Float::MAX.to_i*2).should == -1 + end + + it "returns 1 when self is negative and other is -Infinity" do + (-Float::MAX.to_i*2 <=> -infinity_value).should == 1 + end +end diff --git a/spec/rubyspec/core/float/constants_spec.rb b/spec/rubyspec/core/float/constants_spec.rb new file mode 100644 index 0000000000..31930b125a --- /dev/null +++ b/spec/rubyspec/core/float/constants_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float constant" do + it "DIG is 15" do + Float::DIG.should == 15 + end + + it "EPSILON is 2.220446049250313e-16" do + Float::EPSILON.should == 2.0 ** -52 + Float::EPSILON.should == 2.220446049250313e-16 + end + + it "MANT_DIG is 53" do + Float::MANT_DIG.should == 53 + end + + it "MAX_10_EXP is 308" do + Float::MAX_10_EXP.should == 308 + end + + it "MIN_10_EXP is -308" do + Float::MIN_10_EXP.should == -307 + end + + it "MAX_EXP is 1024" do + Float::MAX_EXP.should == 1024 + end + + it "MIN_EXP is -1021" do + Float::MIN_EXP.should == -1021 + end + + it "MAX is 1.7976931348623157e+308" do + # See https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Double-precision_examples + Float::MAX.should == (1 + (1 - (2 ** -52))) * (2.0 ** 1023) + Float::MAX.should == 1.7976931348623157e+308 + end + + it "MIN is 2.2250738585072014e-308" do + Float::MIN.should == (2.0 ** -1022) + Float::MIN.should == 2.2250738585072014e-308 + end + + it "RADIX is 2" do + Float::RADIX.should == 2 + end + + it "INFINITY is the positive infinity" do + Float::INFINITY.infinite?.should == 1 + end + + it "NAN is 'not a number'" do + Float::NAN.nan?.should be_true + end +end diff --git a/spec/rubyspec/core/float/denominator_spec.rb b/spec/rubyspec/core/float/denominator_spec.rb new file mode 100644 index 0000000000..56f5d288cf --- /dev/null +++ b/spec/rubyspec/core/float/denominator_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#denominator" do + before :each do + @numbers = [ + 0.0, + 29871.22736282, + 7772222663.0, + 1.4592, + ].map {|n| [0-n, n]}.flatten + end + + it "returns an Integer" do + @numbers.each do |number| + number.denominator.should be_kind_of(Integer) + end + end + + it "converts self to a Rational and returns the denominator" do + @numbers.each do |number| + number.denominator.should == Rational(number).denominator + end + end + + it "returns 1 for NaN and Infinity" do + nan_value.denominator.should == 1 + infinity_value.denominator.should == 1 + end +end diff --git a/spec/rubyspec/core/float/divide_spec.rb b/spec/rubyspec/core/float/divide_spec.rb new file mode 100644 index 0000000000..0acd7b20b4 --- /dev/null +++ b/spec/rubyspec/core/float/divide_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/coerce.rb', __FILE__) + +describe "Float#/" do + it "returns self divided by other" do + (5.75 / -2).should be_close(-2.875,TOLERANCE) + (451.0 / 9.3).should be_close(48.494623655914,TOLERANCE) + (91.1 / -0xffffffff).should be_close(-2.12108716418061e-08, TOLERANCE) + end + + it "properly coerces objects" do + (5.0 / FloatSpecs::CanCoerce.new(5)).should be_close(0, TOLERANCE) + end + + it "returns +Infinity when dividing non-zero by zero of the same sign" do + (1.0 / 0.0).should be_positive_infinity + (-1.0 / -0.0).should be_positive_infinity + end + + it "returns -Infinity when dividing non-zero by zero of opposite sign" do + (-1.0 / 0.0).should be_negative_infinity + (1.0 / -0.0).should be_negative_infinity + end + + it "returns NaN when dividing zero by zero" do + (0.0 / 0.0).should be_nan + (-0.0 / 0.0).should be_nan + (0.0 / -0.0).should be_nan + (-0.0 / -0.0).should be_nan + end + + it "raises a TypeError when given a non-Numeric" do + lambda { 13.0 / "10" }.should raise_error(TypeError) + lambda { 13.0 / :symbol }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/float/divmod_spec.rb b/spec/rubyspec/core/float/divmod_spec.rb new file mode 100644 index 0000000000..174f142c86 --- /dev/null +++ b/spec/rubyspec/core/float/divmod_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#divmod" do + it "returns an [quotient, modulus] from dividing self by other" do + values = 3.14.divmod(2) + values[0].should eql(1) + values[1].should be_close(1.14, TOLERANCE) + values = 2.8284.divmod(3.1415) + values[0].should eql(0) + values[1].should be_close(2.8284, TOLERANCE) + values = -1.0.divmod(bignum_value) + values[0].should eql(-1) + values[1].should be_close(9223372036854775808.000, TOLERANCE) + values = -1.0.divmod(1) + values[0].should eql(-1) + values[1].should eql(0.0) + end + + # Behaviour established as correct in r23953 + it "raises a FloatDomainError if self is NaN" do + lambda { nan_value.divmod(1) }.should raise_error(FloatDomainError) + end + + # Behaviour established as correct in r23953 + it "raises a FloatDomainError if other is NaN" do + lambda { 1.divmod(nan_value) }.should raise_error(FloatDomainError) + end + + # Behaviour established as correct in r23953 + it "raises a FloatDomainError if self is Infinity" do + lambda { infinity_value.divmod(1) }.should raise_error(FloatDomainError) + end + + it "raises a ZeroDivisionError if other is zero" do + lambda { 1.0.divmod(0) }.should raise_error(ZeroDivisionError) + lambda { 1.0.divmod(0.0) }.should raise_error(ZeroDivisionError) + end + + # redmine #5276" + it "returns the correct [quotient, modulus] even for large quotient" do + 0.59.divmod(7.761021455128987e-11).first.should eql(7602092113) + end +end diff --git a/spec/rubyspec/core/float/eql_spec.rb b/spec/rubyspec/core/float/eql_spec.rb new file mode 100644 index 0000000000..7c4eef8523 --- /dev/null +++ b/spec/rubyspec/core/float/eql_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#eql?" do + it "returns true if other is a Float equal to self" do + 0.0.eql?(0.0).should be_true + end + + it "returns false if other is a Float not equal to self" do + 1.0.eql?(1.1).should be_false + end + + it "returns false if other is not a Float" do + 1.0.eql?(1).should be_false + 1.0.eql?(:one).should be_false + end +end diff --git a/spec/rubyspec/core/float/equal_value_spec.rb b/spec/rubyspec/core/float/equal_value_spec.rb new file mode 100644 index 0000000000..19ef01fc1c --- /dev/null +++ b/spec/rubyspec/core/float/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Float#==" do + it_behaves_like :float_equal, :== +end diff --git a/spec/rubyspec/core/float/exponent_spec.rb b/spec/rubyspec/core/float/exponent_spec.rb new file mode 100644 index 0000000000..5cbba43a27 --- /dev/null +++ b/spec/rubyspec/core/float/exponent_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#**" do + it "returns self raise to the other power" do + (2.3 ** 3).should be_close(12.167,TOLERANCE) + (5.2 ** -1).should be_close(0.192307692307692,TOLERANCE) + (9.5 ** 0.5).should be_close(3.08220700148449, TOLERANCE) + (9.5 ** 0xffffffff).to_s.should == 'Infinity' + end + + it "returns a complex number when negative and raised to a fractional power" do + ((-8.0) ** (1.0/3)) .should be_close(Complex(1, 1.73205), TOLERANCE) + ((-8.0) ** Rational(1,3)).should be_close(Complex(1, 1.73205), TOLERANCE) + end +end diff --git a/spec/rubyspec/core/float/fdiv_spec.rb b/spec/rubyspec/core/float/fdiv_spec.rb new file mode 100644 index 0000000000..632dd0f293 --- /dev/null +++ b/spec/rubyspec/core/float/fdiv_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quo', __FILE__) + +describe "Float#fdiv" do + it_behaves_like :float_quo, :fdiv +end diff --git a/spec/rubyspec/core/float/finite_spec.rb b/spec/rubyspec/core/float/finite_spec.rb new file mode 100644 index 0000000000..d6a161d4e3 --- /dev/null +++ b/spec/rubyspec/core/float/finite_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#finite?" do + it "returns true for finite values" do + 3.14159.finite?.should == true + end + + it "returns false for positive infinity" do + infinity_value.finite?.should == false + end + + it "returns false for negative infinity" do + (-infinity_value).finite?.should == false + end + + it "returns false for NaN" do + nan_value.finite?.should == false + end +end diff --git a/spec/rubyspec/core/float/fixtures/coerce.rb b/spec/rubyspec/core/float/fixtures/coerce.rb new file mode 100644 index 0000000000..2cf155be95 --- /dev/null +++ b/spec/rubyspec/core/float/fixtures/coerce.rb @@ -0,0 +1,15 @@ +module FloatSpecs + class CanCoerce + def initialize(a) + @a = a + end + + def coerce(b) + [self.class.new(b), @a] + end + + def /(b) + @a.to_i % b.to_i + end + end +end diff --git a/spec/rubyspec/core/float/float_spec.rb b/spec/rubyspec/core/float/float_spec.rb new file mode 100644 index 0000000000..e26e082a55 --- /dev/null +++ b/spec/rubyspec/core/float/float_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float" do + it "includes Comparable" do + Float.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/float/floor_spec.rb b/spec/rubyspec/core/float/floor_spec.rb new file mode 100644 index 0000000000..63a5138366 --- /dev/null +++ b/spec/rubyspec/core/float/floor_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#floor" do + it "returns the largest Integer less than or equal to self" do + -1.2.floor.should eql( -2) + -1.0.floor.should eql( -1) + 0.0.floor.should eql( 0 ) + 1.0.floor.should eql( 1 ) + 5.9.floor.should eql( 5 ) + -9223372036854775808.1.floor.should eql(-9223372036854775808) + +9223372036854775808.1.floor.should eql(+9223372036854775808) + end +end diff --git a/spec/rubyspec/core/float/gt_spec.rb b/spec/rubyspec/core/float/gt_spec.rb new file mode 100644 index 0000000000..9725c6acd7 --- /dev/null +++ b/spec/rubyspec/core/float/gt_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#>" do + it "returns true if self is greater than other" do + (1.5 > 1).should == true + (2.5 > 3).should == false + (45.91 > bignum_value).should == false + end + + it "raises an ArgumentError when given a non-Numeric" do + lambda { 5.0 > "4" }.should raise_error(ArgumentError) + lambda { 5.0 > mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/float/gte_spec.rb b/spec/rubyspec/core/float/gte_spec.rb new file mode 100644 index 0000000000..2c14651dd7 --- /dev/null +++ b/spec/rubyspec/core/float/gte_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#>=" do + it "returns true if self is greater than or equal to other" do + (5.2 >= 5.2).should == true + (9.71 >= 1).should == true + (5.55382 >= 0xfabdafbafcab).should == false + end + + it "raises an ArgumentError when given a non-Numeric" do + lambda { 5.0 >= "4" }.should raise_error(ArgumentError) + lambda { 5.0 >= mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/float/hash_spec.rb b/spec/rubyspec/core/float/hash_spec.rb new file mode 100644 index 0000000000..c638e99347 --- /dev/null +++ b/spec/rubyspec/core/float/hash_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#hash" do + it "is provided" do + 0.0.respond_to?(:hash).should == true + end + + it "is stable" do + 1.0.hash.should == 1.0.hash + end +end diff --git a/spec/rubyspec/core/float/infinite_spec.rb b/spec/rubyspec/core/float/infinite_spec.rb new file mode 100644 index 0000000000..4e31effab7 --- /dev/null +++ b/spec/rubyspec/core/float/infinite_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#infinite?" do + it "returns nil for finite values" do + 1.0.infinite?.should == nil + end + + it "returns 1 for positive infinity" do + infinity_value.infinite?.should == 1 + end + + it "returns -1 for negative infinity" do + (-infinity_value).infinite?.should == -1 + end + + it "returns nil for NaN" do + nan_value.infinite?.should == nil + end +end diff --git a/spec/rubyspec/core/float/lt_spec.rb b/spec/rubyspec/core/float/lt_spec.rb new file mode 100644 index 0000000000..e2e43b0fb7 --- /dev/null +++ b/spec/rubyspec/core/float/lt_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#<" do + it "returns true if self is less than other" do + (71.3 < 91.8).should == true + (192.6 < -500).should == false + (-0.12 < 0x4fffffff).should == true + end + + it "raises an ArgumentError when given a non-Numeric" do + lambda { 5.0 < "4" }.should raise_error(ArgumentError) + lambda { 5.0 < mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/float/lte_spec.rb b/spec/rubyspec/core/float/lte_spec.rb new file mode 100644 index 0000000000..e2e44b2257 --- /dev/null +++ b/spec/rubyspec/core/float/lte_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#<=" do + it "returns true if self is less than or equal to other" do + (2.0 <= 3.14159).should == true + (-2.7183 <= -24).should == false + (0.0 <= 0.0).should == true + (9_235.9 <= bignum_value).should == true + end + + it "raises an ArgumentError when given a non-Numeric" do + lambda { 5.0 <= "4" }.should raise_error(ArgumentError) + lambda { 5.0 <= mock('x') }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/float/magnitude_spec.rb b/spec/rubyspec/core/float/magnitude_spec.rb new file mode 100644 index 0000000000..042356f4c4 --- /dev/null +++ b/spec/rubyspec/core/float/magnitude_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/abs', __FILE__) + +describe "Float#magnitude" do + it_behaves_like(:float_abs, :magnitude) +end diff --git a/spec/rubyspec/core/float/minus_spec.rb b/spec/rubyspec/core/float/minus_spec.rb new file mode 100644 index 0000000000..d5c0d863ed --- /dev/null +++ b/spec/rubyspec/core/float/minus_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#-" do + it "returns self minus other" do + (9_237_212.5280 - 5_280).should be_close(9231932.528, TOLERANCE) + (2_560_496.1691 - bignum_value).should be_close(-9223372036852215808.000, TOLERANCE) + (5.5 - 5.5).should be_close(0.0,TOLERANCE) + end +end diff --git a/spec/rubyspec/core/float/modulo_spec.rb b/spec/rubyspec/core/float/modulo_spec.rb new file mode 100644 index 0000000000..f29e3870da --- /dev/null +++ b/spec/rubyspec/core/float/modulo_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/modulo', __FILE__) + +describe "Float#%" do + it_behaves_like(:float_modulo, :%) +end + +describe "Float#modulo" do + it_behaves_like(:float_modulo, :modulo) +end diff --git a/spec/rubyspec/core/float/multiply_spec.rb b/spec/rubyspec/core/float/multiply_spec.rb new file mode 100644 index 0000000000..14680534c4 --- /dev/null +++ b/spec/rubyspec/core/float/multiply_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#*" do + it "returns self multiplied by other" do + (4923.98221 * 2).should be_close(9847.96442, TOLERANCE) + (6712.5 * 0.25).should be_close(1678.125, TOLERANCE) + (256.4096 * bignum_value).should be_close(2364961134621118431232.000, TOLERANCE) + end + + it "raises a TypeError when given a non-Numeric" do + lambda { 13.0 * "10" }.should raise_error(TypeError) + lambda { 13.0 * :symbol }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/float/nan_spec.rb b/spec/rubyspec/core/float/nan_spec.rb new file mode 100644 index 0000000000..95a61d8872 --- /dev/null +++ b/spec/rubyspec/core/float/nan_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#nan?" do + it "returns true if self is not a valid IEEE floating-point number" do + 0.0.nan?.should == false + -1.5.nan?.should == false + nan_value.nan?.should == true + end +end diff --git a/spec/rubyspec/core/float/next_float_spec.rb b/spec/rubyspec/core/float/next_float_spec.rb new file mode 100644 index 0000000000..d5a7748a06 --- /dev/null +++ b/spec/rubyspec/core/float/next_float_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#next_float" do + it "returns a float the smallest possible step greater than the receiver" do + barely_positive = 0.0.next_float + barely_positive.should == 0.0.next_float + + barely_positive.should > 0.0 + barely_positive.should < barely_positive.next_float + + midpoint = barely_positive / 2 + [0.0, barely_positive].should include midpoint + end + + it "returns Float::INFINITY for Float::INFINITY" do + Float::INFINITY.next_float.should == Float::INFINITY + end + + it "steps directly between MAX and INFINITY" do + (-Float::INFINITY).next_float.should == -Float::MAX + Float::MAX.next_float.should == Float::INFINITY + end + + it "steps directly between 1.0 and 1.0 + EPSILON" do + 1.0.next_float.should == 1.0 + Float::EPSILON + end + + it "steps directly between -1.0 and -1.0 + EPSILON/2" do + (-1.0).next_float.should == -1.0 + Float::EPSILON/2 + end + + it "reverses the effect of prev_float for all Floats except INFINITY and +0.0" do + num = -rand + num.prev_float.next_float.should == num + end + + it "returns negative zero when stepping upward from just below zero" do + x = (-0.0).prev_float.next_float + (1/x).should == -Float::INFINITY + end + + it "gives the same result for -0.0 as for +0.0" do + (-0.0).next_float.should == (0.0).next_float + end + + it "returns NAN if NAN was the receiver" do + Float::NAN.next_float.nan?.should == true + end +end diff --git a/spec/rubyspec/core/float/numerator_spec.rb b/spec/rubyspec/core/float/numerator_spec.rb new file mode 100644 index 0000000000..9644d01c23 --- /dev/null +++ b/spec/rubyspec/core/float/numerator_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#numerator" do + before :all do + @numbers = [ + 29871.2722891, + 999.1**99.928888, + -72628191273.22, + 29282.2827, + -2927.00091, + 12.0, + Float::MAX, + ] + end + + it "converts self to a Rational object then returns its numerator" do + @numbers.each do |number| + number.infinite?.should be_nil + number.numerator.should == Rational(number).numerator + end + end + + it "returns 0 for 0.0" do + 0.0.numerator.should == 0 + end + + it "returns NaN for NaN" do + nan_value.numerator.nan?.should be_true + end + + it "returns Infinity for Infinity" do + infinity_value.numerator.infinite?.should == 1 + end + + it "returns -Infinity for -Infinity" do + (-infinity_value).numerator.infinite?.should == -1 + end + +end diff --git a/spec/rubyspec/core/float/phase_spec.rb b/spec/rubyspec/core/float/phase_spec.rb new file mode 100644 index 0000000000..95d0053816 --- /dev/null +++ b/spec/rubyspec/core/float/phase_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/complex/float/arg', __FILE__) + +describe "Float#phase" do + it_behaves_like :float_arg, :phase +end diff --git a/spec/rubyspec/core/float/plus_spec.rb b/spec/rubyspec/core/float/plus_spec.rb new file mode 100644 index 0000000000..a49124d303 --- /dev/null +++ b/spec/rubyspec/core/float/plus_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#+" do + it "returns self plus other" do + (491.213 + 2).should be_close(493.213, TOLERANCE) + (9.99 + bignum_value).should be_close(9223372036854775808.000, TOLERANCE) + (1001.99 + 5.219).should be_close(1007.209, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/float/prev_float_spec.rb b/spec/rubyspec/core/float/prev_float_spec.rb new file mode 100644 index 0000000000..e07d78c44c --- /dev/null +++ b/spec/rubyspec/core/float/prev_float_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#prev_float" do + it "returns a float the smallest possible step smaller than the receiver" do + barely_negative = 0.0.prev_float + barely_negative.should == 0.0.prev_float + + barely_negative.should < 0.0 + barely_negative.should > barely_negative.prev_float + + midpoint = barely_negative / 2 + [0.0, barely_negative].should include midpoint + end + + it "returns -Float::INFINITY for -Float::INFINITY" do + (-Float::INFINITY).prev_float.should == -Float::INFINITY + end + + it "steps directly between MAX and INFINITY" do + Float::INFINITY.prev_float.should == Float::MAX + (-Float::MAX).prev_float.should == -Float::INFINITY + end + + it "steps directly between 1.0 and 1.0 - EPSILON/2" do + 1.0.prev_float.should == 1.0 - Float::EPSILON/2 + end + + it "steps directly between -1.0 and -1.0 - EPSILON" do + (-1.0).prev_float.should == -1.0 - Float::EPSILON + end + + it "reverses the effect of next_float for all Floats except -INFINITY and -0.0" do + num = rand + num.next_float.prev_float.should == num + end + + it "returns positive zero when stepping downward from just above zero" do + x = 0.0.next_float.prev_float + (1/x).should == Float::INFINITY + end + + it "gives the same result for -0.0 as for +0.0" do + (0.0).prev_float.should == (-0.0).prev_float + end + + it "returns NAN if NAN was the receiver" do + Float::NAN.prev_float.nan?.should == true + end +end diff --git a/spec/rubyspec/core/float/quo_spec.rb b/spec/rubyspec/core/float/quo_spec.rb new file mode 100644 index 0000000000..dcda8f5f3b --- /dev/null +++ b/spec/rubyspec/core/float/quo_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quo', __FILE__) + +describe "Float#quo" do + it_behaves_like :float_quo, :quo +end diff --git a/spec/rubyspec/core/float/rationalize_spec.rb b/spec/rubyspec/core/float/rationalize_spec.rb new file mode 100644 index 0000000000..541080c48b --- /dev/null +++ b/spec/rubyspec/core/float/rationalize_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#rationalize" do + it "returns self as a simplified Rational with no argument" do + (3382729202.92822).rationalize.should == Rational(4806858197361, 1421) + end + + # FIXME: These specs need reviewing by somebody familiar with the + # algorithm used by #rationalize + it "simplifies self to the degree specified by a Rational argument" do + f = 0.3 + f.rationalize(Rational(1,10)).should == Rational(1,3) + f.rationalize(Rational(-1,10)).should == Rational(1,3) + + f = -f + f.rationalize(Rational(1,10)).should == Rational(-1,3) + f.rationalize(Rational(-1,10)).should == Rational(-1,3) + + end + + it "simplifies self to the degree specified by a Float argument" do + f = 0.3 + f.rationalize(0.05).should == Rational(1,3) + f.rationalize(0.001).should == Rational(3, 10) + + f = -f + f.rationalize(0.05).should == Rational(-1,3) + f.rationalize(0.001).should == Rational(-3,10) + end + + it "raises a FloatDomainError for Infinity" do + lambda {infinity_value.rationalize}.should raise_error(FloatDomainError) + end + + it "raises a FloatDomainError for NaN" do + lambda { nan_value.rationalize }.should raise_error(FloatDomainError) + end + + it "raises ArgumentError when passed more than one argument" do + lambda { 0.3.rationalize(0.1, 0.1) }.should raise_error(ArgumentError) + lambda { 0.3.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/float/round_spec.rb b/spec/rubyspec/core/float/round_spec.rb new file mode 100644 index 0000000000..46d23b36d0 --- /dev/null +++ b/spec/rubyspec/core/float/round_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#round" do + it "returns the nearest Integer" do + 5.5.round.should == 6 + 0.4.round.should == 0 + 0.6.round.should == 1 + -1.4.round.should == -1 + -2.8.round.should == -3 + 0.0.round.should == 0 + end + + platform_is_not :mingw32 do + it "returns the nearest Integer for Float near the limit" do + 0.49999999999999994.round.should == 0 + -0.49999999999999994.round.should == 0 + end + end + + it "raises FloatDomainError for exceptional values" do + lambda { (+infinity_value).round }.should raise_error(FloatDomainError) + lambda { (-infinity_value).round }.should raise_error(FloatDomainError) + lambda { nan_value.round }.should raise_error(FloatDomainError) + end + + it "rounds self to an optionally given precision" do + 5.5.round(0).should eql(6) + 5.7.round(1).should eql(5.7) + 1.2345678.round(2).should == 1.23 + 123456.78.round(-2).should eql(123500) # rounded up + -123456.78.round(-2).should eql(-123500) + 12.345678.round(3.999).should == 12.346 + end + + it "returns zero when passed a negative argument with magitude greater the magitude of the whole number portion of the Float" do + 0.8346268.round(-1).should eql(0) + end + + it "raises a TypeError when its argument can not be converted to an Integer" do + lambda { 1.0.round("4") }.should raise_error(TypeError) + lambda { 1.0.round(nil) }.should raise_error(TypeError) + end + + it "raises FloatDomainError for exceptional values when passed a non-positive precision" do + lambda { Float::INFINITY.round( 0) }.should raise_error(FloatDomainError) + lambda { Float::INFINITY.round(-2) }.should raise_error(FloatDomainError) + lambda { (-Float::INFINITY).round( 0) }.should raise_error(FloatDomainError) + lambda { (-Float::INFINITY).round(-2) }.should raise_error(FloatDomainError) + end + + it "raises RangeError for NAN when passed a non-positive precision" do + lambda { Float::NAN.round(0) }.should raise_error(RangeError) + lambda { Float::NAN.round(-2) }.should raise_error(RangeError) + end + + it "returns self for exceptional values when passed a non-negative precision" do + Float::INFINITY.round(2).should == Float::INFINITY + (-Float::INFINITY).round(2).should == -Float::INFINITY + Float::NAN.round(2).should be_nan + end + + # redmine:5227 + it "works for corner cases" do + 42.0.round(308).should eql(42.0) + 1.0e307.round(2).should eql(1.0e307) + end + + # redmine:5271 + it "returns rounded values for big argument" do + 0.42.round(2.0**30).should == 0.42 + end + + it "returns big values rounded to nearest" do + +2.5e20.round(-20).should eql( +3 * 10 ** 20 ) + -2.5e20.round(-20).should eql( -3 * 10 ** 20 ) + end + + # redmine #5272 + it "returns rounded values for big values" do + +2.4e20.round(-20).should eql( +2 * 10 ** 20 ) + -2.4e20.round(-20).should eql( -2 * 10 ** 20 ) + +2.5e200.round(-200).should eql( +3 * 10 ** 200 ) + +2.4e200.round(-200).should eql( +2 * 10 ** 200 ) + -2.5e200.round(-200).should eql( -3 * 10 ** 200 ) + -2.4e200.round(-200).should eql( -2 * 10 ** 200 ) + end +end diff --git a/spec/rubyspec/core/float/shared/abs.rb b/spec/rubyspec/core/float/shared/abs.rb new file mode 100644 index 0000000000..c59198bfe2 --- /dev/null +++ b/spec/rubyspec/core/float/shared/abs.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :float_abs, shared: true do + it "returns the absolute value" do + -99.1.send(@method).should be_close(99.1, TOLERANCE) + 4.5.send(@method).should be_close(4.5, TOLERANCE) + 0.0.send(@method).should be_close(0.0, TOLERANCE) + end + + it "returns 0.0 if -0.0" do + (-0.0).send(@method).should be_positive_zero + end + + it "returns Infinity if -Infinity" do + (-infinity_value).send(@method).infinite?.should == 1 + end + + it "returns NaN if NaN" do + nan_value.send(@method).nan?.should be_true + end +end diff --git a/spec/rubyspec/core/float/shared/equal.rb b/spec/rubyspec/core/float/shared/equal.rb new file mode 100644 index 0000000000..668aa069b5 --- /dev/null +++ b/spec/rubyspec/core/float/shared/equal.rb @@ -0,0 +1,17 @@ +describe :float_equal, shared: true do + it "returns true if self has the same value as other" do + 1.0.send(@method, 1).should == true + 2.71828.send(@method, 1.428).should == false + -4.2.send(@method, 4.2).should == false + end + + it "calls 'other == self' if coercion fails" do + x = mock('other') + def x.==(other) + 2.0 == other + end + + 1.0.send(@method, x).should == false + 2.0.send(@method, x).should == true + end +end diff --git a/spec/rubyspec/core/float/shared/modulo.rb b/spec/rubyspec/core/float/shared/modulo.rb new file mode 100644 index 0000000000..9ac967389c --- /dev/null +++ b/spec/rubyspec/core/float/shared/modulo.rb @@ -0,0 +1,42 @@ +describe :float_modulo, shared: true do + it "returns self modulo other" do + 6543.21.send(@method, 137).should be_close(104.21, TOLERANCE) + 5667.19.send(@method, bignum_value).should be_close(5667.19, TOLERANCE) + 6543.21.send(@method, 137.24).should be_close(92.9299999999996, TOLERANCE) + + -1.0.send(@method, 1).should == 0 + end + + it "returns self when modulus is +Infinity" do + 4.2.send(@method, Float::INFINITY).should == 4.2 + end + + it "returns -Infinity when modulus is -Infinity" do + 4.2.send(@method, -Float::INFINITY).should == -Float::INFINITY + end + + it "returns NaN when called on NaN or Infinities" do + Float::NAN.send(@method, 42).should be_nan + Float::INFINITY.send(@method, 42).should be_nan + (-Float::INFINITY).send(@method, 42).should be_nan + end + + it "returns NaN when modulus is NaN" do + 4.2.send(@method, Float::NAN).should be_nan + end + + it "returns -0.0 when called on -0.0 with a non zero modulus" do + r = (-0.0).send(@method, 42) + r.should == 0 + (1/r).should < 0 + + r = (-0.0).send(@method, Float::INFINITY) + r.should == 0 + (1/r).should < 0 + end + + it "raises a ZeroDivisionError if other is zero" do + lambda { 1.0.send(@method, 0) }.should raise_error(ZeroDivisionError) + lambda { 1.0.send(@method, 0.0) }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/core/float/shared/quo.rb b/spec/rubyspec/core/float/shared/quo.rb new file mode 100644 index 0000000000..afc921a2c1 --- /dev/null +++ b/spec/rubyspec/core/float/shared/quo.rb @@ -0,0 +1,59 @@ +describe :float_quo, shared: true do + it "performs floating-point division between self and a Fixnum" do + 8.9.send(@method, 7).should == 1.2714285714285716 + end + + it "performs floating-point division between self and a Bignum" do + 8.9.send(@method, 9999999999999**9).should == 8.900000000008011e-117 + end + + it "performs floating-point division between self and a Float" do + 2827.22.send(@method, 872.111111).should == 3.2418116961704433 + end + + it "returns NaN when the argument is NaN" do + -1819.999999.send(@method, nan_value).nan?.should be_true + 11109.1981271.send(@method, nan_value).nan?.should be_true + end + + it "returns Infinity when the argument is 0.0" do + 2827.22.send(@method, 0.0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0.0 and self is negative" do + -48229.282.send(@method, 0.0).infinite?.should == -1 + end + + it "returns Infinity when the argument is 0" do + 2827.22.send(@method, 0).infinite?.should == 1 + end + + it "returns -Infinity when the argument is 0 and self is negative" do + -48229.282.send(@method, 0).infinite?.should == -1 + end + + it "returns 0.0 when the argument is Infinity" do + 47292.2821.send(@method, infinity_value).should == 0.0 + end + + it "returns -0.0 when the argument is -Infinity" do + 1.9999918.send(@method, -infinity_value).should == -0.0 + end + + it "performs floating-point division between self and a Rational" do + 74620.09.send(@method, Rational(2,3)).should == 111930.135 + end + + it "performs floating-point division between self and a Complex" do + 74620.09.send(@method, Complex(8,2)).should == Complex( + 8778.834117647059, -2194.7085294117646) + end + + it "raises a TypeError when argument isn't numeric" do + lambda { 27292.2.send(@method, mock('non-numeric')) }.should raise_error(TypeError) + end + + it "raises an ArgumentError when passed multiple arguments" do + lambda { 272.221.send(@method, 6,0.2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/float/shared/to_i.rb b/spec/rubyspec/core/float/shared/to_i.rb new file mode 100644 index 0000000000..960295f095 --- /dev/null +++ b/spec/rubyspec/core/float/shared/to_i.rb @@ -0,0 +1,10 @@ +describe :float_to_i, shared: true do + it "returns self truncated to an Integer" do + 899.2.send(@method).should eql(899) + -1.122256e-45.send(@method).should eql(0) + 5_213_451.9201.send(@method).should eql(5213451) + 1.233450999123389e+12.send(@method).should eql(1233450999123) + -9223372036854775808.1.send(@method).should eql(-9223372036854775808) + 9223372036854775808.1.send(@method).should eql(9223372036854775808) + end +end diff --git a/spec/rubyspec/core/float/to_f_spec.rb b/spec/rubyspec/core/float/to_f_spec.rb new file mode 100644 index 0000000000..02666ae7d7 --- /dev/null +++ b/spec/rubyspec/core/float/to_f_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#to_f" do + it "returns self" do + -500.3.to_f.should == -500.3 + 267.51.to_f.should == 267.51 + 1.1412.to_f.should == 1.1412 + end +end diff --git a/spec/rubyspec/core/float/to_i_spec.rb b/spec/rubyspec/core/float/to_i_spec.rb new file mode 100644 index 0000000000..5bf5a1b42a --- /dev/null +++ b/spec/rubyspec/core/float/to_i_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Float#to_i" do + it_behaves_like(:float_to_i, :to_i) +end diff --git a/spec/rubyspec/core/float/to_int_spec.rb b/spec/rubyspec/core/float/to_int_spec.rb new file mode 100644 index 0000000000..ba31ebc168 --- /dev/null +++ b/spec/rubyspec/core/float/to_int_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Float#to_int" do + it_behaves_like(:float_to_i, :to_int) +end diff --git a/spec/rubyspec/core/float/to_r_spec.rb b/spec/rubyspec/core/float/to_r_spec.rb new file mode 100644 index 0000000000..a0f5cd9700 --- /dev/null +++ b/spec/rubyspec/core/float/to_r_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#to_r" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/float/to_s_spec.rb b/spec/rubyspec/core/float/to_s_spec.rb new file mode 100644 index 0000000000..e76cfc1b7e --- /dev/null +++ b/spec/rubyspec/core/float/to_s_spec.rb @@ -0,0 +1,120 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#to_s" do + it "returns 'NaN' for NaN" do + nan_value().to_s.should == 'NaN' + end + + it "returns 'Infinity' for positive infinity" do + infinity_value().to_s.should == 'Infinity' + end + + it "returns '-Infinity' for negative infinity" do + (-infinity_value()).to_s.should == '-Infinity' + end + + it "returns '0.0' for 0.0" do + 0.0.to_s.should == "0.0" + end + + platform_is_not :openbsd do + it "emits '-' for -0.0" do + -0.0.to_s.should == "-0.0" + end + end + + it "emits a '-' for negative values" do + -3.14.to_s.should == "-3.14" + end + + it "emits a trailing '.0' for a whole number" do + 50.0.to_s.should == "50.0" + end + + it "emits a trailing '.0' for the mantissa in e format" do + 1.0e20.to_s.should == "1.0e+20" + end + + it "uses non-e format for a positive value with fractional part having 5 significant figures" do + 0.0001.to_s.should == "0.0001" + end + + it "uses non-e format for a negative value with fractional part having 5 significant figures" do + -0.0001.to_s.should == "-0.0001" + end + + it "uses e format for a positive value with fractional part having 6 significant figures" do + 0.00001.to_s.should == "1.0e-05" + end + + it "uses e format for a negative value with fractional part having 6 significant figures" do + -0.00001.to_s.should == "-1.0e-05" + end + + it "uses non-e format for a positive value with whole part having 15 significant figures" do + 10000000000000.0.to_s.should == "10000000000000.0" + end + + it "uses non-e format for a negative value with whole part having 15 significant figures" do + -10000000000000.0.to_s.should == "-10000000000000.0" + end + + it "uses non-e format for a positive value with whole part having 16 significant figures" do + 100000000000000.0.to_s.should == "100000000000000.0" + end + + it "uses non-e format for a negative value with whole part having 16 significant figures" do + -100000000000000.0.to_s.should == "-100000000000000.0" + end + + it "uses e format for a positive value with whole part having 18 significant figures" do + 10000000000000000.0.to_s.should == "1.0e+16" + end + + it "uses e format for a negative value with whole part having 18 significant figures" do + -10000000000000000.0.to_s.should == "-1.0e+16" + end + + it "uses non-e format for a positive value with whole part having 17 significant figures" do + 1000000000000000.0.to_s.should == "1.0e+15" + end + + it "uses non-e format for a negative value with whole part having 17 significant figures" do + -1000000000000000.0.to_s.should == "-1.0e+15" + end + + # #3273 + it "outputs the minimal, unique form necessary to recreate the value" do + value = 0.21611564636388508 + string = "0.21611564636388508" + + value.to_s.should == string + string.to_f.should == value + end + + it "outputs the minimal, unique form to represent the value" do + 0.56.to_s.should == "0.56" + end +end + +with_feature :encoding do + describe "Float#to_s" do + before :each do + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @internal + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do + Encoding.default_internal = nil + 1.23.to_s.encoding.should equal(Encoding::US_ASCII) + end + + it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do + Encoding.default_internal = Encoding::IBM437 + 5.47.to_s.encoding.should equal(Encoding::US_ASCII) + end + end +end diff --git a/spec/rubyspec/core/float/truncate_spec.rb b/spec/rubyspec/core/float/truncate_spec.rb new file mode 100644 index 0000000000..85ee8ef580 --- /dev/null +++ b/spec/rubyspec/core/float/truncate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Float#truncate" do + it_behaves_like(:float_to_i, :truncate) +end diff --git a/spec/rubyspec/core/float/uminus_spec.rb b/spec/rubyspec/core/float/uminus_spec.rb new file mode 100644 index 0000000000..70e2ff0580 --- /dev/null +++ b/spec/rubyspec/core/float/uminus_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#-@" do + it "negates self" do + (2.221.send(:-@)).should be_close(-2.221, TOLERANCE) + -2.01.should be_close(-2.01,TOLERANCE) + -2_455_999_221.5512.should be_close(-2455999221.5512, TOLERANCE) + (--5.5).should be_close(5.5, TOLERANCE) + -8.551.send(:-@).should be_close(8.551, TOLERANCE) + end + + it "negates self at Float boundaries" do + Float::MAX.send(:-@).should be_close(0.0 - Float::MAX, TOLERANCE) + Float::MIN.send(:-@).should be_close(0.0 - Float::MIN, TOLERANCE) + end + + it "returns negative infinity for positive infinity" do + infinity_value.send(:-@).infinite?.should == -1 + end + + it "returns positive infinity for negative infinity" do + (-infinity_value).send(:-@).infinite?.should == 1 + end + + it "returns NaN for NaN" do + nan_value.send(:-@).nan?.should == true + end +end diff --git a/spec/rubyspec/core/float/uplus_spec.rb b/spec/rubyspec/core/float/uplus_spec.rb new file mode 100644 index 0000000000..75f18191dd --- /dev/null +++ b/spec/rubyspec/core/float/uplus_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#+@" do + it "returns the same value with same sign (twos complement)" do + 34.56.send(:+@).should == 34.56 + -34.56.send(:+@).should == -34.56 + 0.0.send(:+@).should eql(0.0) + end +end diff --git a/spec/rubyspec/core/float/zero_spec.rb b/spec/rubyspec/core/float/zero_spec.rb new file mode 100644 index 0000000000..3a67551a07 --- /dev/null +++ b/spec/rubyspec/core/float/zero_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Float#zero?" do + it "returns true if self is 0.0" do + 0.0.zero?.should == true + 1.0.zero?.should == false + -1.0.zero?.should == false + end +end diff --git a/spec/rubyspec/core/gc/count_spec.rb b/spec/rubyspec/core/gc/count_spec.rb new file mode 100644 index 0000000000..3caa2c9aac --- /dev/null +++ b/spec/rubyspec/core/gc/count_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "GC.count" do + it "returns an integer" do + GC.count.should be_kind_of(Integer) + end +end diff --git a/spec/rubyspec/core/gc/disable_spec.rb b/spec/rubyspec/core/gc/disable_spec.rb new file mode 100644 index 0000000000..9a8a8f23e9 --- /dev/null +++ b/spec/rubyspec/core/gc/disable_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "GC.disable" do + after :each do + GC.enable + end + + it "returns true iff the garbage collection was previously disabled" do + GC.enable + GC.disable.should == false + GC.disable.should == true + GC.disable.should == true + GC.enable + GC.disable.should == false + GC.disable.should == true + end + +end diff --git a/spec/rubyspec/core/gc/enable_spec.rb b/spec/rubyspec/core/gc/enable_spec.rb new file mode 100644 index 0000000000..d3581ad4d8 --- /dev/null +++ b/spec/rubyspec/core/gc/enable_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "GC.enable" do + + it "returns true iff the garbage collection was already disabled" do + GC.enable + GC.enable.should == false + GC.disable + GC.enable.should == true + GC.enable.should == false + end + +end diff --git a/spec/rubyspec/core/gc/garbage_collect_spec.rb b/spec/rubyspec/core/gc/garbage_collect_spec.rb new file mode 100644 index 0000000000..2b47fdf927 --- /dev/null +++ b/spec/rubyspec/core/gc/garbage_collect_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "GC#garbage_collect" do + + before :each do + @obj = Object.new + @obj.extend(GC) + end + + it "always returns nil" do + @obj.garbage_collect.should == nil + @obj.garbage_collect.should == nil + end + +end diff --git a/spec/rubyspec/core/gc/profiler/clear_spec.rb b/spec/rubyspec/core/gc/profiler/clear_spec.rb new file mode 100644 index 0000000000..47a52a5800 --- /dev/null +++ b/spec/rubyspec/core/gc/profiler/clear_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "GC::Profiler.clear" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/gc/profiler/disable_spec.rb b/spec/rubyspec/core/gc/profiler/disable_spec.rb new file mode 100644 index 0000000000..9b8cedb1d5 --- /dev/null +++ b/spec/rubyspec/core/gc/profiler/disable_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "GC::Profiler.disable" do + before do + @status = GC::Profiler.enabled? + end + + after do + @status ? GC::Profiler.enable : GC::Profiler.disable + end + + it "disables the profiler" do + GC::Profiler.disable + GC::Profiler.enabled?.should == false + end +end diff --git a/spec/rubyspec/core/gc/profiler/enable_spec.rb b/spec/rubyspec/core/gc/profiler/enable_spec.rb new file mode 100644 index 0000000000..49e6fc09e0 --- /dev/null +++ b/spec/rubyspec/core/gc/profiler/enable_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "GC::Profiler.enable" do + + before do + @status = GC::Profiler.enabled? + end + + after do + @status ? GC::Profiler.enable : GC::Profiler.disable + end + + it "enables the profiler" do + GC::Profiler.enable + GC::Profiler.enabled?.should == true + end +end diff --git a/spec/rubyspec/core/gc/profiler/enabled_spec.rb b/spec/rubyspec/core/gc/profiler/enabled_spec.rb new file mode 100644 index 0000000000..12cf3173f4 --- /dev/null +++ b/spec/rubyspec/core/gc/profiler/enabled_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "GC::Profiler.enabled?" do + before do + @status = GC::Profiler.enabled? + end + + after do + @status ? GC::Profiler.enable : GC::Profiler.disable + end + + it "reports as enabled when enabled" do + GC::Profiler.enable + GC::Profiler.enabled?.should be_true + end + + it "reports as disabled when disabled" do + GC::Profiler.disable + GC::Profiler.enabled?.should be_false + end +end diff --git a/spec/rubyspec/core/gc/profiler/report_spec.rb b/spec/rubyspec/core/gc/profiler/report_spec.rb new file mode 100644 index 0000000000..52e5e154ce --- /dev/null +++ b/spec/rubyspec/core/gc/profiler/report_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "GC::Profiler.report" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/gc/profiler/result_spec.rb b/spec/rubyspec/core/gc/profiler/result_spec.rb new file mode 100644 index 0000000000..395cf18400 --- /dev/null +++ b/spec/rubyspec/core/gc/profiler/result_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "GC::Profiler.result" do + it "returns a string" do + GC::Profiler.result.should be_kind_of(String) + end +end diff --git a/spec/rubyspec/core/gc/profiler/total_time_spec.rb b/spec/rubyspec/core/gc/profiler/total_time_spec.rb new file mode 100644 index 0000000000..60fe8f182e --- /dev/null +++ b/spec/rubyspec/core/gc/profiler/total_time_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "GC::Profiler.total_time" do + it "returns an float" do + GC::Profiler.total_time.should be_kind_of(Float) + end +end diff --git a/spec/rubyspec/core/gc/start_spec.rb b/spec/rubyspec/core/gc/start_spec.rb new file mode 100644 index 0000000000..dd24b5d6c6 --- /dev/null +++ b/spec/rubyspec/core/gc/start_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "GC.start" do + it "always returns nil" do + GC.start.should == nil + GC.start.should == nil + end +end diff --git a/spec/rubyspec/core/gc/stress_spec.rb b/spec/rubyspec/core/gc/stress_spec.rb new file mode 100644 index 0000000000..5730848895 --- /dev/null +++ b/spec/rubyspec/core/gc/stress_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "GC.stress" do + after :each do + # make sure that we never leave these tests in stress enabled GC! + GC.stress = false + end + + it "returns current status of GC stress mode" do + GC.stress.should be_false + GC.stress = true + GC.stress.should be_true + GC.stress = false + GC.stress.should be_false + end +end + +describe "GC.stress=" do + after :each do + GC.stress = false + end + + it "sets the stress mode" do + GC.stress = true + GC.stress.should be_true + end +end diff --git a/spec/rubyspec/core/hash/allocate_spec.rb b/spec/rubyspec/core/hash/allocate_spec.rb new file mode 100644 index 0000000000..d607f235bd --- /dev/null +++ b/spec/rubyspec/core/hash/allocate_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Hash.allocate" do + it "returns an instance of Hash" do + hsh = Hash.allocate + hsh.should be_an_instance_of(Hash) + end + + it "returns a fully-formed instance of Hash" do + hsh = Hash.allocate + hsh.size.should == 0 + hsh[:a] = 1 + hsh.should == { a: 1 } + end +end diff --git a/spec/rubyspec/core/hash/any_spec.rb b/spec/rubyspec/core/hash/any_spec.rb new file mode 100644 index 0000000000..ab8d320c18 --- /dev/null +++ b/spec/rubyspec/core/hash/any_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Hash#any?" do + describe 'with no block given' do + it "checks if there are any members of a Hash" do + empty_hash = {} + empty_hash.any?.should == false + + hash_with_members = { 'key' => 'value' } + hash_with_members.any?.should == true + end + end + + describe 'with a block given' do + it 'is false if the hash is empty' do + empty_hash = {} + empty_hash.any? {|k,v| 1 == 1 }.should == false + end + + it 'is true if the block returns true for any member of the hash' do + hash_with_members = { 'a' => false, 'b' => false, 'c' => true, 'd' => false } + hash_with_members.any? {|k,v| v == true}.should == true + end + + it 'is false if the block returns false for all members of the hash' do + hash_with_members = { 'a' => false, 'b' => false, 'c' => true, 'd' => false } + hash_with_members.any? {|k,v| v == 42}.should == false + end + end +end diff --git a/spec/rubyspec/core/hash/assoc_spec.rb b/spec/rubyspec/core/hash/assoc_spec.rb new file mode 100644 index 0000000000..0b10720b6f --- /dev/null +++ b/spec/rubyspec/core/hash/assoc_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Hash#assoc" do + before :each do + @h = {apple: :green, orange: :orange, grape: :green, banana: :yellow} + end + + it "returns an Array if the argument is == to a key of the Hash" do + @h.assoc(:apple).should be_an_instance_of(Array) + end + + it "returns a 2-element Array if the argument is == to a key of the Hash" do + @h.assoc(:grape).size.should == 2 + end + + it "sets the first element of the Array to the located key" do + @h.assoc(:banana).first.should == :banana + end + + it "sets the last element of the Array to the value of the located key" do + @h.assoc(:banana).last.should == :yellow + end + + it "only returns the first matching key-value pair for identity hashes" do + # Avoid literal String keys in Hash#[]= due to https://bugs.ruby-lang.org/issues/12855 + h = {}.compare_by_identity + k1 = 'pear' + h[k1] = :red + k2 = 'pear' + h[k2] = :green + h.size.should == 2 + h.keys.grep(/pear/).size.should == 2 + h.assoc('pear').should == ['pear', :red] + end + + it "uses #== to compare the argument to the keys" do + @h[1.0] = :value + 1.should == 1.0 + @h.assoc(1).should == [1.0, :value] + end + + it "returns nil if the argument is not a key of the Hash" do + @h.assoc(:green).should be_nil + end + + it "returns nil if the argument is not a key of the Hash even when there is a default" do + Hash.new(42).merge!( foo: :bar ).assoc(42).should be_nil + Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).assoc(42).should be_nil + end +end diff --git a/spec/rubyspec/core/hash/clear_spec.rb b/spec/rubyspec/core/hash/clear_spec.rb new file mode 100644 index 0000000000..ea8235451a --- /dev/null +++ b/spec/rubyspec/core/hash/clear_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#clear" do + it "removes all key, value pairs" do + h = { 1 => 2, 3 => 4 } + h.clear.should equal(h) + h.should == {} + end + + it "does not remove default values" do + h = Hash.new(5) + h.clear + h.default.should == 5 + + h = { "a" => 100, "b" => 200 } + h.default = "Go fish" + h.clear + h["z"].should == "Go fish" + end + + it "does not remove default procs" do + h = Hash.new { 5 } + h.clear + h.default_proc.should_not == nil + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.clear }.should raise_error(RuntimeError) + lambda { HashSpecs.empty_frozen_hash.clear }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/clone_spec.rb b/spec/rubyspec/core/hash/clone_spec.rb new file mode 100644 index 0000000000..188ae1c807 --- /dev/null +++ b/spec/rubyspec/core/hash/clone_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Hash#clone" do + it "copies instance variable but not the objects they refer to" do + hash = { 'key' => 'value' } + + clone = hash.clone + + clone.should == hash + clone.object_id.should_not == hash.object_id + end +end + diff --git a/spec/rubyspec/core/hash/compact_spec.rb b/spec/rubyspec/core/hash/compact_spec.rb new file mode 100644 index 0000000000..d9ef8a2987 --- /dev/null +++ b/spec/rubyspec/core/hash/compact_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.4" do + describe "Hash#compact" do + before :each do + @hash = { truthy: true, false: false, nil: nil, nil => true } + @initial_pairs = @hash.dup + @compact = { truthy: true, false: false, nil => true } + end + + it "returns new object that rejects pair has nil value" do + ret = @hash.compact + ret.should_not equal(@hash) + ret.should == @compact + end + + it "keeps own pairs" do + @hash.compact + @hash.should == @initial_pairs + end + end + + describe "Hash#compact!" do + before :each do + @hash = { truthy: true, false: false, nil: nil, nil => true } + @initial_pairs = @hash.dup + @compact = { truthy: true, false: false, nil => true } + end + + it "returns self" do + @hash.compact!.should equal(@hash) + end + + it "rejects own pair has nil value" do + @hash.compact! + @hash.should == @compact + end + + context "when each pair does not have nil value" do + before :each do + @hash.compact! + end + + it "returns nil" do + @hash.compact!.should be_nil + end + end + + describe "on frozen instance" do + before :each do + @hash.freeze + end + + it "keeps pairs and raises a RuntimeError" do + ->{ @hash.compact! }.should raise_error(RuntimeError) + @hash.should == @initial_pairs + end + end + end +end diff --git a/spec/rubyspec/core/hash/compare_by_identity_spec.rb b/spec/rubyspec/core/hash/compare_by_identity_spec.rb new file mode 100644 index 0000000000..a518e0cdb3 --- /dev/null +++ b/spec/rubyspec/core/hash/compare_by_identity_spec.rb @@ -0,0 +1,140 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#compare_by_identity" do + before :each do + @h = {} + @idh = {}.compare_by_identity + end + + it "causes future comparisons on the receiver to be made by identity" do + @h[[1]] = :a + @h[[1]].should == :a + @h.compare_by_identity + @h[[1].dup].should be_nil + end + + it "rehashes internally so that old keys can be looked up" do + h = {} + (1..10).each { |k| h[k] = k } + o = Object.new + def o.hash; 123; end + h[o] = 1 + h.compare_by_identity + h[o].should == 1 + end + + it "returns self" do + h = {} + h[:foo] = :bar + h.compare_by_identity.should equal h + end + + it "has no effect on an already compare_by_identity hash" do + @idh[:foo] = :bar + @idh.compare_by_identity.should equal @idh + @idh.compare_by_identity?.should == true + @idh[:foo].should == :bar + end + + it "uses the semantics of BasicObject#equal? to determine key identity" do + [1].should_not equal([1]) + @idh[[1]] = :c + @idh[[1]] = :d + :bar.should equal(:bar) + @idh[:bar] = :e + @idh[:bar] = :f + @idh.values.should == [:c, :d, :f] + end + + it "uses #equal? semantics, but doesn't actually call #equal? to determine identity" do + obj = mock('equal') + obj.should_not_receive(:equal?) + @idh[:foo] = :glark + @idh[obj] = :a + @idh[obj].should == :a + end + + it "does not call #hash on keys" do + key = HashSpecs::ByIdentityKey.new + @idh[key] = 1 + @idh[key].should == 1 + end + + it "regards #dup'd objects as having different identities" do + key = ['foo'] + @idh[key.dup] = :str + @idh[key].should be_nil + end + + it "regards #clone'd objects as having different identities" do + key = ['foo'] + @idh[key.clone] = :str + @idh[key].should be_nil + end + + it "regards references to the same object as having the same identity" do + o = Object.new + @h[o] = :o + @h[:a] = :a + @h[o].should == :o + end + + it "raises a RuntimeError on frozen hashes" do + @h = @h.freeze + lambda { @h.compare_by_identity }.should raise_error(RuntimeError) + end + + # Behaviour confirmed in bug #1871 + it "persists over #dups" do + @idh['foo'] = :bar + @idh['foo'] = :glark + @idh.dup.should == @idh + @idh.dup.size.should == @idh.size + end + + it "persists over #clones" do + @idh['foo'] = :bar + @idh['foo'] = :glark + @idh.clone.should == @idh + @idh.clone.size.should == @idh.size + end + + it "does not copy string keys" do + foo = 'foo' + @idh[foo] = true + @idh[foo] = true + @idh.size.should == 1 + @idh.keys.first.object_id.should == foo.object_id + end + + ruby_bug "#12855", "2.2.0"..."2.4.1" do + it "gives different identity for string literals" do + @idh['foo'] = 1 + @idh['foo'] = 2 + @idh.values.should == [1, 2] + @idh.size.should == 2 + end + end +end + +describe "Hash#compare_by_identity?" do + it "returns false by default" do + h = {} + h.compare_by_identity?.should be_false + end + + it "returns true once #compare_by_identity has been invoked on self" do + h = {} + h.compare_by_identity + h.compare_by_identity?.should be_true + end + + it "returns true when called multiple times on the same ident hash" do + h = {} + h.compare_by_identity + h.compare_by_identity?.should be_true + h.compare_by_identity?.should be_true + h.compare_by_identity?.should be_true + end +end diff --git a/spec/rubyspec/core/hash/constructor_spec.rb b/spec/rubyspec/core/hash/constructor_spec.rb new file mode 100644 index 0000000000..0f582d91de --- /dev/null +++ b/spec/rubyspec/core/hash/constructor_spec.rb @@ -0,0 +1,110 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash.[]" do + describe "passed zero arguments" do + it "returns an empty hash" do + Hash[].should == {} + end + end + + it "creates a Hash; values can be provided as the argument list" do + Hash[:a, 1, :b, 2].should == { a: 1, b: 2 } + Hash[].should == {} + Hash[:a, 1, :b, { c: 2 }].should == { a: 1, b: { c: 2 } } + end + + it "creates a Hash; values can be provided as one single hash" do + Hash[a: 1, b: 2].should == { a: 1, b: 2 } + Hash[{1 => 2, 3 => 4}].should == {1 => 2, 3 => 4} + Hash[{}].should == {} + end + + describe "passed an array" do + it "treats elements that are 2 element arrays as key and value" do + Hash[[[:a, :b], [:c, :d]]].should == { a: :b, c: :d } + end + + it "treats elements that are 1 element arrays as keys with value nil" do + Hash[[[:a]]].should == { a: nil } + end + end + + # #1000 #1385 + it "creates a Hash; values can be provided as a list of value-pairs in an array" do + Hash[[[:a, 1], [:b, 2]]].should == { a: 1, b: 2 } + end + + it "coerces a single argument which responds to #to_ary" do + ary = mock('to_ary') + ary.should_receive(:to_ary).and_return([[:a, :b]]) + + Hash[ary].should == { a: :b } + end + + it "ignores elements that are not arrays" do + -> { + Hash[[:a]].should == {} + }.should complain(/ignoring wrong elements/) + -> { + Hash[[:nil]].should == {} + }.should complain(/ignoring wrong elements/) + end + + it "raises an ArgumentError for arrays of more than 2 elements" do + lambda{ Hash[[[:a, :b, :c]]].should == {} }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed a list of value-invalid-pairs in an array" do + -> { + -> { + Hash[[[:a, 1], [:b], 42, [:d, 2], [:e, 2, 3], []]] + }.should complain(/ignoring wrong elements/) + }.should raise_error(ArgumentError) + end + + describe "passed a single argument which responds to #to_hash" do + it "coerces it and returns a copy" do + h = { a: :b, c: :d } + to_hash = mock('to_hash') + to_hash.should_receive(:to_hash).and_return(h) + + result = Hash[to_hash] + result.should == h + result.should_not equal(h) + end + end + + it "raises an ArgumentError when passed an odd number of arguments" do + lambda { Hash[1, 2, 3] }.should raise_error(ArgumentError) + lambda { Hash[1, 2, { 3 => 4 }] }.should raise_error(ArgumentError) + end + + it "calls to_hash" do + obj = mock('x') + def obj.to_hash() { 1 => 2, 3 => 4 } end + Hash[obj].should == { 1 => 2, 3 => 4 } + end + + it "returns an instance of a subclass when passed an Array" do + HashSpecs::MyHash[1,2,3,4].should be_an_instance_of(HashSpecs::MyHash) + end + + it "returns instances of subclasses" do + HashSpecs::MyHash[].should be_an_instance_of(HashSpecs::MyHash) + end + + it "returns an instance of the class it's called on" do + Hash[HashSpecs::MyHash[1, 2]].class.should == Hash + HashSpecs::MyHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyHash) + end + + it "does not call #initialize on the subclass instance" do + HashSpecs::MyInitializerHash[Hash[1, 2]].should be_an_instance_of(HashSpecs::MyInitializerHash) + end + + it "removes the default_proc" do + hash = Hash.new { |h, k| h[k] = [] } + Hash[hash].default_proc.should be_nil + end +end diff --git a/spec/rubyspec/core/hash/default_proc_spec.rb b/spec/rubyspec/core/hash/default_proc_spec.rb new file mode 100644 index 0000000000..0bd20d43af --- /dev/null +++ b/spec/rubyspec/core/hash/default_proc_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#default_proc" do + it "returns the block passed to Hash.new" do + h = Hash.new { 'Paris' } + p = h.default_proc + p.call(1).should == 'Paris' + end + + it "returns nil if no block was passed to proc" do + {}.default_proc.should == nil + end +end + +describe "Hash#default_proc=" do + it "replaces the block passed to Hash.new" do + h = Hash.new { 'Paris' } + h.default_proc = Proc.new { 'Montreal' } + p = h.default_proc + p.call(1).should == 'Montreal' + end + + it "uses :to_proc on its argument" do + h = Hash.new { 'Paris' } + obj = mock('to_proc') + obj.should_receive(:to_proc).and_return(Proc.new { 'Montreal' }) + (h.default_proc = obj).should equal(obj) + h[:cool_city].should == 'Montreal' + end + + it "overrides the static default" do + h = Hash.new(42) + h.default_proc = Proc.new { 6 } + h.default.should be_nil + h.default_proc.call.should == 6 + end + + it "raises an error if passed stuff not convertible to procs" do + lambda{{}.default_proc = 42}.should raise_error(TypeError) + end + + it "returns the passed Proc" do + new_proc = Proc.new {} + ({}.default_proc = new_proc).should equal(new_proc) + end + + it "clears the default proc if passed nil" do + h = Hash.new { 'Paris' } + h.default_proc = nil + h.default_proc.should == nil + h[:city].should == nil + end + + it "returns nil if passed nil" do + ({}.default_proc = nil).should be_nil + end + + it "accepts a lambda with an arity of 2" do + h = {} + lambda do + h.default_proc = lambda {|a,b| } + end.should_not raise_error(TypeError) + end + + it "raises a TypeError if passed a lambda with an arity other than 2" do + h = {} + lambda do + h.default_proc = lambda {|a| } + end.should raise_error(TypeError) + lambda do + h.default_proc = lambda {|a,b,c| } + end.should raise_error(TypeError) + end + + it "raises a RuntimeError if self is frozen" do + lambda { {}.freeze.default_proc = Proc.new {} }.should raise_error(RuntimeError) + lambda { {}.freeze.default_proc = nil }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/default_spec.rb b/spec/rubyspec/core/hash/default_spec.rb new file mode 100644 index 0000000000..6c1c7377b7 --- /dev/null +++ b/spec/rubyspec/core/hash/default_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#default" do + it "returns the default value" do + h = Hash.new(5) + h.default.should == 5 + h.default(4).should == 5 + {}.default.should == nil + {}.default(4).should == nil + end + + it "uses the default proc to compute a default value, passing given key" do + h = Hash.new { |*args| args } + h.default(nil).should == [h, nil] + h.default(5).should == [h, 5] + end + + it "calls default proc with nil arg if passed a default proc but no arg" do + h = Hash.new { |*args| args } + h.default.should == nil + end +end + +describe "Hash#default=" do + it "sets the default value" do + h = {} + h.default = 99 + h.default.should == 99 + end + + it "unsets the default proc" do + [99, nil, lambda { 6 }].each do |default| + h = Hash.new { 5 } + h.default_proc.should_not == nil + h.default = default + h.default.should == default + h.default_proc.should == nil + end + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.default = nil }.should raise_error(RuntimeError) + lambda { HashSpecs.empty_frozen_hash.default = nil }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/delete_if_spec.rb b/spec/rubyspec/core/hash/delete_if_spec.rb new file mode 100644 index 0000000000..d739e4fbab --- /dev/null +++ b/spec/rubyspec/core/hash/delete_if_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#delete_if" do + it "yields two arguments: key and value" do + all_args = [] + { 1 => 2, 3 => 4 }.delete_if { |*args| all_args << args } + all_args.sort.should == [[1, 2], [3, 4]] + end + + it "removes every entry for which block is true and returns self" do + h = { a: 1, b: 2, c: 3, d: 4 } + h.delete_if { |k,v| v % 2 == 1 }.should equal(h) + h.should == { b: 2, d: 4 } + end + + it "removes all entries if the block is true" do + h = { a: 1, b: 2, c: 3 } + h.delete_if { |k,v| true }.should equal(h) + h.should == {} + end + + it "processes entries with the same order as each()" do + h = { a: 1, b: 2, c: 3, d: 4 } + + each_pairs = [] + delete_pairs = [] + + h.each_pair { |k,v| each_pairs << [k, v] } + h.delete_if { |k,v| delete_pairs << [k,v] } + + each_pairs.should == delete_pairs + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.delete_if { false } }.should raise_error(RuntimeError) + lambda { HashSpecs.empty_frozen_hash.delete_if { true } }.should raise_error(RuntimeError) + end + + it_behaves_like(:hash_iteration_no_block, :delete_if) + it_behaves_like(:enumeratorized_with_origin_size, :delete_if, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/delete_spec.rb b/spec/rubyspec/core/hash/delete_spec.rb new file mode 100644 index 0000000000..a45b8cd171 --- /dev/null +++ b/spec/rubyspec/core/hash/delete_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#delete" do + it "removes the entry and returns the deleted value" do + h = { a: 5, b: 2 } + h.delete(:b).should == 2 + h.should == { a: 5 } + end + + it "calls supplied block if the key is not found" do + { a: 1, b: 10, c: 100 }.delete(:d) { 5 }.should == 5 + Hash.new(:default).delete(:d) { 5 }.should == 5 + Hash.new { :defualt }.delete(:d) { 5 }.should == 5 + end + + it "returns nil if the key is not found when no block is given" do + { a: 1, b: 10, c: 100 }.delete(:d).should == nil + Hash.new(:default).delete(:d).should == nil + Hash.new { :defualt }.delete(:d).should == nil + end + + # MRI explicitly implements this behavior + it "allows removing a key while iterating" do + h = { a: 1, b: 2 } + visited = [] + h.each_pair { |k,v| + visited << k + h.delete(k) + } + visited.should == [:a, :b] + h.should == {} + end + + it "accepts keys with private #hash method" do + key = HashSpecs::KeyWithPrivateHash.new + { key => 5 }.delete(key).should == 5 + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.delete("foo") }.should raise_error(RuntimeError) + lambda { HashSpecs.empty_frozen_hash.delete("foo") }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/dig_spec.rb b/spec/rubyspec/core/hash/dig_spec.rb new file mode 100644 index 0000000000..237c407e91 --- /dev/null +++ b/spec/rubyspec/core/hash/dig_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.3' do + describe "Hash#dig" do + + it "returns #[] with one arg" do + h = { 0 => false, a: 1 } + h.dig(:a).should == 1 + h.dig(0).should be_false + h.dig(1).should be_nil + end + + it "returns the nested value specified by the sequence of keys" do + h = { foo: { bar: { baz: 1 } } } + h.dig(:foo, :bar, :baz).should == 1 + h.dig(:foo, :bar, :nope).should be_nil + h.dig(:foo, :baz).should be_nil + h.dig(:bar, :baz, :foo).should be_nil + end + + it "returns the nested value specified if the sequence includes an index" do + h = { foo: [1, 2, 3] } + h.dig(:foo, 2).should == 3 + end + + it "returns nil if any intermediate step is nil" do + h = { foo: { bar: { baz: 1 } } } + h.dig(:foo, :zot, :xyz).should == nil + end + + it "raises an ArgumentError if no arguments provided" do + lambda { { the: 'borg' }.dig() }.should raise_error(ArgumentError) + end + + it "handles type-mixed deep digging" do + h = {} + h[:foo] = [ { bar: [ 1 ] }, [ obj = Object.new, 'str' ] ] + def obj.dig(*args); [ 42 ] end + + h.dig(:foo, 0, :bar).should == [ 1 ] + h.dig(:foo, 0, :bar, 0).should == 1 + h.dig(:foo, 1, 1).should == 'str' + # MRI does not recurse values returned from `obj.dig` + h.dig(:foo, 1, 0, 0).should == [ 42 ] + h.dig(:foo, 1, 0, 0, 10).should == [ 42 ] + end + + it "raises TypeError if an intermediate element does not respond to #dig" do + h = {} + h[:foo] = [ { bar: [ 1 ] }, [ nil, 'str' ] ] + lambda { h.dig(:foo, 0, :bar, 0, 0) }.should raise_error(TypeError) + lambda { h.dig(:foo, 1, 1, 0) }.should raise_error(TypeError) + end + + it "calls #dig on the result of #[] with the remaining arguments" do + h = { foo: { bar: { baz: 42 } } } + h[:foo].should_receive(:dig).with(:bar, :baz).and_return(42) + h.dig(:foo, :bar, :baz).should == 42 + end + + it "respects Hash's default" do + default = {bar: 42} + h = Hash.new(default) + h.dig(:foo).should equal default + h.dig(:foo, :bar).should == 42 + end + end +end diff --git a/spec/rubyspec/core/hash/each_key_spec.rb b/spec/rubyspec/core/hash/each_key_spec.rb new file mode 100644 index 0000000000..4a4078a594 --- /dev/null +++ b/spec/rubyspec/core/hash/each_key_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#each_key" do + it "calls block once for each key, passing key" do + r = {} + h = { 1 => -1, 2 => -2, 3 => -3, 4 => -4 } + h.each_key { |k| r[k] = k }.should equal(h) + r.should == { 1 => 1, 2 => 2, 3 => 3, 4 => 4 } + end + + it "processes keys in the same order as keys()" do + keys = [] + h = { 1 => -1, 2 => -2, 3 => -3, 4 => -4 } + h.each_key { |k| keys << k } + keys.should == h.keys + end + + it_behaves_like(:hash_iteration_no_block, :each_key) + it_behaves_like(:enumeratorized_with_origin_size, :each_key, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/each_pair_spec.rb b/spec/rubyspec/core/hash/each_pair_spec.rb new file mode 100644 index 0000000000..285ca01b26 --- /dev/null +++ b/spec/rubyspec/core/hash/each_pair_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../shared/each', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#each_pair" do + it_behaves_like(:hash_each, :each_pair) + it_behaves_like(:hash_iteration_no_block, :each_pair) + it_behaves_like(:enumeratorized_with_origin_size, :each_pair, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/each_spec.rb b/spec/rubyspec/core/hash/each_spec.rb new file mode 100644 index 0000000000..676fd284b9 --- /dev/null +++ b/spec/rubyspec/core/hash/each_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../shared/each', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#each" do + it_behaves_like(:hash_each, :each) + it_behaves_like(:hash_iteration_no_block, :each) + it_behaves_like(:enumeratorized_with_origin_size, :each, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/each_value_spec.rb b/spec/rubyspec/core/hash/each_value_spec.rb new file mode 100644 index 0000000000..d3b2b8692e --- /dev/null +++ b/spec/rubyspec/core/hash/each_value_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#each_value" do + it "calls block once for each key, passing value" do + r = [] + h = { a: -5, b: -3, c: -2, d: -1, e: -1 } + h.each_value { |v| r << v }.should equal(h) + r.sort.should == [-5, -3, -2, -1, -1] + end + + it "processes values in the same order as values()" do + values = [] + h = { a: -5, b: -3, c: -2, d: -1, e: -1 } + h.each_value { |v| values << v } + values.should == h.values + end + + it_behaves_like(:hash_iteration_no_block, :each_value) + it_behaves_like(:enumeratorized_with_origin_size, :each_value, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/element_reference_spec.rb b/spec/rubyspec/core/hash/element_reference_spec.rb new file mode 100644 index 0000000000..b5ca56e84b --- /dev/null +++ b/spec/rubyspec/core/hash/element_reference_spec.rb @@ -0,0 +1,120 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#[]" do + it "returns the value for key" do + obj = mock('x') + h = { 1 => 2, 3 => 4, "foo" => "bar", obj => obj, [] => "baz" } + h[1].should == 2 + h[3].should == 4 + h["foo"].should == "bar" + h[obj].should == obj + h[[]].should == "baz" + end + + it "returns nil as default default value" do + { 0 => 0 }[5].should == nil + end + + it "returns the default (immediate) value for missing keys" do + h = Hash.new(5) + h[:a].should == 5 + h[:a] = 0 + h[:a].should == 0 + h[:b].should == 5 + end + + it "calls subclass implementations of default" do + h = HashSpecs::DefaultHash.new + h[:nothing].should == 100 + end + + it "does not create copies of the immediate default value" do + str = "foo" + h = Hash.new(str) + a = h[:a] + b = h[:b] + a << "bar" + + a.should equal(b) + a.should == "foobar" + b.should == "foobar" + end + + it "returns the default (dynamic) value for missing keys" do + h = Hash.new { |hsh, k| k.kind_of?(Numeric) ? hsh[k] = k + 2 : hsh[k] = k } + h[1].should == 3 + h['this'].should == 'this' + h.should == { 1 => 3, 'this' => 'this' } + + i = 0 + h = Hash.new { |hsh, key| i += 1 } + h[:foo].should == 1 + h[:foo].should == 2 + h[:bar].should == 3 + end + + it "does not return default values for keys with nil values" do + h = Hash.new(5) + h[:a] = nil + h[:a].should == nil + + h = Hash.new { 5 } + h[:a] = nil + h[:a].should == nil + end + + it "compares keys with eql? semantics" do + { 1.0 => "x" }[1].should == nil + { 1.0 => "x" }[1.0].should == "x" + { 1 => "x" }[1.0].should == nil + { 1 => "x" }[1].should == "x" + end + + it "compares key via hash" do + x = mock('0') + x.should_receive(:hash).and_return(0) + + h = {} + # 1.9 only calls #hash if the hash had at least one entry beforehand. + h[:foo] = :bar + h[x].should == nil + end + + it "does not compare keys with different #hash values via #eql?" do + x = mock('x') + x.should_not_receive(:eql?) + x.stub!(:hash).and_return(0) + + y = mock('y') + y.should_not_receive(:eql?) + y.stub!(:hash).and_return(1) + + { y => 1 }[x].should == nil + end + + it "compares keys with the same #hash value via #eql?" do + x = mock('x') + x.should_receive(:eql?).and_return(true) + x.stub!(:hash).and_return(42) + + y = mock('y') + y.should_not_receive(:eql?) + y.stub!(:hash).and_return(42) + + { y => 1 }[x].should == 1 + end + + it "finds a value via an identical key even when its #eql? isn't reflexive" do + x = mock('x') + x.should_receive(:hash).at_least(1).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + { x => :x }[x].should == :x + end + + it "supports keys with private #hash method" do + key = HashSpecs::KeyWithPrivateHash.new + { key => 42 }[key].should == 42 + end +end diff --git a/spec/rubyspec/core/hash/element_set_spec.rb b/spec/rubyspec/core/hash/element_set_spec.rb new file mode 100644 index 0000000000..a2d67c7f22 --- /dev/null +++ b/spec/rubyspec/core/hash/element_set_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/store', __FILE__) + +describe "Hash#[]=" do + it_behaves_like(:hash_store, :[]=) +end diff --git a/spec/rubyspec/core/hash/empty_spec.rb b/spec/rubyspec/core/hash/empty_spec.rb new file mode 100644 index 0000000000..84464f34e1 --- /dev/null +++ b/spec/rubyspec/core/hash/empty_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#empty?" do + it "returns true if the hash has no entries" do + {}.empty?.should == true + { 1 => 1 }.empty?.should == false + end + + it "returns true if the hash has no entries and has a default value" do + Hash.new(5).empty?.should == true + Hash.new { 5 }.empty?.should == true + Hash.new { |hsh, k| hsh[k] = k }.empty?.should == true + end +end diff --git a/spec/rubyspec/core/hash/eql_spec.rb b/spec/rubyspec/core/hash/eql_spec.rb new file mode 100644 index 0000000000..3e22bf1c67 --- /dev/null +++ b/spec/rubyspec/core/hash/eql_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "Hash#eql?" do + it_behaves_like :hash_eql, :eql? + it_behaves_like :hash_eql_additional, :eql? + it_behaves_like :hash_eql_additional_more, :eql? +end diff --git a/spec/rubyspec/core/hash/equal_value_spec.rb b/spec/rubyspec/core/hash/equal_value_spec.rb new file mode 100644 index 0000000000..76e4d796aa --- /dev/null +++ b/spec/rubyspec/core/hash/equal_value_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "Hash#==" do + it_behaves_like :hash_eql, :== + it_behaves_like :hash_eql_additional, :== + it_behaves_like :hash_eql_additional_more, :== + + it "compares values with == semantics" do + l_val = mock("left") + r_val = mock("right") + + l_val.should_receive(:==).with(r_val).and_return(true) + + ({ 1 => l_val } == { 1 => r_val }).should be_true + end +end diff --git a/spec/rubyspec/core/hash/fetch_spec.rb b/spec/rubyspec/core/hash/fetch_spec.rb new file mode 100644 index 0000000000..5e701b1162 --- /dev/null +++ b/spec/rubyspec/core/hash/fetch_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#fetch" do + it "returns the value for key" do + { a: 1, b: -1 }.fetch(:b).should == -1 + end + + it "raises a KeyError if key is not found" do + lambda { {}.fetch(:a) }.should raise_error(KeyError) + lambda { Hash.new(5).fetch(:a) }.should raise_error(KeyError) + lambda { Hash.new { 5 }.fetch(:a) }.should raise_error(KeyError) + end + + it "returns default if key is not found when passed a default" do + {}.fetch(:a, nil).should == nil + {}.fetch(:a, 'not here!').should == "not here!" + { a: nil }.fetch(:a, 'not here!').should == nil + end + + it "returns value of block if key is not found when passed a block" do + {}.fetch('a') { |k| k + '!' }.should == "a!" + end + + it "gives precedence to the default block over the default argument when passed both" do + lambda { + @result = {}.fetch(9, :foo) { |i| i * i } + }.should complain(/block supersedes default value argument/) + @result.should == 81 + end + + it "raises an ArgumentError when not passed one or two arguments" do + lambda { {}.fetch() }.should raise_error(ArgumentError) + lambda { {}.fetch(1, 2, 3) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/hash/fetch_values_spec.rb b/spec/rubyspec/core/hash/fetch_values_spec.rb new file mode 100644 index 0000000000..d6e47d5885 --- /dev/null +++ b/spec/rubyspec/core/hash/fetch_values_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.3" do + describe "Hash#fetch_values" do + before :each do + @hash = { a: 1, b: 2, c: 3 } + end + + describe "with matched keys" do + it "returns the values for keys" do + @hash.fetch_values(:a).should == [1] + @hash.fetch_values(:a, :c).should == [1, 3] + end + end + + describe "with unmatched keys" do + it "raises a KeyError" do + ->{ @hash.fetch_values :z }.should raise_error(KeyError) + ->{ @hash.fetch_values :a, :z }.should raise_error(KeyError) + end + + it "returns the default value from block" do + @hash.fetch_values(:z) { |key| "`#{key}' is not found" }.should == ["`z' is not found"] + @hash.fetch_values(:a, :z) { |key| "`#{key}' is not found" }.should == [1, "`z' is not found"] + end + end + + describe "without keys" do + it "returns an empty Array" do + @hash.fetch_values.should == [] + end + end + end +end diff --git a/spec/rubyspec/core/hash/fixtures/classes.rb b/spec/rubyspec/core/hash/fixtures/classes.rb new file mode 100644 index 0000000000..3d3df576cf --- /dev/null +++ b/spec/rubyspec/core/hash/fixtures/classes.rb @@ -0,0 +1,68 @@ +module HashSpecs + class MyHash < Hash; end + + class MyInitializerHash < Hash + + def initialize + raise "Constructor called" + end + + end + + class NewHash < Hash + def initialize(*args) + args.each_with_index do |val, index| + self[index] = val + end + end + end + + class DefaultHash < Hash + def default(key) + 100 + end + end + + class ToHashHash < Hash + def to_hash + { "to_hash" => "was", "called!" => "duh." } + end + end + + class KeyWithPrivateHash + private :hash + end + + class ByIdentityKey + def hash + fail("#hash should not be called on compare_by_identity Hash") + end + end + + class ByValueKey + attr_reader :n + def initialize(n) + @n = n + end + + def hash + n + end + + def eql? other + ByValueKey === other and @n == other.n + end + end + + def self.empty_frozen_hash + @empty ||= {} + @empty.freeze + @empty + end + + def self.frozen_hash + @hash ||= { 1 => 2, 3 => 4 } + @hash.freeze + @hash + end +end diff --git a/spec/rubyspec/core/hash/flatten_spec.rb b/spec/rubyspec/core/hash/flatten_spec.rb new file mode 100644 index 0000000000..60762f864d --- /dev/null +++ b/spec/rubyspec/core/hash/flatten_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Hash#flatten" do + + before :each do + @h = {plato: :greek, + witgenstein: [:austrian, :british], + russell: :welsh} + end + + it "returns an Array" do + {}.flatten.should be_an_instance_of(Array) + end + + it "returns an empty Array for an empty Hash" do + {}.flatten.should == [] + end + + it "sets each even index of the Array to a key of the Hash" do + a = @h.flatten + a[0].should == :plato + a[2].should == :witgenstein + a[4].should == :russell + end + + it "sets each odd index of the Array to the value corresponding to the previous element" do + a = @h.flatten + a[1].should == :greek + a[3].should == [:austrian, :british] + a[5].should == :welsh + end + + it "does not recursively flatten Array values when called without arguments" do + a = @h.flatten + a[3].should == [:austrian, :british] + end + + it "does not recursively flatten Hash values when called without arguments" do + @h[:russell] = {born: :wales, influenced_by: :mill } + a = @h.flatten + a[5].should_not == {born: :wales, influenced_by: :mill }.flatten + end + + it "recursively flattens Array values when called with an argument >= 2" do + a = @h.flatten(2) + a[3].should == :austrian + a[4].should == :british + end + + it "recursively flattens Array values to the given depth" do + @h[:russell] = [[:born, :wales], [:influenced_by, :mill]] + a = @h.flatten(2) + a[6].should == [:born, :wales] + a[7].should == [:influenced_by, :mill] + end + + it "raises a TypeError if given a non-Integer argument" do + lambda do + @h.flatten(Object.new) + end.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/hash/gt_spec.rb b/spec/rubyspec/core/hash/gt_spec.rb new file mode 100644 index 0000000000..35bf52bf64 --- /dev/null +++ b/spec/rubyspec/core/hash/gt_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/comparison', __FILE__) +require File.expand_path('../shared/greater_than', __FILE__) + +ruby_version_is "2.3" do + describe "Hash#>" do + it_behaves_like :hash_comparison, :> + it_behaves_like :hash_greater_than, :> + + it "returns false if both hashes are identical" do + h = { a: 1, b: 2 } + (h > h).should be_false + end + end + + describe "Hash#>" do + before :each do + @hash = {a:1, b:2} + @bigger = {a:1, b:2, c:3} + @unrelated = {c:3, d:4} + @similar = {a:2, b:3} + end + + it "returns false when receiver size is smaller than argument" do + (@hash > @bigger).should == false + (@unrelated > @bigger).should == false + end + + it "returns false when receiver size is the same as argument" do + (@hash > @hash).should == false + (@hash > @unrelated).should == false + (@unrelated > @hash).should == false + end + + it "returns true when argument is a subset of receiver" do + (@bigger > @hash).should == true + end + + it "returns false when keys match but values don't" do + (@hash > @similar).should == false + (@similar > @hash).should == false + end + end +end diff --git a/spec/rubyspec/core/hash/gte_spec.rb b/spec/rubyspec/core/hash/gte_spec.rb new file mode 100644 index 0000000000..5453440ed6 --- /dev/null +++ b/spec/rubyspec/core/hash/gte_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/comparison', __FILE__) +require File.expand_path('../shared/greater_than', __FILE__) + +ruby_version_is "2.3" do + describe "Hash#>=" do + it_behaves_like :hash_comparison, :>= + it_behaves_like :hash_greater_than, :>= + + it "returns true if both hashes are identical" do + h = { a: 1, b: 2 } + (h >= h).should be_true + end + end + + describe "Hash#>=" do + before :each do + @hash = {a:1, b:2} + @bigger = {a:1, b:2, c:3} + @unrelated = {c:3, d:4} + @similar = {a:2, b:3} + end + + it "returns false when receiver size is smaller than argument" do + (@hash >= @bigger).should == false + (@unrelated >= @bigger).should == false + end + + it "returns false when argument is not a subset or not equals to receiver" do + (@hash >= @unrelated).should == false + (@unrelated >= @hash).should == false + end + + it "returns true when argument is a subset of receiver or equals to receiver" do + (@bigger >= @hash).should == true + (@hash >= @hash).should == true + end + + it "returns false when keys match but values don't" do + (@hash >= @similar).should == false + (@similar >= @hash).should == false + end + end +end diff --git a/spec/rubyspec/core/hash/has_key_spec.rb b/spec/rubyspec/core/hash/has_key_spec.rb new file mode 100644 index 0000000000..1d2aa279f1 --- /dev/null +++ b/spec/rubyspec/core/hash/has_key_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/key', __FILE__) + +describe "Hash#has_key?" do + it_behaves_like(:hash_key_p, :has_key?) +end + diff --git a/spec/rubyspec/core/hash/has_value_spec.rb b/spec/rubyspec/core/hash/has_value_spec.rb new file mode 100644 index 0000000000..dc8fdf9b69 --- /dev/null +++ b/spec/rubyspec/core/hash/has_value_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/value', __FILE__) + +describe "Hash#has_value?" do + it_behaves_like(:hash_value_p, :has_value?) +end + diff --git a/spec/rubyspec/core/hash/hash_spec.rb b/spec/rubyspec/core/hash/hash_spec.rb new file mode 100644 index 0000000000..9d1e984c60 --- /dev/null +++ b/spec/rubyspec/core/hash/hash_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Hash" do + it "includes Enumerable" do + Hash.include?(Enumerable).should == true + end +end + +describe "Hash#hash" do + it "returns a value which doesn't depend on the hash order" do + { 0=>2, 11=>1 }.hash.should == { 11=>1, 0=>2 }.hash + end + + it "generates a hash for recursive hash structures" do + h = {} + h[:a] = h + (h.hash == h[:a].hash).should == true + end + + it "returns the same hash for recursive hashes" do + h = {} ; h[:x] = h + h.hash.should == {x: h}.hash + h.hash.should == {x: {x: h}}.hash + # This is because h.eql?(x: h) + # Remember that if two objects are eql? + # then the need to have the same hash. + # Check the Hash#eql? specs! + end + + it "returns the same hash for recursive hashes through arrays" do + h = {} ; rec = [h] ; h[:x] = rec + h.hash.should == {x: rec}.hash + h.hash.should == {x: [h]}.hash + # Like above, because h.eql?(x: [h]) + end +end diff --git a/spec/rubyspec/core/hash/include_spec.rb b/spec/rubyspec/core/hash/include_spec.rb new file mode 100644 index 0000000000..8731673c19 --- /dev/null +++ b/spec/rubyspec/core/hash/include_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/key', __FILE__) + +describe "Hash#include?" do + it_behaves_like(:hash_key_p, :include?) +end diff --git a/spec/rubyspec/core/hash/index_spec.rb b/spec/rubyspec/core/hash/index_spec.rb new file mode 100644 index 0000000000..28e64f4eb8 --- /dev/null +++ b/spec/rubyspec/core/hash/index_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/index', __FILE__) + +describe "Hash#index" do + it_behaves_like :hash_index, :index +end diff --git a/spec/rubyspec/core/hash/initialize_spec.rb b/spec/rubyspec/core/hash/initialize_spec.rb new file mode 100644 index 0000000000..aa943d333d --- /dev/null +++ b/spec/rubyspec/core/hash/initialize_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#initialize" do + it "is private" do + Hash.should have_private_instance_method("initialize") + end + + it "can be used to reset default_proc" do + h = { "foo" => 1, "bar" => 2 } + h.default_proc.should == nil + h.instance_eval { initialize { |_, k| k * 2 } } + h.default_proc.should_not == nil + h["a"].should == "aa" + end + + it "receives the arguments passed to Hash#new" do + HashSpecs::NewHash.new(:one, :two)[0].should == :one + HashSpecs::NewHash.new(:one, :two)[1].should == :two + end + + it "returns self" do + h = Hash.new + h.send(:initialize).should equal(h) + end + + it "raises a RuntimeError if called on a frozen instance" do + block = lambda { HashSpecs.frozen_hash.instance_eval { initialize() }} + block.should raise_error(RuntimeError) + + block = lambda { HashSpecs.frozen_hash.instance_eval { initialize(nil) } } + block.should raise_error(RuntimeError) + + block = lambda { HashSpecs.frozen_hash.instance_eval { initialize(5) } } + block.should raise_error(RuntimeError) + + block = lambda { HashSpecs.frozen_hash.instance_eval { initialize { 5 } } } + block.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/inspect_spec.rb b/spec/rubyspec/core/hash/inspect_spec.rb new file mode 100644 index 0000000000..b8c42cf269 --- /dev/null +++ b/spec/rubyspec/core/hash/inspect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "Hash#inspect" do + it_behaves_like :hash_to_s, :inspect +end diff --git a/spec/rubyspec/core/hash/invert_spec.rb b/spec/rubyspec/core/hash/invert_spec.rb new file mode 100644 index 0000000000..bc2bf711f7 --- /dev/null +++ b/spec/rubyspec/core/hash/invert_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#invert" do + it "returns a new hash where keys are values and vice versa" do + { 1 => 'a', 2 => 'b', 3 => 'c' }.invert.should == + { 'a' => 1, 'b' => 2, 'c' => 3 } + end + + it "handles collisions by overriding with the key coming later in keys()" do + h = { a: 1, b: 1 } + override_key = h.keys.last + h.invert[1].should == override_key + end + + it "compares new keys with eql? semantics" do + h = { a: 1.0, b: 1 } + i = h.invert + i[1.0].should == :a + i[1].should == :b + end + + it "does not return subclass instances for subclasses" do + HashSpecs::MyHash[1 => 2, 3 => 4].invert.class.should == Hash + HashSpecs::MyHash[].invert.class.should == Hash + end +end diff --git a/spec/rubyspec/core/hash/keep_if_spec.rb b/spec/rubyspec/core/hash/keep_if_spec.rb new file mode 100644 index 0000000000..6b3a1925d3 --- /dev/null +++ b/spec/rubyspec/core/hash/keep_if_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#keep_if" do + it "yields two arguments: key and value" do + all_args = [] + { 1 => 2, 3 => 4 }.keep_if { |*args| all_args << args } + all_args.should == [[1, 2], [3, 4]] + end + + it "keeps every entry for which block is true and returns self" do + h = { a: 1, b: 2, c: 3, d: 4 } + h.keep_if { |k,v| v % 2 == 0 }.should equal(h) + h.should == { b: 2, d: 4 } + end + + it "removes all entries if the block is false" do + h = { a: 1, b: 2, c: 3 } + h.keep_if { |k,v| false }.should equal(h) + h.should == {} + end + + it "returns self even if unmodified" do + h = { 1 => 2, 3 => 4 } + h.keep_if { true }.should equal(h) + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.keep_if { true } }.should raise_error(RuntimeError) + lambda { HashSpecs.empty_frozen_hash.keep_if { false } }.should raise_error(RuntimeError) + end + + it_behaves_like(:hash_iteration_no_block, :keep_if) + it_behaves_like(:enumeratorized_with_origin_size, :keep_if, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/key_spec.rb b/spec/rubyspec/core/hash/key_spec.rb new file mode 100644 index 0000000000..dc78174641 --- /dev/null +++ b/spec/rubyspec/core/hash/key_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/key', __FILE__) +require File.expand_path('../shared/index', __FILE__) + +describe "Hash#key?" do + it_behaves_like(:hash_key_p, :key?) +end + +describe "Hash#key" do + it_behaves_like(:hash_index, :key) +end diff --git a/spec/rubyspec/core/hash/keys_spec.rb b/spec/rubyspec/core/hash/keys_spec.rb new file mode 100644 index 0000000000..bc7855f73b --- /dev/null +++ b/spec/rubyspec/core/hash/keys_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#keys" do + + it "returns an array with the keys in the order they were inserted" do + {}.keys.should == [] + {}.keys.should be_kind_of(Array) + Hash.new(5).keys.should == [] + Hash.new { 5 }.keys.should == [] + { 1 => 2, 4 => 8, 2 => 4 }.keys.should == [1, 4, 2] + { 1 => 2, 2 => 4, 4 => 8 }.keys.should be_kind_of(Array) + { nil => nil }.keys.should == [nil] + end + + it "uses the same order as #values" do + h = { 1 => "1", 2 => "2", 3 => "3", 4 => "4" } + + h.size.times do |i| + h[h.keys[i]].should == h.values[i] + end + end +end diff --git a/spec/rubyspec/core/hash/length_spec.rb b/spec/rubyspec/core/hash/length_spec.rb new file mode 100644 index 0000000000..90751402eb --- /dev/null +++ b/spec/rubyspec/core/hash/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Hash#length" do + it_behaves_like(:hash_length, :length) +end diff --git a/spec/rubyspec/core/hash/lt_spec.rb b/spec/rubyspec/core/hash/lt_spec.rb new file mode 100644 index 0000000000..c32af32017 --- /dev/null +++ b/spec/rubyspec/core/hash/lt_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/comparison', __FILE__) +require File.expand_path('../shared/less_than', __FILE__) + +ruby_version_is "2.3" do + describe "Hash#<" do + it_behaves_like :hash_comparison, :< + it_behaves_like :hash_less_than, :< + + it "returns false if both hashes are identical" do + h = { a: 1, b: 2 } + (h < h).should be_false + end + end + + describe "Hash#<" do + before :each do + @hash = {a:1, b:2} + @bigger = {a:1, b:2, c:3} + @unrelated = {c:3, d:4} + @similar = {a:2, b:3} + end + + it "returns false when receiver size is larger than argument" do + (@bigger < @hash).should == false + (@bigger < @unrelated).should == false + end + + it "returns false when receiver size is the same as argument" do + (@hash < @hash).should == false + (@hash < @unrelated).should == false + (@unrelated < @hash).should == false + end + + it "returns true when receiver is a subset of argument" do + (@hash < @bigger).should == true + end + + it "returns false when keys match but values don't" do + (@hash < @similar).should == false + (@similar < @hash).should == false + end + end +end diff --git a/spec/rubyspec/core/hash/lte_spec.rb b/spec/rubyspec/core/hash/lte_spec.rb new file mode 100644 index 0000000000..1a6926a44a --- /dev/null +++ b/spec/rubyspec/core/hash/lte_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/comparison', __FILE__) +require File.expand_path('../shared/less_than', __FILE__) + +ruby_version_is "2.3" do + describe "Hash#<=" do + it_behaves_like :hash_comparison, :<= + it_behaves_like :hash_less_than, :<= + + it "returns true if both hashes are identical" do + h = { a: 1, b: 2 } + (h <= h).should be_true + end + end + + describe "Hash#<=" do + before :each do + @hash = {a:1, b:2} + @bigger = {a:1, b:2, c:3} + @unrelated = {c:3, d:4} + @similar = {a:2, b:3} + end + + it "returns false when receiver size is larger than argument" do + (@bigger <= @hash).should == false + (@bigger <= @unrelated).should == false + end + + it "returns false when receiver size is the same as argument" do + (@hash <= @unrelated).should == false + (@unrelated <= @hash).should == false + end + + it "returns true when receiver is a subset of argument or equals to argument" do + (@hash <= @bigger).should == true + (@hash <= @hash).should == true + end + + it "returns false when keys match but values don't" do + (@hash <= @similar).should == false + (@similar <= @hash).should == false + end + end +end diff --git a/spec/rubyspec/core/hash/member_spec.rb b/spec/rubyspec/core/hash/member_spec.rb new file mode 100644 index 0000000000..376bb4c006 --- /dev/null +++ b/spec/rubyspec/core/hash/member_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/key', __FILE__) + +describe "Hash#member?" do + it_behaves_like(:hash_key_p, :member?) +end diff --git a/spec/rubyspec/core/hash/merge_spec.rb b/spec/rubyspec/core/hash/merge_spec.rb new file mode 100644 index 0000000000..21401ffd08 --- /dev/null +++ b/spec/rubyspec/core/hash/merge_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Hash#merge" do + it "returns a new hash by combining self with the contents of other" do + h = { 1 => :a, 2 => :b, 3 => :c }.merge(a: 1, c: 2) + h.should == { c: 2, 1 => :a, 2 => :b, a: 1, 3 => :c } + + hash = { a: 1, b: 2 } + {}.merge(hash).should == hash + hash.merge({}).should == hash + + h = { 1 => :a, 2 => :b, 3 => :c }.merge(1 => :b) + h.should == { 1 => :b, 2 => :b, 3 => :c } + + h = { 1 => :a, 2 => :b }.merge(1 => :b, 3 => :c) + h.should == { 1 => :b, 2 => :b, 3 => :c } + end + + it "sets any duplicate key to the value of block if passed a block" do + h1 = { a: 2, b: 1, d: 5 } + h2 = { a: -2, b: 4, c: -3 } + r = h1.merge(h2) { |k,x,y| nil } + r.should == { a: nil, b: nil, c: -3, d: 5 } + + r = h1.merge(h2) { |k,x,y| "#{k}:#{x+2*y}" } + r.should == { a: "a:-2", b: "b:9", c: -3, d: 5 } + + lambda { + h1.merge(h2) { |k, x, y| raise(IndexError) } + }.should raise_error(IndexError) + + r = h1.merge(h1) { |k,x,y| :x } + r.should == { a: :x, b: :x, d: :x } + end + + it "tries to convert the passed argument to a hash using #to_hash" do + obj = mock('{1=>2}') + obj.should_receive(:to_hash).and_return({ 1 => 2 }) + { 3 => 4 }.merge(obj).should == { 1 => 2, 3 => 4 } + end + + it "does not call to_hash on hash subclasses" do + { 3 => 4 }.merge(HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 } + end + + it "returns subclass instance for subclasses" do + HashSpecs::MyHash[1 => 2, 3 => 4].merge({ 1 => 2 }).should be_an_instance_of(HashSpecs::MyHash) + HashSpecs::MyHash[].merge({ 1 => 2 }).should be_an_instance_of(HashSpecs::MyHash) + + { 1 => 2, 3 => 4 }.merge(HashSpecs::MyHash[1 => 2]).class.should == Hash + {}.merge(HashSpecs::MyHash[1 => 2]).class.should == Hash + end + + it "processes entries with same order as each()" do + h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] } + merge_pairs = [] + each_pairs = [] + h.each_pair { |k, v| each_pairs << [k, v] } + h.merge(h) { |k, v1, v2| merge_pairs << [k, v1] } + merge_pairs.should == each_pairs + end + +end + +describe "Hash#merge!" do + it_behaves_like(:hash_update, :merge!) + + it "does not raise an exception if changing the value of an existing key during iteration" do + hash = {1 => 2, 3 => 4, 5 => 6} + hash2 = {1 => :foo, 3 => :bar} + hash.each { hash.merge!(hash2) } + hash.should == {1 => :foo, 3 => :bar, 5 => 6} + end +end diff --git a/spec/rubyspec/core/hash/new_spec.rb b/spec/rubyspec/core/hash/new_spec.rb new file mode 100644 index 0000000000..c31cc2abf4 --- /dev/null +++ b/spec/rubyspec/core/hash/new_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash.new" do + it "creates an empty Hash if passed no arguments" do + Hash.new.should == {} + Hash.new.size.should == 0 + end + + it "creates a new Hash with default object if passed a default argument" do + Hash.new(5).default.should == 5 + Hash.new({}).default.should == {} + end + + it "does not create a copy of the default argument" do + str = "foo" + Hash.new(str).default.should equal(str) + end + + it "creates a Hash with a default_proc if passed a block" do + Hash.new.default_proc.should == nil + + h = Hash.new { |x| "Answer to #{x}" } + h.default_proc.call(5).should == "Answer to 5" + h.default_proc.call("x").should == "Answer to x" + end + + it "raises an ArgumentError if more than one argument is passed" do + lambda { Hash.new(5,6) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed both default argument and default block" do + lambda { Hash.new(5) { 0 } }.should raise_error(ArgumentError) + lambda { Hash.new(nil) { 0 } }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/hash/rassoc_spec.rb b/spec/rubyspec/core/hash/rassoc_spec.rb new file mode 100644 index 0000000000..79c633f95e --- /dev/null +++ b/spec/rubyspec/core/hash/rassoc_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Hash#rassoc" do + before :each do + @h = {apple: :green, orange: :orange, grape: :green, banana: :yellow} + end + + it "returns an Array if the argument is a value of the Hash" do + @h.rassoc(:green).should be_an_instance_of(Array) + end + + it "returns a 2-element Array if the argument is a value of the Hash" do + @h.rassoc(:orange).size.should == 2 + end + + it "sets the first element of the Array to the key of the located value" do + @h.rassoc(:yellow).first.should == :banana + end + + it "sets the last element of the Array to the located value" do + @h.rassoc(:yellow).last.should == :yellow + end + + it "only returns the first matching key-value pair" do + @h.rassoc(:green).should == [:apple, :green] + end + + it "uses #== to compare the argument to the values" do + @h[:key] = 1.0 + 1.should == 1.0 + @h.rassoc(1).should eql [:key, 1.0] + end + + it "returns nil if the argument is not a value of the Hash" do + @h.rassoc(:banana).should be_nil + end + + it "returns nil if the argument is not a value of the Hash even when there is a default" do + Hash.new(42).merge!( foo: :bar ).rassoc(42).should be_nil + Hash.new{|h, k| h[k] = 42}.merge!( foo: :bar ).rassoc(42).should be_nil + end +end diff --git a/spec/rubyspec/core/hash/rehash_spec.rb b/spec/rubyspec/core/hash/rehash_spec.rb new file mode 100644 index 0000000000..09315737b6 --- /dev/null +++ b/spec/rubyspec/core/hash/rehash_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#rehash" do + it "reorganizes the hash by recomputing all key hash codes" do + k1 = [1] + k2 = [2] + h = {} + h[k1] = 0 + h[k2] = 1 + + k1 << 2 + h.key?(k1).should == false + h.keys.include?(k1).should == true + + h.rehash.should equal(h) + h.key?(k1).should == true + h[k1].should == 0 + + k1 = mock('k1') + k2 = mock('k2') + v1 = mock('v1') + v2 = mock('v2') + + v1.should_not_receive(:hash) + v2.should_not_receive(:hash) + + h = { k1 => v1, k2 => v2 } + + k1.should_receive(:hash).twice.and_return(0) + k2.should_receive(:hash).twice.and_return(0) + + h.rehash + h[k1].should == v1 + h[k2].should == v2 + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.rehash }.should raise_error(RuntimeError) + lambda { HashSpecs.empty_frozen_hash.rehash }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/reject_spec.rb b/spec/rubyspec/core/hash/reject_spec.rb new file mode 100644 index 0000000000..21dd7425aa --- /dev/null +++ b/spec/rubyspec/core/hash/reject_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#reject" do + it "returns a new hash removing keys for which the block yields true" do + h = { 1=>false, 2=>true, 3=>false, 4=>true } + h.reject { |k,v| v }.keys.sort.should == [1,3] + end + + it "is equivalent to hsh.dup.delete_if" do + h = { a: 'a', b: 'b', c: 'd' } + h.reject { |k,v| k == 'd' }.should == (h.dup.delete_if { |k, v| k == 'd' }) + + all_args_reject = [] + all_args_delete_if = [] + h = { 1 => 2, 3 => 4 } + h.reject { |*args| all_args_reject << args } + h.delete_if { |*args| all_args_delete_if << args } + all_args_reject.should == all_args_delete_if + + h = { 1 => 2 } + # dup doesn't copy singleton methods + def h.to_a() end + h.reject { false }.to_a.should == [[1, 2]] + end + + context "with extra state" do + it "returns Hash instance for subclasses" do + HashSpecs::MyHash[1 => 2, 3 => 4].reject { false }.should be_kind_of(Hash) + HashSpecs::MyHash[1 => 2, 3 => 4].reject { true }.should be_kind_of(Hash) + end + + it "does not taint the resulting hash" do + h = { a: 1 }.taint + h.reject {false}.tainted?.should == false + end + end + + it "processes entries with the same order as reject!" do + h = { a: 1, b: 2, c: 3, d: 4 } + + reject_pairs = [] + reject_bang_pairs = [] + h.dup.reject { |*pair| reject_pairs << pair } + h.reject! { |*pair| reject_bang_pairs << pair } + + reject_pairs.should == reject_bang_pairs + end + + it_behaves_like(:hash_iteration_no_block, :reject) + it_behaves_like(:enumeratorized_with_origin_size, :reject, { 1 => 2, 3 => 4, 5 => 6 }) +end + +describe "Hash#reject!" do + it "removes keys from self for which the block yields true" do + hsh = {} + (1 .. 10).each { |k| hsh[k] = (k % 2 == 0) } + hsh.reject! { |k,v| v } + hsh.keys.sort.should == [1,3,5,7,9] + end + + it "removes all entries if the block is true" do + h = { a: 1, b: 2, c: 3 } + h.reject! { |k,v| true }.should equal(h) + h.should == {} + end + + it "is equivalent to delete_if if changes are made" do + hsh = { a: 1 } + hsh.reject! { |k,v| v < 2 }.should == hsh.dup.delete_if { |k, v| v < 2 } + end + + it "returns nil if no changes were made" do + { a: 1 }.reject! { |k,v| v > 1 }.should == nil + end + + it "processes entries with the same order as delete_if" do + h = { a: 1, b: 2, c: 3, d: 4 } + + reject_bang_pairs = [] + delete_if_pairs = [] + h.dup.reject! { |*pair| reject_bang_pairs << pair } + h.dup.delete_if { |*pair| delete_if_pairs << pair } + + reject_bang_pairs.should == delete_if_pairs + end + + it "raises a RuntimeError if called on a frozen instance that is modified" do + lambda { HashSpecs.empty_frozen_hash.reject! { true } }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError if called on a frozen instance that would not be modified" do + lambda { HashSpecs.frozen_hash.reject! { false } }.should raise_error(RuntimeError) + end + + it_behaves_like(:hash_iteration_no_block, :reject!) + it_behaves_like(:enumeratorized_with_origin_size, :reject!, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/replace_spec.rb b/spec/rubyspec/core/hash/replace_spec.rb new file mode 100644 index 0000000000..61b3164355 --- /dev/null +++ b/spec/rubyspec/core/hash/replace_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/replace', __FILE__) + +describe "Hash#replace" do + it_behaves_like(:hash_replace, :replace) +end diff --git a/spec/rubyspec/core/hash/select_spec.rb b/spec/rubyspec/core/hash/select_spec.rb new file mode 100644 index 0000000000..449607b606 --- /dev/null +++ b/spec/rubyspec/core/hash/select_spec.rb @@ -0,0 +1,83 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/iteration', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Hash#select" do + before :each do + @hsh = { 1 => 2, 3 => 4, 5 => 6 } + @empty = {} + end + + it "yields two arguments: key and value" do + all_args = [] + { 1 => 2, 3 => 4 }.select { |*args| all_args << args } + all_args.sort.should == [[1, 2], [3, 4]] + end + + it "returns a Hash of entries for which block is true" do + a_pairs = { 'a' => 9, 'c' => 4, 'b' => 5, 'd' => 2 }.select { |k,v| v % 2 == 0 } + a_pairs.should be_an_instance_of(Hash) + a_pairs.sort.should == [['c', 4], ['d', 2]] + end + + it "processes entries with the same order as reject" do + h = { a: 9, c: 4, b: 5, d: 2 } + + select_pairs = [] + reject_pairs = [] + h.dup.select { |*pair| select_pairs << pair } + h.reject { |*pair| reject_pairs << pair } + + select_pairs.should == reject_pairs + end + + it "returns an Enumerator when called on a non-empty hash without a block" do + @hsh.select.should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator when called on an empty hash without a block" do + @empty.select.should be_an_instance_of(Enumerator) + end + + it_behaves_like(:hash_iteration_no_block, :select) + it_behaves_like(:enumeratorized_with_origin_size, :select, { 1 => 2, 3 => 4, 5 => 6 }) +end + +describe "Hash#select!" do + before :each do + @hsh = { 1 => 2, 3 => 4, 5 => 6 } + @empty = {} + end + + it "is equivalent to keep_if if changes are made" do + h = { a: 2 } + h.select! { |k,v| v <= 1 }.should equal h + + h = { 1 => 2, 3 => 4 } + all_args_select = [] + h.dup.select! { |*args| all_args_select << args } + all_args_select.should == [[1, 2], [3, 4]] + end + + it "removes all entries if the block is false" do + h = { a: 1, b: 2, c: 3 } + h.select! { |k,v| false }.should equal(h) + h.should == {} + end + + it "returns nil if no changes were made" do + { a: 1 }.select! { |k,v| v <= 1 }.should == nil + end + + it "raises a RuntimeError if called on an empty frozen instance" do + lambda { HashSpecs.empty_frozen_hash.select! { false } }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError if called on a frozen instance that would not be modified" do + lambda { HashSpecs.frozen_hash.select! { true } }.should raise_error(RuntimeError) + end + + it_behaves_like(:hash_iteration_no_block, :select!) + it_behaves_like(:enumeratorized_with_origin_size, :select!, { 1 => 2, 3 => 4, 5 => 6 }) +end diff --git a/spec/rubyspec/core/hash/shared/comparison.rb b/spec/rubyspec/core/hash/shared/comparison.rb new file mode 100644 index 0000000000..bbb9bfd6ad --- /dev/null +++ b/spec/rubyspec/core/hash/shared/comparison.rb @@ -0,0 +1,15 @@ +describe :hash_comparison, shared: true do + it "raises a TypeError if the right operand is not a hash" do + lambda { { a: 1 }.send(@method, 1) }.should raise_error(TypeError) + lambda { { a: 1 }.send(@method, nil) }.should raise_error(TypeError) + lambda { { a: 1 }.send(@method, []) }.should raise_error(TypeError) + end + + it "returns false if both hashes have the same keys but different values" do + h1 = { a: 1 } + h2 = { a: 2 } + + h1.send(@method, h2).should be_false + h2.send(@method, h1).should be_false + end +end diff --git a/spec/rubyspec/core/hash/shared/each.rb b/spec/rubyspec/core/hash/shared/each.rb new file mode 100644 index 0000000000..bf4c569cfc --- /dev/null +++ b/spec/rubyspec/core/hash/shared/each.rb @@ -0,0 +1,68 @@ +describe :hash_each, shared: true do + it "yields a [[key, value]] Array for each pair to a block expecting |*args|" do + all_args = [] + { 1 => 2, 3 => 4 }.send(@method) { |*args| all_args << args } + all_args.sort.should == [[[1, 2]], [[3, 4]]] + end + + it "yields the key and value of each pair to a block expecting |key, value|" do + r = {} + h = { a: 1, b: 2, c: 3, d: 5 } + h.send(@method) { |k,v| r[k.to_s] = v.to_s }.should equal(h) + r.should == { "a" => "1", "b" => "2", "c" => "3", "d" => "5" } + end + + it "yields the key only to a block expecting |key,|" do + ary = [] + h = { "a" => 1, "b" => 2, "c" => 3 } + h.send(@method) { |k,| ary << k } + ary.sort.should == ["a", "b", "c"] + end + + it "uses the same order as keys() and values()" do + h = { a: 1, b: 2, c: 3, d: 5 } + keys = [] + values = [] + + h.send(@method) do |k, v| + keys << k + values << v + end + + keys.should == h.keys + values.should == h.values + end + + # Confirming the argument-splatting works from child class for both k, v and [k, v] + it "properly expands (or not) child class's 'each'-yielded args" do + cls1 = Class.new(Hash) do + attr_accessor :k_v + def each + super do |k, v| + @k_v = [k, v] + yield k, v + end + end + end + + cls2 = Class.new(Hash) do + attr_accessor :k_v + def each + super do |k, v| + @k_v = [k, v] + yield([k, v]) + end + end + end + + obj1 = cls1.new + obj1['a'] = 'b' + obj1.map {|k, v| [k, v]}.should == [['a', 'b']] + obj1.k_v.should == ['a', 'b'] + + obj2 = cls2.new + obj2['a'] = 'b' + obj2.map {|k, v| [k, v]}.should == [['a', 'b']] + obj2.k_v.should == ['a', 'b'] + end +end diff --git a/spec/rubyspec/core/hash/shared/eql.rb b/spec/rubyspec/core/hash/shared/eql.rb new file mode 100644 index 0000000000..1aed5f51fb --- /dev/null +++ b/spec/rubyspec/core/hash/shared/eql.rb @@ -0,0 +1,216 @@ +describe :hash_eql, shared: true do + it "does not compare values when keys don't match" do + value = mock('x') + value.should_not_receive(:==) + value.should_not_receive(:eql?) + { 1 => value }.send(@method, { 2 => value }).should be_false + end + + it "returns false when the numbers of keys differ without comparing any elements" do + obj = mock('x') + h = { obj => obj } + + obj.should_not_receive(:==) + obj.should_not_receive(:eql?) + + {}.send(@method, h).should be_false + h.send(@method, {}).should be_false + end + + it "first compares keys via hash" do + x = mock('x') + x.should_receive(:hash).any_number_of_times.and_return(0) + y = mock('y') + y.should_receive(:hash).any_number_of_times.and_return(0) + + { x => 1 }.send(@method, { y => 1 }).should be_false + end + + it "does not compare keys with different hash codes via eql?" do + x = mock('x') + y = mock('y') + x.should_not_receive(:eql?) + y.should_not_receive(:eql?) + + x.should_receive(:hash).any_number_of_times.and_return(0) + y.should_receive(:hash).any_number_of_times.and_return(1) + + { x => 1 }.send(@method, { y => 1 }).should be_false + end + + it "computes equality for recursive hashes" do + h = {} + h[:a] = h + h.send(@method, h[:a]).should be_true + (h == h[:a]).should be_true + end + + it "doesn't call to_hash on objects" do + mock_hash = mock("fake hash") + def mock_hash.to_hash() {} end + {}.send(@method, mock_hash).should be_false + end + + it "computes equality for complex recursive hashes" do + a, b = {}, {} + a.merge! self: a, other: b + b.merge! self: b, other: a + a.send(@method, b).should be_true # they both have the same structure! + + c = {} + c.merge! other: c, self: c + c.send(@method, a).should be_true # subtle, but they both have the same structure! + a[:delta] = c[:delta] = a + c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil + c[:delta] = 42 + c.send(@method, a).should be_false + a[:delta] = 42 + c.send(@method, a).should be_false + b[:delta] = 42 + c.send(@method, a).should be_true + end + + it "computes equality for recursive hashes & arrays" do + x, y, z = [], [], [] + a, b, c = {foo: x, bar: 42}, {foo: y, bar: 42}, {foo: z, bar: 42} + x << a + y << c + z << b + b.send(@method, c).should be_true # they clearly have the same structure! + y.send(@method, z).should be_true + a.send(@method, b).should be_true # subtle, but they both have the same structure! + x.send(@method, y).should be_true + y << x + y.send(@method, z).should be_false + z << x + y.send(@method, z).should be_true + + a[:foo], a[:bar] = a[:bar], a[:foo] + a.send(@method, b).should be_false + b[:bar] = b[:foo] + b.send(@method, c).should be_false + end +end + +describe :hash_eql_additional, shared: true do + it "compares values when keys match" do + x = mock('x') + y = mock('y') + def x.==(o) false end + def y.==(o) false end + def x.eql?(o) false end + def y.eql?(o) false end + { 1 => x }.send(@method, { 1 => y }).should be_false + + x = mock('x') + y = mock('y') + def x.==(o) true end + def y.==(o) true end + def x.eql?(o) true end + def y.eql?(o) true end + { 1 => x }.send(@method, { 1 => y }).should be_true + end + + it "compares keys with eql? semantics" do + { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should be_true + { 1.0 => "x" }.send(@method, { 1.0 => "x" }).should be_true + { 1 => "x" }.send(@method, { 1.0 => "x" }).should be_false + { 1.0 => "x" }.send(@method, { 1 => "x" }).should be_false + end + + it "returns true iff other Hash has the same number of keys and each key-value pair matches" do + a = { a: 5 } + b = {} + a.send(@method, b).should be_false + + b[:a] = 5 + a.send(@method, b).should be_true + + not_supported_on :opal do + c = { "a" => 5 } + a.send(@method, c).should be_false + end + + c = { "A" => 5 } + a.send(@method, c).should be_false + + c = { a: 6 } + a.send(@method, c).should be_false + end + + it "does not call to_hash on hash subclasses" do + { 5 => 6 }.send(@method, HashSpecs::ToHashHash[5 => 6]).should be_true + end + + it "ignores hash class differences" do + h = { 1 => 2, 3 => 4 } + HashSpecs::MyHash[h].send(@method, h).should be_true + HashSpecs::MyHash[h].send(@method, HashSpecs::MyHash[h]).should be_true + h.send(@method, HashSpecs::MyHash[h]).should be_true + end + + # Why isn't this true of eql? too ? + it "compares keys with matching hash codes via eql?" do + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + # It's undefined whether the impl does a[0].eql?(a[1]) or + # a[1].eql?(a[0]) so we taint both. + def obj.eql?(o) + return true if self.equal?(o) + taint + o.taint + false + end + + obj + end + + { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_false + a[0].tainted?.should be_true + a[1].tainted?.should be_true + + a = Array.new(2) do + obj = mock('0') + obj.should_receive(:hash).at_least(1).and_return(0) + + def obj.eql?(o) + # It's undefined whether the impl does a[0].send(@method, a[1]) or + # a[1].send(@method, a[0]) so we taint both. + taint + o.taint + true + end + + obj + end + + { a[0] => 1 }.send(@method, { a[1] => 1 }).should be_true + a[0].tainted?.should be_true + a[1].tainted?.should be_true + end + + it "compares the values in self to values in other hash" do + l_val = mock("left") + r_val = mock("right") + + l_val.should_receive(:eql?).with(r_val).and_return(true) + + { 1 => l_val }.eql?({ 1 => r_val }).should be_true + end +end + +describe :hash_eql_additional_more, shared: true do + it "returns true if other Hash has the same number of keys and each key-value pair matches, even though the default-value are not same" do + Hash.new(5).send(@method, Hash.new(1)).should be_true + Hash.new {|h, k| 1}.send(@method, Hash.new {}).should be_true + Hash.new {|h, k| 1}.send(@method, Hash.new(2)).should be_true + + d = Hash.new {|h, k| 1} + e = Hash.new {} + d[1] = 2 + e[1] = 2 + d.send(@method, e).should be_true + end +end diff --git a/spec/rubyspec/core/hash/shared/equal.rb b/spec/rubyspec/core/hash/shared/equal.rb new file mode 100644 index 0000000000..43606437fe --- /dev/null +++ b/spec/rubyspec/core/hash/shared/equal.rb @@ -0,0 +1,90 @@ +describe :hash_equal, shared: true do + it "does not compare values when keys don't match" do + value = mock('x') + value.should_not_receive(:==) + value.should_not_receive(:eql?) + { 1 => value }.send(@method, { 2 => value }).should be_false + end + + it "returns false when the numbers of keys differ without comparing any elements" do + obj = mock('x') + h = { obj => obj } + + obj.should_not_receive(:==) + obj.should_not_receive(:eql?) + + {}.send(@method, h).should be_false + h.send(@method, {}).should be_false + end + + it "first compares keys via hash" do + x = mock('x') + x.should_receive(:hash).and_return(0) + y = mock('y') + y.should_receive(:hash).and_return(0) + + { x => 1 }.send(@method, { y => 1 }).should be_false + end + + it "does not compare keys with different hash codes via eql?" do + x = mock('x') + y = mock('y') + x.should_not_receive(:eql?) + y.should_not_receive(:eql?) + + x.should_receive(:hash).and_return(0) + y.should_receive(:hash).and_return(1) + + def x.hash() 0 end + def y.hash() 1 end + + { x => 1 }.send(@method, { y => 1 }).should be_false + end + + it "computes equality for recursive hashes" do + h = {} + h[:a] = h + h.send(@method, h[:a]).should be_true + (h == h[:a]).should be_true + end + + it "computes equality for complex recursive hashes" do + a, b = {}, {} + a.merge! self: a, other: b + b.merge! self: b, other: a + a.send(@method, b).should be_true # they both have the same structure! + + c = {} + c.merge! other: c, self: c + c.send(@method, a).should be_true # subtle, but they both have the same structure! + a[:delta] = c[:delta] = a + c.send(@method, a).should be_false # not quite the same structure, as a[:other][:delta] = nil + c[:delta] = 42 + c.send(@method, a).should be_false + a[:delta] = 42 + c.send(@method, a).should be_false + b[:delta] = 42 + c.send(@method, a).should be_true + end + + it "computes equality for recursive hashes & arrays" do + x, y, z = [], [], [] + a, b, c = {foo: x, bar: 42}, {foo: y, bar: 42}, {foo: z, bar: 42} + x << a + y << c + z << b + b.send(@method, c).should be_true # they clearly have the same structure! + y.send(@method, z).should be_true + a.send(@method, b).should be_true # subtle, but they both have the same structure! + x.send(@method, y).should be_true + y << x + y.send(@method, z).should be_false + z << x + y.send(@method, z).should be_true + + a[:foo], a[:bar] = a[:bar], a[:foo] + a.send(@method, b).should be_false + b[:bar] = b[:foo] + b.send(@method, c).should be_false + end +end diff --git a/spec/rubyspec/core/hash/shared/greater_than.rb b/spec/rubyspec/core/hash/shared/greater_than.rb new file mode 100644 index 0000000000..1f8b9fcfb7 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/greater_than.rb @@ -0,0 +1,23 @@ +describe :hash_greater_than, shared: true do + before do + @h1 = { a: 1, b: 2, c: 3 } + @h2 = { a: 1, b: 2 } + end + + it "returns true if the other hash is a subset of self" do + @h1.send(@method, @h2).should be_true + end + + it "returns false if the other hash is not a subset of self" do + @h2.send(@method, @h1).should be_false + end + + it "converts the right operand to a hash before comparing" do + o = Object.new + def o.to_hash + { a: 1, b: 2 } + end + + @h1.send(@method, o).should be_true + end +end diff --git a/spec/rubyspec/core/hash/shared/index.rb b/spec/rubyspec/core/hash/shared/index.rb new file mode 100644 index 0000000000..9aa1b2a46f --- /dev/null +++ b/spec/rubyspec/core/hash/shared/index.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :hash_index, shared: true do + it "returns the corresponding key for value" do + { 2 => 'a', 1 => 'b' }.send(@method, 'b').should == 1 + end + + it "returns nil if the value is not found" do + { a: -1, b: 3.14, c: 2.718 }.send(@method, 1).should be_nil + end + + it "doesn't return default value if the value is not found" do + Hash.new(5).send(@method, 5).should be_nil + end + + it "compares values using ==" do + { 1 => 0 }.send(@method, 0.0).should == 1 + { 1 => 0.0 }.send(@method, 0).should == 1 + + needle = mock('needle') + inhash = mock('inhash') + inhash.should_receive(:==).with(needle).and_return(true) + + { 1 => inhash }.send(@method, needle).should == 1 + end +end diff --git a/spec/rubyspec/core/hash/shared/iteration.rb b/spec/rubyspec/core/hash/shared/iteration.rb new file mode 100644 index 0000000000..d27c2443f8 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/iteration.rb @@ -0,0 +1,19 @@ +describe :hash_iteration_no_block, shared: true do + before :each do + @hsh = { 1 => 2, 3 => 4, 5 => 6 } + @empty = {} + end + + it "returns an Enumerator if called on a non-empty hash without a block" do + @hsh.send(@method).should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator if called on an empty hash without a block" do + @empty.send(@method).should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator if called on a frozen instance" do + @hsh.freeze + @hsh.send(@method).should be_an_instance_of(Enumerator) + end +end diff --git a/spec/rubyspec/core/hash/shared/key.rb b/spec/rubyspec/core/hash/shared/key.rb new file mode 100644 index 0000000000..17f9f81457 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/key.rb @@ -0,0 +1,38 @@ +describe :hash_key_p, shared: true do + it "returns true if argument is a key" do + h = { a: 1, b: 2, c: 3, 4 => 0 } + h.send(@method, :a).should == true + h.send(@method, :b).should == true + h.send(@method, 2).should == false + h.send(@method, 4).should == true + + not_supported_on :opal do + h.send(@method, 'b').should == false + h.send(@method, 4.0).should == false + end + end + + it "returns true if the key's matching value was nil" do + { xyz: nil }.send(@method, :xyz).should == true + end + + it "returns true if the key's matching value was false" do + { xyz: false }.send(@method, :xyz).should == true + end + + it "returns true if the key is nil" do + { nil => 'b' }.send(@method, nil).should == true + { nil => nil }.send(@method, nil).should == true + end + + it "compares keys with the same #hash value via #eql?" do + x = mock('x') + x.stub!(:hash).and_return(42) + + y = mock('y') + y.stub!(:hash).and_return(42) + y.should_receive(:eql?).and_return(false) + + { x => nil }.send(@method, y).should == false + end +end diff --git a/spec/rubyspec/core/hash/shared/length.rb b/spec/rubyspec/core/hash/shared/length.rb new file mode 100644 index 0000000000..24f5563759 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/length.rb @@ -0,0 +1,12 @@ +describe :hash_length, shared: true do + it "returns the number of entries" do + { a: 1, b: 'c' }.send(@method).should == 2 + h = { a: 1, b: 2 } + h[:a] = 2 + h.send(@method).should == 2 + { a: 1, b: 1, c: 1 }.send(@method).should == 3 + {}.send(@method).should == 0 + Hash.new(5).send(@method).should == 0 + Hash.new { 5 }.send(@method).should == 0 + end +end diff --git a/spec/rubyspec/core/hash/shared/less_than.rb b/spec/rubyspec/core/hash/shared/less_than.rb new file mode 100644 index 0000000000..cdc6f14546 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/less_than.rb @@ -0,0 +1,23 @@ +describe :hash_less_than, shared: true do + before do + @h1 = { a: 1, b: 2 } + @h2 = { a: 1, b: 2, c: 3 } + end + + it "returns true if self is a subset of the other hash" do + @h1.send(@method, @h2).should be_true + end + + it "returns false if self is not a subset of the other hash" do + @h2.send(@method, @h1).should be_false + end + + it "converts the right operand to a hash before comparing" do + o = Object.new + def o.to_hash + { a: 1, b: 2, c: 3 } + end + + @h1.send(@method, o).should be_true + end +end diff --git a/spec/rubyspec/core/hash/shared/replace.rb b/spec/rubyspec/core/hash/shared/replace.rb new file mode 100644 index 0000000000..463c861395 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/replace.rb @@ -0,0 +1,51 @@ +describe :hash_replace, shared: true do + it "replaces the contents of self with other" do + h = { a: 1, b: 2 } + h.send(@method, c: -1, d: -2).should equal(h) + h.should == { c: -1, d: -2 } + end + + it "tries to convert the passed argument to a hash using #to_hash" do + obj = mock('{1=>2,3=>4}') + obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 }) + + h = {} + h.send(@method, obj) + h.should == { 1 => 2, 3 => 4 } + end + + it "calls to_hash on hash subclasses" do + h = {} + h.send(@method, HashSpecs::ToHashHash[1 => 2]) + h.should == { 1 => 2 } + end + + it "does not transfer default values" do + hash_a = {} + hash_b = Hash.new(5) + hash_a.send(@method, hash_b) + hash_a.default.should == 5 + + hash_a = {} + hash_b = Hash.new { |h, k| k * 2 } + hash_a.send(@method, hash_b) + hash_a.default(5).should == 10 + + hash_a = Hash.new { |h, k| k * 5 } + hash_b = Hash.new(lambda { raise "Should not invoke lambda" }) + hash_a.send(@method, hash_b) + hash_a.default.should == hash_b.default + end + + it "raises a RuntimeError if called on a frozen instance that would not be modified" do + lambda do + HashSpecs.frozen_hash.send(@method, HashSpecs.frozen_hash) + end.should raise_error(RuntimeError) + end + + it "raises a RuntimeError if called on a frozen instance that is modified" do + lambda do + HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash) + end.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/shared/store.rb b/spec/rubyspec/core/hash/shared/store.rb new file mode 100644 index 0000000000..b43dcbc93e --- /dev/null +++ b/spec/rubyspec/core/hash/shared/store.rb @@ -0,0 +1,98 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :hash_store, shared: true do + it "associates the key with the value and return the value" do + h = { a: 1 } + h.send(@method, :b, 2).should == 2 + h.should == { b:2, a:1 } + end + + it "duplicates string keys using dup semantics" do + # dup doesn't copy singleton methods + key = "foo" + def key.reverse() "bar" end + h = {} + h.send(@method, key, 0) + h.keys[0].reverse.should == "oof" + end + + it "stores unequal keys that hash to the same value" do + h = {} + k1 = ["x"] + k2 = ["y"] + # So they end up in the same bucket + k1.should_receive(:hash).and_return(0) + k2.should_receive(:hash).and_return(0) + + h.send(@method, k1, 1) + h.send(@method, k2, 2) + h.size.should == 2 + end + + it "accepts keys with private #hash method" do + key = HashSpecs::KeyWithPrivateHash.new + h = {} + h.send(@method, key, "foo") + h[key].should == "foo" + end + + it " accepts keys with a Bignum hash" do + o = mock(hash: 1 << 100) + h = {} + h[o] = 1 + h[o].should == 1 + end + + it "duplicates and freezes string keys" do + key = "foo" + h = {} + h.send(@method, key, 0) + key << "bar" + + h.should == { "foo" => 0 } + h.keys[0].frozen?.should == true + end + + it "doesn't duplicate and freeze already frozen string keys" do + key = "foo".freeze + h = {} + h.send(@method, key, 0) + h.keys[0].should equal(key) + end + + it "keeps the existing key in the hash if there is a matching one" do + h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 } + key1 = HashSpecs::ByValueKey.new(13) + key2 = HashSpecs::ByValueKey.new(13) + h[key1] = 41 + key_in_hash = h.keys.last + key_in_hash.should equal(key1) + h[key2] = 42 + last_key = h.keys.last + last_key.should equal(key_in_hash) + last_key.should_not equal(key2) + end + + it "keeps the existing String key in the hash if there is a matching one" do + h = { "a" => 1, "b" => 2, "c" => 3, "d" => 4 } + key1 = "foo" + key2 = "foo" + key1.should_not equal(key2) + h[key1] = 41 + frozen_key = h.keys.last + frozen_key.should_not equal(key1) + h[key2] = 42 + h.keys.last.should equal(frozen_key) + h.keys.last.should_not equal(key2) + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.send(@method, 1, 2) }.should raise_error(RuntimeError) + end + + it "does not raise an exception if changing the value of an existing key during iteration" do + hash = {1 => 2, 3 => 4, 5 => 6} + hash.each { hash.send(@method, 1, :foo) } + hash.should == {1 => :foo, 3 => 4, 5 => 6} + end +end diff --git a/spec/rubyspec/core/hash/shared/to_s.rb b/spec/rubyspec/core/hash/shared/to_s.rb new file mode 100644 index 0000000000..7ef2c207d4 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/to_s.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :hash_to_s, shared: true do + + it "returns a string representation with same order as each()" do + h = { a: [1, 2], b: -2, d: -6, nil => nil } + + pairs = [] + h.each do |key, value| + pairs << key.inspect + '=>' + value.inspect + end + + str = '{' + pairs.join(', ') + '}' + h.send(@method).should == str + end + + it "calls inspect on keys and values" do + key = mock('key') + val = mock('val') + key.should_receive(:inspect).and_return('key') + val.should_receive(:inspect).and_return('val') + + { key => val }.send(@method).should == '{key=>val}' + end + + it "handles hashes with recursive values" do + x = {} + x[0] = x + x.send(@method).should == '{0=>{...}}' + + x = {} + y = {} + x[0] = y + y[1] = x + x.send(@method).should == "{0=>{1=>{...}}}" + y.send(@method).should == "{1=>{0=>{...}}}" + end + + it "returns a tainted string if self is tainted and not empty" do + {}.taint.send(@method).tainted?.should be_false + { nil => nil }.taint.send(@method).tainted?.should be_true + end + + it "returns an untrusted string if self is untrusted and not empty" do + {}.untrust.send(@method).untrusted?.should be_false + { nil => nil }.untrust.send(@method).untrusted?.should be_true + end + + ruby_version_is ''...'2.3' do + it "raises if inspected result is not default external encoding" do + utf_16be = mock("utf_16be") + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE)) + + lambda { + {a: utf_16be}.send(@method) + }.should raise_error(Encoding::CompatibilityError) + end + end + + ruby_version_is '2.3' do + it "does not raise if inspected result is not default external encoding" do + utf_16be = mock("utf_16be") + utf_16be.should_receive(:inspect).and_return(%<"utf_16be \u3042">.encode!(Encoding::UTF_16BE)) + + {a: utf_16be}.send(@method).should == '{:a=>"utf_16be \u3042"}' + end + end +end diff --git a/spec/rubyspec/core/hash/shared/update.rb b/spec/rubyspec/core/hash/shared/update.rb new file mode 100644 index 0000000000..b1e3793028 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/update.rb @@ -0,0 +1,59 @@ +describe :hash_update, shared: true do + it "adds the entries from other, overwriting duplicate keys. Returns self" do + h = { _1: 'a', _2: '3' } + h.send(@method, _1: '9', _9: 2).should equal(h) + h.should == { _1: "9", _2: "3", _9: 2 } + end + + it "sets any duplicate key to the value of block if passed a block" do + h1 = { a: 2, b: -1 } + h2 = { a: -2, c: 1 } + h1.send(@method, h2) { |k,x,y| 3.14 }.should equal(h1) + h1.should == { c: 1, b: -1, a: 3.14 } + + h1.send(@method, h1) { nil } + h1.should == { a: nil, b: nil, c: nil } + end + + it "tries to convert the passed argument to a hash using #to_hash" do + obj = mock('{1=>2}') + obj.should_receive(:to_hash).and_return({ 1 => 2 }) + { 3 => 4 }.send(@method, obj).should == { 1 => 2, 3 => 4 } + end + + it "does not call to_hash on hash subclasses" do + { 3 => 4 }.send(@method, HashSpecs::ToHashHash[1 => 2]).should == { 1 => 2, 3 => 4 } + end + + it "processes entries with same order as merge()" do + h = { 1 => 2, 3 => 4, 5 => 6, "x" => nil, nil => 5, [] => [] } + merge_bang_pairs = [] + merge_pairs = [] + h.merge(h) { |*arg| merge_pairs << arg } + h.send(@method, h) { |*arg| merge_bang_pairs << arg } + merge_bang_pairs.should == merge_pairs + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda do + HashSpecs.frozen_hash.send(@method, 1 => 2) + end.should raise_error(RuntimeError) + end + + it "checks frozen status before coercing an object with #to_hash" do + obj = mock("to_hash frozen") + # This is necessary because mock cleanup code cannot run on the frozen + # object. + def obj.to_hash() raise Exception, "should not receive #to_hash" end + obj.freeze + + lambda { HashSpecs.frozen_hash.send(@method, obj) }.should raise_error(RuntimeError) + end + + # see redmine #1571 + it "raises a RuntimeError on a frozen instance that would not be modified" do + lambda do + HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash) + end.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/shared/value.rb b/spec/rubyspec/core/hash/shared/value.rb new file mode 100644 index 0000000000..aac76c253e --- /dev/null +++ b/spec/rubyspec/core/hash/shared/value.rb @@ -0,0 +1,14 @@ +describe :hash_value_p, shared: true do + it "returns true if the value exists in the hash" do + { a: :b }.send(@method, :a).should == false + { 1 => 2 }.send(@method, 2).should == true + h = Hash.new(5) + h.send(@method, 5).should == false + h = Hash.new { 5 } + h.send(@method, 5).should == false + end + + it "uses == semantics for comparing values" do + { 5 => 2.0 }.send(@method, 2).should == true + end +end diff --git a/spec/rubyspec/core/hash/shared/values_at.rb b/spec/rubyspec/core/hash/shared/values_at.rb new file mode 100644 index 0000000000..ef3b0e8ba0 --- /dev/null +++ b/spec/rubyspec/core/hash/shared/values_at.rb @@ -0,0 +1,9 @@ +describe :hash_values_at, shared: true do + it "returns an array of values for the given keys" do + h = { a: 9, b: 'a', c: -10, d: nil } + h.send(@method).should be_kind_of(Array) + h.send(@method).should == [] + h.send(@method, :a, :d, :b).should be_kind_of(Array) + h.send(@method, :a, :d, :b).should == [9, nil, 'a'] + end +end diff --git a/spec/rubyspec/core/hash/shift_spec.rb b/spec/rubyspec/core/hash/shift_spec.rb new file mode 100644 index 0000000000..3991da9656 --- /dev/null +++ b/spec/rubyspec/core/hash/shift_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#shift" do + it "removes a pair from hash and return it" do + h = { a: 1, b: 2, "c" => 3, nil => 4, [] => 5 } + h2 = h.dup + + h.size.times do |i| + r = h.shift + r.should be_kind_of(Array) + h2[r.first].should == r.last + h.size.should == h2.size - i - 1 + end + + h.should == {} + end + + # MRI explicitly implements this behavior + it "allows shifting entries while iterating" do + h = { a: 1, b: 2, c: 3 } + visited = [] + shifted = [] + h.each_pair { |k,v| + visited << k + shifted << h.shift + } + visited.should == [:a, :b, :c] + shifted.should == [[:a, 1], [:b, 2], [:c, 3]] + h.should == {} + end + + it "calls #default with nil if the Hash is empty" do + h = {} + def h.default(key) + key.should == nil + :foo + end + h.shift.should == :foo + end + + it "returns nil from an empty hash" do + {}.shift.should == nil + end + + it "returns (computed) default for empty hashes" do + Hash.new(5).shift.should == 5 + h = Hash.new { |*args| args } + h.shift.should == [h, nil] + end + + it "preserves Hash invariants when removing the last item" do + h = { :a => 1, :b => 2 } + h.shift.should == [:a, 1] + h.shift.should == [:b, 2] + h[:c] = 3 + h.should == {:c => 3} + end + + it "raises a RuntimeError if called on a frozen instance" do + lambda { HashSpecs.frozen_hash.shift }.should raise_error(RuntimeError) + lambda { HashSpecs.empty_frozen_hash.shift }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/size_spec.rb b/spec/rubyspec/core/hash/size_spec.rb new file mode 100644 index 0000000000..71660af038 --- /dev/null +++ b/spec/rubyspec/core/hash/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Hash#size" do + it_behaves_like(:hash_length, :size) +end diff --git a/spec/rubyspec/core/hash/sort_spec.rb b/spec/rubyspec/core/hash/sort_spec.rb new file mode 100644 index 0000000000..06235f7609 --- /dev/null +++ b/spec/rubyspec/core/hash/sort_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#sort" do + it "converts self to a nested array of [key, value] arrays and sort with Array#sort" do + { 'a' => 'b', '1' => '2', 'b' => 'a' }.sort.should == + [["1", "2"], ["a", "b"], ["b", "a"]] + end + + it "works when some of the keys are themselves arrays" do + { [1,2] => 5, [1,1] => 5 }.sort.should == [[[1,1],5], [[1,2],5]] + end + + it "uses block to sort array if passed a block" do + { 1 => 2, 2 => 9, 3 => 4 }.sort { |a,b| b <=> a }.should == [[3, 4], [2, 9], [1, 2]] + end +end diff --git a/spec/rubyspec/core/hash/store_spec.rb b/spec/rubyspec/core/hash/store_spec.rb new file mode 100644 index 0000000000..45ea8da896 --- /dev/null +++ b/spec/rubyspec/core/hash/store_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/store', __FILE__) + +describe "Hash#store" do + it_behaves_like(:hash_store, :store) +end diff --git a/spec/rubyspec/core/hash/to_a_spec.rb b/spec/rubyspec/core/hash/to_a_spec.rb new file mode 100644 index 0000000000..76ca721038 --- /dev/null +++ b/spec/rubyspec/core/hash/to_a_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#to_a" do + it "returns a list of [key, value] pairs with same order as each()" do + h = { a: 1, 1 => :a, 3 => :b, b: 5 } + pairs = [] + + h.each_pair do |key, value| + pairs << [key, value] + end + + h.to_a.should be_kind_of(Array) + h.to_a.should == pairs + end + + it "is called for Enumerable#entries" do + h = { a: 1, 1 => :a, 3 => :b, b: 5 } + pairs = [] + + h.each_pair do |key, value| + pairs << [key, value] + end + + ent = h.entries + ent.should be_kind_of(Array) + ent.should == pairs + end + + it "returns a tainted array if self is tainted" do + {}.taint.to_a.tainted?.should be_true + end + + it "returns an untrusted array if self is untrusted" do + {}.untrust.to_a.untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/hash/to_h_spec.rb b/spec/rubyspec/core/hash/to_h_spec.rb new file mode 100644 index 0000000000..366fd58e8a --- /dev/null +++ b/spec/rubyspec/core/hash/to_h_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#to_h" do + it "returns self for Hash instances" do + h = {} + h.to_h.should equal(h) + end + + describe "when called on a subclass of Hash" do + before :each do + @h = HashSpecs::MyHash.new + @h[:foo] = :bar + end + + it "returns a new Hash instance" do + @h.to_h.should be_an_instance_of(Hash) + @h.to_h.should == @h + @h[:foo].should == :bar + end + + it "copies the default" do + @h.default = 42 + @h.to_h.default.should == 42 + @h[:hello].should == 42 + end + + it "copies the default_proc" do + @h.default_proc = prc = Proc.new{ |h, k| h[k] = 2 * k } + @h.to_h.default_proc.should == prc + @h[42].should == 84 + end + end +end diff --git a/spec/rubyspec/core/hash/to_hash_spec.rb b/spec/rubyspec/core/hash/to_hash_spec.rb new file mode 100644 index 0000000000..5a173c2ab5 --- /dev/null +++ b/spec/rubyspec/core/hash/to_hash_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#to_hash" do + it "returns self for Hash instances" do + h = {} + h.to_hash.should equal(h) + end + + it "returns self for instances of subclasses of Hash" do + h = HashSpecs::MyHash.new + h.to_hash.should equal(h) + end +end diff --git a/spec/rubyspec/core/hash/to_proc_spec.rb b/spec/rubyspec/core/hash/to_proc_spec.rb new file mode 100644 index 0000000000..b486d188b7 --- /dev/null +++ b/spec/rubyspec/core/hash/to_proc_spec.rb @@ -0,0 +1,89 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.3" do + describe "Hash#to_proc" do + before :each do + @key = Object.new + @value = Object.new + @hash = { @key => @value } + @default = Object.new + @unstored = Object.new + end + + it "returns an instance of Proc" do + @hash.to_proc.should be_an_instance_of Proc + end + + describe "the returned proc" do + before :each do + @proc = @hash.to_proc + end + + it "is not a lambda" do + @proc.lambda?.should == false + end + + it "raises ArgumentError if not passed exactly one argument" do + lambda { + @proc.call + }.should raise_error(ArgumentError) + + lambda { + @proc.call 1, 2 + }.should raise_error(ArgumentError) + end + + context "with a stored key" do + it "returns the paired value" do + @proc.call(@key).should equal(@value) + end + end + + context "passed as a block" do + it "retrieves the hash's values" do + [@key].map(&@proc)[0].should equal(@value) + end + + context "to instance_exec" do + it "always retrieves the original hash's values" do + hash = {foo: 1, bar: 2} + proc = hash.to_proc + + hash.instance_exec(:foo, &proc).should == 1 + + hash2 = {quux: 1} + hash2.instance_exec(:foo, &proc).should == 1 + end + end + end + + context "with no stored key" do + it "returns nil" do + @proc.call(@unstored).should be_nil + end + + context "when the hash has a default value" do + before :each do + @hash.default = @default + end + + it "returns the default value" do + @proc.call(@unstored).should equal(@default) + end + end + + context "when the hash has a default proc" do + it "returns an evaluated value from the default proc" do + @hash.default_proc = -> hash, called_with { [hash.keys, called_with] } + @proc.call(@unstored).should == [[@key], @unstored] + end + end + end + + it "raises an ArgumentError when calling #call on the Proc with no arguments" do + lambda { @hash.to_proc.call }.should raise_error(ArgumentError) + end + end + end +end diff --git a/spec/rubyspec/core/hash/to_s_spec.rb b/spec/rubyspec/core/hash/to_s_spec.rb new file mode 100644 index 0000000000..803b1d6236 --- /dev/null +++ b/spec/rubyspec/core/hash/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "Hash#to_s" do + it_behaves_like :hash_to_s, :to_s +end diff --git a/spec/rubyspec/core/hash/transform_values_spec.rb b/spec/rubyspec/core/hash/transform_values_spec.rb new file mode 100644 index 0000000000..7e4ff45bea --- /dev/null +++ b/spec/rubyspec/core/hash/transform_values_spec.rb @@ -0,0 +1,71 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.4" do + describe "Hash#transform_values" do + before :each do + @hash = { a: 1, b: 2, c: 3 } + end + + it "returns new hash" do + ret = @hash.transform_values(&:succ) + ret.should_not equal(@hash) + ret.should be_an_instance_of(Hash) + end + + it "sets the result as transformed values with the given block" do + @hash.transform_values(&:succ).should == { a: 2, b: 3, c: 4 } + end + + context "when no block is given" do + it "returns a sized Enumerator" do + enumerator = @hash.transform_values + enumerator.should be_an_instance_of(Enumerator) + enumerator.size.should == @hash.size + enumerator.each(&:succ).should == { a: 2, b: 3, c: 4 } + end + end + end + + describe "Hash#transform_values!" do + before :each do + @hash = { a: 1, b: 2, c: 3 } + @initial_pairs = @hash.dup + end + + it "returns self" do + @hash.transform_values!(&:succ).should equal(@hash) + end + + it "updates self as transformed values with the given block" do + @hash.transform_values!(&:succ) + @hash.should == { a: 2, b: 3, c: 4 } + end + + context "when no block is given" do + it "returns a sized Enumerator" do + enumerator = @hash.transform_values! + enumerator.should be_an_instance_of(Enumerator) + enumerator.size.should == @hash.size + enumerator.each(&:succ) + @hash.should == { a: 2, b: 3, c: 4 } + end + end + + describe "on frozen instance" do + before :each do + @hash.freeze + end + + it "keeps pairs and raises a RuntimeError" do + ->{ @hash.transform_values!(&:succ) }.should raise_error(RuntimeError) + @hash.should == @initial_pairs + end + + context "when no block is given" do + it "does not raise an exception" do + @hash.transform_values!.should be_an_instance_of(Enumerator) + end + end + end + end +end diff --git a/spec/rubyspec/core/hash/try_convert_spec.rb b/spec/rubyspec/core/hash/try_convert_spec.rb new file mode 100644 index 0000000000..818b08fb32 --- /dev/null +++ b/spec/rubyspec/core/hash/try_convert_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash.try_convert" do + it "returns the argument if it's a Hash" do + x = Hash.new + Hash.try_convert(x).should equal(x) + end + + it "returns the argument if it's a kind of Hash" do + x = HashSpecs::MyHash.new + Hash.try_convert(x).should equal(x) + end + + it "returns nil when the argument does not respond to #to_hash" do + Hash.try_convert(Object.new).should be_nil + end + + it "sends #to_hash to the argument and returns the result if it's nil" do + obj = mock("to_hash") + obj.should_receive(:to_hash).and_return(nil) + Hash.try_convert(obj).should be_nil + end + + it "sends #to_hash to the argument and returns the result if it's a Hash" do + x = Hash.new + obj = mock("to_hash") + obj.should_receive(:to_hash).and_return(x) + Hash.try_convert(obj).should equal(x) + end + + it "sends #to_hash to the argument and returns the result if it's a kind of Hash" do + x = HashSpecs::MyHash.new + obj = mock("to_hash") + obj.should_receive(:to_hash).and_return(x) + Hash.try_convert(obj).should equal(x) + end + + it "sends #to_hash to the argument and raises TypeError if it's not a kind of Hash" do + obj = mock("to_hash") + obj.should_receive(:to_hash).and_return(Object.new) + lambda { Hash.try_convert obj }.should raise_error(TypeError) + end + + it "does not rescue exceptions raised by #to_hash" do + obj = mock("to_hash") + obj.should_receive(:to_hash).and_raise(RuntimeError) + lambda { Hash.try_convert obj }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/hash/update_spec.rb b/spec/rubyspec/core/hash/update_spec.rb new file mode 100644 index 0000000000..6cfedea271 --- /dev/null +++ b/spec/rubyspec/core/hash/update_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Hash#update" do + it_behaves_like(:hash_update, :update) +end diff --git a/spec/rubyspec/core/hash/value_spec.rb b/spec/rubyspec/core/hash/value_spec.rb new file mode 100644 index 0000000000..acfe1968d5 --- /dev/null +++ b/spec/rubyspec/core/hash/value_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/value', __FILE__) + +describe "Hash#value?" do + it_behaves_like(:hash_value_p, :value?) +end + diff --git a/spec/rubyspec/core/hash/values_at_spec.rb b/spec/rubyspec/core/hash/values_at_spec.rb new file mode 100644 index 0000000000..7c39e9b573 --- /dev/null +++ b/spec/rubyspec/core/hash/values_at_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/values_at', __FILE__) + +describe "Hash#values_at" do + it_behaves_like(:hash_values_at, :values_at) +end diff --git a/spec/rubyspec/core/hash/values_spec.rb b/spec/rubyspec/core/hash/values_spec.rb new file mode 100644 index 0000000000..4ce2f3f5f0 --- /dev/null +++ b/spec/rubyspec/core/hash/values_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Hash#values" do + it "returns an array of values" do + h = { 1 => :a, 'a' => :a, 'the' => 'lang' } + h.values.should be_kind_of(Array) + h.values.sort {|a, b| a.to_s <=> b.to_s}.should == [:a, :a, 'lang'] + end +end diff --git a/spec/rubyspec/core/integer/ceil_spec.rb b/spec/rubyspec/core/integer/ceil_spec.rb new file mode 100644 index 0000000000..7a49ede0df --- /dev/null +++ b/spec/rubyspec/core/integer/ceil_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Integer#ceil" do + it_behaves_like(:integer_to_i, :ceil) +end diff --git a/spec/rubyspec/core/integer/chr_spec.rb b/spec/rubyspec/core/integer/chr_spec.rb new file mode 100644 index 0000000000..50a678608e --- /dev/null +++ b/spec/rubyspec/core/integer/chr_spec.rb @@ -0,0 +1,239 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#chr without argument" do + it "returns a String" do + 17.chr.should be_an_instance_of(String) + end + + it "returns a new String for each call" do + 82.chr.should_not equal(82.chr) + end + + it "raises a RangeError is self is less than 0" do + lambda { -1.chr }.should raise_error(RangeError) + lambda { -bignum_value.chr }.should raise_error(RangeError) + end + + describe "when Encoding.default_internal is nil" do + describe "and self is between 0 and 127 (inclusive)" do + it "returns a US-ASCII String" do + (0..127).each do |c| + c.chr.encoding.should == Encoding::US_ASCII + end + end + + it "returns a String encoding self interpreted as a US-ASCII codepoint" do + (0..127).each do |c| + c.chr.bytes.to_a.should == [c] + end + end + end + + describe "and self is between 128 and 255 (inclusive)" do + it "returns an ASCII-8BIT String" do + (128..255).each do |c| + c.chr.encoding.should == Encoding::ASCII_8BIT + end + end + + it "returns a String containing self interpreted as a byte" do + (128..255).each do |c| + c.chr.bytes.to_a.should == [c] + end + end + end + + it "raises a RangeError is self is greater than 255" do + lambda { 256.chr }.should raise_error(RangeError) + lambda { bignum_value.chr }.should raise_error(RangeError) + end + end + + describe "when Encoding.default_internal is not nil" do + before do + @default_internal = Encoding.default_internal + end + + after do + Encoding.default_internal = @default_internal + end + + describe "and self is between 0 and 127 (inclusive)" do + it "returns a US-ASCII String" do + (0..127).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.encoding.should == Encoding::US_ASCII + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.encoding.should == Encoding::US_ASCII + end + end + + it "returns a String encoding self interpreted as a US-ASCII codepoint" do + (0..127).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.bytes.to_a.should == [c] + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.bytes.to_a.should == [c] + end + end + end + + describe "and self is between 128 and 255 (inclusive)" do + it "returns an ASCII-8BIT String" do + (128..255).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.encoding.should == Encoding::ASCII_8BIT + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.encoding.should == Encoding::ASCII_8BIT + end + end + + it "returns a String containing self interpreted as a byte" do + (128..255).each do |c| + Encoding.default_internal = Encoding::UTF_8 + c.chr.bytes.to_a.should == [c] + + Encoding.default_internal = Encoding::SHIFT_JIS + c.chr.bytes.to_a.should == [c] + end + end + end + + describe "and self is greater than 255" do + it "returns a String with the default internal encoding" do + Encoding.default_internal = Encoding::UTF_8 + 0x0100.chr.encoding.should == Encoding::UTF_8 + 0x3000.chr.encoding.should == Encoding::UTF_8 + + Encoding.default_internal = Encoding::SHIFT_JIS + 0x8140.chr.encoding.should == Encoding::SHIFT_JIS + 0xFC4B.chr.encoding.should == Encoding::SHIFT_JIS + end + + it "returns a String encoding self interpreted as a codepoint in the default internal encoding" do + Encoding.default_internal = Encoding::UTF_8 + 0x0100.chr.bytes.to_a.should == [0xC4, 0x80] + 0x3000.chr.bytes.to_a.should == [0xE3, 0x80, 0x80] + + Encoding.default_internal = Encoding::SHIFT_JIS + 0x8140.chr.bytes.to_a.should == [0x81, 0x40] # Smallest assigned CP932 codepoint greater than 255 + 0xFC4B.chr.bytes.to_a.should == [0xFC, 0x4B] # Largest assigned CP932 codepoint + end + + # #5864 + it "raises RangeError if self is invalid as a codepoint in the default internal encoding" do + [ [0x0100, "US-ASCII"], + [0x0100, "ASCII-8BIT"], + [0x0100, "EUC-JP"], + [0xA1A0, "EUC-JP"], + [0x0100, "ISO-8859-9"], + [620, "TIS-620"] + ].each do |integer, encoding_name| + Encoding.default_internal = Encoding.find(encoding_name) + lambda { integer.chr }.should raise_error(RangeError) + end + end + end + end +end + +describe "Integer#chr with an encoding argument" do + it "returns a String" do + 900.chr(Encoding::UTF_8).should be_an_instance_of(String) + end + + it "returns a new String for each call" do + 8287.chr(Encoding::UTF_8).should_not equal(8287.chr(Encoding::UTF_8)) + end + + it "accepts a String as an argument" do + lambda { 0xA4A2.chr('euc-jp') }.should_not raise_error + end + + it "converts a String to an Encoding as Encoding.find does" do + ['utf-8', 'UTF-8', 'Utf-8'].each do |encoding| + 7894.chr(encoding).encoding.should == Encoding::UTF_8 + end + end + + # http://redmine.ruby-lang.org/issues/4869 + it "raises a RangeError is self is less than 0" do + lambda { -1.chr(Encoding::UTF_8) }.should raise_error(RangeError) + lambda { -bignum_value.chr(Encoding::EUC_JP) }.should raise_error(RangeError) + end + + it "returns a String with the specified encoding" do + 0x0000.chr(Encoding::US_ASCII).encoding.should == Encoding::US_ASCII + 0x007F.chr(Encoding::US_ASCII).encoding.should == Encoding::US_ASCII + + 0x0000.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT + 0x007F.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT + 0x0080.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT + 0x00FF.chr(Encoding::ASCII_8BIT).encoding.should == Encoding::ASCII_8BIT + + 0x0000.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x007F.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x0080.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x00FF.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x0100.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + 0x3000.chr(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + + 0x0000.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x007F.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x00A1.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x00DF.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0x8140.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + 0xFC4B.chr(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + end + + it "returns a String encoding self interpreted as a codepoint in the specified encoding" do + 0x0000.chr(Encoding::US_ASCII).bytes.to_a.should == [0x00] + 0x007F.chr(Encoding::US_ASCII).bytes.to_a.should == [0x7F] + + 0x0000.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x00] + 0x007F.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x7F] + 0x0080.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0x80] + 0x00FF.chr(Encoding::ASCII_8BIT).bytes.to_a.should == [0xFF] + + 0x0000.chr(Encoding::UTF_8).bytes.to_a.should == [0x00] + 0x007F.chr(Encoding::UTF_8).bytes.to_a.should == [0x7F] + 0x0080.chr(Encoding::UTF_8).bytes.to_a.should == [0xC2, 0x80] + 0x00FF.chr(Encoding::UTF_8).bytes.to_a.should == [0xC3, 0xBF] + 0x0100.chr(Encoding::UTF_8).bytes.to_a.should == [0xC4, 0x80] + 0x3000.chr(Encoding::UTF_8).bytes.to_a.should == [0xE3, 0x80, 0x80] + + 0x0000.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x00] + 0x007F.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x7F] + 0x00A1.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xA1] + 0x00DF.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xDF] + 0x8140.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0x81, 0x40] # Smallest assigned CP932 codepoint greater than 255 + 0xFC4B.chr(Encoding::SHIFT_JIS).bytes.to_a.should == [0xFC, 0x4B] # Largest assigned CP932 codepoint + end + + # #5864 + it "raises RangeError if self is invalid as a codepoint in the specified encoding" do + [ [0x80, "US-ASCII"], + [0x0100, "ASCII-8BIT"], + [0x0100, "EUC-JP"], + [0xA1A0, "EUC-JP"], + [0xA1, "EUC-JP"], + [0x80, "SHIFT_JIS"], + [0xE0, "SHIFT_JIS"], + [0x0100, "ISO-8859-9"], + [620, "TIS-620"], + [0xD800, "UTF-8"], + [0xDBFF, "UTF-8"], + [0xDC00, "UTF-8"], + [0xDFFF, "UTF-8"], + [0xD800, "UTF-16"], + [0xDBFF, "UTF-16"], + [0xDC00, "UTF-16"], + [0xDFFF, "UTF-16"], + ].each do |integer, encoding_name| + lambda { integer.chr(encoding_name) }.should raise_error(RangeError) + end + end +end diff --git a/spec/rubyspec/core/integer/denominator_spec.rb b/spec/rubyspec/core/integer/denominator_spec.rb new file mode 100644 index 0000000000..6d080ac81f --- /dev/null +++ b/spec/rubyspec/core/integer/denominator_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#denominator" do + # The Numeric child classes override this method, so their behaviour is + # specified in the appropriate place + before :each do + @numbers = [ + 20, # Integer + -2709, # Negative Integer + 99999999**99, # Bignum + -99999**621, # Negative BigNum + 0, + 1 + ] + end + + it "returns 1" do + @numbers.each {|number| number.denominator.should == 1} + end +end diff --git a/spec/rubyspec/core/integer/downto_spec.rb b/spec/rubyspec/core/integer/downto_spec.rb new file mode 100644 index 0000000000..6b7b353760 --- /dev/null +++ b/spec/rubyspec/core/integer/downto_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#downto [stop] when self and stop are Fixnums" do + it "does not yield when stop is greater than self" do + result = [] + 5.downto(6) { |x| result << x } + result.should == [] + end + + it "yields once when stop equals self" do + result = [] + 5.downto(5) { |x| result << x } + result.should == [5] + end + + it "yields while decreasing self until it is less than stop" do + result = [] + 5.downto(2) { |x| result << x } + result.should == [5, 4, 3, 2] + end + + it "yields while decreasing self until it less than ceil for a Float endpoint" do + result = [] + 9.downto(1.3) {|i| result << i} + 3.downto(-1.3) {|i| result << i} + result.should == [9, 8, 7, 6, 5, 4, 3, 2, 3, 2, 1, 0, -1] + end + + it "raises an ArgumentError for invalid endpoints" do + lambda {1.downto("A") {|x| p x } }.should raise_error(ArgumentError) + lambda {1.downto(nil) {|x| p x } }.should raise_error(ArgumentError) + end + + describe "when no block is given" do + it "returns an Enumerator" do + result = [] + + enum = 5.downto(2) + enum.each { |i| result << i } + + result.should == [5, 4, 3, 2] + end + + describe "returned Enumerator" do + describe "size" do + it "raises an ArgumentError for invalid endpoints" do + enum = 1.downto("A") + lambda { enum.size }.should raise_error(ArgumentError) + enum = 1.downto(nil) + lambda { enum.size }.should raise_error(ArgumentError) + end + + it "returns self - stop + 1" do + 10.downto(5).size.should == 6 + 10.downto(1).size.should == 10 + 10.downto(0).size.should == 11 + 0.downto(0).size.should == 1 + -3.downto(-5).size.should == 3 + end + + it "returns 0 when stop > self" do + 4.downto(5).size.should == 0 + -5.downto(0).size.should == 0 + -5.downto(-3).size.should == 0 + end + end + end + end +end diff --git a/spec/rubyspec/core/integer/even_spec.rb b/spec/rubyspec/core/integer/even_spec.rb new file mode 100644 index 0000000000..c14cf84947 --- /dev/null +++ b/spec/rubyspec/core/integer/even_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#even?" do + it "returns true for a Fixnum when it is an even number" do + (-2).even?.should be_true + (-1).even?.should be_false + + 0.even?.should be_true + 1.even?.should be_false + 2.even?.should be_true + end + + it "returns true for a Bignum when it is an even number" do + bignum_value(0).even?.should be_true + bignum_value(1).even?.should be_false + + (-bignum_value(0)).even?.should be_true + (-bignum_value(1)).even?.should be_false + end +end diff --git a/spec/rubyspec/core/integer/floor_spec.rb b/spec/rubyspec/core/integer/floor_spec.rb new file mode 100644 index 0000000000..b533a84ad4 --- /dev/null +++ b/spec/rubyspec/core/integer/floor_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Integer#floor" do + it_behaves_like(:integer_to_i, :floor) +end diff --git a/spec/rubyspec/core/integer/gcd_spec.rb b/spec/rubyspec/core/integer/gcd_spec.rb new file mode 100644 index 0000000000..5428c94ad1 --- /dev/null +++ b/spec/rubyspec/core/integer/gcd_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#gcd" do + it "returns self if equal to the argument" do + 1.gcd(1).should == 1 + 398.gcd(398).should == 398 + end + + it "returns an Integer" do + 36.gcd(6).should be_kind_of(Integer) + 4.gcd(20981).should be_kind_of(Integer) + end + + it "returns the greatest common divisor of self and argument" do + 10.gcd(5).should == 5 + 200.gcd(20).should == 20 + end + + it "returns a positive integer even if self is negative" do + -12.gcd(6).should == 6 + -100.gcd(100).should == 100 + end + + it "returns a positive integer even if the argument is negative" do + 12.gcd(-6).should == 6 + 100.gcd(-100).should == 100 + end + + it "returns a positive integer even if both self and argument are negative" do + -12.gcd(-6).should == 6 + -100.gcd(-100).should == 100 + end + + it "accepts a Bignum argument" do + bignum = 9999**99 + bignum.should be_kind_of(Bignum) + 99.gcd(bignum).should == 99 + end + + it "works if self is a Bignum" do + bignum = 9999**99 + bignum.should be_kind_of(Bignum) + bignum.gcd(99).should == 99 + end + + it "raises an ArgumentError if not given an argument" do + lambda { 12.gcd }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if given more than one argument" do + lambda { 12.gcd(30, 20) }.should raise_error(ArgumentError) + end + + it "raises a TypeError unless the argument is an Integer" do + lambda { 39.gcd(3.8) }.should raise_error(TypeError) + lambda { 45872.gcd([]) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/integer/gcdlcm_spec.rb b/spec/rubyspec/core/integer/gcdlcm_spec.rb new file mode 100644 index 0000000000..86968499b0 --- /dev/null +++ b/spec/rubyspec/core/integer/gcdlcm_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#gcdlcm" do + it "returns [self, self] if self is equal to the argument" do + 1.gcdlcm(1).should == [1, 1] + 398.gcdlcm(398).should == [398, 398] + end + + it "returns an Array" do + 36.gcdlcm(6).should be_kind_of(Array) + 4.gcdlcm(20981).should be_kind_of(Array) + end + + it "returns a two-element Array" do + 36.gcdlcm(876).size.should == 2 + 29.gcdlcm(17).size.should == 2 + end + + it "returns the greatest common divisor of self and argument as the first element" do + 10.gcdlcm(5)[0].should == 10.gcd(5) + 200.gcdlcm(20)[0].should == 200.gcd(20) + end + + it "returns the least common multiple of self and argument as the last element" do + 10.gcdlcm(5)[1].should == 10.lcm(5) + 200.gcdlcm(20)[1].should == 200.lcm(20) + end + + it "accepts a Bignum argument" do + bignum = 91999**99 + bignum.should be_kind_of(Bignum) + 99.gcdlcm(bignum).should == [99.gcd(bignum), 99.lcm(bignum)] + end + + it "works if self is a Bignum" do + bignum = 9999**89 + bignum.should be_kind_of(Bignum) + bignum.gcdlcm(99).should == [bignum.gcd(99), bignum.lcm(99)] + end + + it "raises an ArgumentError if not given an argument" do + lambda { 12.gcdlcm }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if given more than one argument" do + lambda { 12.gcdlcm(30, 20) }.should raise_error(ArgumentError) + end + + it "raises a TypeError unless the argument is an Integer" do + lambda { 39.gcdlcm(3.8) }.should raise_error(TypeError) + lambda { 45872.gcdlcm([]) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/integer/integer_spec.rb b/spec/rubyspec/core/integer/integer_spec.rb new file mode 100644 index 0000000000..a6f406cba0 --- /dev/null +++ b/spec/rubyspec/core/integer/integer_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer" do + it "includes Comparable" do + Integer.include?(Comparable).should == true + end +end + +describe "Integer#integer?" do + it "returns true for Integers" do + 0.integer?.should == true + -1.integer?.should == true + bignum_value.integer?.should == true + end +end diff --git a/spec/rubyspec/core/integer/lcm_spec.rb b/spec/rubyspec/core/integer/lcm_spec.rb new file mode 100644 index 0000000000..ddf25d3853 --- /dev/null +++ b/spec/rubyspec/core/integer/lcm_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#lcm" do + it "returns self if equal to the argument" do + 1.lcm(1).should == 1 + 398.lcm(398).should == 398 + end + + it "returns an Integer" do + 36.lcm(6).should be_kind_of(Integer) + 4.lcm(20981).should be_kind_of(Integer) + end + + it "returns the least common multiple of self and argument" do + 200.lcm(2001).should == 400200 + 99.lcm(90).should == 990 + end + + it "returns a positive integer even if self is negative" do + -12.lcm(6).should == 12 + -100.lcm(100).should == 100 + end + + it "returns a positive integer even if the argument is negative" do + 12.lcm(-6).should == 12 + 100.lcm(-100).should == 100 + end + + it "returns a positive integer even if both self and argument are negative" do + -12.lcm(-6).should == 12 + -100.lcm(-100).should == 100 + end + + it "accepts a Bignum argument" do + bignum = 9999**99 + bignum.should be_kind_of(Bignum) + 99.lcm(bignum).should == bignum + end + + it "works if self is a Bignum" do + bignum = 9999**99 + bignum.should be_kind_of(Bignum) + bignum.lcm(99).should == bignum + end + + it "raises an ArgumentError if not given an argument" do + lambda { 12.lcm }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if given more than one argument" do + lambda { 12.lcm(30, 20) }.should raise_error(ArgumentError) + end + + it "raises a TypeError unless the argument is an Integer" do + lambda { 39.lcm(3.8) }.should raise_error(TypeError) + lambda { 45872.lcm([]) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/integer/next_spec.rb b/spec/rubyspec/core/integer/next_spec.rb new file mode 100644 index 0000000000..a34db0a6f6 --- /dev/null +++ b/spec/rubyspec/core/integer/next_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/next', __FILE__) + +describe "Integer#next" do + it_behaves_like(:integer_next, :next) +end diff --git a/spec/rubyspec/core/integer/numerator_spec.rb b/spec/rubyspec/core/integer/numerator_spec.rb new file mode 100644 index 0000000000..12550b9a31 --- /dev/null +++ b/spec/rubyspec/core/integer/numerator_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#numerator" do + before :all do + @numbers = [ + 0, + 29871, + 99999999999999**99, + 72628191273, + ].map{|n| [-n, n]}.flatten + end + + it "returns self" do + @numbers.each do |number| + number.numerator.should == number + end + end +end diff --git a/spec/rubyspec/core/integer/odd_spec.rb b/spec/rubyspec/core/integer/odd_spec.rb new file mode 100644 index 0000000000..2aa76d054a --- /dev/null +++ b/spec/rubyspec/core/integer/odd_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#odd?" do + it "returns true when self is an odd number" do + (-2).odd?.should be_false + (-1).odd?.should be_true + + 0.odd?.should be_false + 1.odd?.should be_true + 2.odd?.should be_false + + bignum_value(0).odd?.should be_false + bignum_value(1).odd?.should be_true + + (-bignum_value(0)).odd?.should be_false + (-bignum_value(1)).odd?.should be_true + end +end diff --git a/spec/rubyspec/core/integer/ord_spec.rb b/spec/rubyspec/core/integer/ord_spec.rb new file mode 100644 index 0000000000..5941f513fd --- /dev/null +++ b/spec/rubyspec/core/integer/ord_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#ord" do + it "returns self" do + 20.ord.should eql(20) + 40.ord.should eql(40) + + 0.ord.should eql(0) + (-10).ord.should eql(-10) + + ?a.ord.should eql(97) + ?Z.ord.should eql(90) + + bignum_value.ord.should eql(bignum_value) + (-bignum_value).ord.should eql(-bignum_value) + end +end diff --git a/spec/rubyspec/core/integer/pred_spec.rb b/spec/rubyspec/core/integer/pred_spec.rb new file mode 100644 index 0000000000..8b16a7ae03 --- /dev/null +++ b/spec/rubyspec/core/integer/pred_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#pred" do + it "returns the Integer equal to self - 1" do + 0.pred.should eql(-1) + -1.pred.should eql(-2) + bignum_value.pred.should eql(bignum_value(-1)) + fixnum_min.pred.should eql(fixnum_min - 1) + 20.pred.should eql(19) + end +end diff --git a/spec/rubyspec/core/integer/rationalize_spec.rb b/spec/rubyspec/core/integer/rationalize_spec.rb new file mode 100644 index 0000000000..1ff4cfa454 --- /dev/null +++ b/spec/rubyspec/core/integer/rationalize_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#rationalize" do + before :all do + @numbers = [ + 0, + 29871, + 99999999999999**99, + -72628191273, + ] + end + + it "returns a Rational object" do + @numbers.each do |number| + number.rationalize.should be_an_instance_of(Rational) + end + end + + it "uses self as the numerator" do + @numbers.each do |number| + number.rationalize.numerator.should == number + end + end + + it "uses 1 as the denominator" do + @numbers.each do |number| + number.rationalize.denominator.should == 1 + end + end + + it "ignores a single argument" do + 1.rationalize(0.1).should == Rational(1,1) + end + + it "raises ArgumentError when passed more than one argument" do + lambda { 1.rationalize(0.1, 0.1) }.should raise_error(ArgumentError) + lambda { 1.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/integer/round_spec.rb b/spec/rubyspec/core/integer/round_spec.rb new file mode 100644 index 0000000000..5cc9aa3881 --- /dev/null +++ b/spec/rubyspec/core/integer/round_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Integer#round" do + it_behaves_like(:integer_to_i, :round) + + ruby_version_is ""..."2.5" do + it "rounds itself as a float if passed a positive precision" do + [2, -4, 10**70, -10**100].each do |v| + v.round(42).should eql(v.to_f) + end + end + end + + ruby_version_is "2.5" do + it "returns itself if passed a positive precision" do + [2, -4, 10**70, -10**100].each do |v| + v.round(42).should eql(v) + end + end + end + + it "returns itself if passed zero" do + [2, -4, 10**70, -10**100].each do |v| + v.round(0).should eql(v) + end + end + + # redmine:5228 + it "returns itself rounded if passed a negative value" do + +249.round(-2).should eql(+200) + -249.round(-2).should eql(-200) + (+25 * 10**70 - 1).round(-71).should eql(+20 * 10**70) + (-25 * 10**70 + 1).round(-71).should eql(-20 * 10**70) + end + + it "returns itself rounded to nearest if passed a negative value" do + +250.round(-2).should eql(+300) + -250.round(-2).should eql(-300) + (+25 * 10**70).round(-71).should eql(+30 * 10**70) + (-25 * 10**70).round(-71).should eql(-30 * 10**70) + end + + platform_is_not wordsize: 32 do + it "raises a RangeError when passed a big negative value" do + lambda { 42.round(fixnum_min) }.should raise_error(RangeError) + end + end + + it "raises a RangeError when passed Float::INFINITY" do + lambda { 42.round(Float::INFINITY) }.should raise_error(RangeError) + end + + it "raises a RangeError when passed a beyond signed int" do + lambda { 42.round(1<<31) }.should raise_error(RangeError) + end + + it "raises a TypeError when passed a String" do + lambda { 42.round("4") }.should raise_error(TypeError) + end + + it "raises a TypeError when its argument cannot be converted to an Integer" do + lambda { 42.round(nil) }.should raise_error(TypeError) + end + + it "calls #to_int on the argument to convert it to an Integer" do + obj = mock("Object") + obj.should_receive(:to_int).and_return(0) + 42.round(obj) + end + + it "raises a TypeError when #to_int does not return an Integer" do + obj = mock("Object") + obj.stub!(:to_int).and_return([]) + lambda { 42.round(obj) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/integer/shared/next.rb b/spec/rubyspec/core/integer/shared/next.rb new file mode 100644 index 0000000000..85b83d6965 --- /dev/null +++ b/spec/rubyspec/core/integer/shared/next.rb @@ -0,0 +1,25 @@ +describe :integer_next, shared: true do + it "returns the next larger positive Fixnum" do + 2.send(@method).should == 3 + end + + it "returns the next larger negative Fixnum" do + (-2).send(@method).should == -1 + end + + it "returns the next larger positive Bignum" do + bignum_value.send(@method).should == bignum_value(1) + end + + it "returns the next larger negative Bignum" do + (-bignum_value(1)).send(@method).should == -bignum_value + end + + it "overflows a Fixnum to a Bignum" do + fixnum_max.send(@method).should == fixnum_max + 1 + end + + it "underflows a Bignum to a Fixnum" do + (fixnum_min - 1).send(@method).should == fixnum_min + end +end diff --git a/spec/rubyspec/core/integer/shared/to_i.rb b/spec/rubyspec/core/integer/shared/to_i.rb new file mode 100644 index 0000000000..7b974cd3a7 --- /dev/null +++ b/spec/rubyspec/core/integer/shared/to_i.rb @@ -0,0 +1,8 @@ +describe :integer_to_i, shared: true do + it "returns self" do + 10.send(@method).should eql(10) + (-15).send(@method).should eql(-15) + bignum_value.send(@method).should eql(bignum_value) + (-bignum_value).send(@method).should eql(-bignum_value) + end +end diff --git a/spec/rubyspec/core/integer/succ_spec.rb b/spec/rubyspec/core/integer/succ_spec.rb new file mode 100644 index 0000000000..a1405684fc --- /dev/null +++ b/spec/rubyspec/core/integer/succ_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/next', __FILE__) + +describe "Integer#succ" do + it_behaves_like(:integer_next, :succ) +end diff --git a/spec/rubyspec/core/integer/times_spec.rb b/spec/rubyspec/core/integer/times_spec.rb new file mode 100644 index 0000000000..d426bc008c --- /dev/null +++ b/spec/rubyspec/core/integer/times_spec.rb @@ -0,0 +1,79 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#times" do + it "returns self" do + 5.times {}.should == 5 + 9.times {}.should == 9 + 9.times { |n| n - 2 }.should == 9 + end + + it "yields each value from 0 to self - 1" do + a = [] + 9.times { |i| a << i } + -2.times { |i| a << i } + a.should == [0, 1, 2, 3, 4, 5, 6, 7, 8] + end + + it "skips the current iteration when encountering 'next'" do + a = [] + 3.times do |i| + next if i == 1 + a << i + end + a.should == [0, 2] + end + + it "skips all iterations when encountering 'break'" do + a = [] + 5.times do |i| + break if i == 3 + a << i + end + a.should == [0, 1, 2] + end + + it "skips all iterations when encountering break with an argument and returns that argument" do + 9.times { break 2 }.should == 2 + end + + it "executes a nested while loop containing a break expression" do + a = [false] + b = 1.times do |i| + while true + a.shift or break + end + end + a.should == [] + b.should == 1 + end + + it "executes a nested #times" do + a = 0 + b = 3.times do |i| + 2.times { a += 1 } + end + a.should == 6 + b.should == 3 + end + + it "returns an Enumerator" do + result = [] + + enum = 3.times + enum.each { |i| result << i } + + result.should == [0, 1, 2] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "returns self" do + 5.times.size.should == 5 + 10.times.size.should == 10 + 0.times.size.should == 0 + end + end + end + end +end diff --git a/spec/rubyspec/core/integer/to_i_spec.rb b/spec/rubyspec/core/integer/to_i_spec.rb new file mode 100644 index 0000000000..1e6f21f191 --- /dev/null +++ b/spec/rubyspec/core/integer/to_i_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Integer#to_i" do + it_behaves_like(:integer_to_i, :to_i) +end diff --git a/spec/rubyspec/core/integer/to_int_spec.rb b/spec/rubyspec/core/integer/to_int_spec.rb new file mode 100644 index 0000000000..5e87c86ccd --- /dev/null +++ b/spec/rubyspec/core/integer/to_int_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Integer#to_int" do + it_behaves_like(:integer_to_i, :to_int) +end diff --git a/spec/rubyspec/core/integer/to_r_spec.rb b/spec/rubyspec/core/integer/to_r_spec.rb new file mode 100644 index 0000000000..0eb158b100 --- /dev/null +++ b/spec/rubyspec/core/integer/to_r_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#to_r" do + it "returns a Rational object" do + 309.to_r.should be_an_instance_of(Rational) + end + + it "constructs a rational number with self as the numerator" do + 34.to_r.numerator.should == 34 + end + + it "constructs a rational number with 1 as the denominator" do + 298.to_r.denominator.should == 1 + end + + it "works even if self is a Bignum" do + bignum = 99999**999 + bignum.should be_an_instance_of(Bignum) + bignum.to_r.should == Rational(bignum, 1) + end + + it "raises an ArgumentError if given any arguments" do + lambda { 287.to_r(2) }.should raise_error(ArgumentError) + lambda { 9102826.to_r(309, [], 71) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/integer/truncate_spec.rb b/spec/rubyspec/core/integer/truncate_spec.rb new file mode 100644 index 0000000000..b503b1e4e8 --- /dev/null +++ b/spec/rubyspec/core/integer/truncate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Integer#truncate" do + it_behaves_like(:integer_to_i, :truncate) +end diff --git a/spec/rubyspec/core/integer/upto_spec.rb b/spec/rubyspec/core/integer/upto_spec.rb new file mode 100644 index 0000000000..405f8e4817 --- /dev/null +++ b/spec/rubyspec/core/integer/upto_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Integer#upto [stop] when self and stop are Fixnums" do + it "does not yield when stop is less than self" do + result = [] + 5.upto(4) { |x| result << x } + result.should == [] + end + + it "yields once when stop equals self" do + result = [] + 5.upto(5) { |x| result << x } + result.should == [5] + end + + it "yields while increasing self until it is less than stop" do + result = [] + 2.upto(5) { |x| result << x } + result.should == [2, 3, 4, 5] + end + + it "yields while increasing self until it is greater than floor of a Float endpoint" do + result = [] + 9.upto(13.3) {|i| result << i} + -5.upto(-1.3) {|i| result << i} + result.should == [9,10,11,12,13,-5,-4,-3,-2] + end + + it "raises an ArgumentError for non-numeric endpoints" do + lambda { 1.upto("A") {|x| p x} }.should raise_error(ArgumentError) + lambda { 1.upto(nil) {|x| p x} }.should raise_error(ArgumentError) + end + + describe "when no block is given" do + it "returns an Enumerator" do + result = [] + + enum = 2.upto(5) + enum.each { |i| result << i } + + result.should == [2, 3, 4, 5] + end + + describe "returned Enumerator" do + describe "size" do + it "raises an ArgumentError for non-numeric endpoints" do + enum = 1.upto("A") + lambda { enum.size }.should raise_error(ArgumentError) + enum = 1.upto(nil) + lambda { enum.size }.should raise_error(ArgumentError) + end + + it "returns stop - self + 1" do + 5.upto(10).size.should == 6 + 1.upto(10).size.should == 10 + 0.upto(10).size.should == 11 + 0.upto(0).size.should == 1 + -5.upto(-3).size.should == 3 + end + + it "returns 0 when stop < self" do + 5.upto(4).size.should == 0 + 0.upto(-5).size.should == 0 + -3.upto(-5).size.should == 0 + end + end + end + end +end diff --git a/spec/rubyspec/core/io/advise_spec.rb b/spec/rubyspec/core/io/advise_spec.rb new file mode 100644 index 0000000000..bd3bf24bec --- /dev/null +++ b/spec/rubyspec/core/io/advise_spec.rb @@ -0,0 +1,96 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#advise" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "raises a TypeError if advise is not a Symbol" do + lambda { + @io.advise("normal") + }.should raise_error(TypeError) + end + + it "raises a TypeError if offsert cannot be coerced to an Integer" do + lambda { + @io.advise(:normal, "wat") + }.should raise_error(TypeError) + end + + it "raises a TypeError if len cannot be coerced to an Integer" do + lambda { + @io.advise(:normal, 0, "wat") + }.should raise_error(TypeError) + end + + it "raises a RangeError if offset is too big" do + lambda { + @io.advise(:normal, 10 ** 32) + }.should raise_error(RangeError) + end + + it "raises a RangeError if len is too big" do + lambda { + @io.advise(:normal, 0, 10 ** 32) + }.should raise_error(RangeError) + end + + it "raises a NotImplementedError if advise is not recognized" do + lambda{ + @io.advise(:foo) + }.should raise_error(NotImplementedError) + end + + it "supports the normal advice type" do + @io.advise(:normal).should be_nil + end + + it "supports the sequential advice type" do + @io.advise(:sequential).should be_nil + end + + it "supports the random advice type" do + @io.advise(:random).should be_nil + end + + it "supports the dontneed advice type" do + @io.advise(:dontneed).should be_nil + end + + it "supports the noreuse advice type" do + @io.advise(:noreuse).should be_nil + end + + platform_is_not :linux do + it "supports the willneed advice type" do + @io.advise(:willneed).should be_nil + end + end + + platform_is :linux do + require 'etc' + uname = if Etc.respond_to?(:uname) + Etc.uname[:release] + else + `uname -r`.chomp + end + if (uname.split('.').map(&:to_i) <=> [3,6]) < 0 + # [ruby-core:65355] tmpfs is not supported + else + it "supports the willneed advice type" do + @io.advise(:willneed).should be_nil + end + end + end + + it "raises an IOError if the stream is closed" do + @io.close + lambda { @io.advise(:normal) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/binmode_spec.rb b/spec/rubyspec/core/io/binmode_spec.rb new file mode 100644 index 0000000000..f437c8a4a4 --- /dev/null +++ b/spec/rubyspec/core/io/binmode_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#binmode" do + before :each do + @name = tmp("io_binmode.txt") + end + + after :each do + @io.close if @io and !@io.closed? + rm_r @name + end + + it "returns self" do + @io = new_io(@name) + @io.binmode.should equal(@io) + end + + it "raises an IOError on closed stream" do + lambda { IOSpecs.closed_io.binmode }.should raise_error(IOError) + end + + it "sets external encoding to binary" do + @io = new_io(@name, "w:utf-8") + @io.binmode + @io.external_encoding.should == Encoding::BINARY + end + + it "sets internal encoding to nil" do + @io = new_io(@name, "w:utf-8:ISO-8859-1") + @io.binmode + @io.internal_encoding.should == nil + end +end + +describe "IO#binmode?" do + before :each do + @filename = tmp("IO_binmode_file") + @file = File.open(@filename, "w") + @duped = nil + end + + after :each do + @duped.close if @duped + @file.close + rm_r @filename + end + + it "is true after a call to IO#binmode" do + @file.binmode?.should be_false + @file.binmode + @file.binmode?.should be_true + end + + it "propagates to dup'ed IO objects" do + @file.binmode + @duped = @file.dup + @duped.binmode?.should == @file.binmode? + end +end diff --git a/spec/rubyspec/core/io/binread_spec.rb b/spec/rubyspec/core/io/binread_spec.rb new file mode 100644 index 0000000000..b592639f9d --- /dev/null +++ b/spec/rubyspec/core/io/binread_spec.rb @@ -0,0 +1,49 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO.binread" do + before :each do + @internal = Encoding.default_internal + + @fname = tmp('io_read.txt') + @contents = "1234567890" + touch(@fname) { |f| f.write @contents } + end + + after :each do + rm_r @fname + Encoding.default_internal = @internal + end + + it "reads the contents of a file" do + IO.binread(@fname).should == @contents + end + + it "reads the contents of a file up to a certain size when specified" do + IO.binread(@fname, 5).should == @contents.slice(0..4) + end + + it "reads the contents of a file from an offset of a specific size when specified" do + IO.binread(@fname, 5, 3).should == @contents.slice(3, 5) + end + + it "returns a String in ASCII-8BIT encoding" do + IO.binread(@fname).encoding.should == Encoding::ASCII_8BIT + end + + it "returns a String in ASCII-8BIT encoding regardless of Encoding.default_internal" do + Encoding.default_internal = Encoding::EUC_JP + IO.binread(@fname).encoding.should == Encoding::ASCII_8BIT + end + + it "raises an ArgumentError when not passed a valid length" do + lambda { IO.binread @fname, -1 }.should raise_error(ArgumentError) + end + + ruby_version_is "2.3" do # MRI leaks the fd on older versions + it "raises an Errno::EINVAL when not passed a valid offset" do + lambda { IO.binread @fname, 0, -1 }.should raise_error(Errno::EINVAL) + end + end +end diff --git a/spec/rubyspec/core/io/binwrite_spec.rb b/spec/rubyspec/core/io/binwrite_spec.rb new file mode 100644 index 0000000000..ec964d07a8 --- /dev/null +++ b/spec/rubyspec/core/io/binwrite_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/binwrite', __FILE__) + +describe "IO.binwrite" do + it_behaves_like :io_binwrite, :binwrite + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/io/bytes_spec.rb b/spec/rubyspec/core/io/bytes_spec.rb new file mode 100644 index 0000000000..3eb51883c4 --- /dev/null +++ b/spec/rubyspec/core/io/bytes_spec.rb @@ -0,0 +1,43 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#bytes" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "returns an enumerator of the next bytes from the stream" do + enum = @io.bytes + enum.should be_an_instance_of(Enumerator) + @io.readline.should == "Voici la ligne une.\n" + enum.first(5).should == [81, 117, 105, 32, 195] + end + + it "yields each byte" do + count = 0 + ScratchPad.record [] + @io.each_byte do |byte| + ScratchPad << byte + break if 4 < count += 1 + end + + ScratchPad.recorded.should == [86, 111, 105, 99, 105] + end + + it "raises an IOError on closed stream" do + enum = IOSpecs.closed_io.bytes + lambda { enum.first }.should raise_error(IOError) + end + + it "raises an IOError on an enumerator for a stream that has been closed" do + enum = @io.bytes + enum.first.should == 86 + @io.close + lambda { enum.first }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/chars_spec.rb b/spec/rubyspec/core/io/chars_spec.rb new file mode 100644 index 0000000000..e38160274f --- /dev/null +++ b/spec/rubyspec/core/io/chars_spec.rb @@ -0,0 +1,12 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/chars', __FILE__) + +describe "IO#chars" do + it_behaves_like :io_chars, :chars +end + +describe "IO#chars" do + it_behaves_like :io_chars_empty, :chars +end diff --git a/spec/rubyspec/core/io/close_on_exec_spec.rb b/spec/rubyspec/core/io/close_on_exec_spec.rb new file mode 100644 index 0000000000..46a58374f9 --- /dev/null +++ b/spec/rubyspec/core/io/close_on_exec_spec.rb @@ -0,0 +1,121 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :io_close_on_exec, shared: true do + it "sets the close-on-exec flag if true" do + @io.close_on_exec = true + @io.close_on_exec?.should == true + end + + it "sets the close-on-exec flag if non-false" do + @io.close_on_exec = :true + @io.close_on_exec?.should == true + end + + it "unsets the close-on-exec flag if false" do + @io.close_on_exec = true + @io.close_on_exec = false + @io.close_on_exec?.should == false + end + + it "unsets the close-on-exec flag if nil" do + @io.close_on_exec = true + @io.close_on_exec = nil + @io.close_on_exec?.should == false + end + + it "ensures the IO's file descriptor is closed in exec'ed processes" do + require 'fcntl' + @io.close_on_exec = true + (@io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC).should == Fcntl::FD_CLOEXEC + end + + it "raises IOError if called on a closed IO" do + @io.close + lambda { @io.close_on_exec = true }.should raise_error(IOError) + end + + it "returns nil" do + @io.send(:close_on_exec=, true).should be_nil + end +end + +describe "IO#close_on_exec=" do + before :each do + @name = tmp('io_close_on_exec.txt') + @io = new_io @name + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + platform_is :windows do + ruby_version_is ""..."2.3" do + it "returns false from #respond_to?" do + @io.respond_to?(:close_on_exec=).should be_false + end + + it "raises a NotImplementedError when called" do + lambda { @io.close_on_exec = true }.should raise_error(NotImplementedError) + end + end + + ruby_version_is "2.3" do + it_should_behave_like :io_close_on_exec + end + end + + platform_is_not :windows do + it_should_behave_like :io_close_on_exec + end +end + + +describe :io_is_close_on_exec, shared: true do + it "returns true by default" do + @io.close_on_exec?.should == true + end + + it "returns true if set" do + @io.close_on_exec = true + @io.close_on_exec?.should == true + end + + it "raises IOError if called on a closed IO" do + @io.close + lambda { @io.close_on_exec? }.should raise_error(IOError) + end +end + +describe "IO#close_on_exec?" do + before :each do + @name = tmp('io_close_on_exec.txt') + @io = new_io @name + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + platform_is :windows do + ruby_version_is ""..."2.3" do + it "returns false from #respond_to?" do + @io.respond_to?(:close_on_exec?).should be_false + end + + it "raises a NotImplementedError when called" do + lambda { @io.close_on_exec? }.should raise_error(NotImplementedError) + end + end + + ruby_version_is "2.3" do + it_should_behave_like :io_close_on_exec + end + end + + platform_is_not :windows do + it_should_behave_like :io_is_close_on_exec + end +end diff --git a/spec/rubyspec/core/io/close_read_spec.rb b/spec/rubyspec/core/io/close_read_spec.rb new file mode 100644 index 0000000000..b5aba57795 --- /dev/null +++ b/spec/rubyspec/core/io/close_read_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#close_read" do + + before :each do + @io = IO.popen 'cat', "r+" + @path = tmp('io.close.txt') + end + + after :each do + @io.close unless @io.closed? + rm_r @path + end + + it "closes the read end of a duplex I/O stream" do + @io.close_read + + lambda { @io.read }.should raise_error(IOError) + end + + ruby_version_is ''...'2.3' do + it "raises an IOError on subsequent invocations" do + @io.close_read + + lambda { @io.close_read }.should raise_error(IOError) + end + end + + ruby_version_is '2.3' do + it "does nothing on subsequent invocations" do + @io.close_read + + @io.close_read.should be_nil + end + end + + it "allows subsequent invocation of close" do + @io.close_read + + lambda { @io.close }.should_not raise_error + end + + it "raises an IOError if the stream is writable and not duplexed" do + io = File.open @path, 'w' + + begin + lambda { io.close_read }.should raise_error(IOError) + ensure + io.close unless io.closed? + end + end + + it "closes the stream if it is neither writable nor duplexed" do + io_close_path = @path + touch io_close_path + + io = File.open io_close_path + + io.close_read + + io.closed?.should == true + end + + ruby_version_is ''...'2.3' do + it "raises IOError on closed stream" do + @io.close + + lambda { @io.close_read }.should raise_error(IOError) + end + end + + ruby_version_is '2.3' do + it "does nothing on closed stream" do + @io.close + + @io.close_read.should be_nil + end + end +end diff --git a/spec/rubyspec/core/io/close_spec.rb b/spec/rubyspec/core/io/close_spec.rb new file mode 100644 index 0000000000..0e51ec23d2 --- /dev/null +++ b/spec/rubyspec/core/io/close_spec.rb @@ -0,0 +1,82 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#close" do + before :each do + @name = tmp('io_close.txt') + @io = new_io @name + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "closes the stream" do + @io.close + @io.closed?.should == true + end + + it "returns nil" do + @io.close.should == nil + end + + it "raises an IOError reading from a closed IO" do + @io.close + lambda { @io.read }.should raise_error(IOError) + end + + it "raises an IOError writing to a closed IO" do + @io.close + lambda { @io.write "data" }.should raise_error(IOError) + end + + ruby_version_is ''...'2.3' do + it "raises an IOError if closed" do + @io.close + lambda { @io.close }.should raise_error(IOError) + end + end + + ruby_version_is "2.3" do + it "does nothing if already closed" do + @io.close + + @io.close.should be_nil + end + end +end + +describe "IO#close on an IO.popen stream" do + + it "clears #pid" do + io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r' + + io.pid.should_not == 0 + + io.close + + lambda { io.pid }.should raise_error(IOError) + end + + it "sets $?" do + io = IO.popen ruby_cmd('exit 0'), 'r' + io.close + + $?.exitstatus.should == 0 + + io = IO.popen ruby_cmd('exit 1'), 'r' + io.close + + $?.exitstatus.should == 1 + end + + it "waits for the child to exit" do + io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r' + io.close + + $?.exitstatus.should_not == 0 + end + +end + diff --git a/spec/rubyspec/core/io/close_write_spec.rb b/spec/rubyspec/core/io/close_write_spec.rb new file mode 100644 index 0000000000..c901aac499 --- /dev/null +++ b/spec/rubyspec/core/io/close_write_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#close_write" do + before :each do + @io = IO.popen 'cat', 'r+' + @path = tmp('io.close.txt') + end + + after :each do + @io.close unless @io.closed? + rm_r @path + end + + it "closes the write end of a duplex I/O stream" do + @io.close_write + + lambda { @io.write "attempt to write" }.should raise_error(IOError) + end + + ruby_version_is ''...'2.3' do + it "raises an IOError on subsequent invocations" do + @io.close_write + + lambda { @io.close_write }.should raise_error(IOError) + end + end + + ruby_version_is '2.3' do + it "does nothing on subsequent invocations" do + @io.close_write + + @io.close_write.should be_nil + end + end + + it "allows subsequent invocation of close" do + @io.close_write + + lambda { @io.close }.should_not raise_error + end + + it "raises an IOError if the stream is readable and not duplexed" do + io = File.open @path, 'w+' + + begin + lambda { io.close_write }.should raise_error(IOError) + ensure + io.close unless io.closed? + end + end + + it "closes the stream if it is neither readable nor duplexed" do + io = File.open @path, 'w' + + io.close_write + + io.closed?.should == true + end + + it "flushes and closes the write stream" do + @io.puts '12345' + + @io.close_write + + @io.read.should == "12345\n" + end + + ruby_version_is ''...'2.3' do + it "raises IOError on closed stream" do + @io.close + + lambda { @io.close_write }.should raise_error(IOError) + end + end + + ruby_version_is '2.3' do + it "does nothing on closed stream" do + @io.close + + @io.close_write.should be_nil + end + end +end diff --git a/spec/rubyspec/core/io/closed_spec.rb b/spec/rubyspec/core/io/closed_spec.rb new file mode 100644 index 0000000000..80562885a7 --- /dev/null +++ b/spec/rubyspec/core/io/closed_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#closed?" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close + end + + it "returns true on closed stream" do + IOSpecs.closed_io.closed?.should be_true + end + + it "returns false on open stream" do + @io.closed?.should be_false + end +end diff --git a/spec/rubyspec/core/io/codepoints_spec.rb b/spec/rubyspec/core/io/codepoints_spec.rb new file mode 100644 index 0000000000..6e6b9613b5 --- /dev/null +++ b/spec/rubyspec/core/io/codepoints_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/codepoints', __FILE__) + +# See redmine #1667 +describe "IO#codepoints" do + it_behaves_like(:io_codepoints, :codepoints) +end + +describe "IO#codepoints" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "calls the given block" do + r = [] + @io.codepoints { |c| r << c } + r[24].should == 232 + r.last.should == 10 + end +end diff --git a/spec/rubyspec/core/io/constants_spec.rb b/spec/rubyspec/core/io/constants_spec.rb new file mode 100644 index 0000000000..0a2ea70560 --- /dev/null +++ b/spec/rubyspec/core/io/constants_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO::SEEK_SET" do + it "is defined" do + IO.const_defined?(:SEEK_SET).should == true + end +end + +describe "IO::SEEK_CUR" do + it "is defined" do + IO.const_defined?(:SEEK_CUR).should == true + end +end + +describe "IO::SEEK_END" do + it "is defined" do + IO.const_defined?(:SEEK_END).should == true + end +end diff --git a/spec/rubyspec/core/io/copy_stream_spec.rb b/spec/rubyspec/core/io/copy_stream_spec.rb new file mode 100644 index 0000000000..344746fac3 --- /dev/null +++ b/spec/rubyspec/core/io/copy_stream_spec.rb @@ -0,0 +1,282 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :io_copy_stream_to_file, shared: true do + it "copies the entire IO contents to the file" do + IO.copy_stream(@object.from, @to_name) + File.read(@to_name).should == @content + IO.copy_stream(@from_bigfile, @to_name) + File.read(@to_name).should == @content_bigfile + end + + it "returns the number of bytes copied" do + IO.copy_stream(@object.from, @to_name).should == @size + IO.copy_stream(@from_bigfile, @to_name).should == @size_bigfile + end + + it "copies only length bytes when specified" do + IO.copy_stream(@object.from, @to_name, 8).should == 8 + File.read(@to_name).should == "Line one" + end + + it "calls #to_path to convert on object to a file name" do + obj = mock("io_copy_stream_to") + obj.should_receive(:to_path).and_return(@to_name) + + IO.copy_stream(@object.from, obj) + File.read(@to_name).should == @content + end + + it "raises a TypeError if #to_path does not return a String" do + obj = mock("io_copy_stream_to") + obj.should_receive(:to_path).and_return(1) + + lambda { IO.copy_stream(@object.from, obj) }.should raise_error(TypeError) + end +end + +describe :io_copy_stream_to_file_with_offset, shared: true do + platform_is_not :windows do + it "copies only length bytes from the offset" do + IO.copy_stream(@object.from, @to_name, 8, 4).should == 8 + File.read(@to_name).should == " one\n\nLi" + end + end +end + +describe :io_copy_stream_to_io, shared: true do + it "copies the entire IO contents to the IO" do + IO.copy_stream(@object.from, @to_io) + File.read(@to_name).should == @content + IO.copy_stream(@from_bigfile, @to_io) + File.read(@to_name).should == (@content + @content_bigfile) + end + + it "returns the number of bytes copied" do + IO.copy_stream(@object.from, @to_io).should == @size + IO.copy_stream(@from_bigfile, @to_io).should == @size_bigfile + end + + it "starts writing at the destination IO's current position" do + @to_io.write("prelude ") + IO.copy_stream(@object.from, @to_io) + File.read(@to_name).should == ("prelude " + @content) + end + + it "leaves the destination IO position at the last write" do + IO.copy_stream(@object.from, @to_io) + @to_io.pos.should == @size + end + + it "raises an IOError if the destination IO is not open for writing" do + @to_io.close + @to_io = new_io @to_name, "r" + lambda { IO.copy_stream @object.from, @to_io }.should raise_error(IOError) + end + + it "does not close the destination IO" do + IO.copy_stream(@object.from, @to_io) + @to_io.closed?.should be_false + end + + it "copies only length bytes when specified" do + IO.copy_stream(@object.from, @to_io, 8).should == 8 + File.read(@to_name).should == "Line one" + end +end + +describe :io_copy_stream_to_io_with_offset, shared: true do + platform_is_not :windows do + it "copies only length bytes from the offset" do + IO.copy_stream(@object.from, @to_io, 8, 4).should == 8 + File.read(@to_name).should == " one\n\nLi" + end + end +end + +describe "IO.copy_stream" do + before :each do + @from_name = fixture __FILE__, "copy_stream.txt" + @to_name = tmp("io_copy_stream_io_name") + + @content = IO.read(@from_name) + @size = @content.size + + @from_bigfile = tmp("io_copy_stream_bigfile") + @content_bigfile = "A" * 17_000 + touch(@from_bigfile){|f| f.print @content_bigfile } + @size_bigfile = @content_bigfile.size + end + + after :each do + rm_r @to_name, @from_bigfile + end + + describe "from an IO" do + before :each do + @from_io = new_io @from_name, "rb" + IOSpecs::CopyStream.from = @from_io + end + + after :each do + @from_io.close + end + + it "raises an IOError if the source IO is not open for reading" do + @from_io.close + @from_io = new_io @from_bigfile, "a" + lambda { IO.copy_stream @from_io, @to_name }.should raise_error(IOError) + end + + it "does not close the source IO" do + IO.copy_stream(@from_io, @to_name) + @from_io.closed?.should be_false + end + + platform_is_not :windows do + it "does not change the IO offset when an offset is specified" do + @from_io.pos = 10 + IO.copy_stream(@from_io, @to_name, 8, 4) + @from_io.pos.should == 10 + end + end + + it "does change the IO offset when an offset is not specified" do + @from_io.pos = 10 + IO.copy_stream(@from_io, @to_name) + @from_io.pos.should == 42 + end + + describe "to a file name" do + it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream + it_behaves_like :io_copy_stream_to_file_with_offset, nil, IOSpecs::CopyStream + end + + describe "to an IO" do + before :each do + @to_io = new_io @to_name, "wb" + end + + after :each do + @to_io.close + end + + it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream + it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream + end + end + + describe "from a file name" do + before :each do + IOSpecs::CopyStream.from = @from_name + end + + it "calls #to_path to convert on object to a file name" do + obj = mock("io_copy_stream_from") + obj.should_receive(:to_path).and_return(@from_name) + + IO.copy_stream(obj, @to_name) + File.read(@to_name).should == @content + end + + it "raises a TypeError if #to_path does not return a String" do + obj = mock("io_copy_stream_from") + obj.should_receive(:to_path).and_return(1) + + lambda { IO.copy_stream(obj, @to_name) }.should raise_error(TypeError) + end + + describe "to a file name" do + it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream + it_behaves_like :io_copy_stream_to_file_with_offset, nil, IOSpecs::CopyStream + end + + describe "to an IO" do + before :each do + @to_io = new_io @to_name, "wb" + end + + after :each do + @to_io.close + end + + it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream + it_behaves_like :io_copy_stream_to_io_with_offset, nil, IOSpecs::CopyStream + end + end + + describe "from a pipe IO" do + before :each do + @from_io = IOSpecs.pipe_fixture(@content) + IOSpecs::CopyStream.from = @from_io + end + + after :each do + @from_io.close + end + + it "does not close the source IO" do + IO.copy_stream(@from_io, @to_name) + @from_io.closed?.should be_false + end + + platform_is_not :windows do + it "raises an error when an offset is specified" do + lambda { IO.copy_stream(@from_io, @to_name, 8, 4) }.should raise_error(Errno::ESPIPE) + end + end + + describe "to a file name" do + it_behaves_like :io_copy_stream_to_file, nil, IOSpecs::CopyStream + end + + describe "to an IO" do + before :each do + @to_io = new_io @to_name, "wb" + end + + after :each do + @to_io.close + end + + it_behaves_like :io_copy_stream_to_io, nil, IOSpecs::CopyStream + end + end + + describe "with non-IO Objects" do + before do + @io = new_io @from_name, "rb" + end + + after do + @io.close unless @io.closed? + end + + it "calls #readpartial on the source Object if defined" do + from = IOSpecs::CopyStreamReadPartial.new @io + + IO.copy_stream(from, @to_name) + File.read(@to_name).should == @content + end + + it "calls #read on the source Object" do + from = IOSpecs::CopyStreamRead.new @io + + IO.copy_stream(from, @to_name) + File.read(@to_name).should == @content + end + + it "calls #write on the destination Object" do + to = mock("io_copy_stream_to_object") + to.should_receive(:write).with(@content).and_return(@content.size) + + IO.copy_stream(@from_name, to) + end + + it "does not call #pos on the source if no offset is given" do + @io.should_not_receive(:pos) + IO.copy_stream(@io, @to_name) + end + + end +end diff --git a/spec/rubyspec/core/io/dup_spec.rb b/spec/rubyspec/core/io/dup_spec.rb new file mode 100644 index 0000000000..a90b04aa5d --- /dev/null +++ b/spec/rubyspec/core/io/dup_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#dup" do + before :each do + @file = tmp("rubinius_spec_io_dup_#{$$}_#{Time.now.to_f}") + @f = File.open @file, 'w+' + @i = @f.dup + + @f.sync = true + @i.sync = true + end + + after :each do + @i.close if @i && !@i.closed? + @f.close if @f && !@f.closed? + rm_r @file + end + + it "returns a new IO instance" do + @i.class.should == @f.class + end + + it "sets a new descriptor on the returned object" do + @i.fileno.should_not == @f.fileno + end + +quarantine! do # This does not appear to be consistent across platforms + it "shares the original stream between the two IOs" do + start = @f.pos + @i.pos.should == start + + s = "Hello, wo.. wait, where am I?\n" + s2 = " Muhahahaa!" + + @f.write s + @i.pos.should == @f.pos + + @i.rewind + @i.gets.should == s + + @i.rewind + @i.write s2 + + @f.rewind + @f.gets.should == "#{s2}\n" + end +end + + it "allows closing the new IO without affecting the original" do + @i.close + lambda { @f.gets }.should_not raise_error(Exception) + + @i.closed?.should == true + @f.closed?.should == false + end + + it "allows closing the original IO without affecting the new one" do + @f.close + lambda { @i.gets }.should_not raise_error(Exception) + + @i.closed?.should == false + @f.closed?.should == true + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.dup }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/each_byte_spec.rb b/spec/rubyspec/core/io/each_byte_spec.rb new file mode 100644 index 0000000000..0dc535a159 --- /dev/null +++ b/spec/rubyspec/core/io/each_byte_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#each_byte" do + before :each do + ScratchPad.record [] + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.each_byte {} }.should raise_error(IOError) + end + + it "yields each byte" do + count = 0 + @io.each_byte do |byte| + ScratchPad << byte + break if 4 < count += 1 + end + + ScratchPad.recorded.should == [86, 111, 105, 99, 105] + end + + describe "when no block is given" do + it "returns an Enumerator" do + enum = @io.each_byte + enum.should be_an_instance_of(Enumerator) + enum.first(5).should == [86, 111, 105, 99, 105] + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @io.each_byte.size.should == nil + end + end + end + end +end + +describe "IO#each_byte" do + before :each do + @io = IOSpecs.io_fixture "empty.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "returns self on an empty stream" do + @io.each_byte { |b| }.should equal(@io) + end +end diff --git a/spec/rubyspec/core/io/each_char_spec.rb b/spec/rubyspec/core/io/each_char_spec.rb new file mode 100644 index 0000000000..69c6739920 --- /dev/null +++ b/spec/rubyspec/core/io/each_char_spec.rb @@ -0,0 +1,12 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/chars', __FILE__) + +describe "IO#each_char" do + it_behaves_like :io_chars, :each_char +end + +describe "IO#each_char" do + it_behaves_like :io_chars_empty, :each_char +end diff --git a/spec/rubyspec/core/io/each_codepoint_spec.rb b/spec/rubyspec/core/io/each_codepoint_spec.rb new file mode 100644 index 0000000000..eb16e004fa --- /dev/null +++ b/spec/rubyspec/core/io/each_codepoint_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/codepoints', __FILE__) + +# See redmine #1667 +describe "IO#each_codepoint" do + it_behaves_like(:io_codepoints, :codepoints) +end + +describe "IO#each_codepoint" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "calls the given block" do + r = [] + @io.each_codepoint { |c| r << c } + r[24].should == 232 + r.last.should == 10 + end + + it "returns self" do + @io.each_codepoint { |l| l }.should equal(@io) + end +end + +describe "IO#each_codepoint" do + before :each do + @io = IOSpecs.io_fixture("incomplete.txt") + end + + after :each do + @io.close if @io + end + + ruby_version_is "2.3" do # earlier versions stay blocked + it "raises an exception at incomplete character before EOF when conversion takes place" do + lambda { @io.each_codepoint {} }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/io/each_line_spec.rb b/spec/rubyspec/core/io/each_line_spec.rb new file mode 100644 index 0000000000..d4d8af7902 --- /dev/null +++ b/spec/rubyspec/core/io/each_line_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each', __FILE__) + +describe "IO#each_line" do + it_behaves_like :io_each, :each_line +end + +describe "IO#each_line" do + it_behaves_like :io_each_default_separator, :each_line +end diff --git a/spec/rubyspec/core/io/each_spec.rb b/spec/rubyspec/core/io/each_spec.rb new file mode 100644 index 0000000000..4d6aa50fb2 --- /dev/null +++ b/spec/rubyspec/core/io/each_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each', __FILE__) + +describe "IO#each" do + it_behaves_like :io_each, :each +end + +describe "IO#each" do + it_behaves_like :io_each_default_separator, :each +end diff --git a/spec/rubyspec/core/io/eof_spec.rb b/spec/rubyspec/core/io/eof_spec.rb new file mode 100644 index 0000000000..a2c5e563f0 --- /dev/null +++ b/spec/rubyspec/core/io/eof_spec.rb @@ -0,0 +1,107 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#eof?" do + before :each do + @name = tmp("empty.txt") + touch @name + end + + after :each do + rm_r @name + end + + it "returns true on an empty stream that has just been opened" do + File.open(@name) { |empty| empty.eof?.should == true } + end + + it "raises IOError on stream not opened for reading" do + lambda do + File.open(@name, "w") { |f| f.eof? } + end.should raise_error(IOError) + end +end + +describe "IO#eof?" do + before :each do + @name = fixture __FILE__, "lines.txt" + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io && !@io.closed? + end + + it "returns false when not at end of file" do + @io.read 1 + @io.eof?.should == false + end + + it "returns true after reading with read with no parameters" do + @io.read() + @io.eof?.should == true + end + + it "returns true after reading with read" do + @io.read(File.size(@name)) + @io.eof?.should == true + end + + it "returns true after reading with sysread" do + @io.sysread(File.size(@name)) + @io.eof?.should == true + end + + it "returns true after reading with readlines" do + @io.readlines + @io.eof?.should == true + end + + it "returns false on just opened non-empty stream" do + @io.eof?.should == false + end + + it "does not consume the data from the stream" do + @io.eof?.should == false + @io.getc.should == 'V' + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.eof? }.should raise_error(IOError) + end + + it "raises IOError on stream closed for reading by close_read" do + @io.close_read + lambda { @io.eof? }.should raise_error(IOError) + end + + it "returns true on one-byte stream after single-byte read" do + File.open(File.dirname(__FILE__) + '/fixtures/one_byte.txt') { |one_byte| + one_byte.read(1) + one_byte.eof?.should == true + } + end +end + +describe "IO#eof?" do + after :each do + @r.close if @r && !@r.closed? + @w.close if @w && !@w.closed? + end + + it "returns true on receiving side of Pipe when writing side is closed" do + @r, @w = IO.pipe + @w.close + @r.eof?.should == true + end + + it "returns false on receiving side of Pipe when writing side wrote some data" do + @r, @w = IO.pipe + @w.puts "hello" + @r.eof?.should == false + @w.close + @r.eof?.should == false + @r.read + @r.eof?.should == true + end +end diff --git a/spec/rubyspec/core/io/external_encoding_spec.rb b/spec/rubyspec/core/io/external_encoding_spec.rb new file mode 100644 index 0000000000..ec85eba2ee --- /dev/null +++ b/spec/rubyspec/core/io/external_encoding_spec.rb @@ -0,0 +1,218 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe :io_external_encoding_write, shared: true do + describe "when Encoding.default_internal is nil" do + before :each do + Encoding.default_internal = nil + end + + it "returns nil" do + @io = new_io @name, @object + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should be_nil + end + + it "returns the external encoding specified when the instance was created" do + @io = new_io @name, "#{@object}:ibm866" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::IBM866) + end + + it "returns the encoding set by #set_encoding" do + @io = new_io @name, "#{@object}:ibm866" + @io.set_encoding Encoding::EUC_JP, nil + @io.external_encoding.should equal(Encoding::EUC_JP) + end + end + + describe "when Encoding.default_external != Encoding.default_internal" do + before :each do + Encoding.default_external = Encoding::IBM437 + Encoding.default_internal = Encoding::IBM866 + end + + it "returns the value of Encoding.default_external when the instance was created" do + @io = new_io @name, @object + Encoding.default_external = Encoding::UTF_8 + @io.external_encoding.should equal(Encoding::IBM437) + end + + it "returns the external encoding specified when the instance was created" do + @io = new_io @name, "#{@object}:ibm866" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::IBM866) + end + + it "returns the encoding set by #set_encoding" do + @io = new_io @name, "#{@object}:ibm866" + @io.set_encoding Encoding::EUC_JP, nil + @io.external_encoding.should equal(Encoding::EUC_JP) + end + end + + describe "when Encoding.default_external == Encoding.default_internal" do + before :each do + Encoding.default_external = Encoding::IBM866 + Encoding.default_internal = Encoding::IBM866 + end + + it "returns the value of Encoding.default_external when the instance was created" do + @io = new_io @name, @object + Encoding.default_external = Encoding::UTF_8 + @io.external_encoding.should equal(Encoding::IBM866) + end + + it "returns the external encoding specified when the instance was created" do + @io = new_io @name, "#{@object}:ibm866" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::IBM866) + end + + it "returns the encoding set by #set_encoding" do + @io = new_io @name, "#{@object}:ibm866" + @io.set_encoding Encoding::EUC_JP, nil + @io.external_encoding.should equal(Encoding::EUC_JP) + end + end + end + + describe "IO#external_encoding" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + @name = tmp("io_external_encoding") + touch(@name) + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + + @io.close if @io + rm_r @name + end + + describe "with 'r' mode" do + describe "when Encoding.default_internal is nil" do + before :each do + Encoding.default_internal = nil + Encoding.default_external = Encoding::IBM866 + end + + it "returns Encoding.default_external if the external encoding is not set" do + @io = new_io @name, "r" + @io.external_encoding.should equal(Encoding::IBM866) + end + + it "returns Encoding.default_external when that encoding is changed after the instance is created" do + @io = new_io @name, "r" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::IBM437) + end + + it "returns the external encoding specified when the instance was created" do + @io = new_io @name, "r:utf-8" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::UTF_8) + end + + it "returns the encoding set by #set_encoding" do + @io = new_io @name, "r:utf-8" + @io.set_encoding Encoding::EUC_JP, nil + @io.external_encoding.should equal(Encoding::EUC_JP) + end + end + + describe "when Encoding.default_external == Encoding.default_internal" do + before :each do + Encoding.default_external = Encoding::IBM866 + Encoding.default_internal = Encoding::IBM866 + end + + it "returns the value of Encoding.default_external when the instance was created" do + @io = new_io @name, "r" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::IBM866) + end + + it "returns the external encoding specified when the instance was created" do + @io = new_io @name, "r:utf-8" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::UTF_8) + end + + it "returns the encoding set by #set_encoding" do + @io = new_io @name, "r:utf-8" + @io.set_encoding Encoding::EUC_JP, nil + @io.external_encoding.should equal(Encoding::EUC_JP) + end + end + + describe "when Encoding.default_external != Encoding.default_internal" do + before :each do + Encoding.default_external = Encoding::IBM437 + Encoding.default_internal = Encoding::IBM866 + end + + + it "returns the external encoding specified when the instance was created" do + @io = new_io @name, "r:utf-8" + Encoding.default_external = Encoding::IBM437 + @io.external_encoding.should equal(Encoding::UTF_8) + end + + it "returns the encoding set by #set_encoding" do + @io = new_io @name, "r:utf-8" + @io.set_encoding Encoding::EUC_JP, nil + @io.external_encoding.should equal(Encoding::EUC_JP) + end + end + end + + describe "with 'rb' mode" do + it "returns Encoding::ASCII_8BIT" do + @io = new_io @name, "rb" + @io.external_encoding.should equal(Encoding::ASCII_8BIT) + end + + it "returns the external encoding specified by the mode argument" do + @io = new_io @name, "rb:ibm437" + @io.external_encoding.should equal(Encoding::IBM437) + end + end + + describe "with 'r+' mode" do + it_behaves_like :io_external_encoding_write, nil, "r+" + end + + describe "with 'w' mode" do + it_behaves_like :io_external_encoding_write, nil, "w" + end + + describe "with 'wb' mode" do + it "returns Encoding::ASCII_8BIT" do + @io = new_io @name, "wb" + @io.external_encoding.should equal(Encoding::ASCII_8BIT) + end + + it "returns the external encoding specified by the mode argument" do + @io = new_io @name, "wb:ibm437" + @io.external_encoding.should equal(Encoding::IBM437) + end + end + + describe "with 'w+' mode" do + it_behaves_like :io_external_encoding_write, nil, "w+" + end + + describe "with 'a' mode" do + it_behaves_like :io_external_encoding_write, nil, "a" + end + + describe "with 'a+' mode" do + it_behaves_like :io_external_encoding_write, nil, "a+" + end + end +end diff --git a/spec/rubyspec/core/io/fcntl_spec.rb b/spec/rubyspec/core/io/fcntl_spec.rb new file mode 100644 index 0000000000..0e20f50f60 --- /dev/null +++ b/spec/rubyspec/core/io/fcntl_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#fcntl" do + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.fcntl(5, 5) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/fdatasync_spec.rb b/spec/rubyspec/core/io/fdatasync_spec.rb new file mode 100644 index 0000000000..9bcbbda336 --- /dev/null +++ b/spec/rubyspec/core/io/fdatasync_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO#fdatasync" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/io/fileno_spec.rb b/spec/rubyspec/core/io/fileno_spec.rb new file mode 100644 index 0000000000..259ac38073 --- /dev/null +++ b/spec/rubyspec/core/io/fileno_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#fileno" do + it "returns the numeric file descriptor of the given IO object" do + $stdout.fileno.should == 1 + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.fileno }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/fixtures/bom_UTF-16BE.txt b/spec/rubyspec/core/io/fixtures/bom_UTF-16BE.txt new file mode 100644 index 0000000000..c7c42e9de4 Binary files /dev/null and b/spec/rubyspec/core/io/fixtures/bom_UTF-16BE.txt differ diff --git a/spec/rubyspec/core/io/fixtures/bom_UTF-16LE.txt b/spec/rubyspec/core/io/fixtures/bom_UTF-16LE.txt new file mode 100644 index 0000000000..53642b6984 Binary files /dev/null and b/spec/rubyspec/core/io/fixtures/bom_UTF-16LE.txt differ diff --git a/spec/rubyspec/core/io/fixtures/bom_UTF-32BE.txt b/spec/rubyspec/core/io/fixtures/bom_UTF-32BE.txt new file mode 100644 index 0000000000..c5efe6c278 Binary files /dev/null and b/spec/rubyspec/core/io/fixtures/bom_UTF-32BE.txt differ diff --git a/spec/rubyspec/core/io/fixtures/bom_UTF-32LE.txt b/spec/rubyspec/core/io/fixtures/bom_UTF-32LE.txt new file mode 100644 index 0000000000..1168384393 Binary files /dev/null and b/spec/rubyspec/core/io/fixtures/bom_UTF-32LE.txt differ diff --git a/spec/rubyspec/core/io/fixtures/bom_UTF-8.txt b/spec/rubyspec/core/io/fixtures/bom_UTF-8.txt new file mode 100644 index 0000000000..ca971bef61 --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/bom_UTF-8.txt @@ -0,0 +1 @@ +UTF-8 diff --git a/spec/rubyspec/core/io/fixtures/classes.rb b/spec/rubyspec/core/io/fixtures/classes.rb new file mode 100644 index 0000000000..5ff6f9aa72 --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/classes.rb @@ -0,0 +1,178 @@ +# -*- encoding: utf-8 -*- + +module IOSpecs + class SubIO < IO + end + + def self.collector + Proc.new { |x| ScratchPad << x } + end + + def self.lines + [ "Voici la ligne une.\n", + "Qui \303\250 la linea due.\n", + "\n", + "\n", + "Aqu\303\255 est\303\241 la l\303\255nea tres.\n", + "Ist hier Linie vier.\n", + "\n", + "Est\303\241 aqui a linha cinco.\n", + "Here is line six.\n" ] + end + + def self.lines_limit + [ "Voici la l", + "igne une.\n", + "Qui è la ", + "linea due.", + "\n", + "\n", + "\n", + "Aquí está", + " la línea", + " tres.\n", + "Ist hier L", + "inie vier.", + "\n", + "\n", + "Está aqui", + " a linha c", + "inco.\n", + "Here is li", + "ne six.\n" ] + end + + def self.lines_space_separator_limit + [ "Voici ", + "la ", + "ligne ", + "une.\nQui ", + "è ", + "la ", + "linea ", + "due.\n\n\nAqu", + "í ", + "está ", + "la ", + "línea ", + "tres.\nIst ", + "hier ", + "Linie ", + "vier.\n\nEst", + "á ", + "aqui ", + "a ", + "linha ", + "cinco.\nHer", + "e ", + "is ", + "line ", + "six.\n" ] + end + + def self.lines_r_separator + [ "Voici la ligne une.\nQui \303\250 la linea due.\n\n\n" \ + "Aqu\303\255 est\303\241 la l\303\255nea tr", + "es.\nIst hier", + " Linie vier", + ".\n\nEst\303\241 aqui a linha cinco.\nHer", + "e is line six.\n" ] + end + + def self.lines_empty_separator + [ "Voici la ligne une.\nQui \303\250 la linea due.\n\n", + "Aqu\303\255 est\303\241 la l\303\255nea tres.\nIst hier Linie vier.\n\n", + "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ] + end + + def self.lines_space_separator + [ "Voici ", "la ", "ligne ", "une.\nQui ", + "\303\250 ", "la ", "linea ", "due.\n\n\nAqu\303\255 ", + "est\303\241 ", "la ", "l\303\255nea ", "tres.\nIst ", + "hier ", "Linie ", "vier.\n\nEst\303\241 ", "aqui ", "a ", + "linha ", "cinco.\nHere ", "is ", "line ", "six.\n" ] + end + + def self.lines_arbitrary_separator + [ "Voici la ligne une.\nQui \303\250", + " la linea due.\n\n\nAqu\303\255 est\303\241 la l\303\255nea tres.\n" \ + "Ist hier Linie vier.\n\nEst\303\241 aqui a linha cinco.\nHere is line six.\n" ] + end + + def self.paragraphs + [ "Voici la ligne une.\nQui \303\250 la linea due.\n\n", + "Aqu\303\255 est\303\241 la l\303\255nea tres.\nIst hier Linie vier.\n\n", + "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ] + end + + # Creates an IO instance for an existing fixture file. The + # file should obviously not be deleted. + def self.io_fixture(name, options_or_mode="r:utf-8") + path = fixture __FILE__, name + name = path if File.exist? path + new_io name, options_or_mode + end + + # Returns a closed instance of IO that was opened to reference + # a fixture file (i.e. the IO instance was perfectly valid at + # one point but is now closed). + def self.closed_io + io = io_fixture "lines.txt" + io.close + io + end + + # Creates a pipe-based IO fixture containing the specified + # contents + def self.pipe_fixture(content) + source, sink = IO.pipe + sink.write content + sink.close + source + end + + # Defines +method+ on +obj+ using the provided +block+. This + # special helper is needed for e.g. IO.open specs to avoid + # mock methods preventing IO#close from running. + def self.io_mock(obj, method, &block) + obj.singleton_class.send(:define_method, method, &block) + end + + module CopyStream + def self.from=(from) + @from = from + end + + def self.from + @from + end + end + + class CopyStreamRead + def initialize(io) + @io = io + end + + def read(size, buf=nil) + @io.read size, buf + end + + def send(*args) + raise "send called" + end + end + + class CopyStreamReadPartial + def initialize(io) + @io = io + end + + def readpartial(size, buf=nil) + @io.readpartial size, buf + end + + def send(*args) + raise "send called" + end + end +end diff --git a/spec/rubyspec/core/io/fixtures/copy_stream.txt b/spec/rubyspec/core/io/fixtures/copy_stream.txt new file mode 100644 index 0000000000..a2d827b351 --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/copy_stream.txt @@ -0,0 +1,6 @@ +Line one + +Line three +Line four + +Line last diff --git a/spec/rubyspec/core/io/fixtures/empty.txt b/spec/rubyspec/core/io/fixtures/empty.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/rubyspec/core/io/fixtures/incomplete.txt b/spec/rubyspec/core/io/fixtures/incomplete.txt new file mode 100644 index 0000000000..23d432f642 --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/incomplete.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spec/rubyspec/core/io/fixtures/lines.txt b/spec/rubyspec/core/io/fixtures/lines.txt new file mode 100644 index 0000000000..a64d8ff6bb --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/lines.txt @@ -0,0 +1,9 @@ +Voici la ligne une. +Qui è la linea due. + + +Aquí está la línea tres. +Ist hier Linie vier. + +Está aqui a linha cinco. +Here is line six. diff --git a/spec/rubyspec/core/io/fixtures/no_bom_UTF-8.txt b/spec/rubyspec/core/io/fixtures/no_bom_UTF-8.txt new file mode 100644 index 0000000000..7edc66b06a --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/no_bom_UTF-8.txt @@ -0,0 +1 @@ +UTF-8 diff --git a/spec/rubyspec/core/io/fixtures/numbered_lines.txt b/spec/rubyspec/core/io/fixtures/numbered_lines.txt new file mode 100644 index 0000000000..70e49a3d98 --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/numbered_lines.txt @@ -0,0 +1,5 @@ +Line 1: One +Line 2: Two +Line 3: Three +Line 4: Four +Line 5: Five diff --git a/spec/rubyspec/core/io/fixtures/one_byte.txt b/spec/rubyspec/core/io/fixtures/one_byte.txt new file mode 100644 index 0000000000..56a6051ca2 --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/one_byte.txt @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/spec/rubyspec/core/io/fixtures/read_binary.txt b/spec/rubyspec/core/io/fixtures/read_binary.txt new file mode 100644 index 0000000000..fa036dca4b --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/read_binary.txt @@ -0,0 +1 @@ +abcdef diff --git a/spec/rubyspec/core/io/fixtures/read_euc_jp.txt b/spec/rubyspec/core/io/fixtures/read_euc_jp.txt new file mode 100644 index 0000000000..0e17cd717a --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/read_euc_jp.txt @@ -0,0 +1 @@ +꤬Ȥ diff --git a/spec/rubyspec/core/io/fixtures/read_text.txt b/spec/rubyspec/core/io/fixtures/read_text.txt new file mode 100644 index 0000000000..5a7a80f6e4 --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/read_text.txt @@ -0,0 +1 @@ +abcâdef diff --git a/spec/rubyspec/core/io/fixtures/reopen_stdout.rb b/spec/rubyspec/core/io/fixtures/reopen_stdout.rb new file mode 100644 index 0000000000..506ba072bd --- /dev/null +++ b/spec/rubyspec/core/io/fixtures/reopen_stdout.rb @@ -0,0 +1,3 @@ +STDOUT.reopen ARGV[0] +system "echo from system" +exec "echo from exec" diff --git a/spec/rubyspec/core/io/flush_spec.rb b/spec/rubyspec/core/io/flush_spec.rb new file mode 100644 index 0000000000..c877650ecd --- /dev/null +++ b/spec/rubyspec/core/io/flush_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#flush" do + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.flush }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/for_fd_spec.rb b/spec/rubyspec/core/io/for_fd_spec.rb new file mode 100644 index 0000000000..b4abc1f87c --- /dev/null +++ b/spec/rubyspec/core/io/for_fd_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "IO.for_fd" do + it_behaves_like :io_new, :for_fd +end + +describe "IO.for_fd" do + it_behaves_like :io_new_errors, :for_fd +end diff --git a/spec/rubyspec/core/io/foreach_spec.rb b/spec/rubyspec/core/io/foreach_spec.rb new file mode 100644 index 0000000000..f5fa1459e9 --- /dev/null +++ b/spec/rubyspec/core/io/foreach_spec.rb @@ -0,0 +1,81 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/readlines', __FILE__) + +describe "IO.foreach" do + before :each do + @name = fixture __FILE__, "lines.txt" + @count = 0 + ScratchPad.record [] + end + + it "updates $. with each yield" do + IO.foreach(@name) { $..should == @count += 1 } + end + + describe "when the filename starts with |" do + it "gets data from the standard out of the subprocess" do + cmd = "|sh -c 'echo hello;echo line2'" + platform_is :windows do + cmd = "|cmd.exe /C echo hello&echo line2" + end + IO.foreach(cmd) { |l| ScratchPad << l } + ScratchPad.recorded.should == ["hello\n", "line2\n"] + end + + with_feature :fork do + it "gets data from a fork when passed -" do + parent_pid = $$ + + IO.foreach("|-") { |l| ScratchPad << l } + + if $$ == parent_pid + ScratchPad.recorded.should == ["hello\n", "from a fork\n"] + else # child + puts "hello" + puts "from a fork" + exit! + end + end + end + end +end + +describe "IO.foreach" do + before :each do + @external = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + + @name = fixture __FILE__, "lines.txt" + ScratchPad.record [] + end + + after :each do + Encoding.default_external = @external + end + + it "sets $_ to nil" do + $_ = "test" + IO.foreach(@name) { } + $_.should be_nil + end + + describe "when no block is given" do + it "returns an Enumerator" do + IO.foreach(@name).should be_an_instance_of(Enumerator) + IO.foreach(@name).to_a.should == IOSpecs.lines + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + IO.foreach(@name).size.should == nil + end + end + end + end + + it_behaves_like :io_readlines, :foreach, IOSpecs.collector + it_behaves_like :io_readlines_options_19, :foreach, IOSpecs.collector +end diff --git a/spec/rubyspec/core/io/fsync_spec.rb b/spec/rubyspec/core/io/fsync_spec.rb new file mode 100644 index 0000000000..7816ecc42b --- /dev/null +++ b/spec/rubyspec/core/io/fsync_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#fsync" do + before :each do + @name = tmp("io_fsync.txt") + ScratchPad.clear + end + + after :each do + rm_r @name + end + + it "raises an IOError on closed stream" do + lambda { IOSpecs.closed_io.fsync }.should raise_error(IOError) + end + + it "writes the buffered data to permanent storage" do + File.open(@name, "w") do |f| + f.write "one hit wonder" + f.fsync.should == 0 + end + end +end diff --git a/spec/rubyspec/core/io/getbyte_spec.rb b/spec/rubyspec/core/io/getbyte_spec.rb new file mode 100644 index 0000000000..cb8929890f --- /dev/null +++ b/spec/rubyspec/core/io/getbyte_spec.rb @@ -0,0 +1,42 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#getbyte" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "returns the next byte from the stream" do + @io.readline.should == "Voici la ligne une.\n" + letters = @io.getbyte, @io.getbyte, @io.getbyte, @io.getbyte, @io.getbyte + letters.should == [81, 117, 105, 32, 195] + end + + it "returns nil when invoked at the end of the stream" do + @io.read + @io.getbyte.should == nil + end + + it "raises an IOError on closed stream" do + lambda { IOSpecs.closed_io.getbyte }.should raise_error(IOError) + end +end + +describe "IO#getbyte" do + before :each do + @io = IOSpecs.io_fixture "empty.txt" + end + + after :each do + @io.close if @io + end + + it "returns nil on empty stream" do + @io.getbyte.should == nil + end +end diff --git a/spec/rubyspec/core/io/getc_spec.rb b/spec/rubyspec/core/io/getc_spec.rb new file mode 100644 index 0000000000..dfff1a9583 --- /dev/null +++ b/spec/rubyspec/core/io/getc_spec.rb @@ -0,0 +1,42 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#getc" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "returns the next character from the stream" do + @io.readline.should == "Voici la ligne une.\n" + letters = @io.getc, @io.getc, @io.getc, @io.getc, @io.getc + letters.should == ["Q", "u", "i", " ", "è"] + end + + it "returns nil when invoked at the end of the stream" do + @io.read + @io.getc.should be_nil + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.getc }.should raise_error(IOError) + end +end + +describe "IO#getc" do + before :each do + @io = IOSpecs.io_fixture "empty.txt" + end + + after :each do + @io.close if @io + end + + it "returns nil on empty stream" do + @io.getc.should be_nil + end +end diff --git a/spec/rubyspec/core/io/gets_spec.rb b/spec/rubyspec/core/io/gets_spec.rb new file mode 100644 index 0000000000..d984e795c2 --- /dev/null +++ b/spec/rubyspec/core/io/gets_spec.rb @@ -0,0 +1,313 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/gets_ascii', __FILE__) + +describe "IO#gets" do + it_behaves_like :io_gets_ascii, :gets +end + +describe "IO#gets" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + @count = 0 + end + + after :each do + @io.close if @io + end + + it "assigns the returned line to $_" do + IOSpecs.lines.each do |line| + @io.gets + $_.should == line + end + end + + it "returns nil if called at the end of the stream" do + IOSpecs.lines.length.times { @io.gets } + @io.gets.should == nil + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.gets }.should raise_error(IOError) + end + + describe "with no separator" do + it "returns the next line of string that is separated by $/" do + IOSpecs.lines.each { |line| line.should == @io.gets } + end + + it "returns tainted strings" do + while line = @io.gets + line.tainted?.should == true + end + end + + it "updates lineno with each invocation" do + while @io.gets + @io.lineno.should == @count += 1 + end + end + + it "updates $. with each invocation" do + while @io.gets + $..should == @count += 1 + end + end + end + + describe "with nil separator" do + it "returns the entire contents" do + @io.gets(nil).should == IOSpecs.lines.join("") + end + + it "returns tainted strings" do + while line = @io.gets(nil) + line.tainted?.should == true + end + end + + it "updates lineno with each invocation" do + while @io.gets(nil) + @io.lineno.should == @count += 1 + end + end + + it "updates $. with each invocation" do + while @io.gets(nil) + $..should == @count += 1 + end + end + end + + describe "with an empty String separator" do + # Two successive newlines in the input separate paragraphs. + # When there are more than two successive newlines, only two are kept. + it "returns the next paragraph" do + @io.gets("").should == IOSpecs.lines[0,3].join("") + @io.gets("").should == IOSpecs.lines[4,3].join("") + @io.gets("").should == IOSpecs.lines[7,2].join("") + end + + it "reads until the beginning of the next paragraph" do + # There are three newlines between the first and second paragraph + @io.gets("") + @io.gets.should == IOSpecs.lines[4] + end + + it "returns tainted strings" do + while line = @io.gets("") + line.tainted?.should == true + end + end + + it "updates lineno with each invocation" do + while @io.gets("") + @io.lineno.should == @count += 1 + end + end + + it "updates $. with each invocation" do + while @io.gets("") + $..should == @count += 1 + end + end + end + + describe "with an arbitrary String separator" do + it "reads up to and including the separator" do + @io.gets("la linea").should == "Voici la ligne une.\nQui \303\250 la linea" + end + + it "returns tainted strings" do + while line = @io.gets("la") + line.tainted?.should == true + end + end + + it "updates lineno with each invocation" do + while (@io.gets("la")) + @io.lineno.should == @count += 1 + end + end + + it "updates $. with each invocation" do + while @io.gets("la") + $..should == @count += 1 + end + end + end +end + +describe "IO#gets" do + before :each do + @name = tmp("io_gets") + end + + after :each do + rm_r @name + end + + it "raises an IOError if the stream is opened for append only" do + lambda { File.open(@name, fmode("a:utf-8")) { |f| f.gets } }.should raise_error(IOError) + end + + it "raises an IOError if the stream is opened for writing only" do + lambda { File.open(@name, fmode("w:utf-8")) { |f| f.gets } }.should raise_error(IOError) + end +end + +describe "IO#gets" do + before :each do + @name = tmp("io_gets") + touch(@name) { |f| f.write "one\n\ntwo\n\nthree\nfour\n" } + @io = new_io @name, fmode("r:utf-8") + end + + after :each do + @io.close if @io + rm_r @name + end + + it "calls #to_int to convert a single object argument to an Integer limit" do + obj = mock("io gets limit") + obj.should_receive(:to_int).and_return(6) + + @io.gets(obj).should == "one\n" + end + + it "calls #to_int to convert the second object argument to an Integer limit" do + obj = mock("io gets limit") + obj.should_receive(:to_int).and_return(2) + + @io.gets(nil, obj).should == "on" + end + + it "calls #to_str to convert the first argument to a String when passed a limit" do + obj = mock("io gets separator") + obj.should_receive(:to_str).and_return($/) + + @io.gets(obj, 5).should == "one\n" + end + + it "reads to the default seperator when passed a single argument greater than the number of bytes to the separator" do + @io.gets(6).should == "one\n" + end + + it "reads limit bytes when passed a single argument less than the number of bytes to the default separator" do + @io.gets(3).should == "one" + end + + it "reads limit bytes when passed nil and a limit" do + @io.gets(nil, 6).should == "one\n\nt" + end + + it "reads all bytes when the limit is higher than the available bytes" do + @io.gets(nil, 100).should == "one\n\ntwo\n\nthree\nfour\n" + end + + it "reads until the next paragraph when passed '' and a limit greater than the next paragraph" do + @io.gets("", 6).should == "one\n\n" + end + + it "reads limit bytes when passed '' and a limit less than the next paragraph" do + @io.gets("", 3).should == "one" + end + + it "reads all bytes when pass a separator and reading more than all bytes" do + @io.gets("\t", 100).should == "one\n\ntwo\n\nthree\nfour\n" + end +end + +describe "IO#gets" do + before :each do + @name = tmp("io_gets") + # create data "朝日" + "\xE3\x81" * 100 to avoid utf-8 conflicts + data = "朝日" + ([227,129].pack('C*') * 100).force_encoding('utf-8') + touch(@name) { |f| f.write data } + @io = new_io @name, fmode("r:utf-8") + end + + after :each do + @io.close if @io + rm_r @name + end + + it "reads limit bytes and extra bytes when limit is reached not at character boundary" do + [@io.gets(1), @io.gets(1)].should == ["朝", "日"] + end + + it "read limit bytes and extra bytes with maximum of 16" do + # create str "朝日\xE3" + "\x81\xE3" * 8 to avoid utf-8 conflicts + str = "朝日" + ([227] + [129,227] * 8).pack('C*').force_encoding('utf-8') + @io.gets(7).should == str + end +end + +describe "IO#gets" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::UTF_8 + Encoding.default_internal = nil + + @name = tmp("io_gets") + touch(@name) { |f| f.write "line" } + end + + after :each do + @io.close if @io + rm_r @name + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "uses the default external encoding" do + @io = new_io @name, 'r' + @io.gets.encoding.should == Encoding::UTF_8 + end + + it "uses the IO object's external encoding, when set" do + @io = new_io @name, 'r' + @io.set_encoding Encoding::US_ASCII + @io.gets.encoding.should == Encoding::US_ASCII + end + + it "transcodes into the default internal encoding" do + Encoding.default_internal = Encoding::US_ASCII + @io = new_io @name, 'r' + @io.gets.encoding.should == Encoding::US_ASCII + end + + it "transcodes into the IO object's internal encoding, when set" do + Encoding.default_internal = Encoding::US_ASCII + @io = new_io @name, 'r' + @io.set_encoding Encoding::UTF_8, Encoding::UTF_16 + @io.gets.encoding.should == Encoding::UTF_16 + end + + it "overwrites the default external encoding with the IO object's own external encoding" do + Encoding.default_external = Encoding::ASCII_8BIT + Encoding.default_internal = Encoding::UTF_8 + @io = new_io @name, 'r' + @io.set_encoding Encoding::IBM866 + @io.gets.encoding.should == Encoding::UTF_8 + end + + it "ignores the internal encoding if the default external encoding is ASCII-8BIT" do + Encoding.default_external = Encoding::ASCII_8BIT + Encoding.default_internal = Encoding::UTF_8 + @io = new_io @name, 'r' + @io.gets.encoding.should == Encoding::ASCII_8BIT + end + + it "transcodes to internal encoding if the IO object's external encoding is ASCII-8BIT" do + Encoding.default_external = Encoding::ASCII_8BIT + Encoding.default_internal = Encoding::UTF_8 + @io = new_io @name, 'r' + @io.set_encoding Encoding::ASCII_8BIT, Encoding::UTF_8 + @io.gets.encoding.should == Encoding::UTF_8 + end +end diff --git a/spec/rubyspec/core/io/initialize_spec.rb b/spec/rubyspec/core/io/initialize_spec.rb new file mode 100644 index 0000000000..4731257625 --- /dev/null +++ b/spec/rubyspec/core/io/initialize_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#initialize" do + before :each do + @name = tmp("io_initialize.txt") + @io = new_io @name + @fd = @io.fileno + end + + after :each do + @io.close if @io + rm_r @name + end + + it "reassociates the IO instance with the new descriptor when passed a Fixnum" do + fd = new_fd @name, "r:utf-8" + @io.send :initialize, fd, 'r' + @io.fileno.should == fd + # initialize has closed the old descriptor + lambda { IO.for_fd(@fd).close }.should raise_error(Errno::EBADF) + end + + it "calls #to_int to coerce the object passed as an fd" do + obj = mock('fileno') + fd = new_fd @name, "r:utf-8" + obj.should_receive(:to_int).and_return(fd) + @io.send :initialize, obj, 'r' + @io.fileno.should == fd + # initialize has closed the old descriptor + lambda { IO.for_fd(@fd).close }.should raise_error(Errno::EBADF) + end + + it "raises a TypeError when passed an IO" do + lambda { @io.send :initialize, STDOUT, 'w' }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + lambda { @io.send :initialize, nil, 'w' }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { @io.send :initialize, "4", 'w' }.should raise_error(TypeError) + end + + it "raises IOError on closed stream" do + lambda { @io.send :initialize, IOSpecs.closed_io.fileno }.should raise_error(IOError) + end + + it "raises an Errno::EBADF when given an invalid file descriptor" do + lambda { @io.send :initialize, -1, 'w' }.should raise_error(Errno::EBADF) + end +end diff --git a/spec/rubyspec/core/io/inspect_spec.rb b/spec/rubyspec/core/io/inspect_spec.rb new file mode 100644 index 0000000000..1c6bf99b44 --- /dev/null +++ b/spec/rubyspec/core/io/inspect_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO#inspect" do + after :each do + @r.close if @r && !@r.closed? + @w.close if @w && !@w.closed? + end + + it "contains the file descriptor number" do + @r, @w = IO.pipe + @r.inspect.should include("fd #{@r.fileno}") + end + + it "contains \"(closed)\" if the stream is closed" do + @r, @w = IO.pipe + @r.close + @r.inspect.should include("(closed)") + end + + it "reports IO as its Method object's owner" do + IO.instance_method(:inspect).owner.should == IO + end +end diff --git a/spec/rubyspec/core/io/internal_encoding_spec.rb b/spec/rubyspec/core/io/internal_encoding_spec.rb new file mode 100644 index 0000000000..7578559838 --- /dev/null +++ b/spec/rubyspec/core/io/internal_encoding_spec.rb @@ -0,0 +1,140 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe :io_internal_encoding, shared: true do + describe "when Encoding.default_internal is not set" do + before :each do + Encoding.default_internal = nil + end + + it "returns nil if the internal encoding is not set" do + @io = new_io @name, @object + @io.internal_encoding.should be_nil + end + + it "returns nil if Encoding.default_internal is changed after the instance is created" do + @io = new_io @name, @object + Encoding.default_internal = Encoding::IBM437 + @io.internal_encoding.should be_nil + end + + it "returns the value set when the instance was created" do + @io = new_io @name, "#{@object}:utf-8:euc-jp" + Encoding.default_internal = Encoding::IBM437 + @io.internal_encoding.should equal(Encoding::EUC_JP) + end + + it "returns the value set by #set_encoding" do + @io = new_io @name, @object + @io.set_encoding(Encoding::US_ASCII, Encoding::IBM437) + @io.internal_encoding.should equal(Encoding::IBM437) + end + end + + describe "when Encoding.default_internal == Encoding.default_external" do + before :each do + Encoding.default_external = Encoding::IBM866 + Encoding.default_internal = Encoding::IBM866 + end + + it "returns nil" do + @io = new_io @name, @object + @io.internal_encoding.should be_nil + end + + it "returns nil regardless of Encoding.default_internal changes" do + @io = new_io @name, @object + Encoding.default_internal = Encoding::IBM437 + @io.internal_encoding.should be_nil + end + end + + describe "when Encoding.default_internal != Encoding.default_external" do + before :each do + Encoding.default_external = Encoding::IBM437 + Encoding.default_internal = Encoding::IBM866 + end + + it "returns the value of Encoding.default_internal when the instance was created if the internal encoding is not set" do + @io = new_io @name, @object + @io.internal_encoding.should equal(Encoding::IBM866) + end + + it "does not change when Encoding.default_internal is changed" do + @io = new_io @name, @object + Encoding.default_internal = Encoding::IBM437 + @io.internal_encoding.should equal(Encoding::IBM866) + end + + it "returns the internal encoding set when the instance was created" do + @io = new_io @name, "#{@object}:utf-8:euc-jp" + @io.internal_encoding.should equal(Encoding::EUC_JP) + end + + it "does not change when set and Encoding.default_internal is changed" do + @io = new_io @name, "#{@object}:utf-8:euc-jp" + Encoding.default_internal = Encoding::IBM437 + @io.internal_encoding.should equal(Encoding::EUC_JP) + end + + it "returns the value set by #set_encoding" do + @io = new_io @name, @object + @io.set_encoding(Encoding::US_ASCII, Encoding::IBM437) + @io.internal_encoding.should equal(Encoding::IBM437) + end + + it "returns nil when Encoding.default_external is ASCII-8BIT and the internal encoding is not set" do + Encoding.default_external = Encoding::ASCII_8BIT + @io = new_io @name, @object + @io.internal_encoding.should be_nil + end + + it "returns nil when the external encoding is ASCII-8BIT and the internal encoding is not set" do + @io = new_io @name, "#{@object}:ascii-8bit" + @io.internal_encoding.should be_nil + end + end + end + + describe "IO#internal_encoding" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + @name = tmp("io_internal_encoding") + touch(@name) + end + + after :each do + @io.close if @io + rm_r @name + + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + describe "with 'r' mode" do + it_behaves_like :io_internal_encoding, nil, "r" + end + + describe "with 'r+' mode" do + it_behaves_like :io_internal_encoding, nil, "r+" + end + + describe "with 'w' mode" do + it_behaves_like :io_internal_encoding, nil, "w" + end + + describe "with 'w+' mode" do + it_behaves_like :io_internal_encoding, nil, "w+" + end + + describe "with 'a' mode" do + it_behaves_like :io_internal_encoding, nil, "a" + end + + describe "with 'a+' mode" do + it_behaves_like :io_internal_encoding, nil, "a+" + end + end +end diff --git a/spec/rubyspec/core/io/io_spec.rb b/spec/rubyspec/core/io/io_spec.rb new file mode 100644 index 0000000000..e196628ef0 --- /dev/null +++ b/spec/rubyspec/core/io/io_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO" do + it "includes File::Constants" do + IO.include?(File::Constants).should == true + end + + it "includes Enumerable" do + IO.include?(Enumerable).should == true + end +end diff --git a/spec/rubyspec/core/io/ioctl_spec.rb b/spec/rubyspec/core/io/ioctl_spec.rb new file mode 100644 index 0000000000..3b2d271c0c --- /dev/null +++ b/spec/rubyspec/core/io/ioctl_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#ioctl" do + platform_is_not :windows do + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.ioctl(5, 5) }.should raise_error(IOError) + end + end + + platform_is :linux do + platform_is "86" do # x86 / x86_64 + it "resizes an empty String to match the output size" do + File.open(__FILE__, 'r') do |f| + buffer = '' + # FIONREAD in /usr/include/asm-generic/ioctls.h + f.ioctl 0x541B, buffer + buffer.unpack('I').first.should be_kind_of(Integer) + end + end + end + + it "raises an Errno error when ioctl fails" do + File.open(__FILE__, 'r') do |f| + lambda { + # TIOCGWINSZ in /usr/include/asm-generic/ioctls.h + f.ioctl 0x5413, nil + }.should raise_error(Errno::ENOTTY) + end + end + end +end diff --git a/spec/rubyspec/core/io/isatty_spec.rb b/spec/rubyspec/core/io/isatty_spec.rb new file mode 100644 index 0000000000..87172dddd7 --- /dev/null +++ b/spec/rubyspec/core/io/isatty_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/tty', __FILE__) + +describe "IO#isatty" do + it_behaves_like :io_tty, :isatty +end diff --git a/spec/rubyspec/core/io/lineno_spec.rb b/spec/rubyspec/core/io/lineno_spec.rb new file mode 100644 index 0000000000..4fddbf135c --- /dev/null +++ b/spec/rubyspec/core/io/lineno_spec.rb @@ -0,0 +1,95 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#lineno" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "raises an IOError on a closed stream" do + lambda { IOSpecs.closed_io.lineno }.should raise_error(IOError) + end + + it "returns the current line number" do + @io.lineno.should == 0 + + count = 0 + while @io.gets + @io.lineno.should == count += 1 + end + + @io.rewind + @io.lineno.should == 0 + end +end + +describe "IO#lineno=" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "raises an IOError on a closed stream" do + lambda { IOSpecs.closed_io.lineno = 5 }.should raise_error(IOError) + end + + it "calls #to_int on a non-numeric argument" do + obj = mock('123') + obj.should_receive(:to_int).and_return(123) + + @io.lineno = obj + @io.lineno.should == 123 + end + + it "truncates a Float argument" do + @io.lineno = 1.5 + @io.lineno.should == 1 + + @io.lineno = 92233.72036854775808 + @io.lineno.should == 92233 + end + + it "raises TypeError on nil argument" do + lambda { @io.lineno = nil }.should raise_error(TypeError) + end + + it "sets the current line number to the given value" do + @io.lineno = count = 500 + + while @io.gets + @io.lineno.should == count += 1 + end + + @io.rewind + @io.lineno.should == 0 + end + + it "does not change $." do + original_line = $. + numbers = [-2**30, -2**16, -2**8, -100, -10, -1, 0, 1, 10, 2**8, 2**16, 2**30] + numbers.each do |num| + @io.lineno = num + @io.lineno.should == num + $..should == original_line + end + end + + it "does not change $. until next read" do + $. = 0 + $..should == 0 + + @io.lineno = count = 500 + $..should == 0 + + while @io.gets + $..should == count += 1 + end + end +end diff --git a/spec/rubyspec/core/io/lines_spec.rb b/spec/rubyspec/core/io/lines_spec.rb new file mode 100644 index 0000000000..90ddc4e4cf --- /dev/null +++ b/spec/rubyspec/core/io/lines_spec.rb @@ -0,0 +1,42 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#lines" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "returns an Enumerator" do + @io.lines.should be_an_instance_of(Enumerator) + end + + describe "when no block is given" do + it "returns an Enumerator" do + @io.lines.should be_an_instance_of(Enumerator) + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @io.lines.size.should == nil + end + end + end + end + + it "returns a line when accessed" do + enum = @io.lines + enum.first.should == IOSpecs.lines[0] + end + + it "yields each line to the passed block" do + ScratchPad.record [] + @io.lines { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines + end +end diff --git a/spec/rubyspec/core/io/new_spec.rb b/spec/rubyspec/core/io/new_spec.rb new file mode 100644 index 0000000000..ce922a4856 --- /dev/null +++ b/spec/rubyspec/core/io/new_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "IO.new" do + it_behaves_like :io_new, :new +end + +describe "IO.new" do + it_behaves_like :io_new_errors, :new +end diff --git a/spec/rubyspec/core/io/open_spec.rb b/spec/rubyspec/core/io/open_spec.rb new file mode 100644 index 0000000000..f87ee6c7c2 --- /dev/null +++ b/spec/rubyspec/core/io/open_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "IO.open" do + it_behaves_like :io_new, :open +end + +describe "IO.open" do + it_behaves_like :io_new_errors, :open +end + +# These specs use a special mock helper to avoid mock +# methods from preventing IO#close from running and +# which would prevent the file referenced by @fd from +# being deleted on Windows. + +describe "IO.open" do + before :each do + @name = tmp("io_open.txt") + @fd = new_fd @name + ScratchPad.clear + end + + after :each do + rm_r @name + end + + it "calls #close after yielding to the block" do + IO.open(@fd, "w") do |io| + IOSpecs.io_mock(io, :close) do + super() + ScratchPad.record :called + end + io.closed?.should be_false + end + ScratchPad.recorded.should == :called + end + + it "propagates an exception raised by #close that is not a StandardError" do + lambda do + IO.open(@fd, "w") do |io| + IOSpecs.io_mock(io, :close) do + super() + ScratchPad.record :called + raise Exception + end + end + end.should raise_error(Exception) + ScratchPad.recorded.should == :called + end + + it "propagates an exception raised by #close that is a StandardError" do + lambda do + IO.open(@fd, "w") do |io| + IOSpecs.io_mock(io, :close) do + super() + ScratchPad.record :called + raise StandardError + end + end + end.should raise_error(StandardError) + ScratchPad.recorded.should == :called + end + + it "does not propagate a IOError with 'closed stream' message raised by #close" do + IO.open(@fd, "w") do |io| + IOSpecs.io_mock(io, :close) do + super() + ScratchPad.record :called + raise IOError, 'closed stream' + end + end + ScratchPad.recorded.should == :called + end + + it "does not set last error when a IOError with 'closed stream' raised by #close" do + IO.open(@fd, "w") do |io| + IOSpecs.io_mock(io, :close) do + super() + raise IOError, 'closed stream' + end + end + $!.should == nil + end +end diff --git a/spec/rubyspec/core/io/output_spec.rb b/spec/rubyspec/core/io/output_spec.rb new file mode 100644 index 0000000000..ac3e781cee --- /dev/null +++ b/spec/rubyspec/core/io/output_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#<<" do + it "writes an object to the IO stream" do + lambda { + $stderr << "Oh noes, an error!" + }.should output_to_fd("Oh noes, an error!", $stderr) + end + + it "calls #to_s on the object to print it" do + lambda { + $stderr << 1337 + }.should output_to_fd("1337", $stderr) + end + + it "raises an error if the stream is closed" do + io = IOSpecs.closed_io + lambda { io << "test" }.should raise_error(IOError) + end + + it "returns self" do + lambda { + ($stderr << "to_stderr").should == $stderr + }.should output(nil, "to_stderr") + end +end diff --git a/spec/rubyspec/core/io/pid_spec.rb b/spec/rubyspec/core/io/pid_spec.rb new file mode 100644 index 0000000000..a4f6bbfcf8 --- /dev/null +++ b/spec/rubyspec/core/io/pid_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "IO#pid" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "returns nil for IO not associated with a process" do + @io.pid.should == nil + end +end + +describe "IO#pid" do + before :each do + @io = IO.popen ruby_cmd('STDIN.read'), "r+" + end + + after :each do + @io.close if @io && !@io.closed? + end + + it "returns the ID of a process associated with stream" do + @io.pid.should_not be_nil + end + + it "raises an IOError on closed stream" do + @io.close + lambda { @io.pid }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/pipe_spec.rb b/spec/rubyspec/core/io/pipe_spec.rb new file mode 100644 index 0000000000..b4984fdeb7 --- /dev/null +++ b/spec/rubyspec/core/io/pipe_spec.rb @@ -0,0 +1,214 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO.pipe" do + after :each do + @r.close if @r && !@r.closed? + @w.close if @w && !@w.closed? + end + + it "creates a two-ended pipe" do + @r, @w = IO.pipe + @w.puts "test_create_pipe\\n" + @w.close + @r.read(16).should == "test_create_pipe" + end + + it "returns two IO objects" do + @r, @w = IO.pipe + @r.should be_kind_of(IO) + @w.should be_kind_of(IO) + end + + it "returns instances of a subclass when called on a subclass" do + @r, @w = IOSpecs::SubIO.pipe + @r.should be_an_instance_of(IOSpecs::SubIO) + @w.should be_an_instance_of(IOSpecs::SubIO) + end +end + +describe "IO.pipe" do + describe "passed a block" do + it "yields two IO objects" do + IO.pipe do |r, w| + r.should be_kind_of(IO) + w.should be_kind_of(IO) + end + end + + it "returns the result of the block" do + IO.pipe { |r, w| :result }.should == :result + end + + it "closes both IO objects" do + r, w = IO.pipe do |_r, _w| + [_r, _w] + end + r.closed?.should == true + w.closed?.should == true + end + + it "closes both IO objects when the block raises" do + r = w = nil + lambda do + IO.pipe do |_r, _w| + r = _r + w = _w + raise RuntimeError + end + end.should raise_error(RuntimeError) + r.closed?.should == true + w.closed?.should == true + end + + it "allows IO objects to be closed within the block" do + r, w = IO.pipe do |_r, _w| + _r.close + _w.close + [_r, _w] + end + r.closed?.should == true + w.closed?.should == true + end + end +end + +describe "IO.pipe" do + before :each do + @default_external = Encoding.default_external + @default_internal = Encoding.default_internal + end + + after :each do + Encoding.default_external = @default_external + Encoding.default_internal = @default_internal + end + + it "sets the external encoding of the read end to the default when passed no arguments" do + Encoding.default_external = Encoding::ISO_8859_1 + + IO.pipe do |r, w| + r.external_encoding.should == Encoding::ISO_8859_1 + r.internal_encoding.should be_nil + end + end + + it "sets the internal encoding of the read end to the default when passed no arguments" do + Encoding.default_external = Encoding::ISO_8859_1 + Encoding.default_internal = Encoding::UTF_8 + + IO.pipe do |r, w| + r.external_encoding.should == Encoding::ISO_8859_1 + r.internal_encoding.should == Encoding::UTF_8 + end + end + + it "sets the internal encoding to nil if the same as the external" do + Encoding.default_external = Encoding::UTF_8 + Encoding.default_internal = Encoding::UTF_8 + + IO.pipe do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should be_nil + end + end + + it "sets the external encoding of the read end when passed an Encoding argument" do + IO.pipe(Encoding::UTF_8) do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should be_nil + end + end + + it "sets the external and internal encodings of the read end when passed two Encoding arguments" do + IO.pipe(Encoding::UTF_8, Encoding::UTF_16BE) do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::UTF_16BE + end + end + + it "sets the external encoding of the read end when passed the name of an Encoding" do + IO.pipe("UTF-8") do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should be_nil + end + end + + it "accepts 'bom|' prefix for external encoding" do + IO.pipe("BOM|UTF-8") do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should be_nil + end + end + + it "sets the external and internal encodings specified as a String and separated with a colon" do + IO.pipe("UTF-8:ISO-8859-1") do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::ISO_8859_1 + end + end + + it "accepts 'bom|' prefix for external encoding when specifying 'external:internal'" do + IO.pipe("BOM|UTF-8:ISO-8859-1") do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::ISO_8859_1 + end + end + + it "sets the external and internal encoding when passed two String arguments" do + IO.pipe("UTF-8", "UTF-16BE") do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::UTF_16BE + end + end + + it "accepts an options Hash with one String encoding argument" do + IO.pipe("BOM|UTF-8:ISO-8859-1", invalid: :replace) do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::ISO_8859_1 + end + end + + it "accepts an options Hash with two String encoding arguments" do + IO.pipe("UTF-8", "ISO-8859-1", invalid: :replace) do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::ISO_8859_1 + end + end + + it "calls #to_hash to convert an options argument" do + options = mock("io pipe encoding options") + options.should_receive(:to_hash).and_return({ invalid: :replace }) + IO.pipe("UTF-8", "ISO-8859-1", options) { |r, w| } + end + + it "calls #to_str to convert the first argument to a String" do + obj = mock("io_pipe_encoding") + obj.should_receive(:to_str).and_return("UTF-8:UTF-16BE") + IO.pipe(obj) do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::UTF_16BE + end + end + + it "calls #to_str to convert the second argument to a String" do + obj = mock("io_pipe_encoding") + obj.should_receive(:to_str).at_least(1).times.and_return("UTF-16BE") + IO.pipe(Encoding::UTF_8, obj) do |r, w| + r.external_encoding.should == Encoding::UTF_8 + r.internal_encoding.should == Encoding::UTF_16BE + end + end + + it "sets no external encoding for the write end" do + IO.pipe(Encoding::UTF_8) do |r, w| + w.external_encoding.should be_nil + end + end + + it "sets no internal encoding for the write end" do + IO.pipe(Encoding::UTF_8) do |r, w| + w.external_encoding.should be_nil + end + end +end diff --git a/spec/rubyspec/core/io/popen_spec.rb b/spec/rubyspec/core/io/popen_spec.rb new file mode 100644 index 0000000000..0619136320 --- /dev/null +++ b/spec/rubyspec/core/io/popen_spec.rb @@ -0,0 +1,286 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_exe = RUBY_EXE.split + +describe "IO.popen" do + before :each do + @io = nil + end + + after :each do + @io.close if @io + end + + it "returns an open IO" do + @io = IO.popen(ruby_cmd('exit'), "r") + @io.closed?.should be_false + end + + it "reads a read-only pipe" do + @io = IO.popen(ruby_cmd('puts "foo"'), "r") + @io.read.should == "foo\n" + end + + it "raises IOError when writing a read-only pipe" do + @io = IO.popen(ruby_cmd('puts "foo"'), "r") + lambda { @io.write('foo') }.should raise_error(IOError) + end +end + +describe "IO.popen" do + before :each do + @fname = tmp("IO_popen_spec") + @io = nil + end + + after :each do + @io.close if @io and !@io.closed? + rm_r @fname + end + + it "sees an infinitely looping subprocess exit when read pipe is closed" do + io = IO.popen ruby_cmd('r = loop{puts "y"; 0} rescue 1; exit r'), 'r' + io.close + + $?.exitstatus.should_not == 0 + end + + it "writes to a write-only pipe" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)', args: "> #{@fname}"), "w") + @io.write("bar") + @io.close + + File.read(@fname).should == "bar" + end + + it "raises IOError when reading a write-only pipe" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)'), "w") + lambda { @io.read }.should raise_error(IOError) + end + + it "reads and writes a read/write pipe" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)'), "r+") + @io.write("bar") + @io.read(3).should == "bar" + end + + it "waits for the child to finish" do + @io = IO.popen(ruby_cmd('IO.copy_stream(STDIN,STDOUT)', args: "> #{@fname}"), "w") + @io.write("bar") + @io.close + + $?.exitstatus.should == 0 + + File.read(@fname).should == "bar" + end + + it "does not throw an exception if child exited and has been waited for" do + @io = IO.popen(ruby_cmd('sleep')) + Process.kill "KILL", @io.pid + @io.close + platform_is_not :windows do + $?.signaled?.should == true + end + platform_is :windows do + $?.exited?.should == true + end + end + + it "returns an instance of a subclass when called on a subclass" do + @io = IOSpecs::SubIO.popen(ruby_cmd('exit'), "r") + @io.should be_an_instance_of(IOSpecs::SubIO) + end + + it "coerces mode argument with #to_str" do + mode = mock("mode") + mode.should_receive(:to_str).and_return("r") + @io = IO.popen(ruby_cmd('exit 0'), mode) + end +end + +describe "IO.popen" do + before :each do + @io = nil + end + + after :each do + @io.close if @io + end + + describe "with a block" do + it "yields an open IO to the block" do + IO.popen(ruby_cmd('exit'), "r") do |io| + io.closed?.should be_false + end + end + + it "yields an instance of a subclass when called on a subclass" do + IOSpecs::SubIO.popen(ruby_cmd('exit'), "r") do |io| + io.should be_an_instance_of(IOSpecs::SubIO) + end + end + + it "closes the IO after yielding" do + io = IO.popen(ruby_cmd('exit'), "r") { |_io| _io } + io.closed?.should be_true + end + + it "allows the IO to be closed inside the block" do + io = IO.popen(ruby_cmd('exit'), 'r') { |_io| _io.close; _io } + io.closed?.should be_true + end + + it "returns the value of the block" do + IO.popen(ruby_cmd('exit'), "r") { :hello }.should == :hello + end + end + + with_feature :fork do + it "starts returns a forked process if the command is -" do + io = IO.popen("-") + + if io # parent + begin + io.gets.should == "hello from child\n" + ensure + io.close + end + else # child + puts "hello from child" + exit! + end + end + end + + with_feature :encoding do + it "has the given external encoding" do + @io = IO.popen(ruby_cmd('exit'), external_encoding: Encoding::EUC_JP) + @io.external_encoding.should == Encoding::EUC_JP + end + + it "has the given internal encoding" do + @io = IO.popen(ruby_cmd('exit'), internal_encoding: Encoding::EUC_JP) + @io.internal_encoding.should == Encoding::EUC_JP + end + + it "sets the internal encoding to nil if it's the same as the external encoding" do + @io = IO.popen(ruby_cmd('exit'), external_encoding: Encoding::EUC_JP, + internal_encoding: Encoding::EUC_JP) + @io.internal_encoding.should be_nil + end + end + + context "with a leading ENV Hash" do + it "accepts a single String command" do + IO.popen({"FOO" => "bar"}, ruby_cmd('puts ENV["FOO"]')) do |io| + io.read.should == "bar\n" + end + end + + it "accepts a single String command, and an IO mode" do + IO.popen({"FOO" => "bar"}, ruby_cmd('puts ENV["FOO"]'), "r") do |io| + io.read.should == "bar\n" + end + end + + it "accepts a single String command with a trailing Hash of Process.exec options" do + IO.popen({"FOO" => "bar"}, ruby_cmd('STDERR.puts ENV["FOO"]'), + err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts a single String command with a trailing Hash of Process.exec options, and an IO mode" do + IO.popen({"FOO" => "bar"}, ruby_cmd('STDERR.puts ENV["FOO"]'), "r", + err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array of command and arguments" do + exe, *args = *ruby_exe + IO.popen({"FOO" => "bar"}, [[exe, "specfu"], *args, "-e", "puts ENV['FOO']"]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array of command and arguments, and an IO mode" do + exe, *args = *ruby_exe + IO.popen({"FOO" => "bar"}, [[exe, "specfu"], *args, "-e", "puts ENV['FOO']"], "r") do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array command with a separate trailing Hash of Process.exec options" do + IO.popen({"FOO" => "bar"}, [*ruby_exe, "-e", "STDERR.puts ENV['FOO']"], + err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts an Array command with a separate trailing Hash of Process.exec options, and an IO mode" do + IO.popen({"FOO" => "bar"}, [*ruby_exe, "-e", "STDERR.puts ENV['FOO']"], + "r", err: [:child, :out]) do |io| + io.read.should == "bar\n" + end + end + end + + context "with a leading Array argument" do + it "uses the Array as command plus args for the child process" do + IO.popen([*ruby_exe, "-e", "puts 'hello'"]) do |io| + io.read.should == "hello\n" + end + end + + it "accepts a leading ENV Hash" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "puts ENV['FOO']"]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts a trailing Hash of Process.exec options" do + IO.popen([*ruby_exe, "does_not_exist", {err: [:child, :out]}]) do |io| + io.read.should =~ /LoadError/ + end + end + + it "accepts an IO mode argument following the Array" do + IO.popen([*ruby_exe, "does_not_exist", {err: [:child, :out]}], "r") do |io| + io.read.should =~ /LoadError/ + end + end + + it "accepts [env, command, arg1, arg2, ..., exec options]" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]", + err: [:child, :out]]) do |io| + io.read.should == "bar\n" + end + end + + it "accepts '[env, command, arg1, arg2, ..., exec options], mode'" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]", + err: [:child, :out]], "r") do |io| + io.read.should == "bar\n" + end + end + + it "accepts '[env, command, arg1, arg2, ..., exec options], mode, IO options'" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]", + err: [:child, :out]], "r", + internal_encoding: Encoding::EUC_JP) do |io| + io.read.should == "bar\n" + io.internal_encoding.should == Encoding::EUC_JP + end + end + + it "accepts '[env, command, arg1, arg2, ...], mode, IO + exec options'" do + IO.popen([{"FOO" => "bar"}, *ruby_exe, "-e", "STDERR.puts ENV[:FOO.to_s]"], "r", + err: [:child, :out], internal_encoding: Encoding::EUC_JP) do |io| + io.read.should == "bar\n" + io.internal_encoding.should == Encoding::EUC_JP + end + end + end +end diff --git a/spec/rubyspec/core/io/pos_spec.rb b/spec/rubyspec/core/io/pos_spec.rb new file mode 100644 index 0000000000..300925a284 --- /dev/null +++ b/spec/rubyspec/core/io/pos_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "IO#pos" do + it_behaves_like :io_pos, :pos +end + +describe "IO#pos=" do + it_behaves_like :io_set_pos, :pos= +end + diff --git a/spec/rubyspec/core/io/print_spec.rb b/spec/rubyspec/core/io/print_spec.rb new file mode 100644 index 0000000000..e852757385 --- /dev/null +++ b/spec/rubyspec/core/io/print_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe IO, "#print" do + before :each do + @old_separator = $\ + $\ = '->' + @name = tmp("io_print") + end + + after :each do + $\ = @old_separator + rm_r @name + end + + it "writes $_.to_s followed by $\\ (if any) to the stream if no arguments given" do + o = mock('o') + o.should_receive(:to_s).and_return("mockmockmock") + $_ = o + + touch(@name) { |f| f.print } + IO.read(@name).should == "mockmockmock#{$\}" + + # Set $_ to something known + string = File.open(__FILE__) {|f| f.gets } + + touch(@name) { |f| f.print } + IO.read(@name).should == "#{string}#{$\}" + end + + it "calls obj.to_s and not obj.to_str then writes the record separator" do + o = mock('o') + o.should_not_receive(:to_str) + o.should_receive(:to_s).and_return("hello") + + touch(@name) { |f| f.print(o) } + + IO.read(@name).should == "hello#{$\}" + end + + it "writes each obj.to_s to the stream and appends $\\ (if any) given multiple objects" do + o, o2 = Object.new, Object.new + def o.to_s(); 'o'; end + def o2.to_s(); 'o2'; end + + touch(@name) { |f| f.print(o, o2) } + IO.read(@name).should == "#{o.to_s}#{o2.to_s}#{$\}" + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.print("stuff") }.should raise_error(IOError) + end +end + diff --git a/spec/rubyspec/core/io/printf_spec.rb b/spec/rubyspec/core/io/printf_spec.rb new file mode 100644 index 0000000000..34be4c8e79 --- /dev/null +++ b/spec/rubyspec/core/io/printf_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#printf" do + before :each do + @name = tmp("io_printf.txt") + @io = new_io @name + @io.sync = true + end + + after :each do + @io.close if @io + rm_r @name + end + + it "calls #to_str to convert the format object to a String" do + obj = mock("printf format") + obj.should_receive(:to_str).and_return("%s") + + @io.printf obj, "printf" + File.read(@name).should == "printf" + end + + it "writes the #sprintf formatted string" do + @io.printf "%d %s", 5, "cookies" + File.read(@name).should == "5 cookies" + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.printf("stuff") }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/putc_spec.rb b/spec/rubyspec/core/io/putc_spec.rb new file mode 100644 index 0000000000..9803d0c1a1 --- /dev/null +++ b/spec/rubyspec/core/io/putc_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/io/putc', __FILE__) + +describe "IO#putc" do + before :each do + @name = tmp("io_putc.txt") + @io_object = @io = new_io(@name) + end + + it_behaves_like :io_putc, :putc +end diff --git a/spec/rubyspec/core/io/puts_spec.rb b/spec/rubyspec/core/io/puts_spec.rb new file mode 100644 index 0000000000..8f2f4f2c81 --- /dev/null +++ b/spec/rubyspec/core/io/puts_spec.rb @@ -0,0 +1,144 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#puts" do + before :each do + @before_separator = $/ + @name = tmp("io_puts.txt") + @io = new_io @name + ScratchPad.record "" + def @io.write(str) + ScratchPad << str + end + end + + after :each do + ScratchPad.clear + @io.close if @io + rm_r @name + $/ = @before_separator + end + + it "writes just a newline when given no args" do + @io.puts.should == nil + ScratchPad.recorded.should == "\n" + end + + it "writes just a newline when given just a newline" do + lambda { $stdout.puts "\n" }.should output_to_fd("\n", STDOUT) + end + + it "writes empty string with a newline when given nil as an arg" do + @io.puts(nil).should == nil + ScratchPad.recorded.should == "\n" + end + + it "writes empty string with a newline when when given nil as multiple args" do + @io.puts(nil, nil).should == nil + ScratchPad.recorded.should == "\n\n" + end + + it "calls :to_ary before writing non-string objects, regardless of it being implemented in the receiver" do + object = mock('hola') + object.should_receive(:method_missing).with(:to_ary) + object.should_receive(:to_s).and_return("#") + + @io.should_receive(:write).with("#") + @io.should_receive(:write).with("\n") + @io.puts(object).should == nil + end + + it "calls :to_ary before writing non-string objects" do + object = mock('hola') + object.should_receive(:to_ary).and_return(["hola"]) + + @io.should_receive(:write).with("hola") + @io.should_receive(:write).with("\n") + @io.puts(object).should == nil + end + + it "calls :to_s before writing non-string objects that don't respond to :to_ary" do + object = mock('hola') + object.should_receive(:to_s).and_return("hola") + + @io.puts(object).should == nil + ScratchPad.recorded.should == "hola\n" + end + + it "returns general object info if :to_s does not return a string" do + object = mock('hola') + object.should_receive(:to_s).and_return(false) + + @io.should_receive(:write).with(object.inspect.split(" ")[0] + ">") + @io.should_receive(:write).with("\n") + @io.puts(object).should == nil + end + + it "writes each arg if given several" do + @io.puts(1, "two", 3).should == nil + ScratchPad.recorded.should == "1\ntwo\n3\n" + end + + it "flattens a nested array before writing it" do + @io.puts([1, 2, [3]]).should == nil + ScratchPad.recorded.should == "1\n2\n3\n" + end + + it "writes nothing for an empty array" do + x = [] + @io.should_not_receive(:write) + @io.puts(x).should == nil + end + + it "writes [...] for a recursive array arg" do + x = [] + x << 2 << x + @io.puts(x).should == nil + ScratchPad.recorded.should == "2\n[...]\n" + end + + it "writes a newline after objects that do not end in newlines" do + @io.puts(5).should == nil + ScratchPad.recorded.should == "5\n" + end + + it "does not write a newline after objects that end in newlines" do + @io.puts("5\n").should == nil + ScratchPad.recorded.should == "5\n" + end + + it "ignores the $/ separator global" do + $/ = ":" + @io.puts(5).should == nil + ScratchPad.recorded.should == "5\n" + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.puts("stuff") }.should raise_error(IOError) + end + + with_feature :encoding do + it "writes crlf when IO is opened with newline: :crlf" do + File.open(@name, 'wt', newline: :crlf) do |file| + file.puts + end + File.binread(@name).should == "\r\n" + end + + it "writes cr when IO is opened with newline: :cr" do + File.open(@name, 'wt', newline: :cr) do |file| + file.puts + end + File.binread(@name).should == "\r" + end + + platform_is_not :windows do # https://bugs.ruby-lang.org/issues/12436 + it "writes lf when IO is opened with newline: :lf" do + File.open(@name, 'wt', newline: :lf) do |file| + file.puts + end + File.binread(@name).should == "\n" + end + end + end +end diff --git a/spec/rubyspec/core/io/read_nonblock_spec.rb b/spec/rubyspec/core/io/read_nonblock_spec.rb new file mode 100644 index 0000000000..2b314987bc --- /dev/null +++ b/spec/rubyspec/core/io/read_nonblock_spec.rb @@ -0,0 +1,85 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#read_nonblock" do + before :each do + @read, @write = IO.pipe + end + + after :each do + @read.close if @read && !@read.closed? + @write.close if @write && !@write.closed? + end + + it "raises an exception extending IO::WaitReadable when there is no data" do + lambda { @read.read_nonblock(5) }.should raise_error(IO::WaitReadable) { |e| + platform_is_not :windows do + e.should be_kind_of(Errno::EAGAIN) + end + platform_is :windows do + e.should be_kind_of(Errno::EWOULDBLOCK) + end + } + end + + ruby_version_is "2.3" do + context "when exception option is set to false" do + context "when there is no data" do + it "returns :wait_readable" do + @read.read_nonblock(5, exception: false).should == :wait_readable + end + end + + context "when the end is reached" do + it "returns nil" do + @write << "hello" + @write.close + + @read.read_nonblock(5) + + @read.read_nonblock(5, exception: false).should be_nil + end + end + end + end + + it "returns at most the number of bytes requested" do + @write << "hello" + @read.read_nonblock(4).should == "hell" + end + + it "returns less data if that is all that is available" do + @write << "hello" + @read.read_nonblock(10).should == "hello" + end + + it "allows for reading 0 bytes before any write" do + @read.read_nonblock(0).should == "" + end + + it "allows for reading 0 bytes after a write" do + @write.write "1" + @read.read_nonblock(0).should == "" + @read.read_nonblock(1).should == "1" + end + + it "reads into the passed buffer" do + buffer = "" + @write.write("1") + @read.read_nonblock(1, buffer) + buffer.should == "1" + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.read_nonblock(5) }.should raise_error(IOError) + end + + it "raises EOFError when the end is reached" do + @write << "hello" + @write.close + + @read.read_nonblock(5) + + lambda { @read.read_nonblock(5) }.should raise_error(EOFError) + end +end diff --git a/spec/rubyspec/core/io/read_spec.rb b/spec/rubyspec/core/io/read_spec.rb new file mode 100644 index 0000000000..223e3cde06 --- /dev/null +++ b/spec/rubyspec/core/io/read_spec.rb @@ -0,0 +1,616 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO.read" do + before :each do + @fname = tmp("io_read.txt") + @contents = "1234567890" + touch(@fname) { |f| f.write @contents } + end + + after :each do + rm_r @fname + end + + it "reads the contents of a file" do + IO.read(@fname).should == @contents + end + + it "calls #to_path on non-String arguments" do + p = mock('path') + p.should_receive(:to_path).and_return(@fname) + IO.read(p) + end + + it "accepts an empty options Hash" do + IO.read(@fname, {}).should == @contents + end + + it "accepts a length, offset, and empty options Hash" do + IO.read(@fname, 3, 0, {}).should == @contents[0, 3] + end + + it "raises an IOError if the options Hash specifies write mode" do + lambda { IO.read(@fname, 3, 0, {mode: "w"}) }.should raise_error(IOError) + end + + it "raises an IOError if the options Hash specifies append only mode" do + lambda { IO.read(@fname, {mode: "a"}) }.should raise_error(IOError) + end + + it "reads the file if the options Hash includes read mode" do + IO.read(@fname, {mode: "r"}).should == @contents + end + + it "reads the file if the options Hash includes read/write mode" do + IO.read(@fname, {mode: "r+"}).should == @contents + end + + it "reads the file if the options Hash includes read/write append mode" do + IO.read(@fname, {mode: "a+"}).should == @contents + end + + it "treats second nil argument as no length limit" do + IO.read(@fname, nil).should == @contents + IO.read(@fname, nil, 5).should == IO.read(@fname, @contents.length, 5) + end + + it "treats third nil argument as 0" do + IO.read(@fname, nil, nil).should == @contents + IO.read(@fname, 5, nil).should == IO.read(@fname, 5, 0) + end + + it "reads the contents of a file up to a certain size when specified" do + IO.read(@fname, 5).should == @contents.slice(0..4) + end + + it "reads the contents of a file from an offset of a specific size when specified" do + IO.read(@fname, 5, 3).should == @contents.slice(3, 5) + end + + it "returns nil at end-of-file when length is passed" do + IO.read(@fname, 1, 10).should == nil + end + + it "raises an Errno::ENOENT when the requested file does not exist" do + rm_r @fname + lambda { IO.read @fname }.should raise_error(Errno::ENOENT) + end + + it "raises a TypeError when not passed a String type" do + lambda { IO.read nil }.should raise_error(TypeError) + end + + it "raises an ArgumentError when not passed a valid length" do + lambda { IO.read @fname, -1 }.should raise_error(ArgumentError) + end + + it "raises an Errno::EINVAL when not passed a valid offset" do + lambda { IO.read @fname, 0, -1 }.should raise_error(Errno::EINVAL) + lambda { IO.read @fname, -1, -1 }.should raise_error(Errno::EINVAL) + end + + with_feature :encoding do + it "uses the external encoding specified via the :external_encoding option" do + str = IO.read(@fname, external_encoding: Encoding::ISO_8859_1) + str.encoding.should == Encoding::ISO_8859_1 + end + + it "uses the external encoding specified via the :encoding option" do + str = IO.read(@fname, encoding: Encoding::ISO_8859_1) + str.encoding.should == Encoding::ISO_8859_1 + end + end +end + +describe "IO.read from a pipe" do + it "runs the rest as a subprocess and returns the standard output" do + cmd = "|sh -c 'echo hello'" + platform_is :windows do + cmd = "|cmd.exe /C echo hello" + end + IO.read(cmd).should == "hello\n" + end + + with_feature :fork do + it "opens a pipe to a fork if the rest is -" do + str = IO.read("|-") + if str # parent + str.should == "hello from child\n" + else #child + puts "hello from child" + exit! + end + end + end + + it "reads only the specified number of bytes requested" do + cmd = "|sh -c 'echo hello'" + platform_is :windows do + cmd = "|cmd.exe /C echo hello" + end + IO.read(cmd, 1).should == "h" + end + + platform_is_not :windows do + it "raises Errno::ESPIPE if passed an offset" do + lambda { + IO.read("|sh -c 'echo hello'", 1, 1) + }.should raise_error(Errno::ESPIPE) + end + end + +quarantine! do # The process tried to write to a nonexistent pipe. + platform_is :windows do + # TODO: It should raise Errno::ESPIPE on Windows as well + # once https://bugs.ruby-lang.org/issues/12230 is fixed. + it "raises Errno::EINVAL if passed an offset" do + lambda { + IO.read("|cmd.exe /C echo hello", 1, 1) + }.should raise_error(Errno::EINVAL) + end + end +end +end + +describe "IO.read on an empty file" do + before :each do + @fname = tmp("io_read_empty.txt") + touch(@fname) + end + + after :each do + rm_r @fname + end + + it "returns nil when length is passed" do + IO.read(@fname, 1).should == nil + end + + it "returns an empty string when no length is passed" do + IO.read(@fname).should == "" + end +end + +describe "IO#read" do + + before :each do + @fname = tmp("io_read.txt") + @contents = "1234567890" + touch(@fname) { |f| f.write @contents } + + @io = open @fname, "r+" + end + + after :each do + @io.close + rm_r @fname + end + + it "can be read from consecutively" do + @io.read(1).should == '1' + @io.read(2).should == '23' + @io.read(3).should == '456' + @io.read(4).should == '7890' + end + + it "clears the output buffer if there is nothing to read" do + @io.pos = 10 + + buf = 'non-empty string' + + @io.read(10, buf).should == nil + + buf.should == '' + end + + it "consumes zero bytes when reading zero bytes" do + @io.read(0).should == '' + @io.pos.should == 0 + + @io.getc.chr.should == '1' + end + + it "is at end-of-file when everything has been read" do + @io.read + @io.eof?.should == true + end + + it "reads the contents of a file" do + @io.read.should == @contents + end + + it "places the specified number of bytes in the buffer" do + buf = "" + @io.read 5, buf + + buf.should == "12345" + end + + it "expands the buffer when too small" do + buf = "ABCDE" + @io.read nil, buf + + buf.should == @contents + end + + it "overwrites the buffer" do + buf = "ABCDEFGHIJ" + @io.read nil, buf + + buf.should == @contents + end + + it "truncates the buffer when too big" do + buf = "ABCDEFGHIJKLMNO" + @io.read nil, buf + buf.should == @contents + + @io.rewind + + buf = "ABCDEFGHIJKLMNO" + @io.read 5, buf + buf.should == @contents[0..4] + end + + it "returns the given buffer" do + buf = "" + + @io.read(nil, buf).object_id.should == buf.object_id + end + + it "coerces the second argument to string and uses it as a buffer" do + buf = "ABCDE" + obj = mock("buff") + obj.should_receive(:to_str).any_number_of_times.and_return(buf) + + @io.read(15, obj).object_id.should_not == obj.object_id + buf.should == @contents + end + + it "returns an empty string at end-of-file" do + @io.read + @io.read.should == '' + end + + it "reads the contents of a file when more bytes are specified" do + @io.read(@contents.length + 1).should == @contents + end + + it "returns an empty string at end-of-file" do + @io.read + @io.read.should == '' + end + + it "returns an empty string when the current pos is bigger than the content size" do + @io.pos = 1000 + @io.read.should == '' + end + + it "returns nil at end-of-file with a length" do + @io.read + @io.read(1).should == nil + end + + it "with length argument returns nil when the current pos is bigger than the content size" do + @io.pos = 1000 + @io.read(1).should == nil + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.read }.should raise_error(IOError) + end + + + platform_is_not :windows do + it "raises IOError when stream is closed by another thread" do + r, w = IO.pipe + t = Thread.new do + begin + r.read(1) + rescue => e + e + end + end + + Thread.pass until t.stop? + r.close + t.join + t.value.should be_kind_of(IOError) + w.close + end + end +end + +platform_is :windows do + describe "IO#read on Windows" do + before :each do + @fname = tmp("io_read.txt") + touch(@fname, "wb") { |f| f.write "a\r\nb\r\nc" } + end + + after :each do + @io.close if @io + rm_r @fname + end + + it "normalizes line endings in text mode" do + @io = new_io(@fname, "r") + @io.read.should == "a\nb\nc" + end + + it "does not normalize line endings in binary mode" do + @io = new_io(@fname, "rb") + @io.read.should == "a\r\nb\r\nc" + end + end +end + +describe "IO#read" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close if @io + end + + it "ignores unicode encoding" do + @io.readline.should == "Voici la ligne une.\n" + # read "Qui è" + @io.read(5).should == "Qui " + [195].pack('C*') + end +end + +describe "IO#read in binary mode" do + before :each do + @internal = Encoding.default_internal + @name = fixture __FILE__, "read_binary.txt" + end + + after :each do + Encoding.default_internal = @internal + end + + it "does not transcode file contents when Encoding.default_internal is set" do + Encoding.default_internal = "utf-8" + + result = File.open(@name, "rb") { |f| f.read }.chomp + + result.encoding.should == Encoding::ASCII_8BIT + xE2 = [226].pack('C*') + result.should == ("abc" + xE2 + "def").force_encoding(Encoding::ASCII_8BIT) + end + + it "does not transcode file contents when an internal encoding is specified" do + result = File.open(@name, "r:binary:utf-8") { |f| f.read }.chomp + result.encoding.should == Encoding::ASCII_8BIT + xE2 = [226].pack('C*') + result.should == ("abc" + xE2 + "def").force_encoding(Encoding::ASCII_8BIT) + end +end + +describe "IO#read in text mode" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + @name = fixture __FILE__, "read_text.txt" + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "reads data according to the internal encoding" do + Encoding.default_internal = "utf-8" + Encoding.default_external = "utf-8" + + result = File.open(@name, "rt") { |f| f.read }.chomp + + result.encoding.should == Encoding::UTF_8 + result.should == "abcâdef" + end +end + +describe "IO.read with BOM" do + it "reads a file without a bom" do + name = fixture __FILE__, "no_bom_UTF-8.txt" + result = File.read(name, mode: "rb:BOM|utf-8") + result.force_encoding("ascii-8bit").should == "UTF-8\n" + end + + it "reads a file with a utf-8 bom" do + name = fixture __FILE__, "bom_UTF-8.txt" + result = File.read(name, mode: "rb:BOM|utf-16le") + result.force_encoding("ascii-8bit").should == "UTF-8\n" + end + + it "reads a file with a utf-16le bom" do + name = fixture __FILE__, "bom_UTF-16LE.txt" + result = File.read(name, mode: "rb:BOM|utf-8") + result.force_encoding("ascii-8bit").should == "U\x00T\x00F\x00-\x001\x006\x00L\x00E\x00\n\x00" + end + + it "reads a file with a utf-16be bom" do + name = fixture __FILE__, "bom_UTF-16BE.txt" + result = File.read(name, mode: "rb:BOM|utf-8") + result.force_encoding("ascii-8bit").should == "\x00U\x00T\x00F\x00-\x001\x006\x00B\x00E\x00\n" + end + + it "reads a file with a utf-32le bom" do + name = fixture __FILE__, "bom_UTF-32LE.txt" + result = File.read(name, mode: "rb:BOM|utf-8") + result.force_encoding("ascii-8bit").should == "U\x00\x00\x00T\x00\x00\x00F\x00\x00\x00-\x00\x00\x003\x00\x00\x002\x00\x00\x00L\x00\x00\x00E\x00\x00\x00\n\x00\x00\x00" + end + + it "reads a file with a utf-32be bom" do + name = fixture __FILE__, "bom_UTF-32BE.txt" + result = File.read(name, mode: "rb:BOM|utf-8") + result.force_encoding("ascii-8bit").should == "\x00\x00\x00U\x00\x00\x00T\x00\x00\x00F\x00\x00\x00-\x00\x00\x003\x00\x00\x002\x00\x00\x00B\x00\x00\x00E\x00\x00\x00\n" + end +end + +with_feature :encoding do + describe :io_read_internal_encoding, shared: true do + it "returns a transcoded String" do + @io.read.should == "ありがとう\n" + end + + it "sets the String encoding to the internal encoding" do + @io.read.encoding.should equal(Encoding::UTF_8) + end + + describe "when passed nil for limit" do + it "sets the buffer to a transcoded String" do + result = @io.read(nil, buf = "") + buf.should equal(result) + buf.should == "ありがとう\n" + end + + it "sets the buffer's encoding to the internal encoding" do + buf = "".force_encoding Encoding::ISO_8859_1 + @io.read(nil, buf) + buf.encoding.should equal(Encoding::UTF_8) + end + end + end + + describe :io_read_size_internal_encoding, shared: true do + it "reads bytes when passed a size" do + @io.read(2).should == [164, 162].pack('C*').force_encoding(Encoding::ASCII_8BIT) + end + + it "returns a String in ASCII-8BIT when passed a size" do + @io.read(4).encoding.should equal(Encoding::ASCII_8BIT) + end + + it "does not change the buffer's encoding when passed a limit" do + buf = "".force_encoding Encoding::ISO_8859_1 + @io.read(4, buf) + buf.should == [164, 162, 164, 234].pack('C*').force_encoding(Encoding::ISO_8859_1) + buf.encoding.should equal(Encoding::ISO_8859_1) + end + + it "trucates the buffer but does not change the buffer's encoding when no data remains" do + buf = "abc".force_encoding Encoding::ISO_8859_1 + @io.read + + @io.read(1, buf).should be_nil + buf.size.should == 0 + buf.encoding.should equal(Encoding::ISO_8859_1) + end + end + + describe "IO#read" do + describe "when IO#external_encoding and IO#internal_encoding are nil" do + before :each do + @name = tmp("io_read.txt") + touch(@name) { |f| f.write "\x00\x01\x02" } + @io = new_io @name, "r+" + end + + after :each do + @io.close if @io + rm_r @name + end + + it "sets the String encoding to Encoding.default_external" do + @io.read.encoding.should equal(Encoding.default_external) + end + end + + describe "with internal encoding" do + after :each do + @io.close if @io + end + + describe "not specified" do + before :each do + @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp" + end + + it "does not transcode the String" do + @io.read.should == ("ありがとう\n").encode(Encoding::EUC_JP) + end + + it "sets the String encoding to the external encoding" do + @io.read.encoding.should equal(Encoding::EUC_JP) + end + + it_behaves_like :io_read_size_internal_encoding, nil + end + + describe "specified by open mode" do + before :each do + @io = IOSpecs.io_fixture "read_euc_jp.txt", "r:euc-jp:utf-8" + end + + it_behaves_like :io_read_internal_encoding, nil + it_behaves_like :io_read_size_internal_encoding, nil + end + + describe "specified by mode: option" do + before :each do + @io = IOSpecs.io_fixture "read_euc_jp.txt", mode: "r:euc-jp:utf-8" + end + + it_behaves_like :io_read_internal_encoding, nil + it_behaves_like :io_read_size_internal_encoding, nil + end + + describe "specified by internal_encoding: option" do + before :each do + options = { mode: "r", + internal_encoding: "utf-8", + external_encoding: "euc-jp" } + @io = IOSpecs.io_fixture "read_euc_jp.txt", options + end + + it_behaves_like :io_read_internal_encoding, nil + it_behaves_like :io_read_size_internal_encoding, nil + end + + describe "specified by encoding: option" do + before :each do + options = { mode: "r", encoding: "euc-jp:utf-8" } + @io = IOSpecs.io_fixture "read_euc_jp.txt", options + end + + it_behaves_like :io_read_internal_encoding, nil + it_behaves_like :io_read_size_internal_encoding, nil + end + end + end +end + +describe "IO#read with large data" do + before :each do + # TODO: what is the significance of this mystery math? + @data_size = 8096 * 2 + 1024 + @data = "*" * @data_size + + @fname = tmp("io_read.txt") + touch(@fname) { |f| f.write @data } + end + + after :each do + rm_r @fname + end + + it "reads all the data at once" do + File.open(@fname, 'r') { |io| ScratchPad.record io.read } + + ScratchPad.recorded.size.should == @data_size + ScratchPad.recorded.should == @data + end + + it "reads only the requested number of bytes" do + read_size = @data_size / 2 + File.open(@fname, 'r') { |io| ScratchPad.record io.read(read_size) } + + ScratchPad.recorded.size.should == read_size + ScratchPad.recorded.should == @data[0, read_size] + end +end diff --git a/spec/rubyspec/core/io/readbyte_spec.rb b/spec/rubyspec/core/io/readbyte_spec.rb new file mode 100644 index 0000000000..1cc588eea5 --- /dev/null +++ b/spec/rubyspec/core/io/readbyte_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO#readbyte" do + before :each do + @io = File.open(__FILE__, 'r') + end + + after :each do + @io.close + end + + it "reads one byte from the stream" do + byte = @io.readbyte + byte.should == ?r.getbyte(0) + @io.pos.should == 1 + end + + it "raises EOFError on EOF" do + @io.seek(999999) + lambda do + @io.readbyte + end.should raise_error EOFError + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/io/readchar_spec.rb b/spec/rubyspec/core/io/readchar_spec.rb new file mode 100644 index 0000000000..6771fcab59 --- /dev/null +++ b/spec/rubyspec/core/io/readchar_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#readchar" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "returns the next string from the stream" do + @io.readchar.should == 'V' + @io.readchar.should == 'o' + @io.readchar.should == 'i' + # read the rest of line + @io.readline.should == "ci la ligne une.\n" + @io.readchar.should == 'Q' + end + + it "raises an EOFError when invoked at the end of the stream" do + @io.read + lambda { @io.readchar }.should raise_error(EOFError) + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.readchar }.should raise_error(IOError) + end +end + +describe "IO#readchar" do + before :each do + @io = IOSpecs.io_fixture "empty.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "raises EOFError on empty stream" do + lambda { @io.readchar }.should raise_error(EOFError) + end +end diff --git a/spec/rubyspec/core/io/readline_spec.rb b/spec/rubyspec/core/io/readline_spec.rb new file mode 100644 index 0000000000..39706948eb --- /dev/null +++ b/spec/rubyspec/core/io/readline_spec.rb @@ -0,0 +1,45 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#readline" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "returns the next line on the stream" do + @io.readline.should == "Voici la ligne une.\n" + @io.readline.should == "Qui è la linea due.\n" + end + + it "goes back to first position after a rewind" do + @io.readline.should == "Voici la ligne une.\n" + @io.rewind + @io.readline.should == "Voici la ligne une.\n" + end + + it "returns characters after the position set by #seek" do + @io.seek(1) + @io.readline.should == "oici la ligne une.\n" + end + + it "raises EOFError on end of stream" do + IOSpecs.lines.length.times { @io.readline } + lambda { @io.readline }.should raise_error(EOFError) + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.readline }.should raise_error(IOError) + end + + it "assigns the returned line to $_" do + IOSpecs.lines.each do |line| + @io.readline + $_.should == line + end + end +end diff --git a/spec/rubyspec/core/io/readlines_spec.rb b/spec/rubyspec/core/io/readlines_spec.rb new file mode 100644 index 0000000000..22ba844b52 --- /dev/null +++ b/spec/rubyspec/core/io/readlines_spec.rb @@ -0,0 +1,210 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/readlines', __FILE__) + +describe "IO#readlines" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + @orig_exteenc = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + end + + after :each do + @io.close unless @io.closed? + Encoding.default_external = @orig_exteenc + end + + it "raises an IOError if the stream is closed" do + @io.close + lambda { @io.readlines }.should raise_error(IOError) + end + + describe "when passed no arguments" do + before :each do + @sep, $/ = $/, " " + end + + after :each do + $/ = @sep + end + + it "returns an Array containing lines based on $/" do + @io.readlines.should == IOSpecs.lines_space_separator + end + end + + describe "when passed no arguments" do + it "updates self's position" do + @io.readlines + @io.pos.should eql(137) + end + + it "updates self's lineno based on the number of lines read" do + @io.readlines + @io.lineno.should eql(9) + end + + it "does not change $_" do + $_ = "test" + @io.readlines + $_.should == "test" + end + + it "returns an empty Array when self is at the end" do + @io.readlines.should == IOSpecs.lines + @io.readlines.should == [] + end + end + + describe "when passed nil" do + it "returns the remaining content as one line starting at the current position" do + @io.readlines(nil).should == [IOSpecs.lines.join] + end + end + + describe "when passed an empty String" do + it "returns an Array containing all paragraphs" do + @io.readlines("").should == IOSpecs.paragraphs + end + end + + describe "when passed a separator" do + it "returns an Array containing lines based on the separator" do + @io.readlines("r").should == IOSpecs.lines_r_separator + end + + it "returns an empty Array when self is at the end" do + @io.readlines + @io.readlines("r").should == [] + end + + it "updates self's lineno based on the number of lines read" do + @io.readlines("r") + @io.lineno.should eql(5) + end + + it "updates self's position based on the number of characters read" do + @io.readlines("r") + @io.pos.should eql(137) + end + + it "does not change $_" do + $_ = "test" + @io.readlines("r") + $_.should == "test" + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock('to_str') + obj.stub!(:to_str).and_return("r") + @io.readlines(obj).should == IOSpecs.lines_r_separator + end + end + + describe "when passed a string that starts with a |" do + it "gets data from the standard out of the subprocess" do + cmd = "|sh -c 'echo hello;echo line2'" + platform_is :windows do + cmd = "|cmd.exe /C echo hello&echo line2" + end + lines = IO.readlines(cmd) + lines.should == ["hello\n", "line2\n"] + end + + with_feature :fork do + it "gets data from a fork when passed -" do + lines = IO.readlines("|-") + + if lines # parent + lines.should == ["hello\n", "from a fork\n"] + else + puts "hello" + puts "from a fork" + exit! + end + end + end + end +end + +describe "IO#readlines" do + before :each do + @name = tmp("io_readlines") + end + + after :each do + rm_r @name + end + + it "raises an IOError if the stream is opened for append only" do + lambda do + File.open(@name, fmode("a:utf-8")) { |f| f.readlines } + end.should raise_error(IOError) + end + + it "raises an IOError if the stream is opened for write only" do + lambda do + File.open(@name, fmode("w:utf-8")) { |f| f.readlines } + end.should raise_error(IOError) + end +end + +describe "IO.readlines" do + before :each do + @external = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + + @name = fixture __FILE__, "lines.txt" + ScratchPad.record [] + end + + after :each do + Encoding.default_external = @external + end + + it "does not change $_" do + $_ = "test" + IO.readlines(@name) + $_.should == "test" + end + + it_behaves_like :io_readlines, :readlines + it_behaves_like :io_readlines_options_19, :readlines +end + +describe "IO.readlines" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + @name = fixture __FILE__, "lines.txt" + @dollar_slash = $/ + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + $/ = @dollar_slash + end + + it "encodes lines using the default external encoding" do + Encoding.default_external = Encoding::UTF_8 + lines = IO.readlines(@name) + lines.all? { |s| s.encoding == Encoding::UTF_8 }.should be_true + end + + it "encodes lines using the default internal encoding, when set" do + Encoding.default_external = Encoding::UTF_8 + Encoding.default_internal = Encoding::UTF_16 + $/ = $/.encode Encoding::UTF_16 + lines = IO.readlines(@name) + lines.all? { |s| s.encoding == Encoding::UTF_16 }.should be_true + end + + it "ignores the default internal encoding if the external encoding is ASCII-8BIT" do + Encoding.default_external = Encoding::ASCII_8BIT + Encoding.default_internal = Encoding::UTF_8 + lines = IO.readlines(@name) + lines.all? { |s| s.encoding == Encoding::ASCII_8BIT }.should be_true + end +end diff --git a/spec/rubyspec/core/io/readpartial_spec.rb b/spec/rubyspec/core/io/readpartial_spec.rb new file mode 100644 index 0000000000..16cb08dada --- /dev/null +++ b/spec/rubyspec/core/io/readpartial_spec.rb @@ -0,0 +1,96 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#readpartial" do + before :each do + @rd, @wr = IO.pipe + @rd.binmode + @wr.binmode + end + + after :each do + @rd.close unless @rd.closed? + @wr.close unless @wr.closed? + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.readpartial(10) }.should raise_error(IOError) + + @rd.close + lambda { @rd.readpartial(10) }.should raise_error(IOError) + end + + it "reads at most the specified number of bytes" do + @wr.write("foobar") + + # buffered read + @rd.read(1).should == 'f' + # return only specified number, not the whole buffer + @rd.readpartial(1).should == "o" + end + + it "reads after ungetc with data in the buffer" do + @wr.write("foobar") + c = @rd.getc + @rd.ungetc(c) + @rd.readpartial(3).should == "foo" + @rd.readpartial(3).should == "bar" + end + + it "reads after ungetc with multibyte characters in the buffer" do + @wr.write("∂φ/∂x = gaîté") + c = @rd.getc + @rd.ungetc(c) + @rd.readpartial(3).should == "\xE2\x88\x82" + @rd.readpartial(3).should == "\xCF\x86/" + end + + it "reads after ungetc without data in the buffer" do + @wr.write("f") + c = @rd.getc + @rd.ungetc(c) + @rd.readpartial(2).should == "f" + + # now, also check that the ungot char is cleared and + # not returned again + @wr.write("b") + @rd.readpartial(2).should == "b" + end + + it "discards the existing buffer content upon successful read" do + buffer = "existing" + @wr.write("hello world") + @wr.close + @rd.readpartial(11, buffer) + buffer.should == "hello world" + end + + it "raises EOFError on EOF" do + @wr.write("abc") + @wr.close + @rd.readpartial(10).should == 'abc' + lambda { @rd.readpartial(10) }.should raise_error(EOFError) + end + + it "discards the existing buffer content upon error" do + buffer = 'hello' + @wr.close + lambda { @rd.readpartial(1, buffer) }.should raise_error(EOFError) + buffer.should be_empty + end + + it "raises IOError if the stream is closed" do + @wr.close + lambda { @rd.readpartial(1) }.should raise_error(IOError) + end + + it "raises ArgumentError if the negative argument is provided" do + lambda { @rd.readpartial(-1) }.should raise_error(ArgumentError) + end + + it "immediately returns an empty string if the length argument is 0" do + @rd.readpartial(0).should == "" + end + +end diff --git a/spec/rubyspec/core/io/reopen_spec.rb b/spec/rubyspec/core/io/reopen_spec.rb new file mode 100644 index 0000000000..63020d5f3e --- /dev/null +++ b/spec/rubyspec/core/io/reopen_spec.rb @@ -0,0 +1,302 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +require 'fcntl' + +describe "IO#reopen" do + before :each do + @name = tmp("io_reopen.txt") + @other_name = tmp("io_reopen_other.txt") + + @io = new_io @name + @other_io = File.open @other_name, "w" + end + + after :each do + @io.close unless @io.closed? + @other_io.close unless @other_io.closed? + rm_r @name, @other_name + end + + it "calls #to_io to convert an object" do + obj = mock("io") + obj.should_receive(:to_io).and_return(@other_io) + @io.reopen obj + end + + it "changes the class of the instance to the class of the object returned by #to_io" do + obj = mock("io") + obj.should_receive(:to_io).and_return(@other_io) + @io.reopen(obj).should be_an_instance_of(File) + end + + it "raises an IOError if the object returned by #to_io is closed" do + obj = mock("io") + obj.should_receive(:to_io).and_return(IOSpecs.closed_io) + lambda { @io.reopen obj }.should raise_error(IOError) + end + + it "raises a TypeError if #to_io does not return an IO instance" do + obj = mock("io") + obj.should_receive(:to_io).and_return("something else") + lambda { @io.reopen obj }.should raise_error(TypeError) + end + + it "raises an IOError when called on a closed stream with an object" do + @io.close + obj = mock("io") + obj.should_not_receive(:to_io) + lambda { @io.reopen(STDOUT) }.should raise_error(IOError) + end + + it "raises an IOError if the IO argument is closed" do + lambda { @io.reopen(IOSpecs.closed_io) }.should raise_error(IOError) + end + + it "raises an IOError when called on a closed stream with an IO" do + @io.close + lambda { @io.reopen(STDOUT) }.should raise_error(IOError) + end +end + +describe "IO#reopen with a String" do + before :each do + @name = fixture __FILE__, "numbered_lines.txt" + @other_name = tmp("io_reopen.txt") + touch @other_name + @io = IOSpecs.io_fixture "lines.txt" + + @tmp_file = tmp("reopen") + end + + after :each do + @io.close unless @io.closed? + rm_r @other_name, @tmp_file + end + + it "does not raise an exception when called on a closed stream with a path" do + @io.close + @io.reopen @name, "r" + @io.closed?.should be_false + @io.gets.should == "Line 1: One\n" + end + + it "returns self" do + @io.reopen(@name).should equal(@io) + end + + it "positions a newly created instance at the beginning of the new stream" do + @io.reopen(@name) + @io.gets.should == "Line 1: One\n" + end + + it "positions an instance that has been read from at the beginning of the new stream" do + @io.gets + @io.reopen(@name) + @io.gets.should == "Line 1: One\n" + end + + platform_is_not :windows do + it "passes all mode flags through" do + @io.reopen(@tmp_file, "ab") + (@io.fcntl(Fcntl::F_GETFL) & File::APPEND).should == File::APPEND + end + end + + platform_is_not :windows do + # TODO Should this work on Windows? + it "affects exec/system/fork performed after it" do + ruby_exe fixture(__FILE__, "reopen_stdout.rb"), args: @tmp_file + File.read(@tmp_file).should == "from system\nfrom exec\n" + end + end + + it "calls #to_path on non-String arguments" do + obj = mock('path') + obj.should_receive(:to_path).and_return(@other_name) + @io.reopen(obj) + end +end + +describe "IO#reopen with a String" do + before :each do + @name = tmp("io_reopen.txt") + @other_name = tmp("io_reopen_other.txt") + @other_io = nil + + rm_r @other_name + end + + after :each do + @io.close unless @io.closed? + @other_io.close if @other_io and not @other_io.closed? + rm_r @name, @other_name + end + + it "opens a path after writing to the original file descriptor" do + @io = new_io @name, "w" + + @io.print "original data" + @io.reopen @other_name + @io.print "new data" + @io.flush + + File.read(@name).should == "original data" + File.read(@other_name).should == "new data" + end + + it "closes the file descriptor obtained by opening the new file" do + @io = new_io @name, "w" + + @other_io = File.open @other_name, "w" + max = @other_io.fileno + @other_io.close + + @io.reopen @other_name + + @other_io = File.open @other_name, "w" + @other_io.fileno.should == max + end + + it "creates the file if it doesn't exist if the IO is opened in write mode" do + @io = new_io @name, "w" + + @io.reopen(@other_name) + File.exist?(@other_name).should be_true + end + + it "creates the file if it doesn't exist if the IO is opened in write mode" do + @io = new_io @name, "a" + + @io.reopen(@other_name) + File.exist?(@other_name).should be_true + end +end + +describe "IO#reopen with a String" do + before :each do + @name = tmp("io_reopen.txt") + @other_name = tmp("io_reopen_other.txt") + + touch @name + rm_r @other_name + end + + after :each do + @io.close + rm_r @name, @other_name + end + + it "raises an Errno::ENOENT if the file does not exist and the IO is not opened in write mode" do + @io = new_io @name, "r" + lambda { @io.reopen(@other_name) }.should raise_error(Errno::ENOENT) + end +end + +describe "IO#reopen with an IO at EOF" do + before :each do + @name = tmp("io_reopen.txt") + touch(@name) { |f| f.puts "a line" } + @other_name = tmp("io_reopen_other.txt") + touch(@other_name) do |f| + f.puts "Line 1" + f.puts "Line 2" + end + + @io = new_io @name, "r" + @other_io = new_io @other_name, "r" + @io.read + end + + after :each do + @io.close unless @io.closed? + @other_io.close unless @other_io.closed? + rm_r @name, @other_name + end + + it "resets the EOF status to false" do + @io.eof?.should be_true + @io.reopen @other_io + @io.eof?.should be_false + end +end + +describe "IO#reopen with an IO" do + before :each do + @name = tmp("io_reopen.txt") + @other_name = tmp("io_reopen_other.txt") + touch(@other_name) do |f| + f.puts "Line 1" + f.puts "Line 2" + end + + @io = new_io @name + @other_io = new_io @other_name, "r" + end + + after :each do + @io.close unless @io.closed? + @other_io.close unless @other_io.closed? + rm_r @name, @other_name + end + + it "does not call #to_io" do + # Why do we not use #should_not_receive(:to_io) here? Because + # MRI actually changes the class of @io in the call to #reopen + # but does not preserve the existing singleton class of @io. + def @io.to_io; flunk; end + @io.reopen(@other_io).should be_an_instance_of(IO) + end + + it "does not change the object_id" do + obj_id = @io.object_id + @io.reopen @other_io + @io.object_id.should == obj_id + end + + it "reads from the beginning if the other IO has not been read from" do + @io.reopen @other_io + @io.gets.should == "Line 1\n" + end + + it "reads from the current position of the other IO's stream" do + @other_io.gets.should == "Line 1\n" + @io.reopen @other_io + @io.gets.should == "Line 2\n" + end +end + +describe "IO#reopen with an IO" do + before :each do + @name = tmp("io_reopen.txt") + @other_name = tmp("io_reopen_other.txt") + + @io = new_io @name + @other_io = File.open @other_name, "w" + end + + after :each do + @io.close unless @io.closed? + @other_io.close unless @other_io.closed? + rm_r @name, @other_name + end + + it "associates the IO instance with the other IO's stream" do + File.read(@other_name).should == "" + @io.reopen @other_io + @io.print "io data" + @io.flush + File.read(@name).should == "" + File.read(@other_name).should == "io data" + end + + it "may change the class of the instance" do + @io.reopen @other_io + @io.should be_an_instance_of(File) + end + + it "sets path equals to the other IO's path if other IO is File" do + @io.reopen @other_io + @io.path.should == @other_io.path + end +end diff --git a/spec/rubyspec/core/io/rewind_spec.rb b/spec/rubyspec/core/io/rewind_spec.rb new file mode 100644 index 0000000000..ecf8a71891 --- /dev/null +++ b/spec/rubyspec/core/io/rewind_spec.rb @@ -0,0 +1,38 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#rewind" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "positions the instance to the beginning of input" do + @io.readline.should == "Voici la ligne une.\n" + @io.readline.should == "Qui è la linea due.\n" + @io.rewind + @io.readline.should == "Voici la ligne une.\n" + end + + it "positions the instance to the beginning of input and clears EOF" do + value = @io.read + @io.rewind + @io.eof?.should == false + value.should == @io.read + end + + it "sets lineno to 0" do + @io.readline.should == "Voici la ligne une.\n" + @io.lineno.should == 1 + @io.rewind + @io.lineno.should == 0 + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.rewind }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/seek_spec.rb b/spec/rubyspec/core/io/seek_spec.rb new file mode 100644 index 0000000000..f7e138cbe9 --- /dev/null +++ b/spec/rubyspec/core/io/seek_spec.rb @@ -0,0 +1,79 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "IO#seek" do + it_behaves_like :io_set_pos, :seek +end + +describe "IO#seek" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "moves the read position relative to the current position with SEEK_CUR" do + lambda { @io.seek(-1) }.should raise_error(Errno::EINVAL) + @io.seek(10, IO::SEEK_CUR) + @io.readline.should == "igne une.\n" + @io.seek(-5, IO::SEEK_CUR) + @io.readline.should == "une.\n" + end + + it "moves the read position relative to the start with SEEK_SET" do + @io.seek(1) + @io.pos.should == 1 + @io.rewind + @io.seek(43, IO::SEEK_SET) + @io.readline.should == "Aquí está la línea tres.\n" + @io.seek(5, IO::SEEK_SET) + @io.readline.should == " la ligne une.\n" + end + + it "moves the read position relative to the end with SEEK_END" do + @io.seek(0, IO::SEEK_END) + @io.tell.should == 137 + @io.seek(-25, IO::SEEK_END) + @io.readline.should == "cinco.\n" + end + + it "moves the read position and clears EOF with SEEK_SET" do + value = @io.read + @io.seek(0, IO::SEEK_SET) + @io.eof?.should == false + value.should == @io.read + end + + it "moves the read position and clears EOF with SEEK_CUR" do + value = @io.read + @io.seek(-1, IO::SEEK_CUR) + @io.eof?.should == false + value[-1].should == @io.read[0] + end + + it "moves the read position and clears EOF with SEEK_END" do + value = @io.read + @io.seek(-1, IO::SEEK_END) + @io.eof?.should == false + value[-1].should == @io.read[0] + end + + platform_is :darwin do + it "supports seek offsets greater than 2^32" do + begin + zero = File.open('/dev/zero') + offset = 2**33 + zero.seek(offset, File::SEEK_SET) + pos = zero.pos + + pos.should == offset + ensure + zero.close rescue nil + end + end + end +end diff --git a/spec/rubyspec/core/io/select_spec.rb b/spec/rubyspec/core/io/select_spec.rb new file mode 100644 index 0000000000..aa1199c03b --- /dev/null +++ b/spec/rubyspec/core/io/select_spec.rb @@ -0,0 +1,115 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO.select" do + before :each do + @rd, @wr = IO.pipe + end + + after :each do + @rd.close unless @rd.closed? + @wr.close unless @wr.closed? + end + + it "blocks for duration of timeout and returns nil if there are no objects ready for I/O" do + IO.select([@rd], nil, nil, 0.001).should == nil + end + + it "returns immediately all objects that are ready for I/O when timeout is 0" do + @wr.write("be ready") + result = IO.select [@rd], [@wr], nil, 0 + result.should == [[@rd], [@wr], []] + end + + it "returns nil after timeout if there are no objects ready for I/O" do + result = IO.select [@rd], nil, nil, 0 + result.should == nil + end + + it "returns supplied objects when they are ready for I/O" do + main = Thread.current + t = Thread.new { + Thread.pass until main.status == "sleep" + @wr.write "be ready" + } + result = IO.select [@rd], nil, nil, nil + result.should == [[@rd], [], []] + t.join + end + + it "leaves out IO objects for which there is no I/O ready" do + @wr.write "be ready" + platform_is :aix do + # In AIX, when a pipe is readable, select(2) returns the write side + # of the pipe as "readable", even though you cannot actually read + # anything from the write side. + result = IO.select [@wr, @rd], nil, nil, nil + result.should == [[@wr, @rd], [], []] + end + platform_is_not :aix do + # Order matters here. We want to see that @wr doesn't expand the size + # of the returned array, so it must be 1st. + result = IO.select [@wr, @rd], nil, nil, nil + result.should == [[@rd], [], []] + end + end + + it "returns supplied objects correctly even when monitoring the same object in different arrays" do + filename = tmp("IO_select_pipe_file") + $$.to_s + io = File.open(filename, 'w+') + result = IO.select [io], [io], nil, 0 + result.should == [[io], [io], []] + io.close + rm_r filename + end + + it "invokes to_io on supplied objects that are not IO and returns the supplied objects" do + # make some data available + @wr.write("foobar") + + obj = mock("read_io") + obj.should_receive(:to_io).at_least(1).and_return(@rd) + IO.select([obj]).should == [[obj], [], []] + + obj = mock("write_io") + obj.should_receive(:to_io).at_least(1).and_return(@wr) + IO.select(nil, [obj]).should == [[], [obj], []] + end + + it "raises TypeError if supplied objects are not IO" do + lambda { IO.select([Object.new]) }.should raise_error(TypeError) + lambda { IO.select(nil, [Object.new]) }.should raise_error(TypeError) + + obj = mock("io") + obj.should_receive(:to_io).any_number_of_times.and_return(nil) + + lambda { IO.select([obj]) }.should raise_error(TypeError) + lambda { IO.select(nil, [obj]) }.should raise_error(TypeError) + end + + it "raises a TypeError if the specified timeout value is not Numeric" do + lambda { IO.select([@rd], nil, nil, Object.new) }.should raise_error(TypeError) + end + + it "raises TypeError if the first three arguments are not Arrays" do + lambda { IO.select(Object.new)}.should raise_error(TypeError) + lambda { IO.select(nil, Object.new)}.should raise_error(TypeError) + lambda { IO.select(nil, nil, Object.new)}.should raise_error(TypeError) + end + + it "raises an ArgumentError when passed a negative timeout" do + lambda { IO.select(nil, nil, nil, -5)}.should raise_error(ArgumentError) + end +end + +describe "IO.select when passed nil for timeout" do + it "sleeps forever and sets the thread status to 'sleep'" do + t = Thread.new do + IO.select(nil, nil, nil, nil) + end + + Thread.pass while t.status && t.status != "sleep" + t.status.should == "sleep" + t.kill + t.join + end +end diff --git a/spec/rubyspec/core/io/set_encoding_spec.rb b/spec/rubyspec/core/io/set_encoding_spec.rb new file mode 100644 index 0000000000..1d6e2a8f3b --- /dev/null +++ b/spec/rubyspec/core/io/set_encoding_spec.rb @@ -0,0 +1,193 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe :io_set_encoding_write, shared: true do + it "sets the encodings to nil" do + @io = new_io @name, "#{@object}:ibm437:ibm866" + @io.set_encoding nil, nil + + @io.external_encoding.should be_nil + @io.internal_encoding.should be_nil + end + + it "prevents the encodings from changing when Encoding defaults are changed" do + @io = new_io @name, "#{@object}:utf-8:us-ascii" + @io.set_encoding nil, nil + + Encoding.default_external = Encoding::IBM437 + Encoding.default_internal = Encoding::IBM866 + + @io.external_encoding.should be_nil + @io.internal_encoding.should be_nil + end + + it "sets the encodings to the current Encoding defaults" do + @io = new_io @name, @object + + Encoding.default_external = Encoding::IBM437 + Encoding.default_internal = Encoding::IBM866 + + @io.set_encoding nil, nil + + @io.external_encoding.should == Encoding::IBM437 + @io.internal_encoding.should == Encoding::IBM866 + end + end + + describe "IO#set_encoding when passed nil, nil" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::UTF_8 + Encoding.default_internal = nil + + @name = tmp('io_set_encoding.txt') + touch(@name) + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + + @io.close if @io and not @io.closed? + rm_r @name + end + + describe "with 'r' mode" do + it "sets the encodings to the current Encoding defaults" do + @io = new_io @name, "r" + + Encoding.default_external = Encoding::IBM437 + Encoding.default_internal = Encoding::IBM866 + + @io.set_encoding nil, nil + @io.external_encoding.should equal(Encoding::IBM437) + @io.internal_encoding.should equal(Encoding::IBM866) + end + + it "prevents the #internal_encoding from changing when Encoding.default_internal is changed" do + @io = new_io @name, "r" + @io.set_encoding nil, nil + + Encoding.default_internal = Encoding::IBM437 + + @io.internal_encoding.should be_nil + end + + it "allows the #external_encoding to change when Encoding.default_external is changed" do + @io = new_io @name, "r" + @io.set_encoding nil, nil + + Encoding.default_external = Encoding::IBM437 + + @io.external_encoding.should equal(Encoding::IBM437) + end + end + + describe "with 'rb' mode" do + it "returns Encoding.default_external" do + @io = new_io @name, "rb" + @io.external_encoding.should equal(Encoding::ASCII_8BIT) + + @io.set_encoding nil, nil + @io.external_encoding.should equal(Encoding.default_external) + end + end + + describe "with 'r+' mode" do + it_behaves_like :io_set_encoding_write, nil, "r+" + end + + describe "with 'w' mode" do + it_behaves_like :io_set_encoding_write, nil, "w" + end + + describe "with 'w+' mode" do + it_behaves_like :io_set_encoding_write, nil, "w+" + end + + describe "with 'a' mode" do + it_behaves_like :io_set_encoding_write, nil, "a" + end + + describe "with 'a+' mode" do + it_behaves_like :io_set_encoding_write, nil, "a+" + end + end + + describe "IO#set_encoding" do + before :each do + @name = tmp('io_set_encoding.txt') + touch(@name) + @io = new_io @name + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "returns self" do + @io.set_encoding(Encoding::UTF_8).should equal(@io) + end + + it "sets the external encoding when passed an Encoding argument" do + @io.set_encoding(Encoding::UTF_8) + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should be_nil + end + + it "sets the external and internal encoding when passed two Encoding arguments" do + @io.set_encoding(Encoding::UTF_8, Encoding::UTF_16BE) + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should == Encoding::UTF_16BE + end + + it "sets the external encoding when passed the name of an Encoding" do + @io.set_encoding("utf-8") + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should be_nil + end + + it "ignores the internal encoding if the same as external when passed Encoding objects" do + @io.set_encoding(Encoding::UTF_8, Encoding::UTF_8) + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should be_nil + end + + it "ignores the internal encoding if the same as external when passed encoding names separanted by ':'" do + @io.set_encoding("utf-8:utf-8") + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should be_nil + end + + it "sets the external and internal encoding when passed the names of Encodings separated by ':'" do + @io.set_encoding("utf-8:utf-16be") + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should == Encoding::UTF_16BE + end + + it "sets the external and internal encoding when passed two String arguments" do + @io.set_encoding("utf-8", "utf-16be") + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should == Encoding::UTF_16BE + end + + it "calls #to_str to convert an abject to a String" do + obj = mock("io_set_encoding") + obj.should_receive(:to_str).and_return("utf-8:utf-16be") + @io.set_encoding(obj) + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should == Encoding::UTF_16BE + end + + it "calls #to_str to convert the second argument to a String" do + obj = mock("io_set_encoding") + obj.should_receive(:to_str).at_least(1).times.and_return("utf-16be") + @io.set_encoding(Encoding::UTF_8, obj) + @io.external_encoding.should == Encoding::UTF_8 + @io.internal_encoding.should == Encoding::UTF_16BE + end + end +end diff --git a/spec/rubyspec/core/io/shared/binwrite.rb b/spec/rubyspec/core/io/shared/binwrite.rb new file mode 100644 index 0000000000..67f0fd5c86 --- /dev/null +++ b/spec/rubyspec/core/io/shared/binwrite.rb @@ -0,0 +1,78 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :io_binwrite, shared: true do + before :each do + @filename = tmp("IO_binwrite_file") + $$.to_s + File.open(@filename, "w") do |file| + file << "012345678901234567890123456789" + end + end + + after :each do + rm_r @filename + end + + it "coerces the argument to a string using to_s" do + (obj = mock('test')).should_receive(:to_s).and_return('a string') + IO.send(@method, @filename, obj) + end + + it "returns the number of bytes written" do + IO.send(@method, @filename, "abcde").should == 5 + end + + it "creates a file if missing" do + fn = @filename + "xxx" + begin + File.exist?(fn).should be_false + IO.send(@method, fn, "test") + File.exist?(fn).should be_true + ensure + rm_r fn + end + end + + it "creates file if missing even if offset given" do + fn = @filename + "xxx" + begin + File.exist?(fn).should be_false + IO.send(@method, fn, "test", 0) + File.exist?(fn).should be_true + ensure + rm_r fn + end + end + + it "truncates the file and writes the given string" do + IO.send(@method, @filename, "hello, world!") + File.read(@filename).should == "hello, world!" + end + + it "doesn't truncate the file and writes the given string if an offset is given" do + IO.send(@method, @filename, "hello, world!", 0) + File.read(@filename).should == "hello, world!34567890123456789" + IO.send(@method, @filename, "hello, world!", 20) + File.read(@filename).should == "hello, world!3456789hello, world!" + end + + it "doesn't truncate and writes at the given offset after passing empty opts" do + IO.send(@method, @filename, "hello world!", 1, {}) + File.read(@filename).should == "0hello world!34567890123456789" + end + + it "accepts a :mode option" do + IO.send(@method, @filename, "hello, world!", mode: 'a') + File.read(@filename).should == "012345678901234567890123456789hello, world!" + IO.send(@method, @filename, "foo", 2, mode: 'w') + File.read(@filename).should == "\0\0foo" + end + + it "raises an error if readonly mode is specified" do + lambda { IO.send(@method, @filename, "abcde", mode: "r") }.should raise_error(IOError) + end + + it "truncates if empty :opts provided and offset skipped" do + IO.send(@method, @filename, "hello, world!", {}) + File.read(@filename).should == "hello, world!" + end +end diff --git a/spec/rubyspec/core/io/shared/chars.rb b/spec/rubyspec/core/io/shared/chars.rb new file mode 100644 index 0000000000..7f2edd2b6d --- /dev/null +++ b/spec/rubyspec/core/io/shared/chars.rb @@ -0,0 +1,73 @@ +# -*- encoding: utf-8 -*- +describe :io_chars, shared: true do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + ScratchPad.record [] + end + + after :each do + @io.close unless @io.closed? + end + + it "yields each character" do + @io.readline.should == "Voici la ligne une.\n" + + count = 0 + @io.send(@method) do |c| + ScratchPad << c + break if 4 < count += 1 + end + + ScratchPad.recorded.should == ["Q", "u", "i", " ", "è"] + end + + describe "when no block is given" do + it "returns an Enumerator" do + enum = @io.send(@method) + enum.should be_an_instance_of(Enumerator) + enum.first(5).should == ["V", "o", "i", "c", "i"] + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @io.send(@method).size.should == nil + end + end + end + end + + it "returns itself" do + @io.send(@method) { |c| }.should equal(@io) + end + + it "returns an enumerator for a closed stream" do + IOSpecs.closed_io.send(@method).should be_an_instance_of(Enumerator) + end + + it "raises an IOError when an enumerator created on a closed stream is accessed" do + lambda { IOSpecs.closed_io.send(@method).first }.should raise_error(IOError) + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError) + end +end + +describe :io_chars_empty, shared: true do + before :each do + @name = tmp("io_each_char") + @io = new_io @name, "w+:utf-8" + ScratchPad.record [] + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "does not yield any characters on an empty stream" do + @io.send(@method) { |c| ScratchPad << c } + ScratchPad.recorded.should == [] + end +end diff --git a/spec/rubyspec/core/io/shared/codepoints.rb b/spec/rubyspec/core/io/shared/codepoints.rb new file mode 100644 index 0000000000..3bb3dce939 --- /dev/null +++ b/spec/rubyspec/core/io/shared/codepoints.rb @@ -0,0 +1,54 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :io_codepoints, shared: true do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + @enum = @io.send(@method) + end + + after :each do + @io.close + end + + describe "when no block is given" do + it "returns an Enumerator" do + @enum.should be_an_instance_of(Enumerator) + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @enum.size.should == nil + end + end + end + end + + it "yields each codepoint" do + @enum.first(25).should == [ + 86, 111, 105, 99, 105, 32, 108, 97, 32, 108, 105, 103, 110, + 101, 32, 117, 110, 101, 46, 10, 81, 117, 105, 32, 232 + ] + end + + it "yields each codepoint starting from the current position" do + @io.pos = 130 + @enum.to_a.should == [101, 32, 115, 105, 120, 46, 10] + end + + it "raises an error if reading invalid sequence" do + @io.pos = 60 # inside of a multibyte sequence + lambda { @enum.first }.should raise_error(ArgumentError) + end + + it "does not change $_" do + $_ = "test" + @enum.to_a + $_.should == "test" + end + + it "raises an IOError when self is not readable" do + lambda { IOSpecs.closed_io.send(@method).to_a }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/shared/each.rb b/spec/rubyspec/core/io/shared/each.rb new file mode 100644 index 0000000000..dc07434ecd --- /dev/null +++ b/spec/rubyspec/core/io/shared/each.rb @@ -0,0 +1,135 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :io_each, shared: true do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + ScratchPad.record [] + end + + after :each do + @io.close if @io + end + + describe "with no separator" do + it "yields each line to the passed block" do + @io.send(@method) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines + end + + it "yields each line starting from the current position" do + @io.pos = 41 + @io.send(@method) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines[2..-1] + end + + it "returns self" do + @io.send(@method) { |l| l }.should equal(@io) + end + + it "does not change $_" do + $_ = "test" + @io.send(@method) { |s| s } + $_.should == "test" + end + + it "returns self" do + @io.send(@method) { |l| l }.should equal(@io) + end + + it "raises an IOError when self is not readable" do + lambda { IOSpecs.closed_io.send(@method) {} }.should raise_error(IOError) + end + + it "makes line count accessible via lineno" do + @io.send(@method) { ScratchPad << @io.lineno } + ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ] + end + + it "makes line count accessible via $." do + @io.send(@method) { ScratchPad << $. } + ScratchPad.recorded.should == [ 1,2,3,4,5,6,7,8,9 ] + end + + describe "when no block is given" do + it "returns an Enumerator" do + enum = @io.send(@method) + enum.should be_an_instance_of(Enumerator) + + enum.each { |l| ScratchPad << l } + ScratchPad.recorded.should == IOSpecs.lines + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @io.send(@method).size.should == nil + end + end + end + end + end + + describe "with limit" do + describe "when limit is 0" do + it "raises an ArgumentError" do + # must pass block so Enumerator is evaluated and raises + lambda { @io.send(@method, 0){} }.should raise_error(ArgumentError) + end + end + end + + describe "when passed a String containing one space as a separator" do + it "uses the passed argument as the line separator" do + @io.send(@method, " ") { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_space_separator + end + + it "does not change $_" do + $_ = "test" + @io.send(@method, " ") { |s| } + $_.should == "test" + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock("to_str") + obj.stub!(:to_str).and_return(" ") + + @io.send(@method, obj) { |l| ScratchPad << l } + ScratchPad.recorded.should == IOSpecs.lines_space_separator + end + end + + describe "when passed nil as a separator" do + it "yields self's content starting from the current position when the passed separator is nil" do + @io.pos = 100 + @io.send(@method, nil) { |s| ScratchPad << s } + ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six.\n"] + end + end + + describe "when passed an empty String as a separator" do + it "yields each paragraph" do + @io.send(@method, "") { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.paragraphs + end + end +end + +describe :io_each_default_separator, shared: true do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + ScratchPad.record [] + @sep, $/ = $/, " " + end + + after :each do + @io.close if @io + $/ = @sep + end + + it "uses $/ as the default line separator" do + @io.send(@method) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_space_separator + end +end diff --git a/spec/rubyspec/core/io/shared/gets_ascii.rb b/spec/rubyspec/core/io/shared/gets_ascii.rb new file mode 100644 index 0000000000..2a8fe3c9a5 --- /dev/null +++ b/spec/rubyspec/core/io/shared/gets_ascii.rb @@ -0,0 +1,19 @@ +# -*- encoding: binary -*- +describe :io_gets_ascii, shared: true do + describe "with ASCII separator" do + before :each do + @name = tmp("gets_specs.txt") + touch(@name, "wb") { |f| f.print "this is a test\xFFtesty\ntestier" } + + File.open(@name, "rb") { |f| @data = f.send(@method, "\xFF") } + end + + after :each do + rm_r @name + end + + it "returns the separator's character representation" do + @data.should == "this is a test\xFF" + end + end +end diff --git a/spec/rubyspec/core/io/shared/new.rb b/spec/rubyspec/core/io/shared/new.rb new file mode 100644 index 0000000000..12f889f646 --- /dev/null +++ b/spec/rubyspec/core/io/shared/new.rb @@ -0,0 +1,378 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +# This group of specs may ONLY contain specs that do successfully create +# an IO instance from the file descriptor returned by #new_fd helper. +describe :io_new, shared: true do + before :each do + @name = tmp("io_new.txt") + @fd = new_fd @name + @io = nil + end + + after :each do + if @io + @io.close + elsif @fd + IO.new(@fd, "w").close + end + rm_r @name + end + + it "creates an IO instance from a Fixnum argument" do + @io = IO.send(@method, @fd, "w") + @io.should be_an_instance_of(IO) + end + + it "creates an IO instance when STDOUT is closed" do + verbose, $VERBOSE = $VERBOSE, nil + stdout = STDOUT + stdout_file = tmp("stdout.txt") + + begin + @io = IO.send(@method, @fd, "w") + @io.should be_an_instance_of(IO) + ensure + STDOUT = stdout + $VERBOSE = verbose + rm_r stdout_file + end + end + + it "creates an IO instance when STDERR is closed" do + verbose, $VERBOSE = $VERBOSE, nil + stderr = STDERR + stderr_file = tmp("stderr.txt") + STDERR = new_io stderr_file + STDERR.close + + begin + @io = IO.send(@method, @fd, "w") + @io.should be_an_instance_of(IO) + ensure + STDERR = stderr + $VERBOSE = verbose + rm_r stderr_file + end + end + + it "calls #to_int on an object to convert to a Fixnum" do + obj = mock("file descriptor") + obj.should_receive(:to_int).and_return(@fd) + @io = IO.send(@method, obj, "w") + @io.should be_an_instance_of(IO) + end + + it "accepts a :mode option" do + @io = IO.send(@method, @fd, mode: "w") + @io.write("foo").should == 3 + end + + it "accepts a mode argument set to nil with a valid :mode option" do + @io = IO.send(@method, @fd, nil, mode: "w") + @io.write("foo").should == 3 + end + + it "accepts a mode argument with a :mode option set to nil" do + @io = IO.send(@method, @fd, "w", mode: nil) + @io.write("foo").should == 3 + end + + it "uses the external encoding specified in the mode argument" do + @io = IO.send(@method, @fd, 'w:utf-8') + @io.external_encoding.to_s.should == 'UTF-8' + end + + it "uses the external and the internal encoding specified in the mode argument" do + @io = IO.send(@method, @fd, 'w:utf-8:ISO-8859-1') + @io.external_encoding.to_s.should == 'UTF-8' + @io.internal_encoding.to_s.should == 'ISO-8859-1' + end + + it "uses the external encoding specified via the :external_encoding option" do + @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8'}) + @io.external_encoding.to_s.should == 'UTF-8' + end + + it "uses the internal encoding specified via the :internal_encoding option" do + @io = IO.send(@method, @fd, 'w', {internal_encoding: 'ibm866'}) + @io.internal_encoding.to_s.should == 'IBM866' + end + + it "uses the colon-separated encodings specified via the :encoding option" do + @io = IO.send(@method, @fd, 'w', {encoding: 'utf-8:ISO-8859-1'}) + @io.external_encoding.to_s.should == 'UTF-8' + @io.internal_encoding.to_s.should == 'ISO-8859-1' + end + + it "uses the :encoding option as the external encoding when only one is given" do + @io = IO.send(@method, @fd, 'w', {encoding: 'ISO-8859-1'}) + @io.external_encoding.to_s.should == 'ISO-8859-1' + end + + it "uses the :encoding options as the external encoding when it's an Encoding object" do + @io = IO.send(@method, @fd, 'w', {encoding: Encoding::ISO_8859_1}) + @io.external_encoding.should == Encoding::ISO_8859_1 + end + + it "ignores the :encoding option when the :external_encoding option is present" do + lambda { + @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', encoding: 'iso-8859-1:iso-8859-1'}) + }.should complain(/Ignoring encoding parameter/) + @io.external_encoding.to_s.should == 'UTF-8' + end + + it "ignores the :encoding option when the :internal_encoding option is present" do + lambda { + @io = IO.send(@method, @fd, 'w', {internal_encoding: 'ibm866', encoding: 'iso-8859-1:iso-8859-1'}) + }.should complain(/Ignoring encoding parameter/) + @io.internal_encoding.to_s.should == 'IBM866' + end + + it "uses the encoding specified via the :mode option hash" do + @io = IO.send(@method, @fd, {mode: 'w:utf-8:ISO-8859-1'}) + @io.external_encoding.to_s.should == 'UTF-8' + @io.internal_encoding.to_s.should == 'ISO-8859-1' + end + + it "ignores the :internal_encoding option when the same as the external encoding" do + @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', internal_encoding: 'utf-8'}) + @io.external_encoding.to_s.should == 'UTF-8' + @io.internal_encoding.to_s.should == '' + end + + it "sets internal encoding to nil when passed '-'" do + @io = IO.send(@method, @fd, 'w', {external_encoding: 'utf-8', internal_encoding: '-'}) + @io.external_encoding.to_s.should == 'UTF-8' + @io.internal_encoding.to_s.should == '' + end + + it "sets binmode from mode string" do + @io = IO.send(@method, @fd, 'wb') + @io.binmode?.should == true + end + + it "does not set binmode without being asked" do + @io = IO.send(@method, @fd, 'w') + @io.binmode?.should == false + end + + it "sets binmode from :binmode option" do + @io = IO.send(@method, @fd, 'w', {binmode: true}) + @io.binmode?.should == true + end + + it "does not set binmode from false :binmode" do + @io = IO.send(@method, @fd, 'w', {binmode: false}) + @io.binmode?.should == false + end + + it "sets external encoding to binary with binmode in mode string" do + @io = IO.send(@method, @fd, 'wb') + @io.external_encoding.to_s.should == 'ASCII-8BIT' + end + + # #5917 + it "sets external encoding to binary with :binmode option" do + @io = IO.send(@method, @fd, 'w', {binmode: true}) + @io.external_encoding.to_s.should == 'ASCII-8BIT' + end + + it "does not use binary encoding when mode encoding is specified" do + @io = IO.send(@method, @fd, 'wb:iso-8859-1') + @io.external_encoding.to_s.should == 'ISO-8859-1' + end + + it "does not use binary encoding when :encoding option is specified" do + @io = IO.send(@method, @fd, 'wb', encoding: "iso-8859-1") + @io.external_encoding.to_s.should == 'ISO-8859-1' + end + + it "does not use binary encoding when :external_encoding option is specified" do + @io = IO.send(@method, @fd, 'wb', external_encoding: "iso-8859-1") + @io.external_encoding.to_s.should == 'ISO-8859-1' + end + + it "does not use binary encoding when :internal_encoding option is specified" do + @io = IO.send(@method, @fd, 'wb', internal_encoding: "ibm866") + @io.internal_encoding.to_s.should == 'IBM866' + end + + it "accepts nil options" do + @io = IO.send(@method, @fd, 'w', nil) + @io.write("foo").should == 3 + end + + it "coerces mode with #to_str" do + mode = mock("mode") + mode.should_receive(:to_str).and_return('w') + @io = IO.send(@method, @fd, mode) + end + + it "coerces mode with #to_int" do + mode = mock("mode") + mode.should_receive(:to_int).and_return(File::WRONLY) + @io = IO.send(@method, @fd, mode) + end + + it "coerces mode with #to_str when passed in options" do + mode = mock("mode") + mode.should_receive(:to_str).and_return('w') + @io = IO.send(@method, @fd, mode: mode) + end + + it "coerces mode with #to_int when passed in options" do + mode = mock("mode") + mode.should_receive(:to_int).and_return(File::WRONLY) + @io = IO.send(@method, @fd, mode: mode) + end + + it "coerces :encoding option with #to_str" do + encoding = mock("encoding") + encoding.should_receive(:to_str).and_return('utf-8') + @io = IO.send(@method, @fd, 'w', encoding: encoding) + end + + it "coerces :external_encoding option with #to_str" do + encoding = mock("encoding") + encoding.should_receive(:to_str).and_return('utf-8') + @io = IO.send(@method, @fd, 'w', external_encoding: encoding) + end + + it "coerces :internal_encoding option with #to_str" do + encoding = mock("encoding") + encoding.should_receive(:to_str).at_least(:once).and_return('utf-8') + @io = IO.send(@method, @fd, 'w', internal_encoding: encoding) + end + + it "coerces options as third argument with #to_hash" do + options = mock("options") + options.should_receive(:to_hash).and_return({}) + @io = IO.send(@method, @fd, 'w', options) + end + + it "coerces options as second argument with #to_hash" do + options = mock("options") + options.should_receive(:to_hash).and_return({}) + @io = IO.send(@method, @fd, options) + end + + it "accepts an :autoclose option" do + @io = IO.send(@method, @fd, 'w', autoclose: false) + @io.autoclose?.should == false + @io.autoclose = true + end + + it "accepts any truthy option :autoclose" do + @io = IO.send(@method, @fd, 'w', autoclose: 42) + @io.autoclose?.should == true + end +end + +# This group of specs may ONLY contain specs that do not actually create +# an IO instance from the file descriptor returned by #new_fd helper. +describe :io_new_errors, shared: true do + before :each do + @name = tmp("io_new.txt") + @fd = new_fd @name + end + + after :each do + IO.new(@fd, "w").close if @fd + rm_r @name + end + + it "raises an Errno::EBADF if the file descriptor is not valid" do + lambda { IO.send(@method, -1, "w") }.should raise_error(Errno::EBADF) + end + + it "raises an IOError if passed a closed stream" do + lambda { IO.send(@method, IOSpecs.closed_io.fileno, 'w') }.should raise_error(IOError) + end + + platform_is_not :windows do + it "raises an Errno::EINVAL if the new mode is not compatible with the descriptor's current mode" do + lambda { IO.send(@method, @fd, "r") }.should raise_error(Errno::EINVAL) + end + end + + it "raises ArgumentError if passed an empty mode string" do + lambda { IO.send(@method, @fd, "") }.should raise_error(ArgumentError) + end + + it "raises an error if passed modes two ways" do + lambda { + IO.send(@method, @fd, "w", mode: "w") + }.should raise_error(ArgumentError) + end + + it "raises an error if passed encodings two ways" do + lambda { + @io = IO.send(@method, @fd, 'w:ISO-8859-1', {encoding: 'ISO-8859-1'}) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, 'w:ISO-8859-1', {external_encoding: 'ISO-8859-1'}) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, 'w:ISO-8859-1:UTF-8', {internal_encoding: 'ISO-8859-1'}) + }.should raise_error(ArgumentError) + end + + it "raises an error if passed matching binary/text mode two ways" do + lambda { + @io = IO.send(@method, @fd, "wb", binmode: true) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, "wt", textmode: true) + }.should raise_error(ArgumentError) + + lambda { + @io = IO.send(@method, @fd, "wb", textmode: false) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, "wt", binmode: false) + }.should raise_error(ArgumentError) + end + + it "raises an error if passed conflicting binary/text mode two ways" do + lambda { + @io = IO.send(@method, @fd, "wb", binmode: false) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, "wt", textmode: false) + }.should raise_error(ArgumentError) + + lambda { + @io = IO.send(@method, @fd, "wb", textmode: true) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, "wt", binmode: true) + }.should raise_error(ArgumentError) + end + + it "raises an error when trying to set both binmode and textmode" do + lambda { + @io = IO.send(@method, @fd, "w", textmode: true, binmode: true) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, File::Constants::WRONLY, textmode: true, binmode: true) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if not passed a hash or nil for options" do + lambda { + @io = IO.send(@method, @fd, 'w', false) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, false, false) + }.should raise_error(ArgumentError) + lambda { + @io = IO.send(@method, @fd, nil, false) + }.should raise_error(ArgumentError) + end + + it "raises TypeError if passed a hash for mode and nil for options" do + lambda { + @io = IO.send(@method, @fd, {mode: 'w'}, nil) + }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/io/shared/pos.rb b/spec/rubyspec/core/io/shared/pos.rb new file mode 100644 index 0000000000..fef7ab2bf7 --- /dev/null +++ b/spec/rubyspec/core/io/shared/pos.rb @@ -0,0 +1,72 @@ +describe :io_pos, shared: true do + before :each do + @fname = tmp('test.txt') + File.open(@fname, 'w') { |f| f.write "123" } + end + + after :each do + rm_r @fname + end + + it "gets the offset" do + File.open @fname do |f| + f.send(@method).should == 0 + f.read 1 + f.send(@method).should == 1 + f.read 2 + f.send(@method).should == 3 + end + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.send(@method) }.should raise_error(IOError) + end + + it "resets #eof?" do + open @fname do |io| + io.read 1 + io.read 1 + io.send(@method) + io.eof?.should == false + end + end +end + +describe :io_set_pos, shared: true do + before :each do + @fname = tmp('test.txt') + File.open(@fname, 'w') { |f| f.write "123" } + end + + after :each do + rm_r @fname + end + + it "sets the offset" do + File.open @fname do |f| + val1 = f.read 1 + f.send @method, 0 + f.read(1).should == val1 + end + end + + it "converts arguments to Integers" do + File.open @fname do |io| + o = mock("o") + o.should_receive(:to_int).and_return(1) + + io.send @method, o + io.pos.should == 1 + end + end + + it "does not accept Bignums that don't fit in a C long" do + File.open @fname do |io| + lambda { io.send @method, 2**128 }.should raise_error(RangeError) + end + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.send @method, 0 }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/shared/readlines.rb b/spec/rubyspec/core/io/shared/readlines.rb new file mode 100644 index 0000000000..4cb821274a --- /dev/null +++ b/spec/rubyspec/core/io/shared/readlines.rb @@ -0,0 +1,204 @@ +describe :io_readlines, shared: true do + it "raises TypeError if the first parameter is nil" do + lambda { IO.send(@method, nil, &@object) }.should raise_error(TypeError) + end + + it "raises an Errno::ENOENT if the file does not exist" do + name = tmp("nonexistent.txt") + lambda { IO.send(@method, name, &@object) }.should raise_error(Errno::ENOENT) + end + + it "yields a single string with entire content when the separator is nil" do + result = IO.send(@method, @name, nil, &@object) + (result ? result : ScratchPad.recorded).should == [IO.read(@name)] + end + + it "yields a sequence of paragraphs when the separator is an empty string" do + result = IO.send(@method, @name, "", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_empty_separator + end +end + +describe :io_readlines_options_19, shared: true do + before :each do + @filename = tmp("io readlines options") + end + + after :each do + rm_r @filename + end + + describe "when passed name" do + it "calls #to_path to convert the name" do + name = mock("io name to_path") + name.should_receive(:to_path).and_return(@name) + IO.send(@method, name, &@object) + end + + it "defaults to $/ as the separator" do + result = IO.send(@method, @name, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines + end + end + + describe "when passed name, object" do + it "calls #to_str to convert the object to a separator" do + sep = mock("io readlines separator") + sep.should_receive(:to_str).at_least(1).and_return(" ") + result = IO.send(@method, @name, sep, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator + end + + describe "when the object is a Fixnum" do + before :each do + @sep = $/ + end + + after :each do + $/ = @sep + end + + it "defaults to $/ as the separator" do + $/ = " " + result = IO.send(@method, @name, 10, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "uses the object as a limit if it is a Fixnum" do + result = IO.send(@method, @name, 10, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_limit + end + end + + describe "when the object is a String" do + it "uses the value as the separator" do + result = IO.send(@method, @name, " ", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator + end + + it "accepts non-ASCII data as separator" do + result = IO.send(@method, @name, "\303\250".force_encoding("utf-8"), &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_arbitrary_separator + end + end + + describe "when the object is a Hash" do + it "uses the value as the options hash" do + result = IO.send(@method, @name, mode: "r", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines + end + end + end + + describe "when passed name, object, object" do + describe "when the first object is a Fixnum" do + it "uses the second object as an options Hash" do + lambda do + IO.send(@method, @filename, 10, mode: "w", &@object) + end.should raise_error(IOError) + end + + it "calls #to_hash to convert the second object to a Hash" do + options = mock("io readlines options Hash") + options.should_receive(:to_hash).and_return({ mode: "w" }) + lambda do + IO.send(@method, @filename, 10, options, &@object) + end.should raise_error(IOError) + end + end + + describe "when the first object is a String" do + it "uses the second object as a limit if it is a Fixnum" do + result = IO.send(@method, @name, " ", 10, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "calls #to_int to convert the second object" do + limit = mock("io readlines limit") + limit.should_receive(:to_int).at_least(1).and_return(10) + result = IO.send(@method, @name, " ", limit, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "uses the second object as an options Hash" do + lambda do + IO.send(@method, @filename, " ", mode: "w", &@object) + end.should raise_error(IOError) + end + + it "calls #to_hash to convert the second object to a Hash" do + options = mock("io readlines options Hash") + options.should_receive(:to_hash).and_return({ mode: "w" }) + lambda do + IO.send(@method, @filename, " ", options, &@object) + end.should raise_error(IOError) + end + end + + describe "when the first object is not a String or Fixnum" do + it "calls #to_str to convert the object to a String" do + sep = mock("io readlines separator") + sep.should_receive(:to_str).at_least(1).and_return(" ") + result = IO.send(@method, @name, sep, 10, mode: "r", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "uses the second object as a limit if it is a Fixnum" do + result = IO.send(@method, @name, " ", 10, mode: "r", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "calls #to_int to convert the second object" do + limit = mock("io readlines limit") + limit.should_receive(:to_int).at_least(1).and_return(10) + result = IO.send(@method, @name, " ", limit, &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "uses the second object as an options Hash" do + lambda do + IO.send(@method, @filename, " ", mode: "w", &@object) + end.should raise_error(IOError) + end + + it "calls #to_hash to convert the second object to a Hash" do + options = mock("io readlines options Hash") + options.should_receive(:to_hash).and_return({ mode: "w" }) + lambda do + IO.send(@method, @filename, " ", options, &@object) + end.should raise_error(IOError) + end + end + end + + describe "when passed name, separator, limit, options" do + it "calls #to_path to convert the name object" do + name = mock("io name to_path") + name.should_receive(:to_path).and_return(@name) + result = IO.send(@method, name, " ", 10, mode: "r", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "calls #to_str to convert the separator object" do + sep = mock("io readlines separator") + sep.should_receive(:to_str).at_least(1).and_return(" ") + result = IO.send(@method, @name, sep, 10, mode: "r", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "calls #to_int to convert the limit argument" do + limit = mock("io readlines limit") + limit.should_receive(:to_int).at_least(1).and_return(10) + result = IO.send(@method, @name, " ", limit, mode: "r", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit + end + + it "calls #to_hash to convert the options object" do + options = mock("io readlines options Hash") + options.should_receive(:to_hash).and_return({ mode: "w" }) + lambda do + IO.send(@method, @filename, " ", 10, options, &@object) + end.should raise_error(IOError) + end + end +end diff --git a/spec/rubyspec/core/io/shared/tty.rb b/spec/rubyspec/core/io/shared/tty.rb new file mode 100644 index 0000000000..eddc5d15af --- /dev/null +++ b/spec/rubyspec/core/io/shared/tty.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :io_tty, shared: true do + platform_is_not :windows do + it "returns true if this stream is a terminal device (TTY)" do + begin + # check to enabled tty + File.open('/dev/tty') {} + rescue Errno::ENXIO + # workaround for not configured environment like OS X + 1.should == 1 + else + File.open('/dev/tty') { |f| f.send(@method) }.should == true + end + end + end + + it "returns false if this stream is not a terminal device (TTY)" do + File.open(__FILE__) { |f| f.send(@method) }.should == false + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.send @method }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/shared/write.rb b/spec/rubyspec/core/io/shared/write.rb new file mode 100644 index 0000000000..fd4b0af30e --- /dev/null +++ b/spec/rubyspec/core/io/shared/write.rb @@ -0,0 +1,72 @@ +# encoding: utf-8 +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :io_write, shared: true do + before :each do + @filename = tmp("IO_syswrite_file") + $$.to_s + File.open(@filename, "w") do |file| + file.send(@method, "012345678901234567890123456789") + end + @file = File.open(@filename, "r+") + @readonly_file = File.open(@filename) + end + + after :each do + @readonly_file.close if @readonly_file + @file.close if @file + rm_r @filename + end + + it "coerces the argument to a string using to_s" do + (obj = mock('test')).should_receive(:to_s).and_return('a string') + @file.send(@method, obj) + end + + it "checks if the file is writable if writing more than zero bytes" do + lambda { @readonly_file.send(@method, "abcde") }.should raise_error(IOError) + end + + it "returns the number of bytes written" do + written = @file.send(@method, "abcde") + written.should == 5 + end + + it "invokes to_s on non-String argument" do + data = "abcdefgh9876" + (obj = mock(data)).should_receive(:to_s).and_return(data) + @file.send(@method, obj) + @file.seek(0) + @file.read(data.size).should == data + end + + it "writes all of the string's bytes without buffering if mode is sync" do + @file.sync = true + written = @file.send(@method, "abcde") + written.should == 5 + File.open(@filename) do |file| + file.read(10).should == "abcde56789" + end + end + + it "does not warn if called after IO#read" do + @file.read(5) + lambda { @file.send(@method, "fghij") }.should_not complain + end + + it "writes to the current position after IO#read" do + @file.read(5) + @file.send(@method, "abcd") + @file.rewind + @file.read.should == "01234abcd901234567890123456789" + end + + it "advances the file position by the count of given bytes" do + @file.send(@method, "abcde") + @file.read(10).should == "5678901234" + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.send(@method, "hello") }.should raise_error(IOError) + end + +end diff --git a/spec/rubyspec/core/io/stat_spec.rb b/spec/rubyspec/core/io/stat_spec.rb new file mode 100644 index 0000000000..d59535843a --- /dev/null +++ b/spec/rubyspec/core/io/stat_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#stat" do + before :each do + @io = IO.popen 'cat', "r+" + end + + after :each do + @io.close unless @io.closed? + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.stat }.should raise_error(IOError) + end + + it "returns a File::Stat object for the stream" do + STDOUT.stat.should be_an_instance_of(File::Stat) + end + + it "can stat pipes" do + @io.stat.should be_an_instance_of(File::Stat) + end +end diff --git a/spec/rubyspec/core/io/sync_spec.rb b/spec/rubyspec/core/io/sync_spec.rb new file mode 100644 index 0000000000..5cd873d799 --- /dev/null +++ b/spec/rubyspec/core/io/sync_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#sync=" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "sets the sync mode to true or false" do + @io.sync = true + @io.sync.should == true + @io.sync = false + @io.sync.should == false + end + + it "accepts non-boolean arguments" do + @io.sync = 10 + @io.sync.should == true + @io.sync = nil + @io.sync.should == false + @io.sync = Object.new + @io.sync.should == true + end + + it "raises an IOError on closed stream" do + lambda { IOSpecs.closed_io.sync = true }.should raise_error(IOError) + end +end + +describe "IO#sync" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "returns the current sync mode" do + @io.sync.should == false + end + + it "raises an IOError on closed stream" do + lambda { IOSpecs.closed_io.sync }.should raise_error(IOError) + end +end + +describe "IO#sync" do + it "is false by default for STDIN" do + STDIN.sync.should == false + end + + it "is false by default for STDOUT" do + STDOUT.sync.should == false + end + + it "is true by default for STDERR" do + STDERR.sync.should == true + end +end diff --git a/spec/rubyspec/core/io/sysopen_spec.rb b/spec/rubyspec/core/io/sysopen_spec.rb new file mode 100644 index 0000000000..f6d37de364 --- /dev/null +++ b/spec/rubyspec/core/io/sysopen_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO.sysopen" do + before :each do + @filename = tmp("rubinius-spec-io-sysopen-#{$$}.txt") + @fd = nil + end + + after :each do + IO.for_fd(@fd).close if @fd + rm_r @filename + end + + it "returns the file descriptor for a given path" do + @fd = IO.sysopen(@filename, "w") + @fd.should be_kind_of(Fixnum) + @fd.should_not equal(0) + end + + # opening a directory is not supported on Windows + platform_is_not :windows do + it "works on directories" do + @fd = IO.sysopen(tmp("")) # /tmp + @fd.should be_kind_of(Fixnum) + @fd.should_not equal(0) + end + end + + it "calls #to_path to convert an object to a path" do + path = mock('sysopen to_path') + path.should_receive(:to_path).and_return(@filename) + @fd = IO.sysopen(path, 'w') + end + + it "accepts a mode as second argument" do + lambda { @fd = IO.sysopen(@filename, "w") }.should_not raise_error + @fd.should_not equal(0) + end + + it "accepts permissions as third argument" do + @fd = IO.sysopen(@filename, "w", 777) + @fd.should_not equal(0) + end + + it "accepts mode & permission that are nil" do + touch @filename # create the file + @fd = IO.sysopen(@filename, nil, nil) + @fd.should_not equal(0) + end +end diff --git a/spec/rubyspec/core/io/sysread_spec.rb b/spec/rubyspec/core/io/sysread_spec.rb new file mode 100644 index 0000000000..1993653df9 --- /dev/null +++ b/spec/rubyspec/core/io/sysread_spec.rb @@ -0,0 +1,82 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#sysread on a file" do + before :each do + @file_name = tmp("IO_sysread_file") + $$.to_s + File.open(@file_name, "w") do |f| + # write some stuff + f.write("012345678901234567890123456789") + end + @file = File.open(@file_name, "r+") + end + + after :each do + @file.close + rm_r @file_name + end + + it "reads the specified number of bytes from the file" do + @file.sysread(15).should == "012345678901234" + end + + it "reads the specified number of bytes from the file to the buffer" do + buf = "" # empty buffer + @file.sysread(15, buf).should == buf + buf.should == "012345678901234" + + @file.rewind + + buf = "ABCDE" # small buffer + @file.sysread(15, buf).should == buf + buf.should == "012345678901234" + + @file.rewind + + buf = "ABCDE" * 5 # large buffer + @file.sysread(15, buf).should == buf + buf.should == "012345678901234" + end + + it "coerces the second argument to string and uses it as a buffer" do + buf = "ABCDE" + (obj = mock("buff")).should_receive(:to_str).any_number_of_times.and_return(buf) + @file.sysread(15, obj).should == buf + buf.should == "012345678901234" + end + + it "advances the position of the file by the specified number of bytes" do + @file.sysread(15) + @file.sysread(5).should == "56789" + end + + it "reads normally even when called immediately after a buffered IO#read" do + @file.read(15) + @file.sysread(5).should == "56789" + end + + it "does not raise error if called after IO#read followed by IO#write" do + @file.read(5) + @file.write("abcde") + lambda { @file.sysread(5) }.should_not raise_error(IOError) + end + + it "does not raise error if called after IO#read followed by IO#syswrite" do + @file.read(5) + @file.syswrite("abcde") + lambda { @file.sysread(5) }.should_not raise_error(IOError) + end + + it "reads updated content after the flushed buffered IO#write" do + @file.write("abcde") + @file.flush + @file.sysread(5).should == "56789" + File.open(@file_name) do |f| + f.sysread(10).should == "abcde56789" + end + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.sysread(5) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/sysseek_spec.rb b/spec/rubyspec/core/io/sysseek_spec.rb new file mode 100644 index 0000000000..bcce968c7d --- /dev/null +++ b/spec/rubyspec/core/io/sysseek_spec.rb @@ -0,0 +1,44 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "IO#sysseek" do + it_behaves_like :io_set_pos, :seek +end + +describe "IO#sysseek" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "moves the read position relative to the current position with SEEK_CUR" do + @io.sysseek(10, IO::SEEK_CUR) + @io.readline.should == "igne une.\n" + end + + it "raises an error when called after buffered reads" do + @io.readline + lambda { @io.sysseek(-5, IO::SEEK_CUR) }.should raise_error(IOError) + end + + it "moves the read position relative to the start with SEEK_SET" do + @io.sysseek(43, IO::SEEK_SET) + @io.readline.should == "Aquí está la línea tres.\n" + end + + it "moves the read position relative to the end with SEEK_END" do + @io.sysseek(1, IO::SEEK_END) + + # this is the safest way of checking the EOF when + # sys-* methods are invoked + lambda { @io.sysread(1) }.should raise_error(EOFError) + + @io.sysseek(-25, IO::SEEK_END) + @io.sysread(7).should == "cinco.\n" + end +end diff --git a/spec/rubyspec/core/io/syswrite_spec.rb b/spec/rubyspec/core/io/syswrite_spec.rb new file mode 100644 index 0000000000..879423de2e --- /dev/null +++ b/spec/rubyspec/core/io/syswrite_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/write', __FILE__) + +describe "IO#syswrite on a file" do + before :each do + @filename = tmp("IO_syswrite_file") + $$.to_s + File.open(@filename, "w") do |file| + file.syswrite("012345678901234567890123456789") + end + @file = File.open(@filename, "r+") + @readonly_file = File.open(@filename) + end + + after :each do + @file.close + @readonly_file.close + rm_r @filename + end + + it "writes all of the string's bytes but does not buffer them" do + written = @file.syswrite("abcde") + written.should == 5 + File.open(@filename) do |file| + file.sysread(10).should == "abcde56789" + file.seek(0) + @file.fsync + file.sysread(10).should == "abcde56789" + end + end + + it "warns if called immediately after a buffered IO#write" do + @file.write("abcde") + lambda { @file.syswrite("fghij") }.should complain(/syswrite/) + end + + it "does not warn if called after IO#write with intervening IO#sysread" do + @file.syswrite("abcde") + @file.sysread(5) + lambda { @file.syswrite("fghij") }.should_not complain + end + + it "writes to the actual file position when called after buffered IO#read" do + @file.read(5) + @file.syswrite("abcde") + File.open(@filename) do |file| + file.sysread(10).should == "01234abcde" + end + end +end + +describe "IO#syswrite" do + it_behaves_like :io_write, :syswrite +end diff --git a/spec/rubyspec/core/io/tell_spec.rb b/spec/rubyspec/core/io/tell_spec.rb new file mode 100644 index 0000000000..d2f523cf10 --- /dev/null +++ b/spec/rubyspec/core/io/tell_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/pos', __FILE__) + +describe "IO#tell" do + it_behaves_like(:io_pos, :tell) +end diff --git a/spec/rubyspec/core/io/to_i_spec.rb b/spec/rubyspec/core/io/to_i_spec.rb new file mode 100644 index 0000000000..bbe656cdcc --- /dev/null +++ b/spec/rubyspec/core/io/to_i_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#to_i" do + it "returns the numeric file descriptor of the given IO object" do + $stdout.to_i.should == 1 + end + + it "raises IOError on closed stream" do + lambda { IOSpecs.closed_io.to_i }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/to_io_spec.rb b/spec/rubyspec/core/io/to_io_spec.rb new file mode 100644 index 0000000000..76ebefb38f --- /dev/null +++ b/spec/rubyspec/core/io/to_io_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#to_io" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + end + + after :each do + @io.close unless @io.closed? + end + + it "returns self for open stream" do + @io.to_io.should equal(@io) + end + + it "returns self for closed stream" do + io = IOSpecs.closed_io + io.to_io.should equal(io) + end +end diff --git a/spec/rubyspec/core/io/try_convert_spec.rb b/spec/rubyspec/core/io/try_convert_spec.rb new file mode 100644 index 0000000000..0326982ff1 --- /dev/null +++ b/spec/rubyspec/core/io/try_convert_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO.try_convert" do + before :each do + @name = tmp("io_try_convert.txt") + @io = new_io @name + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "returns the passed IO object" do + IO.try_convert(@io).should equal(@io) + end + + it "does not call #to_io on an IO instance" do + @io.should_not_receive(:to_io) + IO.try_convert(@io) + end + + it "calls #to_io to coerce an object" do + obj = mock("io") + obj.should_receive(:to_io).and_return(@io) + IO.try_convert(obj).should equal(@io) + end + + it "returns nil when the passed object does not respond to #to_io" do + IO.try_convert(mock("io")).should be_nil + end + + it "return nil when BasicObject is passed" do + IO.try_convert(BasicObject.new).should be_nil + end + + it "raises a TypeError if the object does not return an IO from #to_io" do + obj = mock("io") + obj.should_receive(:to_io).and_return("io") + lambda { IO.try_convert(obj) }.should raise_error(TypeError) + end + + it "propagates an exception raised by #to_io" do + obj = mock("io") + obj.should_receive(:to_io).and_raise(TypeError.new) + lambda{ IO.try_convert(obj) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/io/tty_spec.rb b/spec/rubyspec/core/io/tty_spec.rb new file mode 100644 index 0000000000..3c1449b030 --- /dev/null +++ b/spec/rubyspec/core/io/tty_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/tty', __FILE__) + +describe "IO#tty?" do + it_behaves_like :io_tty, :tty? +end diff --git a/spec/rubyspec/core/io/ungetbyte_spec.rb b/spec/rubyspec/core/io/ungetbyte_spec.rb new file mode 100644 index 0000000000..ee334b469b --- /dev/null +++ b/spec/rubyspec/core/io/ungetbyte_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "IO#ungetbyte" do + before :each do + @name = tmp("io_ungetbyte") + touch(@name) { |f| f.write "a" } + @io = new_io @name, "r" + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "does nothing when passed nil" do + @io.ungetbyte(nil).should be_nil + @io.getbyte.should == 97 + end + + it "puts back each byte in a String argument" do + @io.ungetbyte("cat").should be_nil + @io.getbyte.should == 99 + @io.getbyte.should == 97 + @io.getbyte.should == 116 + @io.getbyte.should == 97 + end + + it "calls #to_str to convert the argument" do + str = mock("io ungetbyte") + str.should_receive(:to_str).and_return("dog") + + @io.ungetbyte(str).should be_nil + @io.getbyte.should == 100 + @io.getbyte.should == 111 + @io.getbyte.should == 103 + @io.getbyte.should == 97 + end + + it "puts back one byte for an Integer argument" do + @io.ungetbyte(4095).should be_nil + @io.getbyte.should == 255 + end + + it "raises an IOError if the IO is closed" do + @io.close + lambda { @io.ungetbyte(42) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/ungetc_spec.rb b/spec/rubyspec/core/io/ungetc_spec.rb new file mode 100644 index 0000000000..ce4cc9d346 --- /dev/null +++ b/spec/rubyspec/core/io/ungetc_spec.rb @@ -0,0 +1,119 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "IO#ungetc" do + before :each do + @io = IOSpecs.io_fixture "lines.txt" + + @empty = tmp('empty.txt') + end + + after :each do + @io.close unless @io.closed? + rm_r @empty + end + + it "pushes back one character onto stream" do + @io.getc.should == ?V + @io.ungetc(86) + @io.getc.should == ?V + + @io.ungetc(10) + @io.getc.should == ?\n + + @io.getc.should == ?o + @io.getc.should == ?i + # read the rest of line + @io.readline.should == "ci la ligne une.\n" + @io.getc.should == ?Q + @io.ungetc(99) + @io.getc.should == ?c + end + + it "pushes back one character when invoked at the end of the stream" do + # read entire content + @io.read + @io.ungetc(100) + @io.getc.should == ?d + end + + it "pushes back one character when invoked at the start of the stream" do + @io.read(0) + @io.ungetc(100) + @io.getc.should == ?d + end + + it "pushes back one character when invoked on empty stream" do + touch(@empty) + + File.open(@empty) { |empty| + empty.getc().should == nil + empty.ungetc(10) + empty.getc.should == ?\n + } + end + + it "affects EOF state" do + touch(@empty) + + File.open(@empty) { |empty| + empty.eof?.should == true + empty.getc.should == nil + empty.ungetc(100) + empty.eof?.should == false + } + end + + it "adjusts the stream position" do + @io.pos.should == 0 + + # read one char + c = @io.getc + @io.pos.should == 1 + @io.ungetc(c) + @io.pos.should == 0 + + # read all + @io.read + pos = @io.pos + @io.ungetc(98) + @io.pos.should == pos - 1 + end + + it "makes subsequent unbuffered operations to raise IOError" do + @io.getc + @io.ungetc(100) + lambda { @io.sysread(1) }.should raise_error(IOError) + end + + it "does not affect the stream and returns nil when passed nil" do + @io.getc.should == ?V + @io.ungetc(nil) + @io.getc.should == ?o + end + + it "puts one or more characters back in the stream" do + @io.gets + @io.ungetc("Aquí ").should be_nil + @io.gets.chomp.should == "Aquí Qui è la linea due." + end + + it "calls #to_str to convert the argument if it is not an Integer" do + chars = mock("io ungetc") + chars.should_receive(:to_str).and_return("Aquí ") + + @io.ungetc(chars).should be_nil + @io.gets.chomp.should == "Aquí Voici la ligne une." + end + + it "returns nil when invoked on stream that was not yet read" do + @io.ungetc(100).should be_nil + end + + it "raises IOError on closed stream" do + @io.getc + @io.close + lambda { @io.ungetc(100) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/core/io/write_nonblock_spec.rb b/spec/rubyspec/core/io/write_nonblock_spec.rb new file mode 100644 index 0000000000..a6a263e931 --- /dev/null +++ b/spec/rubyspec/core/io/write_nonblock_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/write', __FILE__) + +# See https://bugs.ruby-lang.org/issues/5954#note-5 +platform_is_not :windows do + describe "IO#write_nonblock on a file" do + before :each do + @filename = tmp("IO_syswrite_file") + $$.to_s + File.open(@filename, "w") do |file| + file.write_nonblock("012345678901234567890123456789") + end + @file = File.open(@filename, "r+") + @readonly_file = File.open(@filename) + end + + after :each do + @file.close if @file + @readonly_file.close if @readonly_file + rm_r @filename + end + + it "writes all of the string's bytes but does not buffer them" do + written = @file.write_nonblock("abcde") + written.should == 5 + File.open(@filename) do |file| + file.sysread(10).should == "abcde56789" + file.seek(0) + @file.fsync + file.sysread(10).should == "abcde56789" + end + end + + it "checks if the file is writable if writing zero bytes" do + lambda { @readonly_file.write_nonblock("") }.should raise_error + end + end + + describe "IO#write_nonblock" do + it_behaves_like :io_write, :write_nonblock + end +end + +describe 'IO#write_nonblock' do + before do + @read, @write = IO.pipe + end + + after do + @read.close + @write.close + end + + it "raises an exception extending IO::WaitWritable when the write would block" do + lambda { + loop { @write.write_nonblock('a' * 10_000) } + }.should raise_error(IO::WaitWritable) { |e| + platform_is_not :windows do + e.should be_kind_of(Errno::EAGAIN) + end + platform_is :windows do + e.should be_kind_of(Errno::EWOULDBLOCK) + end + } + end + + ruby_version_is "2.3" do + context "when exception option is set to false" do + it "returns :wait_writable when the operation would block" do + loop { break if @write.write_nonblock("a" * 10_000, exception: false) == :wait_writable } + 1.should == 1 + end + end + end + +end diff --git a/spec/rubyspec/core/io/write_spec.rb b/spec/rubyspec/core/io/write_spec.rb new file mode 100644 index 0000000000..1011efe8d5 --- /dev/null +++ b/spec/rubyspec/core/io/write_spec.rb @@ -0,0 +1,157 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/write', __FILE__) +require File.expand_path('../shared/binwrite', __FILE__) + +describe "IO#write on a file" do + before :each do + @filename = tmp("IO_syswrite_file") + $$.to_s + File.open(@filename, "w") do |file| + file.write("012345678901234567890123456789") + end + @file = File.open(@filename, "r+") + @readonly_file = File.open(@filename) + end + + after :each do + @file.close + @readonly_file.close + rm_r @filename + end + + it "does not check if the file is writable if writing zero bytes" do + lambda { @readonly_file.write("") }.should_not raise_error + end + + it "returns a length of 0 when writing a blank string" do + @file.write('').should == 0 + end + + with_feature :encoding do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + + Encoding.default_external = Encoding::UTF_8 + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it "returns the number of bytes written" do + @file.write("hellø").should == 6 + end + + it "uses the encoding from the given option for non-ascii encoding" do + File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file| + file.write("hi").should == 8 + end + File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000" + end + + it "uses an :open_args option" do + IO.write(@filename, 'hi', open_args: ["w", nil, {encoding: Encoding::UTF_32LE}]).should == 8 + end + + it "raises a invalid byte sequence error if invalid bytes are being written" do + # pack "\xFEhi" to avoid utf-8 conflict + xFEhi = ([254].pack('C*') + 'hi').force_encoding('utf-8') + File.open(@filename, "w", encoding: Encoding::US_ASCII) do |file| + lambda { file.write(xFEhi) }.should raise_error(Encoding::InvalidByteSequenceError) + end + end + + it "writes binary data if no encoding is given" do + File.open(@filename, "w") do |file| + file.write('Hëllö'.encode('ISO-8859-1')) + end + ë = ([235].pack('U')).encode('ISO-8859-1') + ö = ([246].pack('U')).encode('ISO-8859-1') + res = "H#{ë}ll#{ö}" + File.binread(@filename).should == res.force_encoding(Encoding::ASCII_8BIT) + end + end +end + +describe "IO.write" do + it_behaves_like :io_binwrite, :write + + it "uses an :open_args option" do + IO.write(@filename, 'hi', open_args: ["w", nil, {encoding: Encoding::UTF_32LE}]).should == 8 + end + + it "disregards other options if :open_args is given" do + IO.write(@filename, 'hi', 2, mode: "r", encoding: Encoding::UTF_32LE, open_args: ["w"]).should == 2 + File.read(@filename).should == "\0\0hi" + end + + it "uses the given encoding and returns the number of bytes written" do + IO.write(@filename, 'hi', mode: "w", encoding: Encoding::UTF_32LE).should == 8 + end + + it "writes binary data if no encoding is given" do + IO.write(@filename, 'Hëllö'.encode('ISO-8859-1')) + xEB = [235].pack('C*') + xF6 = [246].pack('C*') + File.binread(@filename).should == ("H" + xEB + "ll" + xF6).force_encoding(Encoding::ASCII_8BIT) + end + + platform_is_not :windows do + describe "on a FIFO" do + before :each do + @fifo = tmp("File_open_fifo") + system "mkfifo #{@fifo}" + end + + after :each do + rm_r @fifo + end + + it "writes correctly" do + thr = Thread.new do + IO.read(@fifo) + end + begin + string = "hi" + IO.write(@fifo, string).should == string.length + ensure + thr.join + end + end + end + end +end + +describe "IO#write" do + it_behaves_like :io_write, :write +end + +platform_is :windows do + describe "IO#write on Windows" do + before :each do + @fname = tmp("io_write.txt") + end + + after :each do + rm_r @fname + @io.close if @io and !@io.closed? + end + + it "normalizes line endings in text mode" do + @io = new_io(@fname, "wt") + @io.write "a\nb\nc" + @io.close + File.binread(@fname).should == "a\r\nb\r\nc" + end + + it "does not normalize line endings in binary mode" do + @io = new_io(@fname, "wb") + @io.write "a\r\nb\r\nc" + @io.close + File.binread(@fname).should == "a\r\nb\r\nc" + end + end +end diff --git a/spec/rubyspec/core/kernel/Array_spec.rb b/spec/rubyspec/core/kernel/Array_spec.rb new file mode 100644 index 0000000000..6031a828f6 --- /dev/null +++ b/spec/rubyspec/core/kernel/Array_spec.rb @@ -0,0 +1,97 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel" do + it "has private instance method Array()" do + Kernel.should have_private_instance_method(:Array) + end +end + +describe :kernel_Array, shared: true do + before :each do + @array = [1, 2, 3] + end + + it "does not call #to_ary on an Array" do + @array.should_not_receive(:to_ary) + @object.send(@method, @array).should == @array + end + + it "calls #to_ary to convert the argument to an Array" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_ary).and_return(@array) + obj.should_not_receive(:to_a) + + @object.send(@method, obj).should == @array + end + + it "does not call #to_a on an Array" do + @array.should_not_receive(:to_a) + @object.send(@method, @array).should == @array + end + + it "calls #to_a if the argument does not respond to #to_ary" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_a).and_return(@array) + + @object.send(@method, obj).should == @array + end + + it "calls #to_a if #to_ary returns nil" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_ary).and_return(nil) + obj.should_receive(:to_a).and_return(@array) + + @object.send(@method, obj).should == @array + end + + it "returns an Array containing the argument if #to_a returns nil" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_a).and_return(nil) + + @object.send(@method, obj).should == [obj] + end + + it "calls #to_ary first, even if it's private" do + obj = KernelSpecs::PrivateToAry.new + + @object.send(@method, obj).should == [1, 2] + end + + it "calls #to_a if #to_ary is not defined, even if it's private" do + obj = KernelSpecs::PrivateToA.new + + @object.send(@method, obj).should == [3, 4] + end + + it "returns an Array containing the argument if it responds to neither #to_ary nor #to_a" do + obj = mock("Array(x)") + @object.send(@method, obj).should == [obj] + end + + it "returns an empty Array when passed nil" do + @object.send(@method, nil).should == [] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("Array() string") + obj.should_receive(:to_ary).and_return("string") + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_a does not return an Array" do + obj = mock("Array() string") + obj.should_receive(:to_a).and_return("string") + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end +end + +describe "Kernel.Array" do + it_behaves_like :kernel_Array, :Array_method, KernelSpecs +end + +describe "Kernel#Array" do + it_behaves_like :kernel_Array, :Array_function, KernelSpecs +end diff --git a/spec/rubyspec/core/kernel/Complex_spec.rb b/spec/rubyspec/core/kernel/Complex_spec.rb new file mode 100644 index 0000000000..b156cc4549 --- /dev/null +++ b/spec/rubyspec/core/kernel/Complex_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/Complex', __FILE__) + +describe "Kernel.Complex()" do + it_behaves_like :kernel_Complex, :Complex +end diff --git a/spec/rubyspec/core/kernel/Float_spec.rb b/spec/rubyspec/core/kernel/Float_spec.rb new file mode 100644 index 0000000000..ee20190094 --- /dev/null +++ b/spec/rubyspec/core/kernel/Float_spec.rb @@ -0,0 +1,316 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_float, shared: true do + it "returns the identical Float for numeric Floats" do + float = 1.12 + float2 = @object.send(:Float, float) + float2.should == float + float2.object_id.should == float.object_id + end + + it "returns a Float for Fixnums" do + @object.send(:Float, 1).should == 1.0 + end + + it "returns a Float for Complex with only a real part" do + @object.send(:Float, Complex(1)).should == 1.0 + end + + it "returns a Float for Bignums" do + @object.send(:Float, 1000000000000).should == 1000000000000.0 + end + + it "raises an ArgumentError for nil" do + lambda { @object.send(:Float, nil) }.should raise_error(TypeError) + end + + it "returns the identical NaN for NaN" do + nan = nan_value + nan.nan?.should be_true + nan2 = @object.send(:Float, nan) + nan2.nan?.should be_true + nan2.should equal(nan) + end + + it "returns the same Infinity for Infinity" do + infinity = infinity_value + infinity2 = @object.send(:Float, infinity) + infinity2.should == infinity_value + infinity.should equal(infinity2) + end + + it "converts Strings to floats without calling #to_f" do + string = "10" + string.should_not_receive(:to_f) + @object.send(:Float, string).should == 10.0 + end + + it "converts Strings with decimal points into Floats" do + @object.send(:Float, "10.0").should == 10.0 + end + + it "raises an ArgumentError for a String of word characters" do + lambda { @object.send(:Float, "float") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are two decimal points in the String" do + lambda { @object.send(:Float, "10.0.0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String of numbers followed by word characters" do + lambda { @object.send(:Float, "10D") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String of word characters followed by numbers" do + lambda { @object.send(:Float, "D10") }.should raise_error(ArgumentError) + end + + it "is strict about the string form even across newlines" do + lambda { @object.send(:Float, "not a number\n10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "10\nnot a number") }.should raise_error(ArgumentError) + end + + it "converts String subclasses to floats without calling #to_f" do + my_string = Class.new(String) do + def to_f() 1.2 end + end + + @object.send(:Float, my_string.new("10")).should == 10.0 + end + + it "returns a positive Float if the string is prefixed with +" do + @object.send(:Float, "+10").should == 10.0 + @object.send(:Float, " +10").should == 10.0 + end + + it "returns a negative Float if the string is prefixed with +" do + @object.send(:Float, "-10").should == -10.0 + @object.send(:Float, " -10").should == -10.0 + end + + it "raises an ArgumentError if a + or - is embedded in a String" do + lambda { @object.send(:Float, "1+1") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "1-1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if a String has a trailing + or -" do + lambda { @object.send(:Float, "11+") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "11-") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with a leading _" do + lambda { @object.send(:Float, "_1") }.should raise_error(ArgumentError) + end + + it "returns a value for a String with an embedded _" do + @object.send(:Float, "1_000").should == 1000.0 + end + + it "raises an ArgumentError for a String with a trailing _" do + lambda { @object.send(:Float, "10_") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String of \\0" do + lambda { @object.send(:Float, "\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with a leading \\0" do + lambda { @object.send(:Float, "\01") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with an embedded \\0" do + lambda { @object.send(:Float, "1\01") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with a trailing \\0" do + lambda { @object.send(:Float, "1\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String that is just an empty space" do + lambda { @object.send(:Float, " ") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String that with an embedded space" do + lambda { @object.send(:Float, "1 2") }.should raise_error(ArgumentError) + end + + it "returns a value for a String with a leading space" do + @object.send(:Float, " 1").should == 1.0 + end + + it "returns a value for a String with a trailing space" do + @object.send(:Float, "1 ").should == 1.0 + end + + it "returns a value for a String with any leading whitespace" do + @object.send(:Float, "\t\n1").should == 1.0 + end + + it "returns a value for a String with any trailing whitespace" do + @object.send(:Float, "1\t\n").should == 1.0 + end + + %w(e E).each do |e| + it "raises an ArgumentError if #{e} is the trailing character" do + lambda { @object.send(:Float, "2#{e}") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if #{e} is the leading character" do + lambda { @object.send(:Float, "#{e}2") }.should raise_error(ArgumentError) + end + + it "returns Infinity for '2#{e}1000'" do + @object.send(:Float, "2#{e}1000").should == Float::INFINITY + end + + it "returns 0 for '2#{e}-1000'" do + @object.send(:Float, "2#{e}-1000").should == 0 + end + + it "allows embedded _ in a number on either side of the #{e}" do + @object.send(:Float, "2_0#{e}100").should == 20e100 + @object.send(:Float, "20#{e}1_00").should == 20e100 + @object.send(:Float, "2_0#{e}1_00").should == 20e100 + end + + it "raises an exception if a space is embedded on either side of the '#{e}'" do + lambda { @object.send(:Float, "2 0#{e}100") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "20#{e}1 00") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a leading _ on either side of the '#{e}'" do + lambda { @object.send(:Float, "_20#{e}100") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "20#{e}_100") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a trailing _ on either side of the '#{e}'" do + lambda { @object.send(:Float, "20_#{e}100") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "20#{e}100_") }.should raise_error(ArgumentError) + end + + it "allows decimal points on the left side of the '#{e}'" do + @object.send(:Float, "2.0#{e}2").should == 2e2 + end + + it "raises an ArgumentError if there's a decimal point on the right side of the '#{e}'" do + lambda { @object.send(:Float, "20#{e}2.0") }.should raise_error(ArgumentError) + end + end + + describe "for hexadecimal literals with binary exponent" do + %w(p P).each do |p| + it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do + @object.send(:Float, "0x10#{p}0").should == 16.0 + end + + it "interprets the exponent (on the right of '#{p}') in decimal" do + @object.send(:Float, "0x1#{p}10").should == 1024.0 + end + + it "raises an ArgumentError if #{p} is the trailing character" do + lambda { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if #{p} is the leading character" do + lambda { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError) + end + + it "returns Infinity for '0x1#{p}10000'" do + @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY + end + + it "returns 0 for '0x1#{p}-10000'" do + @object.send(:Float, "0x1#{p}-10000").should == 0 + end + + it "allows embedded _ in a number on either side of the #{p}" do + @object.send(:Float, "0x1_0#{p}10").should == 16384.0 + @object.send(:Float, "0x10#{p}1_0").should == 16384.0 + @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0 + end + + it "raises an exception if a space is embedded on either side of the '#{p}'" do + lambda { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a leading _ on either side of the '#{p}'" do + lambda { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a trailing _ on either side of the '#{p}'" do + lambda { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError) + end + + it "allows hexadecimal points on the left side of the '#{p}'" do + @object.send(:Float, "0x1.8#{p}0").should == 1.5 + end + + it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do + lambda { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError) + end + end + end + + it "returns a Float that can be a parameter to #Float again" do + float = @object.send(:Float, "10") + @object.send(:Float, float).should == 10.0 + end + + it "otherwise, converts the given argument to a Float by calling #to_f" do + (obj = mock('1.2')).should_receive(:to_f).once.and_return(1.2) + obj.should_not_receive(:to_i) + @object.send(:Float, obj).should == 1.2 + end + + it "returns the identical NaN if to_f is called and it returns NaN" do + nan = nan_value + (nan_to_f = mock('NaN')).should_receive(:to_f).once.and_return(nan) + nan2 = @object.send(:Float, nan_to_f) + nan2.nan?.should be_true + nan2.should equal(nan) + end + + it "returns the identical Infinity if to_f is called and it returns Infinity" do + infinity = infinity_value + (infinity_to_f = mock('Infinity')).should_receive(:to_f).once.and_return(infinity) + infinity2 = @object.send(:Float, infinity_to_f) + infinity2.should equal(infinity) + end + + it "raises a TypeError if #to_f is not provided" do + lambda { @object.send(:Float, mock('x')) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_f returns a String" do + (obj = mock('ha!')).should_receive(:to_f).once.and_return('ha!') + lambda { @object.send(:Float, obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_f returns an Integer" do + (obj = mock('123')).should_receive(:to_f).once.and_return(123) + lambda { @object.send(:Float, obj) }.should raise_error(TypeError) + end + + it "raises a RangeError when passed a Complex argument" do + c = Complex(2, 3) + lambda { @object.send(:Float, c) }.should raise_error(RangeError) + end +end + +describe "Kernel.Float" do + it_behaves_like :kernel_float, :Float, Kernel +end + +describe "Kernel#Float" do + it_behaves_like :kernel_float, :Float, Object.new +end + +describe "Kernel#Float" do + it "is a private method" do + Kernel.should have_private_instance_method(:Float) + end +end diff --git a/spec/rubyspec/core/kernel/Hash_spec.rb b/spec/rubyspec/core/kernel/Hash_spec.rb new file mode 100644 index 0000000000..8d51316c75 --- /dev/null +++ b/spec/rubyspec/core/kernel/Hash_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#hash" do + it "needs to be reviewed for spec completeness" +end + +describe "Kernel" do + it "has private instance method Hash()" do + Kernel.should have_private_instance_method(:Hash) + end +end + +describe :kernel_Hash, shared: true do + before :each do + @hash = { a: 1} + end + + it "converts nil to a Hash" do + @object.send(@method, nil).should == {} + end + + it "converts an empty array to a Hash" do + @object.send(@method, []).should == {} + end + + it "does not call #to_hash on an Hash" do + @hash.should_not_receive(:to_hash) + @object.send(@method, @hash).should == @hash + end + + it "calls #to_hash to convert the argument to an Hash" do + obj = mock("Hash(a: 1)") + obj.should_receive(:to_hash).and_return(@hash) + + @object.send(@method, obj).should == @hash + end + + it "raises a TypeError if it doesn't respond to #to_hash" do + lambda { @object.send(@method, mock("")) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_hash does not return an Hash" do + obj = mock("Hash() string") + obj.should_receive(:to_hash).and_return("string") + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end +end + +describe "Kernel.Hash" do + it_behaves_like :kernel_Hash, :Hash_method, KernelSpecs +end + +describe "Kernel#Hash" do + it_behaves_like :kernel_Hash, :Hash_function, KernelSpecs +end diff --git a/spec/rubyspec/core/kernel/Integer_spec.rb b/spec/rubyspec/core/kernel/Integer_spec.rb new file mode 100644 index 0000000000..1e95fc9151 --- /dev/null +++ b/spec/rubyspec/core/kernel/Integer_spec.rb @@ -0,0 +1,697 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_integer, shared: true do + it "returns a Bignum for a Bignum" do + Integer(2e100).should == 2e100 + end + + it "returns a Fixnum for a Fixnum" do + Integer(100).should == 100 + end + + it "uncritically return the value of to_int even if it is not an Integer" do + obj = mock("object") + obj.should_receive(:to_int).and_return("1") + obj.should_not_receive(:to_i) + Integer(obj).should == "1" + end + + it "raises a TypeError when passed nil" do + lambda { Integer(nil) }.should raise_error(TypeError) + end + + it "returns a Fixnum or Bignum object" do + Integer(2).should be_an_instance_of(Fixnum) + Integer(9**99).should be_an_instance_of(Bignum) + end + + it "truncates Floats" do + Integer(3.14).should == 3 + Integer(90.8).should == 90 + end + + it "calls to_i on Rationals" do + Integer(Rational(8,3)).should == 2 + Integer(3.quo(2)).should == 1 + end + + it "returns the value of to_int if the result is a Fixnum" do + obj = mock("object") + obj.should_receive(:to_int).and_return(1) + obj.should_not_receive(:to_i) + Integer(obj).should == 1 + end + + it "returns the value of to_int if the result is a Bignum" do + obj = mock("object") + obj.should_receive(:to_int).and_return(2e100) + obj.should_not_receive(:to_i) + Integer(obj).should == 2e100 + end + + it "calls to_i on an object whose to_int returns nil" do + obj = mock("object") + obj.should_receive(:to_int).and_return(nil) + obj.should_receive(:to_i).and_return(1) + Integer(obj).should == 1 + end + + it "raises a TypeError if to_i returns a value that is not an Integer" do + obj = mock("object") + obj.should_receive(:to_i).and_return("1") + lambda { Integer(obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if no to_int or to_i methods exist" do + obj = mock("object") + lambda { Integer(obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if to_int returns nil and no to_i exists" do + obj = mock("object") + obj.should_receive(:to_i).and_return(nil) + lambda { Integer(obj) }.should raise_error(TypeError) + end + + it "raises a FloatDomainError when passed NaN" do + lambda { Integer(nan_value) }.should raise_error(FloatDomainError) + end + + it "raises a FloatDomainError when passed Infinity" do + lambda { Integer(infinity_value) }.should raise_error(FloatDomainError) + end +end + +describe "Integer() given a String", shared: true do + it "raises an ArgumentError if the String is a null byte" do + lambda { Integer("\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String starts with a null byte" do + lambda { Integer("\01") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String ends with a null byte" do + lambda { Integer("1\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String contains a null byte" do + lambda { Integer("1\01") }.should raise_error(ArgumentError) + end + + it "ignores leading whitespace" do + Integer(" 1").should == 1 + Integer(" 1").should == 1 + Integer("\t\n1").should == 1 + end + + it "ignores trailing whitespace" do + Integer("1 ").should == 1 + Integer("1 ").should == 1 + Integer("1\t\n").should == 1 + end + + it "raises an ArgumentError if there are leading _s" do + lambda { Integer("_1") }.should raise_error(ArgumentError) + lambda { Integer("___1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing _s" do + lambda { Integer("1_") }.should raise_error(ArgumentError) + lambda { Integer("1___") }.should raise_error(ArgumentError) + end + + it "ignores an embedded _" do + Integer("1_1").should == 11 + end + + it "raises an ArgumentError if there are multiple embedded _s" do + lambda { Integer("1__1") }.should raise_error(ArgumentError) + lambda { Integer("1___1") }.should raise_error(ArgumentError) + end + + it "ignores a single leading +" do + Integer("+1").should == 1 + end + + it "raises an ArgumentError if there is a space between the + and number" do + lambda { Integer("+ 1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are multiple leading +s" do + lambda { Integer("++1") }.should raise_error(ArgumentError) + lambda { Integer("+++1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing +s" do + lambda { Integer("1+") }.should raise_error(ArgumentError) + lambda { Integer("1+++") }.should raise_error(ArgumentError) + end + + it "makes the number negative if there's a leading -" do + Integer("-1").should == -1 + end + + it "raises an ArgumentError if there are multiple leading -s" do + lambda { Integer("--1") }.should raise_error(ArgumentError) + lambda { Integer("---1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing -s" do + lambda { Integer("1-") }.should raise_error(ArgumentError) + lambda { Integer("1---") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there is a period" do + lambda { Integer("0.0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for an empty String" do + lambda { Integer("") }.should raise_error(ArgumentError) + end + + it "parses the value as 0 if the string consists of a single zero character" do + Integer("0").should == 0 + end + + %w(x X).each do |x| + it "parses the value as a hex number if there's a leading 0#{x}" do + Integer("0#{x}1").should == 0x1 + Integer("0#{x}dd").should == 0xdd + end + + it "is a positive hex number if there's a leading +0#{x}" do + Integer("+0#{x}1").should == 0x1 + Integer("+0#{x}dd").should == 0xdd + end + + it "is a negative hex number if there's a leading -0#{x}" do + Integer("-0#{x}1").should == -0x1 + Integer("-0#{x}dd").should == -0xdd + end + + it "raises an ArgumentError if the number cannot be parsed as hex" do + lambda { Integer("0#{x}g") }.should raise_error(ArgumentError) + end + end + + %w(b B).each do |b| + it "parses the value as a binary number if there's a leading 0#{b}" do + Integer("0#{b}1").should == 0b1 + Integer("0#{b}10").should == 0b10 + end + + it "is a positive binary number if there's a leading +0#{b}" do + Integer("+0#{b}1").should == 0b1 + Integer("+0#{b}10").should == 0b10 + end + + it "is a negative binary number if there's a leading -0#{b}" do + Integer("-0#{b}1").should == -0b1 + Integer("-0#{b}10").should == -0b10 + end + + it "raises an ArgumentError if the number cannot be parsed as binary" do + lambda { Integer("0#{b}2") }.should raise_error(ArgumentError) + end + end + + ["o", "O", ""].each do |o| + it "parses the value as an octal number if there's a leading 0#{o}" do + Integer("0#{o}1").should == 0O1 + Integer("0#{o}10").should == 0O10 + end + + it "is a positive octal number if there's a leading +0#{o}" do + Integer("+0#{o}1").should == 0O1 + Integer("+0#{o}10").should == 0O10 + end + + it "is a negative octal number if there's a leading -0#{o}" do + Integer("-0#{o}1").should == -0O1 + Integer("-0#{o}10").should == -0O10 + end + + it "raises an ArgumentError if the number cannot be parsed as octal" do + lambda { Integer("0#{o}9") }.should raise_error(ArgumentError) + end + end + + %w(D d).each do |d| + it "parses the value as a decimal number if there's a leading 0#{d}" do + Integer("0#{d}1").should == 1 + Integer("0#{d}10").should == 10 + end + + it "is a positive decimal number if there's a leading +0#{d}" do + Integer("+0#{d}1").should == 1 + Integer("+0#{d}10").should == 10 + end + + it "is a negative decimal number if there's a leading -0#{d}" do + Integer("-0#{d}1").should == -1 + Integer("-0#{d}10").should == -10 + end + + it "raises an ArgumentError if the number cannot be parsed as decimal" do + lambda { Integer("0#{d}a") }.should raise_error(ArgumentError) + end + end +end + +describe "Integer() given a String and base", shared: true do + it "raises an ArgumentError if the String is a null byte" do + lambda { Integer("\0", 2) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String starts with a null byte" do + lambda { Integer("\01", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String ends with a null byte" do + lambda { Integer("1\0", 4) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String contains a null byte" do + lambda { Integer("1\01", 5) }.should raise_error(ArgumentError) + end + + it "ignores leading whitespace" do + Integer(" 16", 16).should == 22 + Integer(" 16", 16).should == 22 + Integer("\t\n16", 16).should == 22 + end + + it "ignores trailing whitespace" do + Integer("16 ", 16).should == 22 + Integer("16 ", 16).should == 22 + Integer("16\t\n", 16).should == 22 + end + + it "raises an ArgumentError if there are leading _s" do + lambda { Integer("_1", 7) }.should raise_error(ArgumentError) + lambda { Integer("___1", 7) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing _s" do + lambda { Integer("1_", 12) }.should raise_error(ArgumentError) + lambda { Integer("1___", 12) }.should raise_error(ArgumentError) + end + + it "ignores an embedded _" do + Integer("1_1", 4).should == 5 + end + + it "raises an ArgumentError if there are multiple embedded _s" do + lambda { Integer("1__1", 4) }.should raise_error(ArgumentError) + lambda { Integer("1___1", 4) }.should raise_error(ArgumentError) + end + + it "ignores a single leading +" do + Integer("+10", 3).should == 3 + end + + it "raises an ArgumentError if there is a space between the + and number" do + lambda { Integer("+ 1", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are multiple leading +s" do + lambda { Integer("++1", 3) }.should raise_error(ArgumentError) + lambda { Integer("+++1", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing +s" do + lambda { Integer("1+", 3) }.should raise_error(ArgumentError) + lambda { Integer("1+++", 12) }.should raise_error(ArgumentError) + end + + it "makes the number negative if there's a leading -" do + Integer("-19", 20).should == -29 + end + + it "raises an ArgumentError if there are multiple leading -s" do + lambda { Integer("--1", 9) }.should raise_error(ArgumentError) + lambda { Integer("---1", 9) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing -s" do + lambda { Integer("1-", 12) }.should raise_error(ArgumentError) + lambda { Integer("1---", 12) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there is a period" do + lambda { Integer("0.0", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for an empty String" do + lambda { Integer("", 12) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a base of 1" do + lambda { Integer("1", 1) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a base of 37" do + lambda { Integer("1", 37) }.should raise_error(ArgumentError) + end + + it "accepts wholly lowercase alphabetic strings for bases > 10" do + Integer('ab',12).should == 131 + Integer('af',20).should == 215 + Integer('ghj',30).should == 14929 + end + + it "accepts wholly uppercase alphabetic strings for bases > 10" do + Integer('AB',12).should == 131 + Integer('AF',20).should == 215 + Integer('GHJ',30).should == 14929 + end + + it "accepts mixed-case alphabetic strings for bases > 10" do + Integer('Ab',12).should == 131 + Integer('aF',20).should == 215 + Integer('GhJ',30).should == 14929 + end + + it "accepts alphanumeric strings for bases > 10" do + Integer('a3e',19).should == 3681 + Integer('12q',31).should == 1049 + Integer('c00o',29).should == 292692 + end + + it "raises an ArgumentError for letters invalid in the given base" do + lambda { Integer('z',19) }.should raise_error(ArgumentError) + lambda { Integer('c00o',2) }.should raise_error(ArgumentError) + end + + %w(x X).each do |x| + it "parses the value as a hex number if there's a leading 0#{x} and a base of 16" do + Integer("0#{x}10", 16).should == 16 + Integer("0#{x}dd", 16).should == 221 + end + + it "is a positive hex number if there's a leading +0#{x} and base of 16" do + Integer("+0#{x}1", 16).should == 0x1 + Integer("+0#{x}dd", 16).should == 0xdd + end + + it "is a negative hex number if there's a leading -0#{x} and a base of 16" do + Integer("-0#{x}1", 16).should == -0x1 + Integer("-0#{x}dd", 16).should == -0xdd + end + + 2.upto(15) do |base| + it "raises an ArgumentError if the number begins with 0#{x} and the base is #{base}" do + lambda { Integer("0#{x}1", base) }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if the number cannot be parsed as hex and the base is 16" do + lambda { Integer("0#{x}g", 16) }.should raise_error(ArgumentError) + end + end + + %w(b B).each do |b| + it "parses the value as a binary number if there's a leading 0#{b} and the base is 2" do + Integer("0#{b}1", 2).should == 0b1 + Integer("0#{b}10", 2).should == 0b10 + end + + it "is a positive binary number if there's a leading +0#{b} and a base of 2" do + Integer("+0#{b}1", 2).should == 0b1 + Integer("+0#{b}10", 2).should == 0b10 + end + + it "is a negative binary number if there's a leading -0#{b} and a base of 2" do + Integer("-0#{b}1", 2).should == -0b1 + Integer("-0#{b}10", 2).should == -0b10 + end + + it "raises an ArgumentError if the number cannot be parsed as binary and the base is 2" do + lambda { Integer("0#{b}2", 2) }.should raise_error(ArgumentError) + end + end + + ["o", "O"].each do |o| + it "parses the value as an octal number if there's a leading 0#{o} and a base of 8" do + Integer("0#{o}1", 8).should == 0O1 + Integer("0#{o}10", 8).should == 0O10 + end + + it "is a positive octal number if there's a leading +0#{o} and a base of 8" do + Integer("+0#{o}1", 8).should == 0O1 + Integer("+0#{o}10", 8).should == 0O10 + end + + it "is a negative octal number if there's a leading -0#{o} and a base of 8" do + Integer("-0#{o}1", 8).should == -0O1 + Integer("-0#{o}10", 8).should == -0O10 + end + + it "raises an ArgumentError if the number cannot be parsed as octal and the base is 8" do + lambda { Integer("0#{o}9", 8) }.should raise_error(ArgumentError) + end + + 2.upto(7) do |base| + it "raises an ArgumentError if the number begins with 0#{o} and the base is #{base}" do + lambda { Integer("0#{o}1", base) }.should raise_error(ArgumentError) + end + end + end + + %w(D d).each do |d| + it "parses the value as a decimal number if there's a leading 0#{d} and a base of 10" do + Integer("0#{d}1", 10).should == 1 + Integer("0#{d}10",10).should == 10 + end + + it "is a positive decimal number if there's a leading +0#{d} and a base of 10" do + Integer("+0#{d}1", 10).should == 1 + Integer("+0#{d}10", 10).should == 10 + end + + it "is a negative decimal number if there's a leading -0#{d} and a base of 10" do + Integer("-0#{d}1", 10).should == -1 + Integer("-0#{d}10", 10).should == -10 + end + + it "raises an ArgumentError if the number cannot be parsed as decimal and the base is 10" do + lambda { Integer("0#{d}a", 10) }.should raise_error(ArgumentError) + end + + 2.upto(9) do |base| + it "raises an ArgumentError if the number begins with 0#{d} and the base is #{base}" do + lambda { Integer("0#{d}1", base) }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if a base is given for a non-String value" do + lambda { Integer(98, 15) }.should raise_error(ArgumentError) + end + end +end + +describe :kernel_Integer, shared: true do + it "raises an ArgumentError when the String contains digits out of range of radix 2" do + str = "23456789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 2) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 3" do + str = "3456789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 4" do + str = "456789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 4) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 5" do + str = "56789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 5) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 6" do + str = "6789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 6) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 7" do + str = "789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 7) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 8" do + str = "89abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 8) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 9" do + str = "9abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 9) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 10" do + str = "abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 10) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 11" do + str = "bcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 11) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 12" do + str = "cdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 12) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 13" do + str = "defghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 13) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 14" do + str = "efghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 14) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 15" do + str = "fghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 15) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 16" do + str = "ghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 16) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 17" do + str = "hijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 17) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 18" do + str = "ijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 18) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 19" do + str = "jklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 19) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 20" do + str = "klmnopqrstuvwxyz" + lambda { @object.send(@method, str, 20) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 21" do + str = "lmnopqrstuvwxyz" + lambda { @object.send(@method, str, 21) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 22" do + str = "mnopqrstuvwxyz" + lambda { @object.send(@method, str, 22) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 23" do + str = "nopqrstuvwxyz" + lambda { @object.send(@method, str, 23) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 24" do + str = "opqrstuvwxyz" + lambda { @object.send(@method, str, 24) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 25" do + str = "pqrstuvwxyz" + lambda { @object.send(@method, str, 25) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 26" do + str = "qrstuvwxyz" + lambda { @object.send(@method, str, 26) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 27" do + str = "rstuvwxyz" + lambda { @object.send(@method, str, 27) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 28" do + str = "stuvwxyz" + lambda { @object.send(@method, str, 28) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 29" do + str = "tuvwxyz" + lambda { @object.send(@method, str, 29) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 30" do + str = "uvwxyz" + lambda { @object.send(@method, str, 30) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 31" do + str = "vwxyz" + lambda { @object.send(@method, str, 31) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 32" do + str = "wxyz" + lambda { @object.send(@method, str, 32) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 33" do + str = "xyz" + lambda { @object.send(@method, str, 33) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 34" do + str = "yz" + lambda { @object.send(@method, str, 34) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 35" do + str = "z" + lambda { @object.send(@method, str, 35) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 36" do + lambda { @object.send(@method, "{", 36) }.should raise_error(ArgumentError) + end +end + +describe "Kernel.Integer" do + it_behaves_like :kernel_Integer, :Integer_method, KernelSpecs + + # TODO: fix these specs + it_behaves_like :kernel_integer, :Integer, Kernel + it_behaves_like "Integer() given a String", :Integer + + it_behaves_like "Integer() given a String and base", :Integer + + it "is a public method" do + Kernel.Integer(10).should == 10 + end +end + +describe "Kernel#Integer" do + it_behaves_like :kernel_Integer, :Integer_function, KernelSpecs + + # TODO: fix these specs + it_behaves_like :kernel_integer, :Integer, Object.new + it_behaves_like "Integer() given a String", :Integer + + it_behaves_like "Integer() given a String and base", :Integer + + it "is a private method" do + Kernel.should have_private_instance_method(:Integer) + end +end diff --git a/spec/rubyspec/core/kernel/Rational_spec.rb b/spec/rubyspec/core/kernel/Rational_spec.rb new file mode 100644 index 0000000000..38f1da6333 --- /dev/null +++ b/spec/rubyspec/core/kernel/Rational_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/rational/Rational', __FILE__) + +describe "Kernel.Rational" do + it_behaves_like :kernel_Rational, :Rational +end diff --git a/spec/rubyspec/core/kernel/String_spec.rb b/spec/rubyspec/core/kernel/String_spec.rb new file mode 100644 index 0000000000..b24bc798e5 --- /dev/null +++ b/spec/rubyspec/core/kernel/String_spec.rb @@ -0,0 +1,106 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_String, shared: true do + it "converts nil to a String" do + @object.send(@method, nil).should == "" + end + + it "converts a Float to a String" do + @object.send(@method, 1.12).should == "1.12" + end + + it "converts a boolean to a String" do + @object.send(@method, false).should == "false" + @object.send(@method, true).should == "true" + end + + it "converts a constant to a String" do + @object.send(@method, Object).should == "Object" + end + + it "calls #to_s to convert an arbitrary object to a String" do + obj = mock('test') + obj.should_receive(:to_s).and_return("test") + + @object.send(@method, obj).should == "test" + end + + it "raises a TypeError if #to_s does not exist" do + obj = mock('to_s') + class << obj + undef_method :to_s + end + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + # #5158 + it "raises a TypeError if respond_to? returns false for #to_s" do + obj = mock("to_s") + class << obj + def respond_to?(meth, include_private=false) + meth == :to_s ? false : super + end + end + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_s is not defined, even though #respond_to?(:to_s) returns true" do + # cannot use a mock because of how RSpec affects #method_missing + obj = Object.new + class << obj + undef_method :to_s + def respond_to?(meth, include_private=false) + meth == :to_s ? true : super + end + end + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "calls #to_s if #respond_to?(:to_s) returns true" do + obj = mock('to_s') + class << obj + undef_method :to_s + def method_missing(meth, *args) + meth == :to_s ? "test" : super + end + end + + @object.send(@method, obj).should == "test" + end + + it "raises a TypeError if #to_s does not return a String" do + (obj = mock('123')).should_receive(:to_s).and_return(123) + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "returns the same object if it is already a String" do + string = "Hello" + string.should_not_receive(:to_s) + string2 = @object.send(@method, string) + string.should equal(string2) + end + + it "returns the same object if it is an instance of a String subclass" do + subklass = Class.new(String) + string = subklass.new("Hello") + string.should_not_receive(:to_s) + string2 = @object.send(@method, string) + string.should equal(string2) + end +end + +describe "Kernel.String" do + it_behaves_like :kernel_String, :String, Kernel +end + +describe "Kernel#String" do + it_behaves_like :kernel_String, :String, Object.new + + it "is a private method" do + Kernel.should have_private_instance_method(:String) + end +end diff --git a/spec/rubyspec/core/kernel/__callee___spec.rb b/spec/rubyspec/core/kernel/__callee___spec.rb new file mode 100644 index 0000000000..91cc4cdafa --- /dev/null +++ b/spec/rubyspec/core/kernel/__callee___spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/__callee__', __FILE__) + +describe "Kernel.__callee__" do + it "returns the current method, even when aliased" do + KernelSpecs::CalleeTest.new.f.should == :f + end + + it "returns the aliased name when aliased method" do + KernelSpecs::CalleeTest.new.g.should == :g + end + + it "returns the caller from blocks too" do + KernelSpecs::CalleeTest.new.in_block.should == [:in_block, :in_block] + end + + it "returns the caller from define_method too" do + KernelSpecs::CalleeTest.new.dm.should == :dm + end + + it "returns the caller from block inside define_method too" do + KernelSpecs::CalleeTest.new.dm_block.should == [:dm_block, :dm_block] + end + + it "returns method name even from send" do + KernelSpecs::CalleeTest.new.from_send.should == :from_send + end + + it "returns method name even from eval" do + KernelSpecs::CalleeTest.new.from_eval.should == :from_eval + end + + it "returns nil from inside a class body" do + KernelSpecs::CalleeTest.new.from_class_body.should == nil + end + + it "returns nil when not called from a method" do + __callee__.should == nil + end + + it "returns the caller from a define_method called from the same class" do + c = Class.new do + define_method(:f) { 1.times{ break __callee__ } } + def g; f end + end + c.new.g.should == :f + end +end diff --git a/spec/rubyspec/core/kernel/__dir___spec.rb b/spec/rubyspec/core/kernel/__dir___spec.rb new file mode 100644 index 0000000000..395d30f494 --- /dev/null +++ b/spec/rubyspec/core/kernel/__dir___spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#__dir__" do + it "returns the real name of the directory containing the currently-executing file" do + __dir__.should == File.realpath(File.dirname(__FILE__)) + end + + context "when used in eval with top level binding" do + it "returns the real name of the directory containing the currently-executing file" do + eval("__dir__", binding).should == File.realpath(File.dirname(__FILE__)) + end + end +end diff --git a/spec/rubyspec/core/kernel/__method___spec.rb b/spec/rubyspec/core/kernel/__method___spec.rb new file mode 100644 index 0000000000..936a6b2f00 --- /dev/null +++ b/spec/rubyspec/core/kernel/__method___spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/__method__', __FILE__) + +describe "Kernel.__method__" do + it "returns the current method, even when aliased" do + KernelSpecs::MethodTest.new.f.should == :f + end + + it "returns the original name when aliased method" do + KernelSpecs::MethodTest.new.g.should == :f + end + + it "returns the caller from blocks too" do + KernelSpecs::MethodTest.new.in_block.should == [:in_block, :in_block] + end + + it "returns the caller from define_method too" do + KernelSpecs::MethodTest.new.dm.should == :dm + end + + it "returns the caller from block inside define_method too" do + KernelSpecs::MethodTest.new.dm_block.should == [:dm_block, :dm_block] + end + + it "returns method name even from send" do + KernelSpecs::MethodTest.new.from_send.should == :from_send + end + + it "returns method name even from eval" do + KernelSpecs::MethodTest.new.from_eval.should == :from_eval + end + + it "returns nil from inside a class body" do + KernelSpecs::MethodTest.new.from_class_body.should == nil + end + + it "returns nil when not called from a method" do + __method__.should == nil + end +end diff --git a/spec/rubyspec/core/kernel/abort_spec.rb b/spec/rubyspec/core/kernel/abort_spec.rb new file mode 100644 index 0000000000..eb9c1c30c7 --- /dev/null +++ b/spec/rubyspec/core/kernel/abort_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/process/abort', __FILE__) + +describe "Kernel#abort" do + it "is a private method" do + Kernel.should have_private_instance_method(:abort) + end + + it_behaves_like :process_abort, :abort, KernelSpecs::Method.new +end + +describe "Kernel.abort" do + it_behaves_like :process_abort, :abort, Kernel +end diff --git a/spec/rubyspec/core/kernel/at_exit_spec.rb b/spec/rubyspec/core/kernel/at_exit_spec.rb new file mode 100644 index 0000000000..9fcb99148c --- /dev/null +++ b/spec/rubyspec/core/kernel/at_exit_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.at_exit" do + it "is a private method" do + Kernel.should have_private_instance_method(:at_exit) + end + + it "runs after all other code" do + ruby_exe("at_exit {print 5}; print 6").should == "65" + end + + it "runs in reverse order of registration" do + code = "at_exit {print 4};at_exit {print 5}; print 6; at_exit {print 7}" + ruby_exe(code).should == "6754" + end + + it "allows calling exit inside at_exit handler" do + code = "at_exit {print 3}; at_exit {print 4; exit; print 5}; at_exit {print 6}" + ruby_exe(code).should == "643" + end + + it "gives access to the last raised exception" do + code = <<-EOC + at_exit do + puts "The exception matches: \#{$! == $exception}" + end + + begin + raise "foo" + rescue => $exception + raise + end + EOC + + result = ruby_exe(code, args: "2>&1", escape: true) + result.should =~ /The exception matches: true/ + end + +end + +describe "Kernel#at_exit" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/autoload_spec.rb b/spec/rubyspec/core/kernel/autoload_spec.rb new file mode 100644 index 0000000000..082903e92d --- /dev/null +++ b/spec/rubyspec/core/kernel/autoload_spec.rb @@ -0,0 +1,122 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# These specs only illustrate the basic autoload cases +# and where toplevel autoload behaves differently from +# Module#autoload. See those specs for more examples. + +autoload :KSAutoloadA, "autoload_a.rb" +autoload :KSAutoloadB, fixture(__FILE__, "autoload_b.rb") +autoload :KSAutoloadC, fixture(__FILE__, "autoload_c.rb") + +def check_autoload(const) + autoload? const +end + +describe "Kernel#autoload" do + before :each do + @loaded_features = $".dup + end + + after :each do + $".replace @loaded_features + end + + it "is a private method" do + Kernel.should have_private_instance_method(:autoload) + end + + it "registers a file to load the first time the named constant is accessed" do + Object.autoload?(:KSAutoloadA).should == "autoload_a.rb" + end + + it "registers a file to load the first time the named constant is accessed" do + check_autoload(:KSAutoloadA).should == "autoload_a.rb" + end + + it "sets the autoload constant in Object's constant table" do + Object.should have_constant(:KSAutoloadA) + end + + it "loads the file when the constant is accessed" do + KSAutoloadB.loaded.should == :ksautoload_b + end + + it "does not call Kernel.require or Kernel.load to load the file" do + Kernel.should_not_receive(:require) + Kernel.should_not_receive(:load) + KSAutoloadC.loaded.should == :ksautoload_c + end + + it "can autoload in instance_eval" do + instance_eval do + # this instance_eval is not needed because specs are run in instance_eval + autoload :KSAutoloadD, fixture(__FILE__, "autoload_d.rb") + KSAutoloadD.loaded.should == :ksautoload_d + end + end + + describe "when Object is frozen" do + it "raises a RuntimeError before defining the constant" do + ruby_exe(fixture(__FILE__, "autoload_frozen.rb")).should == "RuntimeError - nil" + end + end +end + +describe "Kernel#autoload?" do + it "is a private method" do + Kernel.should have_private_instance_method(:autoload?) + end + + it "returns the name of the file that will be autoloaded" do + check_autoload(:KSAutoloadA).should == "autoload_a.rb" + end + + it "returns nil if no file has been registered for a constant" do + check_autoload(:Manualload).should be_nil + end +end + +Kernel.autoload :KSAutoloadBB, "no_autoload.rb" + +describe "Kernel.autoload" do + before :all do + @non_existent = fixture __FILE__, "no_autoload.rb" + end + + before :each do + @loaded_features = $".dup + + ScratchPad.clear + end + + after :each do + $".replace @loaded_features + end + + it "registers a file to load the first time the toplevel constant is accessed" do + Kernel.autoload :KSAutoloadAA, @non_existent + Kernel.autoload?(:KSAutoloadAA).should == @non_existent + end + + it "sets the autoload constant in Object's constant table" do + Object.should have_constant(:KSAutoloadBB) + end + + it "calls #to_path on non-String filenames" do + p = mock('path') + p.should_receive(:to_path).and_return @non_existent + Kernel.autoload :KSAutoloadAA, p + end +end + +describe "Kernel.autoload?" do + it "returns the name of the file that will be autoloaded" do + Kernel.autoload :KSAutoload, "autoload.rb" + Kernel.autoload?(:KSAutoload).should == "autoload.rb" + end + + it "returns nil if no file has been registered for a constant" do + Kernel.autoload?(:Manualload).should be_nil + end +end diff --git a/spec/rubyspec/core/kernel/backtick_spec.rb b/spec/rubyspec/core/kernel/backtick_spec.rb new file mode 100644 index 0000000000..eb25750bc2 --- /dev/null +++ b/spec/rubyspec/core/kernel/backtick_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#`" do + before :each do + @original_external = Encoding.default_external + end + + after :each do + Encoding.default_external = @original_external + end + + it "is a private method" do + Kernel.should have_private_instance_method(:`) + end + + it "returns the standard output of the executed sub-process" do + ip = 'world' + `echo disc #{ip}`.should == "disc world\n" + end + + it "lets the standard error stream pass through to the inherited stderr" do + cmd = ruby_cmd('STDERR.print "error stream"') + lambda { + `#{cmd}`.should == "" + }.should output_to_fd("error stream", STDERR) + end + + it "produces a String in the default external encoding" do + Encoding.default_external = Encoding::SHIFT_JIS + `echo disc`.encoding.should equal(Encoding::SHIFT_JIS) + end + + it "raises an Errno::ENOENT if the command is not executable" do + lambda { `nonexistent_command` }.should raise_error(Errno::ENOENT) + end + + platform_is_not :windows do + it "sets $? to the exit status of the executed sub-process" do + ip = 'world' + `echo disc #{ip}` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 0 + $?.success?.should == true + `echo disc #{ip}; exit 99` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 99 + $?.success?.should == false + end + end + + platform_is :windows do + it "sets $? to the exit status of the executed sub-process" do + ip = 'world' + `echo disc #{ip}` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 0 + $?.success?.should == true + `echo disc #{ip}& exit 99` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 99 + $?.success?.should == false + end + end +end + +describe "Kernel.`" do + it "tries to convert the given argument to String using #to_str" do + (obj = mock('echo test')).should_receive(:to_str).and_return("echo test") + Kernel.`(obj).should == "test\n" #` fix vim syntax highlighting + end +end diff --git a/spec/rubyspec/core/kernel/binding_spec.rb b/spec/rubyspec/core/kernel/binding_spec.rb new file mode 100644 index 0000000000..0b33c86d3c --- /dev/null +++ b/spec/rubyspec/core/kernel/binding_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.binding" do + it "returns a binding for the caller" do + Kernel.binding.eval("self").should == self + end +end + +describe "Kernel#binding" do + it "is a private method" do + Kernel.should have_private_instance_method(:binding) + end + + before :each do + @b1 = KernelSpecs::Binding.new(99).get_binding + ScratchPad.clear + end + + it "returns a Binding object" do + @b1.kind_of?(Binding).should == true + end + + it "encapsulates the execution context properly" do + eval("@secret", @b1).should == 100 + eval("a", @b1).should == true + eval("b", @b1).should == true + eval("@@super_secret", @b1).should == "password" + + eval("square(2)", @b1).should == 4 + eval("self.square(2)", @b1).should == 4 + + eval("a = false", @b1) + eval("a", @b1).should == false + end + + it "raises a NameError on undefined variable" do + lambda { eval("a_fake_variable", @b1) }.should raise_error(NameError) + end + + it "uses the closure's self as self in the binding" do + m = mock(:whatever) + eval('self', m.send(:binding)).should == self + end + + it "uses the class as self in a Class.new block" do + m = mock(:whatever) + cls = Class.new { ScratchPad.record eval('self', m.send(:binding)) } + ScratchPad.recorded.should == cls + end +end diff --git a/spec/rubyspec/core/kernel/block_given_spec.rb b/spec/rubyspec/core/kernel/block_given_spec.rb new file mode 100644 index 0000000000..9454c938ae --- /dev/null +++ b/spec/rubyspec/core/kernel/block_given_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_block_given, shared: true do + it "returns true if and only if a block is supplied" do + @object.accept_block {}.should == true + @object.accept_block_as_argument {}.should == true + + @object.accept_block.should == false + @object.accept_block_as_argument.should == false + end + + # Clarify: Based on http://www.ruby-forum.com/topic/137822 it appears + # that Matz wanted this to be true in 1.9. + it "returns false when a method defined by define_method is called with a block" do + @object.defined_block {}.should == false + end +end + +describe "Kernel#block_given?" do + it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::BlockGiven + + it "returns false outside of a method" do + block_given?.should == false + end + + it "is a private method" do + Kernel.should have_private_instance_method(:block_given?) + end +end + +describe "Kernel.block_given?" do + it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::KernelBlockGiven +end + +describe "self.send(:block_given?)" do + it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::SelfBlockGiven +end diff --git a/spec/rubyspec/core/kernel/caller_locations_spec.rb b/spec/rubyspec/core/kernel/caller_locations_spec.rb new file mode 100644 index 0000000000..69993c3ec0 --- /dev/null +++ b/spec/rubyspec/core/kernel/caller_locations_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/caller_locations', __FILE__) + +describe 'Kernel#caller_locations' do + it 'is a private method' do + Kernel.should have_private_instance_method(:caller_locations) + end + + it 'returns an Array of caller locations' do + KernelSpecs::CallerLocationsTest.locations.empty?.should == false + end + + it 'returns an Array of caller locations using a custom offset' do + locations = KernelSpecs::CallerLocationsTest.locations(2) + + locations[0].absolute_path.end_with?('mspec.rb').should == true + end + + it 'returns an Array of caller locations using a custom limit' do + locations = KernelSpecs::CallerLocationsTest.locations(1, 1) + + locations.length.should == 1 + end + + it 'returns the locations as Thread::Backtrace::Location instances' do + locations = KernelSpecs::CallerLocationsTest.locations + + locations.each do |location| + location.kind_of?(Thread::Backtrace::Location).should == true + end + end +end diff --git a/spec/rubyspec/core/kernel/caller_spec.rb b/spec/rubyspec/core/kernel/caller_spec.rb new file mode 100644 index 0000000000..94fbe3ab7e --- /dev/null +++ b/spec/rubyspec/core/kernel/caller_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/caller', __FILE__) + +describe 'Kernel#caller' do + it 'is a private method' do + Kernel.should have_private_instance_method(:caller) + end + + it 'returns an Array of caller locations' do + KernelSpecs::CallerTest.locations.empty?.should == false + end + + it 'returns an Array of caller locations using a custom offset' do + locations = KernelSpecs::CallerTest.locations(2) + + locations[0].should =~ %r{runner/mspec.rb} + end + + it 'returns an Array of caller locations using a custom limit' do + locations = KernelSpecs::CallerTest.locations(1, 1) + + locations.length.should == 1 + end + + it 'returns the locations as String instances' do + locations = KernelSpecs::CallerTest.locations + line = __LINE__ - 1 + + locations[0].should include("#{__FILE__}:#{line}:in") + end +end diff --git a/spec/rubyspec/core/kernel/case_compare_spec.rb b/spec/rubyspec/core/kernel/case_compare_spec.rb new file mode 100644 index 0000000000..5332aa7647 --- /dev/null +++ b/spec/rubyspec/core/kernel/case_compare_spec.rb @@ -0,0 +1,135 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + + +module Specs + module Kernel + + class HasNone + end + + class HasOpEqual + def ==(other) + other.kind_of? HasOpEqual + end + end + + class HasEqual + def equal?(other) + false + end + end + + class HasOppoOpEqual + def ==(other) + false + end + + def equal?(other) + false + end + end + end +end + + +describe "Kernel#=== for a class with default #== and #equal?" do + before :each do + @o1 = Specs::Kernel::HasNone.new + @o2 = @o1.dup + end + + it "returns true if other object has same object id" do + @o1.object_id.should == @o1.object_id + (@o1 === @o1).should == true + end + + it "returns false if other object does not have same object id" do + @o1.object_id.should_not == @o2.object_id + (@o1 === @o2).should == false + end +end + +describe "Kernel#=== for a class with #== overridden to consider other object's class" do + before :each do + @o = Object.new + @o1 = Specs::Kernel::HasOpEqual.new + @o2 = @o1.dup + end + + it "returns true if #== returns true even if #equal? is false" do + @o1.should_not equal(@o2) + (@o1 == @o2).should == true + (@o1 === @o2).should == true + end + + it "returns true if #equal? returns true" do + @o1.should equal(@o1) + (@o1 === @o1).should == true + end + + it "returns false if neither #== nor #equal? returns true" do + @o1.should_not equal(@o) + (@o1 == @o).should == false + (@o1 === @o).should == false + end +end + +describe "Kernel#=== for a class with #equal? overridden to always be false" do + before :each do + @o = Object.new + @o1 = Specs::Kernel::HasEqual.new + @o2 = @o1.dup + end + + it "returns true if #== returns true even if #equal? is false" do + @o1.should_not equal(@o1) + (@o1 == @o1).should == true + (@o1 === @o1).should == true + end + + it "returns false if neither #== nor #equal? returns true" do + @o1.should_not equal(@o) + (@o1 == @o).should == false + (@o1 === @o).should == false + end +end + +describe "Kernel#=== for a class with #== and #equal? overridden to always be false" do + before :each do + @o = Object.new + @o1 = Specs::Kernel::HasOppoOpEqual.new + @o2 = @o1.dup + end + + it "returns true if the object id is the same even if both #== and #equal? return false" do + @o1.object_id.should == @o1.object_id + + @o1.should_not equal(@o1) + (@o1 == @o1).should == false + + (@o1 === @o1).should == true + end + + it "returns false if the object id is not the same and both #== and #equal? return false" do + @o1.object_id.should_not == @o2.object_id + + @o1.should_not equal(@o2) + (@o1 == @o2).should == false + + (@o1 === @o2).should == false + end +end + +describe "Kernel#=== does not call #object_id nor #equal?" do + before :each do + @o1 = Object.new + @o1.should_not_receive(:object_id) + @o1.should_not_receive(:equal?) + end + + it "but still returns true for #== or #=== on the same object" do + (@o1 == @o1).should == true + (@o1 === @o1).should == true + end +end diff --git a/spec/rubyspec/core/kernel/catch_spec.rb b/spec/rubyspec/core/kernel/catch_spec.rb new file mode 100644 index 0000000000..35a4860f38 --- /dev/null +++ b/spec/rubyspec/core/kernel/catch_spec.rb @@ -0,0 +1,127 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.catch" do + before :each do + ScratchPad.clear + end + + it "executes its block and catches a thrown value matching its argument" do + catch :thrown_key do + ScratchPad.record :catch_block + throw :thrown_key + ScratchPad.record :throw_failed + end + ScratchPad.recorded.should == :catch_block + end + + it "returns the second value passed to throw" do + catch(:thrown_key) { throw :thrown_key, :catch_value }.should == :catch_value + end + + it "returns the last expression evaluated if throw was not called" do + catch(:thrown_key) { 1; :catch_block }.should == :catch_block + end + + it "passes the given symbol to its block" do + catch :thrown_key do |tag| + ScratchPad.record tag + end + ScratchPad.recorded.should == :thrown_key + end + + it "raises an ArgumentError if a Symbol is thrown for a String catch value" do + lambda { catch("exit") { throw :exit } }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if a String with different identity is thrown" do + lambda { catch("exit") { throw "exit" } }.should raise_error(ArgumentError) + end + + it "catches a Symbol when thrown a matching Symbol" do + catch :thrown_key do + ScratchPad.record :catch_block + throw :thrown_key + end + ScratchPad.recorded.should == :catch_block + end + + it "catches a String when thrown a String with the same identity" do + key = "thrown_key" + catch key do + ScratchPad.record :catch_block + throw key + end + ScratchPad.recorded.should == :catch_block + end + + it "accepts an object as an argument" do + catch(Object.new) { :catch_block }.should == :catch_block + end + + it "yields an object when called without arguments" do + catch { |tag| tag }.should be_an_instance_of(Object) + end + + it "can be used even in a method different from where throw is called" do + class CatchSpecs + def self.throwing_method + throw :blah, :thrown_value + end + def self.catching_method + catch :blah do + throwing_method + end + end + end + CatchSpecs.catching_method.should == :thrown_value + end + + describe "when nested" do + before :each do + ScratchPad.record [] + end + + it "catches across invocation boundaries" do + catch :one do + ScratchPad << 1 + catch :two do + ScratchPad << 2 + catch :three do + ScratchPad << 3 + throw :one + ScratchPad << 4 + end + ScratchPad << 5 + end + ScratchPad << 6 + end + + ScratchPad.recorded.should == [1, 2, 3] + end + + it "catches in the nested invocation with the same key object" do + catch :thrown_key do + ScratchPad << 1 + catch :thrown_key do + ScratchPad << 2 + throw :thrown_key + ScratchPad << 3 + end + ScratchPad << 4 + end + + ScratchPad.recorded.should == [1, 2, 4] + end + end + + it "raises LocalJumpError if no block is given" do + lambda { catch :blah }.should raise_error(LocalJumpError) + end +end + +describe "Kernel#catch" do + it "is a private method" do + Kernel.should have_private_instance_method(:catch) + end +end diff --git a/spec/rubyspec/core/kernel/chomp_spec.rb b/spec/rubyspec/core/kernel/chomp_spec.rb new file mode 100644 index 0000000000..4b34784169 --- /dev/null +++ b/spec/rubyspec/core/kernel/chomp_spec.rb @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_chomp, shared: true do + it "removes the final newline of $_" do + KernelSpecs.chomp("abc\n", @method).should == "abc" + end + + it "removes the final carriage return of $_" do + KernelSpecs.chomp("abc\r", @method).should == "abc" + end + + it "removes the final carriage return, newline of $_" do + KernelSpecs.chomp("abc\r\n", @method).should == "abc" + end + + it "removes only the final newline of $_" do + KernelSpecs.chomp("abc\n\n", @method).should == "abc\n" + end + + it "removes the value of $/ from the end of $_" do + KernelSpecs.chomp("abcde", @method, "cde").should == "ab" + end +end + +describe :kernel_chomp_private, shared: true do + it "is a private method" do + KernelSpecs.has_private_method(@method).should be_true + end +end + +describe "Kernel.chomp" do + it_behaves_like :kernel_chomp, "Kernel.chomp" +end + +describe "Kernel#chomp" do + it_behaves_like :kernel_chomp, "chomp" + + it_behaves_like :kernel_chomp_private, :chomp +end + +with_feature :encoding do + describe :kernel_chomp_encoded, shared: true do + before :each do + @external = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + end + + after :each do + Encoding.default_external = @external + end + + it "removes the final carriage return, newline from a multi-byte $_" do + script = fixture __FILE__, "#{@method}.rb" + KernelSpecs.encoded_chomp(script).should == "あれ" + end + end + + describe "Kernel.chomp" do + it_behaves_like :kernel_chomp_encoded, "chomp" + end + + describe "Kernel#chomp" do + it_behaves_like :kernel_chomp_encoded, "chomp_f" + end +end diff --git a/spec/rubyspec/core/kernel/chop_spec.rb b/spec/rubyspec/core/kernel/chop_spec.rb new file mode 100644 index 0000000000..53b6e47cd1 --- /dev/null +++ b/spec/rubyspec/core/kernel/chop_spec.rb @@ -0,0 +1,55 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_chop, shared: true do + it "removes the final character of $_" do + KernelSpecs.chop("abc", @method).should == "ab" + end + + it "removes the final carriage return, newline of $_" do + KernelSpecs.chop("abc\r\n", @method).should == "abc" + end +end + +describe :kernel_chop_private, shared: true do + it "is a private method" do + KernelSpecs.has_private_method(@method).should be_true + end +end + +describe "Kernel.chop" do + it_behaves_like :kernel_chop, "Kernel.chop" +end + +describe "Kernel#chop" do + it_behaves_like :kernel_chop_private, :chop + + it_behaves_like :kernel_chop, "chop" +end + +with_feature :encoding do + describe :kernel_chop_encoded, shared: true do + before :each do + @external = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + end + + after :each do + Encoding.default_external = @external + end + + it "removes the final multi-byte character from $_" do + script = fixture __FILE__, "#{@method}.rb" + KernelSpecs.encoded_chop(script).should == "あ" + end + end + + describe "Kernel.chop" do + it_behaves_like :kernel_chop_encoded, "chop" + end + + describe "Kernel#chop" do + it_behaves_like :kernel_chop_encoded, "chop_f" + end +end diff --git a/spec/rubyspec/core/kernel/class_spec.rb b/spec/rubyspec/core/kernel/class_spec.rb new file mode 100644 index 0000000000..0d7b40c366 --- /dev/null +++ b/spec/rubyspec/core/kernel/class_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#class" do + it "returns the class of the object" do + Object.new.class.should equal(Object) + + 1.class.should equal(Fixnum) + 3.14.class.should equal(Float) + :hello.class.should equal(Symbol) + "hello".class.should equal(String) + [1, 2].class.should equal(Array) + { 1 => 2 }.class.should equal(Hash) + end + + it "returns Class for a class" do + BasicObject.class.should equal(Class) + String.class.should equal(Class) + end + + it "returns the first non-singleton class" do + a = "hello" + def a.my_singleton_method; end + a.class.should equal(String) + end +end diff --git a/spec/rubyspec/core/kernel/clone_spec.rb b/spec/rubyspec/core/kernel/clone_spec.rb new file mode 100644 index 0000000000..0e8216d434 --- /dev/null +++ b/spec/rubyspec/core/kernel/clone_spec.rb @@ -0,0 +1,108 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/dup_clone', __FILE__) + +describe "Kernel#clone" do + it_behaves_like :kernel_dup_clone, :clone + + before :each do + ScratchPad.clear + @obj = KernelSpecs::Duplicate.new 1, :a + end + + it "calls #initialize_copy on the new instance" do + clone = @obj.clone + ScratchPad.recorded.should_not == @obj.object_id + ScratchPad.recorded.should == clone.object_id + end + + it "uses the internal allocator and does not call #allocate" do + klass = Class.new + instance = klass.new + + def klass.allocate + raise "allocate should not be called" + end + + clone = instance.clone + clone.class.should equal klass + end + + it "copies frozen state from the original" do + o2 = @obj.clone + @obj.freeze + o3 = @obj.clone + + o2.frozen?.should == false + o3.frozen?.should == true + end + + it "copies instance variables" do + clone = @obj.clone + clone.one.should == 1 + clone.two.should == :a + end + + it "copies singleton methods" do + def @obj.special() :the_one end + clone = @obj.clone + clone.special.should == :the_one + end + + it "copies modules included in the singleton class" do + class << @obj + include KernelSpecs::DuplicateM + end + + clone = @obj.clone + clone.repr.should == "KernelSpecs::Duplicate" + end + + it "copies constants defined in the singleton class" do + class << @obj + CLONE = :clone + end + + clone = @obj.clone + class << clone + CLONE.should == :clone + end + end + + it "replaces a singleton object's metaclass with a new copy with the same superclass" do + cls = Class.new do + def bar + ['a'] + end + end + + object = cls.new + object.define_singleton_method(:bar) do + ['b', *super()] + end + object.bar.should == ['b', 'a'] + + cloned = object.clone + + cloned.singleton_methods.should == [:bar] + + # bar should replace previous one + cloned.define_singleton_method(:bar) do + ['c', *super()] + end + cloned.bar.should == ['c', 'a'] + + # bar should be removed and call through to superclass + cloned.singleton_class.class_eval do + remove_method :bar + end + + cloned.bar.should == ['a'] + end + + it 'copies frozen? and tainted?' do + o = ''.taint.freeze.clone + o.frozen?.should be_true + o.tainted?.should be_true + end +end diff --git a/spec/rubyspec/core/kernel/comparison_spec.rb b/spec/rubyspec/core/kernel/comparison_spec.rb new file mode 100644 index 0000000000..2aa4358600 --- /dev/null +++ b/spec/rubyspec/core/kernel/comparison_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#<=>" do + it "returns 0 if self" do + obj = Object.new + obj.<=>(obj).should == 0 + end + + it "returns 0 if self is == to the argument" do + obj = mock('has ==') + obj.should_receive(:==).and_return(true) + obj.<=>(Object.new).should == 0 + end + + it "returns nil if self is eql? but not == to the argument" do + obj = mock('has eql?') + obj.should_not_receive(:eql?) + obj.<=>(Object.new).should be_nil + end + + it "returns nil if self.==(arg) returns nil" do + obj = mock('wrong ==') + obj.should_receive(:==).and_return(nil) + obj.<=>(Object.new).should be_nil + end + + it "returns nil if self is not == to the argument" do + obj = Object.new + obj.<=>(3.14).should be_nil + end +end diff --git a/spec/rubyspec/core/kernel/define_singleton_method_spec.rb b/spec/rubyspec/core/kernel/define_singleton_method_spec.rb new file mode 100644 index 0000000000..de6f7fc286 --- /dev/null +++ b/spec/rubyspec/core/kernel/define_singleton_method_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#define_singleton_method" do + describe "when given an UnboundMethod" do + class DefineSingletonMethodSpecClass + MY_CONST = 42 + define_singleton_method(:another_test_method, self.method(:constants)) + end + + it "correctly calls the new method" do + klass = DefineSingletonMethodSpecClass + klass.another_test_method.should == klass.constants + end + + it "adds the new method to the methods list" do + DefineSingletonMethodSpecClass.should have_method(:another_test_method) + end + + it "defines any Child class method from any Parent's class methods" do + um = KernelSpecs::Parent.method(:parent_class_method).unbind + KernelSpecs::Child.send :define_singleton_method, :child_class_method, um + KernelSpecs::Child.child_class_method.should == :foo + lambda{KernelSpecs::Parent.child_class_method}.should raise_error(NoMethodError) + end + + it "will raise when attempting to define an object's singleton method from another object's singleton method" do + other = KernelSpecs::Parent.new + p = KernelSpecs::Parent.new + class << p + def singleton_method + :single + end + end + um = p.method(:singleton_method).unbind + lambda{ other.send :define_singleton_method, :other_singleton_method, um }.should raise_error(TypeError) + end + + end + + it "defines a new method with the given name and the given block as body in self" do + class DefineSingletonMethodSpecClass + define_singleton_method(:block_test1) { self } + define_singleton_method(:block_test2, &lambda { self }) + end + + o = DefineSingletonMethodSpecClass + o.block_test1.should == o + o.block_test2.should == o + end + + it "raises a TypeError when the given method is no Method/Proc" do + lambda { + Class.new { define_singleton_method(:test, "self") } + }.should raise_error(TypeError) + + lambda { + Class.new { define_singleton_method(:test, 1234) } + }.should raise_error(TypeError) + end + + it "defines a new singleton method for objects" do + obj = Object.new + obj.define_singleton_method(:test) { "world!" } + obj.test.should == "world!" + lambda { + Object.new.test + }.should raise_error(NoMethodError) + end + + it "maintains the Proc's scope" do + class DefineMethodByProcClass + in_scope = true + method_proc = proc { in_scope } + + define_singleton_method(:proc_test, &method_proc) + end + + DefineMethodByProcClass.proc_test.should == true + end + + it "raises an ArgumentError when no block is given" do + obj = Object.new + lambda { + obj.define_singleton_method(:test) + }.should raise_error(ArgumentError) + end + + ruby_version_is "2.3" do + it "does not use the caller block when no block is given" do + o = Object.new + def o.define(name) + define_singleton_method(name) + end + + lambda { + o.define(:foo) { raise "not used" } + }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/kernel/display_spec.rb b/spec/rubyspec/core/kernel/display_spec.rb new file mode 100644 index 0000000000..e771e14cdb --- /dev/null +++ b/spec/rubyspec/core/kernel/display_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#display" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/dup_spec.rb b/spec/rubyspec/core/kernel/dup_spec.rb new file mode 100644 index 0000000000..af7e924a66 --- /dev/null +++ b/spec/rubyspec/core/kernel/dup_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/dup_clone', __FILE__) + +describe "Kernel#dup" do + it_behaves_like :kernel_dup_clone, :dup + + before :each do + ScratchPad.clear + @obj = KernelSpecs::Duplicate.new 1, :a + end + + it "calls #initialize_copy on the new instance" do + dup = @obj.dup + ScratchPad.recorded.should_not == @obj.object_id + ScratchPad.recorded.should == dup.object_id + end + + it "uses the internal allocator and does not call #allocate" do + klass = Class.new + instance = klass.new + + def klass.allocate + raise "allocate should not be called" + end + + dup = instance.dup + dup.class.should equal klass + end + + it "does not copy frozen state from the original" do + @obj.freeze + dup = @obj.dup + + dup.frozen?.should == false + end + + it "copies instance variables" do + dup = @obj.dup + dup.one.should == 1 + dup.two.should == :a + end + + it "does not copy singleton methods" do + def @obj.special() :the_one end + dup = @obj.dup + lambda { dup.special }.should raise_error(NameError) + end + + it "does not copy modules included in the singleton class" do + class << @obj + include KernelSpecs::DuplicateM + end + + dup = @obj.dup + lambda { dup.repr }.should raise_error(NameError) + end + + it "does not copy constants defined in the singleton class" do + class << @obj + CLONE = :clone + end + + dup = @obj.dup + lambda { class << dup; CLONE; end }.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/kernel/enum_for_spec.rb b/spec/rubyspec/core/kernel/enum_for_spec.rb new file mode 100644 index 0000000000..819140e0e4 --- /dev/null +++ b/spec/rubyspec/core/kernel/enum_for_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#enum_for" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/eql_spec.rb b/spec/rubyspec/core/kernel/eql_spec.rb new file mode 100644 index 0000000000..39c9fea7eb --- /dev/null +++ b/spec/rubyspec/core/kernel/eql_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/kernel/equal', __FILE__) + +describe "Kernel#eql?" do + it "is a public instance method" do + Kernel.should have_public_instance_method(:eql?) + end + + it_behaves_like :object_equal, :eql? +end + diff --git a/spec/rubyspec/core/kernel/equal_value_spec.rb b/spec/rubyspec/core/kernel/equal_value_spec.rb new file mode 100644 index 0000000000..670987ead2 --- /dev/null +++ b/spec/rubyspec/core/kernel/equal_value_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#==" do + it "returns true only if obj and other are the same object" do + o1 = mock('o1') + o2 = mock('o2') + (o1 == o1).should == true + (o2 == o2).should == true + (o1 == o2).should== false + (nil == nil).should == true + (o1 == nil).should== false + (nil == o2).should== false + end +end diff --git a/spec/rubyspec/core/kernel/eval_spec.rb b/spec/rubyspec/core/kernel/eval_spec.rb new file mode 100644 index 0000000000..96fc0f5b71 --- /dev/null +++ b/spec/rubyspec/core/kernel/eval_spec.rb @@ -0,0 +1,216 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +EvalSpecs::A.new.c + +describe "Kernel#eval" do + it "is a private method" do + Kernel.should have_private_instance_method(:eval) + end + + it "is a module function" do + Kernel.respond_to?(:eval).should == true + end + + it "evaluates the code within" do + eval("2 + 3").should == 5 + end + + it "coerces an object to string" do + eval(EvalSpecs::CoercedObject.new).should == 5 + end + + it "evaluates within the scope of the eval" do + EvalSpecs::A::B.name.should == "EvalSpecs::A::B" + end + + it "evaluates such that consts are scoped to the class of the eval" do + EvalSpecs::A::C.name.should == "EvalSpecs::A::C" + end + + it "finds a local in an enclosing scope" do + a = 1 + eval("a").should == 1 + end + + it "updates a local in an enclosing scope" do + a = 1 + eval("a = 2") + a.should == 2 + end + + it "updates a local in a surrounding block scope" do + EvalSpecs.new.f do + a = 1 + eval("a = 2") + a.should == 2 + end + end + + it "updates a local in a scope above a surrounding block scope" do + a = 1 + EvalSpecs.new.f do + eval("a = 2") + a.should == 2 + end + a.should == 2 + end + + it "updates a local in a scope above when modified in a nested block scope" do + a = 1 + es = EvalSpecs.new + eval("es.f { es.f { a = 2 } }") + a.should == 2 + end + + it "finds locals in a nested eval" do + eval('test = 10; eval("test")').should == 10 + end + + it "does not share locals across eval scopes" do + code = fixture __FILE__, "eval_locals.rb" + ruby_exe(code).chomp.should == "NameError" + end + + it "doesn't accept a Proc object as a binding" do + x = 1 + bind = proc {} + + lambda { eval("x", bind) }.should raise_error(TypeError) + end + + it "does not make Proc locals visible to evaluated code" do + bind = proc { inner = 4 } + lambda { eval("inner", bind.binding) }.should raise_error(NameError) + end + + # REWRITE ME: This obscures the real behavior of where locals are stored + # in eval bindings. + it "allows a binding to be captured inside an eval" do + outer_binding = binding + level1 = eval("binding", outer_binding) + level2 = eval("binding", level1) + + eval("x = 2", outer_binding) + eval("y = 3", level1) + + eval("w=1", outer_binding) + eval("w", outer_binding).should == 1 + eval("w=1", level1).should == 1 + eval("w", level1).should == 1 + eval("w=1", level2).should == 1 + eval("w", level2).should == 1 + + eval("x", outer_binding).should == 2 + eval("x=2", level1) + eval("x", level1).should == 2 + eval("x=2", level2) + eval("x", level2).should == 2 + + eval("y=3", outer_binding) + eval("y", outer_binding).should == 3 + eval("y=3", level1) + eval("y", level1).should == 3 + eval("y=3", level2) + eval("y", level2).should == 3 + end + + it "uses the same scope for local variables when given the same binding" do + outer_binding = binding + + eval("if false; a = 1; end", outer_binding) + eval("a", outer_binding).should be_nil + end + + it "allows creating a new class in a binding" do + bind = proc {} + eval("class EvalBindingProcA; end; EvalBindingProcA.name", bind.binding).should =~ /EvalBindingProcA$/ + end + + it "allows creating a new class in a binding created by #eval" do + bind = eval "binding" + eval("class EvalBindingA; end; EvalBindingA.name", bind).should =~ /EvalBindingA$/ + end + + it "includes file and line information in syntax error" do + expected = 'speccing.rb' + lambda { + eval('if true',TOPLEVEL_BINDING, expected) + }.should raise_error(SyntaxError) { |e| + e.message.should =~ /#{expected}:1:.+/ + } + end + + it "evaluates string with given filename and negative linenumber" do + expected_file = 'speccing.rb' + lambda { + eval('if true',TOPLEVEL_BINDING, expected_file, -100) + }.should raise_error(SyntaxError) { |e| + e.message.should =~ /#{expected_file}:-100:.+/ + } + end + + it "sets constants at the toplevel from inside a block" do + # The class Object bit is needed to workaround some mspec oddness + class Object + [1].each { eval "Const = 1"} + Const.should == 1 + remove_const :Const + end + end + + it "uses the filename of the binding if none is provided" do + eval("__FILE__").should == "(eval)" + eval("__FILE__", binding).should == __FILE__ + eval("__FILE__", binding, "success").should == "success" + eval("eval '__FILE__', binding").should == "(eval)" + eval("eval '__FILE__', binding", binding).should == __FILE__ + eval("eval '__FILE__', binding", binding, 'success').should == 'success' + end + + # Found via Rubinius bug github:#149 + it "does not alter the value of __FILE__ in the binding" do + first_time = EvalSpecs.call_eval + second_time = EvalSpecs.call_eval + + # This bug is seen by calling the method twice and comparing the values + # of __FILE__ each time. If the bug is present, calling eval will set the + # value of __FILE__ to the eval's "filename" argument. + + second_time.should_not == "(eval)" + first_time.should == second_time + end + + it "can be aliased" do + alias aliased_eval eval + x = 2 + aliased_eval('x += 40') + x.should == 42 + end + + # See http://jira.codehaus.org/browse/JRUBY-5163 + it "uses the receiver as self inside the eval" do + eval("self").should equal(self) + Kernel.eval("self").should equal(Kernel) + end + + it "does not pass the block to the method being eval'ed" do + lambda { + eval('KernelSpecs::EvalTest.call_yield') { "content" } + }.should raise_error(LocalJumpError) + end + + it "returns from the scope calling #eval when evaluating 'return'" do + lambda { eval("return :eval") }.call.should == :eval + end + + it "unwinds through a Proc-style closure and returns from a lambda-style closure in the closure chain" do + code = fixture __FILE__, "eval_return_with_lambda.rb" + ruby_exe(code).chomp.should == "a,b,c,eval,f" + end + + it "raises a LocalJumpError if there is no lambda-style closure in the chain" do + code = fixture __FILE__, "eval_return_without_lambda.rb" + ruby_exe(code).chomp.should == "a,b,c,e,LocalJumpError,f" + end +end diff --git a/spec/rubyspec/core/kernel/exec_spec.rb b/spec/rubyspec/core/kernel/exec_spec.rb new file mode 100644 index 0000000000..3a7b656914 --- /dev/null +++ b/spec/rubyspec/core/kernel/exec_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#exec" do + it "is a private method" do + Kernel.should have_private_instance_method(:exec) + end + + it "runs the specified command, replacing current process" do + ruby_exe('exec "echo hello"; puts "fail"', escape: true).should == "hello\n" + end +end + +describe "Kernel.exec" do + it "runs the specified command, replacing current process" do + ruby_exe('Kernel.exec "echo hello"; puts "fail"', escape: true).should == "hello\n" + end +end diff --git a/spec/rubyspec/core/kernel/exit_spec.rb b/spec/rubyspec/core/kernel/exit_spec.rb new file mode 100644 index 0000000000..5e175d5036 --- /dev/null +++ b/spec/rubyspec/core/kernel/exit_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/process/exit', __FILE__) + +describe "Kernel#exit" do + it "is a private method" do + Kernel.should have_private_instance_method(:exit) + end + + it_behaves_like :process_exit, :exit, KernelSpecs::Method.new +end + +describe "Kernel#exit!" do + it "is a private method" do + Kernel.should have_private_instance_method(:exit!) + end + + it_behaves_like :process_exit!, :exit!, KernelSpecs::Method.new +end + +describe "Kernel.exit" do + it_behaves_like :process_exit, :exit, Kernel +end + +describe "Kernel.exit!" do + it_behaves_like :process_exit!, :exit!, Kernel +end diff --git a/spec/rubyspec/core/kernel/extend_spec.rb b/spec/rubyspec/core/kernel/extend_spec.rb new file mode 100644 index 0000000000..482eef32e9 --- /dev/null +++ b/spec/rubyspec/core/kernel/extend_spec.rb @@ -0,0 +1,79 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +module KernelSpecs::M + def self.extend_object(o) + ScratchPad << "extend_object" + super + end + + def self.extended(o) + ScratchPad << "extended" + super + end + + def self.append_features(o) + ScratchPad << "append_features" + super + end +end + +describe "Kernel#extend" do + before :each do + ScratchPad.record [] + end + + it "requires multiple arguments" do + Object.new.method(:extend).arity.should < 0 + end + + it "calls extend_object on argument" do + o = mock('o') + o.extend KernelSpecs::M + ScratchPad.recorded.include?("extend_object").should == true + end + + it "does not calls append_features on arguments metaclass" do + o = mock('o') + o.extend KernelSpecs::M + ScratchPad.recorded.include?("append_features").should == false + end + + it "calls extended on argument" do + o = mock('o') + o.extend KernelSpecs::M + ScratchPad.recorded.include?("extended").should == true + end + + it "makes the class a kind_of? the argument" do + class C + extend KernelSpecs::M + end + (C.kind_of? KernelSpecs::M).should == true + end + + it "raises an ArgumentError when no arguments given" do + lambda { Object.new.extend }.should raise_error(ArgumentError) + end + + it "raises a TypeError when the argument is not a Module" do + o = mock('o') + klass = Class.new + lambda { o.extend(klass) }.should raise_error(TypeError) + end + + describe "on frozen instance" do + before :each do + @frozen = Object.new.freeze + @module = KernelSpecs::M + end + + it "raises an ArgumentError when no arguments given" do + lambda { @frozen.extend }.should raise_error(ArgumentError) + end + + it "raises a RuntimeError" do + lambda { @frozen.extend @module }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/kernel/fail_spec.rb b/spec/rubyspec/core/kernel/fail_spec.rb new file mode 100644 index 0000000000..9f3601c233 --- /dev/null +++ b/spec/rubyspec/core/kernel/fail_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.fail" do + it "is a private method" do + Kernel.should have_private_instance_method(:fail) + end + + it "raises a RuntimeError" do + lambda { fail }.should raise_error(RuntimeError) + end + + it "accepts an Object with an exception method returning an Exception" do + class Boring + def self.exception(msg) + StandardError.new msg + end + end + lambda { fail Boring, "..." }.should raise_error(StandardError) + end + + it "instantiates the specified exception class" do + class LittleBunnyFooFoo < RuntimeError; end + lambda { fail LittleBunnyFooFoo }.should raise_error(LittleBunnyFooFoo) + end + + it "uses the specified message" do + lambda { + begin + fail "the duck is not irish." + rescue => e + e.message.should == "the duck is not irish." + raise + else + raise Exception + end + }.should raise_error(RuntimeError) + end +end + +describe "Kernel#fail" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/fixtures/__callee__.rb b/spec/rubyspec/core/kernel/fixtures/__callee__.rb new file mode 100644 index 0000000000..7138dbc5aa --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/__callee__.rb @@ -0,0 +1,34 @@ +module KernelSpecs + class CalleeTest + def f + __callee__ + end + + alias_method :g, :f + + def in_block + (1..2).map { __callee__ } + end + + define_method(:dm) do + __callee__ + end + + define_method(:dm_block) do + (1..2).map { __callee__ } + end + + def from_send + send "__callee__" + end + + def from_eval + eval "__callee__" + end + + @@method = __callee__ + def from_class_body + @@method + end + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/__method__.rb b/spec/rubyspec/core/kernel/fixtures/__method__.rb new file mode 100644 index 0000000000..9300366b37 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/__method__.rb @@ -0,0 +1,34 @@ +module KernelSpecs + class MethodTest + def f + __method__ + end + + alias_method :g, :f + + def in_block + (1..2).map { __method__ } + end + + define_method(:dm) do + __method__ + end + + define_method(:dm_block) do + (1..2).map { __method__ } + end + + def from_send + send "__method__" + end + + def from_eval + eval "__method__" + end + + @@method = __method__ + def from_class_body + @@method + end + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/autoload_b.rb b/spec/rubyspec/core/kernel/fixtures/autoload_b.rb new file mode 100644 index 0000000000..e8be221ec7 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/autoload_b.rb @@ -0,0 +1,5 @@ +module KSAutoloadB + def self.loaded + :ksautoload_b + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/autoload_c.rb b/spec/rubyspec/core/kernel/fixtures/autoload_c.rb new file mode 100644 index 0000000000..4569b23669 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/autoload_c.rb @@ -0,0 +1,5 @@ +module KSAutoloadC + def self.loaded + :ksautoload_c + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/autoload_d.rb b/spec/rubyspec/core/kernel/fixtures/autoload_d.rb new file mode 100644 index 0000000000..552cb5e82c --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/autoload_d.rb @@ -0,0 +1,5 @@ +module KSAutoloadD + def self.loaded + :ksautoload_d + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/autoload_frozen.rb b/spec/rubyspec/core/kernel/fixtures/autoload_frozen.rb new file mode 100644 index 0000000000..e9dc42912b --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/autoload_frozen.rb @@ -0,0 +1,7 @@ +Object.freeze + +begin + autoload :ANY_CONSTANT, "no_autoload.rb" +rescue Exception => e + print e.class, " - ", defined?(ANY_CONSTANT).inspect +end diff --git a/spec/rubyspec/core/kernel/fixtures/caller.rb b/spec/rubyspec/core/kernel/fixtures/caller.rb new file mode 100644 index 0000000000..ae3e13e9c9 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/caller.rb @@ -0,0 +1,7 @@ +module KernelSpecs + class CallerTest + def self.locations(*args) + caller(*args) + end + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/caller_locations.rb b/spec/rubyspec/core/kernel/fixtures/caller_locations.rb new file mode 100644 index 0000000000..cc4e04d38f --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/caller_locations.rb @@ -0,0 +1,7 @@ +module KernelSpecs + class CallerLocationsTest + def self.locations(*args) + caller_locations(*args) + end + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/chomp.rb b/spec/rubyspec/core/kernel/fixtures/chomp.rb new file mode 100644 index 0000000000..f08dbadce5 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/chomp.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ\r\n" +print Kernel.chomp diff --git a/spec/rubyspec/core/kernel/fixtures/chomp_f.rb b/spec/rubyspec/core/kernel/fixtures/chomp_f.rb new file mode 100644 index 0000000000..3c22cb21e7 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/chomp_f.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ\r\n" +print chomp diff --git a/spec/rubyspec/core/kernel/fixtures/chop.rb b/spec/rubyspec/core/kernel/fixtures/chop.rb new file mode 100644 index 0000000000..dfd0626723 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/chop.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ" +print Kernel.chop diff --git a/spec/rubyspec/core/kernel/fixtures/chop_f.rb b/spec/rubyspec/core/kernel/fixtures/chop_f.rb new file mode 100644 index 0000000000..4ec89eb9ec --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/chop_f.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ" +print chop diff --git a/spec/rubyspec/core/kernel/fixtures/classes.rb b/spec/rubyspec/core/kernel/fixtures/classes.rb new file mode 100644 index 0000000000..118b87fb8d --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/classes.rb @@ -0,0 +1,425 @@ +module KernelSpecs + def self.Array_function(arg) + Array(arg) + end + + def self.Array_method(arg) + Kernel.Array(arg) + end + + def self.Hash_function(arg) + Hash(arg) + end + + def self.Hash_method(arg) + Kernel.Hash(arg) + end + + def self.Integer_function(arg) + Integer(arg) + end + + def self.Integer_method(arg) + Kernel.Integer(arg) + end + + def self.putc_function(arg) + putc arg + end + + def self.putc_method(arg) + Kernel.putc arg + end + + def self.has_private_method(name) + cmd = %[| #{RUBY_EXE} -n -e "print Kernel.private_method_defined?('#{name}')"] + ruby_exe("puts", args: cmd) == "true" + end + + def self.chop(str, method) + cmd = "| #{RUBY_EXE} -n -e '$_ = #{str.inspect}; #{method}; print $_'" + ruby_exe "puts", args: cmd + end + + def self.encoded_chop(file) + ruby_exe "puts", args: "| #{RUBY_EXE} -n #{file}" + end + + def self.chomp(str, method, sep="\n") + cmd = "| #{RUBY_EXE} -n -e '$_ = #{str.inspect}; $/ = #{sep.inspect}; #{method}; print $_'" + ruby_exe "puts", args: cmd + end + + def self.encoded_chomp(file) + ruby_exe "puts", args: "| #{RUBY_EXE} -n #{file}" + end + + # kind_of?, is_a?, instance_of? + module SomeOtherModule; end + module AncestorModule; end + module MyModule; end + module MyExtensionModule; end + + class AncestorClass < String + include AncestorModule + end + + class InstanceClass < AncestorClass + include MyModule + end + + class KindaClass < AncestorClass + include MyModule + def initialize + self.extend MyExtensionModule + end + end + + class Method + public :abort, :exit, :exit!, :fork, :system + end + + class Methods + + module MetaclassMethods + def peekaboo + end + + protected + + def nopeeking + end + + private + + def shoo + end + end + + def self.ichi; end + def ni; end + class << self + def san; end + end + + private + + def self.shi; end + def juu_shi; end + + class << self + def roku; end + + private + + def shichi; end + end + + protected + + def self.hachi; end + def ku; end + + class << self + def juu; end + + protected + + def juu_ichi; end + end + + public + + def self.juu_ni; end + def juu_san; end + end + + class PrivateSup + def public_in_sub + end + + private :public_in_sub + end + + class PublicSub < PrivateSup + def public_in_sub + end + end + + class A + # There is Kernel#public_method, so we don't want this one to clash + def pub_method; :public_method; end + + def undefed_method; :undefed_method; end + undef_method :undefed_method + + protected + def protected_method; :protected_method; end + + private + def private_method; :private_method; end + + public + define_method(:defined_method) { :defined } + end + + class B < A + alias aliased_pub_method pub_method + end + + class VisibilityChange + class << self + private :new + end + end + + class Binding + @@super_secret = "password" + + def initialize(n) + @secret = n + end + + def square(n) + n * n + end + + def get_binding + a = true + @bind = binding + + # Add/Change stuff + b = true + @secret += 1 + + @bind + end + end + + + module BlockGiven + def self.accept_block + block_given? + end + + def self.accept_block_as_argument(&block) + block_given? + end + + class << self + define_method(:defined_block) do + block_given? + end + end + end + + module KernelBlockGiven + def self.accept_block + Kernel.block_given? + end + + def self.accept_block_as_argument(&block) + Kernel.block_given? + end + + class << self + define_method(:defined_block) do + Kernel.block_given? + end + end + end + + module SelfBlockGiven + def self.accept_block + self.send(:block_given?) + end + + def self.accept_block_as_argument(&block) + self.send(:block_given?) + end + + class << self + define_method(:defined_block) do + self.send(:block_given?) + end + end + end + + module KernelBlockGiven + def self.accept_block + Kernel.block_given? + end + + def self.accept_block_as_argument(&block) + Kernel.block_given? + end + + class << self + define_method(:defined_block) do + Kernel.block_given? + end + end + end + + class EvalTest + def self.eval_yield_with_binding + eval("yield", binding) + end + def self.call_yield + yield + end + end + + module DuplicateM + def repr + self.class.name.to_s + end + end + + class Duplicate + attr_accessor :one, :two + + def initialize(one, two) + @one = one + @two = two + end + + def initialize_copy(other) + ScratchPad.record object_id + end + end + + class Clone + def initialize_clone(other) + ScratchPad.record other.object_id + end + end + + class Dup + def initialize_dup(other) + ScratchPad.record other.object_id + end + end + + module ParentMixin + def parent_mixin_method; end + end + + class Parent + include ParentMixin + def parent_method; end + def another_parent_method; end + def self.parent_class_method; :foo; end + end + + class Child < Parent + undef_method :parent_method + end + + class Grandchild < Child + undef_method :parent_mixin_method + end + + # for testing lambda + class Lambda + def outer + inner + end + + def mp(&b); b; end + + def inner + b = mp { return :good } + + pr = lambda { |x| x.call } + + pr.call(b) + + # We shouldn't be here, b should have unwinded through + return :bad + end + end + + class RespondViaMissing + def respond_to_missing?(method, priv=false) + case method + when :handled_publicly + true + when :handled_privately + priv + when :not_handled + false + else + raise "Typo in method name" + end + end + + def method_missing(method, *args) + "Done #{method}(#{args})" + end + end + + class InstanceVariable + def initialize + @greeting = "hello" + end + end + + class PrivateToAry + private + + def to_ary + [1, 2] + end + + def to_a + [3, 4] + end + end + + class PrivateToA + private + + def to_a + [3, 4] + end + end +end + +class EvalSpecs + class A + eval "class B; end" + def c + eval "class C; end" + end + end + + class CoercedObject + def to_str + '2 + 3' + end + + def hash + nil + end + end + + def f + yield + end + + def self.call_eval + f = __FILE__ + eval "true", binding, "(eval)", 1 + return f + end +end + +# for Kernel#sleep to have Channel in it's specs +# TODO: switch directly to queue for both Kernel#sleep and Thread specs? +unless defined? Channel + require 'thread' + class Channel < Queue + alias receive shift + end +end diff --git a/spec/rubyspec/core/kernel/fixtures/eval_locals.rb b/spec/rubyspec/core/kernel/fixtures/eval_locals.rb new file mode 100644 index 0000000000..ca8b381806 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/eval_locals.rb @@ -0,0 +1,6 @@ +begin + eval("a = 2") + eval("p a") +rescue Object => e + puts e.class +end diff --git a/spec/rubyspec/core/kernel/fixtures/eval_return_with_lambda.rb b/spec/rubyspec/core/kernel/fixtures/eval_return_with_lambda.rb new file mode 100644 index 0000000000..a48b5685f3 --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/eval_return_with_lambda.rb @@ -0,0 +1,12 @@ +print "a," +x = lambda do + print "b," + Proc.new do + print "c," + eval("return :eval") + print "d," + end.call + print "e," +end.call +print x, "," +print "f" diff --git a/spec/rubyspec/core/kernel/fixtures/eval_return_without_lambda.rb b/spec/rubyspec/core/kernel/fixtures/eval_return_without_lambda.rb new file mode 100644 index 0000000000..fc8b7f1d4a --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/eval_return_without_lambda.rb @@ -0,0 +1,14 @@ +print "a," +begin + print "b," + x = Proc.new do + print "c," + eval("return :eval") + print "d," + end.call + print x, "," +rescue LocalJumpError => e + print "e," + print e.class, "," +end +print "f" diff --git a/spec/rubyspec/core/kernel/fixtures/test.rb b/spec/rubyspec/core/kernel/fixtures/test.rb new file mode 100644 index 0000000000..949948606f --- /dev/null +++ b/spec/rubyspec/core/kernel/fixtures/test.rb @@ -0,0 +1,362 @@ +def foo1 +end + +def foo2 +end + +def foo3 +end + +def foo4 +end + +def foo5 +end + +def foo6 +end + +def foo7 +end + +def foo8 +end + +def foo9 +end + +def foo10 +end + +def foo11 +end + +def foo12 +end + +def foo13 +end + +def foo14 +end + +def foo15 +end + +def foo16 +end + +def foo17 +end + +def foo18 +end + +def foo19 +end + +def foo20 +end + +def foo21 +end + +def foo22 +end + +def foo23 +end + +def foo24 +end + +def foo25 +end + +def foo26 +end + +def foo27 +end + +def foo28 +end + +def foo29 +end + +def foo30 +end + +def foo31 +end + +def foo32 +end + +def foo33 +end + +def foo34 +end + +def foo35 +end + +def foo36 +end + +def foo37 +end + +def foo38 +end + +def foo39 +end + +def foo40 +end + +def foo41 +end + +def foo42 +end + +def foo43 +end + +def foo44 +end + +def foo45 +end + +def foo46 +end + +def foo47 +end + +def foo48 +end + +def foo49 +end + +def foo50 +end + +def foo51 +end + +def foo52 +end + +def foo53 +end + +def foo54 +end + +def foo55 +end + +def foo56 +end + +def foo57 +end + +def foo58 +end + +def foo59 +end + +def foo60 +end + +def foo61 +end + +def foo62 +end + +def foo63 +end + +def foo64 +end + +def foo65 +end + +def foo66 +end + +def foo67 +end + +def foo68 +end + +def foo69 +end + +def foo70 +end + +def foo71 +end + +def foo72 +end + +def foo73 +end + +def foo74 +end + +def foo75 +end + +def foo76 +end + +def foo77 +end + +def foo78 +end + +def foo79 +end + +def foo80 +end + +def foo81 +end + +def foo82 +end + +def foo83 +end + +def foo84 +end + +def foo85 +end + +def foo86 +end + +def foo87 +end + +def foo88 +end + +def foo89 +end + +def foo90 +end + +def foo91 +end + +def foo92 +end + +def foo93 +end + +def foo94 +end + +def foo95 +end + +def foo96 +end + +def foo97 +end + +def foo98 +end + +def foo99 +end + +def foo100 +end + +def foo101 +end + +def foo102 +end + +def foo103 +end + +def foo104 +end + +def foo105 +end + +def foo106 +end + +def foo107 +end + +def foo108 +end + +def foo109 +end + +def foo110 +end + +def foo111 +end + +def foo112 +end + +def foo113 +end + +def foo114 +end + +def foo115 +end + +def foo116 +end + +def foo117 +end + +def foo118 +end + +def foo119 +end + +def foo120 +end + +def foo121 +end diff --git a/spec/rubyspec/core/kernel/fork_spec.rb b/spec/rubyspec/core/kernel/fork_spec.rb new file mode 100644 index 0000000000..8919d0914d --- /dev/null +++ b/spec/rubyspec/core/kernel/fork_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/process/fork', __FILE__) + +describe "Kernel#fork" do + it "is a private method" do + Kernel.should have_private_instance_method(:fork) + end + + it_behaves_like :process_fork, :fork, KernelSpecs::Method.new +end + +describe "Kernel.fork" do + it_behaves_like :process_fork, :fork, Kernel +end diff --git a/spec/rubyspec/core/kernel/format_spec.rb b/spec/rubyspec/core/kernel/format_spec.rb new file mode 100644 index 0000000000..c73d5f0efb --- /dev/null +++ b/spec/rubyspec/core/kernel/format_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#format" do + it "is a private method" do + Kernel.should have_private_instance_method(:format) + end +end + +describe "Kernel.format" do + it "is accessible as a module function" do + Kernel.format("%s", "hello").should == "hello" + end +end diff --git a/spec/rubyspec/core/kernel/freeze_spec.rb b/spec/rubyspec/core/kernel/freeze_spec.rb new file mode 100644 index 0000000000..961c1bccf0 --- /dev/null +++ b/spec/rubyspec/core/kernel/freeze_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#freeze" do + it "prevents self from being further modified" do + o = mock('o') + o.frozen?.should be_false + o.freeze + o.frozen?.should be_true + end + + it "returns self" do + o = Object.new + o.freeze.should equal(o) + end + + describe "on integers" do + it "has no effect since they are already frozen" do + 1.frozen?.should be_true + 1.freeze + + bignum = bignum_value + bignum.frozen?.should be_true + bignum.freeze + end + end + + describe "on a Float" do + it "has no effect since it is already frozen" do + 1.2.frozen?.should be_true + 1.2.freeze + end + end + + describe "on a Symbol" do + it "has no effect since it is already frozen" do + :sym.frozen?.should be_true + :sym.freeze + end + end + + describe "on true, false and nil" do + it "has no effect since they are already frozen" do + nil.frozen?.should be_true + true.frozen?.should be_true + false.frozen?.should be_true + + nil.freeze + true.freeze + false.freeze + end + end + + it "causes mutative calls to raise RuntimeError" do + o = Class.new do + def mutate; @foo = 1; end + end.new + o.freeze + lambda {o.mutate}.should raise_error(RuntimeError) + end + + it "causes instance_variable_set to raise RuntimeError" do + o = Object.new + o.freeze + lambda {o.instance_variable_set(:@foo, 1)}.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/kernel/frozen_spec.rb b/spec/rubyspec/core/kernel/frozen_spec.rb new file mode 100644 index 0000000000..9444b62249 --- /dev/null +++ b/spec/rubyspec/core/kernel/frozen_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#frozen?" do + it "returns true if self is frozen" do + o = mock('o') + p = mock('p') + p.freeze + o.frozen?.should == false + p.frozen?.should == true + end + + describe "on true, false and nil" do + it "returns true" do + true.frozen?.should be_true + false.frozen?.should be_true + nil.frozen?.should be_true + end + end + + describe "on integers" do + before :each do + @fixnum = 1 + @bignum = bignum_value + end + + it "returns true" do + @fixnum.frozen?.should be_true + @bignum.frozen?.should be_true + end + end + + describe "on a Float" do + before :each do + @float = 0.1 + end + + it "returns true" do + @float.frozen?.should be_true + end + end + + describe "on a Symbol" do + before :each do + @symbol = :symbol + end + + it "returns true" do + @symbol.frozen?.should be_true + end + end +end diff --git a/spec/rubyspec/core/kernel/gets_spec.rb b/spec/rubyspec/core/kernel/gets_spec.rb new file mode 100644 index 0000000000..eb68e093ab --- /dev/null +++ b/spec/rubyspec/core/kernel/gets_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#gets" do + it "is a private method" do + Kernel.should have_private_instance_method(:gets) + end +end + +describe "Kernel.gets" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/global_variables_spec.rb b/spec/rubyspec/core/kernel/global_variables_spec.rb new file mode 100644 index 0000000000..739b800938 --- /dev/null +++ b/spec/rubyspec/core/kernel/global_variables_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.global_variables" do + it "is a private method" do + Kernel.should have_private_instance_method(:global_variables) + end + + before :all do + @i = 0 + end + + it "finds subset starting with std" do + global_variables.grep(/std/).should include(:$stderr, :$stdin, :$stdout) + a = global_variables.size + gvar_name = "$foolish_global_var#{@i += 1}" + global_variables.include?(gvar_name.to_sym).should == false + eval("#{gvar_name} = 1") + global_variables.size.should == a+1 + global_variables.should include(gvar_name.to_sym) + end +end + +describe "Kernel#global_variables" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/gsub_spec.rb b/spec/rubyspec/core/kernel/gsub_spec.rb new file mode 100644 index 0000000000..005ed0063d --- /dev/null +++ b/spec/rubyspec/core/kernel/gsub_spec.rb @@ -0,0 +1,96 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# FIXME: These methods exist only when the -n or -p option is passed to +# ruby, but we currently don't have a way of specifying that. +ruby_version_is ""..."1.9" do + describe "Kernel#gsub" do + it "is a private method" do + Kernel.should have_private_instance_method(:gsub) + end + + it "raises a TypeError if $_ is not a String" do + lambda { + $_ = 123 + gsub(/./, "!") + }.should raise_error(TypeError) + end + + it "when matches sets $_ to a new string, leaving the former value unaltered" do + orig_value = $_ = "hello" + gsub("ello", "ola") + $_.should_not equal(orig_value) + $_.should == "hola" + orig_value.should == "hello" + end + + it "returns a string with the same contents as $_ after the operation" do + $_ = "bye" + gsub("non-match", "?").should == "bye" + + orig_value = $_ = "bye" + gsub(/$/, "!").should == "bye!" + orig_value.should == "bye" + end + + it "accepts Regexps as patterns" do + $_ = "food" + gsub(/.$/, "l") + $_.should == "fool" + end + + it "accepts Strings as patterns, treated literally" do + $_ = "hello, world." + gsub(".", "!") + $_.should == "hello, world!" + end + + it "accepts objects which respond to #to_str as patterns and treats them as strings" do + $_ = "hello, world." + stringlike = mock(".") + stringlike.should_receive(:to_str).and_return(".") + gsub(stringlike, "!") + $_.should == "hello, world!" + end + end + + describe "Kernel#gsub with a pattern and replacement" do + it "accepts strings for replacement" do + $_ = "hello" + gsub(/./, ".") + $_.should == "....." + end + + it "accepts objects which respond to #to_str for replacement" do + o = mock("o") + o.should_receive(:to_str).and_return("o") + $_ = "ping" + gsub("i", o) + $_.should == "pong" + end + + it "replaces \\1 sequences with the regexp's corresponding capture" do + $_ = "hello!" + gsub(/(.)(.)/, '\2\1') + $_.should == "ehll!o" + end + end + + describe "Kernel#gsub with pattern and block" do + it "acts similarly to using $_.gsub" do + $_ = "olleh dlrow" + gsub(/(\w+)/){ $1.reverse } + $_.should == "hello world" + end + end + + describe "Kernel#gsub!" do + it "is a private method" do + Kernel.should have_private_instance_method(:gsub!) + end + end + + describe "Kernel.gsub!" do + it "needs to be reviewed for spec completeness" + end +end diff --git a/spec/rubyspec/core/kernel/inspect_spec.rb b/spec/rubyspec/core/kernel/inspect_spec.rb new file mode 100644 index 0000000000..92129ebbc5 --- /dev/null +++ b/spec/rubyspec/core/kernel/inspect_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#inspect" do + it "returns a String" do + Object.new.inspect.should be_an_instance_of(String) + end + + it "returns a tainted string if self is tainted" do + Object.new.taint.inspect.tainted?.should be_true + end + + it "returns an untrusted string if self is untrusted" do + Object.new.untrust.inspect.untrusted?.should be_true + end + + it "does not call #to_s if it is defined" do + # We must use a bare Object here + obj = Object.new + inspected = obj.inspect + + obj.stub!(:to_s).and_return("to_s'd") + + obj.inspect.should == inspected + end + + it "returns a String with the object class and object_id encoded" do + obj = Object.new + obj.inspect.should =~ /^#$/ + end +end diff --git a/spec/rubyspec/core/kernel/instance_of_spec.rb b/spec/rubyspec/core/kernel/instance_of_spec.rb new file mode 100644 index 0000000000..4801a1ff96 --- /dev/null +++ b/spec/rubyspec/core/kernel/instance_of_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe Kernel, "#instance_of?" do + before :each do + @o = KernelSpecs::InstanceClass.new + end + + it "returns true if given class is object's class" do + @o.instance_of?(KernelSpecs::InstanceClass).should == true + [].instance_of?(Array).should == true + ''.instance_of?(String).should == true + end + + it "returns false if given class is object's ancestor class" do + @o.instance_of?(KernelSpecs::AncestorClass).should == false + end + + it "returns false if given class is not object's class nor object's ancestor class" do + @o.instance_of?(Array).should == false + end + + it "returns false if given a Module that is included in object's class" do + @o.instance_of?(KernelSpecs::MyModule).should == false + end + + it "returns false if given a Module that is included one of object's ancestors only" do + @o.instance_of?(KernelSpecs::AncestorModule).should == false + end + + it "returns false if given a Module that is not included in object's class" do + @o.instance_of?(KernelSpecs::SomeOtherModule).should == false + end + + it "raises a TypeError if given an object that is not a Class nor a Module" do + lambda { @o.instance_of?(Object.new) }.should raise_error(TypeError) + lambda { @o.instance_of?('KernelSpecs::InstanceClass') }.should raise_error(TypeError) + lambda { @o.instance_of?(1) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/kernel/instance_variable_defined_spec.rb b/spec/rubyspec/core/kernel/instance_variable_defined_spec.rb new file mode 100644 index 0000000000..969d36c731 --- /dev/null +++ b/spec/rubyspec/core/kernel/instance_variable_defined_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variable_defined?" do + before do + @instance = KernelSpecs::InstanceVariable.new + end + + describe "when passed a String" do + it "returns false if the instance variable is not defined" do + @instance.instance_variable_defined?("@goodbye").should be_false + end + + it "returns true if the instance variable is defined" do + @instance.instance_variable_defined?("@greeting").should be_true + end + end + + describe "when passed a Symbol" do + it "returns false if the instance variable is not defined" do + @instance.instance_variable_defined?(:@goodbye).should be_false + end + + it "returns true if the instance variable is defined" do + @instance.instance_variable_defined?(:@greeting).should be_true + end + end + + it "raises a TypeError if passed an Object not defining #to_str" do + lambda do + obj = mock("kernel instance_variable_defined?") + @instance.instance_variable_defined? obj + end.should raise_error(TypeError) + end + + it "returns false if the instance variable is not defined for different types" do + [nil, false, true, 1, 2.0, :test, "test"].each do |obj| + obj.instance_variable_defined?("@goodbye").should be_false + end + end +end diff --git a/spec/rubyspec/core/kernel/instance_variable_get_spec.rb b/spec/rubyspec/core/kernel/instance_variable_get_spec.rb new file mode 100644 index 0000000000..0c564f11b6 --- /dev/null +++ b/spec/rubyspec/core/kernel/instance_variable_get_spec.rb @@ -0,0 +1,105 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variable_get" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "tries to convert the passed argument to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("@test") + @obj.instance_variable_get(obj) + end + + it "returns the value of the passed instance variable that is referred to by the conversion result" do + obj = mock("to_str") + obj.stub!(:to_str).and_return("@test") + @obj.instance_variable_get(obj).should == :test + end + + it "returns nil when the referred instance variable does not exist" do + @obj.instance_variable_get(:@does_not_exist).should be_nil + end + + it "raises a TypeError when the passed argument does not respond to #to_str" do + lambda { @obj.instance_variable_get(Object.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when the passed argument can't be converted to a String" do + obj = mock("to_str") + obj.stub!(:to_str).and_return(123) + lambda { @obj.instance_variable_get(obj) }.should raise_error(TypeError) + end + + it "raises a NameError when the conversion result does not start with an '@'" do + obj = mock("to_str") + obj.stub!(:to_str).and_return("test") + lambda { @obj.instance_variable_get(obj) }.should raise_error(NameError) + end + + it "raises a NameError when passed just '@'" do + obj = mock("to_str") + obj.stub!(:to_str).and_return('@') + lambda { @obj.instance_variable_get(obj) }.should raise_error(NameError) + end +end + +describe "Kernel#instance_variable_get when passed Symbol" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "returns the value of the instance variable that is referred to by the passed Symbol" do + @obj.instance_variable_get(:@test).should == :test + end + + it "raises a NameError when passed :@ as an instance variable name" do + lambda { @obj.instance_variable_get(:"@") }.should raise_error(NameError) + end + + it "raises a NameError when the passed Symbol does not start with an '@'" do + lambda { @obj.instance_variable_get(:test) }.should raise_error(NameError) + end + + it "raises a NameError when the passed Symbol is an invalid instance variable name" do + lambda { @obj.instance_variable_get(:"@0") }.should raise_error(NameError) + end +end + +describe "Kernel#instance_variable_get when passed String" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "returns the value of the instance variable that is referred to by the passed String" do + @obj.instance_variable_get("@test").should == :test + end + + it "raises a NameError when the passed String does not start with an '@'" do + lambda { @obj.instance_variable_get("test") }.should raise_error(NameError) + end + + it "raises a NameError when the passed String is an invalid instance variable name" do + lambda { @obj.instance_variable_get("@0") }.should raise_error(NameError) + end + + it "raises a NameError when passed '@' as an instance variable name" do + lambda { @obj.instance_variable_get("@") }.should raise_error(NameError) + end +end + +describe "Kernel#instance_variable_get when passed Fixnum" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "raises a TypeError" do + lambda { @obj.instance_variable_get(10) }.should raise_error(TypeError) + lambda { @obj.instance_variable_get(-10) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/kernel/instance_variable_set_spec.rb b/spec/rubyspec/core/kernel/instance_variable_set_spec.rb new file mode 100644 index 0000000000..bac1bb5f99 --- /dev/null +++ b/spec/rubyspec/core/kernel/instance_variable_set_spec.rb @@ -0,0 +1,93 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variable_set" do + it "sets the value of the specified instance variable" do + class Dog + def initialize(p1, p2) + @a, @b = p1, p2 + end + end + Dog.new('cat', 99).instance_variable_set(:@a, 'dog').should == "dog" + end + + it "sets the value of the instance variable when no instance variables exist yet" do + class NoVariables; end + NoVariables.new.instance_variable_set(:@a, "new").should == "new" + end + + it "raises a NameError exception if the argument is not of form '@x'" do + class NoDog; end + lambda { NoDog.new.instance_variable_set(:c, "cat") }.should raise_error(NameError) + end + + it "raises a NameError exception if the argument is an invalid instance variable name" do + class DigitDog; end + lambda { DigitDog.new.instance_variable_set(:"@0", "cat") }.should raise_error(NameError) + end + + it "raises a NameError when the argument is '@'" do + class DogAt; end + lambda { DogAt.new.instance_variable_set(:"@", "cat") }.should raise_error(NameError) + end + + it "raises a TypeError if the instance variable name is a Fixnum" do + lambda { "".instance_variable_set(1, 2) }.should raise_error(TypeError) + end + + it "raises a TypeError if the instance variable name is an object that does not respond to to_str" do + class KernelSpecs::A; end + lambda { "".instance_variable_set(KernelSpecs::A.new, 3) }.should raise_error(TypeError) + end + + it "raises a NameError if the passed object, when coerced with to_str, does not start with @" do + class KernelSpecs::B + def to_str + ":c" + end + end + lambda { "".instance_variable_set(KernelSpecs::B.new, 4) }.should raise_error(NameError) + end + + it "raises a NameError if pass an object that cannot be a symbol" do + lambda { "".instance_variable_set(:c, 1) }.should raise_error(NameError) + end + + it "accepts as instance variable name any instance of a class that responds to to_str" do + class KernelSpecs::C + def initialize + @a = 1 + end + def to_str + "@a" + end + end + KernelSpecs::C.new.instance_variable_set(KernelSpecs::C.new, 2).should == 2 + end + + describe "on frozen objects" do + before :each do + klass = Class.new do + attr_reader :ivar + def initialize + @ivar = :origin + end + end + + @frozen = klass.new.freeze + end + + it "keeps stored object after any exceptions" do + lambda { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(Exception) + @frozen.ivar.should equal(:origin) + end + + it "raises a RuntimeError when passed replacement is identical to stored object" do + lambda { @frozen.instance_variable_set(:@ivar, :origin) }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when passed replacement is different from stored object" do + lambda { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/kernel/instance_variables_spec.rb b/spec/rubyspec/core/kernel/instance_variables_spec.rb new file mode 100644 index 0000000000..f744ee3c7a --- /dev/null +++ b/spec/rubyspec/core/kernel/instance_variables_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variables" do + describe "immediate values" do + it "returns an empty array if no instance variables are defined" do + 0.instance_variables.should == [] + end + + it "returns the correct array if an instance variable is added" do + a = 0 + lambda{ a.instance_variable_set("@test", 1) }.should raise_error(RuntimeError) + end + end + + describe "regular objects" do + it "returns an empty array if no instance variables are defined" do + Object.new.instance_variables.should == [] + end + + it "returns the correct array if an instance variable is added" do + a = Object.new + a.instance_variable_set("@test", 1) + a.instance_variables.should == [:@test] + end + end +end diff --git a/spec/rubyspec/core/kernel/is_a_spec.rb b/spec/rubyspec/core/kernel/is_a_spec.rb new file mode 100644 index 0000000000..c67c6552a0 --- /dev/null +++ b/spec/rubyspec/core/kernel/is_a_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/kind_of', __FILE__) + +describe "Kernel#is_a?" do + it_behaves_like(:kernel_kind_of , :is_a?) +end diff --git a/spec/rubyspec/core/kernel/iterator_spec.rb b/spec/rubyspec/core/kernel/iterator_spec.rb new file mode 100644 index 0000000000..e85f0dc612 --- /dev/null +++ b/spec/rubyspec/core/kernel/iterator_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#iterator?" do + it "is a private method" do + Kernel.should have_private_instance_method(:iterator?) + end +end + +describe "Kernel.iterator?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/itself_spec.rb b/spec/rubyspec/core/kernel/itself_spec.rb new file mode 100644 index 0000000000..285080ec3b --- /dev/null +++ b/spec/rubyspec/core/kernel/itself_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#itself" do + it "returns the receiver itself" do + foo = Object.new + foo.itself.should equal foo + foo.itself.object_id.should equal foo.object_id + end +end diff --git a/spec/rubyspec/core/kernel/kind_of_spec.rb b/spec/rubyspec/core/kernel/kind_of_spec.rb new file mode 100644 index 0000000000..56a54ec859 --- /dev/null +++ b/spec/rubyspec/core/kernel/kind_of_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/kind_of', __FILE__) + +describe "Kernel#kind_of?" do + it_behaves_like(:kernel_kind_of , :kind_of?) +end diff --git a/spec/rubyspec/core/kernel/lambda_spec.rb b/spec/rubyspec/core/kernel/lambda_spec.rb new file mode 100644 index 0000000000..8fa0075675 --- /dev/null +++ b/spec/rubyspec/core/kernel/lambda_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/lambda', __FILE__) + +# The functionality of lambdas is specified in core/proc + +describe "Kernel.lambda" do + it_behaves_like(:kernel_lambda, :lambda) + + it "is a private method" do + Kernel.should have_private_instance_method(:lambda) + end + + it "creates a lambda-style Proc if given a literal block" do + l = lambda { 42 } + l.lambda?.should be_true + end + + it "returned the passed Proc if given an existing Proc" do + some_proc = proc {} + l = lambda(&some_proc) + l.should equal(some_proc) + l.lambda?.should be_false + end + + it "checks the arity of the call when no args are specified" do + l = lambda { :called } + l.call.should == :called + + lambda { l.call(1) }.should raise_error(ArgumentError) + lambda { l.call(1, 2) }.should raise_error(ArgumentError) + end + + it "checks the arity when 1 arg is specified" do + l = lambda { |a| :called } + l.call(1).should == :called + + lambda { l.call }.should raise_error(ArgumentError) + lambda { l.call(1, 2) }.should raise_error(ArgumentError) + end + + it "does not check the arity when passing a Proc with &" do + l = lambda { || :called } + p = proc { || :called } + + lambda { l.call(1) }.should raise_error(ArgumentError) + p.call(1).should == :called + end + + it "accepts 0 arguments when used with ||" do + lambda { + lambda { || }.call(1) + }.should raise_error(ArgumentError) + end + + it "strictly checks the arity when 0 or 2..inf args are specified" do + l = lambda { |a,b| } + + lambda { + l.call + }.should raise_error(ArgumentError) + + lambda { + l.call(1) + }.should raise_error(ArgumentError) + + lambda { + l.call(1,2) + }.should_not raise_error(ArgumentError) + end + + it "returns from the lambda itself, not the creation site of the lambda" do + @reached_end_of_method = nil + def test + send(:lambda) { return }.call + @reached_end_of_method = true + end + test + @reached_end_of_method.should be_true + end + + it "allows long returns to flow through it" do + KernelSpecs::Lambda.new.outer.should == :good + end +end + diff --git a/spec/rubyspec/core/kernel/load_spec.rb b/spec/rubyspec/core/kernel/load_spec.rb new file mode 100644 index 0000000000..36cc07e38a --- /dev/null +++ b/spec/rubyspec/core/kernel/load_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/code_loading', __FILE__) +require File.expand_path('../shared/load', __FILE__) +require File.expand_path('../shared/require', __FILE__) + +describe "Kernel#load" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "is a private method" do + Kernel.should have_private_instance_method(:load) + end + + it_behaves_like :kernel_require_basic, :load, CodeLoadingSpecs::Method.new +end + +describe "Kernel#load" do + it_behaves_like :kernel_load, :load, CodeLoadingSpecs::Method.new +end + +describe "Kernel.load" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it_behaves_like :kernel_require_basic, :load, Kernel +end + +describe "Kernel.load" do + it_behaves_like :kernel_load, :load, Kernel +end diff --git a/spec/rubyspec/core/kernel/local_variables_spec.rb b/spec/rubyspec/core/kernel/local_variables_spec.rb new file mode 100644 index 0000000000..7e8b364b4f --- /dev/null +++ b/spec/rubyspec/core/kernel/local_variables_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#local_variables" do + after :each do + ScratchPad.clear + end + + it "is a private method" do + Kernel.should have_private_instance_method(:local_variables) + end + + it "contains locals as they are added" do + a = 1 + b = 2 + local_variables.should include(:a, :b) + local_variables.length.should == 2 + end + + it "is accessible from bindings" do + def local_var_foo + a = 1 + b = 2 + binding + end + foo_binding = local_var_foo() + res = eval("local_variables",foo_binding) + res.should include(:a, :b) + res.length.should == 2 + end + + it "is accessible in eval" do + eval "a=1; b=2; ScratchPad.record local_variables" + ScratchPad.recorded.should include(:a, :b) + ScratchPad.recorded.length.should == 2 + end +end diff --git a/spec/rubyspec/core/kernel/loop_spec.rb b/spec/rubyspec/core/kernel/loop_spec.rb new file mode 100644 index 0000000000..f23e3afb02 --- /dev/null +++ b/spec/rubyspec/core/kernel/loop_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.loop" do + it "is a private method" do + Kernel.should have_private_instance_method(:loop) + end + + it "calls block until it is terminated by a break" do + i = 0 + loop do + i += 1 + break if i == 10 + end + + i.should == 10 + end + + it "returns value passed to break" do + loop do + break 123 + end.should == 123 + end + + it "returns nil if no value passed to break" do + loop do + break + end.should == nil + end + + it "returns an enumerator if no block given" do + enum = loop + enum.instance_of?(Enumerator).should be_true + cnt = 0 + enum.each do |*args| + raise "Args should be empty #{args.inspect}" unless args.empty? + cnt += 1 + break cnt if cnt >= 42 + end.should == 42 + end + + it "rescues StopIteration" do + loop do + raise StopIteration + end + 42.should == 42 + end + + it "rescues StopIteration's subclasses" do + finish = Class.new StopIteration + loop do + raise finish + end + 42.should == 42 + end + + it "does not rescue other errors" do + lambda{ loop do raise StandardError end }.should raise_error( StandardError ) + end + + ruby_version_is "2.3" do + it "returns StopIteration#result, the result value of a finished iterator" do + e = Enumerator.new { |y| + y << 1 + y << 2 + :stopped + } + loop { e.next }.should == :stopped + end + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "returns Float::INFINITY" do + loop.size.should == Float::INFINITY + end + end + end + end +end diff --git a/spec/rubyspec/core/kernel/match_spec.rb b/spec/rubyspec/core/kernel/match_spec.rb new file mode 100644 index 0000000000..8a117ed497 --- /dev/null +++ b/spec/rubyspec/core/kernel/match_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#=~" do + it "returns nil matching any object" do + o = Object.new + + (o =~ /Object/).should be_nil + (o =~ 'Object').should be_nil + (o =~ Object).should be_nil + (o =~ Object.new).should be_nil + (o =~ nil).should be_nil + (o =~ true).should be_nil + end +end diff --git a/spec/rubyspec/core/kernel/method_spec.rb b/spec/rubyspec/core/kernel/method_spec.rb new file mode 100644 index 0000000000..09a3f940ca --- /dev/null +++ b/spec/rubyspec/core/kernel/method_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/method', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#method" do + it_behaves_like(:kernel_method, :method) + + before :each do + @obj = KernelSpecs::A.new + end + + it "can be called on a private method" do + @obj.send(:private_method).should == :private_method + @obj.method(:private_method).should be_an_instance_of(Method) + end + + it "can be called on a protected method" do + @obj.send(:protected_method).should == :protected_method + @obj.method(:protected_method).should be_an_instance_of(Method) + end + + it "will see an alias of the original method as == when in a derived class" do + obj = KernelSpecs::B.new + obj.method(:aliased_pub_method).should == obj.method(:pub_method) + end + + it "can call methods created with define_method" do + m = @obj.method(:defined_method) + m.call.should == :defined + end + + it "can be called even if we only repond_to_missing? method, true" do + m = KernelSpecs::RespondViaMissing.new.method(:handled_privately) + m.should be_an_instance_of(Method) + m.call(1, 2, 3).should == "Done handled_privately([1, 2, 3])" + end +end diff --git a/spec/rubyspec/core/kernel/methods_spec.rb b/spec/rubyspec/core/kernel/methods_spec.rb new file mode 100644 index 0000000000..5dfb17d4cb --- /dev/null +++ b/spec/rubyspec/core/kernel/methods_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Kernel#methods" do + it "returns singleton methods defined by obj.meth" do + KernelSpecs::Methods.methods(false).should include(:ichi) + end + + it "returns singleton methods defined in 'class << self'" do + KernelSpecs::Methods.methods(false).should include(:san) + end + + it "returns private singleton methods defined by obj.meth" do + KernelSpecs::Methods.methods(false).should include(:shi) + end + + it "returns singleton methods defined in 'class << self' when it follows 'private'" do + KernelSpecs::Methods.methods(false).should include(:roku) + end + + it "does not return private singleton methods defined in 'class << self'" do + KernelSpecs::Methods.methods(false).should_not include(:shichi) + end + + it "returns the publicly accessible methods of the object" do + meths = KernelSpecs::Methods.methods(false) + meths.should include(:hachi, :ichi, :juu, :juu_ichi, + :juu_ni, :roku, :san, :shi) + + KernelSpecs::Methods.new.methods(false).should == [] + end + + it "returns the publicly accessible methods in the object, its ancestors and mixed-in modules" do + meths = KernelSpecs::Methods.methods(false) & KernelSpecs::Methods.methods + meths.should include(:hachi, :ichi, :juu, :juu_ichi, + :juu_ni, :roku, :san, :shi) + + KernelSpecs::Methods.new.methods.should include(:ku, :ni, :juu_san) + end + + it "returns methods added to the metaclass through extend" do + meth = KernelSpecs::Methods.new + meth.methods.should_not include(:peekaboo) + meth.extend(KernelSpecs::Methods::MetaclassMethods) + meth.methods.should include(:peekaboo) + end + + it "does not return undefined singleton methods defined by obj.meth" do + o = KernelSpecs::Child.new + def o.single; end + o.methods.should include(:single) + + class << o; self; end.send :undef_method, :single + o.methods.should_not include(:single) + end + + it "does not return superclass methods undefined in the object's class" do + KernelSpecs::Child.new.methods.should_not include(:parent_method) + end + + it "does not return superclass methods undefined in a superclass" do + KernelSpecs::Grandchild.new.methods.should_not include(:parent_method) + end + + it "does not return included module methods undefined in the object's class" do + KernelSpecs::Grandchild.new.methods.should_not include(:parent_mixin_method) + end +end + +describe :kernel_methods_supers, shared: true do + before :all do + @ms = [:pro, :pub] + end + + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.methods(*@object) + m.select { |x| @ms.include? x }.sort.should == @ms + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.methods(*@object) + m.select { |x| @ms.include? x }.sort.should == @ms + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.methods(*@object) + m.select { |x| @ms.include? x }.sort.should == @ms + end +end + +describe "Kernel#methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_methods_supers, nil, true + end +end diff --git a/spec/rubyspec/core/kernel/nil_spec.rb b/spec/rubyspec/core/kernel/nil_spec.rb new file mode 100644 index 0000000000..0b5e34f7f1 --- /dev/null +++ b/spec/rubyspec/core/kernel/nil_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#nil?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/not_match_spec.rb b/spec/rubyspec/core/kernel/not_match_spec.rb new file mode 100644 index 0000000000..42bd45c106 --- /dev/null +++ b/spec/rubyspec/core/kernel/not_match_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#!~" do + class KernelSpecs::NotMatch + def !~(obj) + :foo + end + end + + it 'calls =~ internally and negates the result' do + obj = Object.new + obj.should_receive(:=~).and_return(true) + (obj !~ :foo).should == false + end + + it 'can be overridden in subclasses' do + obj = KernelSpecs::NotMatch.new + (obj !~ :bar).should == :foo + end +end diff --git a/spec/rubyspec/core/kernel/object_id_spec.rb b/spec/rubyspec/core/kernel/object_id_spec.rb new file mode 100644 index 0000000000..0a12415a40 --- /dev/null +++ b/spec/rubyspec/core/kernel/object_id_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/kernel/object_id', __FILE__) + +describe "Kernel#object_id" do + it_behaves_like :object_id, :object_id, Object +end diff --git a/spec/rubyspec/core/kernel/open_spec.rb b/spec/rubyspec/core/kernel/open_spec.rb new file mode 100644 index 0000000000..4e99061219 --- /dev/null +++ b/spec/rubyspec/core/kernel/open_spec.rb @@ -0,0 +1,141 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#open" do + + before :each do + @name = tmp("kernel_open.txt") + @content = "This is a test" + touch(@name) { |f| f.write @content } + @file = nil + end + + after :each do + @file.close if @file + rm_r @name + end + + it "is a private method" do + Kernel.should have_private_instance_method(:open) + end + + it "opens a file when given a valid filename" do + @file = open(@name) + @file.should be_kind_of(File) + end + + it "opens a file when called with a block" do + open(@name, "r") { |f| f.gets }.should == @content + end + + platform_is_not :windows do + it "opens an io when path starts with a pipe" do + @io = open("|date") + begin + @io.should be_kind_of(IO) + ensure + @io.close + end + end + + it "opens an io when called with a block" do + @output = open("|date") { |f| f.gets } + @output.should_not == '' + end + + it "opens an io for writing" do + bytes = open("|cat", "w") { |io| io.write(".") } + bytes.should == 1 + end + end + + platform_is :windows do + it "opens an io when path starts with a pipe" do + @io = open("|date /t") + begin + @io.should be_kind_of(IO) + @io.read + ensure + @io.close + end + end + + it "opens an io when called with a block" do + @output = open("|date /t") { |f| f.gets } + @output.should_not == '' + end + end + + it "raises an ArgumentError if not passed one argument" do + lambda { open }.should raise_error(ArgumentError) + end + + describe "when given an object that responds to to_open" do + before :each do + ScratchPad.clear + end + + it "calls #to_path to covert the argument to a String before calling #to_str" do + obj = mock("open to_path") + obj.should_receive(:to_path).at_least(1).times.and_return(@name) + obj.should_not_receive(:to_str) + + open(obj, "r") { |f| f.gets }.should == @content + end + + it "calls #to_str to convert the argument to a String" do + obj = mock("open to_str") + obj.should_receive(:to_str).at_least(1).times.and_return(@name) + + open(obj, "r") { |f| f.gets }.should == @content + end + + it "calls #to_open on argument" do + obj = mock('fileish') + @file = File.open(@name) + obj.should_receive(:to_open).and_return(@file) + @file = open(obj) + @file.should be_kind_of(File) + end + + it "returns the value from #to_open" do + obj = mock('to_open') + obj.should_receive(:to_open).and_return(:value) + + open(obj).should == :value + end + + it "passes its arguments onto #to_open" do + obj = mock('to_open') + obj.should_receive(:to_open).with(1,2,3) + + open(obj, 1, 2, 3) + end + + it "passes the return value from #to_open to a block" do + obj = mock('to_open') + obj.should_receive(:to_open).and_return(:value) + + open(obj) do |mock| + ScratchPad.record(mock) + end + + ScratchPad.recorded.should == :value + end + end + + it "raises a TypeError if passed a non-String that does not respond to #to_open" do + obj = mock('non-fileish') + lambda { open(obj) }.should raise_error(TypeError) + lambda { open(nil) }.should raise_error(TypeError) + lambda { open(7) }.should raise_error(TypeError) + end + + it "accepts nil for mode and permission" do + open(@name, nil, nil) { |f| f.gets }.should == @content + end +end + +describe "Kernel.open" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/p_spec.rb b/spec/rubyspec/core/kernel/p_spec.rb new file mode 100644 index 0000000000..c451f5952a --- /dev/null +++ b/spec/rubyspec/core/kernel/p_spec.rb @@ -0,0 +1,79 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#p" do + before :all do + @rs_f, @rs_b, @rs_c = $/, $\, $, + end + + after :each do + $/, $\, $, = @rs_f, @rs_b, @rs_c + end + + it "is a private method" do + Kernel.should have_private_instance_method(:p) + end + + # TODO: fix + it "flushes output if receiver is a File" do + filename = tmp("Kernel_p_flush") + $$.to_s + begin + File.open(filename, "w") do |f| + begin + old_stdout = $stdout + $stdout = f + p("abcde") + ensure + $stdout = old_stdout + end + + File.open(filename) do |f2| + f2.read(7).should == "\"abcde\"" + end + end + ensure + rm_r filename + end + end + + it "prints obj.inspect followed by system record separator for each argument given" do + o = mock("Inspector Gadget") + o.should_receive(:inspect).any_number_of_times.and_return "Next time, Gadget, NEXT TIME!" + + lambda { p(o) }.should output("Next time, Gadget, NEXT TIME!\n") + lambda { p(*[o]) }.should output("Next time, Gadget, NEXT TIME!\n") + lambda { p(*[o, o]) }.should output("Next time, Gadget, NEXT TIME!\nNext time, Gadget, NEXT TIME!\n") + lambda { p([o])}.should output("[#{o.inspect}]\n") + end + + it "is not affected by setting $\\, $/ or $," do + o = mock("Inspector Gadget") + o.should_receive(:inspect).any_number_of_times.and_return "Next time, Gadget, NEXT TIME!" + + $, = " *helicopter sound*\n" + lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n") + + $\ = " *helicopter sound*\n" + lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n") + + $/ = " *helicopter sound*\n" + lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n") + end + + it "prints nothing if no argument is given" do + lambda { p }.should output("") + end + + it "prints nothing if called splatting an empty Array" do + lambda { p(*[]) }.should output("") + end + +=begin Not sure how to spec this, but wanted to note the behavior here + it "does not flush if receiver is not a TTY or a File" do + end +=end +end + +describe "Kernel.p" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/print_spec.rb b/spec/rubyspec/core/kernel/print_spec.rb new file mode 100644 index 0000000000..3b642538cb --- /dev/null +++ b/spec/rubyspec/core/kernel/print_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#print" do + it "is a private method" do + Kernel.should have_private_instance_method(:print) + end +end + +describe "Kernel.print" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/printf_spec.rb b/spec/rubyspec/core/kernel/printf_spec.rb new file mode 100644 index 0000000000..b4c68fa449 --- /dev/null +++ b/spec/rubyspec/core/kernel/printf_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#printf" do + it "is a private method" do + Kernel.should have_private_instance_method(:printf) + end +end + +describe "Kernel.printf" do + + before :each do + @stdout = $stdout + @name = tmp("kernel_puts.txt") + $stdout = new_io @name + end + + after :each do + $stdout.close + $stdout = @stdout + rm_r @name + end + + it "writes to stdout when a string is the first argument" do + $stdout.should_receive(:write).with("string") + Kernel.printf("%s", "string") + end + + it "calls write on the first argument when it is not a string" do + object = mock('io') + object.should_receive(:write).with("string") + Kernel.printf(object, "%s", "string") + end +end diff --git a/spec/rubyspec/core/kernel/private_methods_spec.rb b/spec/rubyspec/core/kernel/private_methods_spec.rb new file mode 100644 index 0000000000..d0603c72b8 --- /dev/null +++ b/spec/rubyspec/core/kernel/private_methods_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Kernel#private_methods" do + it "returns a list of the names of privately accessible methods in the object" do + m = KernelSpecs::Methods.private_methods(false) + m.should include(:shichi) + m = KernelSpecs::Methods.new.private_methods(false) + m.should include(:juu_shi) + end + + it "returns a list of the names of privately accessible methods in the object and its ancestors and mixed-in modules" do + m = (KernelSpecs::Methods.private_methods(false) & KernelSpecs::Methods.private_methods) + + m.should include(:shichi) + m = KernelSpecs::Methods.new.private_methods + m.should include(:juu_shi) + end + + it "returns private methods mixed in to the metaclass" do + m = KernelSpecs::Methods.new + m.extend(KernelSpecs::Methods::MetaclassMethods) + m.private_methods.should include(:shoo) + end +end + +describe :kernel_private_methods_supers, shared: true do + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.private_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.private_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.private_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end +end + +describe :kernel_private_methods_with_falsy, shared: true do + it "returns a list of private methods in without its ancestors" do + ReflectSpecs::F.private_methods(@object).select{|m|/_pri\z/ =~ m}.sort.should == [:ds_pri, :fs_pri] + ReflectSpecs::F.new.private_methods(@object).should == [:f_pri] + end +end + +describe "Kernel#private_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_private_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_private_methods_supers, nil, true + end + + describe "when passed false" do + it_behaves_like :kernel_private_methods_with_falsy, nil, false + end + + describe "when passed nil" do + it_behaves_like :kernel_private_methods_with_falsy, nil, nil + end +end diff --git a/spec/rubyspec/core/kernel/proc_spec.rb b/spec/rubyspec/core/kernel/proc_spec.rb new file mode 100644 index 0000000000..4e4854f97d --- /dev/null +++ b/spec/rubyspec/core/kernel/proc_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/lambda', __FILE__) + +# The functionality of Proc objects is specified in core/proc + +describe "Kernel.proc" do + it "is a private method" do + Kernel.should have_private_instance_method(:proc) + end + + it "creates a proc-style Proc if given a literal block" do + l = proc { 42 } + l.lambda?.should be_false + end + + it "returned the passed Proc if given an existing Proc" do + some_lambda = lambda {} + some_lambda.lambda?.should be_true + l = proc(&some_lambda) + l.should equal(some_lambda) + l.lambda?.should be_true + end + + it_behaves_like(:kernel_lambda, :proc) + + it "returns from the creation site of the proc, not just the proc itself" do + @reached_end_of_method = nil + def test + proc { return }.call + @reached_end_of_method = true + end + test + @reached_end_of_method.should be_nil + end +end + +describe "Kernel#proc" do + it "uses the implicit block from an enclosing method" do + def some_method + proc + end + + prc = some_method { "hello" } + + prc.call.should == "hello" + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/protected_methods_spec.rb b/spec/rubyspec/core/kernel/protected_methods_spec.rb new file mode 100644 index 0000000000..2e09cead53 --- /dev/null +++ b/spec/rubyspec/core/kernel/protected_methods_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite + +# The reason why having include() is to show the specification explicitly. +# You should use have_protected_method() with the exception of this spec. +describe "Kernel#protected_methods" do + it "returns a list of the names of protected methods accessible in the object" do + KernelSpecs::Methods.protected_methods(false).sort.should include(:juu_ichi) + KernelSpecs::Methods.new.protected_methods(false).should include(:ku) + end + + it "returns a list of the names of protected methods accessible in the object and from its ancestors and mixed-in modules" do + l1 = KernelSpecs::Methods.protected_methods(false) + l2 = KernelSpecs::Methods.protected_methods + (l1 & l2).should include(:juu_ichi) + KernelSpecs::Methods.new.protected_methods.should include(:ku) + end + + it "returns methods mixed in to the metaclass" do + m = KernelSpecs::Methods.new + m.extend(KernelSpecs::Methods::MetaclassMethods) + m.protected_methods.should include(:nopeeking) + end +end + +describe :kernel_protected_methods_supers, shared: true do + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.protected_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.protected_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.protected_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end +end + +describe :kernel_protected_methods_with_falsy, shared: true do + it "returns a list of protected methods in without its ancestors" do + ReflectSpecs::F.protected_methods(@object).select{|m|/_pro\z/ =~ m}.sort.should == [:ds_pro, :fs_pro] + ReflectSpecs::F.new.protected_methods(@object).should == [:f_pro] + end +end + +describe "Kernel#protected_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_protected_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_protected_methods_supers, nil, true + end + + describe "when passed false" do + it_behaves_like :kernel_protected_methods_with_falsy, nil, false + end + + describe "when passed nil" do + it_behaves_like :kernel_protected_methods_with_falsy, nil, nil + end +end diff --git a/spec/rubyspec/core/kernel/public_method_spec.rb b/spec/rubyspec/core/kernel/public_method_spec.rb new file mode 100644 index 0000000000..c4a29d9192 --- /dev/null +++ b/spec/rubyspec/core/kernel/public_method_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/method', __FILE__) + +describe "Kernel#public_method" do + it_behaves_like(:kernel_method, :public_method) + + before :each do + @obj = KernelSpecs::A.new + end + + it "raises a NameError when called on a private method" do + @obj.send(:private_method).should == :private_method + lambda do + @obj.public_method(:private_method) + end.should raise_error(NameError) + end + + it "raises a NameError when called on a protected method" do + @obj.send(:protected_method).should == :protected_method + lambda do + @obj.public_method(:protected_method) + end.should raise_error(NameError) + end + + it "raises a NameError if we only repond_to_missing? method, true" do + obj = KernelSpecs::RespondViaMissing.new + lambda do + obj.public_method(:handled_privately) + end.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/kernel/public_methods_spec.rb b/spec/rubyspec/core/kernel/public_methods_spec.rb new file mode 100644 index 0000000000..b72775483c --- /dev/null +++ b/spec/rubyspec/core/kernel/public_methods_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Kernel#public_methods" do + it "returns a list of the names of publicly accessible methods in the object" do + KernelSpecs::Methods.public_methods(false).sort.should include(:hachi, + :ichi, :juu, :juu_ni, :roku, :san, :shi) + KernelSpecs::Methods.new.public_methods(false).sort.should include(:juu_san, :ni) + end + + it "returns a list of names without protected accessible methods in the object" do + KernelSpecs::Methods.public_methods(false).sort.should_not include(:juu_ichi) + KernelSpecs::Methods.new.public_methods(false).sort.should_not include(:ku) + end + + it "returns a list of the names of publicly accessible methods in the object and its ancestors and mixed-in modules" do + (KernelSpecs::Methods.public_methods(false) & KernelSpecs::Methods.public_methods).sort.should include( + :hachi, :ichi, :juu, :juu_ni, :roku, :san, :shi) + m = KernelSpecs::Methods.new.public_methods + m.should include(:ni, :juu_san) + end + + it "returns methods mixed in to the metaclass" do + m = KernelSpecs::Methods.new + m.extend(KernelSpecs::Methods::MetaclassMethods) + m.public_methods.should include(:peekaboo) + end + + it "returns public methods for immediates" do + 10.public_methods.should include(:divmod) + end +end + +describe :kernel_public_methods_supers, shared: true do + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.public_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.public_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.public_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end +end + +describe :kernel_public_methods_with_falsy, shared: true do + it "returns a list of public methods in without its ancestors" do + ReflectSpecs::F.public_methods(@object).select{|m|/_pub\z/ =~ m}.sort.should == [:ds_pub, :fs_pub] + ReflectSpecs::F.new.public_methods(@object).should == [:f_pub] + end +end + +describe "Kernel#public_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_public_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_public_methods_supers, nil, true + end + + describe "when passed false" do + it_behaves_like :kernel_public_methods_with_falsy, nil, false + end + + describe "when passed nil" do + it_behaves_like :kernel_public_methods_with_falsy, nil, nil + end +end diff --git a/spec/rubyspec/core/kernel/public_send_spec.rb b/spec/rubyspec/core/kernel/public_send_spec.rb new file mode 100644 index 0000000000..2eabbc7dc9 --- /dev/null +++ b/spec/rubyspec/core/kernel/public_send_spec.rb @@ -0,0 +1,108 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/basicobject/send', __FILE__) + +describe "Kernel#public_send" do + it "invokes the named public method" do + class KernelSpecs::Foo + def bar + 'done' + end + end + KernelSpecs::Foo.new.public_send(:bar).should == 'done' + end + + it "invokes the named alias of a public method" do + class KernelSpecs::Foo + def bar + 'done' + end + alias :aka :bar + end + KernelSpecs::Foo.new.public_send(:aka).should == 'done' + end + + it "raises a NoMethodError if the method is protected" do + class KernelSpecs::Foo + protected + def bar + 'done' + end + end + lambda { KernelSpecs::Foo.new.public_send(:bar)}.should raise_error(NoMethodError) + end + + it "raises a NoMethodError if the named method is private" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + end + lambda { + KernelSpecs::Foo.new.public_send(:bar) + }.should raise_error(NoMethodError) + end + + context 'called from own public method' do + before do + class << @receiver = Object.new + def call_protected_method + public_send :protected_method + end + + def call_private_method + public_send :private_method + end + + protected + + def protected_method + raise 'Should not called' + end + + private + + def private_method + raise 'Should not called' + end + end + end + + it "raises a NoMethodError if the method is protected" do + lambda { @receiver.call_protected_method }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError if the method is private" do + lambda { @receiver.call_private_method }.should raise_error(NoMethodError) + end + end + + it "raises a NoMethodError if the named method is an alias of a private method" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + alias :aka :bar + end + lambda { + KernelSpecs::Foo.new.public_send(:aka) + }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError if the named method is an alias of a protected method" do + class KernelSpecs::Foo + protected + def bar + 'done2' + end + alias :aka :bar + end + lambda { + KernelSpecs::Foo.new.public_send(:aka) + }.should raise_error(NoMethodError) + end + + it_behaves_like(:basicobject_send, :public_send) +end diff --git a/spec/rubyspec/core/kernel/putc_spec.rb b/spec/rubyspec/core/kernel/putc_spec.rb new file mode 100644 index 0000000000..1f5e9b6d63 --- /dev/null +++ b/spec/rubyspec/core/kernel/putc_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/io/putc', __FILE__) + +describe "Kernel#putc" do + it "is a private instance method" do + Kernel.should have_private_instance_method(:putc) + end +end + +describe "Kernel.putc" do + before :each do + @name = tmp("kernel_putc.txt") + @io = new_io @name + @io_object = @object + @stdout, $stdout = $stdout, @io + end + + after :each do + $stdout = @stdout + end + + it_behaves_like :io_putc, :putc_method, KernelSpecs +end + +describe "Kernel#putc" do + before :each do + @name = tmp("kernel_putc.txt") + @io = new_io @name + @io_object = @object + @stdout, $stdout = $stdout, @io + end + + after :each do + $stdout = @stdout + end + + it_behaves_like :io_putc, :putc_function, KernelSpecs +end diff --git a/spec/rubyspec/core/kernel/puts_spec.rb b/spec/rubyspec/core/kernel/puts_spec.rb new file mode 100644 index 0000000000..c5297f1cb2 --- /dev/null +++ b/spec/rubyspec/core/kernel/puts_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#puts" do + before :each do + @stdout = $stdout + @name = tmp("kernel_puts.txt") + $stdout = new_io @name + end + + after :each do + $stdout.close + $stdout = @stdout + rm_r @name + end + + it "is a private method" do + Kernel.should have_private_instance_method(:puts) + end + + it "delegates to $stdout.puts" do + $stdout.should_receive(:puts).with(:arg) + puts :arg + end +end + +describe "Kernel.puts" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/raise_spec.rb b/spec/rubyspec/core/kernel/raise_spec.rb new file mode 100644 index 0000000000..6efffd9366 --- /dev/null +++ b/spec/rubyspec/core/kernel/raise_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/kernel/raise', __FILE__) + +describe "Kernel#raise" do + it "is a private method" do + Kernel.should have_private_instance_method(:raise) + end +end + +describe "Kernel#raise" do + it_behaves_like :kernel_raise, :raise, Kernel +end + +describe "Kernel.raise" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/rand_spec.rb b/spec/rubyspec/core/kernel/rand_spec.rb new file mode 100644 index 0000000000..f52b5f75b5 --- /dev/null +++ b/spec/rubyspec/core/kernel/rand_spec.rb @@ -0,0 +1,139 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.rand" do + it "is a private method" do + Kernel.should have_private_instance_method(:rand) + end + + it "returns a float if no argument is passed" do + rand.should be_kind_of(Float) + end + + it "returns an integer for an integer argument" do + rand(77).should be_kind_of(Integer) + end + + it "returns an integer for a float argument greater than 1" do + rand(1.3).should be_kind_of(Integer) + end + + it "returns a float for an argument between -1 and 1" do + rand(-0.999).should be_kind_of(Float) + rand(-0.01).should be_kind_of(Float) + rand(0).should be_kind_of(Float) + rand(0.01).should be_kind_of(Float) + rand(0.999).should be_kind_of(Float) + end + + it "ignores the sign of the argument" do + [0, 1, 2, 3].should include(rand(-4)) + end + + it "never returns a value greater or equal to 1.0 with no arguments" do + 1000.times do + (0...1.0).should include(rand) + end + end + + it "never returns a value greater or equal to any passed in max argument" do + 1000.times do + (0...100).to_a.should include(rand(100)) + end + end + + it "calls to_int on its argument" do + l = mock('limit') + l.should_receive(:to_int).and_return 7 + + rand l + end + + context "given an exclusive range" do + it "returns an Integer between the two Integers" do + 1000.times do + x = rand(4...6) + x.should be_kind_of(Integer) + (4...6).should include(x) + end + end + + it "returns a Float between the given Integer and Float" do + 1000.times do + x = rand(4...6.5) + x.should be_kind_of(Float) + (4...6.5).should include(x) + end + end + + it "returns a Float between the given Float and Integer" do + 1000.times do + x = rand(3.5...6) + x.should be_kind_of(Float) + (3.5...6).should include(x) + end + end + + it "returns a Float between the two given Floats" do + 1000.times do + x = rand(3.5...6.5) + x.should be_kind_of(Float) + (3.5...6.5).should include(x) + end + end + end + + context "given an inclusive range" do + it "returns an Integer between the two Integers" do + 1000.times do + x = rand(4..6) + x.should be_kind_of(Integer) + (4..6).should include(x) + end + end + + it "returns a Float between the given Integer and Float" do + 1000.times do + x = rand(4..6.5) + x.should be_kind_of(Float) + (4..6.5).should include(x) + end + end + + it "returns a Float between the given Float and Integer" do + 1000.times do + x = rand(3.5..6) + x.should be_kind_of(Float) + (3.5..6).should include(x) + end + end + + it "returns a Float between the two given Floats" do + 1000.times do + x = rand(3.5..6.5) + x.should be_kind_of(Float) + (3.5..6.5).should include(x) + end + end + end + + it "returns a numeric for an range argument where max is < 1" do + rand(0.25..0.75).should be_kind_of(Numeric) + end + + it "returns nil when range is backwards" do + rand(1..0).should be_nil + end + + it "returns the range start/end when Float range is 0" do + rand(1.0..1.0).should eql(1.0) + end + + it "returns the range start/end when Integer range is 0" do + rand(42..42).should eql(42) + end +end + +describe "Kernel#rand" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/readline_spec.rb b/spec/rubyspec/core/kernel/readline_spec.rb new file mode 100644 index 0000000000..c69eee0726 --- /dev/null +++ b/spec/rubyspec/core/kernel/readline_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#readline" do + it "is a private method" do + Kernel.should have_private_instance_method(:readline) + end +end + +describe "Kernel.readline" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/readlines_spec.rb b/spec/rubyspec/core/kernel/readlines_spec.rb new file mode 100644 index 0000000000..d5e07f8f75 --- /dev/null +++ b/spec/rubyspec/core/kernel/readlines_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#readlines" do + it "is a private method" do + Kernel.should have_private_instance_method(:readlines) + end +end + +describe "Kernel.readlines" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/remove_instance_variable_spec.rb b/spec/rubyspec/core/kernel/remove_instance_variable_spec.rb new file mode 100644 index 0000000000..6a9f78b9bc --- /dev/null +++ b/spec/rubyspec/core/kernel/remove_instance_variable_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_remove_instance_variable, shared: true do + it "returns the instance variable's value" do + value = @instance.send :remove_instance_variable, @object + value.should == "hello" + end + + it "removes the instance variable" do + @instance.send :remove_instance_variable, @object + @instance.instance_variable_defined?(@object).should be_false + end +end + +describe "Kernel#remove_instance_variable" do + before do + @instance = KernelSpecs::InstanceVariable.new + end + + it "is a public method" do + Kernel.should have_public_instance_method(:remove_instance_variable, false) + end + + it "raises a NameError if the instance variable is not defined" do + lambda do + @instance.send :remove_instance_variable, :@unknown + end.should raise_error(NameError) + end + + it "raises a NameError if the argument is not a valid instance variable name" do + lambda do + @instance.send :remove_instance_variable, :"@0" + end.should raise_error(NameError) + end + + it "raises a TypeError if passed an Object not defining #to_str" do + lambda do + obj = mock("kernel remove_instance_variable") + @instance.send :remove_instance_variable, obj + end.should raise_error(TypeError) + end + + describe "when passed a String" do + it_behaves_like :kernel_remove_instance_variable, nil, "@greeting" + end + + describe "when passed a Symbol" do + it_behaves_like :kernel_remove_instance_variable, nil, :@greeting + end + + describe "when passed an Object" do + it "calls #to_str to convert the argument" do + name = mock("kernel remove_instance_variable") + name.should_receive(:to_str).and_return("@greeting") + @instance.send :remove_instance_variable, name + end + end +end diff --git a/spec/rubyspec/core/kernel/require_relative_spec.rb b/spec/rubyspec/core/kernel/require_relative_spec.rb new file mode 100644 index 0000000000..04cf5444d2 --- /dev/null +++ b/spec/rubyspec/core/kernel/require_relative_spec.rb @@ -0,0 +1,349 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/code_loading', __FILE__) + +describe "Kernel#require_relative with a relative path" do + it "needs to be reviewed for spec completeness" + + before :each do + CodeLoadingSpecs.spec_setup + @dir = "../../fixtures/code" + @abs_dir = File.realpath(@dir, File.dirname(__FILE__)) + @path = "#{@dir}/load_fixture.rb" + @abs_path = File.realpath(@path, File.dirname(__FILE__)) + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + platform_is_not :windows do + describe "when file is a symlink" do + before :each do + @link = tmp("symlink.rb", false) + @real_path = "#{@abs_dir}/symlink/symlink1.rb" + File.symlink(@real_path, @link) + end + + after :each do + rm_r @link + end + + it "loads a path relative to current file" do + require_relative(@link).should be_true + ScratchPad.recorded.should == [:loaded] + end + end + end + + it "loads a path relative to the current file" do + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file defining many methods" do + require_relative("#{@dir}/methods_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a LoadError if the file does not exist" do + lambda { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "raises a LoadError if basepath does not exist" do + lambda { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError) + end + + it "stores the missing path in a LoadError object" do + path = "#{@dir}/nonexistent.rb" + + lambda { + require_relative(path) + }.should(raise_error(LoadError) { |e| + e.path.should == File.expand_path(path, @abs_dir) + }) + end + + it "calls #to_str on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a TypeError if argument does not respond to #to_str" do + lambda { require_relative(nil) }.should raise_error(TypeError) + lambda { require_relative(42) }.should raise_error(TypeError) + lambda { + require_relative([@path,@path]) + }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that has #to_s but not #to_str" do + name = mock("load_fixture.rb mock") + name.stub!(:to_s).and_return(@path) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_str does not return a String" do + name = mock("#to_str returns nil") + name.should_receive(:to_str).at_least(1).times.and_return(nil) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "calls #to_path on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_path).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_str on non-String objects returned by #to_path" do + name = mock("load_fixture.rb mock") + to_path = mock("load_fixture_rb #to_path mock") + name.should_receive(:to_path).and_return(to_path) + to_path.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + describe "(file extensions)" do + it "loads a .rb extensioned file when passed a non-extensioned path" do + require_relative("#{@dir}/load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll" + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a .rb extensioned file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb" + require_relative("#{@dir}/load_fixture").should be_false + ScratchPad.recorded.should == [] + end + + it "loads a .rb extensioned file when passed a non-.rb extensioned path" do + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll" + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb" + require_relative("#{@dir}/load_fixture.ext").should be_false + ScratchPad.recorded.should == [] + end + end + + describe "($LOADED_FEATURES)" do + it "stores an absolute path" do + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + end + + it "does not store the path if the load fails" do + saved_loaded_features = $LOADED_FEATURES.dup + lambda { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError) + $LOADED_FEATURES.should == saved_loaded_features + end + + it "does not load an absolute path that is already stored" do + $LOADED_FEATURES << @abs_path + require_relative(@path).should be_false + ScratchPad.recorded.should == [] + end + + it "adds the suffix of the resolved filename" do + require_relative("#{@dir}/load_fixture").should be_true + $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb") + end + + it "loads a path for a file already loaded with a relative path" do + $LOAD_PATH << File.expand_path(@dir) + $LOADED_FEATURES << "load_fixture.rb" << "load_fixture" + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + ScratchPad.recorded.should == [:loaded] + end + end +end + +describe "Kernel#require_relative with an absolute path" do + it "needs to be reviewed for spec completeness" + + before :each do + CodeLoadingSpecs.spec_setup + @dir = File.expand_path "../../fixtures/code", File.dirname(__FILE__) + @abs_dir = @dir + @path = File.join @dir, "load_fixture.rb" + @abs_path = @path + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "loads a path relative to the current file" do + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file defining many methods" do + require_relative("#{@dir}/methods_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a LoadError if the file does not exist" do + lambda { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "raises a LoadError if basepath does not exist" do + lambda { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError) + end + + it "stores the missing path in a LoadError object" do + path = "#{@dir}/nonexistent.rb" + + lambda { + require_relative(path) + }.should(raise_error(LoadError) { |e| + e.path.should == File.expand_path(path, @abs_dir) + }) + end + + it "calls #to_str on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a TypeError if argument does not respond to #to_str" do + lambda { require_relative(nil) }.should raise_error(TypeError) + lambda { require_relative(42) }.should raise_error(TypeError) + lambda { + require_relative([@path,@path]) + }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that has #to_s but not #to_str" do + name = mock("load_fixture.rb mock") + name.stub!(:to_s).and_return(@path) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_str does not return a String" do + name = mock("#to_str returns nil") + name.should_receive(:to_str).at_least(1).times.and_return(nil) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "calls #to_path on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_path).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_str on non-String objects returned by #to_path" do + name = mock("load_fixture.rb mock") + to_path = mock("load_fixture_rb #to_path mock") + name.should_receive(:to_path).and_return(to_path) + to_path.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + describe "(file extensions)" do + it "loads a .rb extensioned file when passed a non-extensioned path" do + require_relative("#{@dir}/load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll" + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a .rb extensioned file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb" + require_relative("#{@dir}/load_fixture").should be_false + ScratchPad.recorded.should == [] + end + + it "loads a .rb extensioned file when passed a non-.rb extensioned path" do + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll" + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb" + require_relative("#{@dir}/load_fixture.ext").should be_false + ScratchPad.recorded.should == [] + end + end + + describe "($LOAD_FEATURES)" do + it "stores an absolute path" do + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + end + + it "does not store the path if the load fails" do + saved_loaded_features = $LOADED_FEATURES.dup + lambda { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError) + $LOADED_FEATURES.should == saved_loaded_features + end + + it "does not load an absolute path that is already stored" do + $LOADED_FEATURES << @abs_path + require_relative(@path).should be_false + ScratchPad.recorded.should == [] + end + + it "adds the suffix of the resolved filename" do + require_relative("#{@dir}/load_fixture").should be_true + $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb") + end + + it "loads a path for a file already loaded with a relative path" do + $LOAD_PATH << File.expand_path(@dir) + $LOADED_FEATURES << "load_fixture.rb" << "load_fixture" + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + ScratchPad.recorded.should == [:loaded] + end + end +end diff --git a/spec/rubyspec/core/kernel/require_spec.rb b/spec/rubyspec/core/kernel/require_spec.rb new file mode 100644 index 0000000000..75cea7565e --- /dev/null +++ b/spec/rubyspec/core/kernel/require_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/code_loading', __FILE__) +require File.expand_path('../shared/require', __FILE__) + +describe "Kernel#require" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + # if this fails, update your rubygems + it "is a private method" do + Kernel.should have_private_instance_method(:require) + end + + it_behaves_like :kernel_require_basic, :require, CodeLoadingSpecs::Method.new + + it_behaves_like :kernel_require, :require, CodeLoadingSpecs::Method.new +end + +describe "Kernel.require" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it_behaves_like :kernel_require_basic, :require, Kernel + + it_behaves_like :kernel_require, :require, Kernel +end diff --git a/spec/rubyspec/core/kernel/respond_to_missing_spec.rb b/spec/rubyspec/core/kernel/respond_to_missing_spec.rb new file mode 100644 index 0000000000..f116f19dbd --- /dev/null +++ b/spec/rubyspec/core/kernel/respond_to_missing_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#respond_to_missing?" do + before :each do + @a = KernelSpecs::A.new + end + + it "is a private method" do + Kernel.should have_private_instance_method(:respond_to_missing?, false) + end + + it "is only an instance method" do + Kernel.method(:respond_to_missing?).owner.should == Kernel + end + + it "is not called when #respond_to? would return true" do + obj = mock('object') + obj.stub!(:glark) + obj.should_not_receive(:respond_to_missing?) + obj.respond_to?(:glark).should be_true + end + + it "is called with a 2nd argument of false when #respond_to? is" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false) + obj.respond_to?(:undefined_method, false) + end + + it "is called a 2nd argument of false when #respond_to? is called with only 1 argument" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false) + obj.respond_to?(:undefined_method) + end + + it "is called with true as the second argument when #respond_to? is" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, true) + obj.respond_to?(:undefined_method, true) + end + + it "is called when #respond_to? would return false" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false) + obj.respond_to?(:undefined_method) + end + + it "causes #respond_to? to return true if called and not returning false" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(:glark) + obj.respond_to?(:undefined_method).should be_true + end + + it "causes #respond_to? to return false if called and returning false" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(false) + obj.respond_to?(:undefined_method).should be_false + end + + it "causes #respond_to? to return false if called and returning nil" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(nil) + obj.respond_to?(:undefined_method).should be_false + end + + it "isn't called when obj responds to the given public method" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:pub_method).should be_true + end + + it "isn't called when obj responds to the given public method, include_private = true" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:pub_method, true).should be_true + end + + it "is called when obj responds to the given protected method, include_private = false" do + @a.should_receive(:respond_to_missing?) + @a.respond_to?(:protected_method, false).should be_false + end + + it "isn't called when obj responds to the given protected method, include_private = true" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:protected_method, true).should be_true + end + + it "is called when obj responds to the given private method, include_private = false" do + @a.should_receive(:respond_to_missing?).with(:private_method, false) + @a.respond_to?(:private_method) + end + + it "isn't called when obj responds to the given private method, include_private = true" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:private_method, true).should be_true + end + + it "is called for missing class methods" do + @a.class.should_receive(:respond_to_missing?).with(:oOoOoO, false) + @a.class.respond_to?(:oOoOoO) + end +end diff --git a/spec/rubyspec/core/kernel/respond_to_spec.rb b/spec/rubyspec/core/kernel/respond_to_spec.rb new file mode 100644 index 0000000000..aa4379277b --- /dev/null +++ b/spec/rubyspec/core/kernel/respond_to_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#respond_to?" do + before :each do + @a = KernelSpecs::A.new + end + + it "is a public method" do + Kernel.should have_public_instance_method(:respond_to?, false) + end + + it "is only an instance method" do + Kernel.method(:respond_to?).owner.should == Kernel + end + + it "returns false if the given method was undefined" do + @a.respond_to?(:undefed_method).should == false + @a.respond_to?("undefed_method").should == false + end + + it "returns true if obj responds to the given public method" do + @a.respond_to?(:pub_method).should == true + @a.respond_to?("pub_method").should == true + end + + it "throws a type error if argument can't be coerced into a Symbol" do + lambda { @a.respond_to?(Object.new) }.should raise_error(TypeError) + end + + it "returns false if obj responds to the given protected method" do + @a.respond_to?(:protected_method).should == false + @a.respond_to?("protected_method").should == false + end + + it "returns false if obj responds to the given private method" do + @a.respond_to?(:private_method).should == false + @a.respond_to?("private_method").should == false + end + + it "returns true if obj responds to the given protected method (include_private = true)" do + @a.respond_to?(:protected_method, true).should == true + @a.respond_to?("protected_method", true).should == true + end + + it "returns false if obj responds to the given protected method (include_private = false)" do + @a.respond_to?(:protected_method, false).should == false + @a.respond_to?("protected_method", false).should == false + end + + it "returns false even if obj responds to the given private method (include_private = false)" do + @a.respond_to?(:private_method, false).should == false + @a.respond_to?("private_method", false).should == false + end + + it "returns true if obj responds to the given private method (include_private = true)" do + @a.respond_to?(:private_method, true).should == true + @a.respond_to?("private_method", true).should == true + end + + it "does not change method visibility when finding private method" do + KernelSpecs::VisibilityChange.respond_to?(:new, false).should == false + KernelSpecs::VisibilityChange.respond_to?(:new, true).should == true + lambda { KernelSpecs::VisibilityChange.new }.should raise_error(NoMethodError) + end + + it "indicates if an object responds to a particular message" do + class KernelSpecs::Foo; def bar; 'done'; end; end + KernelSpecs::Foo.new.respond_to?(:bar).should == true + KernelSpecs::Foo.new.respond_to?(:invalid_and_silly_method_name).should == false + end + +end diff --git a/spec/rubyspec/core/kernel/select_spec.rb b/spec/rubyspec/core/kernel/select_spec.rb new file mode 100644 index 0000000000..c37c621ae7 --- /dev/null +++ b/spec/rubyspec/core/kernel/select_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#select" do + it "is a private method" do + Kernel.should have_private_instance_method(:select) + end +end + +describe "Kernel.select" do + it "needs to be reviewed for spec completeness" + + it 'does not block when timeout is 0' do + IO.pipe do |read, write| + IO.select([read], [], [], 0).should == nil + write.write 'data' + IO.select([read], [], [], 0).should == [[read], [], []] + end + end +end diff --git a/spec/rubyspec/core/kernel/send_spec.rb b/spec/rubyspec/core/kernel/send_spec.rb new file mode 100644 index 0000000000..8afd16e97c --- /dev/null +++ b/spec/rubyspec/core/kernel/send_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/basicobject/send', __FILE__) + +describe "Kernel#send" do + it "invokes the named public method" do + class KernelSpecs::Foo + def bar + 'done' + end + end + KernelSpecs::Foo.new.send(:bar).should == 'done' + end + + it "invokes the named alias of a public method" do + class KernelSpecs::Foo + def bar + 'done' + end + alias :aka :bar + end + KernelSpecs::Foo.new.send(:aka).should == 'done' + end + + it "invokes the named protected method" do + class KernelSpecs::Foo + protected + def bar + 'done' + end + end + KernelSpecs::Foo.new.send(:bar).should == 'done' + end + + it "invokes the named private method" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + end + KernelSpecs::Foo.new.send(:bar).should == 'done2' + end + + it "invokes the named alias of a private method" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + alias :aka :bar + end + KernelSpecs::Foo.new.send(:aka).should == 'done2' + end + + it "invokes the named alias of a protected method" do + class KernelSpecs::Foo + protected + def bar + 'done2' + end + alias :aka :bar + end + KernelSpecs::Foo.new.send(:aka).should == 'done2' + end + + it_behaves_like(:basicobject_send, :send) +end diff --git a/spec/rubyspec/core/kernel/set_trace_func_spec.rb b/spec/rubyspec/core/kernel/set_trace_func_spec.rb new file mode 100644 index 0000000000..5dafa8b5d3 --- /dev/null +++ b/spec/rubyspec/core/kernel/set_trace_func_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#set_trace_func" do + it "is a private method" do + Kernel.should have_private_instance_method(:set_trace_func) + end +end + +describe "Kernel.set_trace_func" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/shared/dup_clone.rb b/spec/rubyspec/core/kernel/shared/dup_clone.rb new file mode 100644 index 0000000000..320da8fc38 --- /dev/null +++ b/spec/rubyspec/core/kernel/shared/dup_clone.rb @@ -0,0 +1,125 @@ +class ObjectSpecDup + def initialize() + @obj = :original + end + + attr_accessor :obj +end + +class ObjectSpecDupInitCopy + def initialize() + @obj = :original + end + + attr_accessor :obj, :original + + def initialize_copy(original) + @obj = :init_copy + @original = original + end + + private :initialize_copy +end + +describe :kernel_dup_clone, shared: true do + it "returns a new object duplicated from the original" do + o = ObjectSpecDup.new + o2 = ObjectSpecDup.new + + o.obj = 10 + + o3 = o.send(@method) + + o3.obj.should == 10 + o2.obj.should == :original + end + + it "produces a shallow copy, contained objects are not recursively dupped" do + o = ObjectSpecDup.new + array = [1, 2] + o.obj = array + + o2 = o.send(@method) + o2.obj.should equal(o.obj) + end + + it "calls #initialize_copy on the NEW object if available, passing in original object" do + o = ObjectSpecDupInitCopy.new + o2 = o.send(@method) + + o.obj.should == :original + o2.obj.should == :init_copy + o2.original.should equal(o) + end + + it "preserves tainted state from the original" do + o = ObjectSpecDupInitCopy.new + o2 = o.send(@method) + o.taint + o3 = o.send(@method) + + o2.tainted?.should == false + o3.tainted?.should == true + end + + it "does not preserve the object_id" do + o1 = ObjectSpecDupInitCopy.new + old_object_id = o1.object_id + o2 = o1.send(@method) + o2.object_id.should_not == old_object_id + end + + it "preserves untrusted state from the original" do + o = ObjectSpecDupInitCopy.new + o2 = o.send(@method) + o.untrust + o3 = o.send(@method) + + o2.untrusted?.should == false + o3.untrusted?.should == true + end + + ruby_version_is ''...'2.4' do + it "raises a TypeError for NilClass" do + lambda { nil.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for TrueClass" do + lambda { true.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for FalseClass" do + lambda { false.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for Fixnum" do + lambda { 1.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for Symbol" do + lambda { :my_symbol.send(@method) }.should raise_error(TypeError) + end + end + + ruby_version_is '2.4' do + it "returns nil for NilClass" do + nil.send(@method).should == nil + end + + it "returns true for TrueClass" do + true.send(@method).should == true + end + + it "returns false for FalseClass" do + false.send(@method).should == false + end + + it "returns the same Integer for Integer" do + 1.send(@method).should == 1 + end + + it "returns the same Symbol for Symbol" do + :my_symbol.send(@method).should == :my_symbol + end + end +end diff --git a/spec/rubyspec/core/kernel/shared/kind_of.rb b/spec/rubyspec/core/kernel/shared/kind_of.rb new file mode 100644 index 0000000000..e99f46aa14 --- /dev/null +++ b/spec/rubyspec/core/kernel/shared/kind_of.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :kernel_kind_of, shared: true do + before :each do + @o = KernelSpecs::KindaClass.new + end + + it "returns true if given class is the object's class" do + @o.send(@method, KernelSpecs::KindaClass).should == true + end + + it "returns true if given class is an ancestor of the object's class" do + @o.send(@method, KernelSpecs::AncestorClass).should == true + @o.send(@method, String).should == true + @o.send(@method, Object).should == true + end + + it "returns false if the given class is not object's class nor an ancestor" do + @o.send(@method, Array).should == false + end + + it "returns true if given a Module that is included in object's class" do + @o.send(@method, KernelSpecs::MyModule).should == true + end + + it "returns true if given a Module that is included one of object's ancestors only" do + @o.send(@method, KernelSpecs::AncestorModule).should == true + end + + it "returns true if given a Module that object has been extended with" do + @o.send(@method, KernelSpecs::MyExtensionModule).should == true + end + + it "returns false if given a Module not included in object's class nor ancestors" do + @o.send(@method, KernelSpecs::SomeOtherModule).should == false + end + + it "raises a TypeError if given an object that is not a Class nor a Module" do + lambda { @o.send(@method, 1) }.should raise_error(TypeError) + lambda { @o.send(@method, 'KindaClass') }.should raise_error(TypeError) + lambda { @o.send(@method, :KindaClass) }.should raise_error(TypeError) + lambda { @o.send(@method, Object.new) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/kernel/shared/lambda.rb b/spec/rubyspec/core/kernel/shared/lambda.rb new file mode 100644 index 0000000000..bebb111c43 --- /dev/null +++ b/spec/rubyspec/core/kernel/shared/lambda.rb @@ -0,0 +1,9 @@ +describe :kernel_lambda, shared: true do + it "returns a Proc object" do + send(@method) { true }.kind_of?(Proc).should == true + end + + it "raises an ArgumentError when no block is given" do + lambda { send(@method) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/kernel/shared/load.rb b/spec/rubyspec/core/kernel/shared/load.rb new file mode 100644 index 0000000000..0ce7d58d2c --- /dev/null +++ b/spec/rubyspec/core/kernel/shared/load.rb @@ -0,0 +1,139 @@ +describe :kernel_load, shared: true do + before :each do + CodeLoadingSpecs.spec_setup + @path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "loads a non-extensioned file as a Ruby source file" do + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:no_ext] + end + + it "loads a non .rb extensioned file as a Ruby source file" do + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:no_rb_ext] + end + + it "loads from the current working directory" do + Dir.chdir CODE_LOADING_DIR do + @object.load("load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + end + + it "loads a file that recursively requires itself" do + path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR + -> { + $VERBOSE = true + @object.load(path).should be_true + }.should complain(/circular require considered harmful/) + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads a file that recursively loads itself" do + path = File.expand_path "recursive_load_fixture.rb", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads a file each time the method is called" do + @object.load(@path).should be_true + @object.load(@path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads a file even when the name appears in $LOADED_FEATURES" do + $LOADED_FEATURES << @path + @object.load(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file that has been loaded by #require" do + @object.require(@path).should be_true + @object.load(@path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads file even after $LOAD_PATH change" do + $LOAD_PATH << CODE_LOADING_DIR + @object.load("load_fixture.rb").should be_true + $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem" + @object.load("load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded, :loaded_gem] + end + + it "does not cause #require with the same path to fail" do + @object.load(@path).should be_true + @object.require(@path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "does not add the loaded path to $LOADED_FEATURES" do + saved_loaded_features = $LOADED_FEATURES.dup + @object.load(@path).should be_true + $LOADED_FEATURES.should == saved_loaded_features + end + + it "raises a LoadError if passed a non-extensioned path that does not exist but a .rb extensioned path does exist" do + path = File.expand_path "load_ext_fixture", CODE_LOADING_DIR + lambda { @object.load(path) }.should raise_error(LoadError) + end + + describe "when passed true for 'wrap'" do + it "loads from an existing path" do + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true).should be_true + end + + it "sets the enclosing scope to an anonymous module" do + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + + Object.const_defined?(:LoadSpecWrap).should be_false + end + + it "allows referencing outside namespaces" do + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + + ScratchPad.recorded.first.should be_an_instance_of(Class) + end + + describe "with top-level methods" do + before :each do + path = File.expand_path "load_wrap_method_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + end + + it "allows calling top-level methods" do + ScratchPad.recorded.last.should == :load_wrap_loaded + end + + it "does not pollute the receiver" do + lambda { @object.send(:top_level_method) }.should raise_error(NameError) + end + end + end + + describe "(shell expansion)" do + before :each do + @env_home = ENV["HOME"] + ENV["HOME"] = CODE_LOADING_DIR + end + + after :each do + ENV["HOME"] = @env_home + end + + it "expands a tilde to the HOME environment variable as the path to load" do + @object.require("~/load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + end +end diff --git a/spec/rubyspec/core/kernel/shared/method.rb b/spec/rubyspec/core/kernel/shared/method.rb new file mode 100644 index 0000000000..1566c6ab09 --- /dev/null +++ b/spec/rubyspec/core/kernel/shared/method.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :kernel_method, shared: true do + it "returns a method object for a valid method" do + class KernelSpecs::Foo; def bar; 'done'; end; end + m = KernelSpecs::Foo.new.send(@method, :bar) + m.should be_an_instance_of Method + m.call.should == 'done' + end + + it "returns a method object for a valid singleton method" do + class KernelSpecs::Foo; def self.bar; 'class done'; end; end + m = KernelSpecs::Foo.send(@method, :bar) + m.should be_an_instance_of Method + m.call.should == 'class done' + end + + it "returns a method object if we repond_to_missing? method" do + m = KernelSpecs::RespondViaMissing.new.send(@method, :handled_publicly) + m.should be_an_instance_of Method + m.call(42).should == "Done handled_publicly([42])" + end + + it "raises a NameError for an invalid method name" do + class KernelSpecs::Foo; def bar; 'done'; end; end + lambda { + KernelSpecs::Foo.new.send(@method, :invalid_and_silly_method_name) + }.should raise_error(NameError) + end + + it "raises a NameError for an invalid singleton method name" do + class KernelSpecs::Foo; def self.bar; 'done'; end; end + lambda { KernelSpecs::Foo.send(@method, :baz) }.should raise_error(NameError) + end + + it "changes the method called for super on a target aliased method" do + c1 = Class.new do + def a; 'a'; end + def b; 'b'; end + end + c2 = Class.new(c1) do + def a; super; end + alias b a + end + + c2.new.a.should == 'a' + c2.new.b.should == 'a' + c2.new.send(@method, :b).call.should == 'a' + end +end diff --git a/spec/rubyspec/core/kernel/shared/require.rb b/spec/rubyspec/core/kernel/shared/require.rb new file mode 100644 index 0000000000..3296c7f42a --- /dev/null +++ b/spec/rubyspec/core/kernel/shared/require.rb @@ -0,0 +1,703 @@ +describe :kernel_require_basic, shared: true do + describe "(path resolution)" do + it "loads an absolute path" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + @object.send(@method, path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a non-canonical absolute path" do + path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb" + @object.send(@method, path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file defining many methods" do + path = File.expand_path "methods_fixture.rb", CODE_LOADING_DIR + @object.send(@method, path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a LoadError if the file does not exist" do + path = File.expand_path "nonexistent.rb", CODE_LOADING_DIR + File.exist?(path).should be_false + lambda { @object.send(@method, path) }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + # Can't make a file unreadable on these platforms + platform_is_not :windows, :cygwin do + describe "with an unreadable file" do + before :each do + @path = tmp("unreadable_file.rb") + touch @path + File.chmod 0000, @path + end + + after :each do + File.chmod 0666, @path + rm_r @path + end + + it "raises a LoadError" do + File.exist?(@path).should be_true + lambda { @object.send(@method, @path) }.should raise_error(LoadError) + end + end + end + + it "calls #to_str on non-String objects" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + name = mock("load_fixture.rb mock") + name.should_receive(:to_str).and_return(path) + @object.send(@method, name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a TypeError if passed nil" do + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a Fixnum" do + lambda { @object.send(@method, 42) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an Array" do + lambda { @object.send(@method, []) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that does not provide #to_str" do + lambda { @object.send(@method, mock("not a filename")) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that has #to_s but not #to_str" do + name = mock("load_fixture.rb mock") + name.stub!(:to_s).and_return("load_fixture.rb") + $LOAD_PATH << "." + Dir.chdir CODE_LOADING_DIR do + lambda { @object.send(@method, name) }.should raise_error(TypeError) + end + end + + it "raises a TypeError if #to_str does not return a String" do + name = mock("#to_str returns nil") + name.should_receive(:to_str).at_least(1).times.and_return(nil) + lambda { @object.send(@method, name) }.should raise_error(TypeError) + end + + it "calls #to_path on non-String objects" do + name = mock("load_fixture.rb mock") + name.stub!(:to_path).and_return("load_fixture.rb") + $LOAD_PATH << "." + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, name).should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_path on a String" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + str = mock("load_fixture.rb mock") + str.should_receive(:to_path).and_return(path) + @object.send(@method, str).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_str on non-String objects returned by #to_path" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + name = mock("load_fixture.rb mock") + to_path = mock("load_fixture_rb #to_path mock") + name.should_receive(:to_path).and_return(to_path) + to_path.should_receive(:to_str).and_return(path) + @object.send(@method, name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + # "http://redmine.ruby-lang.org/issues/show/2578" + it "loads a ./ relative path from the current working directory with empty $LOAD_PATH" do + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "./load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a ../ relative path from the current working directory with empty $LOAD_PATH" do + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "../code/load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a ./ relative path from the current working directory with non-empty $LOAD_PATH" do + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "./load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a ../ relative path from the current working directory with non-empty $LOAD_PATH" do + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "../code/load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a non-canonical path from the current working directory with non-empty $LOAD_PATH" do + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "../code/../code/load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "resolves a filename against $LOAD_PATH entries" do + $LOAD_PATH << CODE_LOADING_DIR + @object.send(@method, "load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not require file twice after $LOAD_PATH change" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture.rb").should be_true + $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem" + @object.require("load_fixture.rb").should be_false + ScratchPad.recorded.should == [:loaded] + end + + it "does not resolve a ./ relative path against $LOAD_PATH entries" do + $LOAD_PATH << CODE_LOADING_DIR + lambda do + @object.send(@method, "./load_fixture.rb") + end.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "does not resolve a ../ relative path against $LOAD_PATH entries" do + $LOAD_PATH << CODE_LOADING_DIR + lambda do + @object.send(@method, "../code/load_fixture.rb") + end.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "resolves a non-canonical path against $LOAD_PATH entries" do + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.send(@method, "code/../code/load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a path with duplicate path separators" do + $LOAD_PATH << "." + sep = File::Separator + File::Separator + path = ["..", "code", "load_fixture.rb"].join(sep) + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, path).should be_true + end + ScratchPad.recorded.should == [:loaded] + end + end +end + +describe :kernel_require, shared: true do + describe "(path resolution)" do + # For reference see [ruby-core:24155] in which matz confirms this feature is + # intentional for security reasons. + it "does not load a bare filename unless the current working directory is in $LOAD_PATH" do + Dir.chdir CODE_LOADING_DIR do + lambda { @object.require("load_fixture.rb") }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + end + + it "does not load a relative path unless the current working directory is in $LOAD_PATH" do + Dir.chdir File.dirname(CODE_LOADING_DIR) do + lambda do + @object.require("code/load_fixture.rb") + end.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + end + + it "loads a file that recursively requires itself" do + path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR + -> { + $VERBOSE = true + @object.require(path).should be_true + }.should complain(/circular require considered harmful/) + ScratchPad.recorded.should == [:loaded] + end + end + + describe "(non-extensioned path)" do + before :each do + a = File.expand_path "a", CODE_LOADING_DIR + b = File.expand_path "b", CODE_LOADING_DIR + $LOAD_PATH.replace [a, b] + end + + it "loads a .rb extensioned file when a C-extension file exists on an earlier load path" do + @object.require("load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + end + + describe "(file extensions)" do + it "loads a .rb extensioned file when passed a non-extensioned path" do + path = File.expand_path "load_fixture", CODE_LOADING_DIR + File.exist?(path).should be_true + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.bundle", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.dylib", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.so", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.dll", CODE_LOADING_DIR) + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a .rb extensioned file is already loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.rb", CODE_LOADING_DIR) + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.require(path).should be_false + ScratchPad.recorded.should == [] + end + + it "loads a .rb extensioned file when passed a non-.rb extensioned path" do + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + File.exist?(path).should be_true + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.ext.bundle", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.ext.dylib", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.ext.so", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.ext.dll", CODE_LOADING_DIR) + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.ext.rb", CODE_LOADING_DIR) + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + @object.require(path).should be_false + ScratchPad.recorded.should == [] + end + end + + describe "($LOADED_FEATURES)" do + before :each do + @path = File.expand_path("load_fixture.rb", CODE_LOADING_DIR) + end + + it "stores an absolute path" do + @object.require(@path).should be_true + $LOADED_FEATURES.should include(@path) + end + + it "does not store the path if the load fails" do + $LOAD_PATH << CODE_LOADING_DIR + saved_loaded_features = $LOADED_FEATURES.dup + lambda { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError) + $LOADED_FEATURES.should == saved_loaded_features + end + + it "does not load an absolute path that is already stored" do + $LOADED_FEATURES << @path + @object.require(@path).should be_false + ScratchPad.recorded.should == [] + end + + it "does not load a ./ relative path that is already stored" do + $LOADED_FEATURES << "./load_fixture.rb" + Dir.chdir CODE_LOADING_DIR do + @object.require("./load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + it "does not load a ../ relative path that is already stored" do + $LOADED_FEATURES << "../load_fixture.rb" + Dir.chdir CODE_LOADING_DIR do + @object.require("../load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + it "does not load a non-canonical path that is already stored" do + $LOADED_FEATURES << "code/../code/load_fixture.rb" + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.require("code/../code/load_fixture.rb").should be_false + ScratchPad.recorded.should == [] + end + + it "respects being replaced with a new array" do + prev = $LOADED_FEATURES.dup + + @object.require(@path).should be_true + $LOADED_FEATURES.should include(@path) + + $LOADED_FEATURES.replace(prev) + + $LOADED_FEATURES.should_not include(@path) + @object.require(@path).should be_true + $LOADED_FEATURES.should include(@path) + end + + it "does not load twice the same file with and without extension" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture.rb").should be_true + @object.require("load_fixture").should be_false + end + + describe "when a non-extensioned file is in $LOADED_FEATURES" do + before :each do + $LOADED_FEATURES << "load_fixture" + end + + it "loads a .rb extensioned file when a non extensioned file is in $LOADED_FEATURES" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file from a subdirectory" do + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.require("code/load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "returns false if the file is not found" do + Dir.chdir File.dirname(CODE_LOADING_DIR) do + @object.require("load_fixture").should be_false + ScratchPad.recorded.should == [] + end + end + + it "returns false when passed a path and the file is not found" do + $LOADED_FEATURES << "code/load_fixture" + Dir.chdir CODE_LOADING_DIR do + @object.require("code/load_fixture").should be_false + ScratchPad.recorded.should == [] + end + end + end + + it "stores ../ relative paths as absolute paths" do + Dir.chdir CODE_LOADING_DIR do + @object.require("../code/load_fixture.rb").should be_true + end + $LOADED_FEATURES.should include(@path) + end + + it "stores ./ relative paths as absolute paths" do + Dir.chdir CODE_LOADING_DIR do + @object.require("./load_fixture.rb").should be_true + end + $LOADED_FEATURES.should include(@path) + end + + it "collapses duplicate path separators" do + $LOAD_PATH << "." + sep = File::Separator + File::Separator + path = ["..", "code", "load_fixture.rb"].join(sep) + Dir.chdir CODE_LOADING_DIR do + @object.require(path).should be_true + end + $LOADED_FEATURES.should include(@path) + end + + it "canonicalizes non-unique absolute paths" do + path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb" + @object.require(path).should be_true + $LOADED_FEATURES.should include(@path) + end + + it "adds the suffix of the resolved filename" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture").should be_true + $LOADED_FEATURES.should include(@path) + end + + it "does not load a non-canonical path for a file already loaded" do + $LOADED_FEATURES << @path + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.require("code/../code/load_fixture.rb").should be_false + ScratchPad.recorded.should == [] + end + + it "does not load a ./ relative path for a file already loaded" do + $LOADED_FEATURES << @path + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.require("./load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + it "does not load a ../ relative path for a file already loaded" do + $LOADED_FEATURES << @path + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.require("../code/load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + ruby_version_is "2.2"..."2.3" do + it "complex, enumerator, rational and unicode_normalize are already required" do + provided = %w[complex enumerator rational unicode_normalize] + features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems') + provided.each { |feature| + features.should =~ /\b#{feature}\.(rb|so)$/ + } + + code = provided.map { |f| "puts require #{f.inspect}\n" }.join + required = ruby_exe(code, options: '--disable-gems') + required.should == "false\n" * provided.size + end + end + + ruby_version_is "2.3"..."2.5" do + it "complex, enumerator, rational, thread and unicode_normalize are already required" do + provided = %w[complex enumerator rational thread unicode_normalize] + features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems') + provided.each { |feature| + features.should =~ /\b#{feature}\.(rb|so|jar)$/ + } + + code = provided.map { |f| "puts require #{f.inspect}\n" }.join + required = ruby_exe(code, options: '--disable-gems') + required.should == "false\n" * provided.size + end + end + + ruby_version_is "2.5" do + it "complex, enumerator, rational and thread are already required" do + provided = %w[complex enumerator rational thread] + features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems') + provided.each { |feature| + features.should =~ /\b#{feature}\.(rb|so|jar)$/ + } + + code = provided.map { |f| "puts require #{f.inspect}\n" }.join + required = ruby_exe(code, options: '--disable-gems') + required.should == "false\n" * provided.size + end + end + end + + describe "(shell expansion)" do + before :each do + @path = File.expand_path("load_fixture.rb", CODE_LOADING_DIR) + @env_home = ENV["HOME"] + ENV["HOME"] = CODE_LOADING_DIR + end + + after :each do + ENV["HOME"] = @env_home + end + + # "#3171" + it "performs tilde expansion on a .rb file before storing paths in $LOADED_FEATURES" do + @object.require("~/load_fixture.rb").should be_true + $LOADED_FEATURES.should include(@path) + end + + it "performs tilde expansion on a non-extensioned file before storing paths in $LOADED_FEATURES" do + @object.require("~/load_fixture").should be_true + $LOADED_FEATURES.should include(@path) + end + end + + describe "(concurrently)" do + before :each do + ScratchPad.record [] + @path = File.expand_path "concurrent.rb", CODE_LOADING_DIR + @path2 = File.expand_path "concurrent2.rb", CODE_LOADING_DIR + @path3 = File.expand_path "concurrent3.rb", CODE_LOADING_DIR + end + + after :each do + ScratchPad.clear + $LOADED_FEATURES.delete @path + $LOADED_FEATURES.delete @path2 + $LOADED_FEATURES.delete @path3 + end + + # Quick note about these specs: + # + # The behavior we're spec'ing requires that t2 enter #require, see t1 is + # loading @path, grab a lock, and wait on it. + # + # We do make sure that t2 starts the require once t1 is in the middle + # of concurrent.rb, but we then need to get t2 to get far enough into #require + # to see t1's lock and try to lock it. + it "blocks a second thread from returning while the 1st is still requiring" do + fin = false + + t1_res = nil + t2_res = nil + + t2 = nil + t1 = Thread.new do + Thread.pass until t2 + Thread.current[:wait_for] = t2 + t1_res = @object.require(@path) + Thread.pass until fin + ScratchPad.recorded << :t1_post + end + + t2 = Thread.new do + Thread.pass until t1[:in_concurrent_rb] + $VERBOSE, @verbose = nil, $VERBOSE + begin + t2_res = @object.require(@path) + ScratchPad.recorded << :t2_post + ensure + $VERBOSE = @verbose + fin = true + end + end + + t1.join + t2.join + + t1_res.should be_true + t2_res.should be_false + + ScratchPad.recorded.should == [:con_pre, :con_post, :t2_post, :t1_post] + end + + it "blocks based on the path" do + t1_res = nil + t2_res = nil + + t2 = nil + t1 = Thread.new do + Thread.pass until t2 + Thread.current[:concurrent_require_thread] = t2 + t1_res = @object.require(@path2) + end + + t2 = Thread.new do + Thread.pass until t1[:in_concurrent_rb2] + t2_res = @object.require(@path3) + end + + t1.join + t2.join + + t1_res.should be_true + t2_res.should be_true + + ScratchPad.recorded.should == [:con2_pre, :con3, :con2_post] + end + + it "allows a 2nd require if the 1st raised an exception" do + fin = false + + t2_res = nil + + t2 = nil + t1 = Thread.new do + Thread.pass until t2 + Thread.current[:wait_for] = t2 + Thread.current[:con_raise] = true + + lambda { + @object.require(@path) + }.should raise_error(RuntimeError) + + Thread.pass until fin + ScratchPad.recorded << :t1_post + end + + t2 = Thread.new do + Thread.pass until t1[:in_concurrent_rb] + $VERBOSE, @verbose = nil, $VERBOSE + begin + t2_res = @object.require(@path) + ScratchPad.recorded << :t2_post + ensure + $VERBOSE = @verbose + fin = true + end + end + + t1.join + t2.join + + t2_res.should be_true + + ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post] + end + + # "redmine #5754" + it "blocks a 3rd require if the 1st raises an exception and the 2nd is still running" do + fin = false + + t1_res = nil + t2_res = nil + + raised = false + + t2 = nil + t1 = Thread.new do + Thread.current[:con_raise] = true + + lambda { + @object.require(@path) + }.should raise_error(RuntimeError) + + raised = true + + # This hits the bug. Because MRI removes its internal lock from a table + # when the exception is raised, this #require doesn't see that t2 is in + # the middle of requiring the file, so this #require runs when it should not. + Thread.pass until t2 && t2[:in_concurrent_rb] + t1_res = @object.require(@path) + + Thread.pass until fin + ScratchPad.recorded << :t1_post + end + + t2 = Thread.new do + Thread.pass until raised + Thread.current[:wait_for] = t1 + begin + t2_res = @object.require(@path) + ScratchPad.recorded << :t2_post + ensure + fin = true + end + end + + t1.join + t2.join + + t1_res.should be_false + t2_res.should be_true + + ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post] + end + end + + it "stores the missing path in a LoadError object" do + path = "abcd1234" + + lambda { + @object.send(@method, path) + }.should raise_error(LoadError) { |e| + e.path.should == path + } + end +end diff --git a/spec/rubyspec/core/kernel/singleton_class_spec.rb b/spec/rubyspec/core/kernel/singleton_class_spec.rb new file mode 100644 index 0000000000..b5e0703905 --- /dev/null +++ b/spec/rubyspec/core/kernel/singleton_class_spec.rb @@ -0,0 +1,27 @@ +describe "Kernel#singleton_class" do + it "returns class extended from an object" do + x = Object.new + xs = class << x; self; end + xs.should == x.singleton_class + end + + it "returns NilClass for nil" do + nil.singleton_class.should == NilClass + end + + it "returns TrueClass for true" do + true.singleton_class.should == TrueClass + end + + it "returns FalseClass for false" do + false.singleton_class.should == FalseClass + end + + it "raises TypeError for Fixnum" do + lambda { 123.singleton_class }.should raise_error(TypeError) + end + + it "raises TypeError for Symbol" do + lambda { :foo.singleton_class }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/kernel/singleton_methods_spec.rb b/spec/rubyspec/core/kernel/singleton_methods_spec.rb new file mode 100644 index 0000000000..596e5ddad2 --- /dev/null +++ b/spec/rubyspec/core/kernel/singleton_methods_spec.rb @@ -0,0 +1,180 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_singleton_methods, shared: true do + it "returns an empty Array for an object with no singleton methods" do + ReflectSpecs.o.singleton_methods(*@object).should == [] + end + + it "returns the names of module methods for a module" do + ReflectSpecs::M.singleton_methods(*@object).should include(:ms_pro, :ms_pub) + end + + it "does not return private module methods for a module" do + ReflectSpecs::M.singleton_methods(*@object).should_not include(:ms_pri) + end + + it "returns the names of class methods for a class" do + ReflectSpecs::A.singleton_methods(*@object).should include(:as_pro, :as_pub) + end + + it "does not return private class methods for a class" do + ReflectSpecs::A.singleton_methods(*@object).should_not include(:as_pri) + end + + it "returns the names of singleton methods for an object" do + ReflectSpecs.os.singleton_methods(*@object).should include(:os_pro, :os_pub) + end +end + +describe :kernel_singleton_methods_modules, shared: true do + it "does not return any included methods for a module including a module" do + ReflectSpecs::N.singleton_methods(*@object).should include(:ns_pro, :ns_pub) + end + + it "does not return any included methods for a class including a module" do + ReflectSpecs::D.singleton_methods(*@object).should include(:ds_pro, :ds_pub) + end +end + +describe :kernel_singleton_methods_supers, shared: true do + it "returns the names of singleton methods for an object extented with a module" do + ReflectSpecs.oe.singleton_methods(*@object).should include(:m_pro, :m_pub) + end + + it "returns a unique list for an object extended with a module" do + m = ReflectSpecs.oed.singleton_methods(*@object) + r = m.select { |x| x == :pub or x == :pro }.sort + r.should == [:pro, :pub] + end + + it "returns the names of singleton methods for an object extented with two modules" do + ReflectSpecs.oee.singleton_methods(*@object).should include(:m_pro, :m_pub, :n_pro, :n_pub) + end + + it "returns the names of singleton methods for an object extented with a module including a module" do + ReflectSpecs.oei.singleton_methods(*@object).should include(:n_pro, :n_pub, :m_pro, :m_pub) + end + + it "returns the names of inherited singleton methods for a subclass" do + ReflectSpecs::B.singleton_methods(*@object).should include(:as_pro, :as_pub, :bs_pro, :bs_pub) + end + + it "returns a unique list for a subclass" do + m = ReflectSpecs::B.singleton_methods(*@object) + r = m.select { |x| x == :pub or x == :pro }.sort + r.should == [:pro, :pub] + end + + it "returns the names of inherited singleton methods for a subclass including a module" do + ReflectSpecs::C.singleton_methods(*@object).should include(:as_pro, :as_pub, :cs_pro, :cs_pub) + end + + it "returns a unique list for a subclass including a module" do + m = ReflectSpecs::C.singleton_methods(*@object) + r = m.select { |x| x == :pub or x == :pro }.sort + r.should == [:pro, :pub] + end + + it "returns the names of inherited singleton methods for a subclass of a class including a module" do + ReflectSpecs::E.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :es_pro, :es_pub) + end + + it "returns the names of inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do + ReflectSpecs::F.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :fs_pro, :fs_pub) + end + + it "returns the names of inherited singleton methods for a class extended with a module" do + ReflectSpecs::P.singleton_methods(*@object).should include(:m_pro, :m_pub) + end +end + +describe :kernel_singleton_methods_private_supers, shared: true do + it "does not return private singleton methods for an object extended with a module" do + ReflectSpecs.oe.singleton_methods(*@object).should_not include(:m_pri) + end + + it "does not return private singleton methods for an object extended with two modules" do + ReflectSpecs.oee.singleton_methods(*@object).should_not include(:m_pri) + end + + it "does not return private singleton methods for an object extented with a module including a module" do + ReflectSpecs.oei.singleton_methods(*@object).should_not include(:n_pri, :m_pri) + end + + it "does not return private singleton methods for a class extended with a module" do + ReflectSpecs::P.singleton_methods(*@object).should_not include(:m_pri) + end + + it "does not return private inherited singleton methods for a module including a module" do + ReflectSpecs::N.singleton_methods(*@object).should_not include(:ns_pri) + end + + it "does not return private inherited singleton methods for a class including a module" do + ReflectSpecs::D.singleton_methods(*@object).should_not include(:ds_pri) + end + + it "does not return private inherited singleton methods for a subclass" do + ReflectSpecs::B.singleton_methods(*@object).should_not include(:as_pri, :bs_pri) + end + + it "does not return private inherited singleton methods for a subclass including a module" do + ReflectSpecs::C.singleton_methods(*@object).should_not include(:as_pri, :cs_pri) + end + + it "does not return private inherited singleton methods for a subclass of a class including a module" do + ReflectSpecs::E.singleton_methods(*@object).should_not include(:ds_pri, :es_pri) + end + + it "does not return private inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do + ReflectSpecs::F.singleton_methods(*@object).should_not include(:ds_pri, :fs_pri) + end +end + +describe "Kernel#singleton_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_singleton_methods, nil, [] + it_behaves_like :kernel_singleton_methods_supers, nil, [] + it_behaves_like :kernel_singleton_methods_modules, nil, [] + it_behaves_like :kernel_singleton_methods_private_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_singleton_methods, nil, true + it_behaves_like :kernel_singleton_methods_supers, nil, true + it_behaves_like :kernel_singleton_methods_modules, nil, true + it_behaves_like :kernel_singleton_methods_private_supers, nil, true + + end + + describe "when passed false" do + it_behaves_like :kernel_singleton_methods, nil, false + it_behaves_like :kernel_singleton_methods_modules, nil, false + it_behaves_like :kernel_singleton_methods_private_supers, nil, false + + it "returns an empty Array for an object extented with a module" do + ReflectSpecs.oe.singleton_methods(false).should == [] + end + + it "returns an empty Array for an object extented with two modules" do + ReflectSpecs.oee.singleton_methods(false).should == [] + end + + it "returns an empty Array for an object extended with a module including a module" do + ReflectSpecs.oei.singleton_methods(false).should == [] + end + + it "returns the names of singleton methods of the subclass" do + ReflectSpecs::B.singleton_methods(false).should include(:bs_pro, :bs_pub) + end + + it "does not return names of inherited singleton methods for a subclass" do + ReflectSpecs::B.singleton_methods(false).should_not include(:as_pro, :as_pub) + end + + it "does not return the names of inherited singleton methods for a class extended with a module" do + ReflectSpecs::P.singleton_methods(false).should_not include(:m_pro, :m_pub) + end + end +end diff --git a/spec/rubyspec/core/kernel/sleep_spec.rb b/spec/rubyspec/core/kernel/sleep_spec.rb new file mode 100644 index 0000000000..bcb0060aa3 --- /dev/null +++ b/spec/rubyspec/core/kernel/sleep_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#sleep" do + it "is a private method" do + Kernel.should have_private_instance_method(:sleep) + end + + it "accepts a Float" do + sleep(0.1).should be_close(0, 2) + end + + it "accepts a Fixnum" do + sleep(0).should be_close(0, 2) + end + + it "accepts a Rational" do + sleep(Rational(1, 9)).should be_close(0, 2) + end + + it "raises an ArgumentError when passed a negative duration" do + lambda { sleep(-0.1) }.should raise_error(ArgumentError) + lambda { sleep(-1) }.should raise_error(ArgumentError) + end + + it "raises a TypeError when passed nil" do + lambda { sleep(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { sleep('2') }.should raise_error(TypeError) + end + + it "pauses execution indefinitely if not given a duration" do + running = false + t = Thread.new do + running = true + sleep + 5 + end + + Thread.pass until running + Thread.pass while t.status and t.status != "sleep" + + t.wakeup + t.value.should == 5 + end +end + +describe "Kernel.sleep" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/spawn_spec.rb b/spec/rubyspec/core/kernel/spawn_spec.rb new file mode 100644 index 0000000000..ad937b17dd --- /dev/null +++ b/spec/rubyspec/core/kernel/spawn_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# These specs only run a basic usage of #spawn. +# Process.spawn has more complete specs and they are not +# run here as it is redundant and takes too long for little gain. +describe "Kernel#spawn" do + it "is a private method" do + Kernel.should have_private_instance_method(:spawn) + end + + it "executes the given command" do + lambda { + Process.wait spawn("echo spawn") + }.should output_to_fd("spawn\n") + end +end + +describe "Kernel.spawn" do + it "executes the given command" do + lambda { + Process.wait Kernel.spawn("echo spawn") + }.should output_to_fd("spawn\n") + end +end diff --git a/spec/rubyspec/core/kernel/sprintf_spec.rb b/spec/rubyspec/core/kernel/sprintf_spec.rb new file mode 100644 index 0000000000..984f31dc7f --- /dev/null +++ b/spec/rubyspec/core/kernel/sprintf_spec.rb @@ -0,0 +1,310 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#sprintf" do + it "is a private method" do + Kernel.should have_private_instance_method(:sprintf) + end + + it "treats nil arguments as zero-width strings in %s slots" do + sprintf("%s%d%s%s", nil, 4, 'a', 'b').should == '4ab' + end + + it "passes some tests for positive %x" do + sprintf("%x", 123).should == "7b" + sprintf("%0x", 123).should == "7b" + sprintf("% x", 123).should == " 7b" + sprintf("%+x", 123).should == "+7b" + sprintf("%+0x", 123).should == "+7b" + sprintf("%+ x", 123).should == "+7b" + sprintf("% 0x", 123).should == " 7b" + + sprintf("%#x", 123).should == "0x7b" + sprintf("%#0x", 123).should == "0x7b" + sprintf("%# x", 123).should == " 0x7b" + sprintf("%#+x", 123).should == "+0x7b" + sprintf("%#+0x", 123).should == "+0x7b" + sprintf("%#+ x", 123).should == "+0x7b" + sprintf("%# 0x", 123).should == " 0x7b" + + sprintf("%8x", 123).should == " 7b" + sprintf("%08x", 123).should == "0000007b" + sprintf("% 8x", 123).should == " 7b" + sprintf("%+8x", 123).should == " +7b" + sprintf("%+08x", 123).should == "+000007b" + sprintf("%+ 8x", 123).should == " +7b" + sprintf("% 08x", 123).should == " 000007b" + + sprintf("%#8x", 123).should == " 0x7b" + sprintf("%#08x", 123).should == "0x00007b" + sprintf("%# 8x", 123).should == " 0x7b" + sprintf("%#+8x", 123).should == " +0x7b" + sprintf("%#+08x", 123).should == "+0x0007b" + sprintf("%#+ 8x", 123).should == " +0x7b" + sprintf("%# 08x", 123).should == " 0x0007b" + + sprintf("%8.10x", 123).should == "000000007b" + sprintf("%08.10x", 123).should == "000000007b" + sprintf("% 8.10x", 123).should == " 000000007b" + sprintf("%+8.10x", 123).should == "+000000007b" + sprintf("%+08.10x", 123).should == "+000000007b" + sprintf("%+ 8.10x", 123).should == "+000000007b" + sprintf("% 08.10x", 123).should == " 000000007b" + + sprintf("%10.8x", 123).should == " 0000007b" + sprintf("%010.8x", 123).should == " 0000007b" + sprintf("% 10.8x", 123).should == " 0000007b" + sprintf("%+10.8x", 123).should == " +0000007b" + sprintf("%+010.8x", 123).should == " +0000007b" + sprintf("%+ 10.8x", 123).should == " +0000007b" + sprintf("% 010.8x", 123).should == " 0000007b" + end + + describe "with format string that contains %{} sections" do + it "substitutes values for named references" do + sprintf("%{foo}f", {foo: 1}).should == "1f" + end + + it "raises KeyError when no matching key is in second argument" do + lambda { sprintf("%{foo}f", {}) }.should raise_error(KeyError) + end + end + + describe "with format string that contains %<> sections" do + it "formats values for named references" do + sprintf("%f", {foo: 1}).should == "1.000000" + end + + it "raises KeyError when no matching key is in second argument" do + lambda { sprintf("%f", {}) }.should raise_error(KeyError) + end + + it "raises ArgumentError if missing second named argument" do + lambda { sprintf("%d", {key: 1}) }.should raise_error(ArgumentError) + end + end + + describe "with negative values" do + describe "with format %x" do + it "precedes the number with '..'" do + sprintf("%0x", -123).should == "..f85" + sprintf("%#0x", -123).should == "0x..f85" + sprintf("%08x", -123).should == "..ffff85" + sprintf("%#08x", -123).should == "0x..ff85" + sprintf("%8.10x", -123).should == "..ffffff85" + sprintf("%08.10x", -123).should == "..ffffff85" + sprintf("%10.8x", -123).should == " ..ffff85" + sprintf("%010.8x", -123).should == " ..ffff85" + end + end + + describe "with format %b or %B" do + it "precedes the number with '..'" do + sprintf("%.7b", -5).should == "..11011" + sprintf("%.7B", -5).should == "..11011" + sprintf("%0b", -5).should == "..1011" + end + end + end + + it "passes some tests for negative %x" do + sprintf("%x", -123).should == "..f85" + sprintf("% x", -123).should == "-7b" + sprintf("%+x", -123).should == "-7b" + sprintf("%+0x", -123).should == "-7b" + sprintf("%+ x", -123).should == "-7b" + sprintf("% 0x", -123).should == "-7b" + + sprintf("%#x", -123).should == "0x..f85" + sprintf("%# x", -123).should == "-0x7b" + sprintf("%#+x", -123).should == "-0x7b" + sprintf("%#+0x", -123).should == "-0x7b" + sprintf("%#+ x", -123).should == "-0x7b" + sprintf("%# 0x", -123).should == "-0x7b" + + sprintf("%8x", -123).should == " ..f85" + sprintf("% 8x", -123).should == " -7b" + sprintf("%+8x", -123).should == " -7b" + sprintf("%+08x", -123).should == "-000007b" + sprintf("%+ 8x", -123).should == " -7b" + sprintf("% 08x", -123).should == "-000007b" + + sprintf("%#8x", -123).should == " 0x..f85" + sprintf("%# 8x", -123).should == " -0x7b" + sprintf("%#+8x", -123).should == " -0x7b" + sprintf("%#+08x", -123).should == "-0x0007b" + sprintf("%#+ 8x", -123).should == " -0x7b" + sprintf("%# 08x", -123).should == "-0x0007b" + + sprintf("% 8.10x", -123).should == "-000000007b" + sprintf("%+8.10x", -123).should == "-000000007b" + sprintf("%+08.10x", -123).should == "-000000007b" + sprintf("%+ 8.10x", -123).should == "-000000007b" + sprintf("% 08.10x", -123).should == "-000000007b" + + sprintf("% 10.8x", -123).should == " -0000007b" + sprintf("%+10.8x", -123).should == " -0000007b" + sprintf("%+010.8x", -123).should == " -0000007b" + sprintf("%+ 10.8x", -123).should == " -0000007b" + sprintf("% 010.8x", -123).should == " -0000007b" + end + + it "passes some tests for negative %u" do + sprintf("%u", -123).should == "-123" + sprintf("%0u", -123).should == "-123" + sprintf("%#u", -123).should == "-123" + sprintf("%#0u", -123).should == "-123" + sprintf("%8u", -123).should == " -123" + sprintf("%08u", -123).should == "-0000123" + sprintf("%#8u", -123).should == " -123" + sprintf("%#08u", -123).should == "-0000123" + + sprintf("%30u", -123).should == " -123" + sprintf("%030u", -123).should == "-00000000000000000000000000123" + + sprintf("%#30u", -123).should == " -123" + sprintf("%#030u", -123).should == "-00000000000000000000000000123" + + sprintf("%24.30u", -123).should == "-000000000000000000000000000123" + sprintf("%024.30u", -123).should == "-000000000000000000000000000123" + + sprintf("%#24.30u", -123).should == "-000000000000000000000000000123" + sprintf("%#024.30u", -123).should == "-000000000000000000000000000123" + + + sprintf("%30.24u", -123).should == " -000000000000000000000123" + sprintf("%030.24u", -123).should == " -000000000000000000000123" + + sprintf("%#30.24u", -123).should == " -000000000000000000000123" + sprintf("%#030.24u", -123).should == " -000000000000000000000123" + end + + it "passes some tests for positive %u" do + sprintf("%30u", 123).should == " 123" + sprintf("%030u", 123).should == "000000000000000000000000000123" + + sprintf("%#30u", 123).should == " 123" + sprintf("%#030u", 123).should == "000000000000000000000000000123" + + sprintf("%24.30u", 123).should == "000000000000000000000000000123" + sprintf("%024.30u", 123).should == "000000000000000000000000000123" + + sprintf("%#24.30u", 123).should == "000000000000000000000000000123" + sprintf("%#024.30u", 123).should == "000000000000000000000000000123" + + sprintf("%30.24u", 123).should == " 000000000000000000000123" + sprintf("%030.24u", 123).should == " 000000000000000000000123" + + sprintf("%#30.24u", 123).should == " 000000000000000000000123" + sprintf("%#030.24u", 123).should == " 000000000000000000000123" + end + + it "passes some tests for positive %d" do + sprintf("%30d", 123).should == " 123" + sprintf("%030d", 123).should == "000000000000000000000000000123" + + sprintf("%#30d", 123).should == " 123" + sprintf("%#030d", 123).should == "000000000000000000000000000123" + + sprintf("%24.30d", 123).should == "000000000000000000000000000123" + sprintf("%024.30d", 123).should == "000000000000000000000000000123" + + sprintf("%#24.30d", 123).should == "000000000000000000000000000123" + sprintf("%#024.30d", 123).should == "000000000000000000000000000123" + + sprintf("%30.24d", 123).should == " 000000000000000000000123" + sprintf("%030.24d", 123).should == " 000000000000000000000123" + + sprintf("%#30.24d", 123).should == " 000000000000000000000123" + sprintf("%#030.24d", 123).should == " 000000000000000000000123" + end + + it "passes some tests for positive %f" do + sprintf("%30f", 123.1).should == " 123.100000" + sprintf("%030f", 123.1).should == "00000000000000000000123.100000" + + sprintf("%#30f", 123.1).should == " 123.100000" + sprintf("%#030f", 123.1).should == "00000000000000000000123.100000" + + sprintf("%10.4f", 123.1).should == " 123.1000" + sprintf("%010.4f", 123.1).should == "00123.1000" + + sprintf("%10.0f", 123.1).should == " 123" + sprintf("%010.0f", 123.1).should == "0000000123" + end + + it "passes some tests for negative %f" do + sprintf("%30f", -123.5).should == " -123.500000" + sprintf("%030f", -123.5).should == "-0000000000000000000123.500000" + + sprintf("%#30f", -123.5).should == " -123.500000" + sprintf("%#030f", -123.5).should == "-0000000000000000000123.500000" + + sprintf("%10.4f", -123.5).should == " -123.5000" + sprintf("%010.4f", -123.5).should == "-0123.5000" + + sprintf("%10.0f", -123.5).should == " -124" + sprintf("%010.0f", -123.5).should == "-000000124" + end + + it "passes some tests for infinite and nan" do + sprintf("%f", Float::INFINITY).should == "Inf" + sprintf("%f", -Float::INFINITY).should == "-Inf" + sprintf("%f", Float::NAN).should == "NaN" + + sprintf("%10f", Float::INFINITY).should == " Inf" + sprintf("%10f", -Float::INFINITY).should == " -Inf" + sprintf("%10f", Float::NAN).should == " NaN" + end + + it "passes kstephens's tests" do + sprintf("%*1$.*2$3$d", 10, 5, 1).should == " 00001" + sprintf("%b", 0).should == "0" + sprintf("%B", 0).should == "0" + sprintf("%b", -5).should == "..1011" + sprintf("%B", -5).should == "..1011" + sprintf("%+b", -5).should == "-101" + sprintf("%+b", 10).should == "+1010" + sprintf("%+b", 0).should == "+0" + sprintf("%+o", -5).should == "-5" + sprintf("%+o", 10).should == "+12" + sprintf("%+o", 0).should == "+0" + sprintf("%+d", -5).should == "-5" + sprintf("%+d", 10).should == "+10" + sprintf("%+d", 0).should == "+0" + sprintf("%+x", -15).should == "-f" + sprintf("%+x", 100).should == "+64" + sprintf("%+x", 0).should == "+0" + sprintf("%+X", -15).should == "-F" + sprintf("%+X", 100).should == "+64" + sprintf("%+X", 0).should == "+0" + sprintf("=%02X", 1).should == "=01" + sprintf("%+03d", 0).should == "+00" + sprintf("%+03d", 5).should == "+05" + sprintf("%+03d", -5).should == "-05" + sprintf("%+03d", 12).should == "+12" + sprintf("%+03d", -12).should == "-12" + sprintf("%+03d", 123).should == "+123" + sprintf("%+03d", -123).should == "-123" + end + + with_feature :encoding do + it "returns a String in the same encoding as the format String if compatible" do + format = "%.2f %4s".force_encoding(Encoding::KOI8_U) + result = sprintf(format, 1.2, "dogs") + result.encoding.should equal(Encoding::KOI8_U) + end + + it "returns a String in the argument encoding if format encoding is more restrictive" do + format = "foo %s".force_encoding(Encoding::US_ASCII) + arg = "b\303\274r".force_encoding(Encoding::UTF_8) + + result = sprintf(format, arg) + result.encoding.should equal(Encoding::UTF_8) + end + end +end + +describe "Kernel.sprintf" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/srand_spec.rb b/spec/rubyspec/core/kernel/srand_spec.rb new file mode 100644 index 0000000000..33f99f5ac4 --- /dev/null +++ b/spec/rubyspec/core/kernel/srand_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.srand" do + it "is a private method" do + Kernel.should have_private_instance_method(:srand) + end + + it "returns the previous seed value" do + srand(10) + srand(20).should == 10 + end + + it "seeds the RNG correctly and repeatably" do + srand(10) + x = rand + srand(10) + rand.should == x + end + + it "defaults number to a random value" do + lambda { srand }.should_not raise_error + srand.should_not == 0 + end + + it "accepts and uses a seed of 0" do + srand(0) + srand.should == 0 + end + + it "accepts a negative seed" do + srand(-17) + srand.should == -17 + end + + it "accepts a Bignum as a seed" do + srand(0x12345678901234567890) + srand.should == 0x12345678901234567890 + end + + it "calls #to_int on seed" do + srand(3.8) + srand.should == 3 + + s = mock('seed') + s.should_receive(:to_int).and_return 0 + srand(s) + end + + it "raises a TypeError when passed nil" do + lambda { srand(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { srand("7") }.should raise_error(TypeError) + end +end + +describe "Kernel#srand" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/sub_spec.rb b/spec/rubyspec/core/kernel/sub_spec.rb new file mode 100644 index 0000000000..78e5eec4a8 --- /dev/null +++ b/spec/rubyspec/core/kernel/sub_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# FIXME: These methods exist only when the -n or -p option is passed to +# ruby, but we currently don't have a way of specifying that. +ruby_version_is ""..."1.9" do + describe "Kernel#sub" do + it "is a private method" do + Kernel.should have_private_instance_method(:sub) + end + end + + describe "Kernel#sub!" do + it "is a private method" do + Kernel.should have_private_instance_method(:sub!) + end + end + + describe "Kernel.sub" do + it "needs to be reviewed for spec completeness" + end + + describe "Kernel.sub!" do + it "needs to be reviewed for spec completeness" + end +end diff --git a/spec/rubyspec/core/kernel/syscall_spec.rb b/spec/rubyspec/core/kernel/syscall_spec.rb new file mode 100644 index 0000000000..bcea833f1f --- /dev/null +++ b/spec/rubyspec/core/kernel/syscall_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#syscall" do + it "is a private method" do + Kernel.should have_private_instance_method(:syscall) + end +end + +describe "Kernel.syscall" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/system_spec.rb b/spec/rubyspec/core/kernel/system_spec.rb new file mode 100644 index 0000000000..ccbd43cfde --- /dev/null +++ b/spec/rubyspec/core/kernel/system_spec.rb @@ -0,0 +1,107 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_system, shared: true do + it "executes the specified command in a subprocess" do + lambda { @object.system("echo a") }.should output_to_fd("a\n") + + $?.should be_an_instance_of Process::Status + $?.success?.should == true + end + + it "returns true when the command exits with a zero exit status" do + @object.system(ruby_cmd('exit 0')).should == true + + $?.should be_an_instance_of Process::Status + $?.success?.should == true + $?.exitstatus.should == 0 + end + + it "returns false when the command exits with a non-zero exit status" do + @object.system(ruby_cmd('exit 1')).should == false + + $?.should be_an_instance_of Process::Status + $?.success?.should == false + $?.exitstatus.should == 1 + end + + it "returns nil when command execution fails" do + @object.system("sad").should be_nil + + $?.should be_an_instance_of Process::Status + $?.pid.should be_kind_of(Integer) + $?.exitstatus.should == 127 + end + + it "does not write to stderr when command execution fails" do + lambda { @object.system("sad") }.should output_to_fd("", STDERR) + end + + platform_is_not :windows do + before :each do + @shell = ENV['SHELL'] + end + + before :each do + ENV['SHELL'] = @shell + end + + it "executes with `sh` if the command contains shell characters" do + lambda { @object.system("echo $0") }.should output_to_fd("sh\n") + end + + it "ignores SHELL env var and always uses `sh`" do + ENV['SHELL'] = "/bin/zsh" + lambda { @object.system("echo $0") }.should output_to_fd("sh\n") + end + end + + before :each do + ENV['TEST_SH_EXPANSION'] = 'foo' + @shell_var = '$TEST_SH_EXPANSION' + platform_is :windows do + @shell_var = '%TEST_SH_EXPANSION%' + end + end + + after :each do + ENV.delete('TEST_SH_EXPANSION') + end + + it "expands shell variables when given a single string argument" do + lambda { @object.system("echo #{@shell_var}") }.should output_to_fd("foo\n") + end + + platform_is_not :windows do + it "does not expand shell variables when given multiples arguments" do + lambda { @object.system("echo", @shell_var) }.should output_to_fd("#{@shell_var}\n") + end + end + + platform_is :windows do + it "does expand shell variables when given multiples arguments" do + # See https://bugs.ruby-lang.org/issues/12231 + lambda { @object.system("echo", @shell_var) }.should output_to_fd("foo\n") + end + end + + platform_is :windows do + it "runs commands starting with any number of @ using shell" do + `#{ruby_cmd("p system 'does_not_exist'")} 2>NUL`.chomp.should == "nil" + @object.system('@does_not_exist 2>NUL').should == false + @object.system("@@@#{ruby_cmd('exit 0')}").should == true + end + end +end + +describe "Kernel#system" do + it "is a private method" do + Kernel.should have_private_instance_method(:system) + end + + it_behaves_like :kernel_system, :system, KernelSpecs::Method.new +end + +describe "Kernel.system" do + it_behaves_like :kernel_system, :system, Kernel +end diff --git a/spec/rubyspec/core/kernel/taint_spec.rb b/spec/rubyspec/core/kernel/taint_spec.rb new file mode 100644 index 0000000000..0c2fb3286b --- /dev/null +++ b/spec/rubyspec/core/kernel/taint_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#taint" do + it "returns self" do + o = Object.new + o.taint.should equal(o) + end + + it "sets the tainted bit" do + o = Object.new + o.taint + o.tainted?.should == true + end + + it "raises RuntimeError on an untainted, frozen object" do + o = Object.new.freeze + lambda { o.taint }.should raise_error(RuntimeError) + end + + it "does not raise an error on a tainted, frozen object" do + o = Object.new.taint.freeze + o.taint.should equal(o) + end + + it "has no effect on immediate values" do + [nil, true, false].each do |v| + v.taint + v.tainted?.should == false + end + end + + it "no raises a RuntimeError on symbols" do + v = :sym + lambda { v.taint }.should_not raise_error(RuntimeError) + v.tainted?.should == false + end + + it "no raises error on fixnum values" do + [1].each do |v| + lambda { v.taint }.should_not raise_error(RuntimeError) + v.tainted?.should == false + end + end +end diff --git a/spec/rubyspec/core/kernel/tainted_spec.rb b/spec/rubyspec/core/kernel/tainted_spec.rb new file mode 100644 index 0000000000..efb31be9d8 --- /dev/null +++ b/spec/rubyspec/core/kernel/tainted_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#tainted?" do + it "returns true if Object is tainted" do + o = mock('o') + p = mock('p') + p.taint + o.tainted?.should == false + p.tainted?.should == true + end +end diff --git a/spec/rubyspec/core/kernel/tap_spec.rb b/spec/rubyspec/core/kernel/tap_spec.rb new file mode 100644 index 0000000000..312a34426c --- /dev/null +++ b/spec/rubyspec/core/kernel/tap_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#tap" do + it "always yields self and returns self" do + a = KernelSpecs::A.new + a.tap{|o| o.should equal(a); 42}.should equal(a) + end + + it "raises a LocalJumpError when no block given" do + lambda { 3.tap }.should raise_error(LocalJumpError) + end +end diff --git a/spec/rubyspec/core/kernel/test_spec.rb b/spec/rubyspec/core/kernel/test_spec.rb new file mode 100644 index 0000000000..21d338ed07 --- /dev/null +++ b/spec/rubyspec/core/kernel/test_spec.rb @@ -0,0 +1,98 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#test" do + before :all do + @file = File.dirname(__FILE__) + '/fixtures/classes.rb' + @dir = File.dirname(__FILE__) + '/fixtures' + end + + it "is a private method" do + Kernel.should have_private_instance_method(:test) + end + + it "returns true when passed ?f if the argument is a regular file" do + Kernel.test(?f, @file).should == true + end + + it "returns true when passed ?e if the argument is a file" do + Kernel.test(?e, @file).should == true + end + + it "returns true when passed ?d if the argument is a directory" do + Kernel.test(?d, @dir).should == true + end + + platform_is_not :windows do + it "returns true when passed ?l if the argument is a symlink" do + link = tmp("file_symlink.lnk") + File.symlink(@file, link) + begin + Kernel.test(?l, link).should be_true + ensure + rm_r link + end + end + end + + it "returns true when passed ?r if the argument is readable by the effective uid" do + Kernel.test(?r, @file).should be_true + end + + it "returns true when passed ?R if the argument is readable by the real uid" do + Kernel.test(?R, @file).should be_true + end + + it "returns true when passed ?w if the argument is readable by the effective uid" do + Kernel.test(?w, @file).should be_true + end + + it "returns true when passed ?W if the argument is readable by the real uid" do + Kernel.test(?W, @file).should be_true + end + + context "time commands" do + before :each do + @tmp_file = File.new(tmp("file.kernel.test"), "w") + end + + after :each do + @tmp_file.close + rm_r @tmp_file + end + + it "returns the last access time for the provided file when passed ?A" do + Kernel.test(?A, @tmp_file).should == @tmp_file.atime + end + + it "returns the time at which the file was created when passed ?C" do + Kernel.test(?C, @tmp_file).should == @tmp_file.ctime + end + + it "returns the time at which the file was modified when passed ?M" do + Kernel.test(?M, @tmp_file).should == @tmp_file.mtime + end + end + + it "calls #to_path on second argument when passed ?f and a filename" do + p = mock('path') + p.should_receive(:to_path).and_return @file + Kernel.test(?f, p) + end + + it "calls #to_path on second argument when passed ?e and a filename" do + p = mock('path') + p.should_receive(:to_path).and_return @file + Kernel.test(?e, p) + end + + it "calls #to_path on second argument when passed ?d and a directory" do + p = mock('path') + p.should_receive(:to_path).and_return @dir + Kernel.test(?d, p) + end +end + +describe "Kernel.test" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/throw_spec.rb b/spec/rubyspec/core/kernel/throw_spec.rb new file mode 100644 index 0000000000..3f8d272d6d --- /dev/null +++ b/spec/rubyspec/core/kernel/throw_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.throw" do + it "transfers control to the end of the active catch block waiting for symbol" do + catch(:blah) do + :value + throw :blah + fail("throw didn't transfer the control") + end.should be_nil + end + + it "transfers control to the innermost catch block waiting for the same sympol" do + one = two = three = 0 + catch :duplicate do + catch :duplicate do + catch :duplicate do + one = 1 + throw :duplicate + end + two = 2 + throw :duplicate + end + three = 3 + throw :duplicate + end + [one, two, three].should == [1, 2, 3] + end + + it "sets the return value of the catch block to nil by default" do + res = catch :blah do + throw :blah + end + res.should == nil + end + + it "sets the return value of the catch block to a value specified as second parameter" do + res = catch :blah do + throw :blah, :return_value + end + res.should == :return_value + end + + it "raises an ArgumentError if there is no catch block for the symbol" do + lambda { throw :blah }.should raise_error(ArgumentError) + end + + it "raises an UncaughtThrowError if there is no catch block for the symbol" do + lambda { throw :blah }.should raise_error(UncaughtThrowError) + end + + it "raises ArgumentError if 3 or more arguments provided" do + lambda { + catch :blah do + throw :blah, :return_value, 2 + end + }.should raise_error(ArgumentError) + + lambda { + catch :blah do + throw :blah, :return_value, 2, 3, 4, 5 + end + }.should raise_error(ArgumentError) + end + + it "can throw an object" do + lambda { + obj = Object.new + catch obj do + throw obj + end + }.should_not raise_error(NameError) + end +end + +describe "Kernel#throw" do + it "is a private method" do + Kernel.should have_private_instance_method(:throw) + end +end diff --git a/spec/rubyspec/core/kernel/to_enum_spec.rb b/spec/rubyspec/core/kernel/to_enum_spec.rb new file mode 100644 index 0000000000..9fb228f318 --- /dev/null +++ b/spec/rubyspec/core/kernel/to_enum_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#to_enum" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/to_s_spec.rb b/spec/rubyspec/core/kernel/to_s_spec.rb new file mode 100644 index 0000000000..c6fcca54a2 --- /dev/null +++ b/spec/rubyspec/core/kernel/to_s_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#to_s" do + it "returns a String containing the name of self's class" do + Object.new.to_s.should =~ /Object/ + end + + it "returns a tainted result if self is tainted" do + Object.new.taint.to_s.tainted?.should be_true + end + + it "returns an untrusted result if self is untrusted" do + Object.new.untrust.to_s.untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/kernel/trace_var_spec.rb b/spec/rubyspec/core/kernel/trace_var_spec.rb new file mode 100644 index 0000000000..07e02feb72 --- /dev/null +++ b/spec/rubyspec/core/kernel/trace_var_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#trace_var" do + before :each do + $Kernel_trace_var_global = nil + end + + after :each do + untrace_var :$Kernel_trace_var_global + + $Kernel_trace_var_global = nil + $Kernel_trace_var_extra = nil + end + + it "is a private method" do + Kernel.should have_private_instance_method(:trace_var) + end + + it "hooks assignments to a global variable" do + captured = nil + + trace_var :$Kernel_trace_var_global do |value| + captured = value + end + + $Kernel_trace_var_global = 'foo' + captured.should == 'foo' + end + + it "accepts a proc argument instead of a block" do + captured = nil + + trace_var :$Kernel_trace_var_global, proc {|value| captured = value} + + $Kernel_trace_var_global = 'foo' + captured.should == 'foo' + end + + # String arguments should be evaluated in the context of the caller. + it "accepts a String argument instead of a Proc or block" do + trace_var :$Kernel_trace_var_global, '$Kernel_trace_var_extra = true' + + $Kernel_trace_var_global = 'foo' + + $Kernel_trace_var_extra.should == true + end + + it "raises ArgumentError if no block or proc is provided" do + lambda do + trace_var :$Kernel_trace_var_global + end.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/kernel/trap_spec.rb b/spec/rubyspec/core/kernel/trap_spec.rb new file mode 100644 index 0000000000..98f386dc85 --- /dev/null +++ b/spec/rubyspec/core/kernel/trap_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#trap" do + it "is a private method" do + Kernel.should have_private_instance_method(:trap) + end +end + +describe "Kernel.trap" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/trust_spec.rb b/spec/rubyspec/core/kernel/trust_spec.rb new file mode 100644 index 0000000000..a9fda5c5c6 --- /dev/null +++ b/spec/rubyspec/core/kernel/trust_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#trust" do + it "returns self" do + o = Object.new + o.trust.should equal(o) + end + + it "clears the untrusted bit" do + o = Object.new.untrust + o.trust + o.untrusted?.should == false + end + + it "raises RuntimeError on an untrusted, frozen object" do + o = Object.new.untrust.freeze + lambda { o.trust }.should raise_error(RuntimeError) + end + + it "does not raise an error on a trusted, frozen object" do + o = Object.new.freeze + o.trust.should equal(o) + end +end diff --git a/spec/rubyspec/core/kernel/untaint_spec.rb b/spec/rubyspec/core/kernel/untaint_spec.rb new file mode 100644 index 0000000000..5abe5d63fc --- /dev/null +++ b/spec/rubyspec/core/kernel/untaint_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untaint" do + it "returns self" do + o = Object.new + o.untaint.should equal(o) + end + + it "clears the tainted bit" do + o = Object.new.taint + o.untaint + o.tainted?.should == false + end + + it "raises RuntimeError on a tainted, frozen object" do + o = Object.new.taint.freeze + lambda { o.untaint }.should raise_error(RuntimeError) + end + + it "does not raise an error on an untainted, frozen object" do + o = Object.new.freeze + o.untaint.should equal(o) + end +end diff --git a/spec/rubyspec/core/kernel/untrace_var_spec.rb b/spec/rubyspec/core/kernel/untrace_var_spec.rb new file mode 100644 index 0000000000..3af1348ffd --- /dev/null +++ b/spec/rubyspec/core/kernel/untrace_var_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untrace_var" do + it "is a private method" do + Kernel.should have_private_instance_method(:untrace_var) + end +end + +describe "Kernel.untrace_var" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/kernel/untrust_spec.rb b/spec/rubyspec/core/kernel/untrust_spec.rb new file mode 100644 index 0000000000..280a465807 --- /dev/null +++ b/spec/rubyspec/core/kernel/untrust_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untrust" do + it "returns self" do + o = Object.new + o.untrust.should equal(o) + end + + it "sets the untrusted bit" do + o = Object.new + o.untrust + o.untrusted?.should == true + end + + it "raises RuntimeError on a trusted, frozen object" do + o = Object.new.freeze + lambda { o.untrust }.should raise_error(RuntimeError) + end + + it "does not raise an error on an untrusted, frozen object" do + o = Object.new.untrust.freeze + o.untrust.should equal(o) + end +end diff --git a/spec/rubyspec/core/kernel/untrusted_spec.rb b/spec/rubyspec/core/kernel/untrusted_spec.rb new file mode 100644 index 0000000000..43c4c0aa18 --- /dev/null +++ b/spec/rubyspec/core/kernel/untrusted_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untrusted?" do + it "returns the untrusted status of an object" do + o = mock('o') + o.untrusted?.should == false + o.untrust + o.untrusted?.should == true + end + + it "has no effect on immediate values" do + a = nil + b = true + c = false + a.untrust + b.untrust + c.untrust + a.untrusted?.should == false + b.untrusted?.should == false + c.untrusted?.should == false + end + + it "has effect on immediate values" do + d = 1 + lambda { d.untrust }.should_not raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/kernel/warn_spec.rb b/spec/rubyspec/core/kernel/warn_spec.rb new file mode 100644 index 0000000000..c44116dc21 --- /dev/null +++ b/spec/rubyspec/core/kernel/warn_spec.rb @@ -0,0 +1,79 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#warn" do + before :each do + @before_verbose = $VERBOSE + @before_separator = $/ + end + + after :each do + $VERBOSE = @before_verbose + $/ = @before_separator + end + + it "is a private method" do + Kernel.should have_private_instance_method(:warn) + end + + it "requires multiple arguments" do + Kernel.method(:warn).arity.should < 0 + end + + it "does not append line-end if last character is line-end" do + lambda { + $VERBOSE = true + warn("this is some simple text with line-end\n") + }.should output(nil, "this is some simple text with line-end\n") + end + + it "calls #write on $stderr if $VERBOSE is true" do + lambda { + $VERBOSE = true + warn("this is some simple text") + }.should output(nil, "this is some simple text\n") + end + + it "calls #write on $stderr if $VERBOSE is false" do + lambda { + $VERBOSE = false + warn("this is some simple text") + }.should output(nil, "this is some simple text\n") + end + + it "does not call #write on $stderr if $VERBOSE is nil" do + lambda { + $VERBOSE = nil + warn("this is some simple text") + }.should output(nil, "") + end + + it "writes each argument on a line when passed multiple arguments" do + lambda { + $VERBOSE = true + warn("line 1", "line 2") + }.should output(nil, "line 1\nline 2\n") + end + + it "writes each array element on a line when passes an array" do + lambda { + $VERBOSE = true + warn(["line 1", "line 2"]) + }.should output(nil, "line 1\nline 2\n") + end + + it "does not write strings when passed no arguments" do + lambda { + $VERBOSE = true + warn + }.should output("", "") + end + + it "writes the default record separator and NOT $/ to $stderr after the warning message" do + lambda { + $VERBOSE = true + $/ = 'rs' + warn("") + }.should output(nil, /\n/) + end +end diff --git a/spec/rubyspec/core/main/define_method_spec.rb b/spec/rubyspec/core/main/define_method_spec.rb new file mode 100644 index 0000000000..741e0624f8 --- /dev/null +++ b/spec/rubyspec/core/main/define_method_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +script_binding = binding + +describe "main#define_method" do + before :each do + @code = 'define_method(:boom) { :bam }' + end + + after :each do + Object.send :remove_method, :boom + end + + it 'creates a public method in TOPLEVEL_BINDING' do + eval @code, TOPLEVEL_BINDING + Object.should have_method :boom + end + + it 'creates a public method in script binding' do + eval @code, script_binding + Object.should have_method :boom + end + + it 'returns the method name as symbol' do + eval(@code, TOPLEVEL_BINDING).should equal :boom + end +end diff --git a/spec/rubyspec/core/main/fixtures/classes.rb b/spec/rubyspec/core/main/fixtures/classes.rb new file mode 100644 index 0000000000..0b74080492 --- /dev/null +++ b/spec/rubyspec/core/main/fixtures/classes.rb @@ -0,0 +1,15 @@ +module MainSpecs + module Module + end + + module WrapIncludeModule + end +end + +def main_public_method +end +public :main_public_method + +def main_private_method +end +private :main_private_method diff --git a/spec/rubyspec/core/main/fixtures/wrapped_include.rb b/spec/rubyspec/core/main/fixtures/wrapped_include.rb new file mode 100644 index 0000000000..307c98b419 --- /dev/null +++ b/spec/rubyspec/core/main/fixtures/wrapped_include.rb @@ -0,0 +1 @@ +include MainSpecs::WrapIncludeModule diff --git a/spec/rubyspec/core/main/include_spec.rb b/spec/rubyspec/core/main/include_spec.rb new file mode 100644 index 0000000000..1973bc8229 --- /dev/null +++ b/spec/rubyspec/core/main/include_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "main#include" do + it "includes the given Module in Object" do + eval "include MainSpecs::Module", TOPLEVEL_BINDING + Object.ancestors.should include(MainSpecs::Module) + end + + context "in a file loaded with wrapping" do + it "includes the given Module in the load wrapper" do + load(File.expand_path("../fixtures/wrapped_include.rb", __FILE__), true) + Object.ancestors.should_not include(MainSpecs::WrapIncludeModule) + end + end +end diff --git a/spec/rubyspec/core/main/private_spec.rb b/spec/rubyspec/core/main/private_spec.rb new file mode 100644 index 0000000000..7e0a4ed57d --- /dev/null +++ b/spec/rubyspec/core/main/private_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "main#private" do + after :each do + Object.send(:public, :main_public_method) + end + + it "sets the visibility of the given method to private" do + eval "private :main_public_method", TOPLEVEL_BINDING + Object.should have_private_method(:main_public_method) + end + + it "returns Object" do + eval("private :main_public_method", TOPLEVEL_BINDING).should equal(Object) + end + + it "raises a NameError when given an undefined name" do + lambda do + eval "private :main_undefined_method", TOPLEVEL_BINDING + end.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/main/public_spec.rb b/spec/rubyspec/core/main/public_spec.rb new file mode 100644 index 0000000000..6906faee04 --- /dev/null +++ b/spec/rubyspec/core/main/public_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "main#public" do + after :each do + Object.send(:private, :main_private_method) + end + + it "sets the visibility of the given method to public" do + eval "public :main_private_method", TOPLEVEL_BINDING + Object.should_not have_private_method(:main_private_method) + end + + it "returns Object" do + eval("public :main_private_method", TOPLEVEL_BINDING).should equal(Object) + end + + it "raises a NameError when given an undefined name" do + lambda do + eval "public :main_undefined_method", TOPLEVEL_BINDING + end.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/main/to_s_spec.rb b/spec/rubyspec/core/main/to_s_spec.rb new file mode 100644 index 0000000000..dd5a02b0ae --- /dev/null +++ b/spec/rubyspec/core/main/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "main#to_s" do + it "returns 'main'" do + eval('to_s', TOPLEVEL_BINDING).should == "main" + end +end diff --git a/spec/rubyspec/core/marshal/dump_spec.rb b/spec/rubyspec/core/marshal/dump_spec.rb new file mode 100644 index 0000000000..6b369f9bb0 --- /dev/null +++ b/spec/rubyspec/core/marshal/dump_spec.rb @@ -0,0 +1,573 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/marshal_data', __FILE__) + +describe "Marshal.dump" do + it "dumps nil" do + Marshal.dump(nil).should == "\004\b0" + end + + it "dumps true" do + Marshal.dump(true).should == "\004\bT" + end + + it "dumps false" do + Marshal.dump(false).should == "\004\bF" + end + + describe "with a Fixnum" do + it "dumps a Fixnum" do + [ [Marshal, 0, "\004\bi\000"], + [Marshal, 5, "\004\bi\n"], + [Marshal, 8, "\004\bi\r"], + [Marshal, 122, "\004\bi\177"], + [Marshal, 123, "\004\bi\001{"], + [Marshal, 1234, "\004\bi\002\322\004"], + [Marshal, -8, "\004\bi\363"], + [Marshal, -123, "\004\bi\200"], + [Marshal, -124, "\004\bi\377\204"], + [Marshal, -1234, "\004\bi\376.\373"], + [Marshal, -4516727, "\004\bi\375\211\024\273"], + [Marshal, 2**8, "\004\bi\002\000\001"], + [Marshal, 2**16, "\004\bi\003\000\000\001"], + [Marshal, 2**24, "\004\bi\004\000\000\000\001"], + [Marshal, -2**8, "\004\bi\377\000"], + [Marshal, -2**16, "\004\bi\376\000\000"], + [Marshal, -2**24, "\004\bi\375\000\000\000"], + ].should be_computed_by(:dump) + end + + platform_is wordsize: 64 do + it "dumps a positive Fixnum > 31 bits as a Bignum" do + Marshal.dump(2**31 + 1).should == "\x04\bl+\a\x01\x00\x00\x80" + end + + it "dumps a negative Fixnum > 31 bits as a Bignum" do + Marshal.dump(-2**31 - 1).should == "\x04\bl-\a\x01\x00\x00\x80" + end + end + end + + describe "with a Symbol" do + it "dumps a Symbol" do + Marshal.dump(:symbol).should == "\004\b:\vsymbol" + end + + it "dumps a big Symbol" do + Marshal.dump(('big' * 100).to_sym).should == "\004\b:\002,\001#{'big' * 100}" + end + + it "dumps an encoded Symbol" do + s = "\u2192" + [ [Marshal, s.encode("utf-8").to_sym, + "\x04\bI:\b\xE2\x86\x92\x06:\x06ET"], + [Marshal, s.encode("utf-16").to_sym, + "\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16"], + [Marshal, s.encode("euc-jp").to_sym, + "\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP"], + [Marshal, s.encode("sjis").to_sym, + "\x04\bI:\a\x81\xA8\x06:\rencoding\"\x10Windows-31J"] + ].should be_computed_by(:dump) + end + + it "dumps a binary encoded Symbol" do + s = "\u2192".force_encoding("binary").to_sym + Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92" + end + + end + + it "dumps an extended_object" do + Marshal.dump(Object.new.extend(Meths)).should == "\x04\be:\nMethso:\vObject\x00" + end + + it "dumps an object that has had an ivar added and removed as though the ivar never was set" do + obj = Object.new + initial = Marshal.dump(obj) + obj.instance_variable_set(:@ivar, 1) + Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006" + obj.send :remove_instance_variable, :@ivar + Marshal.dump(obj).should == initial + end + + describe "with an object responding to #marshal_dump" do + it "dumps the object returned by #marshal_dump" do + Marshal.dump(UserMarshal.new).should == "\x04\bU:\x10UserMarshal:\tdata" + end + + it "does not use Class#name" do + UserMarshal.should_not_receive(:name) + Marshal.dump(UserMarshal.new) + end + end + + describe "with an object responding to #_dump" do + it "dumps the object returned by #marshal_dump" do + Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000" + end + + it "raises a TypeError if _dump returns a non-string" do + m = mock("marshaled") + m.should_receive(:_dump).and_return(0) + lambda { Marshal.dump(m) }.should raise_error(TypeError) + end + + it "favors marshal_dump over _dump" do + m = mock("marshaled") + m.should_receive(:marshal_dump).and_return(0) + m.should_not_receive(:_dump) + Marshal.dump(m) + end + end + + describe "with a Class" do + it "dumps a builtin Class" do + Marshal.dump(String).should == "\004\bc\vString" + end + + it "dumps a user Class" do + Marshal.dump(UserDefined).should == "\x04\bc\x10UserDefined" + end + + it "dumps a nested Class" do + Marshal.dump(UserDefined::Nested).should == "\004\bc\030UserDefined::Nested" + end + + it "raises TypeError with an anonymous Class" do + lambda { Marshal.dump(Class.new) }.should raise_error(TypeError) + end + + it "raises TypeError with a singleton Class" do + lambda { Marshal.dump(class << self; self end) }.should raise_error(TypeError) + end + end + + describe "with a Module" do + it "dumps a builtin Module" do + Marshal.dump(Marshal).should == "\004\bm\fMarshal" + end + + it "raises TypeError with an anonymous Module" do + lambda { Marshal.dump(Module.new) }.should raise_error(TypeError) + end + end + + describe "with a Float" do + it "dumps a Float" do + [ [Marshal, 0.0, "\004\bf\0060"], + [Marshal, -0.0, "\004\bf\a-0"], + [Marshal, 1.0, "\004\bf\0061"], + [Marshal, 123.4567, "\004\bf\r123.4567"], + [Marshal, -0.841, "\x04\bf\v-0.841"], + [Marshal, -9876.345, "\x04\bf\x0E-9876.345"], + [Marshal, infinity_value, "\004\bf\binf"], + [Marshal, -infinity_value, "\004\bf\t-inf"], + [Marshal, nan_value, "\004\bf\bnan"], + ].should be_computed_by(:dump) + end + end + + describe "with a Bignum" do + it "dumps a Bignum" do + [ [Marshal, -4611686018427387903, "\004\bl-\t\377\377\377\377\377\377\377?"], + [Marshal, -2361183241434822606847, "\004\bl-\n\377\377\377\377\377\377\377\377\177\000"], + ].should be_computed_by(:dump) + end + + it "dumps a Bignum" do + [ [Marshal, 2**64, "\004\bl+\n\000\000\000\000\000\000\000\000\001\000"], + [Marshal, 2**90, "\004\bl+\v#{"\000" * 11}\004"], + [Marshal, -2**63, "\004\bl-\t\000\000\000\000\000\000\000\200"], + [Marshal, -2**64, "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"], + ].should be_computed_by(:dump) + end + end + + describe "with a String" do + it "dumps a blank String" do + Marshal.dump("".force_encoding("binary")).should == "\004\b\"\000" + end + + it "dumps a short String" do + Marshal.dump("short".force_encoding("binary")).should == "\004\b\"\012short" + end + + it "dumps a long String" do + Marshal.dump(("big" * 100).force_encoding("binary")).should == "\004\b\"\002,\001#{"big" * 100}" + end + + it "dumps a String extended with a Module" do + Marshal.dump("".extend(Meths).force_encoding("binary")).should == "\004\be:\nMeths\"\000" + end + + it "dumps a String subclass" do + Marshal.dump(UserString.new.force_encoding("binary")).should == "\004\bC:\017UserString\"\000" + end + + it "dumps a String subclass extended with a Module" do + Marshal.dump(UserString.new.extend(Meths).force_encoding("binary")).should == "\004\be:\nMethsC:\017UserString\"\000" + end + + it "dumps a String with instance variables" do + str = "" + str.instance_variable_set("@foo", "bar") + Marshal.dump(str.force_encoding("binary")).should == "\x04\bI\"\x00\x06:\t@foo\"\bbar" + end + + with_feature :encoding do + it "dumps a US-ASCII String" do + str = "abc".force_encoding("us-ascii") + Marshal.dump(str).should == "\x04\bI\"\babc\x06:\x06EF" + end + + it "dumps a UTF-8 String" do + str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8") + Marshal.dump(str).should == "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET" + end + + it "dumps a String in another encoding" do + str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le") + result = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE" + Marshal.dump(str).should == result + end + + it "dumps multiple strings using symlinks for the :E (encoding) symbol" do + Marshal.dump(["".encode("us-ascii"), "".encode("utf-8")]).should == "\x04\b[\aI\"\x00\x06:\x06EFI\"\x00\x06;\x00T" + end + end + end + + describe "with a Regexp" do + it "dumps a Regexp" do + Marshal.dump(/\A.\Z/).should == "\x04\bI/\n\\A.\\Z\x00\x06:\x06EF" + end + + it "dumps a Regexp with flags" do + Marshal.dump(//im).should == "\x04\bI/\x00\x05\x06:\x06EF" + end + + it "dumps a Regexp with instance variables" do + o = // + o.instance_variable_set(:@ivar, :ivar) + Marshal.dump(o).should == "\x04\bI/\x00\x00\a:\x06EF:\n@ivar:\tivar" + end + + it "dumps an extended Regexp" do + Marshal.dump(//.extend(Meths)).should == "\x04\bIe:\nMeths/\x00\x00\x06:\x06EF" + end + + it "dumps a Regexp subclass" do + Marshal.dump(UserRegexp.new("")).should == "\x04\bIC:\x0FUserRegexp/\x00\x00\x06:\x06EF" + end + + it "dumps a binary Regexp" do + o = Regexp.new("".force_encoding("binary"), Regexp::FIXEDENCODING) + Marshal.dump(o).should == "\x04\b/\x00\x10" + end + + it "dumps a UTF-8 Regexp" do + o = Regexp.new("".force_encoding("utf-8"), Regexp::FIXEDENCODING) + Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\x06ET" + end + + it "dumps a Regexp in another encoding" do + o = Regexp.new("".force_encoding("utf-16le"), Regexp::FIXEDENCODING) + Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\rencoding\"\rUTF-16LE" + end + end + + describe "with an Array" do + it "dumps an empty Array" do + Marshal.dump([]).should == "\004\b[\000" + end + + it "dumps a non-empty Array" do + Marshal.dump([:a, 1, 2]).should == "\004\b[\b:\006ai\006i\a" + end + + it "dumps an Array subclass" do + Marshal.dump(UserArray.new).should == "\004\bC:\016UserArray[\000" + end + + it "dumps a recursive Array" do + a = [] + a << a + Marshal.dump(a).should == "\x04\b[\x06@\x00" + end + + it "dumps an Array with instance variables" do + a = [] + a.instance_variable_set(:@ivar, 1) + Marshal.dump(a).should == "\004\bI[\000\006:\n@ivari\006" + end + + it "dumps an extended Array" do + Marshal.dump([].extend(Meths)).should == "\004\be:\nMeths[\000" + end + end + + describe "with a Hash" do + it "dumps a Hash" do + Marshal.dump({}).should == "\004\b{\000" + end + + it "dumps a Hash subclass" do + Marshal.dump(UserHash.new).should == "\004\bC:\rUserHash{\000" + end + + it "dumps a Hash with a default value" do + Marshal.dump(Hash.new(1)).should == "\004\b}\000i\006" + end + + it "raises a TypeError with hash having default proc" do + lambda { Marshal.dump(Hash.new {}) }.should raise_error(TypeError) + end + + it "dumps a Hash with instance variables" do + a = {} + a.instance_variable_set(:@ivar, 1) + Marshal.dump(a).should == "\004\bI{\000\006:\n@ivari\006" + end + + it "dumps an extended Hash" do + Marshal.dump({}.extend(Meths)).should == "\004\be:\nMeths{\000" + end + + it "dumps an Hash subclass with a parameter to initialize" do + Marshal.dump(UserHashInitParams.new(1)).should == "\004\bIC:\027UserHashInitParams{\000\006:\a@ai\006" + end + end + + describe "with a Struct" do + it "dumps a Struct" do + Marshal.dump(Struct::Pyramid.new).should == "\004\bS:\024Struct::Pyramid\000" + end + + it "dumps a Struct" do + Marshal.dump(Struct::Useful.new(1, 2)).should == "\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a" + end + + it "dumps a Struct with instance variables" do + st = Struct.new("Thick").new + st.instance_variable_set(:@ivar, 1) + Marshal.dump(st).should == "\004\bIS:\022Struct::Thick\000\006:\n@ivari\006" + Struct.send(:remove_const, :Thick) + end + + it "dumps an extended Struct" do + st = Struct.new("Extended", :a, :b).new + Marshal.dump(st.extend(Meths)).should == "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0" + Struct.send(:remove_const, :Extended) + end + end + + describe "with an Object" do + it "dumps an Object" do + Marshal.dump(Object.new).should == "\004\bo:\x0BObject\x00" + end + + it "dumps an extended Object" do + Marshal.dump(Object.new.extend(Meths)).should == "\004\be:\x0AMethso:\x0BObject\x00" + end + + it "dumps an Object with an instance variable" do + obj = Object.new + obj.instance_variable_set(:@ivar, 1) + Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006" + end + + it "dumps an Object that has had an instance variable added and removed as though it was never set" do + obj = Object.new + obj.instance_variable_set(:@ivar, 1) + obj.send(:remove_instance_variable, :@ivar) + Marshal.dump(obj).should == "\004\bo:\x0BObject\x00" + end + + it "dumps an Object if it has a singleton class but no singleton methods" do + obj = Object.new + obj.singleton_class + Marshal.dump(obj).should == "\004\bo:\x0BObject\x00" + end + + it "raises if an Object has a singleton class and singleton methods" do + obj = Object.new + def obj.foo; end + lambda { + Marshal.dump(obj) + }.should raise_error(TypeError, "singleton can't be dumped") + end + + it "dumps a BasicObject subclass if it defines respond_to?" do + obj = MarshalSpec::BasicObjectSubWithRespondToFalse.new + Marshal.dump(obj).should == "\x04\bo:2MarshalSpec::BasicObjectSubWithRespondToFalse\x00" + end + end + + describe "with a Range" do + it "dumps a Range inclusive of end (with indeterminant order)" do + dump = Marshal.dump(1..2) + load = Marshal.load(dump) + load.should == (1..2) + end + + it "dumps a Range exclusive of end (with indeterminant order)" do + dump = Marshal.dump(1...2) + load = Marshal.load(dump) + load.should == (1...2) + end + end + + describe "with a Time" do + before :each do + @internal = Encoding.default_internal + Encoding.default_internal = Encoding::UTF_8 + + @utc = Time.utc(2012, 1, 1) + @utc_dump = @utc.send(:_dump) + + with_timezone 'AST', 3 do + @t = Time.local(2012, 1, 1) + @fract = Time.local(2012, 1, 1, 1, 59, 56.2) + @t_dump = @t.send(:_dump) + @fract_dump = @fract.send(:_dump) + end + end + + after :each do + Encoding.default_internal = @internal + end + + it "dumps the zone and the offset" do + with_timezone 'AST', 3 do + dump = Marshal.dump(@t) + base = "\x04\bIu:\tTime\r#{@t_dump}\a" + offset = ":\voffseti\x020*" + zone = ":\tzoneI\"\bAST\x06:\x06EF" # Last is 'F' (US-ASCII) + [ "#{base}#{offset}#{zone}", "#{base}#{zone}#{offset}" ].should include(dump) + end + + it "dumps the zone, but not the offset if zone is UTC" do + dump = Marshal.dump(@utc) + zone = ":\tzoneI\"\bUTC\x06:\x06EF" # Last is 'F' (US-ASCII) + dump.should == "\x04\bIu:\tTime\r#{@utc_dump}\x06#{zone}" + end + end + + end + + describe "with an Exception" do + it "dumps an empty Exception" do + Marshal.dump(Exception.new).should == "\x04\bo:\x0EException\a:\tmesg0:\abt0" + end + + it "dumps the message for the exception" do + Marshal.dump(Exception.new("foo")).should == "\x04\bo:\x0EException\a:\tmesg\"\bfoo:\abt0" + end + + it "contains the filename in the backtrace" do + obj = Exception.new("foo") + obj.set_backtrace(["foo/bar.rb:10"]) + Marshal.dump(obj).should == "\x04\bo:\x0EException\a:\tmesg\"\bfoo:\abt[\x06\"\x12foo/bar.rb:10" + end + end + + it "dumps subsequent appearances of a symbol as a link" do + Marshal.dump([:a, :a]).should == "\004\b[\a:\006a;\000" + end + + it "dumps subsequent appearances of an object as a link" do + o = Object.new + Marshal.dump([o, o]).should == "\004\b[\ao:\vObject\000@\006" + end + + MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)| + it "#{description} returns a binary string" do + Marshal.dump(object).encoding.should == Encoding::BINARY + end + end + + it "raises an ArgumentError when the recursion limit is exceeded" do + h = {'one' => {'two' => {'three' => 0}}} + lambda { Marshal.dump(h, 3) }.should raise_error(ArgumentError) + lambda { Marshal.dump([h], 4) }.should raise_error(ArgumentError) + lambda { Marshal.dump([], 0) }.should raise_error(ArgumentError) + lambda { Marshal.dump([[[]]], 1) }.should raise_error(ArgumentError) + end + + it "ignores the recursion limit if the limit is negative" do + Marshal.dump([], -1).should == "\004\b[\000" + Marshal.dump([[]], -1).should == "\004\b[\006[\000" + Marshal.dump([[[]]], -1).should == "\004\b[\006[\006[\000" + end + + describe "when passed an IO" do + + it "writes the serialized data to the IO-Object" do + (obj = mock('test')).should_receive(:write).at_least(1) + Marshal.dump("test", obj) + end + + it "returns the IO-Object" do + (obj = mock('test')).should_receive(:write).at_least(1) + Marshal.dump("test", obj).should == obj + end + + it "raises an Error when the IO-Object does not respond to #write" do + obj = mock('test') + lambda { Marshal.dump("test", obj) }.should raise_error(TypeError) + end + + with_feature :encoding do + + it "calls binmode when it's defined" do + obj = mock('test') + obj.should_receive(:write).at_least(1) + obj.should_receive(:binmode).at_least(1) + Marshal.dump("test", obj) + end + + end + + end + + it "raises a TypeError if marshalling a Method instance" do + lambda { Marshal.dump(Marshal.method(:dump)) }.should raise_error(TypeError) + end + + it "raises a TypeError if marshalling a Proc" do + lambda { Marshal.dump(proc {}) }.should raise_error(TypeError) + end + + it "raises a TypeError if dumping a IO/File instance" do + lambda { Marshal.dump(STDIN) }.should raise_error(TypeError) + lambda { File.open(__FILE__) { |f| Marshal.dump(f) } }.should raise_error(TypeError) + end + + it "raises a TypeError if dumping a MatchData instance" do + lambda { Marshal.dump(/(.)/.match("foo")) }.should raise_error(TypeError) + end + + it "returns an untainted string if object is untainted" do + Marshal.dump(Object.new).tainted?.should be_false + end + + it "returns a tainted string if object is tainted" do + Marshal.dump(Object.new.taint).tainted?.should be_true + end + + it "returns a tainted string if nested object is tainted" do + Marshal.dump([[Object.new.taint]]).tainted?.should be_true + end + + it "returns a trusted string if object is trusted" do + Marshal.dump(Object.new).untrusted?.should be_false + end + + it "returns an untrusted string if object is untrusted" do + Marshal.dump(Object.new.untrust).untrusted?.should be_true + end + + it "returns an untrusted string if nested object is untrusted" do + Marshal.dump([[Object.new.untrust]]).untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/marshal/fixtures/marshal_data.rb b/spec/rubyspec/core/marshal/fixtures/marshal_data.rb new file mode 100644 index 0000000000..2931278290 --- /dev/null +++ b/spec/rubyspec/core/marshal/fixtures/marshal_data.rb @@ -0,0 +1,420 @@ +# -*- encoding: binary -*- +class UserDefined + class Nested + def ==(other) + other.kind_of? self.class + end + end + + attr_reader :a, :b + + def initialize + @a = 'stuff' + @b = @a + end + + def _dump(depth) + Marshal.dump [:stuff, :stuff] + end + + def self._load(data) + a, b = Marshal.load data + + obj = allocate + obj.instance_variable_set :@a, a + obj.instance_variable_set :@b, b + + obj + end + + def ==(other) + self.class === other and + @a == other.a and + @b == other.b + end +end + +class UserDefinedWithIvar + attr_reader :a, :b, :c + + def initialize + @a = 'stuff' + @a.instance_variable_set :@foo, :UserDefinedWithIvar + @b = 'more' + @c = @b + end + + def _dump(depth) + Marshal.dump [:stuff, :more, :more] + end + + def self._load(data) + a, b, c = Marshal.load data + + obj = allocate + obj.instance_variable_set :@a, a + obj.instance_variable_set :@b, b + obj.instance_variable_set :@c, c + + obj + end + + def ==(other) + self.class === other and + @a == other.a and + @b == other.b and + @c == other.c and + @a.instance_variable_get(:@foo) == other.a.instance_variable_get(:@foo) + end +end + +class UserDefinedImmediate + def _dump(depth) + '' + end + + def self._load(data) + nil + end +end + +class UserPreviouslyDefinedWithInitializedIvar + attr_accessor :field1, :field2 +end + +class UserMarshal + attr_reader :data + + def initialize + @data = 'stuff' + end + def marshal_dump() :data end + def marshal_load(data) @data = data end + def ==(other) self.class === other and @data == other.data end +end + +class UserMarshalWithClassName < UserMarshal + def self.name + "Never::A::Real::Class" + end +end + +class UserMarshalWithIvar + attr_reader :data + + def initialize + @data = 'my data' + end + + def marshal_dump + [:data] + end + + def marshal_load(o) + @data = o.first + end + + def ==(other) + self.class === other and + @data = other.data + end +end + +class UserArray < Array +end + +class UserHash < Hash +end + +class UserHashInitParams < Hash + def initialize(a) + @a = a + end +end + +class UserObject +end + +class UserRegexp < Regexp +end + +class UserString < String +end + +class UserCustomConstructorString < String + def initialize(arg1, arg2) + end +end + +module Meths + def meths_method() end +end + +module MethsMore + def meths_more_method() end +end + +Struct.new "Pyramid" +Struct.new "Useful", :a, :b + +module MarshalSpec + class StructWithUserInitialize < Struct.new(:a) + THREADLOCAL_KEY = :marshal_load_struct_args + def initialize(*args) + # using thread-local to avoid ivar marshaling + Thread.current[THREADLOCAL_KEY] = args + super(*args) + end + end + + class BasicObjectSubWithRespondToFalse < BasicObject + def respond_to?(method_name, include_all=false) + false + end + end + + def self.random_data + randomizer = Random.new(42) + 1000.times{randomizer.rand} # Make sure we exhaust his first state of 624 random words + dump_data = File.binread(fixture(__FILE__, 'random.dump')) + [randomizer, dump_data] + rescue => e + ["Error when building Random marshal data #{e}", ""] + end + + SwappedClass = nil + def self.set_swapped_class(cls) + remove_const(:SwappedClass) + const_set(:SwappedClass, cls) + end + + def self.reset_swapped_class + set_swapped_class(nil) + end + + DATA = { + "nil" => [nil, "\004\b0"], + "1..2" => [(1..2), + "\004\bo:\nRange\b:\nbegini\006:\texclF:\bendi\a", + { begin: 1, end: 2, :exclude_end? => false }], + "1...2" => [(1...2), + "\004\bo:\nRange\b:\nbegini\006:\texclT:\bendi\a", + { begin: 1, end: 2, :exclude_end? => true }], + "'a'..'b'" => [('a'..'b'), + "\004\bo:\nRange\b:\nbegin\"\006a:\texclF:\bend\"\006b", + { begin: 'a', end: 'b', :exclude_end? => false }], + "Struct" => [Struct::Useful.new(1, 2), + "\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"], + "Symbol" => [:symbol, + "\004\b:\vsymbol"], + "true" => [true, + "\004\bT"], + "false" => [false, + "\004\bF"], + "String empty" => ['', + "\004\b\"\000"], + "String small" => ['small', + "\004\b\"\012small"], + "String big" => ['big' * 100, + "\004\b\"\002,\001#{'big' * 100}"], + "String extended" => [''.extend(Meths), # TODO: check for module on load + "\004\be:\nMeths\"\000"], + "String subclass" => [UserString.new, + "\004\bC:\017UserString\"\000"], + "String subclass extended" => [UserString.new.extend(Meths), + "\004\be:\nMethsC:\017UserString\"\000"], + "Symbol small" => [:big, + "\004\b:\010big"], + "Symbol big" => [('big' * 100).to_sym, + "\004\b:\002,\001#{'big' * 100}"], + "Bignum -2**64" => [-2**64, + "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"], + "Bignum -2**63" => [-2**63, + "\004\bl-\t\000\000\000\000\000\000\000\200"], + "Fixnum -2**24" => [-2**24, + "\004\bi\375\000\000\000"], + "Fixnum -4516727" => [-4516727, + "\004\bi\375\211\024\273"], + "Fixnum -2**16" => [-2**16, + "\004\bi\376\000\000"], + "Fixnum -2**8" => [-2**8, + "\004\bi\377\000"], + "Fixnum -123" => [-123, + "\004\bi\200"], + "Fixnum -124" => [-124, "\004\bi\377\204"], + "Fixnum 0" => [0, + "\004\bi\000"], + "Fixnum 5" => [5, + "\004\bi\n"], + "Fixnum 122" => [122, "\004\bi\177"], + "Fixnum 123" => [123, "\004\bi\001{"], + "Fixnum 2**8" => [2**8, + "\004\bi\002\000\001"], + "Fixnum 2**16" => [2**16, + "\004\bi\003\000\000\001"], + "Fixnum 2**24" => [2**24, + "\004\bi\004\000\000\000\001"], + "Bignum 2**64" => [2**64, + "\004\bl+\n\000\000\000\000\000\000\000\000\001\000"], + "Bignum 2**90" => [2**90, + "\004\bl+\v#{"\000" * 11}\004"], + "Class String" => [String, + "\004\bc\vString"], + "Module Marshal" => [Marshal, + "\004\bm\fMarshal"], + "Module nested" => [UserDefined::Nested.new, + "\004\bo:\030UserDefined::Nested\000"], + "_dump object" => [UserDefinedWithIvar.new, + "\004\bu:\030UserDefinedWithIvar5\004\b[\bI\"\nstuff\006:\t@foo:\030UserDefinedWithIvar\"\tmore@\a"], + "_dump object extended" => [UserDefined.new.extend(Meths), + "\004\bu:\020UserDefined\022\004\b[\a\"\nstuff@\006"], + "marshal_dump object" => [UserMarshalWithIvar.new, + "\004\bU:\030UserMarshalWithIvar[\006\"\fmy data"], + "Regexp" => [/\A.\Z/, + "\004\b/\n\\A.\\Z\000"], + "Regexp subclass /i" => [UserRegexp.new('', Regexp::IGNORECASE), + "\004\bC:\017UserRegexp/\000\001"], + "Float 0.0" => [0.0, + "\004\bf\0060"], + "Float -0.0" => [-0.0, + "\004\bf\a-0"], + "Float Infinity" => [(1.0 / 0.0), + "\004\bf\binf"], + "Float -Infinity" => [(-1.0 / 0.0), + "\004\bf\t-inf"], + "Float 1.0" => [1.0, + "\004\bf\0061"], + "Float 8323434.342" => [8323434.342, + "\004\bf\0328323434.3420000002\000S\370"], + "Float 1.0799999999999912" => [1.0799999999999912, + "\004\bf\0321.0799999999999912\000\341 "], + "Hash" => [Hash.new, + "\004\b{\000"], + "Hash subclass" => [UserHash.new, + "\004\bC:\rUserHash{\000"], + "Array" => [Array.new, + "\004\b[\000"], + "Array subclass" => [UserArray.new, + "\004\bC:\016UserArray[\000"], + "Struct Pyramid" => [Struct::Pyramid.new, + "\004\bS:\024Struct::Pyramid\000"], + } + DATA_19 = { + "nil" => [nil, "\004\b0"], + "1..2" => [(1..2), + "\004\bo:\nRange\b:\nbegini\006:\texclF:\bendi\a", + { begin: 1, end: 2, :exclude_end? => false }], + "1...2" => [(1...2), + "\004\bo:\nRange\b:\nbegini\006:\texclT:\bendi\a", + { begin: 1, end: 2, :exclude_end? => true }], + "'a'..'b'" => [('a'..'b'), + "\004\bo:\nRange\b:\nbegin\"\006a:\texclF:\bend\"\006b", + { begin: 'a', end: 'b', :exclude_end? => false }], + "Struct" => [Struct::Useful.new(1, 2), + "\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"], + "Symbol" => [:symbol, + "\004\b:\vsymbol"], + "true" => [true, + "\004\bT"], + "false" => [false, + "\004\bF"], + "String empty" => ['', + "\x04\bI\"\x00\x06:\x06EF"], + "String small" => ['small', + "\x04\bI\"\nsmall\x06:\x06EF"], + "String big" => ['big' * 100, + "\x04\bI\"\x02,\x01bigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbig\x06:\x06EF"], + "String extended" => [''.extend(Meths), # TODO: check for module on load + "\x04\bIe:\nMeths\"\x00\x06:\x06EF"], + "String subclass" => [UserString.new, + "\004\bC:\017UserString\"\000"], + "String subclass extended" => [UserString.new.extend(Meths), + "\004\be:\nMethsC:\017UserString\"\000"], + "Symbol small" => [:big, + "\004\b:\010big"], + "Symbol big" => [('big' * 100).to_sym, + "\004\b:\002,\001#{'big' * 100}"], + "Bignum -2**64" => [-2**64, + "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"], + "Bignum -2**63" => [-2**63, + "\004\bl-\t\000\000\000\000\000\000\000\200"], + "Fixnum -2**24" => [-2**24, + "\004\bi\375\000\000\000"], + "Fixnum -2**16" => [-2**16, + "\004\bi\376\000\000"], + "Fixnum -2**8" => [-2**8, + "\004\bi\377\000"], + "Fixnum -123" => [-123, + "\004\bi\200"], + "Fixnum 0" => [0, + "\004\bi\000"], + "Fixnum 5" => [5, + "\004\bi\n"], + "Fixnum 2**8" => [2**8, + "\004\bi\002\000\001"], + "Fixnum 2**16" => [2**16, + "\004\bi\003\000\000\001"], + "Fixnum 2**24" => [2**24, + "\004\bi\004\000\000\000\001"], + "Bignum 2**64" => [2**64, + "\004\bl+\n\000\000\000\000\000\000\000\000\001\000"], + "Bignum 2**90" => [2**90, + "\004\bl+\v#{"\000" * 11}\004"], + "Class String" => [String, + "\004\bc\vString"], + "Module Marshal" => [Marshal, + "\004\bm\fMarshal"], + "Module nested" => [UserDefined::Nested.new, + "\004\bo:\030UserDefined::Nested\000"], + "_dump object" => [UserDefinedWithIvar.new, + "\x04\bu:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a"], + "_dump object extended" => [UserDefined.new.extend(Meths), + "\x04\bu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06"], + "marshal_dump object" => [UserMarshalWithIvar.new, + "\x04\bU:\x18UserMarshalWithIvar[\x06I\"\fmy data\x06:\x06EF"], + "Regexp" => [/\A.\Z/, + "\x04\bI/\n\\A.\\Z\x00\x06:\x06EF"], + "Regexp subclass /i" => [UserRegexp.new('', Regexp::IGNORECASE), + "\x04\bIC:\x0FUserRegexp/\x00\x01\x06:\x06EF"], + "Float 0.0" => [0.0, + "\004\bf\0060"], + "Float -0.0" => [-0.0, + "\004\bf\a-0"], + "Float Infinity" => [(1.0 / 0.0), + "\004\bf\binf"], + "Float -Infinity" => [(-1.0 / 0.0), + "\004\bf\t-inf"], + "Float 1.0" => [1.0, + "\004\bf\0061"], + "Hash" => [Hash.new, + "\004\b{\000"], + "Hash subclass" => [UserHash.new, + "\004\bC:\rUserHash{\000"], + "Array" => [Array.new, + "\004\b[\000"], + "Array subclass" => [UserArray.new, + "\004\bC:\016UserArray[\000"], + "Struct Pyramid" => [Struct::Pyramid.new, + "\004\bS:\024Struct::Pyramid\000"], + "Random" => random_data, + } +end + +class ArraySub < Array + def initialize(*args) + super(args) + end +end + +class ArraySubPush < Array + def << value + raise 'broken' + end + alias_method :push, :<< +end + +class SameName +end + +module NamespaceTest +end diff --git a/spec/rubyspec/core/marshal/fixtures/random.dump b/spec/rubyspec/core/marshal/fixtures/random.dump new file mode 100644 index 0000000000..0af56120aa Binary files /dev/null and b/spec/rubyspec/core/marshal/fixtures/random.dump differ diff --git a/spec/rubyspec/core/marshal/float_spec.rb b/spec/rubyspec/core/marshal/float_spec.rb new file mode 100644 index 0000000000..0bdde3ccc1 --- /dev/null +++ b/spec/rubyspec/core/marshal/float_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Marshal.dump with Float" do + it "represents NaN" do + Marshal.dump(nan_value).should == "\004\bf\bnan" + end + + it "represents +Infinity" do + Marshal.dump(infinity_value).should == "\004\bf\binf" + end + + it "represents -Infinity" do + Marshal.dump(-infinity_value).should == "\004\bf\t-inf" + end + + it "represents zero" do + Marshal.dump(0.0).should == "\004\bf\0060" + end + + it "represents a Float less than 1" do + Marshal.dump(0.666666667).should == "\x04\bf\x100.666666667" + end + + it "represents a Float much less than 1" do + Marshal.dump(0.000000001234697).should == "\x04\bf\x101.234697e-9" + end + + it "represents a Float greater than 1" do + Marshal.dump(42.666666667).should == "\x04\bf\x1142.666666667" + end + + it "represents a Float much greater than 1" do + Marshal.dump(98743561239011.0).should == "\x04\bf\x1398743561239011" + end + + it "represents a Float much greater than 1 with a very small fractional part" do + Marshal.dump(799346183459.0000000002999312541).should == "\x04\bf\x11799346183459" + end +end + +describe "Marshal.load with Float" do + it "loads NaN" do + Marshal.load("\004\bf\bnan").should be_nan + end + + it "loads +Infinity" do + Marshal.load("\004\bf\binf").should == infinity_value + end + + it "loads -Infinity" do + Marshal.load("\004\bf\t-inf").should == -infinity_value + end + + it "loads zero" do + Marshal.load("\004\bf\0060").should == 0.0 + end + + it "loads a Float less than 1" do + Marshal.load("\x04\bf\x100.666666667").should == 0.666666667 + end + + it "loads a Float much less than 1" do + Marshal.load("\x04\bf\x101.234697e-9").should == 0.000000001234697 + end + + it "loads a Float greater than 1" do + Marshal.load("\x04\bf\x1142.666666667").should == 42.666666667 + end + + it "loads a Float much greater than 1" do + Marshal.load("\x04\bf\x1398743561239011").should == 98743561239011.0 + end + + it "loads a Float much greater than 1 with a very small fractional part" do + Marshal.load("\x04\bf\x16793468359.0002999").should == 793468359.0002999 + end +end diff --git a/spec/rubyspec/core/marshal/load_spec.rb b/spec/rubyspec/core/marshal/load_spec.rb new file mode 100644 index 0000000000..53d60f0619 --- /dev/null +++ b/spec/rubyspec/core/marshal/load_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/load', __FILE__) + +describe "Marshal.load" do + it_behaves_like :marshal_load, :load +end diff --git a/spec/rubyspec/core/marshal/major_version_spec.rb b/spec/rubyspec/core/marshal/major_version_spec.rb new file mode 100644 index 0000000000..6984e22b19 --- /dev/null +++ b/spec/rubyspec/core/marshal/major_version_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Marshal::MAJOR_VERSION" do + it "is 4" do + Marshal::MAJOR_VERSION.should == 4 + end +end diff --git a/spec/rubyspec/core/marshal/minor_version_spec.rb b/spec/rubyspec/core/marshal/minor_version_spec.rb new file mode 100644 index 0000000000..cfedcb62b2 --- /dev/null +++ b/spec/rubyspec/core/marshal/minor_version_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Marshal::MINOR_VERSION" do + it "is 8" do + Marshal::MINOR_VERSION.should == 8 + end +end diff --git a/spec/rubyspec/core/marshal/restore_spec.rb b/spec/rubyspec/core/marshal/restore_spec.rb new file mode 100644 index 0000000000..3e84a1780c --- /dev/null +++ b/spec/rubyspec/core/marshal/restore_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/load', __FILE__) + +describe "Marshal.restore" do + it_behaves_like :marshal_load, :restore +end diff --git a/spec/rubyspec/core/marshal/shared/load.rb b/spec/rubyspec/core/marshal/shared/load.rb new file mode 100644 index 0000000000..e642f1a66e --- /dev/null +++ b/spec/rubyspec/core/marshal/shared/load.rb @@ -0,0 +1,830 @@ +# -*- encoding: binary -*- +require File.expand_path('../../fixtures/marshal_data', __FILE__) +require 'stringio' + +describe :marshal_load, shared: true do + before :all do + @num_self_class = 1 + end + + it "raises an ArgumentError when the dumped data is truncated" do + obj = {first: 1, second: 2, third: 3} + lambda { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the dumped class is missing" do + Object.send(:const_set, :KaBoom, Class.new) + kaboom = Marshal.dump(KaBoom.new) + Object.send(:remove_const, :KaBoom) + + lambda { Marshal.send(@method, kaboom) }.should raise_error(ArgumentError) + end + + describe "when called with a proc" do + it "returns the value of the proc" do + Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4] + end + + it "calls the proc for recursively visited data" do + a = [1] + a << a + ret = [] + Marshal.send(@method, Marshal.dump(a), proc { |arg| ret << arg; arg }) + ret.first.should == 1 + ret[1].should == [1,a] + ret[2].should == a + ret.size.should == 3 + end + + it "loads an Array with proc" do + arr = [] + s = 'hi' + s.instance_variable_set(:@foo, 5) + st = Struct.new("Brittle", :a).new + st.instance_variable_set(:@clue, 'none') + st.a = 0.0 + h = Hash.new('def') + h['nine'] = 9 + a = [:a, :b, :c] + a.instance_variable_set(:@two, 2) + obj = [s, 10, s, s, st, a] + obj.instance_variable_set(:@zoo, 'ant') + proc = Proc.new { |o| arr << o; o} + + Marshal.send(@method, "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc) + + arr.should == ["hi", false, 5, 10, "hi", "hi", 0.0, st, "none", false, + :b, :c, a, 2, ["hi", 10, "hi", "hi", st, [:a, :b, :c]], "ant", false] + + Struct.send(:remove_const, :Brittle) + end + end + + describe "when called with nil for the proc argument" do + it "behaves as if no proc argument was passed" do + a = [1] + a << a + b = Marshal.send(@method, Marshal.dump(a), nil) + b.should == a + end + end + + describe "when called on objects with custom _dump methods" do + it "does not set instance variables of an object with user-defined _dump/_load" do + # this string represents: <#UserPreviouslyDefinedWithInitializedIvar @field2=7 @field1=6> + dump_str = "\004\bu:-UserPreviouslyDefinedWithInitializedIvar\a:\f@field2i\f:\f@field1i\v" + + UserPreviouslyDefinedWithInitializedIvar.should_receive(:_load).and_return(UserPreviouslyDefinedWithInitializedIvar.new) + marshaled_obj = Marshal.send(@method, dump_str) + + marshaled_obj.should be_an_instance_of(UserPreviouslyDefinedWithInitializedIvar) + marshaled_obj.field1.should be_nil + marshaled_obj.field2.should be_nil + end + + describe "that return an immediate value" do + it "loads an array containing an instance of the object, followed by multiple instances of another object" do + str = "string" + + # this string represents: [<#UserDefinedImmediate A>, <#String "string">, <#String "string">] + marshaled_obj = Marshal.send(@method, "\004\b[\bu:\031UserDefinedImmediate\000\"\vstring@\a") + + marshaled_obj.should == [nil, str, str] + end + + it "loads any structure with multiple references to the same object, followed by multiple instances of another object" do + str = "string" + + # this string represents: {a: <#UserDefinedImmediate A>, b: <#UserDefinedImmediate A>, c: <#String "string">, d: <#String "string">} + hash_dump = "\x04\b{\t:\x06aIu:\x19UserDefinedImmediate\x00\x06:\x06ET:\x06b@\x06:\x06cI\"\vstring\x06;\aT:\x06d@\a" + + marshaled_obj = Marshal.send(@method, hash_dump) + marshaled_obj.should == {a: nil, b: nil, c: str, d: str} + + # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate A>, <#String "string">, <#String "string">] + array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ET@\x06I\"\vstring\x06;\x06T@\a" + + marshaled_obj = Marshal.send(@method, array_dump) + marshaled_obj.should == [nil, nil, str, str] + end + + it "loads an array containing references to multiple instances of the object, followed by multiple instances of another object" do + str = "string" + + # this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate B>, <#String "string">, <#String "string">] + array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ETIu;\x00\x00\x06;\x06TI\"\vstring\x06;\x06T@\b" + + marshaled_obj = Marshal.send(@method, array_dump) + marshaled_obj.should == [nil, nil, str, str] + end + end + end + + it "loads an array containing objects having _dump method, and with proc" do + arr = [] + myproc = Proc.new { |o| arr << o; o } + o1 = UserDefined.new; + o2 = UserDefinedWithIvar.new + obj = [o1, o2, o1, o2] + + Marshal.send(@method, "\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc) + + arr.should == [o1, o2, o1, o2, obj] + end + + it "loads an array containing objects having marshal_dump method, and with proc" do + arr = [] + proc = Proc.new { |o| arr << o; o } + o1 = UserMarshal.new + o2 = UserMarshalWithIvar.new + obj = [o1, o2, o1, o2] + + Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc) + + arr.should == ['stuff', o1, 'my data', ['my data'], o2, o1, o2, obj] + end + + it "assigns classes to nested subclasses of Array correctly" do + arr = ArraySub.new(ArraySub.new) + arr_dump = Marshal.dump(arr) + Marshal.send(@method, arr_dump).class.should == ArraySub + end + + it "loads subclasses of Array with overridden << and push correctly" do + arr = ArraySubPush.new + arr[0] = '1' + arr_dump = Marshal.dump(arr) + Marshal.send(@method, arr_dump).should == arr + end + + it "raises a TypeError with bad Marshal version" do + marshal_data = '\xff\xff' + marshal_data[0] = (Marshal::MAJOR_VERSION).chr + marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr + + lambda { Marshal.send(@method, marshal_data) }.should raise_error(TypeError) + + marshal_data = '\xff\xff' + marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr + marshal_data[1] = (Marshal::MINOR_VERSION).chr + + lambda { Marshal.send(@method, marshal_data) }.should raise_error(TypeError) + end + + it "raises EOFError on loading an empty file" do + temp_file = tmp("marshal.rubyspec.tmp.#{Process.pid}") + file = File.new(temp_file, "w+") + begin + lambda { Marshal.send(@method, file) }.should raise_error(EOFError) + ensure + file.close + rm_r temp_file + end + end + + it "returns an untainted object if source is untainted" do + x = Object.new + y = Marshal.send(@method, Marshal.dump(x)) + y.tainted?.should be_false + end + + describe "when source is tainted" do + it "returns a tainted object" do + x = Object.new + x.taint + s = Marshal.dump(x) + y = Marshal.send(@method, s) + y.tainted?.should be_true + + # note that round-trip via Marshal does not preserve + # the taintedness at each level of the nested structure + y = Marshal.send(@method, Marshal.dump([[x]])) + y.tainted?.should be_true + y.first.tainted?.should be_true + y.first.first.tainted?.should be_true + end + + it "does not taint Symbols" do + x = [:x] + y = Marshal.send(@method, Marshal.dump(x).taint) + y.tainted?.should be_true + y.first.tainted?.should be_false + end + + it "does not taint Fixnums" do + x = [1] + y = Marshal.send(@method, Marshal.dump(x).taint) + y.tainted?.should be_true + y.first.tainted?.should be_false + end + + it "does not taint Bignums" do + x = [bignum_value] + y = Marshal.send(@method, Marshal.dump(x).taint) + y.tainted?.should be_true + y.first.tainted?.should be_false + end + + it "does not taint Floats" do + x = [1.2] + y = Marshal.send(@method, Marshal.dump(x).taint) + y.tainted?.should be_true + y.first.tainted?.should be_false + end + end + + it "preserves taintedness of nested structure" do + x = Object.new + a = [[x]] + x.taint + y = Marshal.send(@method, Marshal.dump(a)) + y.tainted?.should be_true + y.first.tainted?.should be_true + y.first.first.tainted?.should be_true + end + + it "returns a trusted object if source is trusted" do + x = Object.new + y = Marshal.send(@method, Marshal.dump(x)) + y.untrusted?.should be_false + end + + it "returns an untrusted object if source is untrusted" do + x = Object.new + x.untrust + y = Marshal.send(@method, Marshal.dump(x)) + y.untrusted?.should be_true + + # note that round-trip via Marshal does not preserve + # the untrustedness at each level of the nested structure + y = Marshal.send(@method, Marshal.dump([[x]])) + y.untrusted?.should be_true + y.first.untrusted?.should be_true + y.first.first.untrusted?.should be_true + end + + # Note: Ruby 1.9 should be compatible with older marshal format + MarshalSpec::DATA.each do |description, (object, marshal, attributes)| + it "loads a #{description}" do + Marshal.send(@method, marshal).should == object + end + end + + MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)| + it "loads a #{description}" do + Marshal.send(@method, marshal).should == object + end + end + + describe "for an Array" do + it "loads an array containing the same objects" do + s = 'oh' + b = 'hi' + r = // + d = [b, :no, s, :go] + c = String + f = 1.0 + + o1 = UserMarshalWithIvar.new; o2 = UserMarshal.new + + obj = [:so, 'hello', 100, :so, :so, d, :so, o2, :so, :no, o2, + :go, c, nil, Struct::Pyramid.new, f, :go, :no, s, b, r, + :so, 'huh', o1, true, b, b, 99, r, b, s, :so, f, c, :no, o1, d] + + Marshal.send(@method, "\004\b[*:\aso\"\nhelloii;\000;\000[\t\"\ahi:\ano\"\aoh:\ago;\000U:\020UserMarshal\"\nstuff;\000;\006@\n;\ac\vString0S:\024Struct::Pyramid\000f\0061;\a;\006@\t@\b/\000\000;\000\"\bhuhU:\030UserMarshalWithIvar[\006\"\fmy dataT@\b@\bih@\017@\b@\t;\000@\016@\f;\006@\021@\a").should == + obj + end + + it "loads an array having ivar" do + s = 'well' + s.instance_variable_set(:@foo, 10) + obj = ['5', s, 'hi'].extend(Meths, MethsMore) + obj.instance_variable_set(:@mix, s) + new_obj = Marshal.send(@method, "\004\bI[\b\"\0065I\"\twell\006:\t@fooi\017\"\ahi\006:\t@mix@\a") + new_obj.should == obj + new_obj.instance_variable_get(:@mix).should equal new_obj[1] + new_obj[1].instance_variable_get(:@foo).should == 10 + end + + it "loads an extended Array object containing a user-marshaled object" do + obj = [UserMarshal.new, UserMarshal.new].extend(Meths) + new_obj = Marshal.send(@method, "\x04\be:\nMeths[\ao:\x10UserMarshal\x06:\n@dataI\"\nstuff\x06:\x06ETo;\x06\x06;\aI\"\nstuff\x06;\bT") + + new_obj.should == obj + obj_ancestors = class << obj; ancestors[1..-1]; end + new_obj_ancestors = class << new_obj; ancestors[1..-1]; end + obj_ancestors.should == new_obj_ancestors + end + end + + describe "for a Hash" do + it "loads an extended_user_hash with a parameter to initialize" do + obj = UserHashInitParams.new(:abc).extend(Meths) + + new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\027UserHashInitParams{\000\006:\a@a:\babc") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class].should == Meths + new_obj_metaclass_ancestors[@num_self_class+1].should == UserHashInitParams + end + + it "loads an extended hash object containing a user-marshaled object" do + obj = {a: UserMarshal.new}.extend(Meths) + + new_obj = Marshal.send(@method, "\004\be:\nMeths{\006:\006aU:\020UserMarshal\"\nstuff") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class].should == Meths + new_obj_metaclass_ancestors[@num_self_class+1].should == Hash + end + + it "preserves hash ivars when hash contains a string having ivar" do + s = 'string' + s.instance_variable_set :@string_ivar, 'string ivar' + h = { key: s } + h.instance_variable_set :@hash_ivar, 'hash ivar' + + unmarshalled = Marshal.send(@method, Marshal.dump(h)) + unmarshalled.instance_variable_get(:@hash_ivar).should == 'hash ivar' + unmarshalled[:key].instance_variable_get(:@string_ivar).should == 'string ivar' + end + end + + describe "for a String" do + it "loads a string having ivar with ref to self" do + obj = 'hi' + obj.instance_variable_set(:@self, obj) + Marshal.send(@method, "\004\bI\"\ahi\006:\n@self@\000").should == obj + end + + it "loads a string through StringIO stream" do + obj = "This is a string which should be unmarshalled through StringIO stream!" + Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj + end + + it "loads a string with an ivar" do + str = Marshal.send(@method, "\x04\bI\"\x00\x06:\t@fooI\"\bbar\x06:\x06EF") + str.instance_variable_get("@foo").should == "bar" + end + + it "loads a String subclass with custom constructor" do + str = Marshal.send(@method, "\x04\bC: UserCustomConstructorString\"\x00") + str.should be_an_instance_of(UserCustomConstructorString) + end + + with_feature :encoding do + it "loads a US-ASCII String" do + str = "abc".force_encoding("us-ascii") + data = "\x04\bI\"\babc\x06:\x06EF" + result = Marshal.send(@method, data) + result.should == str + result.encoding.should equal(Encoding::US_ASCII) + end + + it "loads a UTF-8 String" do + str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8") + data = "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET" + result = Marshal.send(@method, data) + result.should == str + result.encoding.should equal(Encoding::UTF_8) + end + + it "loads a String in another encoding" do + str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le") + data = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE" + result = Marshal.send(@method, data) + result.should == str + result.encoding.should equal(Encoding::UTF_16LE) + end + + it "loads a String as ASCII-8BIT if no encoding is specified at the end" do + str = "\xC3\xB8".force_encoding("ASCII-8BIT") + data = "\x04\b\"\a\xC3\xB8".force_encoding("UTF-8") + result = Marshal.send(@method, data) + result.encoding.should == Encoding::ASCII_8BIT + result.should == str + end + end + end + + describe "for a Struct" do + it "loads a extended_struct having fields with same objects" do + s = 'hi' + obj = Struct.new("Ure2", :a, :b).new.extend(Meths) + obj.a = [:a, s] + obj.b = [:Meths, s] + + Marshal.send(@method, + "\004\be:\nMethsS:\021Struct::Ure2\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a" + ).should == obj + Struct.send(:remove_const, :Ure2) + end + + it "loads a struct having ivar" do + obj = Struct.new("Thick").new + obj.instance_variable_set(:@foo, 5) + Marshal.send(@method, "\004\bIS:\022Struct::Thick\000\006:\t@fooi\n").should == obj + Struct.send(:remove_const, :Thick) + end + + it "loads a struct having fields" do + obj = Struct.new("Ure1", :a, :b).new + Marshal.send(@method, "\004\bS:\021Struct::Ure1\a:\006a0:\006b0").should == obj + Struct.send(:remove_const, :Ure1) + end + + it "does not call initialize on the unmarshaled struct" do + threadlocal_key = MarshalSpec::StructWithUserInitialize::THREADLOCAL_KEY + + s = MarshalSpec::StructWithUserInitialize.new('foo') + Thread.current[threadlocal_key].should == ['foo'] + s.a.should == 'foo' + + Thread.current[threadlocal_key] = nil + + dumped = Marshal.dump(s) + loaded = Marshal.send(@method, dumped) + + Thread.current[threadlocal_key].should == nil + loaded.a.should == 'foo' + end + end + + describe "for an Exception" do + it "loads a marshalled exception with no message" do + obj = Exception.new + loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg0") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesg0:\abt0") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + end + + it "loads a marshalled exception with a message" do + obj = Exception.new("foo") + loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg\"\bfoo") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt0") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + end + + it "loads a marshalled exception with a backtrace" do + obj = Exception.new("foo") + obj.set_backtrace(["foo/bar.rb:10"]) + loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt[\006\"\022foo/bar.rb:10:\tmesg\"\bfoo") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt[\x06I\"\x12foo/bar.rb:10\x06;\aF") + loaded.message.should == obj.message + loaded.backtrace.should == obj.backtrace + end + end + + describe "for a user Class" do + it "loads a user-marshaled extended object" do + obj = UserMarshal.new.extend(Meths) + + new_obj = Marshal.send(@method, "\004\bU:\020UserMarshal\"\nstuff") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal + end + + it "loads a user_object" do + UserObject.new + Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject) + end + + it "loads an object" do + Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object) + end + + it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do + lambda do + Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd") + end.should raise_error(ArgumentError) + end + + it "loads an extended Object" do + obj = Object.new.extend(Meths) + + new_obj = Marshal.send(@method, "\004\be:\nMethso:\vObject\000") + + new_obj.class.should == obj.class + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object] + end + + describe "that extends a core type other than Object or BasicObject" do + after :each do + MarshalSpec.reset_swapped_class + end + + it "raises ArgumentError if the resulting class does not extend the same type" do + MarshalSpec.set_swapped_class(Class.new(Hash)) + data = Marshal.dump(MarshalSpec::SwappedClass.new) + + MarshalSpec.set_swapped_class(Class.new(Array)) + lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError) + + MarshalSpec.set_swapped_class(Class.new) + lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError) + end + end + + it "loads an object having ivar" do + s = 'hi' + arr = [:so, :so, s, s] + obj = Object.new + obj.instance_variable_set :@str, arr + + new_obj = Marshal.send(@method, "\004\bo:\vObject\006:\t@str[\t:\aso;\a\"\ahi@\a") + new_str = new_obj.instance_variable_get :@str + + new_str.should == arr + end + end + + describe "for a Regexp" do + it "loads an extended Regexp" do + obj = /[a-z]/.extend(Meths, MethsMore) + new_obj = Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000") + + new_obj.should == obj + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class, 3].should == + [Meths, MethsMore, Regexp] + end + + it "loads a extended_user_regexp having ivar" do + obj = UserRegexp.new('').extend(Meths) + obj.instance_variable_set(:@noise, 'much') + + new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\017UserRegexp/\000\000\006:\v@noise\"\tmuch") + + new_obj.should == obj + new_obj.instance_variable_get(:@noise).should == 'much' + new_obj_metaclass_ancestors = class << new_obj; ancestors; end + new_obj_metaclass_ancestors[@num_self_class, 3].should == + [Meths, UserRegexp, Regexp] + end + end + + describe "for a Float" do + it "loads a Float NaN" do + obj = 0.0 / 0.0 + Marshal.send(@method, "\004\bf\bnan").to_s.should == obj.to_s + end + + it "loads a Float 1.3" do + Marshal.send(@method, "\004\bf\v1.3\000\314\315").should == 1.3 + end + + it "loads a Float -5.1867345e-22" do + obj = -5.1867345e-22 + Marshal.send(@method, "\004\bf\037-5.1867345000000008e-22\000\203_").should be_close(obj, 1e-30) + end + + it "loads a Float 1.1867345e+22" do + obj = 1.1867345e+22 + Marshal.send(@method, "\004\bf\0361.1867344999999999e+22\000\344@").should == obj + end + end + + describe "for a Integer" do + it "loads 0" do + Marshal.send(@method, "\004\bi\000").should == 0 + Marshal.send(@method, "\004\bi\005").should == 0 + end + + it "loads an Integer 8" do + Marshal.send(@method, "\004\bi\r" ).should == 8 + end + + it "loads and Integer -8" do + Marshal.send(@method, "\004\bi\363" ).should == -8 + end + + it "loads an Integer 1234" do + Marshal.send(@method, "\004\bi\002\322\004").should == 1234 + end + + it "loads an Integer -1234" do + Marshal.send(@method, "\004\bi\376.\373").should == -1234 + end + + it "loads an Integer 4611686018427387903" do + Marshal.send(@method, "\004\bl+\t\377\377\377\377\377\377\377?").should == 4611686018427387903 + end + + it "loads an Integer -4611686018427387903" do + Marshal.send(@method, "\004\bl-\t\377\377\377\377\377\377\377?").should == -4611686018427387903 + end + + it "loads an Integer 2361183241434822606847" do + Marshal.send(@method, "\004\bl+\n\377\377\377\377\377\377\377\377\177\000").should == 2361183241434822606847 + end + + it "loads an Integer -2361183241434822606847" do + Marshal.send(@method, "\004\bl-\n\377\377\377\377\377\377\377\377\177\000").should == -2361183241434822606847 + end + + it "raises ArgumentError if the input is too short" do + ["\004\bi", + "\004\bi\001", + "\004\bi\002", + "\004\bi\002\0", + "\004\bi\003", + "\004\bi\003\0", + "\004\bi\003\0\0", + "\004\bi\004", + "\004\bi\004\0", + "\004\bi\004\0\0", + "\004\bi\004\0\0\0"].each do |invalid| + lambda { Marshal.send(@method, invalid) }.should raise_error(ArgumentError) + end + end + + if 0.size == 8 # for platforms like x86_64 + it "roundtrips 4611686018427387903 from dump/load correctly" do + Marshal.send(@method, Marshal.dump(4611686018427387903)).should == 4611686018427387903 + end + end + end + + describe "for a Rational" do + it "loads" do + Marshal.send(@method, Marshal.dump(Rational(1, 3))).should == Rational(1, 3) + end + end + + describe "for a Complex" do + it "loads" do + Marshal.send(@method, Marshal.dump(Complex(4, 3))).should == Complex(4, 3) + end + end + + describe "for a Bignum" do + platform_is wordsize: 64 do + context "that is Bignum on 32-bit platforms but Fixnum on 64-bit" do + it "dumps a Fixnum" do + val = Marshal.send(@method, "\004\bl+\ab:wU") + val.should == 1433877090 + val.class.should == Fixnum + end + + it "dumps an array containing multiple references to the Bignum as an array of Fixnum" do + arr = Marshal.send(@method, "\004\b[\al+\a\223BwU@\006") + arr.should == [1433879187, 1433879187] + arr.each { |v| v.class.should == Fixnum } + end + end + end + end + + describe "for a Time" do + it "loads" do + Marshal.send(@method, Marshal.dump(Time.at(1))).should == Time.at(1) + end + + it "loads serialized instance variables" do + t = Time.new + t.instance_variable_set(:@foo, 'bar') + + Marshal.send(@method, Marshal.dump(t)).instance_variable_get(:@foo).should == 'bar' + end + + it "loads Time objects stored as links" do + t = Time.new + + t1, t2 = Marshal.send(@method, Marshal.dump([t, t])) + t1.should equal t2 + end + + it "loads the zone" do + with_timezone 'AST', 3 do + t = Time.local(2012, 1, 1) + Marshal.send(@method, Marshal.dump(t)).zone.should == t.zone + end + end + + it "loads nanoseconds" do + t = Time.now + Marshal.send(@method, Marshal.dump(t)).nsec.should == t.nsec + end + end + + describe "for nil" do + it "loads" do + Marshal.send(@method, "\x04\b0").should be_nil + end + end + + describe "for true" do + it "loads" do + Marshal.send(@method, "\x04\bT").should be_true + end + end + + describe "for false" do + it "loads" do + Marshal.send(@method, "\x04\bF").should be_false + end + end + + describe "for a Class" do + it "loads" do + Marshal.send(@method, "\x04\bc\vString").should == String + end + + it "raises ArgumentError if given the name of a non-Module" do + lambda { Marshal.send(@method, "\x04\bc\vKernel") }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if given a nonexistent class" do + lambda { Marshal.send(@method, "\x04\bc\vStrung") }.should raise_error(ArgumentError) + end + end + + describe "for a Module" do + it "loads a module" do + Marshal.send(@method, "\x04\bm\vKernel").should == Kernel + end + + it "raises ArgumentError if given the name of a non-Class" do + lambda { Marshal.send(@method, "\x04\bm\vString") }.should raise_error(ArgumentError) + end + + it "loads an old module" do + Marshal.send(@method, "\x04\bM\vKernel").should == Kernel + end + end + + describe "for a wrapped C pointer" do + it "loads" do + class DumpableDir < Dir + def _dump_data + path + end + def _load_data path + initialize(path) + end + end + + data = "\x04\bd:\x10DumpableDirI\"\x06.\x06:\x06ET" + + dir = Marshal.send(@method, data) + begin + dir.path.should == '.' + ensure + dir.close + end + end + + it "raises TypeError when the local class is missing _load_data" do + class UnloadableDumpableDir < Dir + def _dump_data + path + end + # no _load_data + end + + data = "\x04\bd:\x1AUnloadableDumpableDirI\"\x06.\x06:\x06ET" + + lambda { Marshal.send(@method, data) }.should raise_error(TypeError) + end + + it "raises ArgumentError when the local class is a regular object" do + data = "\004\bd:\020UserDefined\0" + + lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError) + end + end + + describe "when a class does not exist in the namespace" do + before :each do + NamespaceTest.send(:const_set, :SameName, Class.new) + @data = Marshal.dump(NamespaceTest::SameName.new) + NamespaceTest.send(:remove_const, :SameName) + end + + it "raises an ArgumentError" do + message = "undefined class/module NamespaceTest::SameName" + lambda { Marshal.send(@method, @data) }.should raise_error(ArgumentError, message) + end + end + + it "raises an ArgumentError with full constant name when the dumped constant is missing" do + NamespaceTest.send(:const_set, :KaBoom, Class.new) + @data = Marshal.dump(NamespaceTest::KaBoom.new) + NamespaceTest.send(:remove_const, :KaBoom) + + lambda { Marshal.send(@method, @data) }.should raise_error(ArgumentError, /NamespaceTest::KaBoom/) + end +end diff --git a/spec/rubyspec/core/matchdata/begin_spec.rb b/spec/rubyspec/core/matchdata/begin_spec.rb new file mode 100644 index 0000000000..b3d042066d --- /dev/null +++ b/spec/rubyspec/core/matchdata/begin_spec.rb @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- + +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#begin" do + it "returns the offset of the start of the nth element" do + match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + match_data.begin(0).should == 1 + match_data.begin(2).should == 2 + end + + it "returns nil when the nth match isn't found" do + match_data = /something is( not)? (right)/.match("something is right") + match_data.begin(1).should be_nil + end + + it "returns the offset for multi byte strings" do + match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") + match_data.begin(0).should == 1 + match_data.begin(2).should == 2 + end + + not_supported_on :opal do + it "returns the offset for multi byte strings with unicode regexp" do + match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") + match_data.begin(0).should == 1 + match_data.begin(2).should == 2 + end + end +end diff --git a/spec/rubyspec/core/matchdata/captures_spec.rb b/spec/rubyspec/core/matchdata/captures_spec.rb new file mode 100644 index 0000000000..2a83b26cd4 --- /dev/null +++ b/spec/rubyspec/core/matchdata/captures_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#captures" do + it "returns an array of the match captures" do + /(.)(.)(\d+)(\d)/.match("THX1138.").captures.should == ["H","X","113","8"] + end +end diff --git a/spec/rubyspec/core/matchdata/element_reference_spec.rb b/spec/rubyspec/core/matchdata/element_reference_spec.rb new file mode 100644 index 0000000000..17d16a5526 --- /dev/null +++ b/spec/rubyspec/core/matchdata/element_reference_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#[]" do + it "acts as normal array indexing [index]" do + md = /(.)(.)(\d+)(\d)/.match("THX1138.") + + md[0].should == 'HX1138' + md[1].should == 'H' + md[2].should == 'X' + md[-3].should == 'X' + md[10000].should == nil + md[-10000].should == nil + end + + it "supports accessors [start, length]" do + /(.)(.)(\d+)(\d)/.match("THX1138.")[1, 2].should == %w|H X| + /(.)(.)(\d+)(\d)/.match("THX1138.")[-3, 2].should == %w|X 113| + end + + it "supports ranges [start..end]" do + /(.)(.)(\d+)(\d)/.match("THX1138.")[1..3].should == %w|H X 113| + end +end + +describe "MatchData#[Symbol]" do + it "returns the corresponding named match when given a Symbol" do + md = 'haystack'.match(/(?t(?ack))/) + md[:a].should == 'ack' + md[:t].should == 'tack' + end + + it "returns the corresponding named match when given a String" do + md = 'haystack'.match(/(?t(?ack))/) + md['a'].should == 'ack' + md['t'].should == 'tack' + end + + it "returns the matching version of multiple corresponding named match" do + regexp = /(?: + A(?\w+) + | + B(?\w+) + )/x + md_a = regexp.match("Afoo") + md_b = regexp.match("Bfoo") + + md_a[:word].should == "foo" + md_b[:word].should == "foo" + + md_a['word'].should == "foo" + md_b['word'].should == "foo" + end + + it "returns the last match when multiple named matches exist with the same name" do + md = /(?hay)(?stack)/.match('haystack') + md[:word].should == "stack" + md['word'].should == "stack" + end + + it "returns nil on non-matching named matches" do + regexp = /(?foo )?(?bar)/ + full_match = regexp.match("foo bar") + partial_match = regexp.match("bar") + + full_match[:foo].should == "foo " + partial_match[:foo].should == nil + + full_match['foo'].should == "foo " + partial_match['foo'].should == nil + end + + it "raises an IndexError if there is no named match corresponding to the Symbol" do + md = 'haystack'.match(/(?t(?ack))/) + lambda { md[:baz] }.should raise_error(IndexError, /baz/) + end + + it "raises an IndexError if there is no named match corresponding to the String" do + md = 'haystack'.match(/(?t(?ack))/) + lambda { md['baz'] }.should raise_error(IndexError, /baz/) + end + + it "returns matches in the String's encoding" do + rex = /(?t(?ack))/u + md = 'haystack'.force_encoding('euc-jp').match(rex) + md[:t].encoding.should == Encoding::EUC_JP + end +end diff --git a/spec/rubyspec/core/matchdata/end_spec.rb b/spec/rubyspec/core/matchdata/end_spec.rb new file mode 100644 index 0000000000..4e74492105 --- /dev/null +++ b/spec/rubyspec/core/matchdata/end_spec.rb @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- + +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#end" do + it "returns the offset of the end of the nth element" do + match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + match_data.end(0).should == 7 + match_data.end(2).should == 3 + end + + it "returns nil when the nth match isn't found" do + match_data = /something is( not)? (right)/.match("something is right") + match_data.end(1).should be_nil + end + + it "returns the offset for multi byte strings" do + match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") + match_data.end(0).should == 7 + match_data.end(2).should == 3 + end + + not_supported_on :opal do + it "returns the offset for multi byte strings with unicode regexp" do + match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") + match_data.end(0).should == 7 + match_data.end(2).should == 3 + end + end +end diff --git a/spec/rubyspec/core/matchdata/eql_spec.rb b/spec/rubyspec/core/matchdata/eql_spec.rb new file mode 100644 index 0000000000..192acbcd13 --- /dev/null +++ b/spec/rubyspec/core/matchdata/eql_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "MatchData#eql?" do + it_behaves_like(:matchdata_eql, :eql?) +end diff --git a/spec/rubyspec/core/matchdata/equal_value_spec.rb b/spec/rubyspec/core/matchdata/equal_value_spec.rb new file mode 100644 index 0000000000..0d33d0acf2 --- /dev/null +++ b/spec/rubyspec/core/matchdata/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "MatchData#==" do + it_behaves_like(:matchdata_eql, :==) +end diff --git a/spec/rubyspec/core/matchdata/hash_spec.rb b/spec/rubyspec/core/matchdata/hash_spec.rb new file mode 100644 index 0000000000..1d5c8a203f --- /dev/null +++ b/spec/rubyspec/core/matchdata/hash_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#hash" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/matchdata/inspect_spec.rb b/spec/rubyspec/core/matchdata/inspect_spec.rb new file mode 100644 index 0000000000..3cf968b6f5 --- /dev/null +++ b/spec/rubyspec/core/matchdata/inspect_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#inspect" do + before :each do + @match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + end + + it "returns a String" do + @match_data.inspect.should be_kind_of(String) + end + + it "returns a human readable representation that contains entire matched string and the captures" do + # yeah, hardcoding the inspect output is not ideal, but in this case + # it makes perfect sense. See JRUBY-4558 for example. + @match_data.inspect.should == '#' + end +end diff --git a/spec/rubyspec/core/matchdata/length_spec.rb b/spec/rubyspec/core/matchdata/length_spec.rb new file mode 100644 index 0000000000..75cf607598 --- /dev/null +++ b/spec/rubyspec/core/matchdata/length_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "MatchData#length" do + it_behaves_like(:matchdata_length, :length) +end diff --git a/spec/rubyspec/core/matchdata/names_spec.rb b/spec/rubyspec/core/matchdata/names_spec.rb new file mode 100644 index 0000000000..e298c85593 --- /dev/null +++ b/spec/rubyspec/core/matchdata/names_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#names" do + it "returns an Array" do + md = 'haystack'.match(/(?hay)/) + md.names.should be_an_instance_of(Array) + end + + it "sets each element to a String" do + 'haystack'.match(/(?hay)/).names.all? do |e| + e.should be_an_instance_of(String) + end + end + + it "returns the names of the named capture groups" do + md = 'haystack'.match(/(?hay).(?tack)/) + md.names.should == ['yellow', 'pin'] + end + + it "returns [] if there were no named captures" do + 'haystack'.match(/(hay).(tack)/).names.should == [] + end + + it "returns each name only once" do + md = 'haystack'.match(/(?hay)(?.)(?tack)/) + md.names.should == ['hay', 'dot'] + end + + it "equals Regexp#names" do + r = /(?hay)(?.)(?tack)/ + 'haystack'.match(r).names.should == r.names + end +end diff --git a/spec/rubyspec/core/matchdata/offset_spec.rb b/spec/rubyspec/core/matchdata/offset_spec.rb new file mode 100644 index 0000000000..6ddb56c1a7 --- /dev/null +++ b/spec/rubyspec/core/matchdata/offset_spec.rb @@ -0,0 +1,30 @@ +# -*- encoding: utf-8 -*- + +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#offset" do + it "returns a two element array with the begin and end of the nth match" do + match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + match_data.offset(0).should == [1, 7] + match_data.offset(4).should == [6, 7] + end + + it "returns [nil, nil] when the nth match isn't found" do + match_data = /something is( not)? (right)/.match("something is right") + match_data.offset(1).should == [nil, nil] + end + + it "returns the offset for multi byte strings" do + match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") + match_data.offset(0).should == [1, 7] + match_data.offset(4).should == [6, 7] + end + + not_supported_on :opal do + it "returns the offset for multi byte strings with unicode regexp" do + match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") + match_data.offset(0).should == [1, 7] + match_data.offset(4).should == [6, 7] + end + end +end diff --git a/spec/rubyspec/core/matchdata/post_match_spec.rb b/spec/rubyspec/core/matchdata/post_match_spec.rb new file mode 100644 index 0000000000..670e0887ab --- /dev/null +++ b/spec/rubyspec/core/matchdata/post_match_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#post_match" do + it "returns the string after the match equiv. special var $'" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").post_match.should == ': The Movie' + $'.should == ': The Movie' + end + + it "keeps taint status from the source string" do + str = "THX1138: The Movie" + str.taint + res = /(.)(.)(\d+)(\d)/.match(str).post_match + res.tainted?.should be_true + $'.tainted?.should be_true + end + + it "keeps untrusted status from the source string" do + str = "THX1138: The Movie" + str.untrust + res = /(.)(.)(\d+)(\d)/.match(str).post_match + res.untrusted?.should be_true + $'.untrusted?.should be_true + end + + with_feature :encoding do + it "sets the encoding to the encoding of the source String" do + str = "abc".force_encoding Encoding::EUC_JP + str.match(/b/).post_match.encoding.should equal(Encoding::EUC_JP) + end + + it "sets an empty result to the encoding of the source String" do + str = "abc".force_encoding Encoding::ISO_8859_1 + str.match(/c/).post_match.encoding.should equal(Encoding::ISO_8859_1) + end + end +end diff --git a/spec/rubyspec/core/matchdata/pre_match_spec.rb b/spec/rubyspec/core/matchdata/pre_match_spec.rb new file mode 100644 index 0000000000..1f1f9daec6 --- /dev/null +++ b/spec/rubyspec/core/matchdata/pre_match_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#pre_match" do + it "returns the string before the match, equiv. special var $`" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").pre_match.should == 'T' + $`.should == 'T' + end + + it "keeps taint status from the source string" do + str = "THX1138: The Movie" + str.taint + res = /(.)(.)(\d+)(\d)/.match(str).pre_match + res.tainted?.should be_true + $`.tainted?.should be_true + end + + it "keeps untrusted status from the source string" do + str = "THX1138: The Movie" + str.untrust + res = /(.)(.)(\d+)(\d)/.match(str).pre_match + res.untrusted?.should be_true + $`.untrusted?.should be_true + end + + with_feature :encoding do + it "sets the encoding to the encoding of the source String" do + str = "abc".force_encoding Encoding::EUC_JP + str.match(/b/).pre_match.encoding.should equal(Encoding::EUC_JP) + end + + it "sets an empty result to the encoding of the source String" do + str = "abc".force_encoding Encoding::ISO_8859_1 + str.match(/a/).pre_match.encoding.should equal(Encoding::ISO_8859_1) + end + end +end diff --git a/spec/rubyspec/core/matchdata/regexp_spec.rb b/spec/rubyspec/core/matchdata/regexp_spec.rb new file mode 100644 index 0000000000..2fdca34c30 --- /dev/null +++ b/spec/rubyspec/core/matchdata/regexp_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#regexp" do + it "returns a Regexp object" do + m = 'haystack'.match(/hay/) + m.regexp.should be_an_instance_of(Regexp) + end + + it "returns the pattern used in the match" do + m = 'haystack'.match(/hay/) + m.regexp.should == /hay/ + end +end diff --git a/spec/rubyspec/core/matchdata/shared/eql.rb b/spec/rubyspec/core/matchdata/shared/eql.rb new file mode 100644 index 0000000000..098d82db5b --- /dev/null +++ b/spec/rubyspec/core/matchdata/shared/eql.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :matchdata_eql, shared: true do + it "returns true if both operands have equal target strings, patterns, and match positions" do + a = 'haystack'.match(/hay/) + b = 'haystack'.match(/hay/) + a.send(@method, b).should be_true + end + + it "returns false if the operands have different target strings" do + a = 'hay'.match(/hay/) + b = 'haystack'.match(/hay/) + a.send(@method, b).should be_false + end + + it "returns false if the operands have different patterns" do + a = 'haystack'.match(/h.y/) + b = 'haystack'.match(/hay/) + a.send(@method, b).should be_false + end + + it "returns false if the argument is not a MatchData object" do + a = 'haystack'.match(/hay/) + a.send(@method, Object.new).should be_false + end +end diff --git a/spec/rubyspec/core/matchdata/shared/length.rb b/spec/rubyspec/core/matchdata/shared/length.rb new file mode 100644 index 0000000000..6312a7ed4c --- /dev/null +++ b/spec/rubyspec/core/matchdata/shared/length.rb @@ -0,0 +1,5 @@ +describe :matchdata_length, shared: true do + it "length should return the number of elements in the match array" do + /(.)(.)(\d+)(\d)/.match("THX1138.").send(@method).should == 5 + end +end diff --git a/spec/rubyspec/core/matchdata/size_spec.rb b/spec/rubyspec/core/matchdata/size_spec.rb new file mode 100644 index 0000000000..e043f51870 --- /dev/null +++ b/spec/rubyspec/core/matchdata/size_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "MatchData#size" do + it_behaves_like(:matchdata_length, :size) +end diff --git a/spec/rubyspec/core/matchdata/string_spec.rb b/spec/rubyspec/core/matchdata/string_spec.rb new file mode 100644 index 0000000000..793684d36a --- /dev/null +++ b/spec/rubyspec/core/matchdata/string_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#string" do + it "returns a copy of the match string" do + str = /(.)(.)(\d+)(\d)/.match("THX1138.").string + str.should == "THX1138." + end + + it "returns a frozen copy of the match string" do + str = /(.)(.)(\d+)(\d)/.match("THX1138.").string + str.should == "THX1138." + str.frozen?.should == true + end +end diff --git a/spec/rubyspec/core/matchdata/to_a_spec.rb b/spec/rubyspec/core/matchdata/to_a_spec.rb new file mode 100644 index 0000000000..592a68db0e --- /dev/null +++ b/spec/rubyspec/core/matchdata/to_a_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#to_a" do + it "returns an array of matches" do + /(.)(.)(\d+)(\d)/.match("THX1138.").to_a.should == ["HX1138", "H", "X", "113", "8"] + end +end diff --git a/spec/rubyspec/core/matchdata/to_s_spec.rb b/spec/rubyspec/core/matchdata/to_s_spec.rb new file mode 100644 index 0000000000..3eb3b92533 --- /dev/null +++ b/spec/rubyspec/core/matchdata/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#to_s" do + it "returns the entire matched string" do + /(.)(.)(\d+)(\d)/.match("THX1138.").to_s.should == "HX1138" + end +end diff --git a/spec/rubyspec/core/matchdata/values_at_spec.rb b/spec/rubyspec/core/matchdata/values_at_spec.rb new file mode 100644 index 0000000000..8d786246f9 --- /dev/null +++ b/spec/rubyspec/core/matchdata/values_at_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "MatchData#values_at" do + it "returns an array of the matching value" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0, 2, -2).should == ["HX1138", "X", "113"] + end + + describe "when passed a Range" do + it "returns an array of the matching value" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(2..4, 0..1).should == ["X", "113", "8", "HX1138", "H"] + end + end +end diff --git a/spec/rubyspec/core/math/acos_spec.rb b/spec/rubyspec/core/math/acos_spec.rb new file mode 100644 index 0000000000..b0fd9baa91 --- /dev/null +++ b/spec/rubyspec/core/math/acos_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# arccosine : (-1.0, 1.0) --> (0, PI) +describe "Math.acos" do + before :each do + ScratchPad.clear + end + + it "returns a float" do + Math.acos(1).should be_kind_of(Float ) + end + + it "returns the arccosine of the argument" do + Math.acos(1).should be_close(0.0, TOLERANCE) + Math.acos(0).should be_close(1.5707963267949, TOLERANCE) + Math.acos(-1).should be_close(Math::PI,TOLERANCE) + Math.acos(0.25).should be_close(1.31811607165282, TOLERANCE) + Math.acos(0.50).should be_close(1.0471975511966 , TOLERANCE) + Math.acos(0.75).should be_close(0.722734247813416, TOLERANCE) + end + + conflicts_with :Complex do + it "raises an Errno::EDOM if the argument is greater than 1.0" do + lambda { Math.acos(1.0001) }.should raise_error(Errno::EDOM) + end + + it "raises an Errno::EDOM if the argument is less than -1.0" do + lambda { Math.acos(-1.0001) }.should raise_error(Errno::EDOM) + end + end + + it "raises a TypeError if the string argument cannot be coerced with Float()" do + lambda { Math.acos("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.acos(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.acos(MathSpecs::UserClass.new) }.should raise_error(TypeError) + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.acos(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.acos(MathSpecs::Float.new(0.5)).should be_close(Math.acos(0.5), TOLERANCE) + end +end + +describe "Math#acos" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:acos, 0).should be_close(1.5707963267949, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/acosh_spec.rb b/spec/rubyspec/core/math/acosh_spec.rb new file mode 100644 index 0000000000..272bfaf6a9 --- /dev/null +++ b/spec/rubyspec/core/math/acosh_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.acosh" do + it "returns a float" do + Math.acosh(1.0).should be_kind_of(Float) + end + + it "returns the principle value of the inverse hyperbolic cosine of the argument" do + Math.acosh(14.2).should be_close(3.345146999647, TOLERANCE) + Math.acosh(1.0).should be_close(0.0, TOLERANCE) + end + + conflicts_with :Complex do + it "raises Errno::EDOM if the passed argument is less than -1.0 or greater than 1.0" do + lambda { Math.acosh(1.0 - TOLERANCE) }.should raise_error(Errno::EDOM) + lambda { Math.acosh(0) }.should raise_error(Errno::EDOM) + lambda { Math.acosh(-1.0) }.should raise_error(Errno::EDOM) + end + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.acosh("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.acosh(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.acosh(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.acosh(MathSpecs::Float.new).should == 0.0 + end +end + +describe "Math#acosh" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:acosh, 1.0).should be_close(0.0, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/asin_spec.rb b/spec/rubyspec/core/math/asin_spec.rb new file mode 100644 index 0000000000..ef3426bceb --- /dev/null +++ b/spec/rubyspec/core/math/asin_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# arcsine : (-1.0, 1.0) --> (-PI/2, PI/2) +describe "Math.asin" do + it "returns a float" do + Math.asin(1).should be_kind_of(Float) + end + + it "returns the arcsine of the argument" do + Math.asin(1).should be_close(Math::PI/2, TOLERANCE) + Math.asin(0).should be_close(0.0, TOLERANCE) + Math.asin(-1).should be_close(-Math::PI/2, TOLERANCE) + Math.asin(0.25).should be_close(0.252680255142079, TOLERANCE) + Math.asin(0.50).should be_close(0.523598775598299, TOLERANCE) + Math.asin(0.75).should be_close(0.8480620789814816,TOLERANCE) + end + + conflicts_with :Complex do + it "raises an Errno::EDOM if the argument is greater than 1.0" do + lambda { Math.asin(1.0001) }.should raise_error( Errno::EDOM) + end + + it "raises an Errno::EDOM if the argument is less than -1.0" do + lambda { Math.asin(-1.0001) }.should raise_error( Errno::EDOM) + end + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.asin("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.asin(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.asin(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.asin(MathSpecs::Float.new).should be_close(1.5707963267949, TOLERANCE) + end +end + +describe "Math#asin" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:asin, 0.5).should be_close(0.523598775598299, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/asinh_spec.rb b/spec/rubyspec/core/math/asinh_spec.rb new file mode 100644 index 0000000000..0761285806 --- /dev/null +++ b/spec/rubyspec/core/math/asinh_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.asinh" do + it "returns a float" do + Math.asinh(1.5).should be_kind_of(Float) + end + + it "returns the inverse hyperbolic sin of the argument" do + Math.asinh(1.5).should be_close(1.19476321728711, TOLERANCE) + Math.asinh(-2.97).should be_close(-1.8089166921397, TOLERANCE) + Math.asinh(0.0).should == 0.0 + Math.asinh(-0.0).should == -0.0 + Math.asinh(1.05367e-08).should be_close(1.05367e-08, TOLERANCE) + Math.asinh(-1.05367e-08).should be_close(-1.05367e-08, TOLERANCE) + # Default tolerance does not scale right for these... + #Math.asinh(94906265.62).should be_close(19.0615, TOLERANCE) + #Math.asinh(-94906265.62).should be_close(-19.0615, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.asinh("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.asinh(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.asinh(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.asinh(MathSpecs::Float.new).should be_close(0.881373587019543, TOLERANCE) + end +end + +describe "Math#asinh" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:asinh, 19.275).should be_close(3.65262832292466, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/atan2_spec.rb b/spec/rubyspec/core/math/atan2_spec.rb new file mode 100644 index 0000000000..ef8f9bb78f --- /dev/null +++ b/spec/rubyspec/core/math/atan2_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.atan2" do + it "returns a float" do + Math.atan2(1.2, 0.5).should be_kind_of(Float) + end + + it "returns the arc tangent of y, x" do + Math.atan2(4.2, 0.3).should be_close(1.49948886200961, TOLERANCE) + Math.atan2(0.0, 1.0).should be_close(0.0, TOLERANCE) + Math.atan2(-9.1, 3.2).should be_close(-1.23265379809025, TOLERANCE) + Math.atan2(7.22, -3.3).should be_close(1.99950888779256, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.atan2(1.0, "test") }.should raise_error(TypeError) + lambda { Math.atan2("test", 0.0) }.should raise_error(TypeError) + lambda { Math.atan2("test", "this") }.should raise_error(TypeError) + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.atan2(nil, 1.0) }.should raise_error(TypeError) + lambda { Math.atan2(-1.0, nil) }.should raise_error(TypeError) + lambda { Math.atan2(nil, nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.atan2(MathSpecs::Float.new, MathSpecs::Float.new).should be_close(0.785398163397448, TOLERANCE) + end + + it "returns positive zero when passed 0.0, 0.0" do + Math.atan2(0.0, 0.0).should be_positive_zero + end + + it "returns negative zero when passed -0.0, 0.0" do + Math.atan2(-0.0, 0.0).should be_negative_zero + end + + it "returns Pi when passed 0.0, -0.0" do + Math.atan2(0.0, -0.0).should == Math::PI + end + + it "returns -Pi when passed -0.0, -0.0" do + Math.atan2(-0.0, -0.0).should == -Math::PI + end + +end + +describe "Math#atan2" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:atan2, 1.1, 2.2).should be_close(0.463647609000806, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/atan_spec.rb b/spec/rubyspec/core/math/atan_spec.rb new file mode 100644 index 0000000000..6787479cd9 --- /dev/null +++ b/spec/rubyspec/core/math/atan_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# arctangent : (-Inf, Inf) --> (-PI/2, PI/2) +describe "Math.atan" do + it "returns a float" do + Math.atan(1).should be_kind_of(Float) + end + + it "returns the arctangent of the argument" do + Math.atan(1).should be_close(Math::PI/4, TOLERANCE) + Math.atan(0).should be_close(0.0, TOLERANCE) + Math.atan(-1).should be_close(-Math::PI/4, TOLERANCE) + Math.atan(0.25).should be_close(0.244978663126864, TOLERANCE) + Math.atan(0.50).should be_close(0.463647609000806, TOLERANCE) + Math.atan(0.75).should be_close(0.643501108793284, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.atan("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.atan(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.atan(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.atan(MathSpecs::Float.new).should be_close(0.785398163397448, TOLERANCE) + end +end + +describe "Math#atan" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:atan, 3.1415).should be_close(1.2626187313511, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/atanh_spec.rb b/spec/rubyspec/core/math/atanh_spec.rb new file mode 100644 index 0000000000..ce947ceab4 --- /dev/null +++ b/spec/rubyspec/core/math/atanh_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/math/common', __FILE__) +require File.expand_path('../../../shared/math/atanh', __FILE__) + +describe "Math.atanh" do + it_behaves_like :math_atanh_base, :atanh, Math + it_behaves_like :math_atanh_no_complex, :atanh, Math +end + +describe "Math#atanh" do + it_behaves_like :math_atanh_private, :atanh + it_behaves_like :math_atanh_base, :atanh, IncludesMath.new + it_behaves_like :math_atanh_no_complex, :atanh, IncludesMath.new +end diff --git a/spec/rubyspec/core/math/cbrt_spec.rb b/spec/rubyspec/core/math/cbrt_spec.rb new file mode 100644 index 0000000000..0b608151ab --- /dev/null +++ b/spec/rubyspec/core/math/cbrt_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.cbrt" do + it "returns a float" do + Math.cbrt(1).should be_an_instance_of(Float) + end + + it "returns the cubic root of the argument" do + Math.cbrt(1).should == 1.0 + Math.cbrt(8.0).should == 2.0 + Math.cbrt(-8.0).should == -2.0 + Math.cbrt(3).should be_close(1.44224957030741, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.cbrt("foobar") }.should raise_error(TypeError) + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.cbrt(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.cbrt(MathSpecs::Float.new).should be_close(1.0, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/constants_spec.rb b/spec/rubyspec/core/math/constants_spec.rb new file mode 100644 index 0000000000..8c1b33223e --- /dev/null +++ b/spec/rubyspec/core/math/constants_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math::PI" do + it "approximates the value of pi" do + Math::PI.should be_close(3.14159_26535_89793_23846, TOLERANCE) + end + + it "is accessible to a class that includes Math" do + IncludesMath::PI.should == Math::PI + end +end + +describe "Math::E" do + it "approximates the value of Napier's constant" do + Math::E.should be_close(2.71828_18284_59045_23536, TOLERANCE) + end + + it "is accessible to a class that includes Math" do + IncludesMath::E.should == Math::E + end +end diff --git a/spec/rubyspec/core/math/cos_spec.rb b/spec/rubyspec/core/math/cos_spec.rb new file mode 100644 index 0000000000..59b23b198b --- /dev/null +++ b/spec/rubyspec/core/math/cos_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# cosine : (-Inf, Inf) --> (-1.0, 1.0) +describe "Math.cos" do + it "returns a float" do + Math.cos(Math::PI).should be_kind_of(Float) + end + + it "returns the cosine of the argument expressed in radians" do + Math.cos(Math::PI).should be_close(-1.0, TOLERANCE) + Math.cos(0).should be_close(1.0, TOLERANCE) + Math.cos(Math::PI/2).should be_close(0.0, TOLERANCE) + Math.cos(3*Math::PI/2).should be_close(0.0, TOLERANCE) + Math.cos(2*Math::PI).should be_close(1.0, TOLERANCE) + end + + + it "raises a TypeError unless the argument is Numeric and has #to_f" do + lambda { Math.cos("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.cos(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.cos(nil) }.should raise_error(TypeError) + end + + it "coerces its argument with #to_f" do + f = mock_numeric('8.2') + f.should_receive(:to_f).and_return(8.2) + Math.cos(f).should == Math.cos(8.2) + end +end + +describe "Math#cos" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:cos, 3.1415).should be_close(-0.999999995707656, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/cosh_spec.rb b/spec/rubyspec/core/math/cosh_spec.rb new file mode 100644 index 0000000000..561c3cd312 --- /dev/null +++ b/spec/rubyspec/core/math/cosh_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.cosh" do + it "returns a float" do + Math.cosh(1.0).should be_kind_of(Float) + end + + it "returns the hyperbolic cosine of the argument" do + Math.cosh(0.0).should == 1.0 + Math.cosh(-0.0).should == 1.0 + Math.cosh(1.5).should be_close(2.35240961524325, TOLERANCE) + Math.cosh(-2.99).should be_close(9.96798496414416, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.cosh("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.cosh(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.cosh(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.cosh(MathSpecs::Float.new).should be_close(1.54308063481524, TOLERANCE) + end +end + +describe "Math#cosh" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:cos, 3.1415).should be_close(-0.999999995707656, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/erf_spec.rb b/spec/rubyspec/core/math/erf_spec.rb new file mode 100644 index 0000000000..1ea5693dfe --- /dev/null +++ b/spec/rubyspec/core/math/erf_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# erf method is the "error function" encountered in integrating the normal +# distribution (which is a normalized form of the Gaussian function). +describe "Math.erf" do + it "returns a float" do + Math.erf(1).should be_kind_of(Float) + end + + it "returns the error function of the argument" do + Math.erf(0).should be_close(0.0, TOLERANCE) + Math.erf(1).should be_close(0.842700792949715, TOLERANCE) + Math.erf(-1).should be_close(-0.842700792949715, TOLERANCE) + Math.erf(0.5).should be_close(0.520499877813047, TOLERANCE) + Math.erf(-0.5).should be_close(-0.520499877813047, TOLERANCE) + Math.erf(10000).should be_close(1.0, TOLERANCE) + Math.erf(-10000).should be_close(-1.0, TOLERANCE) + Math.erf(0.00000000000001).should be_close(0.0, TOLERANCE) + Math.erf(-0.00000000000001).should be_close(0.0, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.erf("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.erf(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.erf(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.erf(MathSpecs::Float.new).should be_close(0.842700792949715, TOLERANCE) + end +end + +describe "Math#erf" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:erf, 3.1415).should be_close(0.999991118444483, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/erfc_spec.rb b/spec/rubyspec/core/math/erfc_spec.rb new file mode 100644 index 0000000000..21c9e246ff --- /dev/null +++ b/spec/rubyspec/core/math/erfc_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# erfc is the complementary error function +describe "Math.erfc" do + it "returns a float" do + Math.erf(1).should be_kind_of(Float) + end + + it "returns the complementary error function of the argument" do + Math.erfc(0).should be_close(1.0, TOLERANCE) + Math.erfc(1).should be_close(0.157299207050285, TOLERANCE) + Math.erfc(-1).should be_close(1.84270079294971, TOLERANCE) + Math.erfc(0.5).should be_close(0.479500122186953, TOLERANCE) + Math.erfc(-0.5).should be_close(1.52049987781305, TOLERANCE) + Math.erfc(10000).should be_close(0.0, TOLERANCE) + Math.erfc(-10000).should be_close(2.0, TOLERANCE) + Math.erfc(0.00000000000001).should be_close(0.999999999999989, TOLERANCE) + Math.erfc(-0.00000000000001).should be_close(1.00000000000001, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.erfc("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.erfc(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.erfc(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.erfc(MathSpecs::Float.new).should be_close(0.157299207050285, TOLERANCE) + end +end + +describe "Math#erfc" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:erf, 3.1415).should be_close(0.999991118444483, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/exp_spec.rb b/spec/rubyspec/core/math/exp_spec.rb new file mode 100644 index 0000000000..a727404462 --- /dev/null +++ b/spec/rubyspec/core/math/exp_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.exp" do + it "returns a float" do + Math.exp(1.0).should be_kind_of(Float) + end + + it "returns the base-e exponential of the argument" do + Math.exp(0.0).should == 1.0 + Math.exp(-0.0).should == 1.0 + Math.exp(-1.8).should be_close(0.165298888221587, TOLERANCE) + Math.exp(1.25).should be_close(3.49034295746184, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.exp("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.exp(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.exp(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.exp(MathSpecs::Float.new).should be_close(Math::E, TOLERANCE) + end +end + +describe "Math#exp" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:exp, 23.1415).should be_close(11226018484.0012, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/fixtures/classes.rb b/spec/rubyspec/core/math/fixtures/classes.rb new file mode 100644 index 0000000000..6f2241e739 --- /dev/null +++ b/spec/rubyspec/core/math/fixtures/classes.rb @@ -0,0 +1,28 @@ +class IncludesMath + include Math +end + +module MathSpecs + class Float < Numeric + def initialize(value=1.0) + @value = value + end + + def to_f + @value + end + end + + class Integer + def to_int + 2 + end + end + + class UserClass + end + + class StringSubClass < String + end + +end diff --git a/spec/rubyspec/core/math/frexp_spec.rb b/spec/rubyspec/core/math/frexp_spec.rb new file mode 100644 index 0000000000..4c529b0911 --- /dev/null +++ b/spec/rubyspec/core/math/frexp_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.frexp" do + it "returns the normalized fraction and exponent" do + frac, exp = Math.frexp(102.83) + frac.should be_close(0.803359375, TOLERANCE) + exp.should == 7 + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.frexp("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + frac, _exp = Math.frexp(nan_value) + frac.nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.frexp(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + frac, exp = Math.frexp(MathSpecs::Float.new) + frac.should be_close(0.5, TOLERANCE) + exp.should == 1 + end +end + +describe "Math#frexp" do + it "is accessible as a private instance method" do + frac, exp = IncludesMath.new.send(:frexp, 2.1415) + frac.should be_close(0.535375, TOLERANCE) + exp.should == 2 + end +end diff --git a/spec/rubyspec/core/math/gamma_spec.rb b/spec/rubyspec/core/math/gamma_spec.rb new file mode 100644 index 0000000000..eb26f25bfe --- /dev/null +++ b/spec/rubyspec/core/math/gamma_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Math.gamma" do + it "returns +infinity given 0" do + Math.gamma(0).should == Float::INFINITY + end + + platform_is_not :windows do + # https://bugs.ruby-lang.org/issues/12249 + it "returns -infinity given -0.0" do + Math.gamma(-0.0).should == -Float::INFINITY + end + end + + it "returns Math.sqrt(Math::PI) given 0.5" do + Math.gamma(0.5).should be_close(Math.sqrt(Math::PI), TOLERANCE) + end + + # stop at n == 23 because 23! cannot be exactly represented by IEEE 754 double + it "returns exactly (n-1)! given n for n between 2 and 23" do + fact = 1 + 2.upto(23) do |n| + fact *= (n - 1) + Math.gamma(n).should == fact + end + end + + it "returns approximately (n-1)! given n for n between 24 and 30" do + fact2 = 1124000727777607680000 # 22! + 24.upto(30) do |n| + fact2 *= n - 1 + # compare only the first 12 places, tolerate the rest + Math.gamma(n).should be_close(fact2, fact2.to_s[12..-1].to_i) + end + end + + it "returns good numerical approximation for gamma(3.2)" do + Math.gamma(3.2).should be_close(2.423965, TOLERANCE) + end + + it "returns good numerical approximation for gamma(-2.15)" do + Math.gamma(-2.15).should be_close(-2.999619, TOLERANCE) + end + + it "returns good numerical approximation for gamma(0.00001)" do + Math.gamma(0.00001).should be_close(99999.422794, TOLERANCE) + end + + it "returns good numerical approximation for gamma(-0.00001)" do + Math.gamma(-0.00001).should be_close(-100000.577225, TOLERANCE) + end + + it "raises Math::DomainError given -1" do + lambda { Math.gamma(-1) }.should raise_error(Math::DomainError) + end + + # See https://bugs.ruby-lang.org/issues/10642 + it "returns +infinity given +infinity" do + Math.gamma(infinity_value).infinite?.should == 1 + end + + it "raises Math::DomainError given negative infinity" do + lambda { Math.gamma(-Float::INFINITY) }.should raise_error(Math::DomainError) + end + + it "returns NaN given NaN" do + Math.gamma(nan_value).nan?.should be_true + end +end diff --git a/spec/rubyspec/core/math/hypot_spec.rb b/spec/rubyspec/core/math/hypot_spec.rb new file mode 100644 index 0000000000..f693a719f3 --- /dev/null +++ b/spec/rubyspec/core/math/hypot_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.hypot" do + it "returns a float" do + Math.hypot(3, 4).should be_kind_of(Float) + end + + it "returns the length of the hypotenuse of a right triangle with legs given by the arguments" do + Math.hypot(0, 0).should be_close(0.0, TOLERANCE) + Math.hypot(2, 10).should be_close( 10.1980390271856, TOLERANCE) + Math.hypot(5000, 5000).should be_close(7071.06781186548, TOLERANCE) + Math.hypot(0.0001, 0.0002).should be_close(0.000223606797749979, TOLERANCE) + Math.hypot(-2, -10).should be_close(10.1980390271856, TOLERANCE) + Math.hypot(2, 10).should be_close(10.1980390271856, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.hypot("test", "this") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.hypot(nan_value, 0).nan?.should be_true + Math.hypot(0, nan_value).nan?.should be_true + Math.hypot(nan_value, nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.hypot(nil, nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.hypot(MathSpecs::Float.new, MathSpecs::Float.new).should be_close(1.4142135623731, TOLERANCE) + end +end + +describe "Math#hypot" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:hypot, 2, 3.1415).should be_close(3.72411361937307, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/ldexp_spec.rb b/spec/rubyspec/core/math/ldexp_spec.rb new file mode 100644 index 0000000000..360f5c5e2a --- /dev/null +++ b/spec/rubyspec/core/math/ldexp_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.ldexp" do + it "returns a float" do + Math.ldexp(1.0, 2).should be_kind_of(Float) + end + + it "returns the argument multiplied by 2**n" do + Math.ldexp(0.0, 0.0).should == 0.0 + Math.ldexp(0.0, 1.0).should == 0.0 + Math.ldexp(-1.25, 2).should be_close(-5.0, TOLERANCE) + Math.ldexp(2.1, -3).should be_close(0.2625, TOLERANCE) + Math.ldexp(5.7, 4).should be_close(91.2, TOLERANCE) + end + + it "raises a TypeError if the first argument cannot be coerced with Float()" do + lambda { Math.ldexp("test", 2) }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.ldexp(nan_value, 0).nan?.should be_true + end + + it "raises RangeError if NaN is given as the second arg" do + lambda { Math.ldexp(0, nan_value) }.should raise_error(RangeError) + end + + it "raises a TypeError if the second argument cannot be coerced with Integer()" do + lambda { Math.ldexp(3.2, "this") }.should raise_error(TypeError) + end + + it "raises a TypeError if the first argument is nil" do + lambda { Math.ldexp(nil, 2) }.should raise_error(TypeError) + end + + it "raises a TypeError if the second argument is nil" do + lambda { Math.ldexp(3.1, nil) }.should raise_error(TypeError) + end + + it "accepts any first argument that can be coerced with Float()" do + Math.ldexp(MathSpecs::Float.new, 2).should be_close(4.0, TOLERANCE) + end + + it "accepts any second argument that can be coerced with Integer()" do + Math.ldexp(3.23, MathSpecs::Integer.new).should be_close(12.92, TOLERANCE) + end +end + +describe "Math#ldexp" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:ldexp, 3.1415, 2).should be_close(12.566, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/lgamma_spec.rb b/spec/rubyspec/core/math/lgamma_spec.rb new file mode 100644 index 0000000000..885a1b252c --- /dev/null +++ b/spec/rubyspec/core/math/lgamma_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Math.lgamma" do + it "returns [Infinity, 1] when passed 0" do + Math.lgamma(0).should == [infinity_value, 1] + end + + platform_is_not :windows do + it "returns [Infinity, 1] when passed -1" do + Math.lgamma(-1).should == [infinity_value, 1] + end + end + + ruby_version_is "2.4" do + it "returns [Infinity, -1] when passed -0.0" do + Math.lgamma(-0.0).should == [infinity_value, -1] + end + end + + it "returns [log(sqrt(PI)), 1] when passed 0.5" do + lg1 = Math.lgamma(0.5) + lg1[0].should be_close(Math.log(Math.sqrt(Math::PI)), TOLERANCE) + lg1[1].should == 1 + end + + it "returns [log(2/3*PI, 1] when passed 6.0" do + lg2 = Math.lgamma(6.0) + lg2[0].should be_close(Math.log(120.0), TOLERANCE) + lg2[1].should == 1 + end + + it "returns an approximate value when passed -0.5" do + lg1 = Math.lgamma(-0.5) + lg1[0].should be_close(1.2655121, TOLERANCE) + lg1[1].should == -1 + end + + it "returns an approximate value when passed -1.5" do + lg2 = Math.lgamma(-1.5) + lg2[0].should be_close(0.8600470, TOLERANCE) + lg2[1].should == 1 + end + + it "raises Math::DomainError when passed -Infinity" do + lambda { Math.lgamma(-infinity_value) }.should raise_error(Math::DomainError) + end + + it "returns [Infinity, 1] when passed Infinity" do + Math.lgamma(infinity_value).should == [infinity_value, 1] + end + + it "returns [NaN, 1] when passed NaN" do + Math.lgamma(nan_value)[0].nan?.should be_true + Math.lgamma(nan_value)[1].should == 1 + end +end diff --git a/spec/rubyspec/core/math/log10_spec.rb b/spec/rubyspec/core/math/log10_spec.rb new file mode 100644 index 0000000000..8164b9994d --- /dev/null +++ b/spec/rubyspec/core/math/log10_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# The common logarithm, having base 10 +describe "Math.log10" do + it "returns a float" do + Math.log10(1).should be_kind_of(Float) + end + + it "returns the base-10 logarithm of the argument" do + Math.log10(0.0001).should be_close(-4.0, TOLERANCE) + Math.log10(0.000000000001e-15).should be_close(-27.0, TOLERANCE) + Math.log10(1).should be_close(0.0, TOLERANCE) + Math.log10(10).should be_close(1.0, TOLERANCE) + Math.log10(10e15).should be_close(16.0, TOLERANCE) + end + + conflicts_with :Complex do + it "raises an Errno::EDOM if the argument is less than 0" do + lambda { Math.log10(-1e-15) }.should raise_error( Errno::EDOM) + end + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.log10("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.log10(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.log10(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.log10(MathSpecs::Float.new).should be_close(0.0, TOLERANCE) + end +end + +describe "Math#log10" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:log10, 4.15).should be_close(0.618048096712093, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/log2_spec.rb b/spec/rubyspec/core/math/log2_spec.rb new file mode 100644 index 0000000000..387f05ca9f --- /dev/null +++ b/spec/rubyspec/core/math/log2_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.log2" do + it "returns a float" do + Math.log2(5.79).should be_close(2.53356334821451, TOLERANCE) + end + + it "returns the natural logarithm of the argument" do + Math.log2(1.1).should be_close(0.137503523749935, TOLERANCE) + Math.log2(3.14).should be_close(1.6507645591169, TOLERANCE) + Math.log2((2**101+45677544234809571)).should be_close(101.00000000000003, TOLERANCE) + + Math.log2((2**10001+45677544234809571)).should == 10001.0 + Math.log2((2**301+45677544234809571)).should == 301.0 + end + + it "raises an Errno::EDOM if the argument is less than 0" do + lambda { Math.log2(-1e-15) }.should raise_error( Math::DomainError) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.log2("test") }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a numerical argument as a string" do + lambda { Math.log2("1.0") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.log2(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.log2(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.log2(MathSpecs::Float.new).should be_close(0.0, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/log_spec.rb b/spec/rubyspec/core/math/log_spec.rb new file mode 100644 index 0000000000..9bcccb55e2 --- /dev/null +++ b/spec/rubyspec/core/math/log_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# The natural logarithm, having base Math::E +describe "Math.log" do + it "returns a float" do + Math.log(1).should be_kind_of(Float) + end + + it "returns the natural logarithm of the argument" do + Math.log(0.0001).should be_close(-9.21034037197618, TOLERANCE) + Math.log(0.000000000001e-15).should be_close(-62.1697975108392, TOLERANCE) + Math.log(1).should be_close(0.0, TOLERANCE) + Math.log(10).should be_close( 2.30258509299405, TOLERANCE) + Math.log(10e15).should be_close(36.8413614879047, TOLERANCE) + end + + conflicts_with :Complex do + it "raises an Errno::EDOM if the argument is less than 0" do + lambda { Math.log(-1e-15) }.should raise_error(Errno::EDOM) + end + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.log("test") }.should raise_error(TypeError) + end + + it "raises a TypeError for numerical values passed as string" do + lambda { Math.log("10") }.should raise_error(TypeError) + end + + it "accepts a second argument for the base" do + Math.log(9, 3).should be_close(2, TOLERANCE) + Math.log(8, 1.4142135623730951).should be_close(6, TOLERANCE) + end + + it "raises a TypeError when the numerical base cannot be coerced to a float" do + lambda { Math.log(10, "2") }.should raise_error(TypeError) + lambda { Math.log(10, nil) }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.log(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.log(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.log(MathSpecs::Float.new).should be_close(0.0, TOLERANCE) + end +end + +describe "Math#log" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:log, 5.21).should be_close(1.65057985576528, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/sin_spec.rb b/spec/rubyspec/core/math/sin_spec.rb new file mode 100644 index 0000000000..d8f134e609 --- /dev/null +++ b/spec/rubyspec/core/math/sin_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# sine : (-Inf, Inf) --> (-1.0, 1.0) +describe "Math.sin" do + it "returns a float" do + Math.sin(Math::PI).should be_kind_of(Float) + end + + it "returns the sine of the argument expressed in radians" do + Math.sin(Math::PI).should be_close(0.0, TOLERANCE) + Math.sin(0).should be_close(0.0, TOLERANCE) + Math.sin(Math::PI/2).should be_close(1.0, TOLERANCE) + Math.sin(3*Math::PI/2).should be_close(-1.0, TOLERANCE) + Math.sin(2*Math::PI).should be_close(0.0, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.sin("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.sin(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.sin(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.sin(MathSpecs::Float.new).should be_close(0.841470984807897, TOLERANCE) + end +end + +describe "Math#sin" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:sin, 1.21).should be_close(0.935616001553386, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/sinh_spec.rb b/spec/rubyspec/core/math/sinh_spec.rb new file mode 100644 index 0000000000..daa7d30733 --- /dev/null +++ b/spec/rubyspec/core/math/sinh_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.sinh" do + it "returns a float" do + Math.sinh(1.2).should be_kind_of(Float) + end + + it "returns the hyperbolic sin of the argument" do + Math.sinh(0.0).should == 0.0 + Math.sinh(-0.0).should == 0.0 + Math.sinh(1.5).should be_close(2.12927945509482, TOLERANCE) + Math.sinh(-2.8).should be_close(-8.19191835423591, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.sinh("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.sinh(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.sinh(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.sinh(MathSpecs::Float.new).should be_close(1.1752011936438, TOLERANCE) + end +end + +describe "Math#sinh" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:sinh, 1.99).should be_close(3.58941916843202, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/sqrt_spec.rb b/spec/rubyspec/core/math/sqrt_spec.rb new file mode 100644 index 0000000000..03891b951a --- /dev/null +++ b/spec/rubyspec/core/math/sqrt_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.sqrt" do + it "returns a float" do + Math.sqrt(1).should be_kind_of(Float) + end + + it "returns the square root of the argument" do + Math.sqrt(1).should == 1.0 + Math.sqrt(4.0).should == 2.0 + Math.sqrt(15241578780673814.441547445).should be_close(123456789.123457, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.sqrt("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.sqrt(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.sqrt(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.sqrt(MathSpecs::Float.new).should be_close(1.0, TOLERANCE) + end +end + +describe "Math#sqrt" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:sqrt, 2.23).should be_close(1.49331845230681, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/tan_spec.rb b/spec/rubyspec/core/math/tan_spec.rb new file mode 100644 index 0000000000..0318ef8a14 --- /dev/null +++ b/spec/rubyspec/core/math/tan_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.tan" do + it "returns a float" do + Math.tan(1.35).should be_kind_of(Float) + end + + it "returns the tangent of the argument" do + Math.tan(0.0).should == 0.0 + Math.tan(-0.0).should == -0.0 + Math.tan(4.22).should be_close(1.86406937682395, TOLERANCE) + Math.tan(-9.65).should be_close(-0.229109052606441, TOLERANCE) + end + + it "returns NaN if called with +-Infinitty" do + Math.tan(infinity_value).nan?.should == true + Math.tan(-infinity_value).nan?.should == true + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.tan("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.tan(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.tan(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.tan(MathSpecs::Float.new).should be_close(1.5574077246549, TOLERANCE) + end +end + +describe "Math#tan" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:tan, 1.0).should be_close(1.5574077246549, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/math/tanh_spec.rb b/spec/rubyspec/core/math/tanh_spec.rb new file mode 100644 index 0000000000..8f39dc948b --- /dev/null +++ b/spec/rubyspec/core/math/tanh_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Math.tanh" do + it "returns a float" do + Math.tanh(0.5).should be_kind_of(Float) + end + + it "returns the hyperbolic tangent of the argument" do + Math.tanh(0.0).should == 0.0 + Math.tanh(-0.0).should == -0.0 + Math.tanh(infinity_value).should == 1.0 + Math.tanh(-infinity_value).should == -1.0 + Math.tanh(2.5).should be_close(0.98661429815143, TOLERANCE) + Math.tanh(-4.892).should be_close(-0.999887314427707, TOLERANCE) + end + + it "raises a TypeError if the argument cannot be coerced with Float()" do + lambda { Math.tanh("test") }.should raise_error(TypeError) + end + + it "returns NaN given NaN" do + Math.tanh(nan_value).nan?.should be_true + end + + it "raises a TypeError if the argument is nil" do + lambda { Math.tanh(nil) }.should raise_error(TypeError) + end + + it "accepts any argument that can be coerced with Float()" do + Math.tanh(MathSpecs::Float.new).should be_close(0.761594155955765, TOLERANCE) + end +end + +describe "Math#tanh" do + it "is accessible as a private instance method" do + IncludesMath.new.send(:tanh, 5.21).should be_close(0.99994034202065, TOLERANCE) + end +end diff --git a/spec/rubyspec/core/method/arity_spec.rb b/spec/rubyspec/core/method/arity_spec.rb new file mode 100644 index 0000000000..32a50a0999 --- /dev/null +++ b/spec/rubyspec/core/method/arity_spec.rb @@ -0,0 +1,222 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#arity" do + SpecEvaluate.desc = "for method definition" + + context "returns zero" do + evaluate <<-ruby do + def m() end + ruby + + method(:m).arity.should == 0 + end + + evaluate <<-ruby do + def n(&b) end + ruby + + method(:n).arity.should == 0 + end + end + + context "returns positive values" do + evaluate <<-ruby do + def m(a) end + def n(a, b) end + def o(a, b, c) end + def p(a, b, c, d) end + ruby + + method(:m).arity.should == 1 + method(:n).arity.should == 2 + method(:o).arity.should == 3 + method(:p).arity.should == 4 + end + + evaluate <<-ruby do + def m(a:) end + def n(a:, b:) end + def o(a: 1, b:, c:, d: 2) end + ruby + + method(:m).arity.should == 1 + method(:n).arity.should == 1 + method(:o).arity.should == 1 + end + + evaluate <<-ruby do + def m(a, b:) end + def n(a, b:, &l) end + ruby + + method(:m).arity.should == 2 + method(:n).arity.should == 2 + end + + evaluate <<-ruby do + def m(a, b, c:, d: 1) end + def n(a, b, c:, d: 1, **k, &l) end + ruby + + method(:m).arity.should == 3 + method(:n).arity.should == 3 + end + end + + context "returns negative values" do + evaluate <<-ruby do + def m(a=1) end + def n(a=1, b=2) end + ruby + + method(:m).arity.should == -1 + method(:n).arity.should == -1 + end + + evaluate <<-ruby do + def m(a, b=1) end + def n(a, b, c=1, d=2) end + ruby + + method(:m).arity.should == -2 + method(:n).arity.should == -3 + end + + evaluate <<-ruby do + def m(a=1, *b) end + def n(a=1, b=2, *c) end + ruby + + method(:m).arity.should == -1 + method(:n).arity.should == -1 + end + + evaluate <<-ruby do + def m(*) end + def n(*a) end + ruby + + method(:m).arity.should == -1 + method(:n).arity.should == -1 + end + + evaluate <<-ruby do + def m(a, *) end + def n(a, *b) end + def o(a, b, *c) end + def p(a, b, c, *d) end + ruby + + method(:m).arity.should == -2 + method(:n).arity.should == -2 + method(:o).arity.should == -3 + method(:p).arity.should == -4 + end + + evaluate <<-ruby do + def m(*a, b) end + def n(*a, b, c) end + def o(*a, b, c, d) end + ruby + + method(:m).arity.should == -2 + method(:n).arity.should == -3 + method(:o).arity.should == -4 + end + + evaluate <<-ruby do + def m(a, *b, c) end + def n(a, b, *c, d, e) end + ruby + + method(:m).arity.should == -3 + method(:n).arity.should == -5 + end + + evaluate <<-ruby do + def m(a, b=1, c=2, *d, e, f) end + def n(a, b, c=1, *d, e, f, g) end + ruby + + method(:m).arity.should == -4 + method(:n).arity.should == -6 + end + + evaluate <<-ruby do + def m(a: 1) end + def n(a: 1, b: 2) end + ruby + + method(:m).arity.should == -1 + method(:n).arity.should == -1 + end + + evaluate <<-ruby do + def m(a=1, b: 2) end + def n(*a, b: 1) end + def o(a=1, b: 2) end + def p(a=1, *b, c: 2, &l) end + ruby + + method(:m).arity.should == -1 + method(:n).arity.should == -1 + method(:o).arity.should == -1 + method(:p).arity.should == -1 + end + + evaluate <<-ruby do + def m(**k, &l) end + def n(*a, **k) end + def o(a: 1, b: 2, **k) end + ruby + + method(:m).arity.should == -1 + method(:n).arity.should == -1 + method(:o).arity.should == -1 + end + + evaluate <<-ruby do + def m(a=1, *b, c:, d: 2, **k, &l) end + ruby + + method(:m).arity.should == -2 + end + + evaluate <<-ruby do + def m(a, b=1, *c, d, e:, f: 2, **k, &l) end + def n(a, b=1, *c, d:, e:, f: 2, **k, &l) end + def o(a=0, b=1, *c, d, e:, f: 2, **k, &l) end + def p(a=0, b=1, *c, d:, e:, f: 2, **k, &l) end + ruby + + method(:m).arity.should == -4 + method(:n).arity.should == -3 + method(:o).arity.should == -3 + method(:p).arity.should == -2 + end + end + + context "for a Method generated by respond_to_missing?" do + it "returns -1" do + obj = mock("method arity respond_to_missing") + obj.should_receive(:respond_to_missing?).and_return(true) + + obj.method(:m).arity.should == -1 + end + end + + context "for a Method generated by attr_reader" do + it "return 0" do + obj = MethodSpecs::Methods.new + obj.method(:reader).arity.should == 0 + end + end + + context "for a Method generated by attr_writer" do + it "returns 1" do + obj = MethodSpecs::Methods.new + obj.method(:writer=).arity.should == 1 + end + end +end diff --git a/spec/rubyspec/core/method/call_spec.rb b/spec/rubyspec/core/method/call_spec.rb new file mode 100644 index 0000000000..1a90028176 --- /dev/null +++ b/spec/rubyspec/core/method/call_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/call', __FILE__) + +describe "Method#call" do + it_behaves_like(:method_call, :call) +end diff --git a/spec/rubyspec/core/method/clone_spec.rb b/spec/rubyspec/core/method/clone_spec.rb new file mode 100644 index 0000000000..e3b40254f8 --- /dev/null +++ b/spec/rubyspec/core/method/clone_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#clone" do + it "returns a copy of the method" do + m1 = MethodSpecs::Methods.new.method(:foo) + m2 = m1.clone + + m1.should == m2 + m1.should_not equal(m2) + + m1.call.should == m2.call + end +end diff --git a/spec/rubyspec/core/method/curry_spec.rb b/spec/rubyspec/core/method/curry_spec.rb new file mode 100644 index 0000000000..977f7766d0 --- /dev/null +++ b/spec/rubyspec/core/method/curry_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#curry" do + it "returns a curried proc" do + x = Object.new + def x.foo(a,b,c); [a,b,c]; end + + c = x.method(:foo).curry + c.should be_kind_of(Proc) + c.call(1).call(2, 3).should == [1,2,3] + end + + describe "with optional arity argument" do + before(:each) do + @obj = MethodSpecs::Methods.new + end + + it "returns a curried proc when given correct arity" do + @obj.method(:one_req).curry(1).should be_kind_of(Proc) + @obj.method(:zero_with_splat).curry(100).should be_kind_of(Proc) + @obj.method(:two_req_with_splat).curry(2).should be_kind_of(Proc) + end + + it "raises ArgumentError when the method requires less arguments than the given arity" do + lambda { @obj.method(:zero).curry(1) }.should raise_error(ArgumentError) + lambda { @obj.method(:one_req_one_opt).curry(3) }.should raise_error(ArgumentError) + lambda { @obj.method(:two_req_one_opt_with_block).curry(4) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when the method requires more arguments than the given arity" do + lambda { @obj.method(:two_req_with_splat).curry(1) }.should raise_error(ArgumentError) + lambda { @obj.method(:one_req).curry(0) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/method/element_reference_spec.rb b/spec/rubyspec/core/method/element_reference_spec.rb new file mode 100644 index 0000000000..0be47afede --- /dev/null +++ b/spec/rubyspec/core/method/element_reference_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/call', __FILE__) + +describe "Method#[]" do + it_behaves_like(:method_call, :[]) +end diff --git a/spec/rubyspec/core/method/eql_spec.rb b/spec/rubyspec/core/method/eql_spec.rb new file mode 100644 index 0000000000..f8914e1d12 --- /dev/null +++ b/spec/rubyspec/core/method/eql_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "Method#eql?" do + it_behaves_like(:method_equal, :eql?) +end diff --git a/spec/rubyspec/core/method/equal_value_spec.rb b/spec/rubyspec/core/method/equal_value_spec.rb new file mode 100644 index 0000000000..365e0ac424 --- /dev/null +++ b/spec/rubyspec/core/method/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "Method#==" do + it_behaves_like(:method_equal, :==) +end diff --git a/spec/rubyspec/core/method/fixtures/classes.rb b/spec/rubyspec/core/method/fixtures/classes.rb new file mode 100644 index 0000000000..142cbd1bec --- /dev/null +++ b/spec/rubyspec/core/method/fixtures/classes.rb @@ -0,0 +1,184 @@ +module MethodSpecs + + + class SourceLocation + def self.location # This needs to be on this line + :location # for the spec to pass + end + + def self.redefined + :first + end + + def self.redefined + :last + end + + def original + end + + alias :aka :original + end + + class Methods + def foo + true + end + + alias bar foo + + def same_as_foo + true + end + + def respond_to_missing? method, bool + [:handled_via_method_missing, :also_handled].include? method + end + + def method_missing(method, *arguments) + if [:handled_via_method_missing, :also_handled].include? method + arguments + else + super + end + end + + attr_accessor :attr + + def zero; end + def one_req(a); end + def two_req(a, b); end + + def zero_with_block(&blk); end + def one_req_with_block(a, &blk); end + def two_req_with_block(a, b, &blk); end + + def one_opt(a=nil); end + def one_req_one_opt(a, b=nil); end + def one_req_two_opt(a, b=nil, c=nil); end + def two_req_one_opt(a, b, c=nil); end + + def one_opt_with_block(a=nil, &blk); end + def one_req_one_opt_with_block(a, b=nil, &blk); end + def one_req_two_opt_with_block(a, b=nil, c=nil, &blk); end + def two_req_one_opt_with_block(a, b, c=nil, &blk); end + + def zero_with_splat(*a); end + def one_req_with_splat(a, *b); end + def two_req_with_splat(a, b, *c); end + def one_req_one_opt_with_splat(a, b=nil, *c); end + def two_req_one_opt_with_splat(a, b, c=nil, *d); end + def one_req_two_opt_with_splat(a, b=nil, c=nil, *d); end + + def zero_with_splat_and_block(*a, &blk); end + def one_req_with_splat_and_block(a, *b, &blk); end + def two_req_with_splat_and_block(a, b, *c, &blk); end + def one_req_one_opt_with_splat_and_block(a, b=nil, *c, &blk); end + def two_req_one_opt_with_splat_and_block(a, b, c=nil, *d, &blk); end + def one_req_two_opt_with_splat_and_block(a, b=nil, c=nil, *d, &blk); end + + define_method(:zero_defined_method, Proc.new {||}) + define_method(:zero_with_splat_defined_method, Proc.new {|*x|}) + define_method(:one_req_defined_method, Proc.new {|x|}) + define_method(:two_req_defined_method, Proc.new {|x, y|}) + define_method(:no_args_defined_method) {} + define_method(:two_grouped_defined_method) {|(_x1,_x2)|} + + attr_reader :reader + attr_writer :writer + end + + module MyMod + def bar; :bar; end + end + + class MySuper + include MyMod + end + + class MySub < MySuper; end + + class A + def baz(a, b) + self.class + end + def overridden; end + end + + class B < A + def overridden; end + end + + module BetweenBAndC + def overridden; end + end + + class C < B + include BetweenBAndC + def overridden; end + end + + module OverrideAgain + def overridden; end + end + + class D + def bar() 'done' end + end + + class Eql + + def same_body + 1 + 1 + end + + alias :same_body_alias :same_body + + def same_body_with_args(arg) + 1 + 1 + end + + def different_body + 1 + 2 + end + + def same_body_two + 1 + 1 + end + + private + def same_body_private + 1 + 1 + end + end + + class Eql2 + + def same_body + 1 + 1 + end + + end + + class ToProc + def method_called(a, b) + ScratchPad << [a, b] + end + + def to_proc + method(:method_called).to_proc + end + end + + class ToProcBeta + def method_called(a) + ScratchPad << a + a + end + + def to_proc + method(:method_called).to_proc + end + end + +end diff --git a/spec/rubyspec/core/method/hash_spec.rb b/spec/rubyspec/core/method/hash_spec.rb new file mode 100644 index 0000000000..67bc4c16ac --- /dev/null +++ b/spec/rubyspec/core/method/hash_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#hash" do + it "needs to be reviewed for spec completeness" + + it "returns the same value for user methods that are eql?" do + obj = MethodSpecs::Methods.new + obj.method(:foo).hash.should == obj.method(:bar).hash + end + + # See also redmine #6048 + it "returns the same value for builtin methods that are eql?" do + obj = [42] + obj.method(:to_s).hash.should == obj.method(:inspect).hash + end +end diff --git a/spec/rubyspec/core/method/inspect_spec.rb b/spec/rubyspec/core/method/inspect_spec.rb new file mode 100644 index 0000000000..bfb61daf53 --- /dev/null +++ b/spec/rubyspec/core/method/inspect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "Method#inspect" do + it_behaves_like(:method_to_s, :inspect) +end diff --git a/spec/rubyspec/core/method/name_spec.rb b/spec/rubyspec/core/method/name_spec.rb new file mode 100644 index 0000000000..ebc5f856d1 --- /dev/null +++ b/spec/rubyspec/core/method/name_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#name" do + it "returns the name of the method" do + "abc".method(:upcase).name.should == :upcase + end + + it "returns the name even when aliased" do + obj = MethodSpecs::Methods.new + obj.method(:foo).name.should == :foo + obj.method(:bar).name.should == :bar + obj.method(:bar).unbind.bind(obj).name.should == :bar + end + + describe "for a Method generated by respond_to_missing?" do + it "returns the name passed to respond_to_missing?" do + @m = MethodSpecs::Methods.new + @m.method(:handled_via_method_missing).name.should == :handled_via_method_missing + end + end +end diff --git a/spec/rubyspec/core/method/owner_spec.rb b/spec/rubyspec/core/method/owner_spec.rb new file mode 100644 index 0000000000..3378b7bd1f --- /dev/null +++ b/spec/rubyspec/core/method/owner_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#owner" do + it "returns the owner of the method" do + "abc".method(:upcase).owner.should == String + end + + it "returns the same owner when aliased in the same classes" do + obj = MethodSpecs::Methods.new + obj.method(:foo).owner.should == MethodSpecs::Methods + obj.method(:bar).owner.should == MethodSpecs::Methods + end + + it "returns the class/module it was defined in" do + MethodSpecs::C.new.method(:baz).owner.should == MethodSpecs::A + MethodSpecs::MySuper.new.method(:bar).owner.should == MethodSpecs::MyMod + end + + describe "for a Method generated by respond_to_missing?" do + it "returns the owner of the method" do + @m = MethodSpecs::Methods.new + @m.method(:handled_via_method_missing).owner.should == MethodSpecs::Methods + end + end +end diff --git a/spec/rubyspec/core/method/parameters_spec.rb b/spec/rubyspec/core/method/parameters_spec.rb new file mode 100644 index 0000000000..8808bf40b4 --- /dev/null +++ b/spec/rubyspec/core/method/parameters_spec.rb @@ -0,0 +1,244 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#parameters" do + class MethodSpecs::Methods + def one_key(a: 1); end + def one_keyrest(**a); end + + def one_keyreq(a:); end + + def one_splat_one_req(*a,b); end + def one_splat_two_req(*a,b,c); end + def one_splat_one_req_with_block(*a,b,&blk); end + + def one_opt_with_stabby(a=->(b){true}); end + + def one_unnamed_splat(*); end + + def one_splat_one_block(*args, &block) + local_is_not_parameter = {} + end + + define_method(:one_optional_defined_method) {|x = 1|} + end + + it "returns an empty Array when the method expects no arguments" do + MethodSpecs::Methods.instance_method(:zero).parameters.should == [] + end + + it "returns [[:req,:name]] for a method expecting one required argument called 'name'" do + MethodSpecs::Methods.instance_method(:one_req).parameters.should == [[:req,:a]] + end + + it "returns [[:req,:a],[:req,:b]] for a method expecting two required arguments called 'a' and 'b''" do + m = MethodSpecs::Methods.instance_method(:two_req) + m.parameters.should == [[:req,:a], [:req,:b]] + end + + it "returns [[:block,:blk]] for a method expecting one block argument called 'a'" do + m = MethodSpecs::Methods.instance_method(:zero_with_block) + m.parameters.should == [[:block,:blk]] + end + + it "returns [[:req,:a],[:block,:b] for a method expecting a required argument ('a') and a block argument ('b')" do + m = MethodSpecs::Methods.instance_method(:one_req_with_block) + m.parameters.should == [[:req,:a], [:block,:blk]] + end + + it "returns [[:req,:a],[:req,:b],[:block,:c] for a method expecting two required arguments ('a','b') and a block argument ('c')" do + m = MethodSpecs::Methods.instance_method(:two_req_with_block) + m.parameters.should == [[:req,:a], [:req,:b], [:block,:blk]] + end + + it "returns [[:opt,:a]] for a method expecting one optional argument ('a')" do + m = MethodSpecs::Methods.instance_method(:one_opt) + m.parameters.should == [[:opt,:a]] + end + + it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do + m = MethodSpecs::Methods.instance_method(:one_req_one_opt) + m.parameters.should == [[:req,:a],[:opt,:b]] + end + + it "returns [[:req,:a],[:opt,:b]] for a method expecting one required argument ('a') and one optional argument ('b')" do + m = MethodSpecs::Methods.instance_method(:one_req_one_opt) + m.parameters.should == [[:req,:a],[:opt,:b]] + end + + it "returns [[:req,:a],[:opt,:b],[:opt,:c]] for a method expecting one required argument ('a') and two optional arguments ('b','c')" do + m = MethodSpecs::Methods.instance_method(:one_req_two_opt) + m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c]] + end + + it "returns [[:req,:a],[:req,:b],[:opt,:c]] for a method expecting two required arguments ('a','b') and one optional arguments ('c')" do + m = MethodSpecs::Methods.instance_method(:two_req_one_opt) + m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c]] + end + + it "returns [[:opt,:a],[:block,:b]] for a method expecting one required argument ('a') and one block argument ('b')" do + m = MethodSpecs::Methods.instance_method(:one_opt_with_block) + m.parameters.should == [[:opt,:a],[:block,:blk]] + end + + it "returns [[:req,:a],[:opt,:b],[:block,:c]] for a method expecting one required argument ('a'), one optional argument ('b'), and a block ('c')" do + m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_block) + m.parameters.should == [[:req,:a],[:opt,:b],[:block,:blk]] + end + + it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:block,:d]] for a method expecting one required argument ('a'), two optional arguments ('b','c'), and a block ('d')" do + m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_block) + m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:block,:blk]] + end + + it "returns [[:rest,:a]] for a method expecting a single splat argument ('a')" do + m = MethodSpecs::Methods.instance_method(:zero_with_splat) + m.parameters.should == [[:rest,:a]] + end + + it "returns [[:req,:a],[:rest,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do + m = MethodSpecs::Methods.instance_method(:one_req_with_splat) + m.parameters.should == [[:req,:a],[:rest,:b]] + end + + it "returns [[:req,:a],[:req,:b],[:rest,:c]] for a method expecting two required arguments ('a','b') and a splat argument ('c')" do + m = MethodSpecs::Methods.instance_method(:two_req_with_splat) + m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c]] + end + + it "returns [[:req,:a],[:opt,:b],[:rest,:c]] for a method expecting a required argument ('a','b'), an optional argument ('b'), and a splat argument ('c')" do + m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat) + m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c]] + end + + it "returns [[:req,:a],[:req,:b],[:opt,:b],[:rest,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), and a splat argument ('d')" do + m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat) + m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d]] + end + + it "returns [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]] for a method expecting a required argument ('a'), two optional arguments ('b','c'), and a splat argument ('d')" do + m = MethodSpecs::Methods.instance_method(:one_req_two_opt_with_splat) + m.parameters.should == [[:req,:a],[:opt,:b],[:opt,:c],[:rest,:d]] + end + + it "returns [[:rest,:a],[:block,:b]] for a method expecting a splat argument ('a') and a block argument ('b')" do + m = MethodSpecs::Methods.instance_method(:zero_with_splat_and_block) + m.parameters.should == [[:rest,:a],[:block,:blk]] + end + + it "returns [[:req,:a],[:rest,:b],[:block,:c]] for a method expecting a required argument ('a'), a splat argument ('b'), and a block ('c')" do + m = MethodSpecs::Methods.instance_method(:one_req_with_splat_and_block) + m.parameters.should == [[:req,:a],[:rest,:b],[:block,:blk]] + end + + it "returns [[:req,:a],[:req,:b],[:rest,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), a splat argument ('c'), and a block ('d')" do + m = MethodSpecs::Methods.instance_method(:two_req_with_splat_and_block) + m.parameters.should == [[:req,:a],[:req,:b],[:rest,:c],[:block,:blk]] + end + + it "returns [[:req,:a],[:opt,:b],[:rest,:c],[:block,:d]] for a method expecting a required argument ('a'), a splat argument ('c'), and a block ('d')" do + m = MethodSpecs::Methods.instance_method(:one_req_one_opt_with_splat_and_block) + m.parameters.should == [[:req,:a],[:opt,:b],[:rest,:c],[:block,:blk]] + end + + it "returns [[:req,:a],[:req,:b],[:opt,:c],[:block,:d]] for a method expecting two required arguments ('a','b'), an optional argument ('c'), a splat argument ('d'), and a block ('e')" do + m = MethodSpecs::Methods.instance_method(:two_req_one_opt_with_splat_and_block) + m.parameters.should == [[:req,:a],[:req,:b],[:opt,:c],[:rest,:d],[:block,:blk]] + end + + it "returns [[:rest,:a],[:req,:b]] for a method expecting a splat argument ('a') and a required argument ('b')" do + m = MethodSpecs::Methods.instance_method(:one_splat_one_req) + m.parameters.should == [[:rest,:a],[:req,:b]] + end + + it "returns [[:rest,:a],[:req,:b],[:req,:c]] for a method expecting a splat argument ('a') and two required arguments ('b','c')" do + m = MethodSpecs::Methods.instance_method(:one_splat_two_req) + m.parameters.should == [[:rest,:a],[:req,:b],[:req,:c]] + end + + it "returns [[:rest,:a],[:req,:b],[:block,:c]] for a method expecting a splat argument ('a'), a required argument ('b'), and a block ('c')" do + m = MethodSpecs::Methods.instance_method(:one_splat_one_req_with_block) + m.parameters.should == [[:rest,:a],[:req,:b],[:block,:blk]] + end + + it "returns [[:key,:a]] for a method with a single optional keyword argument" do + m = MethodSpecs::Methods.instance_method(:one_key) + m.parameters.should == [[:key,:a]] + end + + it "returns [[:keyrest,:a]] for a method with a keyword rest argument" do + m = MethodSpecs::Methods.instance_method(:one_keyrest) + m.parameters.should == [[:keyrest,:a]] + end + + it "returns [[:keyreq,:a]] for a method with a single required keyword argument" do + m = MethodSpecs::Methods.instance_method(:one_keyreq) + m.parameters.should == [[:keyreq,:a]] + end + + it "works with ->(){} as the value of an optional argument" do + m = MethodSpecs::Methods.instance_method(:one_opt_with_stabby) + m.parameters.should == [[:opt,:a]] + end + + # define_method variants + it "returns [] for a define_method method with explicit no-args || specification" do + m = MethodSpecs::Methods.instance_method(:zero_defined_method) + m.parameters.should == [] + end + + it "returns [[:rest, :x]] for a define_method method with rest arg 'x' only" do + m = MethodSpecs::Methods.instance_method(:zero_with_splat_defined_method) + m.parameters.should == [[:rest, :x]] + end + + it "returns [[:req, :x]] for a define_method method expecting one required argument 'x'" do + m = MethodSpecs::Methods.instance_method(:one_req_defined_method) + m.parameters.should == [[:req, :x]] + end + + it "returns [[:req, :x], [:req, :y]] for a define_method method expecting two required arguments 'x' and 'y'" do + m = MethodSpecs::Methods.instance_method(:two_req_defined_method) + m.parameters.should == [[:req, :x], [:req, :y]] + end + + it "returns [] for a define_method method with no args specification" do + m = MethodSpecs::Methods.instance_method(:no_args_defined_method) + m.parameters.should == [] + end + + it "returns [[:req]] for a define_method method with a grouping as its only argument" do + m = MethodSpecs::Methods.instance_method(:two_grouped_defined_method) + m.parameters.should == [[:req]] + end + + it "returns [[:opt, :x]] for a define_method method with an optional argument 'x'" do + m = MethodSpecs::Methods.instance_method(:one_optional_defined_method) + m.parameters.should == [[:opt, :x]] + end + + it "returns [[:rest]] for a Method generated by respond_to_missing?" do + m = MethodSpecs::Methods.new + m.method(:handled_via_method_missing).parameters.should == [[:rest]] + end + + it "adds nameless rest arg for \"star\" argument" do + m = MethodSpecs::Methods.new + m.method(:one_unnamed_splat).parameters.should == [[:rest]] + end + + it "returns the args and block for a splat and block argument" do + m = MethodSpecs::Methods.new + m.method(:one_splat_one_block).parameters.should == [[:rest, :args], [:block, :block]] + end + + it "returns [] for a Method generated by attr_reader" do + m = MethodSpecs::Methods.new + m.method(:reader).parameters.should == [] + end + + it "return [[:req]] for a Method generated by attr_writer" do + m = MethodSpecs::Methods.new + m.method(:writer=).parameters.should == [[:req]] + end +end diff --git a/spec/rubyspec/core/method/receiver_spec.rb b/spec/rubyspec/core/method/receiver_spec.rb new file mode 100644 index 0000000000..2c56ab2239 --- /dev/null +++ b/spec/rubyspec/core/method/receiver_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#receiver" do + it "returns the receiver of the method" do + s = "abc" + s.method(:upcase).receiver.should equal(s) + end + + it "returns the right receiver even when aliased" do + obj = MethodSpecs::Methods.new + obj.method(:foo).receiver.should equal(obj) + obj.method(:bar).receiver.should equal(obj) + end + + describe "for a Method generated by respond_to_missing?" do + it "returns the receiver of the method" do + m = MethodSpecs::Methods.new + m.method(:handled_via_method_missing).receiver.should equal(m) + end + end +end diff --git a/spec/rubyspec/core/method/shared/call.rb b/spec/rubyspec/core/method/shared/call.rb new file mode 100644 index 0000000000..f178b9da7d --- /dev/null +++ b/spec/rubyspec/core/method/shared/call.rb @@ -0,0 +1,51 @@ +describe :method_call, shared: true do + it "invokes the method with the specified arguments, returning the method's return value" do + m = 12.method("+") + m.send(@method, 3).should == 15 + m.send(@method, 20).should == 32 + + m = MethodSpecs::Methods.new.method(:attr=) + m.send(@method, 42).should == 42 + end + + it "raises an ArgumentError when given incorrect number of arguments" do + lambda { + MethodSpecs::Methods.new.method(:two_req).send(@method, 1, 2, 3) + }.should raise_error(ArgumentError) + lambda { + MethodSpecs::Methods.new.method(:two_req).send(@method, 1) + }.should raise_error(ArgumentError) + end + + describe "for a Method generated by respond_to_missing?" do + it "invokes method_missing with the specified arguments and returns the result" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + meth.send(@method, :argument).should == [:argument] + end + + it "invokes method_missing with the method name and the specified arguments" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + + @m.should_receive(:method_missing).with(:handled_via_method_missing, :argument) + meth.send(@method, :argument) + end + + it "invokes method_missing dynamically" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + + def @m.method_missing(*); :changed; end + meth.send(@method, :argument).should == :changed + end + + it "does not call the original method name even if it now exists" do + @m = MethodSpecs::Methods.new + meth = @m.method(:handled_via_method_missing) + + def @m.handled_via_method_missing(*); :not_called; end + meth.send(@method, :argument).should == [:argument] + end + end +end diff --git a/spec/rubyspec/core/method/shared/eql.rb b/spec/rubyspec/core/method/shared/eql.rb new file mode 100644 index 0000000000..8cff45760b --- /dev/null +++ b/spec/rubyspec/core/method/shared/eql.rb @@ -0,0 +1,94 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :method_equal, shared: true do + before :each do + @m = MethodSpecs::Methods.new + @m_foo = @m.method(:foo) + @m2 = MethodSpecs::Methods.new + @a = MethodSpecs::A.new + end + + it "returns true if methods are the same" do + m2 = @m.method(:foo) + + @m_foo.send(@method, @m_foo).should be_true + @m_foo.send(@method, m2).should be_true + end + + it "returns true on aliased methods" do + m_bar = @m.method(:bar) + + m_bar.send(@method, @m_foo).should be_true + end + + it "returns true if the two core methods are aliases" do + s = "hello" + a = s.method(:size) + b = s.method(:length) + a.send(@method, b).should be_true + end + + it "returns false on a method which is neither aliased nor the same method" do + m2 = @m.method(:zero) + + @m_foo.send(@method, m2).should be_false + end + + it "returns false for a method which is not bound to the same object" do + m2_foo = @m2.method(:foo) + a_baz = @a.method(:baz) + + @m_foo.send(@method, m2_foo).should be_false + @m_foo.send(@method, a_baz).should be_false + end + + it "returns false if the two methods are bound to the same object but were defined independently" do + m2 = @m.method(:same_as_foo) + @m_foo.send(@method, m2).should be_false + end + + it "returns true if a method was defined using the other one" do + MethodSpecs::Methods.send :define_method, :defined_foo, MethodSpecs::Methods.instance_method(:foo) + m2 = @m.method(:defined_foo) + @m_foo.send(@method, m2).should be_true + end + + it "returns false if comparing a method defined via define_method and def" do + defn = @m.method(:zero) + defined = @m.method(:zero_defined_method) + + defn.send(@method, defined).should be_false + defined.send(@method, defn).should be_false + end + + describe 'missing methods' do + it "returns true for the same method missing" do + miss1 = @m.method(:handled_via_method_missing) + miss1bis = @m.method(:handled_via_method_missing) + miss2 = @m.method(:also_handled) + + miss1.send(@method, miss1bis).should be_true + miss1.send(@method, miss2).should be_false + end + + it 'calls respond_to_missing? with true to include private methods' do + @m.should_receive(:respond_to_missing?).with(:some_missing_method, true).and_return(true) + @m.method(:some_missing_method) + end + end + + it "returns false if the two methods are bound to different objects, have the same names, and identical bodies" do + a = MethodSpecs::Eql.instance_method(:same_body) + b = MethodSpecs::Eql2.instance_method(:same_body) + a.send(@method, b).should be_false + end + + it "returns false if the argument is not a Method object" do + String.instance_method(:size).send(@method, 7).should be_false + end + + it "returns false if the argument is an unbound version of self" do + method(:load).send(@method, method(:load).unbind).should be_false + end +end diff --git a/spec/rubyspec/core/method/shared/to_s.rb b/spec/rubyspec/core/method/shared/to_s.rb new file mode 100644 index 0000000000..239974c8e3 --- /dev/null +++ b/spec/rubyspec/core/method/shared/to_s.rb @@ -0,0 +1,34 @@ +require "#{File.dirname __FILE__}/../../../spec_helper" +require "#{File.dirname __FILE__}/../fixtures/classes" + +describe :method_to_s, shared: true do + before :each do + @m = MethodSpecs::MySub.new.method :bar + @string = @m.send(@method).sub(/0x\w+/, '0xXXXXXX') + end + + it "returns a String" do + @m.send(@method).should be_kind_of(String) + end + + it "returns a String for methods defined with attr_accessor" do + m = MethodSpecs::Methods.new.method :attr + m.send(@method).should be_kind_of(String) + end + + it "returns a String containing 'Method'" do + @string.should =~ /\bMethod\b/ + end + + it "returns a String containing the method name" do + @string.should =~ /\#bar/ + end + + it "returns a String containing the Module the method is defined in" do + @string.should =~ /MethodSpecs::MyMod/ + end + + it "returns a String containing the Module the method is referenced from" do + @string.should =~ /MethodSpecs::MySub/ + end +end diff --git a/spec/rubyspec/core/method/source_location_spec.rb b/spec/rubyspec/core/method/source_location_spec.rb new file mode 100644 index 0000000000..2ba2fdf5e9 --- /dev/null +++ b/spec/rubyspec/core/method/source_location_spec.rb @@ -0,0 +1,95 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#source_location" do + before :each do + @method = MethodSpecs::SourceLocation.method(:location) + end + + it "returns an Array" do + @method.source_location.should be_an_instance_of(Array) + end + + it "sets the first value to the path of the file in which the method was defined" do + file = @method.source_location.first + file.should be_an_instance_of(String) + file.should == File.dirname(__FILE__) + '/fixtures/classes.rb' + end + + it "sets the last value to a Fixnum representing the line on which the method was defined" do + line = @method.source_location.last + line.should be_an_instance_of(Fixnum) + line.should == 5 + end + + it "returns the last place the method was defined" do + MethodSpecs::SourceLocation.method(:redefined).source_location.last.should == 13 + end + + it "returns the location of the original method even if it was aliased" do + MethodSpecs::SourceLocation.new.method(:aka).source_location.last.should == 17 + end + + it "works for methods defined with a block" do + line = nil + klass = Class.new do + line = __LINE__ + 1 + define_method(:f) { } + end + + method = klass.new.method(:f) + method.source_location[0].should =~ /#{__FILE__}/ + method.source_location[1].should == line + end + + it "works for methods defined with a Method" do + line = nil + klass = Class.new do + line = __LINE__ + 1 + def f + end + define_method :g, new.method(:f) + end + + method = klass.new.method(:g) + method.source_location[0].should =~ /#{__FILE__}/ + method.source_location[1].should == line + end + + it "works for methods defined with an UnboundMethod" do + line = nil + klass = Class.new do + line = __LINE__ + 1 + def f + end + define_method :g, instance_method(:f) + end + + method = klass.new.method(:g) + method.source_location[0].should =~ /#{__FILE__}/ + method.source_location[1].should == line + end + + it "works for methods whose visibility has been overridden in a subclass" do + line = nil + superclass = Class.new do + line = __LINE__ + 1 + def f + end + end + subclass = Class.new(superclass) do + private :f + end + + method = subclass.new.method(:f) + method.source_location[0].should =~ /#{__FILE__}/ + method.source_location[1].should == line + end + + describe "for a Method generated by respond_to_missing?" do + it "returns nil" do + m = MethodSpecs::Methods.new + m.method(:handled_via_method_missing).source_location.should be_nil + end + end +end diff --git a/spec/rubyspec/core/method/super_method_spec.rb b/spec/rubyspec/core/method/super_method_spec.rb new file mode 100644 index 0000000000..cbc595b572 --- /dev/null +++ b/spec/rubyspec/core/method/super_method_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#super_method" do + it "returns the method that would be called by super in the method" do + obj = MethodSpecs::C.new + obj.extend MethodSpecs::OverrideAgain + meth = obj.method(:overridden) + + s_meth = meth.super_method + s_meth.owner.should == MethodSpecs::C + s_meth.receiver.should == obj + s_meth.name.should == :overridden + + ss_meth = meth.super_method.super_method + ss_meth.owner.should == MethodSpecs::BetweenBAndC + ss_meth.receiver.should == obj + ss_meth.name.should == :overridden + + sss_meth = meth.super_method.super_method.super_method + sss_meth.owner.should == MethodSpecs::B + sss_meth.receiver.should == obj + sss_meth.name.should == :overridden + end + + it "returns nil when there's no super method in the parent" do + method = Object.new.method(:method) + method.super_method.should == nil + end + + it "returns nil when the parent's method is removed" do + klass = Class.new do + def overridden; end + end + sub = Class.new(klass) do + def overridden; end + end + object = sub.new + method = object.method(:overridden) + + klass.class_eval { undef :overridden } + + method.super_method.should == nil + end +end diff --git a/spec/rubyspec/core/method/to_proc_spec.rb b/spec/rubyspec/core/method/to_proc_spec.rb new file mode 100644 index 0000000000..5a754f8597 --- /dev/null +++ b/spec/rubyspec/core/method/to_proc_spec.rb @@ -0,0 +1,89 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#to_proc" do + before :each do + ScratchPad.record [] + + @m = MethodSpecs::Methods.new + @meth = @m.method(:foo) + end + + it "returns a Proc object corresponding to the method" do + @meth.to_proc.kind_of?(Proc).should == true + end + + it "returns a Proc which does not depends on the value of self" do + 3.instance_exec(4, &5.method(:+)).should == 9 + end + + + it "returns a Proc object with the correct arity" do + # This may seem redundant but this bug has cropped up in jruby, mri and yarv. + # http://jira.codehaus.org/browse/JRUBY-124 + [ :zero, :one_req, :two_req, + :zero_with_block, :one_req_with_block, :two_req_with_block, + :one_opt, :one_req_one_opt, :one_req_two_opt, :two_req_one_opt, + :one_opt_with_block, :one_req_one_opt_with_block, :one_req_two_opt_with_block, :two_req_one_opt_with_block, + :zero_with_splat, :one_req_with_splat, :two_req_with_splat, + :one_req_one_opt_with_splat, :one_req_two_opt_with_splat, :two_req_one_opt_with_splat, + :zero_with_splat_and_block, :one_req_with_splat_and_block, :two_req_with_splat_and_block, + :one_req_one_opt_with_splat_and_block, :one_req_two_opt_with_splat_and_block, :two_req_one_opt_with_splat_and_block + ].each do |m| + @m.method(m).to_proc.arity.should == @m.method(m).arity + end + end + + it "returns a proc that can be used by define_method" do + x = 'test' + to_s = class << x + define_method :foo, method(:to_s).to_proc + to_s + end + + x.foo.should == to_s + end + + it "returns a proc that can be yielded to" do + x = Object.new + def x.foo(*a); a; end + def x.bar; yield; end + def x.baz(*a); yield(*a); end + + m = x.method :foo + x.bar(&m).should == [] + x.baz(1,2,3,&m).should == [1,2,3] + end + + # #5926 + it "returns a proc that can receive a block" do + x = Object.new + def x.foo; yield 'bar'; end + + m = x.method :foo + result = nil + m.to_proc.call {|val| result = val} + result.should == 'bar' + end + + it "can be called directly and not unwrap arguments like a block" do + obj = MethodSpecs::ToProcBeta.new + obj.to_proc.call([1]).should == [1] + end + + it "should correct handle arguments (unwrap)" do + obj = MethodSpecs::ToProcBeta.new + + array = [[1]] + array.each(&obj) + ScratchPad.recorded.should == [[1]] + end + + it "executes method with whole array (one argument)" do + obj = MethodSpecs::ToProcBeta.new + + array = [[1, 2]] + array.each(&obj) + ScratchPad.recorded.should == [[1, 2]] + end +end diff --git a/spec/rubyspec/core/method/to_s_spec.rb b/spec/rubyspec/core/method/to_s_spec.rb new file mode 100644 index 0000000000..1bf341f2a6 --- /dev/null +++ b/spec/rubyspec/core/method/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "Method#to_s" do + it_behaves_like(:method_to_s, :to_s) +end diff --git a/spec/rubyspec/core/method/unbind_spec.rb b/spec/rubyspec/core/method/unbind_spec.rb new file mode 100644 index 0000000000..cb0dc65a75 --- /dev/null +++ b/spec/rubyspec/core/method/unbind_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Method#unbind" do + before :each do + @normal = MethodSpecs::Methods.new + @normal_m = @normal.method :foo + @normal_um = @normal_m.unbind + @pop_um = MethodSpecs::MySub.new.method(:bar).unbind + @string = @pop_um.inspect.sub(/0x\w+/, '0xXXXXXX') + end + + it "returns an UnboundMethod" do + @normal_um.should be_kind_of(UnboundMethod) + end + + it "returns a String containing 'UnboundMethod'" do + @string.should =~ /\bUnboundMethod\b/ + end + + it "returns a String containing the method name" do + @string.should =~ /\#bar/ + end + + it "returns a String containing the Module the method is defined in" do + @string.should =~ /MethodSpecs::MyMod/ + end + + it "returns a String containing the Module the method is referenced from" do + @string.should =~ /MethodSpecs::MySub/ + end + + specify "rebinding UnboundMethod to Method's obj produces exactly equivalent Methods" do + @normal_um.bind(@normal).should == @normal_m + @normal_m.should == @normal_um.bind(@normal) + end +end diff --git a/spec/rubyspec/core/module/alias_method_spec.rb b/spec/rubyspec/core/module/alias_method_spec.rb new file mode 100644 index 0000000000..7ad71f41f9 --- /dev/null +++ b/spec/rubyspec/core/module/alias_method_spec.rb @@ -0,0 +1,150 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#alias_method" do + before :each do + @class = Class.new(ModuleSpecs::Aliasing) + @object = @class.new + end + + it "makes a copy of the method" do + @class.make_alias :uno, :public_one + @class.make_alias :double, :public_two + @object.uno.should == @object.public_one + @object.double(12).should == @object.public_two(12) + end + + it "creates methods that are == to eachother" do + @class.make_alias :uno, :public_one + @object.method(:uno).should == @object.method(:public_one) + end + + it "preserves the arguments information of the original methods" do + @class.make_alias :uno, :public_one + @class.make_alias :double, :public_two + @class.instance_method(:uno).parameters.should == @class.instance_method(:public_one).parameters + @class.instance_method(:double).parameters.should == @class.instance_method(:public_two).parameters + end + + it "retains method visibility" do + @class.make_alias :private_ichi, :private_one + lambda { @object.private_one }.should raise_error(NameError) + lambda { @object.private_ichi }.should raise_error(NameError) + @class.make_alias :public_ichi, :public_one + @object.public_ichi.should == @object.public_one + @class.make_alias :protected_ichi, :protected_one + lambda { @object.protected_ichi }.should raise_error(NameError) + end + + it "handles aliasing a stub that changes visibility" do + @class.__send__ :public, :private_one + @class.make_alias :was_private_one, :private_one + @object.was_private_one.should == 1 + end + + it "fails if origin method not found" do + lambda { @class.make_alias :ni, :san }.should raise_error(NameError) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end + + it "raises RuntimeError if frozen" do + @class.freeze + lambda { @class.make_alias :uno, :public_one }.should raise_error(RuntimeError) + end + + it "converts the names using #to_str" do + @class.make_alias "un", "public_one" + @class.make_alias :deux, "public_one" + @class.make_alias "trois", :public_one + @class.make_alias :quatre, :public_one + name = mock('cinq') + name.should_receive(:to_str).any_number_of_times.and_return("cinq") + @class.make_alias name, "public_one" + @class.make_alias "cinq", name + end + + it "raises a TypeError when the given name can't be converted using to_str" do + lambda { @class.make_alias mock('x'), :public_one }.should raise_error(TypeError) + end + + it "is a private method" do + lambda { @class.alias_method :ichi, :public_one }.should raise_error(NoMethodError) + end + + it "returns self" do + @class.send(:alias_method, :checking_return_value, :public_one).should equal(@class) + end + + it "works in module" do + ModuleSpecs::Allonym.new.publish.should == :report + end + + it "works on private module methods in a module that has been reopened" do + ModuleSpecs::ReopeningModule.foo.should == true + lambda { ModuleSpecs::ReopeningModule.foo2 }.should_not raise_error(NoMethodError) + end + + it "accesses a method defined on Object from Kernel" do + Kernel.should_not have_public_instance_method(:module_specs_public_method_on_object) + + Kernel.should have_public_instance_method(:module_specs_alias_on_kernel) + Object.should have_public_instance_method(:module_specs_alias_on_kernel) + end + + it "can call a method with super aliased twice" do + ModuleSpecs::AliasingSuper::Target.new.super_call(1).should == 1 + end + + it "preserves original super call after alias redefine" do + ModuleSpecs::AliasingSuper::RedefineAfterAlias.new.alias_super_call(1).should == 1 + end + + describe "aliasing special methods" do + before :all do + @class = ModuleSpecs::Aliasing + @subclass = ModuleSpecs::AliasingSubclass + end + + it "keeps initialize private when aliasing" do + @class.make_alias(:initialize, :public_one) + @class.private_instance_methods.include?(:initialize).should be_true + + @subclass.make_alias(:initialize, :public_one) + @subclass.private_instance_methods.include?(:initialize).should be_true + end + + it "keeps initialize_copy private when aliasing" do + @class.make_alias(:initialize_copy, :public_one) + @class.private_instance_methods.include?(:initialize_copy).should be_true + + @subclass.make_alias(:initialize_copy, :public_one) + @subclass.private_instance_methods.include?(:initialize_copy).should be_true + end + + it "keeps initialize_clone private when aliasing" do + @class.make_alias(:initialize_clone, :public_one) + @class.private_instance_methods.include?(:initialize_clone).should be_true + + @subclass.make_alias(:initialize_clone, :public_one) + @subclass.private_instance_methods.include?(:initialize_clone).should be_true + end + + it "keeps initialize_dup private when aliasing" do + @class.make_alias(:initialize_dup, :public_one) + @class.private_instance_methods.include?(:initialize_dup).should be_true + + @subclass.make_alias(:initialize_dup, :public_one) + @subclass.private_instance_methods.include?(:initialize_dup).should be_true + end + + it "keeps respond_to_missing? private when aliasing" do + @class.make_alias(:respond_to_missing?, :public_one) + @class.private_instance_methods.include?(:respond_to_missing?).should be_true + + @subclass.make_alias(:respond_to_missing?, :public_one) + @subclass.private_instance_methods.include?(:respond_to_missing?).should be_true + end + end +end diff --git a/spec/rubyspec/core/module/allocate_spec.rb b/spec/rubyspec/core/module/allocate_spec.rb new file mode 100644 index 0000000000..306426881a --- /dev/null +++ b/spec/rubyspec/core/module/allocate_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Module.allocate" do + it "returns an instance of Module" do + mod = Module.allocate + mod.should be_an_instance_of(Module) + end + + it "returns a fully-formed instance of Module" do + mod = Module.allocate + mod.constants.should_not == nil + mod.methods.should_not == nil + end +end diff --git a/spec/rubyspec/core/module/ancestors_spec.rb b/spec/rubyspec/core/module/ancestors_spec.rb new file mode 100644 index 0000000000..1cd537f7a0 --- /dev/null +++ b/spec/rubyspec/core/module/ancestors_spec.rb @@ -0,0 +1,70 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#ancestors" do + it "returns a list of modules included in self (including self)" do + BasicObject.ancestors.should == [BasicObject] + ModuleSpecs.ancestors.should == [ModuleSpecs] + ModuleSpecs::Basic.ancestors.should == [ModuleSpecs::Basic] + ModuleSpecs::Super.ancestors.should == [ModuleSpecs::Super, ModuleSpecs::Basic] + ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should == + [ModuleSpecs::Parent, Object, Kernel, BasicObject] + ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should == + [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Kernel, BasicObject] + end + + it "returns only modules and classes" do + class << ModuleSpecs::Child; self; end.ancestors.should include(ModuleSpecs::Internal, Class, Module, Object, Kernel) + end + + it "has 1 entry per module or class" do + ModuleSpecs::Parent.ancestors.should == ModuleSpecs::Parent.ancestors.uniq + end + + describe "when called on a singleton class" do + it "includes the singleton classes of ancestors" do + parent = Class.new + child = Class.new(parent) + schild = child.singleton_class + + schild.ancestors.should include(schild, + parent.singleton_class, + Object.singleton_class, + BasicObject.singleton_class, + Class, + Module, + Object, + Kernel, + BasicObject) + + end + + describe 'for a standalone module' do + it 'does not include Class' do + s_mod = ModuleSpecs.singleton_class + s_mod.ancestors.should_not include(Class) + end + + it 'does not include other singleton classes' do + s_standalone_mod = ModuleSpecs.singleton_class + s_module = Module.singleton_class + s_object = Object.singleton_class + s_basic_object = BasicObject.singleton_class + + s_standalone_mod.ancestors.should_not include(s_module, s_object, s_basic_object) + end + + it 'includes its own singleton class' do + s_mod = ModuleSpecs.singleton_class + + s_mod.ancestors.should include(s_mod) + end + + it 'includes standard chain' do + s_mod = ModuleSpecs.singleton_class + + s_mod.ancestors.should include(Module, Object, Kernel, BasicObject) + end + end + end +end diff --git a/spec/rubyspec/core/module/append_features_spec.rb b/spec/rubyspec/core/module/append_features_spec.rb new file mode 100644 index 0000000000..ceb8c3f8eb --- /dev/null +++ b/spec/rubyspec/core/module/append_features_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#append_features" do + it "is a private method" do + Module.should have_private_instance_method(:append_features) + end + + describe "on Class" do + it "is undefined" do + Class.should_not have_private_instance_method(:append_features, true) + end + + it "raises a TypeError if calling after rebinded to Class" do + lambda { + Module.instance_method(:append_features).bind(Class.new).call Module.new + }.should raise_error(TypeError) + end + end + + it "gets called when self is included in another module/class" do + begin + m = Module.new do + def self.append_features(mod) + $appended_to = mod + end + end + + c = Class.new do + include m + end + + $appended_to.should == c + ensure + $appended_to = nil + end + end + + it "raises an ArgumentError on a cyclic include" do + lambda { + ModuleSpecs::CyclicAppendA.send(:append_features, ModuleSpecs::CyclicAppendA) + }.should raise_error(ArgumentError) + + lambda { + ModuleSpecs::CyclicAppendB.send(:append_features, ModuleSpecs::CyclicAppendA) + }.should raise_error(ArgumentError) + + end + + it "copies own tainted status to the given module" do + other = Module.new + Module.new.taint.send :append_features, other + other.tainted?.should be_true + end + + it "copies own untrusted status to the given module" do + other = Module.new + Module.new.untrust.send :append_features, other + other.untrusted?.should be_true + end + + describe "when other is frozen" do + before :each do + @receiver = Module.new + @other = Module.new.freeze + end + + it "raises a RuntimeError before appending self" do + lambda { @receiver.send(:append_features, @other) }.should raise_error(RuntimeError) + @other.ancestors.should_not include(@receiver) + end + end +end diff --git a/spec/rubyspec/core/module/attr_accessor_spec.rb b/spec/rubyspec/core/module/attr_accessor_spec.rb new file mode 100644 index 0000000000..4bd198dedc --- /dev/null +++ b/spec/rubyspec/core/module/attr_accessor_spec.rb @@ -0,0 +1,90 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#attr_accessor" do + it "creates a getter and setter for each given attribute name" do + c = Class.new do + attr_accessor :a, "b" + end + + o = c.new + + ['a','b'].each do |x| + o.respond_to?(x).should == true + o.respond_to?("#{x}=").should == true + end + + o.a = "a" + o.a.should == "a" + + o.b = "b" + o.b.should == "b" + o.a = o.b = nil + + o.send(:a=,"a") + o.send(:a).should == "a" + + o.send(:b=, "b") + o.send(:b).should == "b" + end + + it "not allows creating an attr_accessor on an immediate class" do + class TrueClass + attr_accessor :spec_attr_accessor + end + + lambda { true.spec_attr_accessor = "a" }.should raise_error(RuntimeError) + end + + it "converts non string/symbol/fixnum names to strings using to_str" do + (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test") + c = Class.new do + attr_accessor o + end + + c.new.respond_to?("test").should == true + c.new.respond_to?("test=").should == true + end + + it "raises a TypeError when the given names can't be converted to strings using to_str" do + o = mock('o') + lambda { Class.new { attr_accessor o } }.should raise_error(TypeError) + (o = mock('123')).should_receive(:to_str).and_return(123) + lambda { Class.new { attr_accessor o } }.should raise_error(TypeError) + end + + it "applies current visibility to methods created" do + c = Class.new do + protected + attr_accessor :foo + end + + lambda { c.new.foo }.should raise_error(NoMethodError) + lambda { c.new.foo=1 }.should raise_error(NoMethodError) + end + + it "is a private method" do + lambda { Class.new.attr_accessor(:foo) }.should raise_error(NoMethodError) + end + + describe "on immediates" do + before :each do + class Fixnum + attr_accessor :foobar + end + end + + after :each do + if Fixnum.method_defined?(:foobar) + Fixnum.send(:remove_method, :foobar) + end + if Fixnum.method_defined?(:foobar=) + Fixnum.send(:remove_method, :foobar=) + end + end + + it "can read through the accessor" do + 1.foobar.should be_nil + end + end +end diff --git a/spec/rubyspec/core/module/attr_reader_spec.rb b/spec/rubyspec/core/module/attr_reader_spec.rb new file mode 100644 index 0000000000..f2b968d3fc --- /dev/null +++ b/spec/rubyspec/core/module/attr_reader_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#attr_reader" do + it "creates a getter for each given attribute name" do + c = Class.new do + attr_reader :a, "b" + + def initialize + @a = "test" + @b = "test2" + end + end + + o = c.new + %w{a b}.each do |x| + o.respond_to?(x).should == true + o.respond_to?("#{x}=").should == false + end + + o.a.should == "test" + o.b.should == "test2" + o.send(:a).should == "test" + o.send(:b).should == "test2" + end + + it "not allows for adding an attr_reader to an immediate" do + class TrueClass + attr_reader :spec_attr_reader + end + + lambda { true.instance_variable_set("@spec_attr_reader", "a") }.should raise_error(RuntimeError) + end + + it "converts non string/symbol/fixnum names to strings using to_str" do + (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test") + c = Class.new do + attr_reader o + end + + c.new.respond_to?("test").should == true + c.new.respond_to?("test=").should == false + end + + it "raises a TypeError when the given names can't be converted to strings using to_str" do + o = mock('o') + lambda { Class.new { attr_reader o } }.should raise_error(TypeError) + (o = mock('123')).should_receive(:to_str).and_return(123) + lambda { Class.new { attr_reader o } }.should raise_error(TypeError) + end + + it "applies current visibility to methods created" do + c = Class.new do + protected + attr_reader :foo + end + + lambda { c.new.foo }.should raise_error(NoMethodError) + end + + it "is a private method" do + lambda { Class.new.attr_reader(:foo) }.should raise_error(NoMethodError) + end +end diff --git a/spec/rubyspec/core/module/attr_spec.rb b/spec/rubyspec/core/module/attr_spec.rb new file mode 100644 index 0000000000..9a469707a1 --- /dev/null +++ b/spec/rubyspec/core/module/attr_spec.rb @@ -0,0 +1,149 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#attr" do + before :each do + $VERBOSE, @verbose = false, $VERBOSE + end + + after :each do + $VERBOSE = @verbose + end + + it "creates a getter for the given attribute name" do + c = Class.new do + attr :attr + attr "attr3" + + def initialize + @attr, @attr2, @attr3 = "test", "test2", "test3" + end + end + + o = c.new + + %w{attr attr3}.each do |a| + o.respond_to?(a).should == true + o.respond_to?("#{a}=").should == false + end + + o.attr.should == "test" + o.attr3.should == "test3" + o.send(:attr).should == "test" + o.send(:attr3).should == "test3" + end + + it "creates a setter for the given attribute name if writable is true" do + c = Class.new do + attr :attr, true + attr "attr3", true + + def initialize + @attr, @attr2, @attr3 = "test", "test2", "test3" + end + end + + o = c.new + + %w{attr attr3}.each do |a| + o.respond_to?(a).should == true + o.respond_to?("#{a}=").should == true + end + + o.attr = "test updated" + o.attr3 = "test3 updated" + end + + it "creates a getter and setter for the given attribute name if called with and without writeable is true" do + c = Class.new do + attr :attr, true + attr :attr + + attr "attr3", true + attr "attr3" + + def initialize + @attr, @attr2, @attr3 = "test", "test2", "test3" + end + end + + o = c.new + + %w{attr attr3}.each do |a| + o.respond_to?(a).should == true + o.respond_to?("#{a}=").should == true + end + + o.attr.should == "test" + o.attr = "test updated" + o.attr.should == "test updated" + + o.attr3.should == "test3" + o.attr3 = "test3 updated" + o.attr3.should == "test3 updated" + end + + it "applies current visibility to methods created" do + c = Class.new do + protected + attr :foo, true + end + + lambda { c.new.foo }.should raise_error(NoMethodError) + lambda { c.new.foo=1 }.should raise_error(NoMethodError) + end + + it "creates a getter but no setter for all given attribute names" do + c = Class.new do + attr :attr, "attr2", "attr3" + + def initialize + @attr, @attr2, @attr3 = "test", "test2", "test3" + end + end + + o = c.new + + %w{attr attr2 attr3}.each do |a| + o.respond_to?(a).should == true + o.respond_to?("#{a}=").should == false + end + + o.attr.should == "test" + o.attr2.should == "test2" + o.attr3.should == "test3" + end + + it "applies current visibility to methods created" do + c = Class.new do + protected + attr :foo, :bar + end + + lambda { c.new.foo }.should raise_error(NoMethodError) + lambda { c.new.bar }.should raise_error(NoMethodError) + end + + it "converts non string/symbol/fixnum names to strings using to_str" do + (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test") + Class.new { attr o }.new.respond_to?("test").should == true + end + + it "raises a TypeError when the given names can't be converted to strings using to_str" do + o = mock('o') + lambda { Class.new { attr o } }.should raise_error(TypeError) + (o = mock('123')).should_receive(:to_str).and_return(123) + lambda { Class.new { attr o } }.should raise_error(TypeError) + end + + it "with a boolean argument emits a warning when $VERBOSE is true" do + lambda { + $VERBOSE = true + Class.new { attr :foo, true } + }.should complain(/boolean argument is obsoleted/) + end + + it "is a private method" do + lambda { Class.new.attr(:foo) }.should raise_error(NoMethodError) + end +end diff --git a/spec/rubyspec/core/module/attr_writer_spec.rb b/spec/rubyspec/core/module/attr_writer_spec.rb new file mode 100644 index 0000000000..e1308d19bb --- /dev/null +++ b/spec/rubyspec/core/module/attr_writer_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#attr_writer" do + it "creates a setter for each given attribute name" do + c = Class.new do + attr_writer :test1, "test2" + end + o = c.new + + o.respond_to?("test1").should == false + o.respond_to?("test2").should == false + + o.respond_to?("test1=").should == true + o.test1 = "test_1" + o.instance_variable_get(:@test1).should == "test_1" + + o.respond_to?("test2=").should == true + o.test2 = "test_2" + o.instance_variable_get(:@test2).should == "test_2" + o.send(:test1=,"test_1 updated") + o.instance_variable_get(:@test1).should == "test_1 updated" + o.send(:test2=,"test_2 updated") + o.instance_variable_get(:@test2).should == "test_2 updated" + end + + it "not allows for adding an attr_writer to an immediate" do + class TrueClass + attr_writer :spec_attr_writer + end + + lambda { true.spec_attr_writer = "a" }.should raise_error(RuntimeError) + end + + it "converts non string/symbol/fixnum names to strings using to_str" do + (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test") + c = Class.new do + attr_writer o + end + + c.new.respond_to?("test").should == false + c.new.respond_to?("test=").should == true + end + + it "raises a TypeError when the given names can't be converted to strings using to_str" do + o = mock('test1') + lambda { Class.new { attr_writer o } }.should raise_error(TypeError) + (o = mock('123')).should_receive(:to_str).and_return(123) + lambda { Class.new { attr_writer o } }.should raise_error(TypeError) + end + + it "applies current visibility to methods created" do + c = Class.new do + protected + attr_writer :foo + end + + lambda { c.new.foo=1 }.should raise_error(NoMethodError) + end + + it "is a private method" do + lambda { Class.new.attr_writer(:foo) }.should raise_error(NoMethodError) + end +end diff --git a/spec/rubyspec/core/module/autoload_spec.rb b/spec/rubyspec/core/module/autoload_spec.rb new file mode 100644 index 0000000000..b479d049bb --- /dev/null +++ b/spec/rubyspec/core/module/autoload_spec.rb @@ -0,0 +1,462 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'thread' + +describe "Module#autoload?" do + it "returns the name of the file that will be autoloaded" do + ModuleSpecs::Autoload.autoload :Autoload, "autoload.rb" + ModuleSpecs::Autoload.autoload?(:Autoload).should == "autoload.rb" + end + + it "returns nil if no file has been registered for a constant" do + ModuleSpecs::Autoload.autoload?(:Manualload).should be_nil + end +end + +describe "Module#autoload" do + before :all do + @non_existent = fixture __FILE__, "no_autoload.rb" + end + + before :each do + @loaded_features = $".dup + @frozen_module = Module.new.freeze + + ScratchPad.clear + end + + after :each do + $".replace @loaded_features + end + + it "registers a file to load the first time the named constant is accessed" do + ModuleSpecs::Autoload.autoload :A, @non_existent + ModuleSpecs::Autoload.autoload?(:A).should == @non_existent + end + + it "sets the autoload constant in the constants table" do + ModuleSpecs::Autoload.autoload :B, @non_existent + ModuleSpecs::Autoload.should have_constant(:B) + end + + it "loads the registered constant when it is accessed" do + ModuleSpecs::Autoload.should_not have_constant(:X) + ModuleSpecs::Autoload.autoload :X, fixture(__FILE__, "autoload_x.rb") + ModuleSpecs::Autoload::X.should == :x + ModuleSpecs::Autoload.send(:remove_const, :X) + end + + it "loads the registered constant into a dynamically created class" do + cls = Class.new { autoload :C, fixture(__FILE__, "autoload_c.rb") } + ModuleSpecs::Autoload::DynClass = cls + + ScratchPad.recorded.should be_nil + ModuleSpecs::Autoload::DynClass::C.new.loaded.should == :dynclass_c + ScratchPad.recorded.should == :loaded + end + + it "loads the registered constant into a dynamically created module" do + mod = Module.new { autoload :D, fixture(__FILE__, "autoload_d.rb") } + ModuleSpecs::Autoload::DynModule = mod + + ScratchPad.recorded.should be_nil + ModuleSpecs::Autoload::DynModule::D.new.loaded.should == :dynmodule_d + ScratchPad.recorded.should == :loaded + end + + it "loads the registered constant when it is opened as a class" do + ModuleSpecs::Autoload.autoload :E, fixture(__FILE__, "autoload_e.rb") + class ModuleSpecs::Autoload::E + end + ModuleSpecs::Autoload::E.new.loaded.should == :autoload_e + end + + it "loads the registered constant when it is opened as a module" do + ModuleSpecs::Autoload.autoload :F, fixture(__FILE__, "autoload_f.rb") + module ModuleSpecs::Autoload::F + end + ModuleSpecs::Autoload::F.loaded.should == :autoload_f + end + + it "loads the registered constant when it is inherited from" do + ModuleSpecs::Autoload.autoload :G, fixture(__FILE__, "autoload_g.rb") + class ModuleSpecs::Autoload::Gsub < ModuleSpecs::Autoload::G + end + ModuleSpecs::Autoload::Gsub.new.loaded.should == :autoload_g + end + + it "loads the registered constant when it is included" do + ModuleSpecs::Autoload.autoload :H, fixture(__FILE__, "autoload_h.rb") + class ModuleSpecs::Autoload::HClass + include ModuleSpecs::Autoload::H + end + ModuleSpecs::Autoload::HClass.new.loaded.should == :autoload_h + end + + it "does not load the file when the constant is already set" do + ModuleSpecs::Autoload.autoload :I, fixture(__FILE__, "autoload_i.rb") + ModuleSpecs::Autoload.const_set :I, 3 + ModuleSpecs::Autoload::I.should == 3 + ScratchPad.recorded.should be_nil + end + + it "loads a file with .rb extension when passed the name without the extension" do + ModuleSpecs::Autoload.autoload :J, fixture(__FILE__, "autoload_j") + ModuleSpecs::Autoload::J.should == :autoload_j + end + + it "does not load the file if the file is manually required" do + filename = fixture(__FILE__, "autoload_k.rb") + ModuleSpecs::Autoload.autoload :KHash, filename + + require filename + ScratchPad.recorded.should == :loaded + ScratchPad.clear + + ModuleSpecs::Autoload::KHash.should be_kind_of(Class) + ModuleSpecs::Autoload::KHash::K.should == :autoload_k + ScratchPad.recorded.should be_nil + end + + it "ignores the autoload request if the file is already loaded" do + filename = fixture(__FILE__, "autoload_s.rb") + + require filename + + ScratchPad.recorded.should == :loaded + ScratchPad.clear + + ModuleSpecs::Autoload.autoload :S, filename + ModuleSpecs::Autoload.autoload?(:S).should be_nil + ModuleSpecs::Autoload.send(:remove_const, :S) + end + + it "retains the autoload even if the request to require fails" do + filename = fixture(__FILE__, "a_path_that_should_not_exist.rb") + + ModuleSpecs::Autoload.autoload :NotThere, filename + ModuleSpecs::Autoload.autoload?(:NotThere).should == filename + + lambda { + require filename + }.should raise_error(LoadError) + + ModuleSpecs::Autoload.autoload?(:NotThere).should == filename + end + + it "allows multiple autoload constants for a single file" do + filename = fixture(__FILE__, "autoload_lm.rb") + ModuleSpecs::Autoload.autoload :L, filename + ModuleSpecs::Autoload.autoload :M, filename + ModuleSpecs::Autoload::L.should == :autoload_l + ModuleSpecs::Autoload::M.should == :autoload_m + end + + it "runs for an exception condition class and doesn't trample the exception" do + filename = fixture(__FILE__, "autoload_ex1.rb") + ModuleSpecs::Autoload.autoload :EX1, filename + ModuleSpecs::Autoload.use_ex1.should == :good + end + + it "does not load the file when referring to the constant in defined?" do + module ModuleSpecs::Autoload::Q + autoload :R, fixture(__FILE__, "autoload.rb") + defined?(R).should == "constant" + end + ModuleSpecs::Autoload::Q.should have_constant(:R) + end + + it "does not remove the constant from the constant table if load fails" do + ModuleSpecs::Autoload.autoload :Fail, @non_existent + ModuleSpecs::Autoload.should have_constant(:Fail) + + lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError) + ModuleSpecs::Autoload.should have_constant(:Fail) + end + + it "does not remove the constant from the constant table if the loaded files does not define it" do + ModuleSpecs::Autoload.autoload :O, fixture(__FILE__, "autoload_o.rb") + ModuleSpecs::Autoload.should have_constant(:O) + + lambda { ModuleSpecs::Autoload::O }.should raise_error(NameError) + ModuleSpecs::Autoload.should have_constant(:O) + end + + it "returns 'constant' on referring the constant with defined?()" do + module ModuleSpecs::Autoload::Q + autoload :R, fixture(__FILE__, "autoload.rb") + defined?(R).should == 'constant' + end + ModuleSpecs::Autoload::Q.should have_constant(:R) + end + + it "does not load the file when removing an autoload constant" do + module ModuleSpecs::Autoload::Q + autoload :R, fixture(__FILE__, "autoload.rb") + remove_const :R + end + ModuleSpecs::Autoload::Q.should_not have_constant(:R) + end + + it "does not load the file when accessing the constants table of the module" do + ModuleSpecs::Autoload.autoload :P, @non_existent + ModuleSpecs::Autoload.const_defined?(:P).should be_true + end + + it "loads the file when opening a module that is the autoloaded constant" do + module ModuleSpecs::Autoload::U + autoload :V, fixture(__FILE__, "autoload_v.rb") + + class V + X = get_value + end + end + + ModuleSpecs::Autoload::U::V::X.should == :autoload_uvx + end + + it "loads the file that defines subclass XX::YY < YY and YY is a top level constant" do + + module ModuleSpecs::Autoload::XX + autoload :YY, fixture(__FILE__, "autoload_subclass.rb") + end + + ModuleSpecs::Autoload::XX::YY.superclass.should == YY + end + + + it "looks up the constant in the scope where it is referred" do + module ModuleSpecs + module Autoload + autoload :QQ, fixture(__FILE__, "autoload_scope.rb") + class PP + QQ.new.should be_kind_of(ModuleSpecs::Autoload::PP::QQ) + end + end + end + end + + it "looks up the constant when in a meta class scope" do + module ModuleSpecs + module Autoload + autoload :R, fixture(__FILE__, "autoload_r.rb") + class << self + def r + R.new + end + end + end + end + ModuleSpecs::Autoload.r.should be_kind_of(ModuleSpecs::Autoload::R) + end + + # [ruby-core:19127] [ruby-core:29941] + it "does NOT raise a NameError when the autoload file did not define the constant and a module is opened with the same name" do + module ModuleSpecs::Autoload + class W + autoload :Y, fixture(__FILE__, "autoload_w.rb") + + class Y + end + end + end + + ModuleSpecs::Autoload::W::Y.should be_kind_of(Class) + ScratchPad.recorded.should == :loaded + ModuleSpecs::Autoload::W.send(:remove_const, :Y) + end + + it "calls #to_path on non-string filenames" do + p = mock('path') + p.should_receive(:to_path).and_return @non_existent + ModuleSpecs.autoload :A, p + end + + it "raises an ArgumentError when an empty filename is given" do + lambda { ModuleSpecs.autoload :A, "" }.should raise_error(ArgumentError) + end + + it "raises a NameError when the constant name starts with a lower case letter" do + lambda { ModuleSpecs.autoload "a", @non_existent }.should raise_error(NameError) + end + + it "raises a NameError when the constant name starts with a number" do + lambda { ModuleSpecs.autoload "1two", @non_existent }.should raise_error(NameError) + end + + it "raises a NameError when the constant name has a space in it" do + lambda { ModuleSpecs.autoload "a name", @non_existent }.should raise_error(NameError) + end + + it "shares the autoload request across dup'ed copies of modules" do + require fixture(__FILE__, "autoload_s.rb") + filename = fixture(__FILE__, "autoload_t.rb") + mod1 = Module.new { autoload :T, filename } + lambda { + ModuleSpecs::Autoload::S = mod1 + }.should complain(/already initialized constant/) + mod2 = mod1.dup + + mod1.autoload?(:T).should == filename + mod2.autoload?(:T).should == filename + + mod1::T.should == :autoload_t + lambda { mod2::T }.should raise_error(NameError) + end + + it "raises a TypeError if opening a class with a different superclass than the class defined in the autoload file" do + ModuleSpecs::Autoload.autoload :Z, fixture(__FILE__, "autoload_z.rb") + class ModuleSpecs::Autoload::ZZ + end + + lambda do + class ModuleSpecs::Autoload::Z < ModuleSpecs::Autoload::ZZ + end + end.should raise_error(TypeError) + end + + it "raises a TypeError if not passed a String or object respodning to #to_path for the filename" do + name = mock("autoload_name.rb") + + lambda { ModuleSpecs::Autoload.autoload :Str, name }.should raise_error(TypeError) + end + + it "calls #to_path on non-String filename arguments" do + name = mock("autoload_name.rb") + name.should_receive(:to_path).and_return("autoload_name.rb") + + lambda { ModuleSpecs::Autoload.autoload :Str, name }.should_not raise_error + end + + describe "on a frozen module" do + it "raises a RuntimeError before setting the name" do + lambda { @frozen_module.autoload :Foo, @non_existent }.should raise_error(RuntimeError) + @frozen_module.should_not have_constant(:Foo) + end + end + + describe "(concurrently)" do + it "blocks a second thread while a first is doing the autoload" do + ModuleSpecs::Autoload.autoload :Concur, fixture(__FILE__, "autoload_concur.rb") + + start = false + + ScratchPad.record [] + + t1_val = nil + t2_val = nil + + fin = false + + t1 = Thread.new do + Thread.pass until start + t1_val = ModuleSpecs::Autoload::Concur + ScratchPad.recorded << :t1_post + fin = true + end + + t2_exc = nil + + t2 = Thread.new do + Thread.pass until t1 and t1[:in_autoload_rb] + begin + t2_val = ModuleSpecs::Autoload::Concur + rescue Exception => e + t2_exc = e + else + Thread.pass until fin + ScratchPad.recorded << :t2_post + end + end + + start = true + + t1.join + t2.join + + ScratchPad.recorded.should == [:con_pre, :con_post, :t1_post, :t2_post] + + t1_val.should == 1 + t2_val.should == t1_val + + t2_exc.should be_nil + + ModuleSpecs::Autoload.send(:remove_const, :Concur) + end + end + + describe "when changing $LOAD_PATH" do + + before do + $LOAD_PATH.unshift(File.expand_path('../fixtures/path1', __FILE__)) + end + + after do + $LOAD_PATH.shift + $LOAD_PATH.shift + end + + it "does not reload a file due to a different load path" do + ModuleSpecs::Autoload.autoload :LoadPath, "load_path" + ModuleSpecs::Autoload::LoadPath.loaded.should == :autoload_load_path + end + + end + + describe "(concurrently)" do + ruby_bug "#10892", ""..."2.3" do + it "blocks others threads while doing an autoload" do + file_path = fixture(__FILE__, "repeated_concurrent_autoload.rb") + autoload_path = file_path.sub(/\.rb\Z/, '') + mod_count = 30 + thread_count = 16 + + mod_names = [] + mod_count.times do |i| + mod_name = :"Mod#{i}" + autoload mod_name, autoload_path + mod_names << mod_name + end + + barrier = ModuleSpecs::CyclicBarrier.new thread_count + ScratchPad.record ModuleSpecs::ThreadSafeCounter.new + + threads = (1..thread_count).map do + Thread.new do + mod_names.each do |mod_name| + break false unless barrier.enabled? + + was_last_one_in = barrier.await # wait for all threads to finish the iteration + # clean up so we can autoload the same file again + $LOADED_FEATURES.delete(file_path) if was_last_one_in && $LOADED_FEATURES.include?(file_path) + barrier.await # get ready for race + + begin + Object.const_get(mod_name).foo + rescue NoMethodError + barrier.disable! + break false + end + end + end + end + + # check that no thread got a NoMethodError because of partially loaded module + threads.all? {|t| t.value}.should be_true + + # check that the autoloaded file was evaled exactly once + ScratchPad.recorded.get.should == mod_count + + mod_names.each do |mod_name| + Object.send(:remove_const, mod_name) + end + end + end + end + + it "loads the registered constant even if the constant was already loaded by another thread" do + Thread.new { + ModuleSpecs::Autoload::FromThread::D.foo + }.value.should == :foo + end +end diff --git a/spec/rubyspec/core/module/case_compare_spec.rb b/spec/rubyspec/core/module/case_compare_spec.rb new file mode 100644 index 0000000000..92f2c2065b --- /dev/null +++ b/spec/rubyspec/core/module/case_compare_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#===" do + it "returns true when the given Object is an instance of self or of self's descendants" do + (ModuleSpecs::Child === ModuleSpecs::Child.new).should == true + (ModuleSpecs::Parent === ModuleSpecs::Parent.new).should == true + + (ModuleSpecs::Parent === ModuleSpecs::Child.new).should == true + (Object === ModuleSpecs::Child.new).should == true + + (ModuleSpecs::Child === String.new).should == false + (ModuleSpecs::Child === mock('x')).should == false + end + + it "returns true when the given Object's class includes self or when the given Object is extended by self" do + (ModuleSpecs::Basic === ModuleSpecs::Child.new).should == true + (ModuleSpecs::Super === ModuleSpecs::Child.new).should == true + (ModuleSpecs::Basic === mock('x').extend(ModuleSpecs::Super)).should == true + (ModuleSpecs::Super === mock('y').extend(ModuleSpecs::Super)).should == true + + (ModuleSpecs::Basic === ModuleSpecs::Parent.new).should == false + (ModuleSpecs::Super === ModuleSpecs::Parent.new).should == false + (ModuleSpecs::Basic === mock('z')).should == false + (ModuleSpecs::Super === mock('a')).should == false + end + + it "does not let a module singleton class interfere when its on the RHS" do + (Class === ModuleSpecs::CaseCompareOnSingleton).should == false + end +end diff --git a/spec/rubyspec/core/module/class_eval_spec.rb b/spec/rubyspec/core/module/class_eval_spec.rb new file mode 100644 index 0000000000..90deef9c2f --- /dev/null +++ b/spec/rubyspec/core/module/class_eval_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/class_eval', __FILE__) + +describe "Module#class_eval" do + it_behaves_like :module_class_eval, :class_eval +end diff --git a/spec/rubyspec/core/module/class_exec_spec.rb b/spec/rubyspec/core/module/class_exec_spec.rb new file mode 100644 index 0000000000..f9c12cfa48 --- /dev/null +++ b/spec/rubyspec/core/module/class_exec_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/class_exec', __FILE__) + +describe "Module#class_exec" do + it_behaves_like :module_class_exec, :class_exec +end diff --git a/spec/rubyspec/core/module/class_variable_defined_spec.rb b/spec/rubyspec/core/module/class_variable_defined_spec.rb new file mode 100644 index 0000000000..d47329455f --- /dev/null +++ b/spec/rubyspec/core/module/class_variable_defined_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#class_variable_defined?" do + it "returns true if a class variable with the given name is defined in self" do + c = Class.new { class_variable_set :@@class_var, "test" } + c.class_variable_defined?(:@@class_var).should == true + c.class_variable_defined?("@@class_var").should == true + c.class_variable_defined?(:@@no_class_var).should == false + c.class_variable_defined?("@@no_class_var").should == false + ModuleSpecs::CVars.class_variable_defined?("@@cls").should == true + end + + it "returns true if a class variable with the given name is defined in the metaclass" do + ModuleSpecs::CVars.class_variable_defined?("@@meta").should == true + end + + it "returns true if the class variable is defined in a metaclass" do + obj = mock("metaclass class variable") + meta = obj.singleton_class + meta.send :class_variable_set, :@@var, 1 + meta.send(:class_variable_defined?, :@@var).should be_true + end + + it "returns false if the class variable is not defined in a metaclass" do + obj = mock("metaclass class variable") + meta = obj.singleton_class + meta.class_variable_defined?(:@@var).should be_false + end + + it "returns true if a class variables with the given name is defined in an included module" do + c = Class.new { include ModuleSpecs::MVars } + c.class_variable_defined?("@@mvar").should == true + end + + it "returns false if a class variables with the given name is defined in an extended module" do + c = Class.new + c.extend ModuleSpecs::MVars + c.class_variable_defined?("@@mvar").should == false + end + + it "raises a NameError when the given name is not allowed" do + c = Class.new + + lambda { + c.class_variable_defined?(:invalid_name) + }.should raise_error(NameError) + + lambda { + c.class_variable_defined?("@invalid_name") + }.should raise_error(NameError) + end + + it "converts a non string/symbol/fixnum name to string using to_str" do + c = Class.new { class_variable_set :@@class_var, "test" } + (o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var") + c.class_variable_defined?(o).should == true + end + + it "raises a TypeError when the given names can't be converted to strings using to_str" do + c = Class.new { class_variable_set :@@class_var, "test" } + o = mock('123') + lambda { + c.class_variable_defined?(o) + }.should raise_error(TypeError) + + o.should_receive(:to_str).and_return(123) + lambda { + c.class_variable_defined?(o) + }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/class_variable_get_spec.rb b/spec/rubyspec/core/module/class_variable_get_spec.rb new file mode 100644 index 0000000000..7068801cc0 --- /dev/null +++ b/spec/rubyspec/core/module/class_variable_get_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#class_variable_get" do + it "returns the value of the class variable with the given name" do + c = Class.new { class_variable_set :@@class_var, "test" } + c.send(:class_variable_get, :@@class_var).should == "test" + c.send(:class_variable_get, "@@class_var").should == "test" + end + + it "returns the value of a class variable with the given name defined in an included module" do + c = Class.new { include ModuleSpecs::MVars } + c.send(:class_variable_get, "@@mvar").should == :mvar + end + + it "raises a NameError for a class variable named '@@'" do + c = Class.new + lambda { c.send(:class_variable_get, "@@") }.should raise_error(NameError) + lambda { c.send(:class_variable_get, :"@@") }.should raise_error(NameError) + end + + it "raises a NameError for a class variables with the given name defined in an extended module" do + c = Class.new + c.extend ModuleSpecs::MVars + lambda { + c.send(:class_variable_get, "@@mvar") + }.should raise_error(NameError) + end + + it "returns class variables defined in the class body and accessed in the metaclass" do + ModuleSpecs::CVars.cls.should == :class + end + + it "returns class variables defined in the metaclass and accessed by class methods" do + ModuleSpecs::CVars.meta.should == :metainfo + end + + it "returns class variables defined in the metaclass and accessed by instance methods" do + ModuleSpecs::CVars.new.meta.should == :metainfo + end + + it "returns a class variable defined in a metaclass" do + obj = mock("metaclass class variable") + meta = obj.singleton_class + meta.send :class_variable_set, :@@var, :cvar_value + meta.send(:class_variable_get, :@@var).should == :cvar_value + end + + it "raises a NameError when an uninitialized class variable is accessed" do + c = Class.new + [:@@no_class_var, "@@no_class_var"].each do |cvar| + lambda { c.send(:class_variable_get, cvar) }.should raise_error(NameError) + end + end + + it "raises a NameError when the given name is not allowed" do + c = Class.new + + lambda { c.send(:class_variable_get, :invalid_name) }.should raise_error(NameError) + lambda { c.send(:class_variable_get, "@invalid_name") }.should raise_error(NameError) + end + + it "converts a non string/symbol/fixnum name to string using to_str" do + c = Class.new { class_variable_set :@@class_var, "test" } + (o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var") + c.send(:class_variable_get, o).should == "test" + end + + it "raises a TypeError when the given names can't be converted to strings using to_str" do + c = Class.new { class_variable_set :@@class_var, "test" } + o = mock('123') + lambda { c.send(:class_variable_get, o) }.should raise_error(TypeError) + o.should_receive(:to_str).and_return(123) + lambda { c.send(:class_variable_get, o) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/class_variable_set_spec.rb b/spec/rubyspec/core/module/class_variable_set_spec.rb new file mode 100644 index 0000000000..6d36298f5f --- /dev/null +++ b/spec/rubyspec/core/module/class_variable_set_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#class_variable_set" do + it "sets the class variable with the given name to the given value" do + c = Class.new + + c.send(:class_variable_set, :@@test, "test") + c.send(:class_variable_set, "@@test3", "test3") + + c.send(:class_variable_get, :@@test).should == "test" + c.send(:class_variable_get, :@@test3).should == "test3" + end + + it "sets a class variable on a metaclass" do + obj = mock("metaclass class variable") + meta = obj.singleton_class + meta.send(:class_variable_set, :@@var, :cvar_value).should == :cvar_value + meta.send(:class_variable_get, :@@var).should == :cvar_value + end + + it "sets the value of a class variable with the given name defined in an included module" do + c = Class.new { include ModuleSpecs::MVars.dup } + c.send(:class_variable_set, "@@mvar", :new_mvar).should == :new_mvar + c.send(:class_variable_get, "@@mvar").should == :new_mvar + end + + it "raises a RuntimeError when self is frozen" do + lambda { + Class.new.freeze.send(:class_variable_set, :@@test, "test") + }.should raise_error(RuntimeError) + lambda { + Module.new.freeze.send(:class_variable_set, :@@test, "test") + }.should raise_error(RuntimeError) + end + + it "raises a NameError when the given name is not allowed" do + c = Class.new + + lambda { + c.send(:class_variable_set, :invalid_name, "test") + }.should raise_error(NameError) + lambda { + c.send(:class_variable_set, "@invalid_name", "test") + }.should raise_error(NameError) + end + + it "converts a non string/symbol/fixnum name to string using to_str" do + (o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var") + c = Class.new + c.send(:class_variable_set, o, "test") + c.send(:class_variable_get, :@@class_var).should == "test" + end + + it "raises a TypeError when the given names can't be converted to strings using to_str" do + c = Class.new { class_variable_set :@@class_var, "test" } + o = mock('123') + lambda { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError) + o.should_receive(:to_str).and_return(123) + lambda { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/class_variables_spec.rb b/spec/rubyspec/core/module/class_variables_spec.rb new file mode 100644 index 0000000000..066052cd01 --- /dev/null +++ b/spec/rubyspec/core/module/class_variables_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#class_variables" do + it "returns an Array with the names of class variables of self" do + ModuleSpecs::ClassVars::A.class_variables.should include(:@@a_cvar) + ModuleSpecs::ClassVars::M.class_variables.should include(:@@m_cvar) + end + + it "returns an Array of Symbols of class variable names defined in a metaclass" do + obj = mock("metaclass class variable") + meta = obj.singleton_class + meta.send :class_variable_set, :@@var, :cvar_value + meta.class_variables.should == [:@@var] + end + + it "returns an Array with names of class variables defined in metaclasses" do + ModuleSpecs::CVars.class_variables.should include(:@@cls, :@@meta) + end + + it "does not return class variables defined in extended modules" do + c = Class.new + c.extend ModuleSpecs::MVars + c.class_variables.should_not include(:@@mvar) + end +end diff --git a/spec/rubyspec/core/module/comparison_spec.rb b/spec/rubyspec/core/module/comparison_spec.rb new file mode 100644 index 0000000000..e7608d64b9 --- /dev/null +++ b/spec/rubyspec/core/module/comparison_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#<=>" do + it "returns -1 if self is a subclass of or includes the given module" do + (ModuleSpecs::Child <=> ModuleSpecs::Parent).should == -1 + (ModuleSpecs::Child <=> ModuleSpecs::Basic).should == -1 + (ModuleSpecs::Child <=> ModuleSpecs::Super).should == -1 + (ModuleSpecs::Super <=> ModuleSpecs::Basic).should == -1 + end + + it "returns 0 if self is the same as the given module" do + (ModuleSpecs::Child <=> ModuleSpecs::Child).should == 0 + (ModuleSpecs::Parent <=> ModuleSpecs::Parent).should == 0 + (ModuleSpecs::Basic <=> ModuleSpecs::Basic).should == 0 + (ModuleSpecs::Super <=> ModuleSpecs::Super).should == 0 + end + + it "returns +1 if self is a superclas of or included by the given module" do + (ModuleSpecs::Parent <=> ModuleSpecs::Child).should == +1 + (ModuleSpecs::Basic <=> ModuleSpecs::Child).should == +1 + (ModuleSpecs::Super <=> ModuleSpecs::Child).should == +1 + (ModuleSpecs::Basic <=> ModuleSpecs::Super).should == +1 + end + + it "returns nil if self and the given module are not related" do + (ModuleSpecs::Parent <=> ModuleSpecs::Basic).should == nil + (ModuleSpecs::Parent <=> ModuleSpecs::Super).should == nil + (ModuleSpecs::Basic <=> ModuleSpecs::Parent).should == nil + (ModuleSpecs::Super <=> ModuleSpecs::Parent).should == nil + end + + it "returns nil if the argument is not a class/module" do + (ModuleSpecs::Parent <=> mock('x')).should == nil + end +end diff --git a/spec/rubyspec/core/module/const_defined_spec.rb b/spec/rubyspec/core/module/const_defined_spec.rb new file mode 100644 index 0000000000..527b682414 --- /dev/null +++ b/spec/rubyspec/core/module/const_defined_spec.rb @@ -0,0 +1,144 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/constants', __FILE__) +require File.expand_path('../fixtures/constant_unicode', __FILE__) + +describe "Module#const_defined?" do + it "returns true if the given Symbol names a constant defined in the receiver" do + ConstantSpecs.const_defined?(:CS_CONST2).should == true + ConstantSpecs.const_defined?(:ModuleA).should == true + ConstantSpecs.const_defined?(:ClassA).should == true + ConstantSpecs::ContainerA.const_defined?(:ChildA).should == true + end + + it "returns true if the constant is defined in the reciever's superclass" do + # CS_CONST4 is defined in the superclass of ChildA + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4).should be_true + end + + it "returns true if the constant is defined in a mixed-in module of the reciever" do + # CS_CONST10 is defined in a module included by ChildA + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST10).should be_true + end + + it "returns true if the constant is defined in Object and the receiver is a module" do + # CS_CONST1 is defined in Object + ConstantSpecs::ModuleA.const_defined?(:CS_CONST1).should be_true + end + + it "returns true if the constant is defined in Object and the receiver is a class that has Object among its ancestors" do + # CS_CONST1 is defined in Object + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST1).should be_true + end + + it "returns false if the constant is defined in the receiver's superclass and the inherit flag is false" do + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, false).should be_false + end + + it "returns true if the constant is defined in the receiver's superclass and the inherit flag is true" do + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, true).should be_true + end + + it "returns true if the given String names a constant defined in the receiver" do + ConstantSpecs.const_defined?("CS_CONST2").should == true + ConstantSpecs.const_defined?("ModuleA").should == true + ConstantSpecs.const_defined?("ClassA").should == true + ConstantSpecs::ContainerA.const_defined?("ChildA").should == true + end + + it "returns true when passed a constant name with unicode characters" do + ConstantUnicodeSpecs.const_defined?("CS_CONSTλ").should be_true + end + + it "returns true when passed a constant name with EUC-JP characters" do + str = "CS_CONSTλ".encode("euc-jp") + ConstantSpecs.const_set str, 1 + ConstantSpecs.const_defined?(str).should be_true + end + + it "returns false if the constant is not defined in the receiver, its superclass, or any included modules" do + # The following constant isn't defined at all. + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4726).should be_false + # DETACHED_CONSTANT is defined in ConstantSpecs::Detached, which isn't + # included by or inherited from ParentA + ConstantSpecs::ParentA.const_defined?(:DETACHED_CONSTANT).should be_false + end + + it "does not call #const_missing if the constant is not defined in the receiver" do + ConstantSpecs::ClassA.should_not_receive(:const_missing) + ConstantSpecs::ClassA.const_defined?(:CS_CONSTX).should == false + end + + it "calls #to_str to convert the given name to a String" do + name = mock("ClassA") + name.should_receive(:to_str).and_return("ClassA") + ConstantSpecs.const_defined?(name).should == true + end + + it "special cases Object and checks it's included Modules" do + Object.const_defined?(:CS_CONST10).should be_true + end + + it "returns true for toplevel constant when the name begins with '::'" do + ConstantSpecs.const_defined?("::Array").should be_true + end + + it "returns true when passed a scoped constant name" do + ConstantSpecs.const_defined?("ClassC::CS_CONST1").should be_true + end + + it "returns true when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is default" do + ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2").should be_true + end + + it "returns true when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is true" do + ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", true).should be_true + end + + it "returns false when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is false" do + ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", false).should be_false + end + + it "returns false when the name begins with '::' and the toplevel constant does not exist" do + ConstantSpecs.const_defined?("::Name").should be_false + end + + it "raises a NameError if the name does not start with a capital letter" do + lambda { ConstantSpecs.const_defined? "name" }.should raise_error(NameError) + end + + it "raises a NameError if the name starts with '_'" do + lambda { ConstantSpecs.const_defined? "__CONSTX__" }.should raise_error(NameError) + end + + it "raises a NameError if the name starts with '@'" do + lambda { ConstantSpecs.const_defined? "@Name" }.should raise_error(NameError) + end + + it "raises a NameError if the name starts with '!'" do + lambda { ConstantSpecs.const_defined? "!Name" }.should raise_error(NameError) + end + + it "returns true or false for the nested name" do + ConstantSpecs.const_defined?("NotExist::Name").should == false + ConstantSpecs.const_defined?("::Name").should == false + ConstantSpecs.const_defined?("::Object").should == true + ConstantSpecs.const_defined?("ClassA::CS_CONST10").should == true + ConstantSpecs.const_defined?("ClassA::CS_CONST10_").should == false + end + + it "raises a NameError if the name contains non-alphabetic characters except '_'" do + ConstantSpecs.const_defined?("CS_CONSTX").should == false + lambda { ConstantSpecs.const_defined? "Name=" }.should raise_error(NameError) + lambda { ConstantSpecs.const_defined? "Name?" }.should raise_error(NameError) + end + + it "raises a TypeError if conversion to a String by calling #to_str fails" do + name = mock('123') + lambda { ConstantSpecs.const_defined? name }.should raise_error(TypeError) + + name.should_receive(:to_str).and_return(123) + lambda { ConstantSpecs.const_defined? name }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/const_get_spec.rb b/spec/rubyspec/core/module/const_get_spec.rb new file mode 100644 index 0000000000..6f8d5d930f --- /dev/null +++ b/spec/rubyspec/core/module/const_get_spec.rb @@ -0,0 +1,208 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/constants', __FILE__) + +describe "Module#const_get" do + it "accepts a String or Symbol name" do + Object.const_get(:CS_CONST1).should == :const1 + Object.const_get("CS_CONST1").should == :const1 + end + + it "raises a NameError if no constant is defined in the search path" do + lambda { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError) + end + + it "raises a NameError with the not found constant symbol" do + error_inspection = lambda { |e| e.name.should == :CS_CONSTX } + lambda { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError, &error_inspection) + end + + it "raises a NameError if the name does not start with a capital letter" do + lambda { ConstantSpecs.const_get "name" }.should raise_error(NameError) + end + + it "raises a NameError if the name starts with a non-alphabetic character" do + lambda { ConstantSpecs.const_get "__CONSTX__" }.should raise_error(NameError) + lambda { ConstantSpecs.const_get "@CS_CONST1" }.should raise_error(NameError) + lambda { ConstantSpecs.const_get "!CS_CONST1" }.should raise_error(NameError) + end + + it "raises a NameError if the name contains non-alphabetic characters except '_'" do + Object.const_get("CS_CONST1").should == :const1 + lambda { ConstantSpecs.const_get "CS_CONST1=" }.should raise_error(NameError) + lambda { ConstantSpecs.const_get "CS_CONST1?" }.should raise_error(NameError) + end + + it "calls #to_str to convert the given name to a String" do + name = mock("ClassA") + name.should_receive(:to_str).and_return("ClassA") + ConstantSpecs.const_get(name).should == ConstantSpecs::ClassA + end + + it "raises a TypeError if conversion to a String by calling #to_str fails" do + name = mock('123') + lambda { ConstantSpecs.const_get(name) }.should raise_error(TypeError) + + name.should_receive(:to_str).and_return(123) + lambda { ConstantSpecs.const_get(name) }.should raise_error(TypeError) + end + + it "calls #const_missing on the receiver if unable to locate the constant" do + ConstantSpecs::ContainerA.should_receive(:const_missing).with(:CS_CONSTX) + ConstantSpecs::ContainerA.const_get(:CS_CONSTX) + end + + it "does not search the singleton class of a Class or Module" do + lambda do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST14) + end.should raise_error(NameError) + lambda { ConstantSpecs.const_get(:CS_CONST14) }.should raise_error(NameError) + end + + it "does not search the containing scope" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST20).should == :const20_2 + lambda do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST5) + end.should raise_error(NameError) + end + + it "raises a NameError if the constant is defined in the receiver's supperclass and the inherit flag is false" do + lambda do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST4, false) + end.should raise_error(NameError) + end + + it "searches into the receiver superclasses if the inherit flag is true" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST4, true).should == :const4 + end + + it "raises a NameError when the receiver is a Module, the constant is defined at toplevel and the inherit flag is false" do + lambda do + ConstantSpecs::ModuleA.const_get(:CS_CONST1, false) + end.should raise_error(NameError) + end + + it "raises a NameError when the receiver is a Class, the constant is defined at toplevel and the inherit flag is false" do + lambda do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1, false) + end.should raise_error(NameError) + end + + it "accepts a toplevel scope qualifier" do + ConstantSpecs.const_get("::CS_CONST1").should == :const1 + end + + it "accepts a scoped constant name" do + ConstantSpecs.const_get("ClassA::CS_CONST10").should == :const10_10 + end + + it "raises a NameError if only '::' is passed" do + lambda { ConstantSpecs.const_get("::") }.should raise_error(NameError) + end + + it "raises a NameError if a Symbol has a toplevel scope qualifier" do + lambda { ConstantSpecs.const_get(:'::CS_CONST1') }.should raise_error(NameError) + end + + it "raises a NameError if a Symbol is a scoped constant name" do + lambda { ConstantSpecs.const_get(:'ClassA::CS_CONST10') }.should raise_error(NameError) + end + + it "does read private constants" do + ConstantSpecs.const_get(:CS_PRIVATE).should == :cs_private + end + + describe "with statically assigned constants" do + it "searches the immediate class or module first" do + ConstantSpecs::ClassA.const_get(:CS_CONST10).should == :const10_10 + ConstantSpecs::ModuleA.const_get(:CS_CONST10).should == :const10_1 + ConstantSpecs::ParentA.const_get(:CS_CONST10).should == :const10_5 + ConstantSpecs::ContainerA.const_get(:CS_CONST10).should == :const10_2 + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST10).should == :const10_3 + end + + it "searches a module included in the immediate class before the superclass" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST15).should == :const15_1 + end + + it "searches the superclass before a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST11).should == :const11_1 + end + + it "searches a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST12).should == :const12_1 + end + + it "searches the superclass chain" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST13).should == :const13 + end + + it "returns a toplevel constant when the receiver is a Class" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1).should == :const1 + end + + it "returns a toplevel constant when the receiver is a Module" do + ConstantSpecs.const_get(:CS_CONST1).should == :const1 + ConstantSpecs::ModuleA.const_get(:CS_CONST1).should == :const1 + end + end + + describe "with dynamically assigned constants" do + it "searches the immediate class or module first" do + ConstantSpecs::ClassA::CS_CONST301 = :const301_1 + ConstantSpecs::ClassA.const_get(:CS_CONST301).should == :const301_1 + + ConstantSpecs::ModuleA::CS_CONST301 = :const301_2 + ConstantSpecs::ModuleA.const_get(:CS_CONST301).should == :const301_2 + + ConstantSpecs::ParentA::CS_CONST301 = :const301_3 + ConstantSpecs::ParentA.const_get(:CS_CONST301).should == :const301_3 + + ConstantSpecs::ContainerA::ChildA::CS_CONST301 = :const301_5 + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST301).should == :const301_5 + end + + it "searches a module included in the immediate class before the superclass" do + ConstantSpecs::ParentB::CS_CONST302 = :const302_1 + ConstantSpecs::ModuleF::CS_CONST302 = :const302_2 + ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST302).should == :const302_2 + end + + it "searches the superclass before a module included in the superclass" do + ConstantSpecs::ModuleE::CS_CONST303 = :const303_1 + ConstantSpecs::ParentB::CS_CONST303 = :const303_2 + ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST303).should == :const303_2 + end + + it "searches a module included in the superclass" do + ConstantSpecs::ModuleA::CS_CONST304 = :const304_1 + ConstantSpecs::ModuleE::CS_CONST304 = :const304_2 + ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST304).should == :const304_2 + end + + it "searches the superclass chain" do + ConstantSpecs::ModuleA::CS_CONST305 = :const305 + ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST305).should == :const305 + end + + it "returns a toplevel constant when the receiver is a Class" do + Object::CS_CONST306 = :const306 + ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST306).should == :const306 + end + + it "returns a toplevel constant when the receiver is a Module" do + Object::CS_CONST308 = :const308 + ConstantSpecs.const_get(:CS_CONST308).should == :const308 + ConstantSpecs::ModuleA.const_get(:CS_CONST308).should == :const308 + end + + it "returns the updated value of a constant" do + ConstantSpecs::ClassB::CS_CONST309 = :const309_1 + ConstantSpecs::ClassB.const_get(:CS_CONST309).should == :const309_1 + + lambda { + ConstantSpecs::ClassB::CS_CONST309 = :const309_2 + }.should complain(/already initialized constant/) + ConstantSpecs::ClassB.const_get(:CS_CONST309).should == :const309_2 + end + end +end diff --git a/spec/rubyspec/core/module/const_missing_spec.rb b/spec/rubyspec/core/module/const_missing_spec.rb new file mode 100644 index 0000000000..24f2b49edc --- /dev/null +++ b/spec/rubyspec/core/module/const_missing_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/constants', __FILE__) + +describe "Module#const_missing" do + it "is called when an undefined constant is referenced via literal form" do + ConstantSpecs::ClassA::CS_CONSTX.should == :CS_CONSTX + end + + it "is called when an undefined constant is referenced via #const_get" do + ConstantSpecs::ClassA.const_get(:CS_CONSTX).should == :CS_CONSTX + end + + it "raises NameError and includes the name of the value that wasn't found" do + lambda { + ConstantSpecs.const_missing("HelloMissing") + }.should raise_error(NameError, /ConstantSpecs::HelloMissing/) + end + + it "raises NameError and does not include toplevel Object" do + begin + Object.const_missing("HelloMissing") + rescue NameError => e + e.message.should_not =~ / Object::/ + end + end + +end diff --git a/spec/rubyspec/core/module/const_set_spec.rb b/spec/rubyspec/core/module/const_set_spec.rb new file mode 100644 index 0000000000..6f4f6f980f --- /dev/null +++ b/spec/rubyspec/core/module/const_set_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/constants', __FILE__) + +describe "Module#const_set" do + it "sets the constant specified by a String or Symbol to the given value" do + ConstantSpecs.const_set :CS_CONST401, :const401 + ConstantSpecs::CS_CONST401.should == :const401 + + ConstantSpecs.const_set "CS_CONST402", :const402 + ConstantSpecs.const_get(:CS_CONST402).should == :const402 + end + + it "returns the value set" do + ConstantSpecs.const_set(:CS_CONST403, :const403).should == :const403 + end + + it "sets the name of an anonymous module" do + m = Module.new + ConstantSpecs.const_set(:CS_CONST1000, m) + m.name.should == "ConstantSpecs::CS_CONST1000" + end + + it "does not set the name of a module scoped by an anonymous module" do + a, b = Module.new, Module.new + a.const_set :B, b + b.name.should be_nil + end + + it "sets the name of contained modules when assigning a toplevel anonymous module" do + a, b, c, d = Module.new, Module.new, Module.new, Module.new + a::B = b + a::B::C = c + a::B::C::E = c + a::D = d + + Object.const_set :ModuleSpecs_CS3, a + a.name.should == "ModuleSpecs_CS3" + b.name.should == "ModuleSpecs_CS3::B" + c.name.should == "ModuleSpecs_CS3::B::C" + d.name.should == "ModuleSpecs_CS3::D" + end + + it "raises a NameError if the name does not start with a capital letter" do + lambda { ConstantSpecs.const_set "name", 1 }.should raise_error(NameError) + end + + it "raises a NameError if the name starts with a non-alphabetic character" do + lambda { ConstantSpecs.const_set "__CONSTX__", 1 }.should raise_error(NameError) + lambda { ConstantSpecs.const_set "@Name", 1 }.should raise_error(NameError) + lambda { ConstantSpecs.const_set "!Name", 1 }.should raise_error(NameError) + lambda { ConstantSpecs.const_set "::Name", 1 }.should raise_error(NameError) + end + + it "raises a NameError if the name contains non-alphabetic characters except '_'" do + ConstantSpecs.const_set("CS_CONST404", :const404).should == :const404 + lambda { ConstantSpecs.const_set "Name=", 1 }.should raise_error(NameError) + lambda { ConstantSpecs.const_set "Name?", 1 }.should raise_error(NameError) + end + + it "calls #to_str to convert the given name to a String" do + name = mock("CS_CONST405") + name.should_receive(:to_str).and_return("CS_CONST405") + ConstantSpecs.const_set(name, :const405).should == :const405 + ConstantSpecs::CS_CONST405.should == :const405 + end + + it "raises a TypeError if conversion to a String by calling #to_str fails" do + name = mock('123') + lambda { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError) + + name.should_receive(:to_str).and_return(123) + lambda { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError) + end + + describe "on a frozen module" do + before :each do + @frozen = Module.new.freeze + @name = :Foo + end + + it "raises a RuntimeError before setting the name" do + lambda { @frozen.const_set @name, nil }.should raise_error(RuntimeError) + @frozen.should_not have_constant(@name) + end + end +end diff --git a/spec/rubyspec/core/module/constants_spec.rb b/spec/rubyspec/core/module/constants_spec.rb new file mode 100644 index 0000000000..c331482bfb --- /dev/null +++ b/spec/rubyspec/core/module/constants_spec.rb @@ -0,0 +1,91 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/constants', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module.constants" do + it "returns an array of the names of all toplevel constants" do + count = Module.constants.size + module ConstantSpecsAdded + end + Module.constants.size.should == count + 1 + Object.send(:remove_const, :ConstantSpecsAdded) + end + + it "returns an array of Symbol names" do + # This in NOT an exhaustive list + Module.constants.should include(:Array, :Bignum, :Class, :Comparable, :Dir, + :Enumerable, :ENV, :Exception, :FalseClass, + :File, :Fixnum, :Float, :Hash, :Integer, :IO, + :Kernel, :Math, :Method, :Module, :NilClass, + :Numeric, :Object, :Range, :Regexp, :String, + :Symbol, :Thread, :Time, :TrueClass) + end + + it "returns Module's constants when given a parameter" do + direct = Module.constants(false) + indirect = Module.constants(true) + module ConstantSpecsIncludedModule + MODULE_CONSTANTS_SPECS_INDIRECT = :foo + end + + class Module + MODULE_CONSTANTS_SPECS_DIRECT = :bar + include ConstantSpecsIncludedModule + end + (Module.constants(false) - direct).should == [:MODULE_CONSTANTS_SPECS_DIRECT] + (Module.constants(true) - indirect).sort.should == [:MODULE_CONSTANTS_SPECS_DIRECT, :MODULE_CONSTANTS_SPECS_INDIRECT] + + Module.send(:remove_const, :MODULE_CONSTANTS_SPECS_DIRECT) + ConstantSpecsIncludedModule.send(:remove_const, :MODULE_CONSTANTS_SPECS_INDIRECT) + end +end + +describe "Module#constants" do + it "returns an array of Symbol names of all constants defined in the module and all included modules" do + ConstantSpecs::ContainerA.constants.sort.should == [ + :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA + ] + end + + it "returns all constants including inherited when passed true" do + ConstantSpecs::ContainerA.constants(true).sort.should == [ + :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA + ] + end + + it "returns all constants including inherited when passed some object" do + ConstantSpecs::ContainerA.constants(Object.new).sort.should == [ + :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA + ] + end + + it "doesn't returns inherited constants when passed false" do + ConstantSpecs::ContainerA.constants(false).sort.should == [ + :CS_CONST10, :CS_CONST23, :CS_CONST5, :ChildA + ] + end + + it "doesn't returns inherited constants when passed nil" do + ConstantSpecs::ContainerA.constants(nil).sort.should == [ + :CS_CONST10, :CS_CONST23, :CS_CONST5, :ChildA + ] + end + + it "returns only public constants" do + ModuleSpecs::PrivConstModule.constants.should == [:PUBLIC_CONSTANT] + end +end + +describe "Module#constants" do + before :each do + ConstantSpecs::ModuleM::CS_CONST251 = :const251 + end + + after :each do + ConstantSpecs::ModuleM.send(:remove_const, :CS_CONST251) + end + + it "includes names of constants defined after a module is included" do + ConstantSpecs::ContainerA.constants.should include(:CS_CONST251) + end +end diff --git a/spec/rubyspec/core/module/define_method_spec.rb b/spec/rubyspec/core/module/define_method_spec.rb new file mode 100644 index 0000000000..412a69a65a --- /dev/null +++ b/spec/rubyspec/core/module/define_method_spec.rb @@ -0,0 +1,626 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +class DefineMethodSpecClass +end + +describe "passed { |a, b = 1| } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { |a, b = 1| return a, b } + end + end + + it "raises an ArgumentError when passed zero arguments" do + lambda { @klass.new.m }.should raise_error(ArgumentError) + end + + it "has a default value for b when passed one argument" do + @klass.new.m(1).should == [1, 1] + end + + it "overrides the default argument when passed two arguments" do + @klass.new.m(1, 2).should == [1, 2] + end + + it "raises an ArgumentError when passed three arguments" do + lambda { @klass.new.m(1, 2, 3) }.should raise_error(ArgumentError) + end +end + +describe "Module#define_method when given an UnboundMethod" do + it "passes the given arguments to the new method" do + klass = Class.new do + def test_method(arg1, arg2) + [arg1, arg2] + end + define_method(:another_test_method, instance_method(:test_method)) + end + + klass.new.another_test_method(1, 2).should == [1, 2] + end + + it "adds the new method to the methods list" do + klass = Class.new do + def test_method(arg1, arg2) + [arg1, arg2] + end + define_method(:another_test_method, instance_method(:test_method)) + end + klass.new.should have_method(:another_test_method) + end + + describe "defining a method on a singleton class" do + before do + klass = Class.new + class << klass + def test_method + :foo + end + end + child = Class.new(klass) + sc = class << child; self; end + sc.send :define_method, :another_test_method, klass.method(:test_method).unbind + + @class = child + end + + it "doesn't raise TypeError when calling the method" do + @class.another_test_method.should == :foo + end + end + + it "sets the new method's visibility to the current frame's visibility" do + foo = Class.new do + def ziggy + 'piggy' + end + private :ziggy + + # make sure frame visibility is public + public + + define_method :piggy, instance_method(:ziggy) + end + + lambda { foo.new.ziggy }.should raise_error(NoMethodError) + foo.new.piggy.should == 'piggy' + end +end + +describe "Module#define_method when name is not a special private name" do + describe "given an UnboundMethod" do + describe "and called from the target module" do + it "sets the visibility of the method to the current visibility" do + klass = Class.new do + define_method(:bar, ModuleSpecs::EmptyFooMethod) + private + define_method(:baz, ModuleSpecs::EmptyFooMethod) + end + + klass.should have_public_instance_method(:bar) + klass.should have_private_instance_method(:baz) + end + end + + describe "and called from another module" do + it "sets the visibility of the method to public" do + klass = Class.new + Class.new do + klass.send(:define_method, :bar, ModuleSpecs::EmptyFooMethod) + private + klass.send(:define_method, :baz, ModuleSpecs::EmptyFooMethod) + end + + klass.should have_public_instance_method(:bar) + klass.should have_public_instance_method(:baz) + end + end + end + + describe "passed a block" do + describe "and called from the target module" do + it "sets the visibility of the method to the current visibility" do + klass = Class.new do + define_method(:bar) {} + private + define_method(:baz) {} + end + + klass.should have_public_instance_method(:bar) + klass.should have_private_instance_method(:baz) + end + end + + describe "and called from another module" do + it "sets the visibility of the method to public" do + klass = Class.new + Class.new do + klass.send(:define_method, :bar) {} + private + klass.send(:define_method, :baz) {} + end + + klass.should have_public_instance_method(:bar) + klass.should have_public_instance_method(:baz) + end + end + end +end + +describe "Module#define_method when name is :initialize" do + describe "passed a block" do + it "sets visibility to private when method name is :initialize" do + klass = Class.new do + define_method(:initialize) { } + end + klass.should have_private_instance_method(:initialize) + end + end + + describe "given an UnboundMethod" do + it "sets the visibility to private when method is named :initialize" do + klass = Class.new do + def test_method + end + define_method(:initialize, instance_method(:test_method)) + end + klass.should have_private_instance_method(:initialize) + end + end +end + +describe "Module#define_method" do + it "defines the given method as an instance method with the given name in self" do + class DefineMethodSpecClass + def test1 + "test" + end + define_method(:another_test, instance_method(:test1)) + end + + o = DefineMethodSpecClass.new + o.test1.should == o.another_test + end + + it "calls #method_added after the method is added to the Module" do + DefineMethodSpecClass.should_receive(:method_added).with(:test_ma) + + class DefineMethodSpecClass + define_method(:test_ma) { true } + end + end + + it "defines a new method with the given name and the given block as body in self" do + class DefineMethodSpecClass + define_method(:block_test1) { self } + define_method(:block_test2, &lambda { self }) + end + + o = DefineMethodSpecClass.new + o.block_test1.should == o + o.block_test2.should == o + end + + it "raises a TypeError when the given method is no Method/Proc" do + lambda { + Class.new { define_method(:test, "self") } + }.should raise_error(TypeError) + + lambda { + Class.new { define_method(:test, 1234) } + }.should raise_error(TypeError) + + lambda { + Class.new { define_method(:test, nil) } + }.should raise_error(TypeError) + end + + it "raises an ArgumentError when no block is given" do + lambda { + Class.new { define_method(:test) } + }.should raise_error(ArgumentError) + end + + ruby_version_is "2.3" do + it "does not use the caller block when no block is given" do + o = Object.new + def o.define(name) + self.class.class_eval do + define_method(name) + end + end + + lambda { + o.define(:foo) { raise "not used" } + }.should raise_error(ArgumentError) + end + end + + it "does not change the arity check style of the original proc" do + class DefineMethodSpecClass + prc = Proc.new { || true } + define_method("proc_style_test", &prc) + end + + obj = DefineMethodSpecClass.new + lambda { obj.proc_style_test :arg }.should raise_error(ArgumentError) + end + + it "raises a RuntimeError if frozen" do + lambda { + Class.new { freeze; define_method(:foo) {} } + }.should raise_error(RuntimeError) + end + + it "accepts a Method (still bound)" do + class DefineMethodSpecClass + attr_accessor :data + def inspect_data + "data is #{@data}" + end + end + o = DefineMethodSpecClass.new + o.data = :foo + m = o.method(:inspect_data) + m.should be_an_instance_of(Method) + klass = Class.new(DefineMethodSpecClass) + klass.send(:define_method,:other_inspect, m) + c = klass.new + c.data = :bar + c.other_inspect.should == "data is bar" + lambda{o.other_inspect}.should raise_error(NoMethodError) + end + + it "raises a TypeError when a Method from a singleton class is defined on another class" do + c = Class.new do + class << self + def foo + end + end + end + m = c.method(:foo) + + lambda { + Class.new { define_method :bar, m } + }.should raise_error(TypeError) + end + + it "raises a TypeError when a Method from one class is defined on an unrelated class" do + c = Class.new do + def foo + end + end + m = c.new.method(:foo) + + lambda { + Class.new { define_method :bar, m } + }.should raise_error(TypeError) + end + + it "accepts an UnboundMethod from an attr_accessor method" do + class DefineMethodSpecClass + attr_accessor :accessor_method + end + + m = DefineMethodSpecClass.instance_method(:accessor_method) + o = DefineMethodSpecClass.new + + DefineMethodSpecClass.send(:undef_method, :accessor_method) + lambda { o.accessor_method }.should raise_error(NoMethodError) + + DefineMethodSpecClass.send(:define_method, :accessor_method, m) + + o.accessor_method = :abc + o.accessor_method.should == :abc + end + + it "accepts a proc from a method" do + class ProcFromMethod + attr_accessor :data + def cool_method + "data is #{@data}" + end + end + + object1 = ProcFromMethod.new + object1.data = :foo + + method_proc = object1.method(:cool_method).to_proc + klass = Class.new(ProcFromMethod) + klass.send(:define_method, :other_cool_method, &method_proc) + + object2 = klass.new + object2.data = :bar + object2.other_cool_method.should == "data is foo" + end + + it "maintains the Proc's scope" do + class DefineMethodByProcClass + in_scope = true + method_proc = proc { in_scope } + + define_method(:proc_test, &method_proc) + end + + o = DefineMethodByProcClass.new + o.proc_test.should be_true + end + + it "accepts a String method name" do + klass = Class.new do + define_method("string_test") do + "string_test result" + end + end + + klass.new.string_test.should == "string_test result" + end + + it "is private" do + Module.should have_private_instance_method(:define_method) + end + + it "returns its symbol" do + class DefineMethodSpecClass + method = define_method("return_test") { true } + method.should == :return_test + end + end + + it "allows an UnboundMethod from a module to be defined on a class" do + klass = Class.new { + define_method :bar, ModuleSpecs::UnboundMethodTest.instance_method(:foo) + } + klass.new.should respond_to(:bar) + end + + it "allows an UnboundMethod from a parent class to be defined on a child class" do + parent = Class.new { define_method(:foo) { :bar } } + child = Class.new(parent) { + define_method :baz, parent.instance_method(:foo) + } + child.new.should respond_to(:baz) + end + + it "allows an UnboundMethod from a module to be defined on another unrelated module" do + mod = Module.new { + define_method :bar, ModuleSpecs::UnboundMethodTest.instance_method(:foo) + } + klass = Class.new { include mod } + klass.new.should respond_to(:bar) + end + + it "raises a TypeError when an UnboundMethod from a child class is defined on a parent class" do + lambda { + ParentClass = Class.new { define_method(:foo) { :bar } } + ChildClass = Class.new(ParentClass) { define_method(:foo) { :baz } } + ParentClass.send :define_method, :foo, ChildClass.instance_method(:foo) + }.should raise_error(TypeError) + end + + it "raises a TypeError when an UnboundMethod from one class is defined on an unrelated class" do + lambda { + DestinationClass = Class.new { + define_method :bar, ModuleSpecs::InstanceMeth.instance_method(:foo) + } + }.should raise_error(TypeError) + end +end + +describe "Module#define_method" do + describe "passed { } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { :called } + end + end + + it "returns the value computed by the block when passed zero arguments" do + @klass.new.m().should == :called + end + + it "raises an ArgumentError when passed one argument" do + lambda { @klass.new.m 1 }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed two arguments" do + lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError) + end + end + + describe "passed { || } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { || :called } + end + end + + it "returns the value computed by the block when passed zero arguments" do + @klass.new.m().should == :called + end + + it "raises an ArgumentError when passed one argument" do + lambda { @klass.new.m 1 }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed two arguments" do + lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError) + end + end + + describe "passed { |a| } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { |a| a } + end + end + + it "raises an ArgumentError when passed zero arguments" do + lambda { @klass.new.m }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed zero arguments and a block" do + lambda { @klass.new.m { :computed } }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed two arguments" do + lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError) + end + + it "receives the value passed as the argument when passed one argument" do + @klass.new.m(1).should == 1 + end + + end + + describe "passed { |*a| } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { |*a| a } + end + end + + it "receives an empty array as the argument when passed zero arguments" do + @klass.new.m().should == [] + end + + it "receives the value in an array when passed one argument" do + @klass.new.m(1).should == [1] + end + + it "receives the values in an array when passed two arguments" do + @klass.new.m(1, 2).should == [1, 2] + end + end + + describe "passed { |a, *b| } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { |a, *b| return a, b } + end + end + + it "raises an ArgumentError when passed zero arguments" do + lambda { @klass.new.m }.should raise_error(ArgumentError) + end + + it "returns the value computed by the block when passed one argument" do + @klass.new.m(1).should == [1, []] + end + + it "returns the value computed by the block when passed two arguments" do + @klass.new.m(1, 2).should == [1, [2]] + end + + it "returns the value computed by the block when passed three arguments" do + @klass.new.m(1, 2, 3).should == [1, [2, 3]] + end + end + + describe "passed { |a, b| } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { |a, b| return a, b } + end + end + + it "returns the value computed by the block when passed two arguments" do + @klass.new.m(1, 2).should == [1, 2] + end + + it "raises an ArgumentError when passed zero arguments" do + lambda { @klass.new.m }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed one argument" do + lambda { @klass.new.m 1 }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed one argument and a block" do + lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed three arguments" do + lambda { @klass.new.m 1, 2, 3 }.should raise_error(ArgumentError) + end + end + + describe "passed { |a, b, *c| } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { |a, b, *c| return a, b, c } + end + end + + it "raises an ArgumentError when passed zero arguments" do + lambda { @klass.new.m }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed one argument" do + lambda { @klass.new.m 1 }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed one argument and a block" do + lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError) + end + + it "receives an empty array as the third argument when passed two arguments" do + @klass.new.m(1, 2).should == [1, 2, []] + end + + it "receives the third argument in an array when passed three arguments" do + @klass.new.m(1, 2, 3).should == [1, 2, [3]] + end + end +end + +describe "Method#define_method when passed a Method object" do + before :each do + @klass = Class.new do + def m(a, b, *c) + :m + end + end + + @obj = @klass.new + m = @obj.method :m + + @klass.class_exec do + define_method :n, m + end + end + + it "defines a method with the same #arity as the original" do + @obj.method(:n).arity.should == @obj.method(:m).arity + end + + it "defines a method with the same #parameters as the original" do + @obj.method(:n).parameters.should == @obj.method(:m).parameters + end +end + +describe "Method#define_method when passed an UnboundMethod object" do + before :each do + @klass = Class.new do + def m(a, b, *c) + :m + end + end + + @obj = @klass.new + m = @klass.instance_method :m + + @klass.class_exec do + define_method :n, m + end + end + + it "defines a method with the same #arity as the original" do + @obj.method(:n).arity.should == @obj.method(:m).arity + end + + it "defines a method with the same #parameters as the original" do + @obj.method(:n).parameters.should == @obj.method(:m).parameters + end +end diff --git a/spec/rubyspec/core/module/define_singleton_method_spec.rb b/spec/rubyspec/core/module/define_singleton_method_spec.rb new file mode 100644 index 0000000000..0841d5d7e2 --- /dev/null +++ b/spec/rubyspec/core/module/define_singleton_method_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Module#define_singleton_method" do + it "defines the given method as an class method with the given name in self" do + klass = Module.new do + define_singleton_method :a do + 42 + end + define_singleton_method(:b, lambda {|x| 2*x }) + end + + klass.a.should == 42 + klass.b(10).should == 20 + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/module/deprecate_constant_spec.rb b/spec/rubyspec/core/module/deprecate_constant_spec.rb new file mode 100644 index 0000000000..8c7a170f1c --- /dev/null +++ b/spec/rubyspec/core/module/deprecate_constant_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.3" do + describe "Module#deprecate_constant" do + before :each do + @module = Module.new + @value = :value + @module::PUBLIC1 = @value + @module::PUBLIC2 = @value + @module::PRIVATE = @value + @module.private_constant :PRIVATE + @module.deprecate_constant :PRIVATE + @pattern = /deprecated/ + end + + describe "when accessing the deprecated module" do + it "passes the accessing" do + @module.deprecate_constant :PUBLIC1 + + value = nil + lambda { + value = @module::PUBLIC1 + }.should complain(@pattern) + value.should equal(@value) + + lambda { @module::PRIVATE }.should raise_error(NameError) + end + + it "warns with a message" do + @module.deprecate_constant :PUBLIC1 + + lambda { @module::PUBLIC1 }.should complain(@pattern) + lambda { @module.const_get :PRIVATE }.should complain(@pattern) + end + end + + it "accepts multiple symbols and strings as constant names" do + @module.deprecate_constant "PUBLIC1", :PUBLIC2 + + lambda { @module::PUBLIC1 }.should complain(@pattern) + lambda { @module::PUBLIC2 }.should complain(@pattern) + end + + it "returns self" do + @module.deprecate_constant(:PUBLIC1).should equal(@module) + end + + it "raises a NameError when given an undefined name" do + lambda { @module.deprecate_constant :UNDEFINED }.should raise_error(NameError) + end + end +end diff --git a/spec/rubyspec/core/module/eql_spec.rb b/spec/rubyspec/core/module/eql_spec.rb new file mode 100644 index 0000000000..f8878d7f4e --- /dev/null +++ b/spec/rubyspec/core/module/eql_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Module#eql?" do + it_behaves_like(:module_equal, :eql?) +end diff --git a/spec/rubyspec/core/module/equal_spec.rb b/spec/rubyspec/core/module/equal_spec.rb new file mode 100644 index 0000000000..bc70a6277a --- /dev/null +++ b/spec/rubyspec/core/module/equal_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Module#equal?" do + it_behaves_like(:module_equal, :equal?) +end diff --git a/spec/rubyspec/core/module/equal_value_spec.rb b/spec/rubyspec/core/module/equal_value_spec.rb new file mode 100644 index 0000000000..12494477f9 --- /dev/null +++ b/spec/rubyspec/core/module/equal_value_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Module#==" do + it_behaves_like(:module_equal, :==) +end diff --git a/spec/rubyspec/core/module/extend_object_spec.rb b/spec/rubyspec/core/module/extend_object_spec.rb new file mode 100644 index 0000000000..73c1623dce --- /dev/null +++ b/spec/rubyspec/core/module/extend_object_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#extend_object" do + before :each do + ScratchPad.clear + end + + it "is a private method" do + Module.should have_private_instance_method(:extend_object) + end + + describe "on Class" do + it "is undefined" do + Class.should_not have_private_instance_method(:extend_object, true) + end + + it "raises a TypeError if calling after rebinded to Class" do + lambda { + Module.instance_method(:extend_object).bind(Class.new).call Object.new + }.should raise_error(TypeError) + end + end + + it "is called when #extend is called on an object" do + ModuleSpecs::ExtendObject.should_receive(:extend_object) + obj = mock("extended object") + obj.extend ModuleSpecs::ExtendObject + end + + it "extends the given object with its constants and methods by default" do + obj = mock("extended direct") + ModuleSpecs::ExtendObject.send :extend_object, obj + + obj.test_method.should == "hello test" + obj.singleton_class.const_get(:C).should == :test + end + + it "is called even when private" do + obj = mock("extended private") + obj.extend ModuleSpecs::ExtendObjectPrivate + ScratchPad.recorded.should == :extended + end + + it "does not copy own tainted status to the given object" do + other = Object.new + Module.new.taint.send :extend_object, other + other.tainted?.should be_false + end + + it "does not copy own untrusted status to the given object" do + other = Object.new + Module.new.untrust.send :extend_object, other + other.untrusted?.should be_false + end + + describe "when given a frozen object" do + before :each do + @receiver = Module.new + @object = Object.new.freeze + end + + it "raises a RuntimeError before extending the object" do + lambda { @receiver.send(:extend_object, @object) }.should raise_error(RuntimeError) + @object.should_not be_kind_of(@receiver) + end + end +end diff --git a/spec/rubyspec/core/module/extended_spec.rb b/spec/rubyspec/core/module/extended_spec.rb new file mode 100644 index 0000000000..0a62dd9850 --- /dev/null +++ b/spec/rubyspec/core/module/extended_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#extended" do + it "is called when an object gets extended with self" do + begin + m = Module.new do + def self.extended(o) + $extended_object = o + end + end + + (o = mock('x')).extend(m) + + $extended_object.should == o + ensure + $extended_object = nil + end + end + + it "is called after Module#extend_object" do + begin + m = Module.new do + def self.extend_object(o) + $extended_object = nil + end + + def self.extended(o) + $extended_object = o + end + end + + (o = mock('x')).extend(m) + + $extended_object.should == o + ensure + $extended_object = nil + end + end + + it "is private in its default implementation" do + Module.new.private_methods.should include(:extended) + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload.rb b/spec/rubyspec/core/module/fixtures/autoload.rb new file mode 100644 index 0000000000..5a77a2d9d4 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload.rb @@ -0,0 +1 @@ +$m.const_set(:AAA, "test") unless $m.nil? diff --git a/spec/rubyspec/core/module/fixtures/autoload_abc.rb b/spec/rubyspec/core/module/fixtures/autoload_abc.rb new file mode 100644 index 0000000000..ffaec91cfe --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_abc.rb @@ -0,0 +1,11 @@ +module ModuleSpecs::Autoload::FromThread + module A + class B + class C + def self.foo + :foo + end + end + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_c.rb b/spec/rubyspec/core/module/fixtures/autoload_c.rb new file mode 100644 index 0000000000..ff2bcc548c --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_c.rb @@ -0,0 +1,11 @@ +module ModuleSpecs::Autoload + class DynClass + class C + def loaded + :dynclass_c + end + end + end +end + +ScratchPad.record :loaded diff --git a/spec/rubyspec/core/module/fixtures/autoload_concur.rb b/spec/rubyspec/core/module/fixtures/autoload_concur.rb new file mode 100644 index 0000000000..0585e36880 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_concur.rb @@ -0,0 +1,9 @@ +ScratchPad.recorded << :con_pre +Thread.current[:in_autoload_rb] = true +sleep 0.1 + +module ModuleSpecs::Autoload + Concur = 1 +end + +ScratchPad.recorded << :con_post diff --git a/spec/rubyspec/core/module/fixtures/autoload_d.rb b/spec/rubyspec/core/module/fixtures/autoload_d.rb new file mode 100644 index 0000000000..6f5eee741c --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_d.rb @@ -0,0 +1,11 @@ +module ModuleSpecs::Autoload + module DynModule + class D + def loaded + :dynmodule_d + end + end + end +end + +ScratchPad.record :loaded diff --git a/spec/rubyspec/core/module/fixtures/autoload_e.rb b/spec/rubyspec/core/module/fixtures/autoload_e.rb new file mode 100644 index 0000000000..fb78c6cbd4 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_e.rb @@ -0,0 +1,7 @@ +module ModuleSpecs::Autoload + class E + def loaded + :autoload_e + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_empty.rb b/spec/rubyspec/core/module/fixtures/autoload_empty.rb new file mode 100644 index 0000000000..d7116f3049 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_empty.rb @@ -0,0 +1 @@ +# This file is left empty on purpose diff --git a/spec/rubyspec/core/module/fixtures/autoload_ex1.rb b/spec/rubyspec/core/module/fixtures/autoload_ex1.rb new file mode 100644 index 0000000000..a90092389c --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_ex1.rb @@ -0,0 +1,16 @@ + +class ModuleSpecs::Autoload::EX1 < Exception + def self.trample1 + 1.times { return } + end + + def self.trample2 + begin + raise "hello" + rescue + end + end + + trample1 + trample2 +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_f.rb b/spec/rubyspec/core/module/fixtures/autoload_f.rb new file mode 100644 index 0000000000..54c2b05b7b --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_f.rb @@ -0,0 +1,7 @@ +module ModuleSpecs::Autoload + module F + def self.loaded + :autoload_f + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_g.rb b/spec/rubyspec/core/module/fixtures/autoload_g.rb new file mode 100644 index 0000000000..a1c4e429d9 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_g.rb @@ -0,0 +1,7 @@ +module ModuleSpecs::Autoload + class G + def loaded + :autoload_g + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_h.rb b/spec/rubyspec/core/module/fixtures/autoload_h.rb new file mode 100644 index 0000000000..53988c5382 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_h.rb @@ -0,0 +1,7 @@ +module ModuleSpecs::Autoload + module H + def loaded + :autoload_h + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_i.rb b/spec/rubyspec/core/module/fixtures/autoload_i.rb new file mode 100644 index 0000000000..f7f720516e --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_i.rb @@ -0,0 +1,5 @@ +module ModuleSpecs::Autoload + I = :autoloaded +end + +ScratchPad.record :loaded diff --git a/spec/rubyspec/core/module/fixtures/autoload_j.rb b/spec/rubyspec/core/module/fixtures/autoload_j.rb new file mode 100644 index 0000000000..da6d35d43d --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_j.rb @@ -0,0 +1,3 @@ +module ModuleSpecs::Autoload + J = :autoload_j +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_k.rb b/spec/rubyspec/core/module/fixtures/autoload_k.rb new file mode 100644 index 0000000000..431602bf80 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_k.rb @@ -0,0 +1,7 @@ +module ModuleSpecs::Autoload + class KHash < Hash + K = :autoload_k + end +end + +ScratchPad.record :loaded diff --git a/spec/rubyspec/core/module/fixtures/autoload_lm.rb b/spec/rubyspec/core/module/fixtures/autoload_lm.rb new file mode 100644 index 0000000000..d93d783cd0 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_lm.rb @@ -0,0 +1,4 @@ +module ModuleSpecs::Autoload + L = :autoload_l + M = :autoload_m +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_o.rb b/spec/rubyspec/core/module/fixtures/autoload_o.rb new file mode 100644 index 0000000000..6d54ddaf12 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_o.rb @@ -0,0 +1 @@ +# does not define ModuleSpecs::Autoload::O diff --git a/spec/rubyspec/core/module/fixtures/autoload_r.rb b/spec/rubyspec/core/module/fixtures/autoload_r.rb new file mode 100644 index 0000000000..34209d20c3 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_r.rb @@ -0,0 +1,4 @@ +module ModuleSpecs::Autoload + class R + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_s.rb b/spec/rubyspec/core/module/fixtures/autoload_s.rb new file mode 100644 index 0000000000..f5d412ff18 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_s.rb @@ -0,0 +1,5 @@ +module ModuleSpecs::Autoload + S = :autoload_s +end + +ScratchPad.record :loaded diff --git a/spec/rubyspec/core/module/fixtures/autoload_scope.rb b/spec/rubyspec/core/module/fixtures/autoload_scope.rb new file mode 100644 index 0000000000..04193687b5 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_scope.rb @@ -0,0 +1,8 @@ +module ModuleSpecs + module Autoload + class PP + class QQ + end + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_subclass.rb b/spec/rubyspec/core/module/fixtures/autoload_subclass.rb new file mode 100644 index 0000000000..569972118c --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_subclass.rb @@ -0,0 +1,11 @@ +class YY +end + +module ModuleSpecs + module Autoload + module XX + class YY < YY + end + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_t.rb b/spec/rubyspec/core/module/fixtures/autoload_t.rb new file mode 100644 index 0000000000..4962c97f4c --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_t.rb @@ -0,0 +1,3 @@ +module ModuleSpecs::Autoload::S + T = :autoload_t +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_v.rb b/spec/rubyspec/core/module/fixtures/autoload_v.rb new file mode 100644 index 0000000000..2aa8c44169 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_v.rb @@ -0,0 +1,7 @@ +module ModuleSpecs::Autoload::U + class V + def self.get_value + :autoload_uvx + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_w.rb b/spec/rubyspec/core/module/fixtures/autoload_w.rb new file mode 100644 index 0000000000..997273812e --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_w.rb @@ -0,0 +1,2 @@ +# Fails to define ModuleSpecs::Autoload::W::Y +ScratchPad.record :loaded diff --git a/spec/rubyspec/core/module/fixtures/autoload_w2.rb b/spec/rubyspec/core/module/fixtures/autoload_w2.rb new file mode 100644 index 0000000000..a8dbebf322 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_w2.rb @@ -0,0 +1 @@ +ScratchPad.record :loaded diff --git a/spec/rubyspec/core/module/fixtures/autoload_x.rb b/spec/rubyspec/core/module/fixtures/autoload_x.rb new file mode 100644 index 0000000000..a6c5842609 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_x.rb @@ -0,0 +1,3 @@ +module ModuleSpecs::Autoload + X = :x +end diff --git a/spec/rubyspec/core/module/fixtures/autoload_z.rb b/spec/rubyspec/core/module/fixtures/autoload_z.rb new file mode 100644 index 0000000000..cce1c13f37 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/autoload_z.rb @@ -0,0 +1,5 @@ +class ModuleSpecs::Autoload::YY +end + +class ModuleSpecs::Autoload::Z < ModuleSpecs::Autoload::YY +end diff --git a/spec/rubyspec/core/module/fixtures/classes.rb b/spec/rubyspec/core/module/fixtures/classes.rb new file mode 100644 index 0000000000..f93c39683e --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/classes.rb @@ -0,0 +1,605 @@ +module ModuleSpecs + def self.without_test_modules(modules) + ignore = %w[MSpecRSpecAdapter PP::ObjectMixin ModuleSpecs::IncludedInObject MainSpecs::Module ConstantSpecs::ModuleA] + modules.reject { |k| ignore.include?(k.name) } + end + + CONST = :plain_constant + + module PrivConstModule + PRIVATE_CONSTANT = 1 + private_constant :PRIVATE_CONSTANT + PUBLIC_CONSTANT = 2 + end + + class Subclass < Module + end + + class SubclassSpec + end + + class RemoveClassVariable + end + + module LookupModInMod + INCS = :ethereal + end + + module LookupMod + include LookupModInMod + + MODS = :rockers + end + + class Lookup + include LookupMod + LOOKIE = :lookie + end + + class LookupChild < Lookup + end + + class Parent + # For private_class_method spec + def self.private_method; end + private_class_method :private_method + + def undefed_method() end + undef_method :undefed_method + + def parent_method; end + def another_parent_method; end + + # For public_class_method spec + private + def self.public_method; end + public_class_method :public_method + + public + def public_parent() end + + protected + def protected_parent() end + + private + def private_parent() end + end + + module Basic + def public_module() end + + protected + def protected_module() end + + private + def private_module() end + end + + module Super + include Basic + + def public_super_module() end + + protected + def protected_super_module() end + + private + def private_super_module() end + + def super_included_method; end + + class SuperChild + end + end + + module Internal + end + + class Child < Parent + include Super + + class << self + include Internal + end + attr_accessor :accessor_method + + def public_child() end + + undef_method :parent_method + undef_method :another_parent_method + + protected + def protected_child() end + + private + def private_child() end + end + + class Grandchild < Child + undef_method :super_included_method + end + + class Child2 < Parent + attr_reader :foo + end + + # Be careful touching the Counts* classes as there used for testing + # private_instance_methods, public_instance_methods, etc. So adding, removing + # a method will break those tests. + module CountsMixin + def public_3; end + public :public_3 + + def private_3; end + private :private_3 + + def protected_3; end + protected :protected_3 + end + + class CountsParent + include CountsMixin + + def public_2; end + + private + def private_2; end + + protected + def protected_2; end + end + + class CountsChild < CountsParent + def public_1; end + + private + def private_1; end + + protected + def protected_1; end + end + + module AddConstant + end + + module A + CONSTANT_A = :a + OVERRIDE = :a + def ma(); :a; end + def self.cma(); :a; end + end + + module B + CONSTANT_B = :b + OVERRIDE = :b + include A + def mb(); :b; end + def self.cmb(); :b; end + end + + class C + OVERRIDE = :c + include B + end + + module Z + MODULE_SPEC_TOPLEVEL_CONSTANT = 1 + end + + module Alias + def report() :report end + alias publish report + end + + class Allonym + include ModuleSpecs::Alias + end + + class Aliasing + def self.make_alias(*a) + alias_method(*a) + end + + def public_one; 1; end + + def public_two(n); n * 2; end + + private + def private_one; 1; end + + protected + def protected_one; 1; end + end + + class AliasingSubclass < Aliasing + end + + module AliasingSuper + + module Parent + def super_call(arg) + arg + end + end + + module Child + include Parent + def super_call(arg) + super(arg) + end + end + + class Target + include Child + alias_method :alias_super_call, :super_call + alias_method :super_call, :alias_super_call + end + + class RedefineAfterAlias + include Parent + + def super_call(arg) + super(arg) + end + + alias_method :alias_super_call, :super_call + + def super_call(arg) + :wrong + end + end + end + + + module ReopeningModule + def foo; true; end + module_function :foo + private :foo + end + + # Yes, we want to re-open the module + module ReopeningModule + alias :foo2 :foo + module_function :foo2 + end + + module Nesting + @tests = {} + def self.[](name); @tests[name]; end + def self.[]=(name, val); @tests[name] = val; end + def self.meta; class << self; self; end; end + + Nesting[:basic] = Module.nesting + + module ::ModuleSpecs + Nesting[:open_first_level] = Module.nesting + end + + class << self + Nesting[:open_meta] = Module.nesting + end + + def self.called_from_module_method + Module.nesting + end + + class NestedClass + Nesting[:nest_class] = Module.nesting + + def self.called_from_class_method + Module.nesting + end + + def called_from_inst_method + Module.nesting + end + end + + end + + Nesting[:first_level] = Module.nesting + + module InstanceMethMod + def bar(); :bar; end + end + + class InstanceMeth + include InstanceMethMod + def foo(); :foo; end + end + + class InstanceMethChild < InstanceMeth + end + + module ClassVars + class A + @@a_cvar = :a_cvar + end + + module M + @@m_cvar = :m_cvar + end + + class B < A + include M + + @@b_cvar = :b_cvar + end + end + + class CVars + @@cls = :class + + # Singleton class lexical scopes are ignored for class variables + class << self + def cls + # This looks in the parent lexical scope, class CVars + @@cls + end + # This actually adds it to the parent lexical scope, class CVars + @@meta = :metainfo + end + + def self.meta + @@meta + end + + def meta + @@meta + end + end + + module MVars + @@mvar = :mvar + end + + class SubModule < Module + attr_reader :special + def initialize + @special = 10 + end + end + + module MA; end + module MB + include MA + end + module MC; end + + class MultipleIncludes + include MB + end + + # empty modules + module M1; end + module M2; end + + module Autoload + def self.use_ex1 + begin + begin + raise "test exception" + rescue ModuleSpecs::Autoload::EX1 + end + rescue RuntimeError + return :good + end + end + + module FromThread + module A + autoload :B, fixture(__FILE__, "autoload_empty.rb") + + class B + autoload :C, fixture(__FILE__, "autoload_abc.rb") + + def self.foo + C.foo + end + end + end + + class D < A::B; end + end + end + + # This class isn't inherited from or included in anywhere. + # It exists to test the constant scoping rules. + class Detached + DETACHED_CONSTANT = :d + end + + class ParentPrivateMethodRedef + private + def private_method_redefined + :before_redefinition + end + end + + class ChildPrivateMethodMadePublic < ParentPrivateMethodRedef + public :private_method_redefined + end + + class ParentPrivateMethodRedef + def private_method_redefined + :after_redefinition + end + end + + module CyclicAppendA + end + + module CyclicAppendB + include CyclicAppendA + end + + module CyclicPrepend + end + + module ExtendObject + C = :test + def test_method + "hello test" + end + end + + module ExtendObjectPrivate + class << self + def extend_object(obj) + ScratchPad.record :extended + end + private :extend_object + end + end + + class CyclicBarrier + def initialize(count = 1) + @count = count + @state = 0 + @mutex = Mutex.new + @cond = ConditionVariable.new + end + + def await + @mutex.synchronize do + @state += 1 + if @state >= @count + @state = 0 + @cond.broadcast + true + else + @cond.wait @mutex + false + end + end + end + + def enabled? + @mutex.synchronize { @count != -1 } + end + + def disable! + @mutex.synchronize do + @count = -1 + @cond.broadcast + end + end + end + + class ThreadSafeCounter + def initialize(value = 0) + @value = 0 + @mutex = Mutex.new + end + + def get + @mutex.synchronize { @value } + end + + def increment_and_get + @mutex.synchronize do + prev_value = @value + @value += 1 + prev_value + end + end + end + + module ShadowingOuter + module M + SHADOW = 123 + end + + module N + SHADOW = 456 + end + end + + module UnboundMethodTest + def foo + 'bar' + end + end + + module ClassEvalTest + def self.get_constant_from_scope + module_eval("Lookup") + end + + def self.get_constant_from_scope_with_send(method) + send(method, "Lookup") + end + end + + class RecordIncludedModules + def self.inherited(base) + ScratchPad.record base + end + end + + module SingletonOnModuleCase + module Foo + class << Foo + def included(base) + base.included_called + super + end + end + end + + class Bar + @included_called = false + + class << self + def included_called + @included_called = true + end + + def included_called? + @included_called + end + end + end + end + + module CaseCompareOnSingleton + def self.===(*) + raise 'method contents are irrelevant to test' + end + end + + m = Module.new do + def foo + end + private :foo + end + EmptyFooMethod = m.instance_method(:foo) +end + +class Object + def module_specs_public_method_on_object; end + + def module_specs_private_method_on_object; end + private :module_specs_private_method_on_object + + def module_specs_protected_method_on_object; end + protected :module_specs_private_method_on_object + + def module_specs_private_method_on_object_for_kernel_public; end + private :module_specs_private_method_on_object_for_kernel_public + + def module_specs_public_method_on_object_for_kernel_protected; end + def module_specs_public_method_on_object_for_kernel_private; end +end + +module Kernel + def module_specs_public_method_on_kernel; end + + alias_method :module_specs_alias_on_kernel, :module_specs_public_method_on_object + + public :module_specs_private_method_on_object_for_kernel_public + protected :module_specs_public_method_on_object_for_kernel_protected + private :module_specs_public_method_on_object_for_kernel_private +end + +ModuleSpecs::Nesting[:root_level] = Module.nesting diff --git a/spec/rubyspec/core/module/fixtures/constant_unicode.rb b/spec/rubyspec/core/module/fixtures/constant_unicode.rb new file mode 100644 index 0000000000..415911576d --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/constant_unicode.rb @@ -0,0 +1,5 @@ +# encoding: utf-8 + +module ConstantUnicodeSpecs + CS_CONSTλ = :const_unicode +end diff --git a/spec/rubyspec/core/module/fixtures/module.rb b/spec/rubyspec/core/module/fixtures/module.rb new file mode 100644 index 0000000000..9050a272ec --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/module.rb @@ -0,0 +1,4 @@ +module ModuleSpecs + module Anonymous + end +end diff --git a/spec/rubyspec/core/module/fixtures/name.rb b/spec/rubyspec/core/module/fixtures/name.rb new file mode 100644 index 0000000000..fb9e66c309 --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/name.rb @@ -0,0 +1,10 @@ +# -*- encoding: utf-8 -*- +module ModuleSpecs + class NameEncoding + class Cß + end + def name + Cß.name + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/path1/load_path.rb b/spec/rubyspec/core/module/fixtures/path1/load_path.rb new file mode 100644 index 0000000000..d4c45463dc --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/path1/load_path.rb @@ -0,0 +1,9 @@ +$LOAD_PATH.unshift(File.expand_path('../../path2', __FILE__)) + +module ModuleSpecs::Autoload + module LoadPath + def self.loaded + :autoload_load_path + end + end +end diff --git a/spec/rubyspec/core/module/fixtures/path2/load_path.rb b/spec/rubyspec/core/module/fixtures/path2/load_path.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/rubyspec/core/module/fixtures/repeated_concurrent_autoload.rb b/spec/rubyspec/core/module/fixtures/repeated_concurrent_autoload.rb new file mode 100644 index 0000000000..32b770e6cf --- /dev/null +++ b/spec/rubyspec/core/module/fixtures/repeated_concurrent_autoload.rb @@ -0,0 +1,8 @@ +prev_value = ScratchPad.recorded.increment_and_get +eval <<-RUBY_EVAL + module Mod#{prev_value} + sleep(0.05) + def self.foo + end + end +RUBY_EVAL diff --git a/spec/rubyspec/core/module/freeze_spec.rb b/spec/rubyspec/core/module/freeze_spec.rb new file mode 100644 index 0000000000..07a0ee0837 --- /dev/null +++ b/spec/rubyspec/core/module/freeze_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#freeze" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/module/gt_spec.rb b/spec/rubyspec/core/module/gt_spec.rb new file mode 100644 index 0000000000..73a6c80b69 --- /dev/null +++ b/spec/rubyspec/core/module/gt_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#>" do + it "returns false if self is a subclass of or includes the given module" do + (ModuleSpecs::Child > ModuleSpecs::Parent).should be_false + (ModuleSpecs::Child > ModuleSpecs::Basic).should be_false + (ModuleSpecs::Child > ModuleSpecs::Super).should be_false + (ModuleSpecs::Super > ModuleSpecs::Basic).should be_false + end + + it "returns true if self is a superclass of or included by the given module" do + (ModuleSpecs::Parent > ModuleSpecs::Child).should == true + (ModuleSpecs::Basic > ModuleSpecs::Child).should == true + (ModuleSpecs::Super > ModuleSpecs::Child).should == true + (ModuleSpecs::Basic > ModuleSpecs::Super).should == true + end + + it "returns false if self is the same as the given module" do + (ModuleSpecs::Child > ModuleSpecs::Child).should == false + (ModuleSpecs::Parent > ModuleSpecs::Parent).should == false + (ModuleSpecs::Basic > ModuleSpecs::Basic).should == false + (ModuleSpecs::Super > ModuleSpecs::Super).should == false + end + + it "returns nil if self is not related to the given module" do + (ModuleSpecs::Parent > ModuleSpecs::Basic).should == nil + (ModuleSpecs::Parent > ModuleSpecs::Super).should == nil + (ModuleSpecs::Basic > ModuleSpecs::Parent).should == nil + (ModuleSpecs::Super > ModuleSpecs::Parent).should == nil + end + + it "raises a TypeError if the argument is not a class/module" do + lambda { ModuleSpecs::Parent > mock('x') }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/gte_spec.rb b/spec/rubyspec/core/module/gte_spec.rb new file mode 100644 index 0000000000..84758e2df6 --- /dev/null +++ b/spec/rubyspec/core/module/gte_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#>=" do + it "returns true if self is a superclass of, the same as or included by given module" do + (ModuleSpecs::Parent >= ModuleSpecs::Child).should == true + (ModuleSpecs::Basic >= ModuleSpecs::Child).should == true + (ModuleSpecs::Super >= ModuleSpecs::Child).should == true + (ModuleSpecs::Basic >= ModuleSpecs::Super).should == true + (ModuleSpecs::Child >= ModuleSpecs::Child).should == true + (ModuleSpecs::Parent >= ModuleSpecs::Parent).should == true + (ModuleSpecs::Basic >= ModuleSpecs::Basic).should == true + (ModuleSpecs::Super >= ModuleSpecs::Super).should == true + end + + it "returns nil if self is not related to the given module" do + (ModuleSpecs::Parent >= ModuleSpecs::Basic).should == nil + (ModuleSpecs::Parent >= ModuleSpecs::Super).should == nil + (ModuleSpecs::Basic >= ModuleSpecs::Parent).should == nil + (ModuleSpecs::Super >= ModuleSpecs::Parent).should == nil + end + + it "returns false if self is a subclass of or includes the given module" do + (ModuleSpecs::Child >= ModuleSpecs::Parent).should == false + (ModuleSpecs::Child >= ModuleSpecs::Basic).should == false + (ModuleSpecs::Child >= ModuleSpecs::Super).should == false + (ModuleSpecs::Super >= ModuleSpecs::Basic).should == false + end + + it "raises a TypeError if the argument is not a class/module" do + lambda { ModuleSpecs::Parent >= mock('x') }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/include_spec.rb b/spec/rubyspec/core/module/include_spec.rb new file mode 100644 index 0000000000..e5d19fc820 --- /dev/null +++ b/spec/rubyspec/core/module/include_spec.rb @@ -0,0 +1,270 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#include" do + it "is a public method" do + Module.should have_public_instance_method(:include, false) + end + + it "calls #append_features(self) in reversed order on each module" do + $appended_modules = [] + + m = Module.new do + def self.append_features(mod) + $appended_modules << [ self, mod ] + end + end + + m2 = Module.new do + def self.append_features(mod) + $appended_modules << [ self, mod ] + end + end + + m3 = Module.new do + def self.append_features(mod) + $appended_modules << [ self, mod ] + end + end + + c = Class.new { include(m, m2, m3) } + + $appended_modules.should == [ [ m3, c], [ m2, c ], [ m, c ] ] + end + + it "adds all ancestor modules when a previously included module is included again" do + ModuleSpecs::MultipleIncludes.ancestors.should include(ModuleSpecs::MA, ModuleSpecs::MB) + ModuleSpecs::MB.include(ModuleSpecs::MC) + ModuleSpecs::MultipleIncludes.include(ModuleSpecs::MB) + ModuleSpecs::MultipleIncludes.ancestors.should include(ModuleSpecs::MA, ModuleSpecs::MB, ModuleSpecs::MC) + end + + it "raises a TypeError when the argument is not a Module" do + lambda { ModuleSpecs::Basic.include(Class.new) }.should raise_error(TypeError) + end + + it "does not raise a TypeError when the argument is an instance of a subclass of Module" do + lambda { ModuleSpecs::SubclassSpec.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) + end + + it "imports constants to modules and classes" do + ModuleSpecs::A.constants.should include(:CONSTANT_A) + ModuleSpecs::B.constants.should include(:CONSTANT_A, :CONSTANT_B) + ModuleSpecs::C.constants.should include(:CONSTANT_A, :CONSTANT_B) + end + + it "shadows constants from ancestors" do + klass = Class.new + klass.include ModuleSpecs::ShadowingOuter::M + klass::SHADOW.should == 123 + klass.include ModuleSpecs::ShadowingOuter::N + klass::SHADOW.should == 456 + end + + it "does not override existing constants in modules and classes" do + ModuleSpecs::A::OVERRIDE.should == :a + ModuleSpecs::B::OVERRIDE.should == :b + ModuleSpecs::C::OVERRIDE.should == :c + end + + it "imports instance methods to modules and classes" do + ModuleSpecs::A.instance_methods.should include(:ma) + ModuleSpecs::B.instance_methods.should include(:ma,:mb) + ModuleSpecs::C.instance_methods.should include(:ma,:mb) + end + + it "does not import methods to modules and classes" do + ModuleSpecs::A.methods.include?(:cma).should == true + ModuleSpecs::B.methods.include?(:cma).should == false + ModuleSpecs::B.methods.include?(:cmb).should == true + ModuleSpecs::C.methods.include?(:cma).should == false + ModuleSpecs::C.methods.include?(:cmb).should == false + end + + it "attaches the module as the caller's immediate ancestor" do + module IncludeSpecsTop + def value; 5; end + end + + module IncludeSpecsMiddle + include IncludeSpecsTop + def value; 6; end + end + + class IncludeSpecsClass + include IncludeSpecsMiddle + end + + IncludeSpecsClass.new.value.should == 6 + end + + it "doesn't include module if it is included in a super class" do + module ModuleSpecs::M1 + module M; end + class A; include M; end + class B < A; include M; end + + all = [A,B,M] + + (B.ancestors & all).should == [B, A, M] + end + end + + it "recursively includes new mixins" do + module ModuleSpecs::M1 + module U; end + module V; end + module W; end + module X; end + module Y; end + class A; include X; end; + class B < A; include U, V, W; end; + + # update V + module V; include X, U, Y; end + + # This code used to use Array#& and then compare 2 arrays, but + # the ordering from Array#& is undefined, as it uses Hash internally. + # + # Loop is more verbose, but more explicit in what we're testing. + + anc = B.ancestors + [B, U, V, W, A, X].each do |i| + anc.include?(i).should be_true + end + + class B; include V; end + + # the only new module is Y, it is added after U since it follows U in V mixin list: + anc = B.ancestors + [B, U, Y, V, W, A, X].each do |i| + anc.include?(i).should be_true + end + end + end + + it "preserves ancestor order" do + module ModuleSpecs::M2 + module M1; end + module M2; end + module M3; include M2; end + + module M2; include M1; end + module M3; include M2; end + + M3.ancestors.should == [M3, M2, M1] + + end + end + + it "detects cyclic includes" do + lambda { + module ModuleSpecs::M + include ModuleSpecs::M + end + }.should raise_error(ArgumentError) + end + + ruby_version_is ''...'2.4' do + it "accepts no-arguments" do + lambda { + Module.new do + include + end + }.should_not raise_error + end + end + + ruby_version_is '2.4' do + it "doesn't accept no-arguments" do + lambda { + Module.new do + include + end + }.should raise_error(ArgumentError) + end + end + + it "returns the class it's included into" do + m = Module.new + r = nil + c = Class.new { r = include m } + r.should == c + end + + it "ignores modules it has already included via module mutual inclusion" do + module ModuleSpecs::AlreadyInc + module M0 + end + + module M + include M0 + end + + class K + include M + include M + end + + K.ancestors[0].should == K + K.ancestors[1].should == M + K.ancestors[2].should == M0 + end + end + + it "clears any caches" do + module ModuleSpecs::M3 + module M1 + def foo + :m1 + end + end + + module M2 + def foo + :m2 + end + end + + class C + include M1 + + def get + foo + end + end + + c = C.new + c.get.should == :m1 + + class C + include M2 + end + + c.get.should == :m2 + + remove_const :C + end + end +end + +describe "Module#include?" do + it "returns true if the given module is included by self or one of it's ancestors" do + ModuleSpecs::Super.include?(ModuleSpecs::Basic).should == true + ModuleSpecs::Child.include?(ModuleSpecs::Basic).should == true + ModuleSpecs::Child.include?(ModuleSpecs::Super).should == true + ModuleSpecs::Child.include?(Kernel).should == true + + ModuleSpecs::Parent.include?(ModuleSpecs::Basic).should == false + ModuleSpecs::Basic.include?(ModuleSpecs::Super).should == false + end + + it "returns false if given module is equal to self" do + ModuleSpecs.include?(ModuleSpecs).should == false + end + + it "raises a TypeError when no module was given" do + lambda { ModuleSpecs::Child.include?("Test") }.should raise_error(TypeError) + lambda { ModuleSpecs::Child.include?(ModuleSpecs::Parent) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/included_modules_spec.rb b/spec/rubyspec/core/module/included_modules_spec.rb new file mode 100644 index 0000000000..91e1298eef --- /dev/null +++ b/spec/rubyspec/core/module/included_modules_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#included_modules" do + it "returns a list of modules included in self" do + ModuleSpecs.included_modules.should == [] + ModuleSpecs::Child.included_modules.should include(ModuleSpecs::Super, ModuleSpecs::Basic, Kernel) + ModuleSpecs::Parent.included_modules.should include(Kernel) + ModuleSpecs::Basic.included_modules.should == [] + ModuleSpecs::Super.included_modules.should include(ModuleSpecs::Basic) + end +end diff --git a/spec/rubyspec/core/module/included_spec.rb b/spec/rubyspec/core/module/included_spec.rb new file mode 100644 index 0000000000..39a08b6ed8 --- /dev/null +++ b/spec/rubyspec/core/module/included_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#included" do + it "is invoked when self is included in another module or class" do + begin + m = Module.new do + def self.included(o) + $included_by = o + end + end + + c = Class.new { include m } + + $included_by.should == c + ensure + $included_by = nil + end + end + + it "allows extending self with the object into which it is being included" do + m = Module.new do + def self.included(o) + o.extend(self) + end + + def test + :passed + end + end + + c = Class.new{ include(m) } + c.test.should == :passed + end + + it "is private in its default implementation" do + Module.should have_private_instance_method(:included) + end + + it "works with super using a singleton class" do + ModuleSpecs::SingletonOnModuleCase::Bar.include ModuleSpecs::SingletonOnModuleCase::Foo + ModuleSpecs::SingletonOnModuleCase::Bar.included_called?.should == true + end +end diff --git a/spec/rubyspec/core/module/initialize_copy_spec.rb b/spec/rubyspec/core/module/initialize_copy_spec.rb new file mode 100644 index 0000000000..85f1bbeb17 --- /dev/null +++ b/spec/rubyspec/core/module/initialize_copy_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Module#initialize_copy" do + it "should retain singleton methods when duped" do + mod = Module.new + def mod.hello + end + mod.dup.methods(false).should == [:hello] + end +end diff --git a/spec/rubyspec/core/module/initialize_spec.rb b/spec/rubyspec/core/module/initialize_spec.rb new file mode 100644 index 0000000000..b32e0f5d2a --- /dev/null +++ b/spec/rubyspec/core/module/initialize_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#initialize" do + it "accepts a block" do + m = Module.new do + const_set :A, "A" + end + m.const_get("A").should == "A" + end + + it "is called on subclasses" do + m = ModuleSpecs::SubModule.new + m.special.should == 10 + m.methods.should_not == nil + m.constants.should_not == nil + end +end diff --git a/spec/rubyspec/core/module/instance_method_spec.rb b/spec/rubyspec/core/module/instance_method_spec.rb new file mode 100644 index 0000000000..82e397b4bc --- /dev/null +++ b/spec/rubyspec/core/module/instance_method_spec.rb @@ -0,0 +1,85 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#instance_method" do + before :all do + @parent_um = ModuleSpecs::InstanceMeth.instance_method(:foo) + @child_um = ModuleSpecs::InstanceMethChild.instance_method(:foo) + @mod_um = ModuleSpecs::InstanceMethChild.instance_method(:bar) + end + + it "is a public method" do + Module.should have_public_instance_method(:instance_method, false) + end + + it "requires an argument" do + Module.new.method(:instance_method).arity.should == 1 + end + + it "returns an UnboundMethod corresponding to the given name" do + @parent_um.should be_kind_of(UnboundMethod) + @parent_um.bind(ModuleSpecs::InstanceMeth.new).call.should == :foo + end + + it "returns an UnboundMethod corresponding to the given name from a superclass" do + @child_um.should be_kind_of(UnboundMethod) + @child_um.bind(ModuleSpecs::InstanceMethChild.new).call.should == :foo + end + + it "returns an UnboundMethod corresponding to the given name from an included Module" do + @mod_um.should be_kind_of(UnboundMethod) + @mod_um.bind(ModuleSpecs::InstanceMethChild.new).call.should == :bar + end + + it "returns an UnboundMethod when given a protected method name" do + ModuleSpecs::Basic.instance_method(:protected_module).should be_an_instance_of(UnboundMethod) + end + + it "returns an UnboundMethod when given a private method name" do + ModuleSpecs::Basic.instance_method(:private_module).should be_an_instance_of(UnboundMethod) + end + + it "gives UnboundMethod method name, Module defined in and Module extracted from" do + @parent_um.inspect.should =~ /\bfoo\b/ + @parent_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/ + @parent_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/ + @child_um.inspect.should =~ /\bfoo\b/ + @child_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/ + @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/ + @mod_um.inspect.should =~ /\bbar\b/ + @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethMod\b/ + @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/ + end + + it "raises a TypeError if not passed a symbol" do + lambda { Object.instance_method([]) }.should raise_error(TypeError) + lambda { Object.instance_method(0) }.should raise_error(TypeError) + end + + it "raises a TypeError if the given name is not a string/symbol" do + lambda { Object.instance_method(nil) }.should raise_error(TypeError) + lambda { Object.instance_method(mock('x')) }.should raise_error(TypeError) + end + + it "raises a NameError if the method has been undefined" do + child = Class.new(ModuleSpecs::InstanceMeth) + child.send :undef_method, :foo + um = ModuleSpecs::InstanceMeth.instance_method(:foo) + um.should == @parent_um + lambda do + child.instance_method(:foo) + end.should raise_error(NameError) + end + + it "raises a NameError if the method does not exist" do + lambda { Object.instance_method(:missing) }.should raise_error(NameError) + end + + it "sets the NameError#name attribute to the name of the missing method" do + begin + Object.instance_method(:missing) + rescue NameError => e + e.name.should == :missing + end + end +end diff --git a/spec/rubyspec/core/module/instance_methods_spec.rb b/spec/rubyspec/core/module/instance_methods_spec.rb new file mode 100644 index 0000000000..289879cdbc --- /dev/null +++ b/spec/rubyspec/core/module/instance_methods_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#instance_methods" do + it "does not return methods undefined in a superclass" do + methods = ModuleSpecs::Parent.instance_methods(false) + methods.should_not include(:undefed_method) + end + + it "only includes module methods on an included module" do + methods = ModuleSpecs::Basic.instance_methods(false) + methods.should include(:public_module) + # Child is an including class + methods = ModuleSpecs::Child.instance_methods(false) + methods.should include(:public_child) + methods.should_not include(:public_module) + end + + it "does not return methods undefined in a subclass" do + methods = ModuleSpecs::Grandchild.instance_methods + methods.should_not include(:parent_method, :another_parent_method) + end + + it "does not return methods undefined in the current class" do + class ModuleSpecs::Child + def undefed_child + end + end + ModuleSpecs::Child.send(:undef_method, :undefed_child) + methods = ModuleSpecs::Child.instance_methods + methods.should_not include(:undefed_method, :undefed_child) + end + + it "does not return methods from an included module that are undefined in the class" do + ModuleSpecs::Grandchild.instance_methods.should_not include(:super_included_method) + end + + it "returns the public and protected methods of self if include_super is false" do + methods = ModuleSpecs::Parent.instance_methods(false) + methods.should include(:protected_parent, :public_parent) + + methods = ModuleSpecs::Child.instance_methods(false) + methods.should include(:protected_child, :public_child) + end + + it "returns the public and protected methods of self and it's ancestors" do + methods = ModuleSpecs::Basic.instance_methods + methods.should include(:protected_module, :public_module) + + methods = ModuleSpecs::Super.instance_methods + methods.should include(:protected_module, :protected_super_module, + :public_module, :public_super_module) + end + + it "makes a private Object instance method public in Kernel" do + methods = Kernel.instance_methods + methods.should include(:module_specs_private_method_on_object_for_kernel_public) + methods = Object.instance_methods + methods.should_not include(:module_specs_private_method_on_object_for_kernel_public) + end +end diff --git a/spec/rubyspec/core/module/lt_spec.rb b/spec/rubyspec/core/module/lt_spec.rb new file mode 100644 index 0000000000..ce0d25b5a2 --- /dev/null +++ b/spec/rubyspec/core/module/lt_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#<" do + it "returns true if self is a subclass of or includes the given module" do + (ModuleSpecs::Child < ModuleSpecs::Parent).should == true + (ModuleSpecs::Child < ModuleSpecs::Basic).should == true + (ModuleSpecs::Child < ModuleSpecs::Super).should == true + (ModuleSpecs::Super < ModuleSpecs::Basic).should == true + end + + it "returns false if self is a superclass of or included by the given module" do + (ModuleSpecs::Parent < ModuleSpecs::Child).should be_false + (ModuleSpecs::Basic < ModuleSpecs::Child).should be_false + (ModuleSpecs::Super < ModuleSpecs::Child).should be_false + (ModuleSpecs::Basic < ModuleSpecs::Super).should be_false + end + + it "returns false if self is the same as the given module" do + (ModuleSpecs::Child < ModuleSpecs::Child).should == false + (ModuleSpecs::Parent < ModuleSpecs::Parent).should == false + (ModuleSpecs::Basic < ModuleSpecs::Basic).should == false + (ModuleSpecs::Super < ModuleSpecs::Super).should == false + end + + it "returns nil if self is not related to the given module" do + (ModuleSpecs::Parent < ModuleSpecs::Basic).should == nil + (ModuleSpecs::Parent < ModuleSpecs::Super).should == nil + (ModuleSpecs::Basic < ModuleSpecs::Parent).should == nil + (ModuleSpecs::Super < ModuleSpecs::Parent).should == nil + end + + it "raises a TypeError if the argument is not a class/module" do + lambda { ModuleSpecs::Parent < mock('x') }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/lte_spec.rb b/spec/rubyspec/core/module/lte_spec.rb new file mode 100644 index 0000000000..8a699b4714 --- /dev/null +++ b/spec/rubyspec/core/module/lte_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#<=" do + it "returns true if self is a subclass of, the same as or includes the given module" do + (ModuleSpecs::Child <= ModuleSpecs::Parent).should == true + (ModuleSpecs::Child <= ModuleSpecs::Basic).should == true + (ModuleSpecs::Child <= ModuleSpecs::Super).should == true + (ModuleSpecs::Super <= ModuleSpecs::Basic).should == true + (ModuleSpecs::Child <= ModuleSpecs::Child).should == true + (ModuleSpecs::Parent <= ModuleSpecs::Parent).should == true + (ModuleSpecs::Basic <= ModuleSpecs::Basic).should == true + (ModuleSpecs::Super <= ModuleSpecs::Super).should == true + end + + it "returns nil if self is not related to the given module" do + (ModuleSpecs::Parent <= ModuleSpecs::Basic).should == nil + (ModuleSpecs::Parent <= ModuleSpecs::Super).should == nil + (ModuleSpecs::Basic <= ModuleSpecs::Parent).should == nil + (ModuleSpecs::Super <= ModuleSpecs::Parent).should == nil + end + + it "returns false if self is a superclass of or is included by the given module" do + (ModuleSpecs::Parent <= ModuleSpecs::Child).should == false + (ModuleSpecs::Basic <= ModuleSpecs::Child).should == false + (ModuleSpecs::Super <= ModuleSpecs::Child).should == false + (ModuleSpecs::Basic <= ModuleSpecs::Super).should == false + end + + it "raises a TypeError if the argument is not a class/module" do + lambda { ModuleSpecs::Parent <= mock('x') }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/module/method_added_spec.rb b/spec/rubyspec/core/module/method_added_spec.rb new file mode 100644 index 0000000000..643291be17 --- /dev/null +++ b/spec/rubyspec/core/module/method_added_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#method_added" do + it "is a private instance method" do + Module.should have_private_instance_method(:method_added) + end + + it "returns nil in the default implementation" do + Module.new do + method_added(:test).should == nil + end + end + + it "is called when a new instance method is defined in self" do + ScratchPad.record [] + + Module.new do + def self.method_added(name) + ScratchPad << name + end + + def test() end + def test2() end + def test() end + alias_method :aliased_test, :test + alias aliased_test2 test + end + + ScratchPad.recorded.should == [:test, :test2, :test, :aliased_test, :aliased_test2] + end + + it "is not called when a singleton method is added" do + # obj.singleton_method_added is called instead + ScratchPad.record [] + + klass = Class.new + def klass.method_added(name) + ScratchPad << name + end + + obj = klass.new + def obj.new_singleton_method + end + + ScratchPad.recorded.should == [] + end + + it "is not called when a method is undefined in self" do + m = Module.new do + def method_to_undef + end + + def self.method_added(name) + fail("method_added called by undef_method") + end + + undef_method :method_to_undef + end + m.should_not have_method(:method_to_undef) + end +end diff --git a/spec/rubyspec/core/module/method_defined_spec.rb b/spec/rubyspec/core/module/method_defined_spec.rb new file mode 100644 index 0000000000..d6206fa57e --- /dev/null +++ b/spec/rubyspec/core/module/method_defined_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#method_defined?" do + it "returns true if a public or private method with the given name is defined in self, self's ancestors or one of self's included modules" do + # Defined in Child + ModuleSpecs::Child.method_defined?(:public_child).should == true + ModuleSpecs::Child.method_defined?("private_child").should == false + ModuleSpecs::Child.method_defined?(:accessor_method).should == true + + # Defined in Parent + ModuleSpecs::Child.method_defined?("public_parent").should == true + ModuleSpecs::Child.method_defined?(:private_parent).should == false + + # Defined in Module + ModuleSpecs::Child.method_defined?(:public_module).should == true + ModuleSpecs::Child.method_defined?(:protected_module).should == true + ModuleSpecs::Child.method_defined?(:private_module).should == false + + # Defined in SuperModule + ModuleSpecs::Child.method_defined?(:public_super_module).should == true + ModuleSpecs::Child.method_defined?(:protected_super_module).should == true + ModuleSpecs::Child.method_defined?(:private_super_module).should == false + end + + # unlike alias_method, module_function, public, and friends, + it "does not search Object or Kernel when called on a module" do + m = Module.new + + m.method_defined?(:module_specs_public_method_on_kernel).should be_false + end + + it "raises a TypeError when the given object is not a string/symbol/fixnum" do + c = Class.new + o = mock('123') + + lambda { c.method_defined?(o) }.should raise_error(TypeError) + + o.should_receive(:to_str).and_return(123) + lambda { c.method_defined?(o) }.should raise_error(TypeError) + end + + it "converts the given name to a string using to_str" do + c = Class.new { def test(); end } + (o = mock('test')).should_receive(:to_str).and_return("test") + + c.method_defined?(o).should == true + end +end diff --git a/spec/rubyspec/core/module/method_removed_spec.rb b/spec/rubyspec/core/module/method_removed_spec.rb new file mode 100644 index 0000000000..4c1443cfaa --- /dev/null +++ b/spec/rubyspec/core/module/method_removed_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#method_removed" do + it "is a private instance method" do + Module.should have_private_instance_method(:method_removed) + end + + it "returns nil in the default implementation" do + Module.new do + method_removed(:test).should == nil + end + end + + it "is called when a method is removed from self" do + begin + Module.new do + def self.method_removed(name) + $method_removed = name + end + + def test + "test" + end + remove_method :test + end + + $method_removed.should == :test + ensure + $method_removed = nil + end + end +end diff --git a/spec/rubyspec/core/module/method_undefined_spec.rb b/spec/rubyspec/core/module/method_undefined_spec.rb new file mode 100644 index 0000000000..65001b9421 --- /dev/null +++ b/spec/rubyspec/core/module/method_undefined_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#method_undefined" do + it "is a private instance method" do + Module.should have_private_instance_method(:method_undefined) + end + + it "returns nil in the default implementation" do + Module.new do + method_undefined(:test).should == nil + end + end + + it "is called when a method is undefined from self" do + begin + Module.new do + def self.method_undefined(name) + $method_undefined = name + end + + def test + "test" + end + undef_method :test + end + + $method_undefined.should == :test + ensure + $method_undefined = nil + end + end +end diff --git a/spec/rubyspec/core/module/module_eval_spec.rb b/spec/rubyspec/core/module/module_eval_spec.rb new file mode 100644 index 0000000000..2bef0e9abf --- /dev/null +++ b/spec/rubyspec/core/module/module_eval_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/class_eval', __FILE__) + +describe "Module#module_eval" do + it_behaves_like :module_class_eval, :module_eval +end diff --git a/spec/rubyspec/core/module/module_exec_spec.rb b/spec/rubyspec/core/module/module_exec_spec.rb new file mode 100644 index 0000000000..77d74a083b --- /dev/null +++ b/spec/rubyspec/core/module/module_exec_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/class_exec', __FILE__) + +describe "Module#module_exec" do + it_behaves_like :module_class_exec, :module_exec +end diff --git a/spec/rubyspec/core/module/module_function_spec.rb b/spec/rubyspec/core/module/module_function_spec.rb new file mode 100644 index 0000000000..8ced48c505 --- /dev/null +++ b/spec/rubyspec/core/module/module_function_spec.rb @@ -0,0 +1,277 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#module_function" do + it "is a private method" do + Module.should have_private_instance_method(:module_function) + end + + describe "on Class" do + it "is undefined" do + Class.should_not have_private_instance_method(:module_function, true) + end + + it "raises a TypeError if calling after rebinded to Class" do + lambda { + Module.instance_method(:module_function).bind(Class.new).call + }.should raise_error(TypeError) + + lambda { + Module.instance_method(:module_function).bind(Class.new).call :foo + }.should raise_error(TypeError) + end + end +end + +describe "Module#module_function with specific method names" do + it "creates duplicates of the given instance methods on the Module object" do + m = Module.new do + def test() end + def test2() end + def test3() end + + module_function :test, :test2 + end + + m.respond_to?(:test).should == true + m.respond_to?(:test2).should == true + m.respond_to?(:test3).should == false + end + + it "returns the current module" do + x = nil + m = Module.new do + def test() end + x = module_function :test + end + + x.should == m + end + + it "creates an independent copy of the method, not a redirect" do + module Mixin + def test + "hello" + end + module_function :test + end + + class BaseClass + include Mixin + def call_test + test + end + end + + Mixin.test.should == "hello" + c = BaseClass.new + c.call_test.should == "hello" + + module Mixin + def test + "goodbye" + end + end + + Mixin.test.should == "hello" + c.call_test.should == "goodbye" + end + + it "makes the instance methods private" do + m = Module.new do + def test() "hello" end + module_function :test + end + + (o = mock('x')).extend(m) + o.respond_to?(:test).should == false + m.should have_private_instance_method(:test) + o.send(:test).should == "hello" + lambda { o.test }.should raise_error(NoMethodError) + end + + it "makes the new Module methods public" do + m = Module.new do + def test() "hello" end + module_function :test + end + + m.public_methods.map {|me| me.to_s }.include?('test').should == true + end + + it "tries to convert the given names to strings using to_str" do + (o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test") + (o2 = mock('test2')).should_receive(:to_str).any_number_of_times.and_return("test2") + + m = Module.new do + def test() end + def test2() end + module_function o, o2 + end + + m.respond_to?(:test).should == true + m.respond_to?(:test2).should == true + end + + it "raises a TypeError when the given names can't be converted to string using to_str" do + o = mock('123') + + lambda { Module.new { module_function(o) } }.should raise_error(TypeError) + + o.should_receive(:to_str).and_return(123) + lambda { Module.new { module_function(o) } }.should raise_error(TypeError) + end + + it "can make accessible private methods" do # JRUBY-4214 + m = Module.new do + module_function :require + end + m.respond_to?(:require).should be_true + end + + it "creates Module methods that super up the singleton class of the module" do + super_m = Module.new do + def foo + "super_m" + end + end + + m = Module.new do + extend super_m + module_function + def foo + ["m", super] + end + end + + m.foo.should == ["m", "super_m"] + end +end + +describe "Module#module_function as a toggle (no arguments) in a Module body" do + it "makes any subsequently defined methods module functions with the normal semantics" do + m = Module.new { + module_function + def test1() end + def test2() end + } + + m.respond_to?(:test1).should == true + m.respond_to?(:test2).should == true + end + + it "returns the current module" do + x = nil + m = Module.new { + x = module_function + } + + x.should == m + end + + it "stops creating module functions if the body encounters another toggle " \ + "like public/protected/private without arguments" do + m = Module.new { + module_function + def test1() end + def test2() end + public + def test3() end + } + + m.respond_to?(:test1).should == true + m.respond_to?(:test2).should == true + m.respond_to?(:test3).should == false + end + + it "does not stop creating module functions if the body encounters " \ + "public/protected/private WITH arguments" do + m = Module.new { + def foo() end + + module_function + def test1() end + def test2() end + + public :foo + + def test3() end + } + + m.respond_to?(:test1).should == true + m.respond_to?(:test2).should == true + m.respond_to?(:test3).should == true + end + + it "does not affect module_evaled method definitions also if outside the eval itself" do + m = Module.new { + module_function + + module_eval { def test1() end } + module_eval " def test2() end " + } + + m.respond_to?(:test1).should == false + m.respond_to?(:test2).should == false + end + + it "has no effect if inside a module_eval if the definitions are outside of it" do + m = Module.new { + module_eval { module_function } + + def test1() end + def test2() end + } + + m.respond_to?(:test1).should == false + m.respond_to?(:test2).should == false + end + + it "functions normally if both toggle and definitions inside a module_eval" do + m = Module.new { + module_eval { + module_function + + def test1() end + def test2() end + } + } + + m.respond_to?(:test1).should == true + m.respond_to?(:test2).should == true + end + + it "affects evaled method definitions also even when outside the eval itself" do + m = Module.new { + module_function + + eval "def test1() end" + } + + m.respond_to?(:test1).should == true + end + + it "doesn't affect definitions when inside an eval even if the definitions are outside of it" do + m = Module.new { + eval "module_function" + + def test1() end + } + + m.respond_to?(:test1).should == false + end + + it "functions normally if both toggle and definitions inside a eval" do + m = Module.new { + eval <<-CODE + module_function + + def test1() end + def test2() end + CODE + } + + m.respond_to?(:test1).should == true + m.respond_to?(:test2).should == true + end +end diff --git a/spec/rubyspec/core/module/name_spec.rb b/spec/rubyspec/core/module/name_spec.rb new file mode 100644 index 0000000000..0727278591 --- /dev/null +++ b/spec/rubyspec/core/module/name_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/module', __FILE__) + +describe "Module#name" do + it "is nil for an anonymous module" do + Module.new.name.should be_nil + end + + it "is nil when assigned to a constant in an anonymous module" do + m = Module.new + m::N = Module.new + m::N.name.should be_nil + end + + it "is not nil for a nested module created with the module keyword" do + m = Module.new + module m::N; end + m::N.name.should =~ /#::N/ + end + + it "is set when opened with the module keyword" do + ModuleSpecs.name.should == "ModuleSpecs" + end + + it "is set when a nested module is opened with the module keyword" do + ModuleSpecs::Anonymous.name.should == "ModuleSpecs::Anonymous" + end + + it "is set when assigning to a constant" do + m = Module.new + ModuleSpecs::Anonymous::A = m + m.name.should == "ModuleSpecs::Anonymous::A" + end + + it "is not modified when assigning to a new constant after it has been accessed" do + m = Module.new + ModuleSpecs::Anonymous::B = m + m.name.should == "ModuleSpecs::Anonymous::B" + ModuleSpecs::Anonymous::C = m + m.name.should == "ModuleSpecs::Anonymous::B" + end + + # http://bugs.ruby-lang.org/issues/6067 + it "is set with a conditional assignment to a nested constant" do + eval("ModuleSpecs::Anonymous::F ||= Module.new") + ModuleSpecs::Anonymous::F.name.should == "ModuleSpecs::Anonymous::F" + end + + it "is set with a conditional assignment to a constant" do + module ModuleSpecs::Anonymous + D ||= Module.new + end + ModuleSpecs::Anonymous::D.name.should == "ModuleSpecs::Anonymous::D" + end + + # http://redmine.ruby-lang.org/issues/show/1833 + it "preserves the encoding in which the class was defined" do + require fixture(__FILE__, "name") + ModuleSpecs::NameEncoding.new.name.encoding.should == Encoding::UTF_8 + end + + it "is set when the anonymous outer module name is set" do + m = Module.new + m::N = Module.new + ModuleSpecs::Anonymous::E = m + m::N.name.should == "ModuleSpecs::Anonymous::E::N" + end +end diff --git a/spec/rubyspec/core/module/nesting_spec.rb b/spec/rubyspec/core/module/nesting_spec.rb new file mode 100644 index 0000000000..0a86e5b8fc --- /dev/null +++ b/spec/rubyspec/core/module/nesting_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module::Nesting" do + + it "returns the list of Modules nested at the point of call" do + ModuleSpecs::Nesting[:root_level].should == [] + ModuleSpecs::Nesting[:first_level].should == [ModuleSpecs] + ModuleSpecs::Nesting[:basic].should == [ModuleSpecs::Nesting, ModuleSpecs] + ModuleSpecs::Nesting[:open_first_level].should == + [ModuleSpecs, ModuleSpecs::Nesting, ModuleSpecs] + ModuleSpecs::Nesting[:open_meta].should == + [ModuleSpecs::Nesting.meta, ModuleSpecs::Nesting, ModuleSpecs] + ModuleSpecs::Nesting[:nest_class].should == + [ModuleSpecs::Nesting::NestedClass, ModuleSpecs::Nesting, ModuleSpecs] + end + + it "returns the nesting for module/class declaring the called method" do + ModuleSpecs::Nesting.called_from_module_method.should == + [ModuleSpecs::Nesting, ModuleSpecs] + ModuleSpecs::Nesting::NestedClass.called_from_class_method.should == + [ModuleSpecs::Nesting::NestedClass, ModuleSpecs::Nesting, ModuleSpecs] + ModuleSpecs::Nesting::NestedClass.new.called_from_inst_method.should == + [ModuleSpecs::Nesting::NestedClass, ModuleSpecs::Nesting, ModuleSpecs] + end + +end + +describe "Module.nesting" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/module/new_spec.rb b/spec/rubyspec/core/module/new_spec.rb new file mode 100644 index 0000000000..21d3f954fa --- /dev/null +++ b/spec/rubyspec/core/module/new_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module.new" do + it "creates a new anonymous Module" do + Module.new.is_a?(Module).should == true + end + + it "creates a new Module and passes it to the provided block" do + test_mod = nil + m = Module.new do |mod| + mod.should_not == nil + self.should == mod + test_mod = mod + mod.is_a?(Module).should == true + Object.new # trying to return something + end + test_mod.should == m + end + + it "evaluates a passed block in the context of the module" do + fred = Module.new do + def hello() "hello" end + def bye() "bye" end + end + + (o = mock('x')).extend(fred) + o.hello.should == "hello" + o.bye.should == "bye" + end +end diff --git a/spec/rubyspec/core/module/prepend_features_spec.rb b/spec/rubyspec/core/module/prepend_features_spec.rb new file mode 100644 index 0000000000..6f341804d1 --- /dev/null +++ b/spec/rubyspec/core/module/prepend_features_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#prepend_features" do + it "is a private method" do + Module.should have_private_instance_method(:prepend_features, true) + end + + it "gets called when self is included in another module/class" do + ScratchPad.record [] + + m = Module.new do + def self.prepend_features(mod) + ScratchPad << mod + end + end + + c = Class.new do + prepend m + end + + ScratchPad.recorded.should == [c] + end + + it "raises an ArgumentError on a cyclic prepend" do + lambda { + ModuleSpecs::CyclicPrepend.send(:prepend_features, ModuleSpecs::CyclicPrepend) + }.should raise_error(ArgumentError) + end + + it "copies own tainted status to the given module" do + other = Module.new + Module.new.taint.send :prepend_features, other + other.tainted?.should be_true + end + + it "copies own untrusted status to the given module" do + other = Module.new + Module.new.untrust.send :prepend_features, other + other.untrusted?.should be_true + end + + it "clears caches of the given module" do + parent = Class.new do + def bar; :bar; end + end + + child = Class.new(parent) do + def foo; :foo; end + def bar; super; end + end + + mod = Module.new do + def foo; :fooo; end + end + + child.new.foo + child.new.bar + + child.prepend(mod) + + child.new.bar.should == :bar + end + + describe "on Class" do + it "is undefined" do + Class.should_not have_private_instance_method(:prepend_features, true) + end + + it "raises a TypeError if calling after rebinded to Class" do + lambda { + Module.instance_method(:prepend_features).bind(Class.new).call Module.new + }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/core/module/prepend_spec.rb b/spec/rubyspec/core/module/prepend_spec.rb new file mode 100644 index 0000000000..7d162bd5c5 --- /dev/null +++ b/spec/rubyspec/core/module/prepend_spec.rb @@ -0,0 +1,345 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#prepend" do + it "is a public method" do + Module.should have_public_instance_method(:prepend, false) + end + + it "does not affect the superclass" do + Class.new { prepend Module.new }.superclass.should == Object + end + + it "calls #prepend_features(self) in reversed order on each module" do + ScratchPad.record [] + + m = Module.new do + def self.prepend_features(mod) + ScratchPad << [ self, mod ] + end + end + + m2 = Module.new do + def self.prepend_features(mod) + ScratchPad << [ self, mod ] + end + end + + m3 = Module.new do + def self.prepend_features(mod) + ScratchPad << [ self, mod ] + end + end + + c = Class.new { prepend(m, m2, m3) } + + ScratchPad.recorded.should == [ [ m3, c], [ m2, c ], [ m, c ] ] + end + + it "raises a TypeError when the argument is not a Module" do + lambda { ModuleSpecs::Basic.prepend(Class.new) }.should raise_error(TypeError) + end + + it "does not raise a TypeError when the argument is an instance of a subclass of Module" do + lambda { ModuleSpecs::SubclassSpec.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) + end + + it "imports constants" do + m1 = Module.new + m1::MY_CONSTANT = 1 + m2 = Module.new { prepend(m1) } + m2.constants.should include(:MY_CONSTANT) + end + + it "imports instance methods" do + Module.new { prepend ModuleSpecs::A }.instance_methods.should include(:ma) + end + + it "does not import methods to modules and classes" do + Module.new { prepend ModuleSpecs::A }.methods.should_not include(:ma) + end + + it "allows wrapping methods" do + m = Module.new { def calc(x) super + 3 end } + c = Class.new { def calc(x) x*2 end } + c.prepend(m) + c.new.calc(1).should == 5 + end + + it "also prepends included modules" do + a = Module.new { def calc(x) x end } + b = Module.new { include a } + c = Class.new { prepend b } + c.new.calc(1).should == 1 + end + + it "prepends multiple modules in the right order" do + m1 = Module.new { def chain; super << :m1; end } + m2 = Module.new { def chain; super << :m2; end; prepend(m1) } + c = Class.new { def chain; [:c]; end; prepend(m2) } + c.new.chain.should == [:c, :m2, :m1] + end + + it "includes prepended modules in ancestors" do + m = Module.new + Class.new { prepend(m) }.ancestors.should include(m) + end + + it "reports the prepended module as the method owner" do + m = Module.new { def meth; end } + c = Class.new { def meth; end; prepend(m) } + c.new.method(:meth).owner.should == m + end + + it "reports the prepended module as the unbound method owner" do + m = Module.new { def meth; end } + c = Class.new { def meth; end; prepend(m) } + c.instance_method(:meth).owner.should == m + c.public_instance_method(:meth).owner.should == m + end + + it "causes the prepended module's method to be aliased by alias_method" do + m = Module.new { def meth; :m end } + c = Class.new { def meth; :c end; prepend(m); alias_method :alias, :meth } + c.new.alias.should == :m + end + + it "sees an instance of a prepended class as kind of the prepended module" do + m = Module.new + c = Class.new { prepend(m) } + c.new.should be_kind_of(m) + end + + it "keeps the module in the chain when dupping the class" do + m = Module.new + c = Class.new { prepend(m) } + c.dup.new.should be_kind_of(m) + end + + it "keeps the module in the chain when dupping an intermediate module" do + m1 = Module.new { def calc(x) x end } + m2 = Module.new { prepend(m1) } + c1 = Class.new { prepend(m2) } + m2dup = m2.dup + m2dup.ancestors.should == [m2dup,m1,m2] + c2 = Class.new { prepend(m2dup) } + c1.ancestors[0,3].should == [m1,m2,c1] + c1.new.should be_kind_of(m1) + c2.ancestors[0,4].should == [m2dup,m1,m2,c2] + c2.new.should be_kind_of(m1) + end + + it "depends on prepend_features to add the module" do + m = Module.new { def self.prepend_features(mod) end } + Class.new { prepend(m) }.ancestors.should_not include(m) + end + + it "adds the module in the subclass chains" do + parent = Class.new { def chain; [:parent]; end } + child = Class.new(parent) { def chain; super << :child; end } + mod = Module.new { def chain; super << :mod; end } + parent.prepend(mod) + parent.ancestors[0,2].should == [mod, parent] + child.ancestors[0,3].should == [child, mod, parent] + + parent.new.chain.should == [:parent, :mod] + child.new.chain.should == [:parent, :mod, :child] + end + + it "inserts a later prepended module into the chain" do + m1 = Module.new { def chain; super << :m1; end } + m2 = Module.new { def chain; super << :m2; end } + c1 = Class.new { def chain; [:c1]; end; prepend m1 } + c2 = Class.new(c1) { def chain; super << :c2; end } + c2.new.chain.should == [:c1, :m1, :c2] + c1.prepend(m2) + c2.new.chain.should == [:c1, :m1, :m2, :c2] + end + + it "works with subclasses" do + m = Module.new do + def chain + super << :module + end + end + + c = Class.new do + prepend m + def chain + [:class] + end + end + + s = Class.new(c) do + def chain + super << :subclass + end + end + + s.new.chain.should == [:class, :module, :subclass] + end + + it "throws a NoMethodError when there is no more superclass" do + m = Module.new do + def chain + super << :module + end + end + + c = Class.new do + prepend m + def chain + super << :class + end + end + lambda { c.new.chain }.should raise_error(NoMethodError) + end + + it "calls prepended after prepend_features" do + ScratchPad.record [] + + m = Module.new do + def self.prepend_features(klass) + ScratchPad << [:prepend_features, klass] + end + def self.prepended(klass) + ScratchPad << [:prepended, klass] + end + end + + c = Class.new { prepend(m) } + ScratchPad.recorded.should == [[:prepend_features, c], [:prepended, c]] + end + + it "detects cyclic prepends" do + lambda { + module ModuleSpecs::P + prepend ModuleSpecs::P + end + }.should raise_error(ArgumentError) + end + + ruby_version_is ''...'2.4' do + it "accepts no-arguments" do + lambda { + Module.new do + prepend + end + }.should_not raise_error + end + end + + ruby_version_is '2.4' do + it "doesn't accept no-arguments" do + lambda { + Module.new do + prepend + end + }.should raise_error(ArgumentError) + end + end + + it "returns the class it's included into" do + m = Module.new + r = nil + c = Class.new { r = prepend m } + r.should == c + end + + it "clears any caches" do + module ModuleSpecs::M3 + module PM1 + def foo + :m1 + end + end + + module PM2 + def foo + :m2 + end + end + + klass = Class.new do + prepend PM1 + + def get + foo + end + end + + o = klass.new + o.get.should == :m1 + + klass.class_eval do + prepend PM2 + end + + o.get.should == :m2 + end + end + + it "supports super when the module is prepended into a singleton class" do + ScratchPad.record [] + + mod = Module.new do + def self.inherited(base) + super + end + end + + module_with_singleton_class_prepend = Module.new do + singleton_class.prepend(mod) + end + + klass = Class.new(ModuleSpecs::RecordIncludedModules) do + include module_with_singleton_class_prepend + end + + ScratchPad.recorded.should == klass + end + + it "supports super when the module is prepended into a singleton class with a class super" do + ScratchPad.record [] + + base_class = Class.new(ModuleSpecs::RecordIncludedModules) do + def self.inherited(base) + super + end + end + + prepended_module = Module.new + base_class.singleton_class.prepend(prepended_module) + + child_class = Class.new(base_class) + ScratchPad.recorded.should == child_class + end + + it "does not interfere with a define_method super in the original class" do + base_class = Class.new do + def foo(ary) + ary << 1 + end + end + + child_class = Class.new(base_class) do + define_method :foo do |ary| + ary << 2 + super(ary) + end + end + + prep_mod = Module.new do + def foo(ary) + ary << 3 + super(ary) + end + end + + child_class.prepend(prep_mod) + + ary = [] + child_class.new.foo(ary) + ary.should == [3, 2, 1] + end +end diff --git a/spec/rubyspec/core/module/prepended_spec.rb b/spec/rubyspec/core/module/prepended_spec.rb new file mode 100644 index 0000000000..ed8e473941 --- /dev/null +++ b/spec/rubyspec/core/module/prepended_spec.rb @@ -0,0 +1,25 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Module#prepended" do + before :each do + ScratchPad.clear + end + + it "is a private method" do + Module.should have_private_instance_method(:prepended, true) + end + + it "is invoked when self is prepended to another module or class" do + m = Module.new do + def self.prepended(o) + ScratchPad.record o + end + end + + c = Class.new { prepend m } + + ScratchPad.recorded.should == c + end +end diff --git a/spec/rubyspec/core/module/private_class_method_spec.rb b/spec/rubyspec/core/module/private_class_method_spec.rb new file mode 100644 index 0000000000..ec10bcbf87 --- /dev/null +++ b/spec/rubyspec/core/module/private_class_method_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#private_class_method" do + before :each do + # This is not in classes.rb because after marking a class method private it + # will stay private. + class << ModuleSpecs::Parent + public + def private_method_1; end + def private_method_2; end + end + end + + after :each do + class << ModuleSpecs::Parent + remove_method :private_method_1 + remove_method :private_method_2 + end + end + + it "makes an existing class method private" do + ModuleSpecs::Parent.private_method_1.should == nil + ModuleSpecs::Parent.private_class_method :private_method_1 + lambda { ModuleSpecs::Parent.private_method_1 }.should raise_error(NoMethodError) + + # Technically above we're testing the Singleton classes, class method(right?). + # Try a "real" class method set private. + lambda { ModuleSpecs::Parent.private_method }.should raise_error(NoMethodError) + end + + it "makes an existing class method private up the inheritance tree" do + ModuleSpecs::Child.public_class_method :private_method_1 + ModuleSpecs::Child.private_method_1.should == nil + ModuleSpecs::Child.private_class_method :private_method_1 + + lambda { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError) + lambda { ModuleSpecs::Child.private_method }.should raise_error(NoMethodError) + end + + it "accepts more than one method at a time" do + ModuleSpecs::Parent.private_method_1.should == nil + ModuleSpecs::Parent.private_method_2.should == nil + + ModuleSpecs::Child.private_class_method :private_method_1, :private_method_2 + + lambda { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError) + lambda { ModuleSpecs::Child.private_method_2 }.should raise_error(NoMethodError) + end + + it "raises a NameError if class method doesn't exist" do + lambda do + ModuleSpecs.private_class_method :no_method_here + end.should raise_error(NameError) + end + + it "makes a class method private" do + c = Class.new do + def self.foo() "foo" end + private_class_method :foo + end + lambda { c.foo }.should raise_error(NoMethodError) + end + + it "raises a NameError when the given name is not a method" do + lambda do + Class.new do + private_class_method :foo + end + end.should raise_error(NameError) + end + + it "raises a NameError when the given name is an instance method" do + lambda do + Class.new do + def foo() "foo" end + private_class_method :foo + end + end.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/module/private_constant_spec.rb b/spec/rubyspec/core/module/private_constant_spec.rb new file mode 100644 index 0000000000..af0b1facdd --- /dev/null +++ b/spec/rubyspec/core/module/private_constant_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Module#private_constant" do + it "can only be passed constant names defined in the target (self) module" do + cls1 = Class.new + cls1.const_set :Foo, true + cls2 = Class.new(cls1) + + lambda do + cls2.send :private_constant, :Foo + end.should raise_error(NameError) + end + + it "accepts strings as constant names" do + cls = Class.new + cls.const_set :Foo, true + cls.send :private_constant, "Foo" + + lambda { cls::Foo }.should raise_error(NameError) + end + + it "accepts multiple names" do + mod = Module.new + mod.const_set :Foo, true + mod.const_set :Bar, true + + mod.send :private_constant, :Foo, :Bar + + lambda {mod::Foo}.should raise_error(NameError) + lambda {mod::Bar}.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/module/private_instance_methods_spec.rb b/spec/rubyspec/core/module/private_instance_methods_spec.rb new file mode 100644 index 0000000000..f5c4c3a0f9 --- /dev/null +++ b/spec/rubyspec/core/module/private_instance_methods_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Module#private_instance_methods" do + it "returns a list of private methods in module and its ancestors" do + ModuleSpecs::CountsMixin.should have_private_instance_method(:private_3) + + ModuleSpecs::CountsParent.should have_private_instance_method(:private_2) + ModuleSpecs::CountsParent.should have_private_instance_method(:private_3) + + ModuleSpecs::CountsChild.should have_private_instance_method(:private_1) + ModuleSpecs::CountsChild.should have_private_instance_method(:private_2) + ModuleSpecs::CountsChild.should have_private_instance_method(:private_3) + end + + it "when passed false as a parameter, should return only methods defined in that module" do + ModuleSpecs::CountsMixin.should have_private_instance_method(:private_3, false) + ModuleSpecs::CountsParent.should have_private_instance_method(:private_2, false) + ModuleSpecs::CountsChild.should have_private_instance_method(:private_1, false) + end + + it "default list should be the same as passing true as an argument" do + ModuleSpecs::CountsMixin.private_instance_methods(true).should == + ModuleSpecs::CountsMixin.private_instance_methods + ModuleSpecs::CountsParent.private_instance_methods(true).should == + ModuleSpecs::CountsParent.private_instance_methods + ModuleSpecs::CountsChild.private_instance_methods(true).should == + ModuleSpecs::CountsChild.private_instance_methods + end +end + +describe :module_private_instance_methods_supers, shared: true do + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.private_instance_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end + + it "returns a unique list for a subclass" do + m = ReflectSpecs::E.private_instance_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end +end + +describe "Module#private_instance_methods" do + describe "when not passed an argument" do + it_behaves_like :module_private_instance_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :module_private_instance_methods_supers, nil, true + end +end diff --git a/spec/rubyspec/core/module/private_method_defined_spec.rb b/spec/rubyspec/core/module/private_method_defined_spec.rb new file mode 100644 index 0000000000..b3e1814056 --- /dev/null +++ b/spec/rubyspec/core/module/private_method_defined_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#private_method_defined?" do + it "returns true if the named private method is defined by module or its ancestors" do + ModuleSpecs::CountsMixin.private_method_defined?("private_3").should == true + + ModuleSpecs::CountsParent.private_method_defined?("private_3").should == true + ModuleSpecs::CountsParent.private_method_defined?("private_2").should == true + + ModuleSpecs::CountsChild.private_method_defined?("private_3").should == true + ModuleSpecs::CountsChild.private_method_defined?("private_2").should == true + ModuleSpecs::CountsChild.private_method_defined?("private_1").should == true + end + + it "returns false if method is not a private method" do + ModuleSpecs::CountsChild.private_method_defined?("public_3").should == false + ModuleSpecs::CountsChild.private_method_defined?("public_2").should == false + ModuleSpecs::CountsChild.private_method_defined?("public_1").should == false + + ModuleSpecs::CountsChild.private_method_defined?("protected_3").should == false + ModuleSpecs::CountsChild.private_method_defined?("protected_2").should == false + ModuleSpecs::CountsChild.private_method_defined?("protected_1").should == false + end + + it "returns false if the named method is not defined by the module or its ancestors" do + ModuleSpecs::CountsMixin.private_method_defined?(:private_10).should == false + end + + it "accepts symbols for the method name" do + ModuleSpecs::CountsMixin.private_method_defined?(:private_3).should == true + end + + it "raises a TypeError if passed a Fixnum" do + lambda do + ModuleSpecs::CountsMixin.private_method_defined?(1) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed nil" do + lambda do + ModuleSpecs::CountsMixin.private_method_defined?(nil) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed false" do + lambda do + ModuleSpecs::CountsMixin.private_method_defined?(false) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that does not defined #to_str" do + lambda do + ModuleSpecs::CountsMixin.private_method_defined?(mock('x')) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that defines #to_sym" do + sym = mock('symbol') + def sym.to_sym() :private_3 end + + lambda do + ModuleSpecs::CountsMixin.private_method_defined?(sym) + end.should raise_error(TypeError) + end + + it "calls #to_str to convert an Object" do + str = mock('string') + def str.to_str() 'private_3' end + ModuleSpecs::CountsMixin.private_method_defined?(str).should == true + end +end diff --git a/spec/rubyspec/core/module/private_spec.rb b/spec/rubyspec/core/module/private_spec.rb new file mode 100644 index 0000000000..bfdb5016c2 --- /dev/null +++ b/spec/rubyspec/core/module/private_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_visibility', __FILE__) + +describe "Module#private" do + it_behaves_like :set_visibility, :private + + it "makes the target method uncallable from other types" do + obj = Object.new + class << obj + def foo; true; end + end + + obj.foo.should == true + + class << obj + private :foo + end + + lambda { obj.foo }.should raise_error(NoMethodError) + end + + it "makes a public Object instance method private in a new module" do + m = Module.new do + private :module_specs_public_method_on_object + end + + m.should have_private_instance_method(:module_specs_public_method_on_object) + + # Ensure we did not change Object's method + Object.should_not have_private_instance_method(:module_specs_public_method_on_object) + end + + it "makes a public Object instance method private in Kernel" do + Kernel.should have_private_instance_method( + :module_specs_public_method_on_object_for_kernel_private) + Object.should_not have_private_instance_method( + :module_specs_public_method_on_object_for_kernel_private) + end + + it "returns self" do + (class << Object.new; self; end).class_eval do + def foo; end + private(:foo).should equal(self) + private.should equal(self) + end + end + + it "raises a NameError when given an undefined name" do + lambda do + Module.new.send(:private, :undefined) + end.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/module/protected_instance_methods_spec.rb b/spec/rubyspec/core/module/protected_instance_methods_spec.rb new file mode 100644 index 0000000000..d5c9e5cd33 --- /dev/null +++ b/spec/rubyspec/core/module/protected_instance_methods_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Module#protected_instance_methods" do + it "returns a list of protected methods in module and its ancestors" do + methods = ModuleSpecs::CountsMixin.protected_instance_methods + methods.should include(:protected_3) + + methods = ModuleSpecs::CountsParent.protected_instance_methods + methods.should include(:protected_3) + methods.should include(:protected_2) + + methods = ModuleSpecs::CountsChild.protected_instance_methods + methods.should include(:protected_3) + methods.should include(:protected_2) + methods.should include(:protected_1) + end + + it "when passed false as a parameter, should return only methods defined in that module" do + ModuleSpecs::CountsMixin.protected_instance_methods(false).should == [:protected_3] + ModuleSpecs::CountsParent.protected_instance_methods(false).should == [:protected_2] + ModuleSpecs::CountsChild.protected_instance_methods(false).should == [:protected_1] + end + + it "default list should be the same as passing true as an argument" do + ModuleSpecs::CountsMixin.protected_instance_methods(true).should == + ModuleSpecs::CountsMixin.protected_instance_methods + ModuleSpecs::CountsParent.protected_instance_methods(true).should == + ModuleSpecs::CountsParent.protected_instance_methods + ModuleSpecs::CountsChild.protected_instance_methods(true).should == + ModuleSpecs::CountsChild.protected_instance_methods + end +end + +describe :module_protected_instance_methods_supers, shared: true do + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.protected_instance_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end + + it "returns a unique list for a subclass" do + m = ReflectSpecs::E.protected_instance_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end +end + +describe "Module#protected_instance_methods" do + describe "when not passed an argument" do + it_behaves_like :module_protected_instance_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :module_protected_instance_methods_supers, nil, true + end +end diff --git a/spec/rubyspec/core/module/protected_method_defined_spec.rb b/spec/rubyspec/core/module/protected_method_defined_spec.rb new file mode 100644 index 0000000000..af08efae81 --- /dev/null +++ b/spec/rubyspec/core/module/protected_method_defined_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#protected_method_defined?" do + it "returns true if the named protected method is defined by module or its ancestors" do + ModuleSpecs::CountsMixin.protected_method_defined?("protected_3").should == true + + ModuleSpecs::CountsParent.protected_method_defined?("protected_3").should == true + ModuleSpecs::CountsParent.protected_method_defined?("protected_2").should == true + + ModuleSpecs::CountsChild.protected_method_defined?("protected_3").should == true + ModuleSpecs::CountsChild.protected_method_defined?("protected_2").should == true + ModuleSpecs::CountsChild.protected_method_defined?("protected_1").should == true + end + + it "returns false if method is not a protected method" do + ModuleSpecs::CountsChild.protected_method_defined?("public_3").should == false + ModuleSpecs::CountsChild.protected_method_defined?("public_2").should == false + ModuleSpecs::CountsChild.protected_method_defined?("public_1").should == false + + ModuleSpecs::CountsChild.protected_method_defined?("private_3").should == false + ModuleSpecs::CountsChild.protected_method_defined?("private_2").should == false + ModuleSpecs::CountsChild.protected_method_defined?("private_1").should == false + end + + it "returns false if the named method is not defined by the module or its ancestors" do + ModuleSpecs::CountsMixin.protected_method_defined?(:protected_10).should == false + end + + it "accepts symbols for the method name" do + ModuleSpecs::CountsMixin.protected_method_defined?(:protected_3).should == true + end + + it "raises a TypeError if passed a Fixnum" do + lambda do + ModuleSpecs::CountsMixin.protected_method_defined?(1) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed nil" do + lambda do + ModuleSpecs::CountsMixin.protected_method_defined?(nil) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed false" do + lambda do + ModuleSpecs::CountsMixin.protected_method_defined?(false) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that does not defined #to_str" do + lambda do + ModuleSpecs::CountsMixin.protected_method_defined?(mock('x')) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that defines #to_sym" do + sym = mock('symbol') + def sym.to_sym() :protected_3 end + + lambda do + ModuleSpecs::CountsMixin.protected_method_defined?(sym) + end.should raise_error(TypeError) + end + + it "calls #to_str to convert an Object" do + str = mock('protected_3') + str.should_receive(:to_str).and_return("protected_3") + ModuleSpecs::CountsMixin.protected_method_defined?(str).should == true + end +end diff --git a/spec/rubyspec/core/module/protected_spec.rb b/spec/rubyspec/core/module/protected_spec.rb new file mode 100644 index 0000000000..7e77138c12 --- /dev/null +++ b/spec/rubyspec/core/module/protected_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_visibility', __FILE__) + +describe "Module#protected" do + before :each do + class << ModuleSpecs::Parent + def protected_method_1; 5; end + end + end + + it_behaves_like :set_visibility, :protected + + it "makes an existing class method protected" do + ModuleSpecs::Parent.protected_method_1.should == 5 + + class << ModuleSpecs::Parent + protected :protected_method_1 + end + + lambda { ModuleSpecs::Parent.protected_method_1 }.should raise_error(NoMethodError) + end + + it "makes a public Object instance method protected in a new module" do + m = Module.new do + protected :module_specs_public_method_on_object + end + + m.should have_protected_instance_method(:module_specs_public_method_on_object) + + # Ensure we did not change Object's method + Object.should_not have_protected_instance_method(:module_specs_public_method_on_object) + end + + it "makes a public Object instance method protected in Kernel" do + Kernel.should have_protected_instance_method( + :module_specs_public_method_on_object_for_kernel_protected) + Object.should_not have_protected_instance_method( + :module_specs_public_method_on_object_for_kernel_protected) + end + + it "returns self" do + (class << Object.new; self; end).class_eval do + def foo; end + protected(:foo).should equal(self) + protected.should equal(self) + end + end + + it "raises a NameError when given an undefined name" do + lambda do + Module.new.send(:protected, :undefined) + end.should raise_error(NameError) + end +end + diff --git a/spec/rubyspec/core/module/public_class_method_spec.rb b/spec/rubyspec/core/module/public_class_method_spec.rb new file mode 100644 index 0000000000..2c909b1223 --- /dev/null +++ b/spec/rubyspec/core/module/public_class_method_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#public_class_method" do + before :each do + class << ModuleSpecs::Parent + private + def public_method_1; end + def public_method_2; end + end + end + + after :each do + class << ModuleSpecs::Parent + remove_method :public_method_1 + remove_method :public_method_2 + end + end + + it "makes an existing class method public" do + lambda { ModuleSpecs::Parent.public_method_1 }.should raise_error(NoMethodError) + ModuleSpecs::Parent.public_class_method :public_method_1 + ModuleSpecs::Parent.public_method_1.should == nil + + # Technically above we're testing the Singleton classes, class method(right?). + # Try a "real" class method set public. + ModuleSpecs::Parent.public_method.should == nil + end + + it "makes an existing class method public up the inheritance tree" do + ModuleSpecs::Child.private_class_method :public_method_1 + lambda { ModuleSpecs::Child.public_method_1 }.should raise_error(NoMethodError) + ModuleSpecs::Child.public_class_method :public_method_1 + + ModuleSpecs::Child.public_method_1.should == nil + ModuleSpecs::Child.public_method.should == nil + end + + it "accepts more than one method at a time" do + lambda { ModuleSpecs::Parent.public_method_1 }.should raise_error(NameError) + lambda { ModuleSpecs::Parent.public_method_2 }.should raise_error(NameError) + + ModuleSpecs::Child.public_class_method :public_method_1, :public_method_2 + + ModuleSpecs::Child.public_method_1.should == nil + ModuleSpecs::Child.public_method_2.should == nil + end + + it "raises a NameError if class method doesn't exist" do + lambda do + ModuleSpecs.public_class_method :no_method_here + end.should raise_error(NameError) + end + + it "makes a class method public" do + c = Class.new do + def self.foo() "foo" end + public_class_method :foo + end + + c.foo.should == "foo" + end + + it "raises a NameError when the given name is not a method" do + lambda do + Class.new do + public_class_method :foo + end + end.should raise_error(NameError) + end + + it "raises a NameError when the given name is an instance method" do + lambda do + Class.new do + def foo() "foo" end + public_class_method :foo + end + end.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/module/public_constant_spec.rb b/spec/rubyspec/core/module/public_constant_spec.rb new file mode 100644 index 0000000000..cbf51a6df6 --- /dev/null +++ b/spec/rubyspec/core/module/public_constant_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Module#public_constant" do + it "can only be passed constant names defined in the target (self) module" do + cls1 = Class.new + cls1.const_set :Foo, true + cls2 = Class.new(cls1) + + lambda do + cls2.send :public_constant, :Foo + end.should raise_error(NameError) + end + + it "accepts strings as constant names" do + cls = Class.new + cls.const_set :Foo, true + + cls.send :private_constant, :Foo + cls.send :public_constant, "Foo" + + cls::Foo.should == true + end + + # [ruby-list:48558] + it "accepts multiple names" do + mod = Module.new + mod.const_set :Foo, true + mod.const_set :Bar, true + + mod.send :private_constant, :Foo + mod.send :private_constant, :Bar + + mod.send :public_constant, :Foo, :Bar + + mod::Foo.should == true + mod::Bar.should == true + end +end diff --git a/spec/rubyspec/core/module/public_instance_method_spec.rb b/spec/rubyspec/core/module/public_instance_method_spec.rb new file mode 100644 index 0000000000..c1e94c5fed --- /dev/null +++ b/spec/rubyspec/core/module/public_instance_method_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#public_instance_method" do + it "is a public method" do + Module.should have_public_instance_method(:public_instance_method, false) + end + + it "requires an argument" do + Module.new.method(:public_instance_method).arity.should == 1 + end + + describe "when given a public method name" do + it "returns an UnboundMethod corresponding to the defined Module" do + ret = ModuleSpecs::Super.public_instance_method(:public_module) + ret.should be_an_instance_of(UnboundMethod) + ret.owner.should equal(ModuleSpecs::Basic) + + ret = ModuleSpecs::Super.public_instance_method(:public_super_module) + ret.should be_an_instance_of(UnboundMethod) + ret.owner.should equal(ModuleSpecs::Super) + end + + it "accepts if the name is a Symbol or String" do + ret = ModuleSpecs::Basic.public_instance_method(:public_module) + ModuleSpecs::Basic.public_instance_method("public_module").should == ret + end + end + + it "raises a TypeError when given a name is not Symbol or String" do + lambda { Module.new.public_instance_method(nil) }.should raise_error(TypeError) + end + + it "raises a NameError when given a protected method name" do + lambda do + ModuleSpecs::Basic.public_instance_method(:protected_module) + end.should raise_error(NameError) + end + + it "raises a NameError if the method is private" do + lambda do + ModuleSpecs::Basic.public_instance_method(:private_module) + end.should raise_error(NameError) + end + + it "raises a NameError if the method has been undefined" do + lambda do + ModuleSpecs::Parent.public_instance_method(:undefed_method) + end.should raise_error(NameError) + end + + it "raises a NameError if the method does not exist" do + lambda do + Module.new.public_instance_method(:missing) + end.should raise_error(NameError) + end + + it "sets the NameError#name attribute to the name of the missing method" do + begin + Module.new.public_instance_method(:missing) + rescue NameError => e + e.name.should == :missing + end + end +end diff --git a/spec/rubyspec/core/module/public_instance_methods_spec.rb b/spec/rubyspec/core/module/public_instance_methods_spec.rb new file mode 100644 index 0000000000..14453109c3 --- /dev/null +++ b/spec/rubyspec/core/module/public_instance_methods_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite + +describe "Module#public_instance_methods" do + it "returns a list of public methods in module and its ancestors" do + methods = ModuleSpecs::CountsMixin.public_instance_methods + methods.should include(:public_3) + + methods = ModuleSpecs::CountsParent.public_instance_methods + methods.should include(:public_3) + methods.should include(:public_2) + + methods = ModuleSpecs::CountsChild.public_instance_methods + methods.should include(:public_3) + methods.should include(:public_2) + methods.should include(:public_1) + + methods = ModuleSpecs::Child2.public_instance_methods + methods.should include(:foo) + end + + it "when passed false as a parameter, should return only methods defined in that module" do + ModuleSpecs::CountsMixin.public_instance_methods(false).should == [:public_3] + ModuleSpecs::CountsParent.public_instance_methods(false).should == [:public_2] + ModuleSpecs::CountsChild.public_instance_methods(false).should == [:public_1] + end + + it "default list should be the same as passing true as an argument" do + ModuleSpecs::CountsMixin.public_instance_methods(true).should == + ModuleSpecs::CountsMixin.public_instance_methods + ModuleSpecs::CountsParent.public_instance_methods(true).should == + ModuleSpecs::CountsParent.public_instance_methods + ModuleSpecs::CountsChild.public_instance_methods(true).should == + ModuleSpecs::CountsChild.public_instance_methods + end +end + +describe :module_public_instance_methods_supers, shared: true do + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.public_instance_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end + + it "returns a unique list for a subclass" do + m = ReflectSpecs::E.public_instance_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end +end + +describe "Module#public_instance_methods" do + describe "when not passed an argument" do + it_behaves_like :module_public_instance_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :module_public_instance_methods_supers, nil, true + end +end diff --git a/spec/rubyspec/core/module/public_method_defined_spec.rb b/spec/rubyspec/core/module/public_method_defined_spec.rb new file mode 100644 index 0000000000..329778ac15 --- /dev/null +++ b/spec/rubyspec/core/module/public_method_defined_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#public_method_defined?" do + it "returns true if the named public method is defined by module or its ancestors" do + ModuleSpecs::CountsMixin.public_method_defined?("public_3").should == true + + ModuleSpecs::CountsParent.public_method_defined?("public_3").should == true + ModuleSpecs::CountsParent.public_method_defined?("public_2").should == true + + ModuleSpecs::CountsChild.public_method_defined?("public_3").should == true + ModuleSpecs::CountsChild.public_method_defined?("public_2").should == true + ModuleSpecs::CountsChild.public_method_defined?("public_1").should == true + end + + it "returns false if method is not a public method" do + ModuleSpecs::CountsChild.public_method_defined?("private_3").should == false + ModuleSpecs::CountsChild.public_method_defined?("private_2").should == false + ModuleSpecs::CountsChild.public_method_defined?("private_1").should == false + + ModuleSpecs::CountsChild.public_method_defined?("protected_3").should == false + ModuleSpecs::CountsChild.public_method_defined?("protected_2").should == false + ModuleSpecs::CountsChild.public_method_defined?("protected_1").should == false + end + + it "returns false if the named method is not defined by the module or its ancestors" do + ModuleSpecs::CountsMixin.public_method_defined?(:public_10).should == false + end + + it "accepts symbols for the method name" do + ModuleSpecs::CountsMixin.public_method_defined?(:public_3).should == true + end + + it "raises a TypeError if passed a Fixnum" do + lambda do + ModuleSpecs::CountsMixin.public_method_defined?(1) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed nil" do + lambda do + ModuleSpecs::CountsMixin.public_method_defined?(nil) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed false" do + lambda do + ModuleSpecs::CountsMixin.public_method_defined?(false) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that does not defined #to_str" do + lambda do + ModuleSpecs::CountsMixin.public_method_defined?(mock('x')) + end.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that defines #to_sym" do + sym = mock('symbol') + def sym.to_sym() :public_3 end + + lambda do + ModuleSpecs::CountsMixin.public_method_defined?(sym) + end.should raise_error(TypeError) + end + + it "calls #to_str to convert an Object" do + str = mock('public_3') + def str.to_str() 'public_3' end + ModuleSpecs::CountsMixin.public_method_defined?(str).should == true + end +end diff --git a/spec/rubyspec/core/module/public_spec.rb b/spec/rubyspec/core/module/public_spec.rb new file mode 100644 index 0000000000..8507689752 --- /dev/null +++ b/spec/rubyspec/core/module/public_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_visibility', __FILE__) + +describe "Module#public" do + it_behaves_like :set_visibility, :public + + it "on a superclass method calls the redefined method" do + ModuleSpecs::ChildPrivateMethodMadePublic.new.private_method_redefined.should == :after_redefinition + end + + it "makes a private Object instance method public in a new module" do + m = Module.new do + public :module_specs_private_method_on_object + end + + m.should have_public_instance_method(:module_specs_private_method_on_object) + + # Ensure we did not change Object's method + Object.should_not have_public_instance_method(:module_specs_private_method_on_object) + end + + it "makes a private Object instance method public in Kernel" do + Kernel.should have_public_instance_method( + :module_specs_private_method_on_object_for_kernel_public) + Object.should_not have_public_instance_method( + :module_specs_private_method_on_object_for_kernel_public) + end + + it "returns self" do + (class << Object.new; self; end).class_eval do + def foo; end + private :foo + public(:foo).should equal(self) + public.should equal(self) + end + end + + it "raises a NameError when given an undefined name" do + lambda do + Module.new.send(:public, :undefined) + end.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/core/module/remove_class_variable_spec.rb b/spec/rubyspec/core/module/remove_class_variable_spec.rb new file mode 100644 index 0000000000..adf2c1d1f8 --- /dev/null +++ b/spec/rubyspec/core/module/remove_class_variable_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#remove_class_variable" do + it "removes class variable" do + m = ModuleSpecs::MVars.dup + m.send(:remove_class_variable, :@@mvar) + m.class_variable_defined?(:@@mvar).should == false + end + + it "returns the value of removing class variable" do + m = ModuleSpecs::MVars.dup + m.send(:remove_class_variable, :@@mvar).should == :mvar + end + + it "removes a class variable defined in a metaclass" do + obj = mock("metaclass class variable") + meta = obj.singleton_class + meta.send :class_variable_set, :@@var, 1 + meta.send(:remove_class_variable, :@@var).should == 1 + meta.class_variable_defined?(:@@var).should be_false + end + + it "raises a NameError when removing class variable declared in included module" do + c = ModuleSpecs::RemoveClassVariable.new { include ModuleSpecs::MVars.dup } + lambda { c.send(:remove_class_variable, :@@mvar) }.should raise_error(NameError) + end + + it "raises a NameError when passed a symbol with one leading @" do + lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@mvar) }.should raise_error(NameError) + end + + it "raises a NameError when passed a symbol with no leading @" do + lambda { ModuleSpecs::MVars.send(:remove_class_variable, :mvar) }.should raise_error(NameError) + end + + it "raises a NameError when an uninitialized class variable is given" do + lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@@nonexisting_class_variable) }.should raise_error(NameError) + end + + it "is public" do + Module.should_not have_private_instance_method(:remove_class_variable) + end +end diff --git a/spec/rubyspec/core/module/remove_const_spec.rb b/spec/rubyspec/core/module/remove_const_spec.rb new file mode 100644 index 0000000000..0c1b87598e --- /dev/null +++ b/spec/rubyspec/core/module/remove_const_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/constants', __FILE__) + +describe "Module#remove_const" do + it "removes the constant specified by a String or Symbol from the receiver's constant table" do + ConstantSpecs::ModuleM::CS_CONST252 = :const252 + ConstantSpecs::ModuleM::CS_CONST252.should == :const252 + + ConstantSpecs::ModuleM.send :remove_const, :CS_CONST252 + lambda { ConstantSpecs::ModuleM::CS_CONST252 }.should raise_error(NameError) + + ConstantSpecs::ModuleM::CS_CONST253 = :const253 + ConstantSpecs::ModuleM::CS_CONST253.should == :const253 + + ConstantSpecs::ModuleM.send :remove_const, "CS_CONST253" + lambda { ConstantSpecs::ModuleM::CS_CONST253 }.should raise_error(NameError) + end + + it "returns the value of the removed constant" do + ConstantSpecs::ModuleM::CS_CONST254 = :const254 + ConstantSpecs::ModuleM.send(:remove_const, :CS_CONST254).should == :const254 + end + + it "raises a NameError and does not call #const_missing if the constant is not defined" do + ConstantSpecs.should_not_receive(:const_missing) + lambda { ConstantSpecs.send(:remove_const, :Nonexistent) }.should raise_error(NameError) + end + + it "raises a NameError and does not call #const_missing if the constant is not defined directly in the module" do + begin + ConstantSpecs::ModuleM::CS_CONST255 = :const255 + ConstantSpecs::ContainerA::CS_CONST255.should == :const255 + ConstantSpecs::ContainerA.should_not_receive(:const_missing) + + lambda do + ConstantSpecs::ContainerA.send :remove_const, :CS_CONST255 + end.should raise_error(NameError) + ensure + ConstantSpecs::ModuleM.send :remove_const, "CS_CONST255" + end + end + + it "raises a NameError if the name does not start with a capital letter" do + lambda { ConstantSpecs.send :remove_const, "name" }.should raise_error(NameError) + end + + it "raises a NameError if the name starts with a non-alphabetic character" do + lambda { ConstantSpecs.send :remove_const, "__CONSTX__" }.should raise_error(NameError) + lambda { ConstantSpecs.send :remove_const, "@Name" }.should raise_error(NameError) + lambda { ConstantSpecs.send :remove_const, "!Name" }.should raise_error(NameError) + lambda { ConstantSpecs.send :remove_const, "::Name" }.should raise_error(NameError) + end + + it "raises a NameError if the name contains non-alphabetic characters except '_'" do + ConstantSpecs::ModuleM::CS_CONST256 = :const256 + ConstantSpecs::ModuleM.send :remove_const, "CS_CONST256" + lambda { ConstantSpecs.send :remove_const, "Name=" }.should raise_error(NameError) + lambda { ConstantSpecs.send :remove_const, "Name?" }.should raise_error(NameError) + end + + it "calls #to_str to convert the given name to a String" do + ConstantSpecs::CS_CONST257 = :const257 + name = mock("CS_CONST257") + name.should_receive(:to_str).and_return("CS_CONST257") + ConstantSpecs.send(:remove_const, name).should == :const257 + end + + it "raises a TypeError if conversion to a String by calling #to_str fails" do + name = mock('123') + lambda { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError) + + name.should_receive(:to_str).and_return(123) + lambda { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError) + end + + it "is a private method" do + Module.private_methods.should include(:remove_const) + end + + it "returns nil when removing autoloaded constant" do + ConstantSpecs.autoload :AutoloadedConstant, 'a_file' + ConstantSpecs.send(:remove_const, :AutoloadedConstant).should be_nil + end +end diff --git a/spec/rubyspec/core/module/remove_method_spec.rb b/spec/rubyspec/core/module/remove_method_spec.rb new file mode 100644 index 0000000000..f98cfa8daa --- /dev/null +++ b/spec/rubyspec/core/module/remove_method_spec.rb @@ -0,0 +1,109 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +module ModuleSpecs + class Parent + def method_to_remove; 1; end + end + + class First + def method_to_remove; 1; end + end + + class Second < First + def method_to_remove; 2; end + end +end + +describe "Module#remove_method" do + before :each do + @module = Module.new { def method_to_remove; end } + end + + it "is a private method" do + Module.should have_private_instance_method(:remove_method, false) + end + + it "removes the method from a class" do + klass = Class.new do + def method_to_remove; 1; end + end + x = klass.new + klass.send(:remove_method, :method_to_remove) + x.respond_to?(:method_to_remove).should == false + end + + it "removes method from subclass, but not parent" do + child = Class.new(ModuleSpecs::Parent) do + def method_to_remove; 2; end + remove_method :method_to_remove + end + x = child.new + x.respond_to?(:method_to_remove).should == true + x.method_to_remove.should == 1 + end + + it "removes multiple methods with 1 call" do + klass = Class.new do + def method_to_remove_1; 1; end + def method_to_remove_2; 2; end + remove_method :method_to_remove_1, :method_to_remove_2 + end + x = klass.new + x.respond_to?(:method_to_remove_1).should == false + x.respond_to?(:method_to_remove_2).should == false + end + + it "accepts multiple arguments" do + Module.instance_method(:remove_method).arity.should < 0 + end + + it "does not remove any instance methods when argument not given" do + before = @module.instance_methods(true) + @module.private_instance_methods(true) + @module.send :remove_method + after = @module.instance_methods(true) + @module.private_instance_methods(true) + before.sort.should == after.sort + end + + it "returns self" do + @module.send(:remove_method, :method_to_remove).should equal(@module) + end + + it "raises a NameError when attempting to remove method further up the inheritance tree" do + lambda { + class Third < ModuleSpecs::Second + remove_method :method_to_remove + end + }.should raise_error(NameError) + end + + it "raises a NameError when attempting to remove a missing method" do + lambda { + class Third < ModuleSpecs::Second + remove_method :blah + end + }.should raise_error(NameError) + end + + describe "on frozen instance" do + before :each do + @frozen = @module.dup.freeze + end + + it "raises a RuntimeError when passed a name" do + lambda { @frozen.send :remove_method, :method_to_remove }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when passed a missing name" do + lambda { @frozen.send :remove_method, :not_exist }.should raise_error(RuntimeError) + end + + it "raises a TypeError when passed a not name" do + lambda { @frozen.send :remove_method, Object.new }.should raise_error(TypeError) + end + + it "does not raise exceptions when no arguments given" do + @frozen.send(:remove_method).should equal(@frozen) + end + end +end diff --git a/spec/rubyspec/core/module/shared/class_eval.rb b/spec/rubyspec/core/module/shared/class_eval.rb new file mode 100644 index 0000000000..6bb9668fee --- /dev/null +++ b/spec/rubyspec/core/module/shared/class_eval.rb @@ -0,0 +1,115 @@ +describe :module_class_eval, shared: true do + # TODO: This should probably be replaced with a "should behave like" that uses + # the many scoping/binding specs from kernel/eval_spec, since most of those + # behaviors are the same for instance_eval. See also module_eval/class_eval. + + it "evaluates a given string in the context of self" do + ModuleSpecs.send(@method, "self").should == ModuleSpecs + ModuleSpecs.send(@method, "1 + 1").should == 2 + end + + it "does not add defined methods to other classes" do + FalseClass.send(@method) do + def foo + 'foo' + end + end + lambda {42.foo}.should raise_error(NoMethodError) + end + + it "resolves constants in the caller scope" do + ModuleSpecs::ClassEvalTest.get_constant_from_scope.should == ModuleSpecs::Lookup + end + + it "resolves constants in the caller scope ignoring send" do + ModuleSpecs::ClassEvalTest.get_constant_from_scope_with_send(@method).should == ModuleSpecs::Lookup + end + + it "resolves constants in the receiver's scope" do + ModuleSpecs.send(@method, "Lookup").should == ModuleSpecs::Lookup + ModuleSpecs.send(@method, "Lookup::LOOKIE").should == ModuleSpecs::Lookup::LOOKIE + end + + it "defines constants in the receiver's scope" do + ModuleSpecs.send(@method, "module NewEvaluatedModule;end") + ModuleSpecs.const_defined?(:NewEvaluatedModule, false).should == true + end + + it "evaluates a given block in the context of self" do + ModuleSpecs.send(@method) { self }.should == ModuleSpecs + ModuleSpecs.send(@method) { 1 + 1 }.should == 2 + end + + it "passes the module as the first argument of the block" do + given = nil + ModuleSpecs.send(@method) do |block_parameter| + given = block_parameter + end + given.should equal ModuleSpecs + end + + it "uses the optional filename and lineno parameters for error messages" do + ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102] + end + + it "converts a non-string filename to a string using to_str" do + (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) + ModuleSpecs.send(@method, "1+1", file) + end + + it "raises a TypeError when the given filename can't be converted to string using to_str" do + (file = mock('123')).should_receive(:to_str).and_return(123) + lambda { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError) + end + + it "converts non string eval-string to string using to_str" do + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.send(@method, o).should == 2 + end + + it "raises a TypeError when the given eval-string can't be converted to string using to_str" do + o = mock('x') + lambda { ModuleSpecs.send(@method, o) }.should raise_error(TypeError) + + (o = mock('123')).should_receive(:to_str).and_return(123) + lambda { ModuleSpecs.send(@method, o) }.should raise_error(TypeError) + end + + it "raises an ArgumentError when no arguments and no block are given" do + lambda { ModuleSpecs.send(@method) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when more than 3 arguments are given" do + lambda { + ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus") + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when a block and normal arguments are given" do + lambda { + ModuleSpecs.send(@method, "1 + 1") { 1 + 1 } + }.should raise_error(ArgumentError) + end + + # This case was found because Rubinius was caching the compiled + # version of the string and not duping the methods within the + # eval, causing the method addition to change the static scope + # of the shared CompiledCode. + it "adds methods respecting the lexical constant scope" do + code = "def self.attribute; C; end" + + a = Class.new do + self::C = "A" + end + + b = Class.new do + self::C = "B" + end + + a.send @method, code + b.send @method, code + + a.attribute.should == "A" + b.attribute.should == "B" + end +end diff --git a/spec/rubyspec/core/module/shared/class_exec.rb b/spec/rubyspec/core/module/shared/class_exec.rb new file mode 100644 index 0000000000..c5c18b0a34 --- /dev/null +++ b/spec/rubyspec/core/module/shared/class_exec.rb @@ -0,0 +1,29 @@ +describe :module_class_exec, shared: true do + it "does not add defined methods to other classes" do + FalseClass.send(@method) do + def foo + 'foo' + end + end + lambda {42.foo}.should raise_error(NoMethodError) + end + + it "defines method in the receiver's scope" do + ModuleSpecs::Subclass.send(@method) { def foo; end } + ModuleSpecs::Subclass.new.respond_to?(:foo).should == true + end + + it "evaluates a given block in the context of self" do + ModuleSpecs::Subclass.send(@method) { self }.should == ModuleSpecs::Subclass + ModuleSpecs::Subclass.new.send(@method) { 1 + 1 }.should == 2 + end + + it "raises a LocalJumpError when no block is given" do + lambda { ModuleSpecs::Subclass.send(@method) }.should raise_error(LocalJumpError) + end + + it "passes arguments to the block" do + a = ModuleSpecs::Subclass + a.send(@method, 1) { |b| b }.should equal(1) + end +end diff --git a/spec/rubyspec/core/module/shared/equal_value.rb b/spec/rubyspec/core/module/shared/equal_value.rb new file mode 100644 index 0000000000..f1227d873c --- /dev/null +++ b/spec/rubyspec/core/module/shared/equal_value.rb @@ -0,0 +1,14 @@ +describe :module_equal, shared: true do + it "returns true if self and the given module are the same" do + ModuleSpecs.send(@method, ModuleSpecs).should == true + ModuleSpecs::Child.send(@method, ModuleSpecs::Child).should == true + ModuleSpecs::Parent.send(@method, ModuleSpecs::Parent).should == true + ModuleSpecs::Basic.send(@method, ModuleSpecs::Basic).should == true + ModuleSpecs::Super.send(@method, ModuleSpecs::Super).should == true + + ModuleSpecs::Child.send(@method, ModuleSpecs).should == false + ModuleSpecs::Child.send(@method, ModuleSpecs::Parent).should == false + ModuleSpecs::Child.send(@method, ModuleSpecs::Basic).should == false + ModuleSpecs::Child.send(@method, ModuleSpecs::Super).should == false + end +end diff --git a/spec/rubyspec/core/module/shared/set_visibility.rb b/spec/rubyspec/core/module/shared/set_visibility.rb new file mode 100644 index 0000000000..45eedc13fa --- /dev/null +++ b/spec/rubyspec/core/module/shared/set_visibility.rb @@ -0,0 +1,135 @@ +# -*- encoding: us-ascii -*- + +describe :set_visibility, shared: true do + it "is a private method" do + Module.should have_private_instance_method(@method, false) + end + + describe "without arguments" do + it "sets visibility to following method definitions" do + visibility = @method + mod = Module.new { + send visibility + + def test1() end + def test2() end + } + + mod.should send(:"have_#{@method}_instance_method", :test1, false) + mod.should send(:"have_#{@method}_instance_method", :test2, false) + end + + it "stops setting visibility if the body encounters other visibility setters without arguments" do + visibility = @method + new_visibility = nil + mod = Module.new { + send visibility + new_visibility = [:protected, :private].find {|vis| vis != visibility } + send new_visibility + def test1() end + } + + mod.should send(:"have_#{new_visibility}_instance_method", :test1, false) + end + + it "continues setting visibility if the body encounters other visibility setters with arguments" do + visibility = @method + mod = Module.new { + send visibility + def test1() end + send([:protected, :private].find {|vis| vis != visibility }, :test1) + def test2() end + } + + mod.should send(:"have_#{@method}_instance_method", :test2, false) + end + + it "does not affect module_evaled method definitions when itself is outside the eval" do + visibility = @method + mod = Module.new { + send visibility + + module_eval { def test1() end } + module_eval " def test2() end " + } + + mod.should have_public_instance_method(:test1, false) + mod.should have_public_instance_method(:test2, false) + end + + it "does not affect outside method definitions when itself is inside a module_eval" do + visibility = @method + mod = Module.new { + module_eval { send visibility } + + def test1() end + } + + mod.should have_public_instance_method(:test1, false) + end + + it "affects normally if itself and method definitions are inside a module_eval" do + visibility = @method + mod = Module.new { + module_eval { + send visibility + + def test1() end + } + } + + mod.should send(:"have_#{@method}_instance_method", :test1, false) + end + + it "does not affect method definitions when itself is inside an eval and method definitions are outside" do + visibility = @method + initialized_visibility = [:public, :protected, :private].find {|sym| sym != visibility } + mod = Module.new { + send initialized_visibility + eval visibility.to_s + + def test1() end + } + + mod.should send(:"have_#{initialized_visibility}_instance_method", :test1, false) + end + + it "affects evaled method definitions when itself is outside the eval" do + visibility = @method + mod = Module.new { + send visibility + + eval "def test1() end" + } + + mod.should send(:"have_#{@method}_instance_method", :test1, false) + end + + it "affects normally if itself and following method definitions are inside a eval" do + visibility = @method + mod = Module.new { + eval <<-CODE + #{visibility} + + def test1() end + CODE + } + + mod.should send(:"have_#{@method}_instance_method", :test1, false) + end + + describe "within a closure" do + it "sets the visibility outside the closure" do + visibility = @method + mod = Module.new { + 1.times { + send visibility + } + def test1() end + } + + mod.should send(:"have_#{@method}_instance_method", :test1, false) + end + end + end +end diff --git a/spec/rubyspec/core/module/singleton_class_spec.rb b/spec/rubyspec/core/module/singleton_class_spec.rb new file mode 100644 index 0000000000..177dfc224f --- /dev/null +++ b/spec/rubyspec/core/module/singleton_class_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Module#singleton_class?" do + it "returns true for singleton classes" do + xs = self.singleton_class + xs.singleton_class?.should == true + end + + it "returns false for other classes" do + c = Class.new + c.singleton_class?.should == false + end + + describe "with singleton values" do + it "returns false for nil's singleton class" do + NilClass.singleton_class?.should == false + end + + it "returns false for true's singleton class" do + TrueClass.singleton_class?.should == false + end + + it "returns false for false's singleton class" do + FalseClass.singleton_class?.should == false + end + end +end diff --git a/spec/rubyspec/core/module/to_s_spec.rb b/spec/rubyspec/core/module/to_s_spec.rb new file mode 100644 index 0000000000..de924a8739 --- /dev/null +++ b/spec/rubyspec/core/module/to_s_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Module#to_s" do + it "returns the full constant path leading to the module" do + ModuleSpecs::LookupMod.to_s.should == "ModuleSpecs::LookupMod" + end + + it "works with an anonymous module" do + m = Module.new + m.to_s.should =~ /#/ + end + + it "works with an anonymous class" do + c = Class.new + c.to_s.should =~ /#/ + end +end diff --git a/spec/rubyspec/core/module/undef_method_spec.rb b/spec/rubyspec/core/module/undef_method_spec.rb new file mode 100644 index 0000000000..663ab2ffc8 --- /dev/null +++ b/spec/rubyspec/core/module/undef_method_spec.rb @@ -0,0 +1,152 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +module ModuleSpecs + class Parent + def method_to_undef() 1 end + def another_method_to_undef() 1 end + end + + class Ancestor + def method_to_undef() 1 end + def another_method_to_undef() 1 end + end +end + +describe "Module#undef_method" do + before :each do + @module = Module.new { def method_to_undef; end } + end + + it "is a private method" do + Module.should have_private_instance_method(:undef_method, false) + end + + it "requires multiple arguments" do + Module.instance_method(:undef_method).arity.should < 0 + end + + it "allows multiple methods to be removed at once" do + klass = Class.new do + def method_to_undef() 1 end + def another_method_to_undef() 1 end + end + x = klass.new + klass.send(:undef_method, :method_to_undef, :another_method_to_undef) + + lambda { x.method_to_undef }.should raise_error(NoMethodError) + lambda { x.another_method_to_undef }.should raise_error(NoMethodError) + end + + it "does not undef any instance methods when argument not given" do + before = @module.instance_methods(true) + @module.private_instance_methods(true) + @module.send :undef_method + after = @module.instance_methods(true) + @module.private_instance_methods(true) + before.sort.should == after.sort + end + + it "returns self" do + @module.send(:undef_method, :method_to_undef).should equal(@module) + end + + it "raises a NameError when passed a missing name" do + lambda { @module.send :undef_method, :not_exist }.should raise_error(NameError) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end + + describe "on frozen instance" do + before :each do + @frozen = @module.dup.freeze + end + + it "raises a RuntimeError when passed a name" do + lambda { @frozen.send :undef_method, :method_to_undef }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when passed a missing name" do + lambda { @frozen.send :undef_method, :not_exist }.should raise_error(RuntimeError) + end + + it "raises a TypeError when passed a not name" do + lambda { @frozen.send :undef_method, Object.new }.should raise_error(TypeError) + end + + it "does not raise exceptions when no arguments given" do + @frozen.send(:undef_method).should equal(@frozen) + end + end +end + +describe "Module#undef_method with symbol" do + it "removes a method defined in a class" do + klass = Class.new do + def method_to_undef() 1 end + def another_method_to_undef() 1 end + end + x = klass.new + + x.method_to_undef.should == 1 + + klass.send :undef_method, :method_to_undef + + lambda { x.method_to_undef }.should raise_error(NoMethodError) + end + + it "removes a method defined in a super class" do + child_class = Class.new(ModuleSpecs::Parent) + child = child_class.new + child.method_to_undef.should == 1 + + child_class.send :undef_method, :method_to_undef + + lambda { child.method_to_undef }.should raise_error(NoMethodError) + end + + it "does not remove a method defined in a super class when removed from a subclass" do + descendant = Class.new(ModuleSpecs::Ancestor) + ancestor = ModuleSpecs::Ancestor.new + ancestor.method_to_undef.should == 1 + + descendant.send :undef_method, :method_to_undef + + ancestor.method_to_undef.should == 1 + end +end + +describe "Module#undef_method with string" do + it "removes a method defined in a class" do + klass = Class.new do + def method_to_undef() 1 end + def another_method_to_undef() 1 end + end + x = klass.new + + x.another_method_to_undef.should == 1 + + klass.send :undef_method, 'another_method_to_undef' + + lambda { x.another_method_to_undef }.should raise_error(NoMethodError) + end + + it "removes a method defined in a super class" do + child_class = Class.new(ModuleSpecs::Parent) + child = child_class.new + child.another_method_to_undef.should == 1 + + child_class.send :undef_method, 'another_method_to_undef' + + lambda { child.another_method_to_undef }.should raise_error(NoMethodError) + end + + it "does not remove a method defined in a super class when removed from a subclass" do + descendant = Class.new(ModuleSpecs::Ancestor) + ancestor = ModuleSpecs::Ancestor.new + ancestor.another_method_to_undef.should == 1 + + descendant.send :undef_method, 'another_method_to_undef' + + ancestor.another_method_to_undef.should == 1 + end +end diff --git a/spec/rubyspec/core/mutex/lock_spec.rb b/spec/rubyspec/core/mutex/lock_spec.rb new file mode 100644 index 0000000000..98deabe056 --- /dev/null +++ b/spec/rubyspec/core/mutex/lock_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Mutex#lock" do + before :each do + ScratchPad.clear + end + + it "returns self" do + m = Mutex.new + m.lock.should == m + m.unlock + end + + it "waits if the lock is not available" do + m = Mutex.new + + m.lock + + th = Thread.new do + m.lock + ScratchPad.record :after_lock + end + + Thread.pass while th.status and th.status != "sleep" + + ScratchPad.recorded.should be_nil + m.unlock + th.join + ScratchPad.recorded.should == :after_lock + end + + # Unable to find a specific ticket but behavior change may be + # related to this ML thread. + it "raises a ThreadError when used recursively" do + m = Mutex.new + + th = Thread.new do + m.lock + m.lock + end + + lambda do + th.join + end.should raise_error(ThreadError) + end +end diff --git a/spec/rubyspec/core/mutex/locked_spec.rb b/spec/rubyspec/core/mutex/locked_spec.rb new file mode 100644 index 0000000000..7b92534be4 --- /dev/null +++ b/spec/rubyspec/core/mutex/locked_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Mutex#locked?" do + it "returns true if locked" do + m = Mutex.new + m.lock + m.locked?.should be_true + end + + it "returns false if unlocked" do + m = Mutex.new + m.locked?.should be_false + end + + it "returns the status of the lock" do + m1 = Mutex.new + m2 = Mutex.new + + m2.lock # hold th with only m1 locked + m1_locked = false + + th = Thread.new do + m1.lock + m1_locked = true + m2.lock + end + + Thread.pass until m1_locked + + m1.locked?.should be_true + m2.unlock # release th + th.join + # A Thread releases its locks upon termination + m1.locked?.should be_false + end +end diff --git a/spec/rubyspec/core/mutex/owned_spec.rb b/spec/rubyspec/core/mutex/owned_spec.rb new file mode 100644 index 0000000000..2e1c3f2481 --- /dev/null +++ b/spec/rubyspec/core/mutex/owned_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Mutex#owned?" do + describe "when unlocked" do + it "returns false" do + m = Mutex.new + m.owned?.should be_false + end + end + + describe "when locked by the current thread" do + it "returns true" do + m = Mutex.new + m.lock + m.owned?.should be_true + end + end + + describe "when locked by another thread" do + before :each do + @checked = false + end + + after :each do + @checked = true + @th.join + end + + it "returns false" do + m = Mutex.new + locked = false + + @th = Thread.new do + m.lock + locked = true + Thread.pass until @checked + end + + Thread.pass until locked + m.owned?.should be_false + end + end +end diff --git a/spec/rubyspec/core/mutex/sleep_spec.rb b/spec/rubyspec/core/mutex/sleep_spec.rb new file mode 100644 index 0000000000..a3fb86fba7 --- /dev/null +++ b/spec/rubyspec/core/mutex/sleep_spec.rb @@ -0,0 +1,74 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Mutex#sleep" do + describe "when not locked by the current thread" do + it "raises a ThreadError" do + m = Mutex.new + lambda { m.sleep }.should raise_error(ThreadError) + end + + it "raises an ArgumentError if passed a negative duration" do + m = Mutex.new + lambda { m.sleep(-0.1) }.should raise_error(ArgumentError) + lambda { m.sleep(-1) }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if passed a negative duration" do + m = Mutex.new + m.lock + lambda { m.sleep(-0.1) }.should raise_error(ArgumentError) + lambda { m.sleep(-1) }.should raise_error(ArgumentError) + end + + it "pauses execution for approximately the duration requested" do + m = Mutex.new + m.lock + duration = 0.1 + start = Time.now + m.sleep duration + (Time.now - start).should be_close(duration, 0.2) + end + + it "unlocks the mutex while sleeping" do + m = Mutex.new + locked = false + th = Thread.new { m.lock; locked = true; m.sleep } + Thread.pass until locked + Thread.pass while th.status and th.status != "sleep" + m.locked?.should be_false + th.run + th.join + end + + it "relocks the mutex when woken" do + m = Mutex.new + m.lock + m.sleep(0.01) + m.locked?.should be_true + end + + it "relocks the mutex when woken by an exception being raised" do + m = Mutex.new + locked = false + th = Thread.new do + m.lock + locked = true + begin + m.sleep + rescue Exception + m.locked? + end + end + Thread.pass until locked + Thread.pass while th.status and th.status != "sleep" + th.raise(Exception) + th.value.should be_true + end + + it "returns the rounded number of seconds asleep" do + m = Mutex.new + m.lock + m.sleep(0.01).should be_kind_of(Integer) + end +end diff --git a/spec/rubyspec/core/mutex/synchronize_spec.rb b/spec/rubyspec/core/mutex/synchronize_spec.rb new file mode 100644 index 0000000000..ec64aa60fa --- /dev/null +++ b/spec/rubyspec/core/mutex/synchronize_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Mutex#synchronize" do + it "wraps the lock/unlock pair in an ensure" do + m1 = Mutex.new + m2 = Mutex.new + m2.lock + synchronized = false + + th = Thread.new do + lambda do + m1.synchronize do + synchronized = true + m2.lock + raise Exception + end + end.should raise_error(Exception) + end + + Thread.pass until synchronized + + m1.locked?.should be_true + m2.unlock + th.join + m1.locked?.should be_false + end +end diff --git a/spec/rubyspec/core/mutex/try_lock_spec.rb b/spec/rubyspec/core/mutex/try_lock_spec.rb new file mode 100644 index 0000000000..3e875ff9ec --- /dev/null +++ b/spec/rubyspec/core/mutex/try_lock_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Mutex#try_lock" do + describe "when unlocked" do + it "returns true" do + m = Mutex.new + m.try_lock.should be_true + end + + it "locks the mutex" do + m = Mutex.new + m.try_lock + m.locked?.should be_true + end + end + + describe "when locked by the current thread" do + it "returns false" do + m = Mutex.new + m.lock + m.try_lock.should be_false + end + end + + describe "when locked by another thread" do + it "returns false" do + m = Mutex.new + m.lock + Thread.new { m.try_lock }.value.should be_false + end + end +end diff --git a/spec/rubyspec/core/mutex/unlock_spec.rb b/spec/rubyspec/core/mutex/unlock_spec.rb new file mode 100644 index 0000000000..4601fde634 --- /dev/null +++ b/spec/rubyspec/core/mutex/unlock_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Mutex#unlock" do + it "raises ThreadError unless Mutex is locked" do + mutex = Mutex.new + lambda { mutex.unlock }.should raise_error(ThreadError) + end + + it "raises ThreadError unless thread owns Mutex" do + mutex = Mutex.new + wait = Mutex.new + wait.lock + th = Thread.new do + mutex.lock + wait.lock + end + + # avoid race on mutex.lock + Thread.pass until mutex.locked? + Thread.pass while th.status and th.status != "sleep" + + lambda { mutex.unlock }.should raise_error(ThreadError) + + wait.unlock + th.join + end + + it "raises ThreadError if previously locking thread is gone" do + mutex = Mutex.new + th = Thread.new do + mutex.lock + end + + th.join + + lambda { mutex.unlock }.should raise_error(ThreadError) + end +end diff --git a/spec/rubyspec/core/nil/and_spec.rb b/spec/rubyspec/core/nil/and_spec.rb new file mode 100644 index 0000000000..96e0472661 --- /dev/null +++ b/spec/rubyspec/core/nil/and_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#&" do + it "returns false" do + (nil & nil).should == false + (nil & true).should == false + (nil & false).should == false + (nil & "").should == false + (nil & mock('x')).should == false + end +end diff --git a/spec/rubyspec/core/nil/inspect_spec.rb b/spec/rubyspec/core/nil/inspect_spec.rb new file mode 100644 index 0000000000..f7a5e7d1af --- /dev/null +++ b/spec/rubyspec/core/nil/inspect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#inspect" do + it "returns the string 'nil'" do + nil.inspect.should == "nil" + end +end diff --git a/spec/rubyspec/core/nil/nil_spec.rb b/spec/rubyspec/core/nil/nil_spec.rb new file mode 100644 index 0000000000..8369b21a66 --- /dev/null +++ b/spec/rubyspec/core/nil/nil_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#nil?" do + it "returns true" do + nil.nil?.should == true + end +end diff --git a/spec/rubyspec/core/nil/or_spec.rb b/spec/rubyspec/core/nil/or_spec.rb new file mode 100644 index 0000000000..2c5811b8c2 --- /dev/null +++ b/spec/rubyspec/core/nil/or_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#|" do + it "returns false if other is nil or false, otherwise true" do + (nil | nil).should == false + (nil | true).should == true + (nil | false).should == false + (nil | "").should == true + (nil | mock('x')).should == true + end +end diff --git a/spec/rubyspec/core/nil/rationalize_spec.rb b/spec/rubyspec/core/nil/rationalize_spec.rb new file mode 100644 index 0000000000..1292cfd250 --- /dev/null +++ b/spec/rubyspec/core/nil/rationalize_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#rationalize" do + it "returns 0/1" do + nil.rationalize.should == Rational(0, 1) + end + + it "ignores a single argument" do + nil.rationalize(0.1).should == Rational(0, 1) + end + + it "raises ArgumentError when passed more than one argument" do + lambda { nil.rationalize(0.1, 0.1) }.should raise_error(ArgumentError) + lambda { nil.rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/nil/to_a_spec.rb b/spec/rubyspec/core/nil/to_a_spec.rb new file mode 100644 index 0000000000..6febd88c40 --- /dev/null +++ b/spec/rubyspec/core/nil/to_a_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#to_a" do + it "returns an empty array" do + nil.to_a.should == [] + end +end diff --git a/spec/rubyspec/core/nil/to_c_spec.rb b/spec/rubyspec/core/nil/to_c_spec.rb new file mode 100644 index 0000000000..5b768b4afc --- /dev/null +++ b/spec/rubyspec/core/nil/to_c_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#to_c" do + it "returns Complex(0, 0)" do + nil.to_c.should eql(Complex(0, 0)) + end +end diff --git a/spec/rubyspec/core/nil/to_f_spec.rb b/spec/rubyspec/core/nil/to_f_spec.rb new file mode 100644 index 0000000000..d4890cd42b --- /dev/null +++ b/spec/rubyspec/core/nil/to_f_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#to_f" do + it "returns 0.0" do + nil.to_f.should == 0.0 + end + + it "does not cause NilClass to be coerced to Float" do + (0.0 == nil).should == false + end +end diff --git a/spec/rubyspec/core/nil/to_h_spec.rb b/spec/rubyspec/core/nil/to_h_spec.rb new file mode 100644 index 0000000000..5300802d19 --- /dev/null +++ b/spec/rubyspec/core/nil/to_h_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#to_h" do + it "returns an empty hash" do + nil.to_h.should == {} + nil.to_h.default.should == nil + end +end diff --git a/spec/rubyspec/core/nil/to_i_spec.rb b/spec/rubyspec/core/nil/to_i_spec.rb new file mode 100644 index 0000000000..91282822a8 --- /dev/null +++ b/spec/rubyspec/core/nil/to_i_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#to_i" do + it "returns 0" do + nil.to_i.should == 0 + end + + it "does not cause NilClass to be coerced to Fixnum" do + (0 == nil).should == false + end +end diff --git a/spec/rubyspec/core/nil/to_r_spec.rb b/spec/rubyspec/core/nil/to_r_spec.rb new file mode 100644 index 0000000000..efbd2f5540 --- /dev/null +++ b/spec/rubyspec/core/nil/to_r_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#to_r" do + it "returns 0/1" do + nil.to_r.should == Rational(0, 1) + end +end diff --git a/spec/rubyspec/core/nil/to_s_spec.rb b/spec/rubyspec/core/nil/to_s_spec.rb new file mode 100644 index 0000000000..935bb63a9b --- /dev/null +++ b/spec/rubyspec/core/nil/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#to_s" do + it "returns the string ''" do + nil.to_s.should == "" + end +end diff --git a/spec/rubyspec/core/nil/xor_spec.rb b/spec/rubyspec/core/nil/xor_spec.rb new file mode 100644 index 0000000000..7f2131e795 --- /dev/null +++ b/spec/rubyspec/core/nil/xor_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "NilClass#^" do + it "returns false if other is nil or false, otherwise true" do + (nil ^ nil).should == false + (nil ^ true).should == true + (nil ^ false).should == false + (nil ^ "").should == true + (nil ^ mock('x')).should == true + end +end diff --git a/spec/rubyspec/core/numeric/abs2_spec.rb b/spec/rubyspec/core/numeric/abs2_spec.rb new file mode 100644 index 0000000000..f1094845fb --- /dev/null +++ b/spec/rubyspec/core/numeric/abs2_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Numeric#abs2" do + before :each do + @numbers = [ + 0, + 0.0, + 1, + 20, + bignum_value, + 278202.292871, + 72829, + 3.333333333333, + 0.1, + infinity_value + ].map { |n| [-n, n] }.flatten + end + + it "returns the square of the absolute value of self" do + @numbers.each do |number| + number.abs2.should eql(number.abs ** 2) + end + end + + it "calls #* on self" do + number = mock_numeric('numeric') + number.should_receive(:*).and_return(:result) + number.abs2.should == :result + end + + it "returns NaN when self is NaN" do + nan_value.abs2.nan?.should be_true + end +end diff --git a/spec/rubyspec/core/numeric/abs_spec.rb b/spec/rubyspec/core/numeric/abs_spec.rb new file mode 100644 index 0000000000..4aa25359a2 --- /dev/null +++ b/spec/rubyspec/core/numeric/abs_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/abs', __FILE__) + +describe "Numeric#abs" do + it_behaves_like(:numeric_abs, :abs) +end diff --git a/spec/rubyspec/core/numeric/angle_spec.rb b/spec/rubyspec/core/numeric/angle_spec.rb new file mode 100644 index 0000000000..d7134168b3 --- /dev/null +++ b/spec/rubyspec/core/numeric/angle_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/arg', __FILE__) + +describe "Numeric#angle" do + it_behaves_like(:numeric_arg, :angle) +end diff --git a/spec/rubyspec/core/numeric/arg_spec.rb b/spec/rubyspec/core/numeric/arg_spec.rb new file mode 100644 index 0000000000..0729a29226 --- /dev/null +++ b/spec/rubyspec/core/numeric/arg_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/arg', __FILE__) + +describe "Numeric#arg" do + it_behaves_like(:numeric_arg, :arg) +end diff --git a/spec/rubyspec/core/numeric/ceil_spec.rb b/spec/rubyspec/core/numeric/ceil_spec.rb new file mode 100644 index 0000000000..6e7d8211fb --- /dev/null +++ b/spec/rubyspec/core/numeric/ceil_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#ceil" do + it "converts self to a Float (using #to_f) and returns the #ceil'ed result" do + o = mock_numeric("ceil") + o.should_receive(:to_f).and_return(1 + TOLERANCE) + o.ceil.should == 2 + + o2 = mock_numeric("ceil") + v = -1 - TOLERANCE + o2.should_receive(:to_f).and_return(v) + o2.ceil.should == -1 + end +end diff --git a/spec/rubyspec/core/numeric/coerce_spec.rb b/spec/rubyspec/core/numeric/coerce_spec.rb new file mode 100644 index 0000000000..9b6297d5da --- /dev/null +++ b/spec/rubyspec/core/numeric/coerce_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#coerce" do + before :each do + @obj = NumericSpecs::Subclass.new + @obj.should_receive(:to_f).any_number_of_times.and_return(10.5) + end + + it "returns [other, self] if self and other are instances of the same class" do + a = NumericSpecs::Subclass.new + b = NumericSpecs::Subclass.new + + a.coerce(b).should == [b, a] + end + + # I (emp) think that this behavior is actually a bug in MRI. It's here as documentation + # of the behavior until we find out if it's a bug. + quarantine! do + it "considers the presense of a metaclass when checking the class of the objects" do + a = NumericSpecs::Subclass.new + b = NumericSpecs::Subclass.new + + # inject a metaclass on a + class << a; true; end + + # watch it explode + lambda { a.coerce(b) }.should raise_error(TypeError) + end + end + + it "calls #to_f to convert other if self responds to #to_f" do + # Do not use NumericSpecs::Subclass here, because coerce checks the classes of the receiver + # and arguments before calling #to_f. + other = mock("numeric") + lambda { @obj.coerce(other) }.should raise_error(TypeError) + end + + it "returns [other.to_f, self.to_f] if self and other are instances of different classes" do + result = @obj.coerce(2.5) + result.should == [2.5, 10.5] + result.first.should be_kind_of(Float) + result.last.should be_kind_of(Float) + + result = @obj.coerce(3) + result.should == [3.0, 10.5] + result.first.should be_kind_of(Float) + result.last.should be_kind_of(Float) + + result = @obj.coerce("4.4") + result.should == [4.4, 10.5] + result.first.should be_kind_of(Float) + result.last.should be_kind_of(Float) + + result = @obj.coerce(bignum_value) + result.should == [bignum_value.to_f, 10.5] + result.first.should be_kind_of(Float) + result.last.should be_kind_of(Float) + end + + it "raises a TypeError when passed nil" do + lambda { @obj.coerce(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a boolean" do + lambda { @obj.coerce(false) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a Symbol" do + lambda { @obj.coerce(:symbol) }.should raise_error(TypeError) + end + + it "raises an ArgumentError when passed a String" do + lambda { @obj.coerce("test") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/numeric/comparison_spec.rb b/spec/rubyspec/core/numeric/comparison_spec.rb new file mode 100644 index 0000000000..59676c01ad --- /dev/null +++ b/spec/rubyspec/core/numeric/comparison_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#<=>" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "returns 0 if self equals other" do + (@obj <=> @obj).should == 0 + end + + it "returns nil if self does not equal other" do + (@obj <=> NumericSpecs::Subclass.new).should == nil + (@obj <=> 10).should == nil + (@obj <=> -3.5).should == nil + (@obj <=> bignum_value).should == nil + end + + describe "with subclasses of Numeric" do + before :each do + @a = NumericSpecs::Comparison.new + @b = NumericSpecs::Comparison.new + + ScratchPad.clear + end + + it "is called when instances are compared with #<" do + (@a < @b).should be_false + ScratchPad.recorded.should == :numeric_comparison + end + + it "is called when instances are compared with #<=" do + (@a <= @b).should be_false + ScratchPad.recorded.should == :numeric_comparison + end + + it "is called when instances are compared with #>" do + (@a > @b).should be_true + ScratchPad.recorded.should == :numeric_comparison + end + + it "is called when instances are compared with #>=" do + (@a >= @b).should be_true + ScratchPad.recorded.should == :numeric_comparison + end + end +end diff --git a/spec/rubyspec/core/numeric/conj_spec.rb b/spec/rubyspec/core/numeric/conj_spec.rb new file mode 100644 index 0000000000..8fa0fd9457 --- /dev/null +++ b/spec/rubyspec/core/numeric/conj_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/conj', __FILE__) + +describe "Numeric#conj" do + it_behaves_like(:numeric_conj, :conj) +end diff --git a/spec/rubyspec/core/numeric/conjugate_spec.rb b/spec/rubyspec/core/numeric/conjugate_spec.rb new file mode 100644 index 0000000000..f7e095514e --- /dev/null +++ b/spec/rubyspec/core/numeric/conjugate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/conj', __FILE__) + +describe "Numeric#conjugate" do + it_behaves_like(:numeric_conj, :conjugate) +end diff --git a/spec/rubyspec/core/numeric/denominator_spec.rb b/spec/rubyspec/core/numeric/denominator_spec.rb new file mode 100644 index 0000000000..3ae4530c18 --- /dev/null +++ b/spec/rubyspec/core/numeric/denominator_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Numeric#denominator" do + # The Numeric child classes override this method, so their behaviour is + # specified in the appropriate place + before :each do + @numbers = [ + 20, # Integer + 99999999**99, # Bignum + ] + end + + it "returns 1" do + @numbers.each {|number| number.denominator.should == 1} + end + + it "works with Numeric subclasses" do + rational = mock_numeric('rational') + rational.should_receive(:denominator).and_return(:denominator) + numeric = mock_numeric('numeric') + numeric.should_receive(:to_r).and_return(rational) + numeric.denominator.should == :denominator + end +end diff --git a/spec/rubyspec/core/numeric/div_spec.rb b/spec/rubyspec/core/numeric/div_spec.rb new file mode 100644 index 0000000000..f3f82c3ca4 --- /dev/null +++ b/spec/rubyspec/core/numeric/div_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#div" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "calls self#/ with other, then returns the #floor'ed result" do + result = mock("Numeric#div result") + result.should_receive(:floor).and_return(12) + @obj.should_receive(:/).with(10).and_return(result) + + @obj.div(10).should == 12 + end + + it "raises ZeroDivisionError for 0" do + lambda { @obj.div(0) }.should raise_error(ZeroDivisionError) + lambda { @obj.div(0.0) }.should raise_error(ZeroDivisionError) + lambda { @obj.div(Complex(0,0)) }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/core/numeric/divmod_spec.rb b/spec/rubyspec/core/numeric/divmod_spec.rb new file mode 100644 index 0000000000..5de2d86c77 --- /dev/null +++ b/spec/rubyspec/core/numeric/divmod_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#divmod" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "returns [quotient, modulus], with quotient being obtained as in Numeric#div then #floor and modulus being obtained by calling self#- with quotient * other" do + @obj.should_receive(:/).twice.with(10).and_return(13 - TOLERANCE, 13 - TOLERANCE) + @obj.should_receive(:-).with(120).and_return(3) + + @obj.divmod(10).should == [12, 3] + end +end diff --git a/spec/rubyspec/core/numeric/eql_spec.rb b/spec/rubyspec/core/numeric/eql_spec.rb new file mode 100644 index 0000000000..367a298a74 --- /dev/null +++ b/spec/rubyspec/core/numeric/eql_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#eql?" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "returns false if self's and other's types don't match" do + @obj.should_not eql(1) + @obj.should_not eql(-1.5) + @obj.should_not eql(bignum_value) + @obj.should_not eql(:sym) + end + + it "returns the result of calling self#== with other when self's and other's types match" do + other = NumericSpecs::Subclass.new + @obj.should_receive(:==).with(other).and_return("result", nil) + @obj.should eql(other) + @obj.should_not eql(other) + end +end diff --git a/spec/rubyspec/core/numeric/fdiv_spec.rb b/spec/rubyspec/core/numeric/fdiv_spec.rb new file mode 100644 index 0000000000..4b27382012 --- /dev/null +++ b/spec/rubyspec/core/numeric/fdiv_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quo', __FILE__) + +describe "Numeric#fdiv" do + it "coerces self with #to_f" do + numeric = mock_numeric('numeric') + numeric.should_receive(:to_f).and_return(3.0) + numeric.fdiv(0.5).should == 6.0 + end + + it "coerces other with #to_f" do + numeric = mock_numeric('numeric') + numeric.should_receive(:to_f).and_return(3.0) + 6.fdiv(numeric).should == 2.0 + end + + it "performs floating-point division" do + 3.fdiv(2).should == 1.5 + end + + it "returns a Float" do + bignum_value.fdiv(Float::MAX).should be_an_instance_of(Float) + end + + it "returns Infinity if other is 0" do + 8121.92821.fdiv(0).infinite?.should == 1 + end + + it "returns NaN if other is NaN" do + 3334.fdiv(nan_value).nan?.should be_true + end +end diff --git a/spec/rubyspec/core/numeric/fixtures/classes.rb b/spec/rubyspec/core/numeric/fixtures/classes.rb new file mode 100644 index 0000000000..1505584889 --- /dev/null +++ b/spec/rubyspec/core/numeric/fixtures/classes.rb @@ -0,0 +1,17 @@ +module NumericSpecs + class Comparison < Numeric + # This method is used because we cannot define + # singleton methods on subclasses of Numeric, + # which is needed for a.should_receive to work. + def <=>(other) + ScratchPad.record :numeric_comparison + 1 + end + end + + class Subclass < Numeric + # Allow methods to be mocked + def singleton_method_added(val) + end + end +end diff --git a/spec/rubyspec/core/numeric/floor_spec.rb b/spec/rubyspec/core/numeric/floor_spec.rb new file mode 100644 index 0000000000..c68ed0423a --- /dev/null +++ b/spec/rubyspec/core/numeric/floor_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#floor" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "converts self to a Float (using #to_f) and returns the #floor'ed result" do + @obj.should_receive(:to_f).and_return(2 - TOLERANCE, TOLERANCE - 2) + @obj.floor.should == 1 + @obj.floor.should == -2 + end +end diff --git a/spec/rubyspec/core/numeric/i_spec.rb b/spec/rubyspec/core/numeric/i_spec.rb new file mode 100644 index 0000000000..fae4fefe3d --- /dev/null +++ b/spec/rubyspec/core/numeric/i_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Numeric#i" do + it "returns a Complex object" do + 34.i.should be_an_instance_of(Complex) + end + + it "sets the real part to 0" do + 7342.i.real.should == 0 + end + + it "sets the imaginary part to self" do + 62.81.i.imag.should == 62.81 + end +end diff --git a/spec/rubyspec/core/numeric/imag_spec.rb b/spec/rubyspec/core/numeric/imag_spec.rb new file mode 100644 index 0000000000..a80e42d265 --- /dev/null +++ b/spec/rubyspec/core/numeric/imag_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/imag', __FILE__) + +describe "Numeric#imag" do + it_behaves_like(:numeric_imag, :imag) +end diff --git a/spec/rubyspec/core/numeric/imaginary_spec.rb b/spec/rubyspec/core/numeric/imaginary_spec.rb new file mode 100644 index 0000000000..41226569b3 --- /dev/null +++ b/spec/rubyspec/core/numeric/imaginary_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/imag', __FILE__) + +describe "Numeric#imaginary" do + it_behaves_like(:numeric_imag, :imaginary) +end diff --git a/spec/rubyspec/core/numeric/integer_spec.rb b/spec/rubyspec/core/numeric/integer_spec.rb new file mode 100644 index 0000000000..acff8eb830 --- /dev/null +++ b/spec/rubyspec/core/numeric/integer_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#integer?" do + it "returns false" do + NumericSpecs::Subclass.new.integer?.should == false + end +end diff --git a/spec/rubyspec/core/numeric/magnitude_spec.rb b/spec/rubyspec/core/numeric/magnitude_spec.rb new file mode 100644 index 0000000000..947ee69730 --- /dev/null +++ b/spec/rubyspec/core/numeric/magnitude_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/abs', __FILE__) + +describe "Numeric#magnitude" do + it_behaves_like(:numeric_abs, :magnitude) +end diff --git a/spec/rubyspec/core/numeric/modulo_spec.rb b/spec/rubyspec/core/numeric/modulo_spec.rb new file mode 100644 index 0000000000..a6ea7a8f14 --- /dev/null +++ b/spec/rubyspec/core/numeric/modulo_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :numeric_modulo_19, shared: true do + it "returns self - other * self.div(other)" do + s = mock_numeric('self') + o = mock_numeric('other') + n3 = mock_numeric('n3') + n4 = mock_numeric('n4') + n5 = mock_numeric('n5') + s.should_receive(:div).with(o).and_return(n3) + o.should_receive(:*).with(n3).and_return(n4) + s.should_receive(:-).with(n4).and_return(n5) + s.send(@method, o).should == n5 + end +end + +describe "Numeric#modulo" do + it_behaves_like :numeric_modulo_19, :modulo +end + +describe "Numeric#%" do + it_behaves_like :numeric_modulo_19, :% +end diff --git a/spec/rubyspec/core/numeric/negative_spec.rb b/spec/rubyspec/core/numeric/negative_spec.rb new file mode 100644 index 0000000000..27e5c65fe3 --- /dev/null +++ b/spec/rubyspec/core/numeric/negative_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.3" do + describe "Numeric#negative?" do + describe "on positive numbers" do + it "returns false" do + 1.negative?.should be_false + 0.1.negative?.should be_false + end + end + + describe "on zero" do + it "returns false" do + 0.negative?.should be_false + 0.0.negative?.should be_false + end + end + + describe "on negative numbers" do + it "returns true" do + -1.negative?.should be_true + -0.1.negative?.should be_true + end + end + end + + describe "Numeric#negative?" do + before(:each) do + @obj = NumericSpecs::Subclass.new + end + + it "returns true if self is less than 0" do + @obj.should_receive(:<).with(0).and_return(true) + @obj.negative?.should == true + end + + it "returns false if self is greater than 0" do + @obj.should_receive(:<).with(0).and_return(false) + @obj.negative?.should == false + end + end +end diff --git a/spec/rubyspec/core/numeric/nonzero_spec.rb b/spec/rubyspec/core/numeric/nonzero_spec.rb new file mode 100644 index 0000000000..55b7880d0e --- /dev/null +++ b/spec/rubyspec/core/numeric/nonzero_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#nonzero?" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "returns self if self#zero? is false" do + @obj.should_receive(:zero?).and_return(false) + @obj.nonzero?.should == @obj + end + + it "returns nil if self#zero? is true" do + @obj.should_receive(:zero?).and_return(true) + @obj.nonzero?.should == nil + end +end diff --git a/spec/rubyspec/core/numeric/numerator_spec.rb b/spec/rubyspec/core/numeric/numerator_spec.rb new file mode 100644 index 0000000000..4a20f8792a --- /dev/null +++ b/spec/rubyspec/core/numeric/numerator_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Numeric#numerator" do + before :all do + @numbers = [ + 0, + 29871, + 99999999999999**99, + -72628191273, + 29282.2827, + -2927.00091, + 0.0, + 12.0, + Float::MAX, + ] + end + + # This isn't entirely true, as NaN.numerator works, whereas + # Rational(NaN) raises an exception, but we test this in Float#numerator + it "converts self to a Rational object then returns its numerator" do + @numbers.each do |number| + number.numerator.should == Rational(number).numerator + end + end + + it "works with Numeric subclasses" do + rational = mock_numeric('rational') + rational.should_receive(:numerator).and_return(:numerator) + numeric = mock_numeric('numeric') + numeric.should_receive(:to_r).and_return(rational) + numeric.numerator.should == :numerator + end +end diff --git a/spec/rubyspec/core/numeric/numeric_spec.rb b/spec/rubyspec/core/numeric/numeric_spec.rb new file mode 100644 index 0000000000..9429ab55a0 --- /dev/null +++ b/spec/rubyspec/core/numeric/numeric_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Numeric" do + it "includes Comparable" do + Numeric.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/numeric/phase_spec.rb b/spec/rubyspec/core/numeric/phase_spec.rb new file mode 100644 index 0000000000..7c408db83b --- /dev/null +++ b/spec/rubyspec/core/numeric/phase_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/arg', __FILE__) + +describe "Numeric#phase" do + it_behaves_like(:numeric_arg, :phase) +end diff --git a/spec/rubyspec/core/numeric/polar_spec.rb b/spec/rubyspec/core/numeric/polar_spec.rb new file mode 100644 index 0000000000..5492483215 --- /dev/null +++ b/spec/rubyspec/core/numeric/polar_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/polar', __FILE__) + +describe "Numeric#polar" do + it_behaves_like(:numeric_polar, :polar) +end diff --git a/spec/rubyspec/core/numeric/positive_spec.rb b/spec/rubyspec/core/numeric/positive_spec.rb new file mode 100644 index 0000000000..516de7db89 --- /dev/null +++ b/spec/rubyspec/core/numeric/positive_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +ruby_version_is "2.3" do + describe "Numeric#positive?" do + describe "on positive numbers" do + it "returns true" do + 1.positive?.should be_true + 0.1.positive?.should be_true + end + end + + describe "on zero" do + it "returns false" do + 0.positive?.should be_false + 0.0.positive?.should be_false + end + end + + describe "on negative numbers" do + it "returns false" do + -1.positive?.should be_false + -0.1.positive?.should be_false + end + end + end + + describe "Numeric#positive?" do + before(:each) do + @obj = NumericSpecs::Subclass.new + end + + it "returns true if self is greater than 0" do + @obj.should_receive(:>).with(0).and_return(true) + @obj.positive?.should == true + end + + it "returns false if self is less than 0" do + @obj.should_receive(:>).with(0).and_return(false) + @obj.positive?.should == false + end + end +end diff --git a/spec/rubyspec/core/numeric/quo_spec.rb b/spec/rubyspec/core/numeric/quo_spec.rb new file mode 100644 index 0000000000..24c752aac1 --- /dev/null +++ b/spec/rubyspec/core/numeric/quo_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/quo', __FILE__) + +describe "Numeric#quo" do + it "returns the result of self divided by the given Integer as a Rational" do + 5.quo(2).should eql(Rational(5,2)) + end + + it "returns the result of self divided by the given Float as a Float" do + 2.quo(2.5).should eql(0.8) + end + + it "returns the result of self divided by the given Bignum as a Float" do + 45.quo(bignum_value).should be_close(1.04773789668636e-08, TOLERANCE) + end + + it "raises a ZeroDivisionError when the given Integer is 0" do + lambda { 0.quo(0) }.should raise_error(ZeroDivisionError) + lambda { 10.quo(0) }.should raise_error(ZeroDivisionError) + lambda { -10.quo(0) }.should raise_error(ZeroDivisionError) + lambda { bignum_value.quo(0) }.should raise_error(ZeroDivisionError) + lambda { -bignum_value.quo(0) }.should raise_error(ZeroDivisionError) + end + + it "calls #to_r to convert the object to a Rational" do + obj = NumericSpecs::Subclass.new + obj.should_receive(:to_r).and_return(Rational(1)) + + obj.quo(19).should == Rational(1, 19) + end + + it "raises a TypeError of #to_r does not return a Rational" do + obj = NumericSpecs::Subclass.new + obj.should_receive(:to_r).and_return(1) + + lambda { obj.quo(19) }.should raise_error(TypeError) + end + + it "raises a TypeError when given a non-Integer" do + lambda { + (obj = mock('x')).should_not_receive(:to_int) + 13.quo(obj) + }.should raise_error(TypeError) + lambda { 13.quo("10") }.should raise_error(TypeError) + lambda { 13.quo(:symbol) }.should raise_error(TypeError) + end + + it "returns the result of calling self#/ with other" do + obj = NumericSpecs::Subclass.new + obj.should_receive(:to_r).and_return(19.quo(20)) + + obj.quo(19).should == 1.quo(20) + end +end diff --git a/spec/rubyspec/core/numeric/real_spec.rb b/spec/rubyspec/core/numeric/real_spec.rb new file mode 100644 index 0000000000..3e34410155 --- /dev/null +++ b/spec/rubyspec/core/numeric/real_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/numeric/real', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#real" do + it_behaves_like(:numeric_real, :real) +end + +describe "Numeric#real?" do + it "returns true" do + NumericSpecs::Subclass.new.real?.should == true + end +end diff --git a/spec/rubyspec/core/numeric/rect_spec.rb b/spec/rubyspec/core/numeric/rect_spec.rb new file mode 100644 index 0000000000..88d5ee3881 --- /dev/null +++ b/spec/rubyspec/core/numeric/rect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rect', __FILE__) + +describe "Numeric#rect" do + it_behaves_like(:numeric_rect, :rect) +end diff --git a/spec/rubyspec/core/numeric/rectangular_spec.rb b/spec/rubyspec/core/numeric/rectangular_spec.rb new file mode 100644 index 0000000000..b34100ca74 --- /dev/null +++ b/spec/rubyspec/core/numeric/rectangular_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rect', __FILE__) + +describe "Numeric#rectangular" do + it_behaves_like(:numeric_rect, :rectangular) +end diff --git a/spec/rubyspec/core/numeric/remainder_spec.rb b/spec/rubyspec/core/numeric/remainder_spec.rb new file mode 100644 index 0000000000..6d26d39669 --- /dev/null +++ b/spec/rubyspec/core/numeric/remainder_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#remainder" do + before :each do + @obj = NumericSpecs::Subclass.new + @result = mock("Numeric#% result") + @other = mock("Passed Object") + end + + it "returns the result of calling self#% with other if self is 0" do + @obj.should_receive(:%).with(@other).and_return(@result) + @result.should_receive(:==).with(0).and_return(true) + + @obj.remainder(@other).should equal(@result) + end + + it "returns the result of calling self#% with other if self and other are greater than 0" do + @obj.should_receive(:%).with(@other).and_return(@result) + @result.should_receive(:==).with(0).and_return(false) + + @obj.should_receive(:<).with(0).and_return(false) + + @obj.should_receive(:>).with(0).and_return(true) + @other.should_receive(:<).with(0).and_return(false) + + @obj.remainder(@other).should equal(@result) + end + + it "returns the result of calling self#% with other if self and other are less than 0" do + @obj.should_receive(:%).with(@other).and_return(@result) + @result.should_receive(:==).with(0).and_return(false) + + @obj.should_receive(:<).with(0).and_return(true) + @other.should_receive(:>).with(0).and_return(false) + + @obj.should_receive(:>).with(0).and_return(false) + + @obj.remainder(@other).should equal(@result) + end + + it "returns the result of calling self#% with other - other if self is greater than 0 and other is less than 0" do + @obj.should_receive(:%).with(@other).and_return(@result) + @result.should_receive(:==).with(0).and_return(false) + + @obj.should_receive(:<).with(0).and_return(false) + + @obj.should_receive(:>).with(0).and_return(true) + @other.should_receive(:<).with(0).and_return(true) + + @result.should_receive(:-).with(@other).and_return(:result) + + @obj.remainder(@other).should == :result + end + + it "returns the result of calling self#% with other - other if self is less than 0 and other is greater than 0" do + @obj.should_receive(:%).with(@other).and_return(@result) + @result.should_receive(:==).with(0).and_return(false) + + @obj.should_receive(:<).with(0).and_return(true) + @other.should_receive(:>).with(0).and_return(true) + + @result.should_receive(:-).with(@other).and_return(:result) + + @obj.remainder(@other).should == :result + end +end diff --git a/spec/rubyspec/core/numeric/round_spec.rb b/spec/rubyspec/core/numeric/round_spec.rb new file mode 100644 index 0000000000..6e79decfa0 --- /dev/null +++ b/spec/rubyspec/core/numeric/round_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#round" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "converts self to a Float (using #to_f) and returns the #round'ed result" do + @obj.should_receive(:to_f).and_return(2 - TOLERANCE, TOLERANCE - 2) + @obj.round.should == 2 + @obj.round.should == -2 + end +end diff --git a/spec/rubyspec/core/numeric/shared/abs.rb b/spec/rubyspec/core/numeric/shared/abs.rb new file mode 100644 index 0000000000..406c9f3981 --- /dev/null +++ b/spec/rubyspec/core/numeric/shared/abs.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :numeric_abs, shared: true do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "returns self when self is greater than 0" do + @obj.should_receive(:<).with(0).and_return(false) + @obj.send(@method).should == @obj + end + + it "returns self\#@- when self is less than 0" do + @obj.should_receive(:<).with(0).and_return(true) + @obj.should_receive(:-@).and_return(:absolute_value) + @obj.send(@method).should == :absolute_value + end +end diff --git a/spec/rubyspec/core/numeric/shared/quo.rb b/spec/rubyspec/core/numeric/shared/quo.rb new file mode 100644 index 0000000000..2392636fe7 --- /dev/null +++ b/spec/rubyspec/core/numeric/shared/quo.rb @@ -0,0 +1,7 @@ +describe :numeric_quo_18, shared: true do + it "returns the result of calling self#/ with other" do + obj = mock_numeric('numeric') + obj.should_receive(:/).with(19).and_return(:result) + obj.send(@method, 19).should == :result + end +end diff --git a/spec/rubyspec/core/numeric/shared/rect.rb b/spec/rubyspec/core/numeric/shared/rect.rb new file mode 100644 index 0000000000..cda5fede7f --- /dev/null +++ b/spec/rubyspec/core/numeric/shared/rect.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :numeric_rect, shared: true do + before :each do + @numbers = [ + 20, # Integer + 398.72, # Float + Rational(3, 4), # Rational + 99999999**99, # Bignum + infinity_value, + nan_value + ] + end + + it "returns an Array" do + @numbers.each do |number| + number.send(@method).should be_an_instance_of(Array) + end + end + + it "returns a two-element Array" do + @numbers.each do |number| + number.send(@method).size.should == 2 + end + end + + it "returns self as the first element" do + @numbers.each do |number| + if Float === number and number.nan? + number.send(@method).first.nan?.should be_true + else + number.send(@method).first.should == number + end + end + end + + it "returns 0 as the last element" do + @numbers.each do |number| + number.send(@method).last.should == 0 + end + end + + it "raises an ArgumentError if given any arguments" do + @numbers.each do |number| + lambda { number.send(@method, number) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/numeric/shared/step.rb b/spec/rubyspec/core/numeric/shared/step.rb new file mode 100644 index 0000000000..6898a9ff3c --- /dev/null +++ b/spec/rubyspec/core/numeric/shared/step.rb @@ -0,0 +1,413 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +# Describes Numeric#step shared specs between different argument styles. +# To be able to do it, the @step_args var must contain a Proc that transforms +# the step call arguments passed as positional arguments to the style of +# arguments pretended to test. +describe :numeric_step, :shared => true do + before :each do + ScratchPad.record [] + @prc = lambda { |x| ScratchPad << x } + end + + it "defaults to step = 1" do + 1.send(@method, *@step_args.call(5), &@prc) + ScratchPad.recorded.should eql [1, 2, 3, 4, 5] + end + + describe "when self, stop and step are Fixnums" do + it "yields only Fixnums" do + 1.send(@method, *@step_args.call(5, 1)) { |x| x.should be_an_instance_of(Fixnum) } + end + + describe "with a positive step" do + it "yields while increasing self by step until stop is reached" do + 1.send(@method, *@step_args.call(5, 1), &@prc) + ScratchPad.recorded.should eql [1, 2, 3, 4, 5] + end + + it "yields once when self equals stop" do + 1.send(@method, *@step_args.call(1, 1), &@prc) + ScratchPad.recorded.should eql [1] + end + + it "does not yield when self is greater than stop" do + 2.send(@method, *@step_args.call(1, 1), &@prc) + ScratchPad.recorded.should eql [] + end + end + + describe "with a negative step" do + it "yields while decreasing self by step until stop is reached" do + 5.send(@method, *@step_args.call(1, -1), &@prc) + ScratchPad.recorded.should eql [5, 4, 3, 2, 1] + end + + it "yields once when self equals stop" do + 5.send(@method, *@step_args.call(5, -1), &@prc) + ScratchPad.recorded.should eql [5] + end + + it "does not yield when self is less than stop" do + 1.send(@method, *@step_args.call(5, -1), &@prc) + ScratchPad.recorded.should == [] + end + end + end + + describe "when at least one of self, stop or step is a Float" do + it "yields Floats even if only self is a Float" do + 1.5.send(@method, *@step_args.call(5, 1)) { |x| x.should be_an_instance_of(Float) } + end + + it "yields Floats even if only stop is a Float" do + 1.send(@method, *@step_args.call(5.0, 1)) { |x| x.should be_an_instance_of(Float) } + end + + it "yields Floats even if only step is a Float" do + 1.send(@method, *@step_args.call(5, 1.0)) { |x| x.should be_an_instance_of(Float) } + end + + describe "with a positive step" do + it "yields while increasing self by step while < stop" do + 1.5.send(@method, *@step_args.call(5, 1), &@prc) + ScratchPad.recorded.should eql [1.5, 2.5, 3.5, 4.5] + end + + it "yields once when self equals stop" do + 1.5.send(@method, *@step_args.call(1.5, 1), &@prc) + ScratchPad.recorded.should eql [1.5] + end + + it "does not yield when self is greater than stop" do + 2.5.send(@method, *@step_args.call(1.5, 1), &@prc) + ScratchPad.recorded.should == [] + end + + it "is careful about not yielding a value greater than limit" do + # As 9*1.3+1.0 == 12.700000000000001 > 12.7, we test: + 1.0.send(@method, *@step_args.call(12.7, 1.3), &@prc) + ScratchPad.recorded.should eql [1.0, 2.3, 3.6, 4.9, 6.2, 7.5, 8.8, 10.1, 11.4, 12.7] + end + end + + describe "with a negative step" do + it "yields while decreasing self by step while self > stop" do + 5.send(@method, *@step_args.call(1.5, -1), &@prc) + ScratchPad.recorded.should eql [5.0, 4.0, 3.0, 2.0] + end + + it "yields once when self equals stop" do + 1.5.send(@method, *@step_args.call(1.5, -1), &@prc) + ScratchPad.recorded.should eql [1.5] + end + + it "does not yield when self is less than stop" do + 1.send(@method, *@step_args.call(5, -1.5), &@prc) + ScratchPad.recorded.should == [] + end + + it "is careful about not yielding a value smaller than limit" do + # As -9*1.3-1.0 == -12.700000000000001 < -12.7, we test: + -1.0.send(@method, *@step_args.call(-12.7, -1.3), &@prc) + ScratchPad.recorded.should eql [-1.0, -2.3, -3.6, -4.9, -6.2, -7.5, -8.8, -10.1, -11.4, -12.7] + end + end + + describe "with a positive Infinity step" do + it "yields once if self < stop" do + 42.send(@method, *@step_args.call(100, infinity_value), &@prc) + ScratchPad.recorded.should eql [42.0] + end + + it "yields once when stop is Infinity" do + 42.send(@method, *@step_args.call(infinity_value, infinity_value), &@prc) + ScratchPad.recorded.should eql [42.0] + end + + it "yields once when self equals stop" do + 42.send(@method, *@step_args.call(42, infinity_value), &@prc) + ScratchPad.recorded.should eql [42.0] + end + + it "yields once when self and stop are Infinity" do + (infinity_value).send(@method, *@step_args.call(infinity_value, infinity_value), &@prc) + ScratchPad.recorded.should == [infinity_value] + end + + it "does not yield when self > stop" do + 100.send(@method, *@step_args.call(42, infinity_value), &@prc) + ScratchPad.recorded.should == [] + end + + it "does not yield when stop is -Infinity" do + 42.send(@method, *@step_args.call(-infinity_value, infinity_value), &@prc) + ScratchPad.recorded.should == [] + end + end + + describe "with a negative Infinity step" do + it "yields once if self > stop" do + 42.send(@method, *@step_args.call(6, -infinity_value), &@prc) + ScratchPad.recorded.should eql [42.0] + end + + it "yields once if stop is -Infinity" do + 42.send(@method, *@step_args.call(-infinity_value, -infinity_value), &@prc) + ScratchPad.recorded.should eql [42.0] + end + + it "yields once when self equals stop" do + 42.send(@method, *@step_args.call(42, -infinity_value), &@prc) + ScratchPad.recorded.should eql [42.0] + end + + it "yields once when self and stop are Infinity" do + (infinity_value).send(@method, *@step_args.call(infinity_value, -infinity_value), &@prc) + ScratchPad.recorded.should == [infinity_value] + end + + it "does not yield when self > stop" do + 42.send(@method, *@step_args.call(100, -infinity_value), &@prc) + ScratchPad.recorded.should == [] + end + + it "does not yield when stop is Infinity" do + 42.send(@method, *@step_args.call(infinity_value, -infinity_value), &@prc) + ScratchPad.recorded.should == [] + end + end + + describe "with a Infinity stop and a positive step" do + it "does not yield when self is infinity" do + (infinity_value).send(@method, *@step_args.call(infinity_value, 1), &@prc) + ScratchPad.recorded.should == [] + end + end + + describe "with a Infinity stop and a negative step" do + it "does not yield when self is negative infinity" do + (-infinity_value).send(@method, *@step_args.call(infinity_value, -1), &@prc) + ScratchPad.recorded.should == [] + end + + it "does not yield when self is positive infinity" do + infinity_value.send(@method, *@step_args.call(infinity_value, -1), &@prc) + ScratchPad.recorded.should == [] + end + end + + describe "with a negative Infinity stop and a positive step" do + it "does not yield when self is negative infinity" do + (-infinity_value).send(@method, *@step_args.call(-infinity_value, 1), &@prc) + ScratchPad.recorded.should == [] + end + end + + describe "with a negative Infinity stop and a negative step" do + it "does not yield when self is negative infinity" do + (-infinity_value).send(@method, *@step_args.call(-infinity_value, -1), &@prc) + ScratchPad.recorded.should == [] + end + end + + end + + describe "when step is a String" do + error = nil + ruby_version_is ""..."2.4" do + error = ArgumentError + end + ruby_version_is "2.4"..."2.5" do + error = TypeError + end + ruby_version_is "2.5" do + error = ArgumentError + end + + describe "with self and stop as Fixnums" do + it "raises an #{error} when step is a numeric representation" do + lambda { 1.send(@method, *@step_args.call(5, "1")) {} }.should raise_error(error) + lambda { 1.send(@method, *@step_args.call(5, "0.1")) {} }.should raise_error(error) + lambda { 1.send(@method, *@step_args.call(5, "1/3")) {} }.should raise_error(error) + end + it "raises an #{error} with step as an alphanumeric string" do + lambda { 1.send(@method, *@step_args.call(5, "foo")) {} }.should raise_error(error) + end + end + + describe "with self and stop as Floats" do + it "raises an #{error} when step is a numeric representation" do + lambda { 1.1.send(@method, *@step_args.call(5.1, "1")) {} }.should raise_error(error) + lambda { 1.1.send(@method, *@step_args.call(5.1, "0.1")) {} }.should raise_error(error) + lambda { 1.1.send(@method, *@step_args.call(5.1, "1/3")) {} }.should raise_error(error) + end + it "raises an #{error} with step as an alphanumeric string" do + lambda { 1.1.send(@method, *@step_args.call(5.1, "foo")) {} }.should raise_error(error) + end + end + end + + it "does not rescue ArgumentError exceptions" do + lambda { 1.send(@method, *@step_args.call(2)) { raise ArgumentError, "" }}.should raise_error(ArgumentError) + end + + it "does not rescue TypeError exceptions" do + lambda { 1.send(@method, *@step_args.call(2)) { raise TypeError, "" } }.should raise_error(TypeError) + end + + describe "when no block is given" do + it "returns an Enumerator when step is 0" do + 1.send(@method, *@step_args.call(2, 0)).should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator when not passed a block and self > stop" do + 1.send(@method, *@step_args.call(0, 2)).should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator when not passed a block and self < stop" do + 1.send(@method, *@step_args.call(2, 3)).should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator that uses the given step" do + 0.send(@method, *@step_args.call(5, 2)).to_a.should eql [0, 2, 4] + end + + describe "when step is a String" do + describe "with self and stop as Fixnums" do + it "returns an Enumerator" do + 1.send(@method, *@step_args.call(5, "foo")).should be_an_instance_of(Enumerator) + end + end + + describe "with self and stop as Floats" do + it "returns an Enumerator" do + 1.1.send(@method, *@step_args.call(5.1, "foo")).should be_an_instance_of(Enumerator) + end + end + end + + describe "returned Enumerator" do + describe "size" do + describe "when step is a String" do + error = nil + ruby_version_is ""..."2.4" do + error = ArgumentError + end + ruby_version_is "2.4"..."2.5" do + error = TypeError + end + ruby_version_is "2.5" do + error = ArgumentError + end + + describe "with self and stop as Fixnums" do + it "raises an #{error} when step is a numeric representation" do + lambda { 1.send(@method, *@step_args.call(5, "1")).size }.should raise_error(error) + lambda { 1.send(@method, *@step_args.call(5, "0.1")).size }.should raise_error(error) + lambda { 1.send(@method, *@step_args.call(5, "1/3")).size }.should raise_error(error) + end + it "raises an #{error} with step as an alphanumeric string" do + lambda { 1.send(@method, *@step_args.call(5, "foo")).size }.should raise_error(error) + end + end + + describe "with self and stop as Floats" do + it "raises an #{error} when step is a numeric representation" do + lambda { 1.1.send(@method, *@step_args.call(5.1, "1")).size }.should raise_error(error) + lambda { 1.1.send(@method, *@step_args.call(5.1, "0.1")).size }.should raise_error(error) + lambda { 1.1.send(@method, *@step_args.call(5.1, "1/3")).size }.should raise_error(error) + end + it "raises an #{error} with step as an alphanumeric string" do + lambda { 1.1.send(@method, *@step_args.call(5.1, "foo")).size }.should raise_error(error) + end + end + end + + describe "when self, stop and step are Fixnums and step is positive" do + it "returns the difference between self and stop divided by the number of steps" do + 5.send(@method, *@step_args.call(10, 11)).size.should == 1 + 5.send(@method, *@step_args.call(10, 6)).size.should == 1 + 5.send(@method, *@step_args.call(10, 5)).size.should == 2 + 5.send(@method, *@step_args.call(10, 4)).size.should == 2 + 5.send(@method, *@step_args.call(10, 2)).size.should == 3 + 5.send(@method, *@step_args.call(10, 1)).size.should == 6 + 5.send(@method, *@step_args.call(10)).size.should == 6 + 10.send(@method, *@step_args.call(10, 1)).size.should == 1 + end + + it "returns 0 if value > limit" do + 11.send(@method, *@step_args.call(10, 1)).size.should == 0 + end + end + + describe "when self, stop and step are Fixnums and step is negative" do + it "returns the difference between self and stop divided by the number of steps" do + 10.send(@method, *@step_args.call(5, -11)).size.should == 1 + 10.send(@method, *@step_args.call(5, -6)).size.should == 1 + 10.send(@method, *@step_args.call(5, -5)).size.should == 2 + 10.send(@method, *@step_args.call(5, -4)).size.should == 2 + 10.send(@method, *@step_args.call(5, -2)).size.should == 3 + 10.send(@method, *@step_args.call(5, -1)).size.should == 6 + 10.send(@method, *@step_args.call(10, -1)).size.should == 1 + end + + it "returns 0 if value < limit" do + 10.send(@method, *@step_args.call(11, -1)).size.should == 0 + end + end + + describe "when self, stop or step is a Float" do + describe "and step is positive" do + it "returns the difference between self and stop divided by the number of steps" do + 5.send(@method, *@step_args.call(10, 11.0)).size.should == 1 + 5.send(@method, *@step_args.call(10, 6.0)).size.should == 1 + 5.send(@method, *@step_args.call(10, 5.0)).size.should == 2 + 5.send(@method, *@step_args.call(10, 4.0)).size.should == 2 + 5.send(@method, *@step_args.call(10, 2.0)).size.should == 3 + 5.send(@method, *@step_args.call(10, 0.5)).size.should == 11 + 5.send(@method, *@step_args.call(10, 1.0)).size.should == 6 + 5.send(@method, *@step_args.call(10.5)).size.should == 6 + 10.send(@method, *@step_args.call(10, 1.0)).size.should == 1 + end + + it "returns 0 if value > limit" do + 10.send(@method, *@step_args.call(5.5)).size.should == 0 + 11.send(@method, *@step_args.call(10, 1.0)).size.should == 0 + 11.send(@method, *@step_args.call(10, 1.5)).size.should == 0 + 10.send(@method, *@step_args.call(5, infinity_value)).size.should == 0 + end + + it "returns 1 if step is infinity_value" do + 5.send(@method, *@step_args.call(10, infinity_value)).size.should == 1 + end + end + + describe "and step is negative" do + it "returns the difference between self and stop divided by the number of steps" do + 10.send(@method, *@step_args.call(5, -11.0)).size.should == 1 + 10.send(@method, *@step_args.call(5, -6.0)).size.should == 1 + 10.send(@method, *@step_args.call(5, -5.0)).size.should == 2 + 10.send(@method, *@step_args.call(5, -4.0)).size.should == 2 + 10.send(@method, *@step_args.call(5, -2.0)).size.should == 3 + 10.send(@method, *@step_args.call(5, -0.5)).size.should == 11 + 10.send(@method, *@step_args.call(5, -1.0)).size.should == 6 + 10.send(@method, *@step_args.call(10, -1.0)).size.should == 1 + end + + it "returns 0 if value < limit" do + 10.send(@method, *@step_args.call(11, -1.0)).size.should == 0 + 10.send(@method, *@step_args.call(11, -1.5)).size.should == 0 + 5.send(@method, *@step_args.call(10, -infinity_value)).size.should == 0 + end + + it "returns 1 if step is infinity_value" do + 10.send(@method, *@step_args.call(5, -infinity_value)).size.should == 1 + end + end + end + end + end + end +end diff --git a/spec/rubyspec/core/numeric/singleton_method_added_spec.rb b/spec/rubyspec/core/numeric/singleton_method_added_spec.rb new file mode 100644 index 0000000000..a650d286f2 --- /dev/null +++ b/spec/rubyspec/core/numeric/singleton_method_added_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#singleton_method_added" do + before :all do + class ::NumericSpecs::Subclass + # We want restore default Numeric behaviour for this particular test + remove_method :singleton_method_added + end + end + + after :all do + class ::NumericSpecs::Subclass + # Allow mocking methods again + def singleton_method_added(val) + end + end + end + + it "raises a TypeError when trying to define a singleton method on a Numeric" do + lambda do + a = NumericSpecs::Subclass.new + def a.test; end + end.should raise_error(TypeError) + + lambda do + a = 1 + def a.test; end + end.should raise_error(TypeError) + + lambda do + a = 1.5 + def a.test; end + end.should raise_error(TypeError) + + lambda do + a = bignum_value + def a.test; end + end.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/numeric/step_spec.rb b/spec/rubyspec/core/numeric/step_spec.rb new file mode 100644 index 0000000000..2c3237e194 --- /dev/null +++ b/spec/rubyspec/core/numeric/step_spec.rb @@ -0,0 +1,151 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/step', __FILE__) + +describe "Numeric#step" do + + describe 'with positional args' do + it "raises an ArgumentError when step is 0" do + lambda { 1.step(5, 0) {} }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when step is 0.0" do + lambda { 1.step(2, 0.0) {} }.should raise_error(ArgumentError) + end + + before :all do + # This lambda definition limits to return the arguments it receives. + # It's needed to test numeric_step behaviour with positional arguments. + @step_args = ->(*args) { args } + end + + it_behaves_like :numeric_step, :step + + describe "when no block is given" do + it "returns an Enumerator when step is 0" do + 1.step(5, 0).should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator when step is 0.0" do + 1.step(2, 0.0).should be_an_instance_of(Enumerator) + end + + describe "returned Enumerator" do + describe "size" do + it "raises an ArgumentError when step is 0" do + enum = 1.step(5, 0) + lambda { enum.size }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when step is 0.0" do + enum = 1.step(2, 0.0) + lambda { enum.size }.should raise_error(ArgumentError) + end + end + end + end + + end + + describe 'with keyword arguments' do + it "doesn't raise an error when step is 0" do + lambda { 1.step(to: 5, by: 0) { break } }.should_not raise_error + end + + it "doesn't raise an error when step is 0.0" do + lambda { 1.step(to: 2, by: 0.0) { break } }.should_not raise_error + end + + it "should loop over self when step is 0 or 0.0" do + 1.step(to: 2, by: 0.0).take(5).should eql [1.0, 1.0, 1.0, 1.0, 1.0] + 1.step(to: 2, by: 0).take(5).should eql [1, 1, 1, 1, 1] + 1.1.step(to: 2, by: 0).take(5).should eql [1.1, 1.1, 1.1, 1.1, 1.1] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "should return infinity_value when step is 0" do + 1.step(to: 5, by: 0).size.should == infinity_value + end + + it "should return infinity_value when step is 0.0" do + 1.step(to: 2, by: 0.0).size.should == infinity_value + end + + it "should return infinity_value when the limit is Float::INFINITY" do + 1.step(to: Float::INFINITY, by: 42).size.should == infinity_value + end + + it "should return 1 when the both limit and step are Float::INFINITY" do + 1.step(to: Float::INFINITY, by: Float::INFINITY).size.should == 1 + end + end + end + end + + before :all do + # This lambda transforms a positional step method args into + # keyword arguments. + # It's needed to test numeric_step behaviour with keyword arguments. + @step_args = ->(*args) do + kw_args = {to: args[0]} + kw_args[:by] = args[1] if args.size == 2 + [kw_args] + end + end + it_behaves_like :numeric_step, :step + end + + describe 'with mixed arguments' do + it "doesn't raise an error when step is 0" do + lambda { 1.step(5, by: 0) { break } }.should_not raise_error + end + + it "doesn't raise an error when step is 0.0" do + lambda { 1.step(2, by: 0.0) { break } }.should_not raise_error + end + + it "raises a ArgumentError when limit and to are defined" do + lambda { 1.step(5, 1, to: 5) { break } }.should raise_error(ArgumentError) + end + + it "raises a ArgumentError when step and by are defined" do + lambda { 1.step(5, 1, by: 5) { break } }.should raise_error(ArgumentError) + end + + it "should loop over self when step is 0 or 0.0" do + 1.step(2, by: 0.0).take(5).should eql [1.0, 1.0, 1.0, 1.0, 1.0] + 1.step(2, by: 0).take(5).should eql [1, 1, 1, 1, 1] + 1.1.step(2, by: 0).take(5).should eql [1.1, 1.1, 1.1, 1.1, 1.1] + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "should return infinity_value when step is 0" do + 1.step(5, by: 0).size.should == infinity_value + end + + it "should return infinity_value when step is 0.0" do + 1.step(2, by: 0.0).size.should == infinity_value + end + end + end + end + before :all do + # This lambda definition transforms a positional step method args into + # a mix of positional and keyword arguments. + # It's needed to test numeric_step behaviour with positional mixed with + # keyword arguments. + @step_args = ->(*args) do + if args.size == 2 + [args[0], {by: args[1]}] + else + args + end + end + end + it_behaves_like :numeric_step, :step + end +end diff --git a/spec/rubyspec/core/numeric/to_c_spec.rb b/spec/rubyspec/core/numeric/to_c_spec.rb new file mode 100644 index 0000000000..38452231b0 --- /dev/null +++ b/spec/rubyspec/core/numeric/to_c_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Numeric#to_c" do + before :all do + @numbers = [ + 0, + 29871, + 99999999999999**99, + -72628191273, + Rational(2,3), + Rational(1.898), + Rational(-238), + 29282.2827, + -2927.00091, + 0.0, + 12.0, + Float::MAX, + infinity_value, + nan_value + ] + end + + it "returns a Complex object" do + @numbers.each do |number| + number.to_c.should be_an_instance_of(Complex) + end + end + + it "uses self as the real component" do + @numbers.each do |number| + real = number.to_c.real + if Float === number and number.nan? + real.nan?.should be_true + else + real.should == number + end + end + end + + it "uses 0 as the imaginary component" do + @numbers.each do |number| + number.to_c.imag.should == 0 + end + end +end diff --git a/spec/rubyspec/core/numeric/to_int_spec.rb b/spec/rubyspec/core/numeric/to_int_spec.rb new file mode 100644 index 0000000000..4f1df3e042 --- /dev/null +++ b/spec/rubyspec/core/numeric/to_int_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#to_int" do + it "returns self#to_i" do + obj = NumericSpecs::Subclass.new + obj.should_receive(:to_i).and_return(:result) + obj.to_int.should == :result + end +end diff --git a/spec/rubyspec/core/numeric/truncate_spec.rb b/spec/rubyspec/core/numeric/truncate_spec.rb new file mode 100644 index 0000000000..f1a2d4de64 --- /dev/null +++ b/spec/rubyspec/core/numeric/truncate_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#truncate" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "converts self to a Float (using #to_f) and returns the #truncate'd result" do + @obj.should_receive(:to_f).and_return(2.5555, -2.3333) + @obj.truncate.should == 2 + @obj.truncate.should == -2 + end +end diff --git a/spec/rubyspec/core/numeric/uminus_spec.rb b/spec/rubyspec/core/numeric/uminus_spec.rb new file mode 100644 index 0000000000..7385f5f599 --- /dev/null +++ b/spec/rubyspec/core/numeric/uminus_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Numeric#-@" do + it "returns the same value with opposite sign (integers)" do + 0.send(:-@).should == 0 + 100.send(:-@).should == -100 + -100.send(:-@).should == 100 + end + + it "returns the same value with opposite sign (floats)" do + 34.56.send(:-@).should == -34.56 + -34.56.send(:-@).should == 34.56 + end + + it "returns the same value with opposite sign (two complement)" do + 2147483648.send(:-@).should == -2147483648 + -2147483648.send(:-@).should == 2147483648 + 9223372036854775808.send(:-@).should == -9223372036854775808 + -9223372036854775808.send(:-@).should == 9223372036854775808 + end + + describe "with a Numeric subclass" do + it "calls #coerce(0) on self, then subtracts the second element of the result from the first" do + ten = mock_numeric('10') + zero = mock_numeric('0') + ten.should_receive(:coerce).with(0).and_return([zero, ten]) + zero.should_receive(:-).with(ten).and_return(-10) + ten.send(:-@).should == -10 + end + end +end diff --git a/spec/rubyspec/core/numeric/uplus_spec.rb b/spec/rubyspec/core/numeric/uplus_spec.rb new file mode 100644 index 0000000000..557142295a --- /dev/null +++ b/spec/rubyspec/core/numeric/uplus_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#+@" do + it "returns self" do + obj = NumericSpecs::Subclass.new + obj.send(:+@).should == obj + end +end diff --git a/spec/rubyspec/core/numeric/zero_spec.rb b/spec/rubyspec/core/numeric/zero_spec.rb new file mode 100644 index 0000000000..d46e8807ea --- /dev/null +++ b/spec/rubyspec/core/numeric/zero_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Numeric#zero?" do + before :each do + @obj = NumericSpecs::Subclass.new + end + + it "returns true if self is 0" do + @obj.should_receive(:==).with(0).and_return(true) + @obj.zero?.should == true + end + + it "returns false if self is not 0" do + @obj.should_receive(:==).with(0).and_return(false) + @obj.zero?.should == false + end +end diff --git a/spec/rubyspec/core/objectspace/_id2ref_spec.rb b/spec/rubyspec/core/objectspace/_id2ref_spec.rb new file mode 100644 index 0000000000..6e0b6e03fb --- /dev/null +++ b/spec/rubyspec/core/objectspace/_id2ref_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace._id2ref" do + it "converts an object id to a reference to the object" do + s = "I am a string" + r = ObjectSpace._id2ref(s.object_id) + r.should == s + end + + it "retrieves a Fixnum by object_id" do + f = 1 + r = ObjectSpace._id2ref(f.object_id) + r.should == f + end + + it "retrieves a Symbol by object_id" do + s = :sym + r = ObjectSpace._id2ref(s.object_id) + r.should == s + end + + it 'raises RangeError when an object could not be found' do + proc { ObjectSpace._id2ref(1 << 60) }.should raise_error(RangeError) + end +end diff --git a/spec/rubyspec/core/objectspace/add_finalizer_spec.rb b/spec/rubyspec/core/objectspace/add_finalizer_spec.rb new file mode 100644 index 0000000000..2d529f2d6c --- /dev/null +++ b/spec/rubyspec/core/objectspace/add_finalizer_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace.add_finalizer" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/objectspace/call_finalizer_spec.rb b/spec/rubyspec/core/objectspace/call_finalizer_spec.rb new file mode 100644 index 0000000000..bb7cee68a4 --- /dev/null +++ b/spec/rubyspec/core/objectspace/call_finalizer_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace.call_finalizer" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/objectspace/count_objects_spec.rb b/spec/rubyspec/core/objectspace/count_objects_spec.rb new file mode 100644 index 0000000000..3e77d562a8 --- /dev/null +++ b/spec/rubyspec/core/objectspace/count_objects_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace.count_objects" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/objectspace/define_finalizer_spec.rb b/spec/rubyspec/core/objectspace/define_finalizer_spec.rb new file mode 100644 index 0000000000..969e8b16b0 --- /dev/null +++ b/spec/rubyspec/core/objectspace/define_finalizer_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# NOTE: A call to define_finalizer does not guarantee that the +# passed proc or callable will be called at any particular time. +# It is highly questionable whether these aspects of ObjectSpace +# should be spec'd at all. +describe "ObjectSpace.define_finalizer" do + it "raises an ArgumentError if the action does not respond to call" do + lambda { + ObjectSpace.define_finalizer("", mock("ObjectSpace.define_finalizer no #call")) + }.should raise_error(ArgumentError) + end + + it "accepts an object and a proc" do + handler = lambda { |obj| obj } + ObjectSpace.define_finalizer("garbage", handler).should == [0, handler] + end + + it "accepts an object and a callable" do + handler = mock("callable") + def handler.call(obj) end + ObjectSpace.define_finalizer("garbage", handler).should == [0, handler] + end + + it "raises ArgumentError trying to define a finalizer on a non-reference" do + lambda { + ObjectSpace.define_finalizer(:blah) { 1 } + }.should raise_error(ArgumentError) + end + + # see [ruby-core:24095] + with_feature :fork do + it "calls finalizer on process termination" do + rd, wr = IO.pipe + pid = Process.fork do + rd.close + handler = ObjectSpaceFixtures.scoped(wr) + obj = "Test" + ObjectSpace.define_finalizer(obj, handler) + exit 0 + end + + wr.close + begin + rd.read.should == "finalized" + ensure + rd.close + Process.wait pid + end + end + + it "calls finalizer at exit even if it is self-referencing" do + rd, wr = IO.pipe + pid = Process.fork do + rd.close + obj = "Test" + handler = Proc.new { wr.write "finalized"; wr.close } + ObjectSpace.define_finalizer(obj, handler) + exit 0 + end + + wr.close + begin + rd.read.should == "finalized" + ensure + rd.close + Process.wait pid + end + end + + # These specs are defined under the fork specs because there is no + # deterministic way to force finalizers to be run, except process exit, so + # we rely on that. + it "allows multiple finalizers with different 'callables' to be defined" do + rd1, wr1 = IO.pipe + rd2, wr2 = IO.pipe + + pid = Kernel::fork do + rd1.close + rd2.close + obj = mock("ObjectSpace.define_finalizer multiple") + + ObjectSpace.define_finalizer(obj, Proc.new { wr1.write "finalized1"; wr1.close }) + ObjectSpace.define_finalizer(obj, Proc.new { wr2.write "finalized2"; wr2.close }) + + exit 0 + end + + wr1.close + wr2.close + + rd1.read.should == "finalized1" + rd2.read.should == "finalized2" + + rd1.close + rd2.close + Process.wait pid + end + end +end diff --git a/spec/rubyspec/core/objectspace/each_object_spec.rb b/spec/rubyspec/core/objectspace/each_object_spec.rb new file mode 100644 index 0000000000..604b45cb34 --- /dev/null +++ b/spec/rubyspec/core/objectspace/each_object_spec.rb @@ -0,0 +1,225 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "ObjectSpace.each_object" do + it "calls the block once for each living, non-immediate object in the Ruby process" do + klass = Class.new + new_obj = klass.new + + yields = 0 + count = ObjectSpace.each_object(klass) do |obj| + obj.should == new_obj + yields += 1 + end + count.should == 1 + yields.should == 1 + + # this is needed to prevent the new_obj from being GC'd too early + new_obj.should_not == nil + end + + it "calls the block once for each class, module in the Ruby process" do + klass = Class.new + mod = Module.new + + [klass, mod].each do |k| + yields = 0 + got_it = false + count = ObjectSpace.each_object(k.class) do |obj| + got_it = true if obj == k + yields += 1 + end + got_it.should == true + count.should == yields + end + end + + it "returns an enumerator if not given a block" do + klass = Class.new + new_obj = klass.new + + counter = ObjectSpace.each_object(klass) + counter.should be_an_instance_of(Enumerator) + counter.each{}.should == 1 + # this is needed to prevent the new_obj from being GC'd too early + new_obj.should_not == nil + end + + it "finds an object stored in a global variable" do + $object_space_global_variable = ObjectSpaceFixtures::ObjectToBeFound.new(:global) + ObjectSpaceFixtures.to_be_found_symbols.should include(:global) + end + + it "finds an object stored in a top-level constant" do + ObjectSpaceFixtures.to_be_found_symbols.should include(:top_level_constant) + end + + it "finds an object stored in a second-level constant" do + ObjectSpaceFixtures.to_be_found_symbols.should include(:second_level_constant) + end + + it "finds an object stored in a local variable" do + local = ObjectSpaceFixtures::ObjectToBeFound.new(:local) + ObjectSpaceFixtures.to_be_found_symbols.should include(:local) + end + + it "finds an object stored in a local variable captured in a block explicitly" do + proc = Proc.new { + local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_explicit) + Proc.new { local_in_block } + }.call + + ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_explicit) + end + + it "finds an object stored in a local variable captured in a block implicitly" do + proc = Proc.new { + local_in_block = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_block_implicit) + Proc.new { } + }.call + + ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_block_implicit) + end + + it "finds an object stored in a local variable captured in by a method defined with a block" do + ObjectSpaceFixtures.to_be_found_symbols.should include(:captured_by_define_method) + end + + it "finds an object stored in a local variable captured in a Proc#binding" do + binding = Proc.new { + local_in_proc_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_proc_binding) + Proc.new { }.binding + }.call + + ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_proc_binding) + end + + it "finds an object stored in a local variable captured in a Kernel#binding" do + b = Proc.new { + local_in_kernel_binding = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_kernel_binding) + binding + }.call + + ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_kernel_binding) + end + + it "finds an object stored in a local variable set in a binding manually" do + b = binding + b.eval("local = ObjectSpaceFixtures::ObjectToBeFound.new(:local_in_manual_binding)") + ObjectSpaceFixtures.to_be_found_symbols.should include(:local_in_manual_binding) + end + + it "finds an object stored in an array" do + array = [ObjectSpaceFixtures::ObjectToBeFound.new(:array)] + ObjectSpaceFixtures.to_be_found_symbols.should include(:array) + end + + it "finds an object stored in a hash key" do + hash = {ObjectSpaceFixtures::ObjectToBeFound.new(:hash_key) => :value} + ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_key) + end + + it "finds an object stored in a hash value" do + hash = {a: ObjectSpaceFixtures::ObjectToBeFound.new(:hash_value)} + ObjectSpaceFixtures.to_be_found_symbols.should include(:hash_value) + end + + it "finds an object stored in an instance variable" do + local = ObjectSpaceFixtures::ObjectWithInstanceVariable.new + ObjectSpaceFixtures.to_be_found_symbols.should include(:instance_variable) + end + + it "finds an object stored in a thread local" do + thread = Thread.new {} + thread.thread_variable_set(:object_space_thread_local, ObjectSpaceFixtures::ObjectToBeFound.new(:thread_local)) + ObjectSpaceFixtures.to_be_found_symbols.should include(:thread_local) + thread.join + end + + it "finds an object stored in a fiber local" do + Thread.current[:object_space_fiber_local] = ObjectSpaceFixtures::ObjectToBeFound.new(:fiber_local) + ObjectSpaceFixtures.to_be_found_symbols.should include(:fiber_local) + end + + it "finds an object captured in an at_exit handler" do + Proc.new { + local = ObjectSpaceFixtures::ObjectToBeFound.new(:at_exit) + + at_exit do + local + end + }.call + + ObjectSpaceFixtures.to_be_found_symbols.should include(:at_exit) + end + + it "finds an object captured in finalizer" do + alive = Object.new + + Proc.new { + local = ObjectSpaceFixtures::ObjectToBeFound.new(:finalizer) + + ObjectSpace.define_finalizer(alive, Proc.new { + local + }) + }.call + + ObjectSpaceFixtures.to_be_found_symbols.should include(:finalizer) + + alive.should_not be_nil + end + + describe "on singleton classes" do + before :each do + @klass = Class.new + instance = @klass.new + @sclass = instance.singleton_class + @meta = @klass.singleton_class + end + + it "does not walk hidden metaclasses" do + klass = Class.new.singleton_class + ancestors = ObjectSpace.each_object(Class).select { |c| klass.is_a? c } + hidden = ancestors.find { |h| h.inspect.include? klass.inspect } + hidden.should == nil + end + + ruby_version_is ""..."2.3" do + it "does not walk singleton classes" do + @sclass.should be_kind_of(@meta) + ObjectSpace.each_object(@meta).to_a.should_not include(@sclass) + end + end + + ruby_version_is "2.3" do + it "walks singleton classes" do + @sclass.should be_kind_of(@meta) + ObjectSpace.each_object(@meta).to_a.should include(@sclass) + end + end + end + + it "walks a class and its normal descendants when passed the class's singleton class" do + a = Class.new + b = Class.new(a) + c = Class.new(a) + d = Class.new(b) + + c_instance = c.new + c_sclass = c_instance.singleton_class + + expected = [ a, b, c, d ] + + # singleton classes should be walked only on >= 2.3 + ruby_version_is "2.3" do + expected << c_sclass + c_sclass.should be_kind_of(a.singleton_class) + end + + b.extend Enumerable # included modules should not be walked + + classes = ObjectSpace.each_object(a.singleton_class).to_a + + classes.sort_by(&:object_id).should == expected.sort_by(&:object_id) + end +end diff --git a/spec/rubyspec/core/objectspace/finalizers_spec.rb b/spec/rubyspec/core/objectspace/finalizers_spec.rb new file mode 100644 index 0000000000..8d5f6cc029 --- /dev/null +++ b/spec/rubyspec/core/objectspace/finalizers_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace.finalizers" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/objectspace/fixtures/classes.rb b/spec/rubyspec/core/objectspace/fixtures/classes.rb new file mode 100644 index 0000000000..9a4b0aa865 --- /dev/null +++ b/spec/rubyspec/core/objectspace/fixtures/classes.rb @@ -0,0 +1,64 @@ +module ObjectSpaceFixtures + def self.garbage + blah + end + + def self.blah + o = "hello" + @garbage_objid = o.object_id + return o + end + + @last_objid = nil + + def self.last_objid + @last_objid + end + + def self.garbage_objid + @garbage_objid + end + + def self.make_finalizer + proc { |obj_id| @last_objid = obj_id } + end + + def self.define_finalizer + handler = lambda { |obj| ScratchPad.record :finalized } + ObjectSpace.define_finalizer "#{rand 5}", handler + end + + def self.scoped(wr) + return Proc.new { wr.write "finalized"; wr.close } + end + + class ObjectToBeFound + attr_reader :name + + def initialize(name) + @name = name + end + end + + class ObjectWithInstanceVariable + def initialize + @instance_variable = ObjectToBeFound.new(:instance_variable) + end + end + + def self.to_be_found_symbols + ObjectSpace.each_object(ObjectToBeFound).map do |o| + o.name + end + end + + o = ObjectToBeFound.new(:captured_by_define_method) + define_method :capturing_method do + o + end + + SECOND_LEVEL_CONSTANT = ObjectToBeFound.new(:second_level_constant) + +end + +OBJECT_SPACE_TOP_LEVEL_CONSTANT = ObjectSpaceFixtures::ObjectToBeFound.new(:top_level_constant) diff --git a/spec/rubyspec/core/objectspace/garbage_collect_spec.rb b/spec/rubyspec/core/objectspace/garbage_collect_spec.rb new file mode 100644 index 0000000000..aa7e233b25 --- /dev/null +++ b/spec/rubyspec/core/objectspace/garbage_collect_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace.garbage_collect" do + + it "can be invoked without any exceptions" do + lambda { ObjectSpace.garbage_collect }.should_not raise_error + end + + it "doesn't accept any arguments" do + lambda { ObjectSpace.garbage_collect(1) }.should raise_error(ArgumentError) + end + + it "ignores the supplied block" do + lambda { ObjectSpace.garbage_collect {} }.should_not raise_error + end + + it "always returns nil" do + ObjectSpace.garbage_collect.should == nil + ObjectSpace.garbage_collect.should == nil + end + +end diff --git a/spec/rubyspec/core/objectspace/remove_finalizer_spec.rb b/spec/rubyspec/core/objectspace/remove_finalizer_spec.rb new file mode 100644 index 0000000000..3053c31511 --- /dev/null +++ b/spec/rubyspec/core/objectspace/remove_finalizer_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace.remove_finalizer" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb b/spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb new file mode 100644 index 0000000000..98f521db98 --- /dev/null +++ b/spec/rubyspec/core/objectspace/undefine_finalizer_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ObjectSpace.undefine_finalizer" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/proc/allocate_spec.rb b/spec/rubyspec/core/proc/allocate_spec.rb new file mode 100644 index 0000000000..6bfb94dbc2 --- /dev/null +++ b/spec/rubyspec/core/proc/allocate_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc.allocate" do + it "raises a TypeError" do + lambda { + Proc.allocate + }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/proc/arity_spec.rb b/spec/rubyspec/core/proc/arity_spec.rb new file mode 100644 index 0000000000..251710a663 --- /dev/null +++ b/spec/rubyspec/core/proc/arity_spec.rb @@ -0,0 +1,640 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc#arity" do + SpecEvaluate.desc = "for definition" + + context "for instances created with -> () { }" do + context "returns zero" do + evaluate <<-ruby do + @a = -> () {} + ruby + + @a.arity.should == 0 + end + + evaluate <<-ruby do + @a = -> (&b) {} + ruby + + @a.arity.should == 0 + end + end + + context "returns positive values" do + evaluate <<-ruby do + @a = -> (a) { } + @b = -> (a, b) { } + @c = -> (a, b, c) { } + @d = -> (a, b, c, d) { } + ruby + + @a.arity.should == 1 + @b.arity.should == 2 + @c.arity.should == 3 + @d.arity.should == 4 + end + + evaluate <<-ruby do + @a = -> (a:) { } + @b = -> (a:, b:) { } + @c = -> (a: 1, b:, c:, d: 2) { } + ruby + + @a.arity.should == 1 + @b.arity.should == 1 + @c.arity.should == 1 + end + + evaluate <<-ruby do + @a = -> (a, b:) { } + @b = -> (a, b:, &l) { } + ruby + + @a.arity.should == 2 + @b.arity.should == 2 + end + + evaluate <<-ruby do + @a = -> (a, b, c:, d: 1) { } + @b = -> (a, b, c:, d: 1, **k, &l) { } + ruby + + @a.arity.should == 3 + @b.arity.should == 3 + end + + evaluate <<-ruby do + @a = -> ((a, (*b, c))) { } + @b = -> (a, (*b, c), d, (*e), (*)) { } + ruby + + @a.arity.should == 1 + @b.arity.should == 5 + end + end + + context "returns negative values" do + evaluate <<-ruby do + @a = -> (a=1) { } + @b = -> (a=1, b=2) { } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = -> (a, b=1) { } + @b = -> (a, b, c=1, d=2) { } + ruby + + @a.arity.should == -2 + @b.arity.should == -3 + end + + evaluate <<-ruby do + @a = -> (a=1, *b) { } + @b = -> (a=1, b=2, *c) { } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = -> (*) { } + @b = -> (*a) { } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = -> (a, *) { } + @b = -> (a, *b) { } + @c = -> (a, b, *c) { } + @d = -> (a, b, c, *d) { } + ruby + + @a.arity.should == -2 + @b.arity.should == -2 + @c.arity.should == -3 + @d.arity.should == -4 + end + + evaluate <<-ruby do + @a = -> (*a, b) { } + @b = -> (*a, b, c) { } + @c = -> (*a, b, c, d) { } + ruby + + @a.arity.should == -2 + @b.arity.should == -3 + @c.arity.should == -4 + end + + evaluate <<-ruby do + @a = -> (a, *b, c) { } + @b = -> (a, b, *c, d, e) { } + ruby + + @a.arity.should == -3 + @b.arity.should == -5 + end + + evaluate <<-ruby do + @a = -> (a, b=1, c=2, *d, e, f) { } + @b = -> (a, b, c=1, *d, e, f, g) { } + ruby + + @a.arity.should == -4 + @b.arity.should == -6 + end + + evaluate <<-ruby do + @a = -> (a: 1) { } + @b = -> (a: 1, b: 2) { } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = -> (a=1, b: 2) { } + @b = -> (*a, b: 1) { } + @c = -> (a=1, b: 2) { } + @d = -> (a=1, *b, c: 2, &l) { } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + @c.arity.should == -1 + @d.arity.should == -1 + end + + evaluate <<-ruby do + @a = -> (**k, &l) { } + @b= -> (*a, **k) { } + @c = ->(a: 1, b: 2, **k) { } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + @c.arity.should == -1 + end + + evaluate <<-ruby do + @a = -> (a=1, *b, c:, d: 2, **k, &l) { } + ruby + + @a.arity.should == -2 + end + + evaluate <<-ruby do + @a = -> (a, b=1, *c, d, e:, f: 2, **k, &l) { } + @b = -> (a, b=1, *c, d:, e:, f: 2, **k, &l) { } + @c = -> (a=0, b=1, *c, d, e:, f: 2, **k, &l) { } + @d = -> (a=0, b=1, *c, d:, e:, f: 2, **k, &l) { } + ruby + + @a.arity.should == -4 + @b.arity.should == -3 + @c.arity.should == -3 + @d.arity.should == -2 + end + end + end + + context "for instances created with lambda { || }" do + context "returns zero" do + evaluate <<-ruby do + @a = lambda { } + @b = lambda { || } + ruby + + @a.arity.should == 0 + @b.arity.should == 0 + end + + evaluate <<-ruby do + @a = lambda { |&b| } + ruby + + @a.arity.should == 0 + end + end + + context "returns positive values" do + evaluate <<-ruby do + @a = lambda { |a| } + @b = lambda { |a, b| } + @c = lambda { |a, b, c| } + @d = lambda { |a, b, c, d| } + ruby + + @a.arity.should == 1 + @b.arity.should == 2 + @c.arity.should == 3 + @d.arity.should == 4 + end + + evaluate <<-ruby do + @a = lambda { |a:| } + @b = lambda { |a:, b:| } + @c = lambda { |a: 1, b:, c:, d: 2| } + ruby + + @a.arity.should == 1 + @b.arity.should == 1 + @c.arity.should == 1 + end + + evaluate <<-ruby do + @a = lambda { |a, b:| } + @b = lambda { |a, b:, &l| } + ruby + + @a.arity.should == 2 + @b.arity.should == 2 + end + + evaluate <<-ruby do + @a = lambda { |a, b, c:, d: 1| } + @b = lambda { |a, b, c:, d: 1, **k, &l| } + ruby + + @a.arity.should == 3 + @b.arity.should == 3 + end + end + + context "returns negative values" do + evaluate <<-ruby do + @a = lambda { |a=1| } + @b = lambda { |a=1, b=2| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = lambda { |a, b=1| } + @b = lambda { |a, b, c=1, d=2| } + ruby + + @a.arity.should == -2 + @b.arity.should == -3 + end + + evaluate <<-ruby do + @a = lambda { |a=1, *b| } + @b = lambda { |a=1, b=2, *c| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = lambda { |*| } + @b = lambda { |*a| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = lambda { |a, *| } + @b = lambda { |a, *b| } + @c = lambda { |a, b, *c| } + @d = lambda { |a, b, c, *d| } + ruby + + @a.arity.should == -2 + @b.arity.should == -2 + @c.arity.should == -3 + @d.arity.should == -4 + end + + evaluate <<-ruby do + @a = lambda { |*a, b| } + @b = lambda { |*a, b, c| } + @c = lambda { |*a, b, c, d| } + ruby + + @a.arity.should == -2 + @b.arity.should == -3 + @c.arity.should == -4 + end + + evaluate <<-ruby do + @a = lambda { |a, *b, c| } + @b = lambda { |a, b, *c, d, e| } + ruby + + @a.arity.should == -3 + @b.arity.should == -5 + end + + evaluate <<-ruby do + @a = lambda { |a, b=1, c=2, *d, e, f| } + @b = lambda { |a, b, c=1, *d, e, f, g| } + ruby + + @a.arity.should == -4 + @b.arity.should == -6 + end + + evaluate <<-ruby do + @a = lambda { |a: 1| } + @b = lambda { |a: 1, b: 2| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = lambda { |a=1, b: 2| } + @b = lambda { |*a, b: 1| } + @c = lambda { |a=1, b: 2| } + @d = lambda { |a=1, *b, c: 2, &l| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + @c.arity.should == -1 + @d.arity.should == -1 + end + + evaluate <<-ruby do + @a = lambda { |**k, &l| } + @b = lambda { |*a, **k| } + @c = lambda { |a: 1, b: 2, **k| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + @c.arity.should == -1 + end + + evaluate <<-ruby do + @a = lambda { |a=1, *b, c:, d: 2, **k, &l| } + ruby + + @a.arity.should == -2 + end + + evaluate <<-ruby do + @a = lambda { |(a, (*b, c)), d=1| } + @b = lambda { |a, (*b, c), d, (*e), (*), **k| } + @c = lambda { |a, (b, c), *, d:, e: 2, **| } + ruby + + @a.arity.should == -2 + @b.arity.should == -6 + @c.arity.should == -4 + end + + evaluate <<-ruby do + @a = lambda { |a, b=1, *c, d, e:, f: 2, **k, &l| } + @b = lambda { |a, b=1, *c, d:, e:, f: 2, **k, &l| } + @c = lambda { |a=0, b=1, *c, d, e:, f: 2, **k, &l| } + @d = lambda { |a=0, b=1, *c, d:, e:, f: 2, **k, &l| } + ruby + + @a.arity.should == -4 + @b.arity.should == -3 + @c.arity.should == -3 + @d.arity.should == -2 + end + end + end + + context "for instances created with proc { || }" do + context "returns zero" do + evaluate <<-ruby do + @a = proc { } + @b = proc { || } + ruby + + @a.arity.should == 0 + @b.arity.should == 0 + end + + evaluate <<-ruby do + @a = proc { |&b| } + ruby + + @a.arity.should == 0 + end + + evaluate <<-ruby do + @a = proc { |a=1| } + @b = proc { |a=1, b=2| } + ruby + + @a.arity.should == 0 + @b.arity.should == 0 + end + + evaluate <<-ruby do + @a = proc { |a: 1| } + @b = proc { |a: 1, b: 2| } + ruby + + @a.arity.should == 0 + @b.arity.should == 0 + end + + evaluate <<-ruby do + @a = proc { |**k, &l| } + @b = proc { |a: 1, b: 2, **k| } + ruby + + @a.arity.should == 0 + @b.arity.should == 0 + end + + evaluate <<-ruby do + @a = proc { |a=1, b: 2| } + @b = proc { |a=1, b: 2| } + ruby + + @a.arity.should == 0 + @b.arity.should == 0 + end + end + + context "returns positive values" do + evaluate <<-ruby do + @a = proc { |a| } + @b = proc { |a, b| } + @c = proc { |a, b, c| } + @d = proc { |a, b, c, d| } + ruby + + @a.arity.should == 1 + @b.arity.should == 2 + @c.arity.should == 3 + @d.arity.should == 4 + end + + evaluate <<-ruby do + @a = proc { |a, b=1| } + @b = proc { |a, b, c=1, d=2| } + ruby + + @a.arity.should == 1 + @b.arity.should == 2 + end + + evaluate <<-ruby do + @a = lambda { |a:| } + @b = lambda { |a:, b:| } + @c = lambda { |a: 1, b:, c:, d: 2| } + ruby + + @a.arity.should == 1 + @b.arity.should == 1 + @c.arity.should == 1 + end + + evaluate <<-ruby do + @a = proc { |a, b:| } + @b = proc { |a, b:, &l| } + ruby + + @a.arity.should == 2 + @b.arity.should == 2 + end + + evaluate <<-ruby do + @a = proc { |a, b, c:, d: 1| } + @b = proc { |a, b, c:, d: 1, **k, &l| } + ruby + + @a.arity.should == 3 + @b.arity.should == 3 + end + + evaluate <<-ruby do + @a = proc { |(a, (*b, c)), d=1| } + @b = proc { |a, (*b, c), d, (*e), (*), **k| } + ruby + + @a.arity.should == 1 + @b.arity.should == 5 + end + end + + context "returns negative values" do + evaluate <<-ruby do + @a = proc { |a=1, *b| } + @b = proc { |a=1, b=2, *c| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = proc { |*| } + @b = proc { |*a| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = proc { |a, *| } + @b = proc { |a, *b| } + @c = proc { |a, b, *c| } + @d = proc { |a, b, c, *d| } + ruby + + @a.arity.should == -2 + @b.arity.should == -2 + @c.arity.should == -3 + @d.arity.should == -4 + end + + evaluate <<-ruby do + @a = proc { |*a, b| } + @b = proc { |*a, b, c| } + @c = proc { |*a, b, c, d| } + ruby + + @a.arity.should == -2 + @b.arity.should == -3 + @c.arity.should == -4 + end + + evaluate <<-ruby do + @a = proc { |a, *b, c| } + @b = proc { |a, b, *c, d, e| } + ruby + + @a.arity.should == -3 + @b.arity.should == -5 + end + + evaluate <<-ruby do + @a = proc { |a, b=1, c=2, *d, e, f| } + @b = proc { |a, b, c=1, *d, e, f, g| } + ruby + + @a.arity.should == -4 + @b.arity.should == -6 + end + + evaluate <<-ruby do + @a = proc { |*a, b: 1| } + @b = proc { |a=1, *b, c: 2, &l| } + ruby + + @a.arity.should == -1 + @b.arity.should == -1 + end + + evaluate <<-ruby do + @a = proc { |*a, **k| } + ruby + + @a.arity.should == -1 + end + + evaluate <<-ruby do + @a = proc { |a=1, *b, c:, d: 2, **k, &l| } + ruby + + @a.arity.should == -2 + end + + evaluate <<-ruby do + @a = proc { |a, (b, c), *, d:, e: 2, **| } + ruby + + @a.arity.should == -4 + end + + evaluate <<-ruby do + @a = proc { |a, b=1, *c, d, e:, f: 2, **k, &l| } + @b = proc { |a, b=1, *c, d:, e:, f: 2, **k, &l| } + @c = proc { |a=0, b=1, *c, d, e:, f: 2, **k, &l| } + @d = proc { |a=0, b=1, *c, d:, e:, f: 2, **k, &l| } + ruby + + @a.arity.should == -4 + @b.arity.should == -3 + @c.arity.should == -3 + @d.arity.should == -2 + end + end + end +end diff --git a/spec/rubyspec/core/proc/binding_spec.rb b/spec/rubyspec/core/proc/binding_spec.rb new file mode 100644 index 0000000000..05cc68217e --- /dev/null +++ b/spec/rubyspec/core/proc/binding_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc#binding" do + it "returns a Binding instance" do + [Proc.new{}, lambda {}, proc {}].each { |p| + p.binding.should be_kind_of(Binding) + } + end + + it "returns the binding associated with self" do + obj = mock('binding') + def obj.test_binding(some, params) + lambda {} + end + + lambdas_binding = obj.test_binding(1, 2).binding + + eval("some", lambdas_binding).should == 1 + eval("params", lambdas_binding).should == 2 + end +end diff --git a/spec/rubyspec/core/proc/block_pass_spec.rb b/spec/rubyspec/core/proc/block_pass_spec.rb new file mode 100644 index 0000000000..e956885654 --- /dev/null +++ b/spec/rubyspec/core/proc/block_pass_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc as a block pass argument" do + def revivify(&b) + b + end + + it "remains the same object if re-vivified by the target method" do + p = Proc.new {} + p2 = revivify(&p) + p.object_id.should == p2.object_id + p.should == p2 + end + + it "remains the same object if reconstructed with Proc.new" do + p = Proc.new {} + p2 = Proc.new(&p) + p.object_id.should == p2.object_id + p.should == p2 + end +end + +describe "Proc as an implicit block pass argument" do + def revivify + Proc.new + end + + it "remains the same object if re-vivified by the target method" do + p = Proc.new {} + p2 = revivify(&p) + p.object_id.should == p2.object_id + p.should == p2 + end + + it "remains the same object if reconstructed with Proc.new" do + p = Proc.new {} + p2 = Proc.new(&p) + p.object_id.should == p2.object_id + p.should == p2 + end +end diff --git a/spec/rubyspec/core/proc/call_spec.rb b/spec/rubyspec/core/proc/call_spec.rb new file mode 100644 index 0000000000..1c28eae9b0 --- /dev/null +++ b/spec/rubyspec/core/proc/call_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/call', __FILE__) +require File.expand_path('../shared/call_arguments', __FILE__) + +describe "Proc#call" do + it_behaves_like :proc_call, :call + it_behaves_like :proc_call_block_args, :call +end + +describe "Proc#call on a Proc created with Proc.new" do + it_behaves_like :proc_call_on_proc_new, :call +end + +describe "Proc#call on a Proc created with Kernel#lambda or Kernel#proc" do + it_behaves_like :proc_call_on_proc_or_lambda, :call +end diff --git a/spec/rubyspec/core/proc/case_compare_spec.rb b/spec/rubyspec/core/proc/case_compare_spec.rb new file mode 100644 index 0000000000..55760c4ff3 --- /dev/null +++ b/spec/rubyspec/core/proc/case_compare_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/call', __FILE__) +require File.expand_path('../shared/call_arguments', __FILE__) + +describe "Proc#===" do + it_behaves_like :proc_call, :=== + it_behaves_like :proc_call_block_args, :=== +end + +describe "Proc#=== on a Proc created with Proc.new" do + it_behaves_like :proc_call_on_proc_new, :=== +end + +describe "Proc#=== on a Proc created with Kernel#lambda or Kernel#proc" do + it_behaves_like :proc_call_on_proc_or_lambda, :=== +end diff --git a/spec/rubyspec/core/proc/clone_spec.rb b/spec/rubyspec/core/proc/clone_spec.rb new file mode 100644 index 0000000000..0f0806645b --- /dev/null +++ b/spec/rubyspec/core/proc/clone_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/dup', __FILE__) + +describe "Proc#clone" do + it_behaves_like(:proc_dup, :clone) +end diff --git a/spec/rubyspec/core/proc/curry_spec.rb b/spec/rubyspec/core/proc/curry_spec.rb new file mode 100644 index 0000000000..a294606957 --- /dev/null +++ b/spec/rubyspec/core/proc/curry_spec.rb @@ -0,0 +1,180 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc#curry" do + before :each do + @proc_add = Proc.new {|x,y,z| (x||0) + (y||0) + (z||0) } + @lambda_add = lambda {|x,y,z| (x||0) + (y||0) + (z||0) } + end + + it "returns a Proc when called on a proc" do + p = proc { true } + p.curry.should be_an_instance_of(Proc) + end + + it "returns a Proc when called on a lambda" do + p = lambda { true } + p.curry.should be_an_instance_of(Proc) + end + + it "calls the curried proc with the arguments if sufficient arguments have been given" do + @proc_add.curry[1][2][3].should == 6 + @lambda_add.curry[1][2][3].should == 6 + end + + it "returns a Proc that consumes the remainder of the arguments unless sufficient arguments have been given" do + proc2 = @proc_add.curry[1][2] + proc2.should be_an_instance_of(Proc) + proc2.call(3).should == 6 + + lambda2 = @lambda_add.curry[1][2] + lambda2.should be_an_instance_of(Proc) + lambda2.call(3).should == 6 + + @proc_add.curry.call(1,2,3).should == 6 + @lambda_add.curry.call(1,2,3).should == 6 + end + + it "can be called multiple times on the same Proc" do + @proc_add.curry + lambda { @proc_add.curry }.should_not raise_error + + @lambda_add.curry + lambda { @lambda_add.curry }.should_not raise_error + end + + it "can be passed superfluous arguments if created from a proc" do + @proc_add.curry[1,2,3,4].should == 6 + + @proc_add.curry[1,2].curry[3,4,5,6].should == 6 + end + + it "raises an ArgumentError if passed superfluous arguments when created from a lambda" do + lambda { @lambda_add.curry[1,2,3,4] }.should raise_error(ArgumentError) + lambda { @lambda_add.curry[1,2].curry[3,4,5,6] }.should raise_error(ArgumentError) + end + + it "returns Procs with arities of -1" do + @proc_add.curry.arity.should == -1 + @lambda_add.curry.arity.should == -1 + l = lambda { |*a| } + l.curry.arity.should == -1 + end + + it "produces Procs that raise ArgumentError for #binding" do + lambda do + @proc_add.curry.binding + end.should raise_error(ArgumentError) + end + + it "produces Procs that return [[:rest]] for #parameters" do + @proc_add.curry.parameters.should == [[:rest]] + end + + it "produces Procs that return nil for #source_location" do + @proc_add.curry.source_location.should == nil + end + + it "produces Procs that can be passed as the block for instance_exec" do + curried = @proc_add.curry.call(1, 2) + + instance_exec(3, &curried).should == 6 + end + + it "combines arguments and calculates incoming arity accurately for successively currying" do + l = lambda{|a,b,c| a+b+c } + l1 = l.curry.call(1) + # the l1 currying seems unnecessary, but it triggered the original issue + l2 = l1.curry.call(2) + + l2.curry.call(3).should == 6 + l1.curry.call(2,3).should == 6 + end +end + +describe "Proc#curry with arity argument" do + before :each do + @proc_add = proc {|x,y,z| (x||0) + (y||0) + (z||0) } + @lambda_add = lambda {|x,y,z| (x||0) + (y||0) + (z||0) } + end + + it "accepts an optional Integer argument for the arity" do + lambda { @proc_add.curry(3) }.should_not raise_error + lambda { @lambda_add.curry(3) }.should_not raise_error + end + + it "returns a Proc when called on a proc" do + @proc_add.curry(3).should be_an_instance_of(Proc) + end + + it "returns a Proc when called on a lambda" do + @lambda_add.curry(3).should be_an_instance_of(Proc) + end + + # [ruby-core:24127] + it "retains the lambda-ness of the Proc on which its called" do + @lambda_add.curry(3).lambda?.should be_true + @proc_add.curry(3).lambda?.should be_false + end + + it "raises an ArgumentError if called on a lambda that requires more than _arity_ arguments" do + lambda { @lambda_add.curry(2) }.should raise_error(ArgumentError) + lambda { lambda{|x, y, z, *more|}.curry(2) }.should raise_error(ArgumentError) + end + + it 'returns a Proc if called on a lambda that requires fewer than _arity_ arguments but may take more' do + lambda{|a, b, c, d=nil, e=nil|}.curry(4).should be_an_instance_of(Proc) + lambda{|a, b, c, d=nil, *e|}.curry(4).should be_an_instance_of(Proc) + lambda{|a, b, c, *d|}.curry(4).should be_an_instance_of(Proc) + end + + it "raises an ArgumentError if called on a lambda that requires fewer than _arity_ arguments" do + lambda { @lambda_add.curry(4) }.should raise_error(ArgumentError) + lambda { lambda { true }.curry(1) }.should raise_error(ArgumentError) + lambda { lambda {|a, b=nil|}.curry(5) }.should raise_error(ArgumentError) + lambda { lambda {|a, &b|}.curry(2) }.should raise_error(ArgumentError) + lambda { lambda {|a, b=nil, &c|}.curry(3) }.should raise_error(ArgumentError) + end + + it "calls the curried proc with the arguments if _arity_ arguments have been given" do + @proc_add.curry(3)[1][2][3].should == 6 + @lambda_add.curry(3)[1][2][3].should == 6 + end + + it "returns a Proc that consumes the remainder of the arguments when fewer than _arity_ arguments are given" do + proc2 = @proc_add.curry(3)[1][2] + proc2.should be_an_instance_of(Proc) + proc2.call(3).should == 6 + + lambda2 = @lambda_add.curry(3)[1][2] + lambda2.should be_an_instance_of(Proc) + lambda2.call(3).should == 6 + end + + it "can be specified multiple times on the same Proc" do + @proc_add.curry(2) + lambda { @proc_add.curry(1) }.should_not raise_error + + @lambda_add.curry(3) + lambda { @lambda_add.curry(3) }.should_not raise_error + end + + it "can be passed more than _arity_ arguments if created from a proc" do + lambda { @proc_add.curry(3)[1,2,3,4].should == 6 }.should_not + raise_error(ArgumentError) + lambda { @proc_add.curry(1)[1,2].curry(3)[3,4,5,6].should == 6 }.should_not + raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed more than _arity_ arguments when created from a lambda" do + lambda { @lambda_add.curry(3)[1,2,3,4] }.should raise_error(ArgumentError) + lambda { @lambda_add.curry(1)[1,2].curry(3)[3,4,5,6] }.should raise_error(ArgumentError) + end + + it "returns Procs with arities of -1 regardless of the value of _arity_" do + @proc_add.curry(1).arity.should == -1 + @proc_add.curry(2).arity.should == -1 + @lambda_add.curry(3).arity.should == -1 + l = lambda { |*a| } + l.curry(3).arity.should == -1 + end +end diff --git a/spec/rubyspec/core/proc/dup_spec.rb b/spec/rubyspec/core/proc/dup_spec.rb new file mode 100644 index 0000000000..ea3fe8aac9 --- /dev/null +++ b/spec/rubyspec/core/proc/dup_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/dup', __FILE__) + +describe "Proc#dup" do + it_behaves_like(:proc_dup, :dup) +end diff --git a/spec/rubyspec/core/proc/element_reference_spec.rb b/spec/rubyspec/core/proc/element_reference_spec.rb new file mode 100644 index 0000000000..f3dec21253 --- /dev/null +++ b/spec/rubyspec/core/proc/element_reference_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/call', __FILE__) +require File.expand_path('../shared/call_arguments', __FILE__) + +describe "Proc#[]" do + it_behaves_like :proc_call, :[] + it_behaves_like :proc_call_block_args, :[] +end + +describe "Proc#call on a Proc created with Proc.new" do + it_behaves_like :proc_call_on_proc_new, :call +end + +describe "Proc#call on a Proc created with Kernel#lambda or Kernel#proc" do + it_behaves_like :proc_call_on_proc_or_lambda, :call +end diff --git a/spec/rubyspec/core/proc/eql_spec.rb b/spec/rubyspec/core/proc/eql_spec.rb new file mode 100644 index 0000000000..e0cc80bb48 --- /dev/null +++ b/spec/rubyspec/core/proc/eql_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Proc#eql?" do + it_behaves_like(:proc_equal_undefined, :eql?) +end diff --git a/spec/rubyspec/core/proc/equal_value_spec.rb b/spec/rubyspec/core/proc/equal_value_spec.rb new file mode 100644 index 0000000000..e50ea1a53b --- /dev/null +++ b/spec/rubyspec/core/proc/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal', __FILE__) + +describe "Proc#==" do + it_behaves_like(:proc_equal_undefined, :==) +end diff --git a/spec/rubyspec/core/proc/fixtures/common.rb b/spec/rubyspec/core/proc/fixtures/common.rb new file mode 100644 index 0000000000..6e27a2dee7 --- /dev/null +++ b/spec/rubyspec/core/proc/fixtures/common.rb @@ -0,0 +1,51 @@ +module ProcSpecs + class ToAryAsNil + def to_ary + nil + end + end + def self.new_proc_in_method + Proc.new + end + + def self.new_proc_from_amp(&block) + block + end + + def self.proc_for_1 + proc { 1 } + end + + class ProcSubclass < Proc + end + + def self.new_proc_subclass_in_method + ProcSubclass.new + end + + class MyProc < Proc + end + + class MyProc2 < Proc + def initialize(a, b) + @first = a + @second = b + end + + attr_reader :first, :second + end + + class Arity + def arity_check(&block) + pn = Proc.new(&block).arity + pr = proc(&block).arity + lm = lambda(&block).arity + + if pn == pr and pr == lm + return pn + else + return :arity_check_failed + end + end + end +end diff --git a/spec/rubyspec/core/proc/fixtures/source_location.rb b/spec/rubyspec/core/proc/fixtures/source_location.rb new file mode 100644 index 0000000000..468262e02a --- /dev/null +++ b/spec/rubyspec/core/proc/fixtures/source_location.rb @@ -0,0 +1,55 @@ +module ProcSpecs + class SourceLocation + def self.my_proc + proc { true } + end + + def self.my_lambda + lambda { true } + end + + def self.my_proc_new + Proc.new { true } + end + + def self.my_method + method(__method__).to_proc + end + + def self.my_multiline_proc + proc do + 'a'.upcase + 1 + 22 + end + end + + def self.my_multiline_lambda + lambda do + 'a'.upcase + 1 + 22 + end + end + + def self.my_multiline_proc_new + Proc.new do + 'a'.upcase + 1 + 22 + end + end + + def self.my_detached_proc + body = proc { true } + proc(&body) + end + + def self.my_detached_lambda + body = lambda { true } + lambda(&body) + end + + def self.my_detached_proc_new + body = Proc.new { true } + Proc.new(&body) + end + end +end diff --git a/spec/rubyspec/core/proc/hash_spec.rb b/spec/rubyspec/core/proc/hash_spec.rb new file mode 100644 index 0000000000..1f5b6d5aa1 --- /dev/null +++ b/spec/rubyspec/core/proc/hash_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc#hash" do + it "is provided" do + proc {}.respond_to?(:hash).should be_true + lambda {}.respond_to?(:hash).should be_true + end + + it "returns an Integer" do + proc { 1 + 489 }.hash.should be_kind_of(Fixnum) + end + + it "is stable" do + body = proc { :foo } + proc(&body).hash.should == proc(&body).hash + end +end diff --git a/spec/rubyspec/core/proc/inspect_spec.rb b/spec/rubyspec/core/proc/inspect_spec.rb new file mode 100644 index 0000000000..860647baa7 --- /dev/null +++ b/spec/rubyspec/core/proc/inspect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "Proc#inspect" do + it_behaves_like :proc_to_s, :inspect +end diff --git a/spec/rubyspec/core/proc/lambda_spec.rb b/spec/rubyspec/core/proc/lambda_spec.rb new file mode 100644 index 0000000000..33c9ebdd55 --- /dev/null +++ b/spec/rubyspec/core/proc/lambda_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Proc#lambda?" do + it "returns true if the Proc was created from a block with the lambda keyword" do + lambda {}.lambda?.should be_true + end + + it "returns false if the Proc was created from a block with the proc keyword" do + proc {}.lambda?.should be_false + end + + it "returns false if the Proc was created from a block with Proc.new" do + Proc.new {}.lambda?.should be_false + end + + it "is preserved when passing a Proc with & to the lambda keyword" do + lambda(&lambda{}).lambda?.should be_true + lambda(&proc{}).lambda?.should be_false + end + + it "is preserved when passing a Proc with & to the proc keyword" do + proc(&lambda{}).lambda?.should be_true + proc(&proc{}).lambda?.should be_false + end + + it "is preserved when passing a Proc with & to Proc.new" do + Proc.new(&lambda{}).lambda?.should be_true + Proc.new(&proc{}).lambda?.should be_false + end + + it "returns false if the Proc was created from a block with &" do + ProcSpecs.new_proc_from_amp{}.lambda?.should be_false + end + + it "is preserved when the Proc was passed using &" do + ProcSpecs.new_proc_from_amp(&lambda{}).lambda?.should be_true + ProcSpecs.new_proc_from_amp(&proc{}).lambda?.should be_false + ProcSpecs.new_proc_from_amp(&Proc.new{}).lambda?.should be_false + end + + it "returns true for a Method converted to a Proc" do + m = :foo.method(:to_s) + m.to_proc.lambda?.should be_true + ProcSpecs.new_proc_from_amp(&m).lambda?.should be_true + end + + # [ruby-core:24127] + it "is preserved when a Proc is curried" do + lambda{}.curry.lambda?.should be_true + proc{}.curry.lambda?.should be_false + Proc.new{}.curry.lambda?.should be_false + end + + it "is preserved when a curried Proc is called without enough arguments" do + lambda{|x,y|}.curry.call(42).lambda?.should be_true + proc{|x,y|}.curry.call(42).lambda?.should be_false + Proc.new{|x,y|}.curry.call(42).lambda?.should be_false + end +end diff --git a/spec/rubyspec/core/proc/new_spec.rb b/spec/rubyspec/core/proc/new_spec.rb new file mode 100644 index 0000000000..5dc0061a47 --- /dev/null +++ b/spec/rubyspec/core/proc/new_spec.rb @@ -0,0 +1,190 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Proc.new with an associated block" do + it "returns a proc that represents the block" do + Proc.new { }.call.should == nil + Proc.new { "hello" }.call.should == "hello" + end + + describe "called on a subclass of Proc" do + before :each do + @subclass = Class.new(Proc) do + attr_reader :ok + def initialize + @ok = true + super + end + end + end + + it "returns an instance of the subclass" do + proc = @subclass.new {"hello"} + + proc.class.should == @subclass + proc.call.should == "hello" + proc.ok.should == true + end + + # JRUBY-5026 + describe "using a reified block parameter" do + it "returns an instance of the subclass" do + cls = Class.new do + def self.subclass=(subclass) + @subclass = subclass + end + def self.foo(&block) + @subclass.new(&block) + end + end + cls.subclass = @subclass + proc = cls.foo {"hello"} + + proc.class.should == @subclass + proc.call.should == "hello" + proc.ok.should == true + end + end + end + + # JRUBY-5261; Proc sets up the block during .new, not in #initialize + describe "called on a subclass of Proc that does not 'super' in 'initialize'" do + before :each do + @subclass = Class.new(Proc) do + attr_reader :ok + def initialize + @ok = true + end + end + end + + it "still constructs a functional proc" do + proc = @subclass.new {'ok'} + proc.call.should == 'ok' + proc.ok.should == true + end + end + + it "raises a LocalJumpError when context of the block no longer exists" do + def some_method + Proc.new { return } + end + res = some_method() + + lambda { res.call }.should raise_error(LocalJumpError) + end + + it "returns from within enclosing method when 'return' is used in the block" do + # we essentially verify that the created instance behaves like proc, + # not like lambda. + def some_method + Proc.new { return :proc_return_value }.call + :method_return_value + end + some_method.should == :proc_return_value + end + + it "returns a subclass of Proc" do + obj = ProcSpecs::MyProc.new { } + obj.should be_kind_of(ProcSpecs::MyProc) + end + + it "calls initialize on the Proc object" do + obj = ProcSpecs::MyProc2.new(:a, 2) { } + obj.first.should == :a + obj.second.should == 2 + end + + it "returns a new Proc instance from the block passed to the containing method" do + prc = ProcSpecs.new_proc_in_method { "hello" } + prc.should be_an_instance_of(Proc) + prc.call.should == "hello" + end + + it "returns a new Proc instance from the block passed to the containing method" do + prc = ProcSpecs.new_proc_subclass_in_method { "hello" } + prc.should be_an_instance_of(ProcSpecs::ProcSubclass) + prc.call.should == "hello" + end +end + +describe "Proc.new with a block argument" do + it "returns the passed proc created from a block" do + passed_prc = Proc.new { "hello".size } + prc = Proc.new(&passed_prc) + + prc.should equal(passed_prc) + prc.call.should == 5 + end + + it "returns the passed proc created from a method" do + method = "hello".method(:size) + passed_prc = Proc.new(&method) + prc = Proc.new(&passed_prc) + + prc.should equal(passed_prc) + prc.call.should == 5 + end + + it "returns the passed proc created from a symbol" do + passed_prc = Proc.new(&:size) + prc = Proc.new(&passed_prc) + + prc.should equal(passed_prc) + prc.call("hello").should == 5 + end +end + +describe "Proc.new with a block argument called indirectly from a subclass" do + it "returns the passed proc created from a block" do + passed_prc = ProcSpecs::MyProc.new { "hello".size } + passed_prc.class.should == ProcSpecs::MyProc + prc = ProcSpecs::MyProc.new(&passed_prc) + + prc.should equal(passed_prc) + prc.call.should == 5 + end + + it "returns the passed proc created from a method" do + method = "hello".method(:size) + passed_prc = ProcSpecs::MyProc.new(&method) + passed_prc.class.should == ProcSpecs::MyProc + prc = ProcSpecs::MyProc.new(&passed_prc) + + prc.should equal(passed_prc) + prc.call.should == 5 + end + + it "returns the passed proc created from a symbol" do + passed_prc = ProcSpecs::MyProc.new(&:size) + passed_prc.class.should == ProcSpecs::MyProc + prc = ProcSpecs::MyProc.new(&passed_prc) + + prc.should equal(passed_prc) + prc.call("hello").should == 5 + end +end + +describe "Proc.new without a block" do + it "raises an ArgumentError" do + lambda { Proc.new }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if invoked from within a method with no block" do + lambda { ProcSpecs.new_proc_in_method }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if invoked on a subclass from within a method with no block" do + lambda { ProcSpecs.new_proc_subclass_in_method }.should raise_error(ArgumentError) + end + + it "uses the implicit block from an enclosing method" do + def some_method + Proc.new + end + + prc = some_method { "hello" } + + prc.call.should == "hello" + end +end diff --git a/spec/rubyspec/core/proc/parameters_spec.rb b/spec/rubyspec/core/proc/parameters_spec.rb new file mode 100644 index 0000000000..3ba8a0cc32 --- /dev/null +++ b/spec/rubyspec/core/proc/parameters_spec.rb @@ -0,0 +1,95 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc#parameters" do + it "returns an empty Array for a proc expecting no parameters" do + proc {}.parameters.should == [] + end + + it "returns an Array of Arrays for a proc expecting parameters" do + p = proc {|x| } + p.parameters.should be_an_instance_of(Array) + p.parameters.first.should be_an_instance_of(Array) + end + + it "sets the first element of each sub-Array to :opt for optional arguments" do + proc {|x| }.parameters.first.first.should == :opt + proc {|y,*x| }.parameters.first.first.should == :opt + end + + it "regards named parameters in procs as optional" do + proc {|x| }.parameters.first.first.should == :opt + end + + it "regards optional keyword parameters in procs as optional" do + proc {|x: :y| }.parameters.first.first.should == :key + end + + it "regards parameters with default values as optional" do + lambda {|x=1| }.parameters.first.first.should == :opt + proc {|x=1| }.parameters.first.first.should == :opt + end + + it "sets the first element of each sub-Array to :req for required arguments" do + lambda {|x,y=[]| }.parameters.first.first.should == :req + lambda {|y,*x| }.parameters.first.first.should == :req + end + + it "regards named parameters in lambdas as required" do + lambda {|x| }.parameters.first.first.should == :req + end + + it "regards keyword parameters in lambdas as required" do + eval("lambda {|x:| }").parameters.first.first.should == :keyreq + end + + it "sets the first element of each sub-Array to :rest for parameters prefixed with asterisks" do + lambda {|*x| }.parameters.first.first.should == :rest + lambda {|x,*y| }.parameters.last.first.should == :rest + proc {|*x| }.parameters.first.first.should == :rest + proc {|x,*y| }.parameters.last.first.should == :rest + end + + it "sets the first element of each sub-Array to :keyrest for parameters prefixed with double asterisks" do + lambda {|**x| }.parameters.first.first.should == :keyrest + lambda {|x,**y| }.parameters.last.first.should == :keyrest + proc {|**x| }.parameters.first.first.should == :keyrest + proc {|x,**y| }.parameters.last.first.should == :keyrest + end + + it "sets the first element of each sub-Array to :block for parameters prefixed with ampersands" do + lambda {|&x| }.parameters.first.first.should == :block + lambda {|x,&y| }.parameters.last.first.should == :block + proc {|&x| }.parameters.first.first.should == :block + proc {|x,&y| }.parameters.last.first.should == :block + end + + it "sets the second element of each sub-Array to the name of the argument" do + lambda {|x| }.parameters.first.last.should == :x + lambda {|x=Math::PI| }.parameters.first.last.should == :x + lambda {|an_argument, glark, &foo| }.parameters[1].last.should == :glark + lambda {|*rest| }.parameters.first.last.should == :rest + lambda {|&block| }.parameters.first.last.should == :block + proc {|x| }.parameters.first.last.should == :x + proc {|x=Math::PI| }.parameters.first.last.should == :x + proc {|an_argument, glark, &foo| }.parameters[1].last.should == :glark + proc {|*rest| }.parameters.first.last.should == :rest + proc {|&block| }.parameters.first.last.should == :block + end + + it "ignores unnamed rest args" do + lambda {|x,|}.parameters.should == [[:req, :x]] + end + + it "adds nameless rest arg for \"star\" argument" do + lambda {|x,*|}.parameters.should == [[:req, :x], [:rest]] + end + + it "does not add locals as block options with a block and splat" do + lambda do |*args, &blk| + local_is_not_parameter = {} + end.parameters.should == [[:rest, :args], [:block, :blk]] + proc do |*args, &blk| + local_is_not_parameter = {} + end.parameters.should == [[:rest, :args], [:block, :blk]] + end +end diff --git a/spec/rubyspec/core/proc/shared/call.rb b/spec/rubyspec/core/proc/shared/call.rb new file mode 100644 index 0000000000..11355537b0 --- /dev/null +++ b/spec/rubyspec/core/proc/shared/call.rb @@ -0,0 +1,96 @@ +require File.expand_path('../../fixtures/common', __FILE__) + +describe :proc_call, shared: true do + it "invokes self" do + Proc.new { "test!" }.send(@method).should == "test!" + lambda { "test!" }.send(@method).should == "test!" + proc { "test!" }.send(@method).should == "test!" + end + + it "sets self's parameters to the given values" do + Proc.new { |a, b| a + b }.send(@method, 1, 2).should == 3 + Proc.new { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4] + Proc.new { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3] + + lambda { |a, b| a + b }.send(@method, 1, 2).should == 3 + lambda { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4] + lambda { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3] + + proc { |a, b| a + b }.send(@method, 1, 2).should == 3 + proc { |*args| args }.send(@method, 1, 2, 3, 4).should == [1, 2, 3, 4] + proc { |_, *args| args }.send(@method, 1, 2, 3).should == [2, 3] + end +end + + +describe :proc_call_on_proc_new, shared: true do + it "replaces missing arguments with nil" do + Proc.new { |a, b| [a, b] }.send(@method).should == [nil, nil] + Proc.new { |a, b| [a, b] }.send(@method, 1).should == [1, nil] + end + + it "silently ignores extra arguments" do + Proc.new { |a, b| a + b }.send(@method, 1, 2, 5).should == 3 + end + + it "auto-explodes a single Array argument" do + p = Proc.new { |a, b| [a, b] } + p.send(@method, 1, 2).should == [1, 2] + p.send(@method, [1, 2]).should == [1, 2] + p.send(@method, [1, 2, 3]).should == [1, 2] + p.send(@method, [1, 2, 3], 4).should == [[1, 2, 3], 4] + end +end + +describe :proc_call_on_proc_or_lambda, shared: true do + it "ignores excess arguments when self is a proc" do + a = proc {|x| x}.send(@method, 1, 2) + a.should == 1 + + a = proc {|x| x}.send(@method, 1, 2, 3) + a.should == 1 + end + + it "will call #to_ary on argument and return self if return is nil" do + argument = ProcSpecs::ToAryAsNil.new + result = proc { |x, _| x }.send(@method, argument) + result.should == argument + end + + it "substitutes nil for missing arguments when self is a proc" do + proc {|x,y| [x,y]}.send(@method).should == [nil,nil] + + a = proc {|x,y| [x, y]}.send(@method, 1) + a.should == [1,nil] + end + + it "raises an ArgumentError on excess arguments when self is a lambda" do + lambda { + lambda {|x| x}.send(@method, 1, 2) + }.should raise_error(ArgumentError) + + lambda { + lambda {|x| x}.send(@method, 1, 2, 3) + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError on missing arguments when self is a lambda" do + lambda { + lambda {|x| x}.send(@method) + }.should raise_error(ArgumentError) + + lambda { + lambda {|x,y| [x,y]}.send(@method, 1) + }.should raise_error(ArgumentError) + end + + it "treats a single Array argument as a single argument when self is a lambda" do + lambda { |a| a }.send(@method, [1, 2]).should == [1, 2] + lambda { |a, b| [a, b] }.send(@method, [1, 2], 3).should == [[1,2], 3] + end + + it "treats a single Array argument as a single argument when self is a proc" do + proc { |a| a }.send(@method, [1, 2]).should == [1, 2] + proc { |a, b| [a, b] }.send(@method, [1, 2], 3).should == [[1,2], 3] + end +end diff --git a/spec/rubyspec/core/proc/shared/call_arguments.rb b/spec/rubyspec/core/proc/shared/call_arguments.rb new file mode 100644 index 0000000000..2e510b194e --- /dev/null +++ b/spec/rubyspec/core/proc/shared/call_arguments.rb @@ -0,0 +1,7 @@ +describe :proc_call_block_args, shared: true do + it "can receive block arguments" do + Proc.new {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2 + lambda {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2 + proc {|&b| b.send(@method)}.send(@method) {1 + 1}.should == 2 + end +end diff --git a/spec/rubyspec/core/proc/shared/dup.rb b/spec/rubyspec/core/proc/shared/dup.rb new file mode 100644 index 0000000000..fb6fff299d --- /dev/null +++ b/spec/rubyspec/core/proc/shared/dup.rb @@ -0,0 +1,10 @@ +describe :proc_dup, shared: true do + it "returns a copy of self" do + a = lambda { "hello" } + b = a.send(@method) + + a.should_not equal(b) + + a.call.should == b.call + end +end diff --git a/spec/rubyspec/core/proc/shared/equal.rb b/spec/rubyspec/core/proc/shared/equal.rb new file mode 100644 index 0000000000..c2735ffb2f --- /dev/null +++ b/spec/rubyspec/core/proc/shared/equal.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe :proc_equal, shared: true do + it "is a public method" do + Proc.should have_public_instance_method(@method, false) + end + + it "returns true if self and other are the same object" do + p = proc { :foo } + p.send(@method, p).should be_true + + p = Proc.new { :foo } + p.send(@method, p).should be_true + + p = lambda { :foo } + p.send(@method, p).should be_true + end + + it "returns true if other is a dup of the original" do + p = proc { :foo } + p.send(@method, p.dup).should be_true + + p = Proc.new { :foo } + p.send(@method, p.dup).should be_true + + p = lambda { :foo } + p.send(@method, p.dup).should be_true + end + + # identical here means the same method invocation. + it "returns false when bodies are the same but capture env is not identical" do + a = ProcSpecs.proc_for_1 + b = ProcSpecs.proc_for_1 + + a.send(@method, b).should be_false + end + + it "returns true if both procs have the same body and environment" do + p = proc { :foo } + p2 = proc { :foo } + p.send(@method, p2).should be_true + end + + it "returns true if both lambdas with the same body and environment" do + x = lambda { :foo } + x2 = lambda { :foo } + x.send(@method, x2).should be_true + end + + it "returns true if both different kinds of procs with the same body and env" do + p = lambda { :foo } + p2 = proc { :foo } + p.send(@method, p2).should be_true + + x = proc { :bar } + x2 = lambda { :bar } + x.send(@method, x2).should be_true + end + + it "returns false if other is not a Proc" do + p = proc { :foo } + p.send(@method, []).should be_false + + p = Proc.new { :foo } + p.send(@method, Object.new).should be_false + + p = lambda { :foo } + p.send(@method, :foo).should be_false + end + + it "returns false if self and other are both procs but have different bodies" do + p = proc { :bar } + p2 = proc { :foo } + p.send(@method, p2).should be_false + end + + it "returns false if self and other are both lambdas but have different bodies" do + p = lambda { :foo } + p2 = lambda { :bar } + p.send(@method, p2).should be_false + end +end + +describe :proc_equal_undefined, shared: true do + it "is not defined" do + Proc.should_not have_instance_method(@method, false) + end + + it "returns false if other is a dup of the original" do + p = proc { :foo } + p.send(@method, p.dup).should be_false + + p = Proc.new { :foo } + p.send(@method, p.dup).should be_false + + p = lambda { :foo } + p.send(@method, p.dup).should be_false + end +end diff --git a/spec/rubyspec/core/proc/shared/to_s.rb b/spec/rubyspec/core/proc/shared/to_s.rb new file mode 100644 index 0000000000..c3f82a73f3 --- /dev/null +++ b/spec/rubyspec/core/proc/shared/to_s.rb @@ -0,0 +1,27 @@ +describe :proc_to_s, shared: true do + describe "for a proc created with Proc.new" do + it "returns a description optionally including file and line number" do + Proc.new { "hello" }.send(@method).should =~ /^#$/ + end + end + + describe "for a proc created with lambda" do + it "returns a description including '(lambda)' and optionally including file and line number" do + lambda { "hello" }.send(@method).should =~ /^#$/ + end + end + + describe "for a proc created with proc" do + it "returns a description optionally including file and line number" do + proc { "hello" }.send(@method).should =~ /^#$/ + end + end + + describe "for a proc created with UnboundMethod#to_proc" do + it "returns a description including '(lambda)' and optionally including file and line number" do + def hello; end + + method("hello").to_proc.send(@method).should =~ /^#$/ + end + end +end diff --git a/spec/rubyspec/core/proc/source_location_spec.rb b/spec/rubyspec/core/proc/source_location_spec.rb new file mode 100644 index 0000000000..311d13c050 --- /dev/null +++ b/spec/rubyspec/core/proc/source_location_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/source_location', __FILE__) + +describe "Proc#source_location" do + before :each do + @proc = ProcSpecs::SourceLocation.my_proc + @lambda = ProcSpecs::SourceLocation.my_lambda + @proc_new = ProcSpecs::SourceLocation.my_proc_new + @method = ProcSpecs::SourceLocation.my_method + end + + it "returns an Array" do + @proc.source_location.should be_an_instance_of(Array) + @proc_new.source_location.should be_an_instance_of(Array) + @lambda.source_location.should be_an_instance_of(Array) + @method.source_location.should be_an_instance_of(Array) + end + + it "sets the first value to the path of the file in which the proc was defined" do + file = @proc.source_location.first + file.should be_an_instance_of(String) + file.should == File.dirname(__FILE__) + '/fixtures/source_location.rb' + + file = @proc_new.source_location.first + file.should be_an_instance_of(String) + file.should == File.dirname(__FILE__) + '/fixtures/source_location.rb' + + file = @lambda.source_location.first + file.should be_an_instance_of(String) + file.should == File.dirname(__FILE__) + '/fixtures/source_location.rb' + + file = @method.source_location.first + file.should be_an_instance_of(String) + file.should == File.dirname(__FILE__) + '/fixtures/source_location.rb' + end + + it "sets the last value to a Fixnum representing the line on which the proc was defined" do + line = @proc.source_location.last + line.should be_an_instance_of(Fixnum) + line.should == 4 + + line = @proc_new.source_location.last + line.should be_an_instance_of(Fixnum) + line.should == 12 + + line = @lambda.source_location.last + line.should be_an_instance_of(Fixnum) + line.should == 8 + + line = @method.source_location.last + line.should be_an_instance_of(Fixnum) + line.should == 15 + end + + it "works even if the proc was created on the same line" do + proc { true }.source_location.should == [__FILE__, __LINE__] + Proc.new { true }.source_location.should == [__FILE__, __LINE__] + lambda { true }.source_location.should == [__FILE__, __LINE__] + end + + it "returns the first line of a multi-line proc (i.e. the line containing 'proc do')" do + ProcSpecs::SourceLocation.my_multiline_proc.source_location.last.should == 20 + ProcSpecs::SourceLocation.my_multiline_proc_new.source_location.last.should == 34 + ProcSpecs::SourceLocation.my_multiline_lambda.source_location.last.should == 27 + end + + it "returns the location of the proc's body; not necessarily the proc itself" do + ProcSpecs::SourceLocation.my_detached_proc.source_location.last.should == 41 + ProcSpecs::SourceLocation.my_detached_proc_new.source_location.last.should == 51 + ProcSpecs::SourceLocation.my_detached_lambda.source_location.last.should == 46 + end +end diff --git a/spec/rubyspec/core/proc/to_proc_spec.rb b/spec/rubyspec/core/proc/to_proc_spec.rb new file mode 100644 index 0000000000..e5637c9f41 --- /dev/null +++ b/spec/rubyspec/core/proc/to_proc_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Proc#to_proc" do + it "returns self" do + [Proc.new {}, lambda {}, proc {}].each { |p| + p.to_proc.should equal(p) + } + end +end diff --git a/spec/rubyspec/core/proc/to_s_spec.rb b/spec/rubyspec/core/proc/to_s_spec.rb new file mode 100644 index 0000000000..e0b248c369 --- /dev/null +++ b/spec/rubyspec/core/proc/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "Proc#to_s" do + it_behaves_like :proc_to_s, :to_s +end diff --git a/spec/rubyspec/core/proc/yield_spec.rb b/spec/rubyspec/core/proc/yield_spec.rb new file mode 100644 index 0000000000..930e44ea78 --- /dev/null +++ b/spec/rubyspec/core/proc/yield_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/call', __FILE__) +require File.expand_path('../shared/call_arguments', __FILE__) + +describe "Proc#yield" do + it_behaves_like :proc_call, :yield + it_behaves_like :proc_call_block_args, :yield +end + +describe "Proc#yield on a Proc created with Proc.new" do + it_behaves_like :proc_call_on_proc_new, :yield +end + +describe "Proc#yield on a Proc created with Kernel#lambda or Kernel#proc" do + it_behaves_like :proc_call_on_proc_or_lambda, :yield +end diff --git a/spec/rubyspec/core/process/abort_spec.rb b/spec/rubyspec/core/process/abort_spec.rb new file mode 100644 index 0000000000..f7113c8b7e --- /dev/null +++ b/spec/rubyspec/core/process/abort_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/process/abort', __FILE__) + +describe "Process.abort" do + it_behaves_like :process_abort, :abort, Process +end diff --git a/spec/rubyspec/core/process/constants_spec.rb b/spec/rubyspec/core/process/constants_spec.rb new file mode 100644 index 0000000000..2fa93ad8bf --- /dev/null +++ b/spec/rubyspec/core/process/constants_spec.rb @@ -0,0 +1,63 @@ + +describe "Process::Constants" do + platform_is :darwin, :netbsd, :freebsd do + it "has the correct constant values on BSD-like systems" do + Process::WNOHANG.should == 1 + Process::WUNTRACED.should == 2 + Process::PRIO_PROCESS.should == 0 + Process::PRIO_PGRP.should == 1 + Process::PRIO_USER.should == 2 + Process::RLIM_INFINITY.should == 9223372036854775807 + Process::RLIMIT_CPU.should == 0 + Process::RLIMIT_FSIZE.should == 1 + Process::RLIMIT_DATA.should == 2 + Process::RLIMIT_STACK.should == 3 + Process::RLIMIT_CORE.should == 4 + Process::RLIMIT_RSS.should == 5 + Process::RLIMIT_MEMLOCK.should == 6 + Process::RLIMIT_NPROC.should == 7 + Process::RLIMIT_NOFILE.should == 8 + end + end + + platform_is :darwin do + it "has the correct constant values on Darwin" do + Process::RLIM_SAVED_MAX.should == 9223372036854775807 + Process::RLIM_SAVED_CUR.should == 9223372036854775807 + Process::RLIMIT_AS.should == 5 + end + end + + platform_is :linux do + it "has the correct constant values on Linux" do + Process::WNOHANG.should == 1 + Process::WUNTRACED.should == 2 + Process::PRIO_PROCESS.should == 0 + Process::PRIO_PGRP.should == 1 + Process::PRIO_USER.should == 2 + Process::RLIMIT_CPU.should == 0 + Process::RLIMIT_FSIZE.should == 1 + Process::RLIMIT_DATA.should == 2 + Process::RLIMIT_STACK.should == 3 + Process::RLIMIT_CORE.should == 4 + Process::RLIMIT_RSS.should == 5 + Process::RLIMIT_NPROC.should == 6 + Process::RLIMIT_NOFILE.should == 7 + Process::RLIMIT_MEMLOCK.should == 8 + Process::RLIMIT_AS.should == 9 + + # These values appear to change according to the platform. + values = [4294967295, 9223372036854775807, 18446744073709551615] + values.include?(Process::RLIM_INFINITY).should be_true + values.include?(Process::RLIM_SAVED_MAX).should be_true + values.include?(Process::RLIM_SAVED_CUR).should be_true + end + end + + platform_is :netbsd, :freebsd do + it "Process::RLIMIT_SBSIZE" do + Process::RLIMIT_SBSIZE.should == 9 # FIXME: what's it equal? + Process::RLIMIT_AS.should == 10 + end + end +end diff --git a/spec/rubyspec/core/process/daemon_spec.rb b/spec/rubyspec/core/process/daemon_spec.rb new file mode 100644 index 0000000000..f68ddfe253 --- /dev/null +++ b/spec/rubyspec/core/process/daemon_spec.rb @@ -0,0 +1,123 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +platform_is_not :windows do + describe :process_daemon_keep_stdio_open_false, shared: true do + it "redirects stdout to /dev/null" do + @daemon.invoke("keep_stdio_open_false_stdout", @object).should == "" + end + + it "redirects stderr to /dev/null" do + @daemon.invoke("keep_stdio_open_false_stderr", @object).should == "" + end + + it "redirects stdin to /dev/null" do + @daemon.invoke("keep_stdio_open_false_stdin", @object).should == "" + end + + it "does not close open files" do + @daemon.invoke("keep_stdio_open_files", @object).should == "false" + end + end + + describe :process_daemon_keep_stdio_open_true, shared: true do + it "does not redirect stdout to /dev/null" do + @daemon.invoke("keep_stdio_open_true_stdout", @object).should == "writing to stdout" + end + + it "does not redirect stderr to /dev/null" do + @daemon.invoke("keep_stdio_open_true_stderr", @object).should == "writing to stderr" + end + + it "does not redirect stdin to /dev/null" do + @daemon.invoke("keep_stdio_open_true_stdin", @object).should == "reading from stdin" + end + + it "does not close open files" do + @daemon.invoke("keep_stdio_open_files", @object).should == "false" + end + end + + describe "Process.daemon" do + before :each do + @invoke_dir = Dir.pwd + @daemon = ProcessSpecs::Daemonizer.new + end + + after :each do + rm_r @daemon.input, @daemon.data if @daemon + end + + it "returns 0" do + @daemon.invoke("return_value").should == "0" + end + + it "has a different PID after daemonizing" do + parent, daemon = @daemon.invoke("pid").split(":") + parent.should_not == daemon + end + + it "has a different process group after daemonizing" do + parent, daemon = @daemon.invoke("process_group").split(":") + parent.should_not == daemon + end + + it "does not run existing at_exit handlers when daemonizing" do + @daemon.invoke("daemonizing_at_exit").should == "not running at_exit" + end + + it "runs at_exit handlers when the daemon exits" do + @daemon.invoke("daemon_at_exit").should == "running at_exit" + end + + it "changes directory to the root directory if the first argument is not given" do + @daemon.invoke("stay_in_dir").should == "/" + end + + it "changes directory to the root directory if the first argument is false" do + @daemon.invoke("stay_in_dir", [false]).should == "/" + end + + it "changes directory to the root directory if the first argument is nil" do + @daemon.invoke("stay_in_dir", [nil]).should == "/" + end + + it "does not change to the root directory if the first argument is true" do + @daemon.invoke("stay_in_dir", [true]).should == @invoke_dir + end + + it "does not change to the root directory if the first argument is non-false" do + @daemon.invoke("stay_in_dir", [:yes]).should == @invoke_dir + end + + describe "when the second argument is not given" do + it_behaves_like :process_daemon_keep_stdio_open_false, nil, [false] + end + + describe "when the second argument is false" do + it_behaves_like :process_daemon_keep_stdio_open_false, nil, [false, false] + end + + describe "when the second argument is nil" do + it_behaves_like :process_daemon_keep_stdio_open_false, nil, [false, nil] + end + + describe "when the second argument is true" do + it_behaves_like :process_daemon_keep_stdio_open_true, nil, [false, true] + end + + describe "when the second argument is non-false" do + it_behaves_like :process_daemon_keep_stdio_open_true, nil, [false, :yes] + end + end +end + +platform_is :windows do + describe "Process.daemon" do + it "raises a NotImplementedError" do + lambda { + Process.daemon + }.should raise_error(NotImplementedError) + end + end +end diff --git a/spec/rubyspec/core/process/detach_spec.rb b/spec/rubyspec/core/process/detach_spec.rb new file mode 100644 index 0000000000..7a38e290ce --- /dev/null +++ b/spec/rubyspec/core/process/detach_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.detach" do + platform_is_not :windows do + it "returns a thread" do + pid = Process.fork { Process.exit! } + thr = Process.detach(pid) + thr.should be_kind_of(Thread) + thr.join + end + + it "produces the exit Process::Status as the thread value" do + pid = Process.fork { Process.exit! } + thr = Process.detach(pid) + thr.join + + status = thr.value + status.should be_kind_of(Process::Status) + status.pid.should == pid + end + + platform_is_not :openbsd do + it "reaps the child process's status automatically" do + pid = Process.fork { Process.exit! } + Process.detach(pid).join + lambda { Process.waitpid(pid) }.should raise_error(Errno::ECHILD) + end + end + + it "sets the :pid thread-local to the PID" do + pid = Process.fork { Process.exit! } + thr = Process.detach(pid) + thr.join + + thr[:pid].should == pid + end + + it "provides a #pid method on the returned thread which returns the PID" do + pid = Process.fork { Process.exit! } + thr = Process.detach(pid) + thr.join + + thr.pid.should == pid + end + end +end diff --git a/spec/rubyspec/core/process/egid_spec.rb b/spec/rubyspec/core/process/egid_spec.rb new file mode 100644 index 0000000000..316343944c --- /dev/null +++ b/spec/rubyspec/core/process/egid_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.egid" do + it "returns the effective group ID for this process" do + Process.egid.should be_kind_of(Integer) + end + + it "also goes by Process::GID.eid" do + Process::GID.eid.should == Process.egid + end + + it "also goes by Process::Sys.getegid" do + Process::Sys.getegid.should == Process.egid + end +end + +describe "Process.egid=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/euid_spec.rb b/spec/rubyspec/core/process/euid_spec.rb new file mode 100644 index 0000000000..1855ef66f5 --- /dev/null +++ b/spec/rubyspec/core/process/euid_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.euid" do + it "returns the effective user ID for this process" do + Process.euid.should be_kind_of(Fixnum) + end + + it "also goes by Process::UID.eid" do + Process::UID.eid.should == Process.euid + end + + it "also goes by Process::Sys.geteuid" do + Process::Sys.geteuid.should == Process.euid + end +end + +describe "Process.euid=" do + + platform_is_not :windows do + it "raises TypeError if not passed an Integer" do + lambda { Process.euid = Object.new }.should raise_error(TypeError) + end + + as_user do + it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id" do + lambda { (Process.euid = 0)}.should raise_error(Errno::EPERM) + end + + it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id from username" do + lambda { Process.euid = "root" }.should raise_error(Errno::EPERM) + end + end + + as_superuser do + describe "if run by a superuser" do + with_feature :fork do + it "sets the effective user id for the current process if run by a superuser" do + read, write = IO.pipe + pid = Process.fork do + begin + read.close + Process.euid = 1 + write << Process.euid + write.close + rescue Exception => e + write << e << e.backtrace + end + Process.exit! + end + write.close + euid = read.gets + euid.should == "1" + Process.wait pid + end + end + end + end + end +end diff --git a/spec/rubyspec/core/process/exec_spec.rb b/spec/rubyspec/core/process/exec_spec.rb new file mode 100644 index 0000000000..b90720b533 --- /dev/null +++ b/spec/rubyspec/core/process/exec_spec.rb @@ -0,0 +1,216 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.exec" do + it "raises Errno::ENOENT for an empty string" do + lambda { Process.exec "" }.should raise_error(Errno::ENOENT) + end + + it "raises Errno::ENOENT for a command which does not exist" do + lambda { Process.exec "bogus-noent-script.sh" }.should raise_error(Errno::ENOENT) + end + + it "raises an ArgumentError if the command includes a null byte" do + lambda { Process.exec "\000" }.should raise_error(ArgumentError) + end + + unless File.executable?(__FILE__) # Some FS (e.g. vboxfs) locate all files executable + platform_is_not :windows do + it "raises Errno::EACCES when the file does not have execute permissions" do + lambda { Process.exec __FILE__ }.should raise_error(Errno::EACCES) + end + end + + platform_is :windows do + it "raises Errno::ENOEXEC when the file is not an executable file" do + lambda { Process.exec __FILE__ }.should raise_error(Errno::ENOEXEC) + end + end + end + + platform_is_not :openbsd do + it "raises Errno::EACCES when passed a directory" do + lambda { Process.exec File.dirname(__FILE__) }.should raise_error(Errno::EACCES) + end + end + + platform_is :openbsd do + it "raises Errno::EISDIR when passed a directory" do + lambda { Process.exec File.dirname(__FILE__) }.should raise_error(Errno::EISDIR) + end + end + + it "runs the specified command, replacing current process" do + ruby_exe('Process.exec "echo hello"; puts "fail"', escape: true).should == "hello\n" + end + + it "sets the current directory when given the :chdir option" do + tmpdir = tmp("")[0..-2] + platform_is_not :windows do + ruby_exe("Process.exec(\"pwd\", chdir: #{tmpdir.inspect})", escape: true).should == "#{tmpdir}\n" + end + platform_is :windows do + ruby_exe("Process.exec(\"cd\", chdir: #{tmpdir.inspect})", escape: true).tr('\\', '/').should == "#{tmpdir}\n" + end + end + + it "flushes STDOUT upon exit when it's not set to sync" do + ruby_exe("STDOUT.sync = false; STDOUT.write 'hello'").should == "hello" + end + + it "flushes STDERR upon exit when it's not set to sync" do + ruby_exe("STDERR.sync = false; STDERR.write 'hello'", args: "2>&1").should == "hello" + end + + describe "with a single argument" do + before :each do + @dir = tmp("exec_with_dir", false) + Dir.mkdir @dir + + @name = "some_file" + @path = tmp("exec_with_dir/#{@name}", false) + touch @path + end + + after :each do + rm_r @path + rm_r @dir + end + + platform_is_not :windows do + it "subjects the specified command to shell expansion" do + result = Dir.chdir(@dir) do + ruby_exe('Process.exec "echo *"', escape: true) + end + result.chomp.should == @name + end + + it "creates an argument array with shell parsing semantics for whitespace" do + ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n" + end + end + + platform_is :windows do + # There is no shell expansion on Windows + it "does not subject the specified command to shell expansion on Windows" do + result = Dir.chdir(@dir) do + ruby_exe('Process.exec "echo *"', escape: true) + end + result.should == "*\n" + end + + it "does not create an argument array with shell parsing semantics for whitespace on Windows" do + ruby_exe('Process.exec "echo a b c d"', escape: true).should == "a b c d\n" + end + end + + end + + describe "with multiple arguments" do + it "does not subject the arguments to shell expansion" do + cmd = '"echo", "*"' + platform_is :windows do + cmd = '"cmd.exe", "/C", "echo", "*"' + end + ruby_exe("Process.exec #{cmd}", escape: true).should == "*\n" + end + end + + describe "(environment variables)" do + before :each do + ENV["FOO"] = "FOO" + end + + after :each do + ENV["FOO"] = nil + end + + var = '$FOO' + platform_is :windows do + var = '%FOO%' + end + + it "sets environment variables in the child environment" do + ruby_exe('Process.exec({"FOO" => "BAR"}, "echo ' + var + '")', escape: true).should == "BAR\n" + end + + it "unsets environment variables whose value is nil" do + platform_is_not :windows do + ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == "\n" + end + platform_is :windows do + # On Windows, echo-ing a non-existent env var is treated as echo-ing any other string of text + ruby_exe('Process.exec({"FOO" => nil}, "echo ' + var + '")', escape: true).should == var + "\n" + end + end + + it "coerces environment argument using to_hash" do + ruby_exe('o = Object.new; def o.to_hash; {"FOO" => "BAR"}; end; Process.exec(o, "echo ' + var + '")', escape: true).should == "BAR\n" + end + + it "unsets other environment variables when given a true :unsetenv_others option" do + platform_is_not :windows do + ruby_exe('Process.exec("echo ' + var + '", unsetenv_others: true)', escape: true).should == "\n" + end + platform_is :windows do + ruby_exe('Process.exec("' + ENV['COMSPEC'].gsub('\\', '\\\\\\') + ' /C echo ' + var + '", unsetenv_others: true)', escape: true).should == var + "\n" + end + end + end + + describe "with a command array" do + it "uses the first element as the command name and the second as the argv[0] value" do + platform_is_not :windows do + ruby_exe('Process.exec(["/bin/sh", "argv_zero"], "-c", "echo $0")', escape: true).should == "argv_zero\n" + end + platform_is :windows do + ruby_exe('Process.exec(["cmd.exe", "/C"], "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n" + end + end + + it "coerces the argument using to_ary" do + platform_is_not :windows do + ruby_exe('o = Object.new; def o.to_ary; ["/bin/sh", "argv_zero"]; end; Process.exec(o, "-c", "echo $0")', escape: true).should == "argv_zero\n" + end + platform_is :windows do + ruby_exe('o = Object.new; def o.to_ary; ["cmd.exe", "/C"]; end; Process.exec(o, "/C", "echo", "argv_zero")', escape: true).should == "argv_zero\n" + end + end + + it "raises an ArgumentError if the Array does not have exactly two elements" do + lambda { Process.exec([]) }.should raise_error(ArgumentError) + lambda { Process.exec([:a]) }.should raise_error(ArgumentError) + lambda { Process.exec([:a, :b, :c]) }.should raise_error(ArgumentError) + end + end + + platform_is_not :windows do + describe "with an options Hash" do + describe "with Integer option keys" do + before :each do + @name = tmp("exec_fd_map.txt") + @child_fd_file = tmp("child_fd_file.txt") + end + + after :each do + rm_r @name, @child_fd_file + end + + it "maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value" do + map_fd_fixture = fixture __FILE__, "map_fd.rb" + cmd = <<-EOC + f = File.open("#{@name}", "w+") + child_fd = f.fileno + 1 + File.open("#{@child_fd_file}", "w") { |io| io.print child_fd } + Process.exec "#{ruby_cmd(map_fd_fixture)} \#{child_fd}", { child_fd => f } + EOC + + ruby_exe(cmd, escape: true) + child_fd = IO.read(@child_fd_file).to_i + child_fd.to_i.should > STDERR.fileno + + File.read(@name).should == "writing to fd: #{child_fd}" + end + end + end + end +end diff --git a/spec/rubyspec/core/process/exit_spec.rb b/spec/rubyspec/core/process/exit_spec.rb new file mode 100644 index 0000000000..7cbd0c363e --- /dev/null +++ b/spec/rubyspec/core/process/exit_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/process/exit', __FILE__) + +describe "Process.exit" do + it_behaves_like :process_exit, :exit, Process +end + +describe "Process.exit!" do + it_behaves_like :process_exit!, :exit!, Process +end diff --git a/spec/rubyspec/core/process/fixtures/common.rb b/spec/rubyspec/core/process/fixtures/common.rb new file mode 100644 index 0000000000..abfd05bb18 --- /dev/null +++ b/spec/rubyspec/core/process/fixtures/common.rb @@ -0,0 +1,79 @@ +module ProcessSpecs + class Daemonizer + attr_reader :input, :data + + def initialize + # Fast feedback for implementations without Process.daemon + raise NotImplementedError, "Process.daemon is not implemented" unless Process.respond_to? :daemon + + @script = fixture __FILE__, "daemon.rb" + @input = tmp("process_daemon_input_file") + @data = tmp("process_daemon_data_file") + @args = [] + end + + def wait_for_daemon + sleep 0.001 until File.exist?(@data) and File.size?(@data) + end + + def invoke(behavior, arguments=[]) + args = Marshal.dump(arguments).unpack("H*") + args << @input << @data << behavior + + ruby_exe @script, args: args + + wait_for_daemon + + return unless File.exist? @data + + File.open(@data, "rb") { |f| return f.read.chomp } + end + end + + class Signalizer + attr_reader :pid_file, :pid + + def initialize(scenario=nil) + platform_is :windows do + fail "not supported on windows" + end + @script = fixture __FILE__, "kill.rb" + @pid = nil + @pid_file = tmp("process_kill_signal_file") + rm_r @pid_file + + @thread = Thread.new do + Thread.current.abort_on_exception = true + args = [@pid_file] + args << scenario if scenario + args << RUBY_EXE.inspect if scenario + @result = ruby_exe @script, args: args + end + Thread.pass while @thread.status and !File.exist?(@pid_file) + while @thread.status && (@pid.nil? || @pid == 0) + @pid = IO.read(@pid_file).chomp.to_i + end + end + + def wait_on_result + # Ensure the process exits + begin + Process.kill :TERM, pid if pid + rescue Errno::ESRCH + # Ignore the process not existing + end + + @thread.join + end + + def cleanup + wait_on_result + rm_r pid_file + end + + def result + wait_on_result + @result.chomp if @result + end + end +end diff --git a/spec/rubyspec/core/process/fixtures/daemon.rb b/spec/rubyspec/core/process/fixtures/daemon.rb new file mode 100644 index 0000000000..772df2d09e --- /dev/null +++ b/spec/rubyspec/core/process/fixtures/daemon.rb @@ -0,0 +1,111 @@ +module ProcessSpecs + class Daemon + def initialize(argv) + args, @input, @data, @behavior = argv + @args = Marshal.load [args].pack("H*") + @no_at_exit = false + end + + def run + send @behavior + + # Exit without running any at_exit handlers + exit!(0) if @no_at_exit + end + + def write(data) + File.open(@data, "wb") { |f| f.puts data } + end + + def daemonizing_at_exit + at_exit do + write "running at_exit" + end + + @no_at_exit = true + Process.daemon + write "not running at_exit" + end + + def return_value + write Process.daemon.to_s + end + + def pid + parent = Process.pid + Process.daemon + daemon = Process.pid + write "#{parent}:#{daemon}" + end + + def process_group + parent = Process.getpgrp + Process.daemon + daemon = Process.getpgrp + write "#{parent}:#{daemon}" + end + + def daemon_at_exit + at_exit do + write "running at_exit" + end + + Process.daemon + end + + def stay_in_dir + Process.daemon(*@args) + write Dir.pwd + end + + def keep_stdio_open_false_stdout + Process.daemon(*@args) + $stdout.write "writing to stdout" + write "" + end + + def keep_stdio_open_false_stderr + Process.daemon(*@args) + $stderr.write "writing to stderr" + write "" + end + + def keep_stdio_open_false_stdin + Process.daemon(*@args) + + # Reading from /dev/null will return right away. If STDIN were not + # /dev/null, reading would block and the spec would hang. This is not a + # perfect way to spec the behavior but it works. + write $stdin.read + end + + def keep_stdio_open_true_stdout + $stdout.reopen @data + Process.daemon(*@args) + $stdout.write "writing to stdout" + end + + def keep_stdio_open_true_stderr + $stderr.reopen @data + Process.daemon(*@args) + $stderr.write "writing to stderr" + end + + def keep_stdio_open_true_stdin + File.open(@input, "w") { |f| f.puts "reading from stdin" } + + $stdin.reopen @input, "r" + Process.daemon(*@args) + write $stdin.read + end + + def keep_stdio_open_files + file = File.open @input, "w" + + Process.daemon(*@args) + write file.closed? + end + end +end + +ProcessSpecs::Daemon.new(ARGV).run diff --git a/spec/rubyspec/core/process/fixtures/env.rb b/spec/rubyspec/core/process/fixtures/env.rb new file mode 100644 index 0000000000..2d626caf26 --- /dev/null +++ b/spec/rubyspec/core/process/fixtures/env.rb @@ -0,0 +1 @@ +File.write ARGV[0], ENV["FOO"] diff --git a/spec/rubyspec/core/process/fixtures/kill.rb b/spec/rubyspec/core/process/fixtures/kill.rb new file mode 100644 index 0000000000..dfebc5b3c3 --- /dev/null +++ b/spec/rubyspec/core/process/fixtures/kill.rb @@ -0,0 +1,49 @@ +require 'thread' + +pid_file = ARGV.shift +scenario = ARGV.shift +ruby_exe = ARGV.shift + +# We must do this first otherwise there will be a race with the process that +# creates this process and the TERM signal below could go to that process +# instead, which will likely abort the specs process. +Process.setsid if scenario && Process.respond_to?(:setsid) + +signaled = false +mutex = Mutex.new + +Signal.trap(:TERM) do + if mutex.try_lock + unless signaled + signaled = true + STDOUT.puts "signaled" + STDOUT.flush + end + end +end + +File.open(pid_file, "wb") { |f| f.puts Process.pid } + +if scenario + # We are sending a signal to ourselves or the process group + process = Process.respond_to?(:getpgid) ? "Process.getpgid(Process.pid)" : "Process.pid" + + case scenario + when "self" + signal = %["SIGTERM"] + process = "0" + when "group_numeric" + signal = %[-Signal.list["TERM"]] + when "group_short_string" + signal = %["-TERM"] + when "group_full_string" + signal = %["-SIGTERM"] + else + raise "unknown scenario: #{scenario.inspect}" + end + + cmd = %[#{ruby_exe} -e 'Process.kill(#{signal}, #{process})'] + Thread.new { system cmd }.join +end + +sleep 0.1 until signaled diff --git a/spec/rubyspec/core/process/fixtures/map_fd.rb b/spec/rubyspec/core/process/fixtures/map_fd.rb new file mode 100644 index 0000000000..fc542625b0 --- /dev/null +++ b/spec/rubyspec/core/process/fixtures/map_fd.rb @@ -0,0 +1,8 @@ +fd = ARGV.shift.to_i + +f = File.for_fd fd +begin + f.write "writing to fd: #{fd}" +ensure + f.close +end diff --git a/spec/rubyspec/core/process/fixtures/print.rb b/spec/rubyspec/core/process/fixtures/print.rb new file mode 100644 index 0000000000..3697f7dc93 --- /dev/null +++ b/spec/rubyspec/core/process/fixtures/print.rb @@ -0,0 +1 @@ +print :glark diff --git a/spec/rubyspec/core/process/fork_spec.rb b/spec/rubyspec/core/process/fork_spec.rb new file mode 100644 index 0000000000..2de2231dc5 --- /dev/null +++ b/spec/rubyspec/core/process/fork_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/process/fork', __FILE__) + +describe "Process.fork" do + it_behaves_like :process_fork, :fork, Process +end diff --git a/spec/rubyspec/core/process/getpgid_spec.rb b/spec/rubyspec/core/process/getpgid_spec.rb new file mode 100644 index 0000000000..b92fb15152 --- /dev/null +++ b/spec/rubyspec/core/process/getpgid_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.getpgid" do + platform_is_not :windows do + it "coerces the argument to an Integer" do + Process.getpgid(mock_int(Process.pid)).should == Process.getpgrp + end + + it "returns the process group ID for the given process id" do + Process.getpgid(Process.pid).should == Process.getpgrp + end + + it "returns the process group ID for the calling process id when passed 0" do + Process.getpgid(0).should == Process.getpgrp + end + end +end diff --git a/spec/rubyspec/core/process/getpgrp_spec.rb b/spec/rubyspec/core/process/getpgrp_spec.rb new file mode 100644 index 0000000000..9643a16b2a --- /dev/null +++ b/spec/rubyspec/core/process/getpgrp_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +# see setpgrp_spec.rb + +describe "Process.getpgrp" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/getpriority_spec.rb b/spec/rubyspec/core/process/getpriority_spec.rb new file mode 100644 index 0000000000..68307b9bd5 --- /dev/null +++ b/spec/rubyspec/core/process/getpriority_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.getpriority" do + platform_is_not :windows do + + it "coerces arguments to Integers" do + ret = Process.getpriority mock_int(Process::PRIO_PROCESS), mock_int(0) + ret.should be_kind_of(Fixnum) + end + + it "gets the scheduling priority for a specified process" do + Process.getpriority(Process::PRIO_PROCESS, 0).should be_kind_of(Fixnum) + end + + it "gets the scheduling priority for a specified process group" do + Process.getpriority(Process::PRIO_PGRP, 0).should be_kind_of(Fixnum) + end + + it "gets the scheduling priority for a specified user" do + Process.getpriority(Process::PRIO_USER, 0).should be_kind_of(Fixnum) + end + end +end diff --git a/spec/rubyspec/core/process/getrlimit_spec.rb b/spec/rubyspec/core/process/getrlimit_spec.rb new file mode 100644 index 0000000000..7924d43081 --- /dev/null +++ b/spec/rubyspec/core/process/getrlimit_spec.rb @@ -0,0 +1,91 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +platform_is :aix do + # In AIX, if getrlimit(2) is called multiple times with RLIMIT_DATA, + # the first call and the subequent calls return slightly different + # values of rlim_cur, even if the process does nothing between + # the calls. This behavior causes some of the tests in this spec + # to fail, so call Process.getrlimit(:DATA) once and discard the result. + # Subsequent calls to Process.getrlimit(:DATA) should return + # a consistent value of rlim_cur. + Process.getrlimit(:DATA) +end + +platform_is_not :windows do + describe "Process.getrlimit" do + it "returns a two-element Array of Integers" do + result = Process.getrlimit Process::RLIMIT_CORE + result.size.should == 2 + result.first.should be_kind_of(Integer) + result.last.should be_kind_of(Integer) + end + + context "when passed an Object" do + before do + @resource = Process::RLIMIT_CORE + end + + it "calls #to_int to convert to an Integer" do + obj = mock("process getrlimit integer") + obj.should_receive(:to_int).and_return(@resource) + + Process.getrlimit(obj).should == Process.getrlimit(@resource) + end + + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("process getrlimit integer") + obj.should_receive(:to_int).and_return(nil) + + lambda { Process.getrlimit(obj) }.should raise_error(TypeError) + end + end + + context "when passed a Symbol" do + Process.constants.grep(/\ARLIMIT_/) do |fullname| + short = $' + it "coerces :#{short} into #{fullname}" do + Process.getrlimit(short.to_sym).should == Process.getrlimit(Process.const_get(fullname)) + end + end + + it "raises ArgumentError when passed an unknown resource" do + lambda { Process.getrlimit(:FOO) }.should raise_error(ArgumentError) + end + end + + context "when passed a String" do + Process.constants.grep(/\ARLIMIT_/) do |fullname| + short = $' + it "coerces '#{short}' into #{fullname}" do + Process.getrlimit(short).should == Process.getrlimit(Process.const_get(fullname)) + end + end + + it "raises ArgumentError when passed an unknown resource" do + lambda { Process.getrlimit("FOO") }.should raise_error(ArgumentError) + end + end + + context "when passed on Object" do + before do + @resource = Process::RLIMIT_CORE + end + + it "calls #to_str to convert to a String" do + obj = mock("process getrlimit string") + obj.should_receive(:to_str).and_return("CORE") + obj.should_not_receive(:to_int) + + Process.getrlimit(obj).should == Process.getrlimit(@resource) + end + + it "calls #to_int if #to_str does not return a String" do + obj = mock("process getrlimit string") + obj.should_receive(:to_str).and_return(nil) + obj.should_receive(:to_int).and_return(@resource) + + Process.getrlimit(obj).should == Process.getrlimit(@resource) + end + end + end +end diff --git a/spec/rubyspec/core/process/gid/change_privilege_spec.rb b/spec/rubyspec/core/process/gid/change_privilege_spec.rb new file mode 100644 index 0000000000..9670e4970f --- /dev/null +++ b/spec/rubyspec/core/process/gid/change_privilege_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.change_privilege" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid/eid_spec.rb b/spec/rubyspec/core/process/gid/eid_spec.rb new file mode 100644 index 0000000000..6d6a672fea --- /dev/null +++ b/spec/rubyspec/core/process/gid/eid_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.eid" do + it "needs to be reviewed for spec completeness" +end + +describe "Process::GID.eid=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid/grant_privilege_spec.rb b/spec/rubyspec/core/process/gid/grant_privilege_spec.rb new file mode 100644 index 0000000000..27373c7113 --- /dev/null +++ b/spec/rubyspec/core/process/gid/grant_privilege_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.grant_privilege" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid/re_exchange_spec.rb b/spec/rubyspec/core/process/gid/re_exchange_spec.rb new file mode 100644 index 0000000000..4f3a242680 --- /dev/null +++ b/spec/rubyspec/core/process/gid/re_exchange_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.re_exchange" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid/re_exchangeable_spec.rb b/spec/rubyspec/core/process/gid/re_exchangeable_spec.rb new file mode 100644 index 0000000000..1fc5298f26 --- /dev/null +++ b/spec/rubyspec/core/process/gid/re_exchangeable_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.re_exchangeable?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid/rid_spec.rb b/spec/rubyspec/core/process/gid/rid_spec.rb new file mode 100644 index 0000000000..fb5c51e412 --- /dev/null +++ b/spec/rubyspec/core/process/gid/rid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.rid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid/sid_available_spec.rb b/spec/rubyspec/core/process/gid/sid_available_spec.rb new file mode 100644 index 0000000000..59b2c3ae1e --- /dev/null +++ b/spec/rubyspec/core/process/gid/sid_available_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.sid_available?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid/switch_spec.rb b/spec/rubyspec/core/process/gid/switch_spec.rb new file mode 100644 index 0000000000..7ae6dae9a7 --- /dev/null +++ b/spec/rubyspec/core/process/gid/switch_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::GID.switch" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/gid_spec.rb b/spec/rubyspec/core/process/gid_spec.rb new file mode 100644 index 0000000000..c39c60a95f --- /dev/null +++ b/spec/rubyspec/core/process/gid_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.gid" do + platform_is_not :windows do + it "returns the correct gid for the user executing this process" do + current_gid_according_to_unix = `id -gr`.to_i + Process.gid.should == current_gid_according_to_unix + end + end + + it "also goes by Process::GID.rid" do + Process::GID.rid.should == Process.gid + end + + it "also goes by Process::Sys.getgid" do + Process::Sys.getgid.should == Process.gid + end +end + +describe "Process.gid=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/groups_spec.rb b/spec/rubyspec/core/process/groups_spec.rb new file mode 100644 index 0000000000..0669258bf3 --- /dev/null +++ b/spec/rubyspec/core/process/groups_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.groups" do + platform_is_not :windows do + it "gets an Array of the gids of groups in the supplemental group access list" do + groups = `id -G`.scan(/\d+/).map { |i| i.to_i } + gid = Process.gid + + expected = (groups.sort - [gid]).sort + actual = (Process.groups - [gid]).sort + actual.should == expected + end + + # NOTE: This is kind of sketchy. + it "sets the list of gids of groups in the supplemental group access list" do + groups = Process.groups + if Process.uid == 0 + Process.groups = [] + Process.groups.should == [] + Process.groups = groups + Process.groups.sort.should == groups.sort + else + platform_is :aix do + # setgroups() is not part of the POSIX standard, + # so its behavior varies from OS to OS. AIX allows a non-root + # process to set the supplementary group IDs, as long as + # they are presently in its supplementary group IDs. + # The order of the following tests matters. + # After this process executes "Process.groups = []" + # it should no longer be able to set any supplementary + # group IDs, even if it originally belonged to them. + # It should only be able to set its primary group ID. + Process.groups = groups + Process.groups.sort.should == groups.sort + Process.groups = [] + Process.groups.should == [] + Process.groups = [ Process.gid ] + Process.groups.should == [ Process.gid ] + supplementary = groups - [ Process.gid ] + if supplementary.length > 0 + lambda { Process.groups = supplementary }.should raise_error(Errno::EPERM) + end + end + platform_is_not :aix do + lambda { Process.groups = [] }.should raise_error(Errno::EPERM) + end + end + end + end +end + +describe "Process.groups=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/initgroups_spec.rb b/spec/rubyspec/core/process/initgroups_spec.rb new file mode 100644 index 0000000000..084c52652c --- /dev/null +++ b/spec/rubyspec/core/process/initgroups_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.initgroups" do + platform_is_not :windows do + it "initializes the supplemental group access list" do + name = `id -un`.strip + groups = Process.groups + gid = groups.max.to_i + 1 + augmented_groups = `id -G`.scan(/\d+/).map {|i| i.to_i} << gid + if Process.uid == 0 + Process.groups = [] + Process.initgroups(name, gid).sort.should == augmented_groups.sort + Process.groups.sort.should == augmented_groups.sort + Process.groups = groups + else + lambda { Process.initgroups(name, gid) }.should raise_error(Errno::EPERM) + end + end + end +end diff --git a/spec/rubyspec/core/process/kill_spec.rb b/spec/rubyspec/core/process/kill_spec.rb new file mode 100644 index 0000000000..c7b0e2a129 --- /dev/null +++ b/spec/rubyspec/core/process/kill_spec.rb @@ -0,0 +1,152 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "Process.kill" do + before :each do + @pid = Process.pid + end + + it "raises an ArgumentError for unknown signals" do + lambda { Process.kill("FOO", @pid) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed a lowercase signal name" do + lambda { Process.kill("term", @pid) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if signal is not a Fixnum or String" do + signal = mock("process kill signal") + signal.should_not_receive(:to_int) + + lambda { Process.kill(signal, @pid) }.should raise_error(ArgumentError) + end + + it "raises Errno::ESRCH if the process does not exist" do + pid = Process.spawn(ruby_cmd("sleep 10")) + Process.kill("SIGKILL", pid) + Process.wait(pid) + lambda { + Process.kill("SIGKILL", pid) + }.should raise_error(Errno::ESRCH) + end +end + +platform_is_not :windows do + describe "Process.kill" do + before :each do + @sp = ProcessSpecs::Signalizer.new + end + + after :each do + @sp.cleanup + end + + it "accepts a Symbol as a signal name" do + Process.kill(:SIGTERM, @sp.pid) + @sp.result.should == "signaled" + end + + it "accepts a String as signal name" do + Process.kill("SIGTERM", @sp.pid) + @sp.result.should == "signaled" + end + + it "accepts a signal name without the 'SIG' prefix" do + Process.kill("TERM", @sp.pid) + @sp.result.should == "signaled" + end + + it "accepts a signal name with the 'SIG' prefix" do + Process.kill("SIGTERM", @sp.pid) + @sp.result.should == "signaled" + end + + it "acceps an Integer as a signal value" do + Process.kill(15, @sp.pid) + @sp.result.should == "signaled" + end + + it "calls #to_int to coerce the pid to an Integer" do + Process.kill("SIGTERM", mock_int(@sp.pid)) + @sp.result.should == "signaled" + end + end + + describe "Process.kill" do + before :each do + @sp1 = ProcessSpecs::Signalizer.new + @sp2 = ProcessSpecs::Signalizer.new + end + + after :each do + @sp1.cleanup + @sp2.cleanup + end + + it "signals multiple processes" do + Process.kill("SIGTERM", @sp1.pid, @sp2.pid) + @sp1.result.should == "signaled" + @sp2.result.should == "signaled" + end + + it "returns the number of processes signaled" do + Process.kill("SIGTERM", @sp1.pid, @sp2.pid).should == 2 + end + end + + describe "Process.kill" do + before :each do + @sp = ProcessSpecs::Signalizer.new "self" + end + + after :each do + @sp.cleanup + end + + it "signals the process group if the PID is zero" do + @sp.result.should == "signaled" + end + end + + describe "Process.kill" do + before :each do + @sp = ProcessSpecs::Signalizer.new "group_numeric" + end + + after :each do + @sp.cleanup + end + + it "signals the process group if the signal number is negative" do + @sp.result.should == "signaled" + end + end + + describe "Process.kill" do + before :each do + @sp = ProcessSpecs::Signalizer.new "group_short_string" + end + + after :each do + @sp.cleanup + end + + it "signals the process group if the short signal name starts with a minus sign" do + @sp.result.should == "signaled" + end + end + + describe "Process.kill" do + before :each do + @sp = ProcessSpecs::Signalizer.new "group_full_string" + end + + after :each do + @sp.cleanup + end + + it "signals the process group if the full signal name starts with a minus sign" do + @sp.result.should == "signaled" + end + end +end diff --git a/spec/rubyspec/core/process/maxgroups_spec.rb b/spec/rubyspec/core/process/maxgroups_spec.rb new file mode 100644 index 0000000000..8a40a1aba6 --- /dev/null +++ b/spec/rubyspec/core/process/maxgroups_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +platform_is_not :windows do + describe "Process.maxgroups" do + it "returns the maximum number of gids allowed in the supplemental group access list" do + Process.maxgroups.should be_kind_of(Fixnum) + end + + it "sets the maximum number of gids allowed in the supplemental group access list" do + n = Process.maxgroups + begin + Process.maxgroups = n - 1 + Process.maxgroups.should == n - 1 + ensure + Process.maxgroups = n + end + end + end +end diff --git a/spec/rubyspec/core/process/pid_spec.rb b/spec/rubyspec/core/process/pid_spec.rb new file mode 100644 index 0000000000..114b20f11f --- /dev/null +++ b/spec/rubyspec/core/process/pid_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.pid" do + it "returns the process id of this process" do + pid = Process.pid + pid.should be_kind_of(Fixnum) + Process.pid.should == pid + end +end diff --git a/spec/rubyspec/core/process/ppid_spec.rb b/spec/rubyspec/core/process/ppid_spec.rb new file mode 100644 index 0000000000..ec6ce865ee --- /dev/null +++ b/spec/rubyspec/core/process/ppid_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.ppid" do + with_feature :fork do + it "returns the process id of the parent of this process" do + + read, write = IO.pipe + + child_pid = Process.fork { + read.close + write << "#{Process.ppid}\n" + write.close + exit! + } + + write.close + pid = read.gets + read.close + Process.wait(child_pid) + pid.to_i.should == Process.pid + end + end +end diff --git a/spec/rubyspec/core/process/set_proctitle_spec.rb b/spec/rubyspec/core/process/set_proctitle_spec.rb new file mode 100644 index 0000000000..e004f8efc9 --- /dev/null +++ b/spec/rubyspec/core/process/set_proctitle_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +# Note that there's no way to get the current process title defined as a spec +# somewhere. Process.setproctitle explicitly does not change `$0` so the only +# way to get the process title is to shell out. +describe 'Process.setproctitle' do + platform_is :linux, :darwin do + before :each do + @old_title = $0 + end + + after :each do + Process.setproctitle(@old_title) + end + + it 'should set the process title' do + title = 'rubyspec-proctitle-test' + + Process.setproctitle(title).should == title + `ps -ocommand= -p#{$$}`.should include(title) + end + end +end diff --git a/spec/rubyspec/core/process/setpgid_spec.rb b/spec/rubyspec/core/process/setpgid_spec.rb new file mode 100644 index 0000000000..bd1596d0eb --- /dev/null +++ b/spec/rubyspec/core/process/setpgid_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.setpgid" do + with_feature :fork do + it "sets the process group id of the specified process" do + rd, wr = IO.pipe + + pid = Process.fork do + wr.close + rd.read + rd.close + Process.exit! + end + + rd.close + + begin + Process.getpgid(pid).should == Process.getpgrp + Process.setpgid(mock_int(pid), mock_int(pid)).should == 0 + Process.getpgid(pid).should == pid + ensure + wr.write ' ' + wr.close + Process.wait pid + end + end + end +end diff --git a/spec/rubyspec/core/process/setpgrp_spec.rb b/spec/rubyspec/core/process/setpgrp_spec.rb new file mode 100644 index 0000000000..8e72795f96 --- /dev/null +++ b/spec/rubyspec/core/process/setpgrp_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +# TODO: put these in the right files. +describe "Process.setpgrp and Process.getpgrp" do + platform_is_not :windows do + it "sets and gets the process group ID of the calling process" do + # there are two synchronization points here: + # One for the child to let the parent know that it has finished + # setting its process group; + # and another for the parent to let the child know that it's ok to die. + read1, write1 = IO.pipe + read2, write2 = IO.pipe + pid = Process.fork do + read1.close + write2.close + Process.setpgrp + write1 << Process.getpgrp + write1.close + read2.read(1) + read2.close + Process.exit! + end + write1.close + read2.close + pgid = read1.read # wait for child to change process groups + read1.close + + begin + Process.getpgid(pid).should == pgid.to_i + ensure + write2 << "!" + write2.close + Process.wait pid + end + end + end +end diff --git a/spec/rubyspec/core/process/setpriority_spec.rb b/spec/rubyspec/core/process/setpriority_spec.rb new file mode 100644 index 0000000000..baccd2244e --- /dev/null +++ b/spec/rubyspec/core/process/setpriority_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.setpriority" do + # Needs a valid version written for Linux + platform_is :darwin do + it "sets the scheduling priority for a specified process" do + p = Process.getpriority(Process::PRIO_PROCESS, 0) + Process.setpriority(mock_int(Process::PRIO_PROCESS), + mock_int(0), + mock_int(p + 1)).should == 0 + Process.getpriority(Process::PRIO_PROCESS, 0).should == (p + 1) + if Process.uid == 0 + Process.setpriority(Process::PRIO_PROCESS, 0, p).should == 0 + else + lambda { + Process.setpriority(Process::PRIO_PROCESS, 0, p) + }.should raise_error(Errno::EACCES) + end + end + end + + # Darwin and FreeBSD don't seem to handle these at all, getting all out of + # whack with either permission errors or just the wrong value + platform_is_not :darwin, :freebsd, :windows do + it "sets the scheduling priority for a specified process group" do + pr = Process.getpriority(Process::PRIO_PGRP, 0) + + Process.setpriority(Process::PRIO_PGRP, 0, pr + 1).should == 0 + Process.getpriority(Process::PRIO_PGRP, 0).should == (pr + 1) + if Process.uid == 0 + Process.setpriority(Process::PRIO_PGRP, 0, pr).should == 0 + else + # EACCESS is not always raised. It's a stupid OS behavior. + ok = false + begin + Process.setpriority(Process::PRIO_PGRP, 0, pr) + ok = true + rescue Errno::EACCES + ok = true + rescue Object + ok = false + end + + ok.should == true + end + end + end + + platform_is_not :windows do + as_superuser do + it "sets the scheduling priority for a specified user" do + p = Process.getpriority(Process::PRIO_USER, 0) + Process.setpriority(Process::PRIO_USER, 0, p + 1).should == 0 + Process.getpriority(Process::PRIO_USER, 0).should == (p + 1) + Process.setpriority(Process::PRIO_USER, 0, p).should == 0 + end + end + end + +end diff --git a/spec/rubyspec/core/process/setrlimit_spec.rb b/spec/rubyspec/core/process/setrlimit_spec.rb new file mode 100644 index 0000000000..89cd39746f --- /dev/null +++ b/spec/rubyspec/core/process/setrlimit_spec.rb @@ -0,0 +1,232 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +platform_is_not :windows do + describe "Process.setrlimit" do + context "when passed an Object" do + before do + @resource = Process::RLIMIT_CORE + @limit, @max = Process.getrlimit @resource + end + + it "calls #to_int to convert resource to an Integer" do + Process.setrlimit(mock_int(@resource), @limit, @max).should be_nil + end + + it "raises a TypeError if #to_int for resource does not return an Integer" do + obj = mock("process getrlimit integer") + obj.should_receive(:to_int).and_return(nil) + + lambda { Process.setrlimit(obj, @limit, @max) }.should raise_error(TypeError) + end + + it "calls #to_int to convert the soft limit to an Integer" do + Process.setrlimit(@resource, mock_int(@limit), @max).should be_nil + end + + it "raises a TypeError if #to_int for resource does not return an Integer" do + obj = mock("process getrlimit integer") + obj.should_receive(:to_int).and_return(nil) + + lambda { Process.setrlimit(@resource, obj, @max) }.should raise_error(TypeError) + end + + it "calls #to_int to convert the hard limit to an Integer" do + Process.setrlimit(@resource, @limit, mock_int(@max)).should be_nil + end + + it "raises a TypeError if #to_int for resource does not return an Integer" do + obj = mock("process getrlimit integer") + obj.should_receive(:to_int).and_return(nil) + + lambda { Process.setrlimit(@resource, @limit, obj) }.should raise_error(TypeError) + end + end + + context "when passed a Symbol" do + platform_is_not :openbsd do + it "coerces :AS into RLIMIT_AS" do + Process.setrlimit(:AS, *Process.getrlimit(Process::RLIMIT_AS)).should be_nil + end + end + + it "coerces :CORE into RLIMIT_CORE" do + Process.setrlimit(:CORE, *Process.getrlimit(Process::RLIMIT_CORE)).should be_nil + end + + it "coerces :CPU into RLIMIT_CPU" do + Process.setrlimit(:CPU, *Process.getrlimit(Process::RLIMIT_CPU)).should be_nil + end + + it "coerces :DATA into RLIMIT_DATA" do + Process.setrlimit(:DATA, *Process.getrlimit(Process::RLIMIT_DATA)).should be_nil + end + + it "coerces :FSIZE into RLIMIT_FSIZE" do + Process.setrlimit(:FSIZE, *Process.getrlimit(Process::RLIMIT_FSIZE)).should be_nil + end + + it "coerces :NOFILE into RLIMIT_NOFILE" do + Process.setrlimit(:NOFILE, *Process.getrlimit(Process::RLIMIT_NOFILE)).should be_nil + end + + it "coerces :STACK into RLIMIT_STACK" do + Process.setrlimit(:STACK, *Process.getrlimit(Process::RLIMIT_STACK)).should be_nil + end + + platform_is_not :solaris do + platform_is_not :aix do + it "coerces :MEMLOCK into RLIMIT_MEMLOCK" do + Process.setrlimit(:MEMLOCK, *Process.getrlimit(Process::RLIMIT_MEMLOCK)).should be_nil + end + end + + it "coerces :NPROC into RLIMIT_NPROC" do + Process.setrlimit(:NPROC, *Process.getrlimit(Process::RLIMIT_NPROC)).should be_nil + end + + it "coerces :RSS into RLIMIT_RSS" do + Process.setrlimit(:RSS, *Process.getrlimit(Process::RLIMIT_RSS)).should be_nil + end + end + + platform_is :netbsd, :freebsd do + it "coerces :SBSIZE into RLIMIT_SBSIZE" do + Process.setrlimit(:SBSIZE, *Process.getrlimit(Process::RLIMIT_SBSIZE)).should be_nil + end + end + + platform_is :linux do + it "coerces :RTPRIO into RLIMIT_RTPRIO" do + Process.setrlimit(:RTPRIO, *Process.getrlimit(Process::RLIMIT_RTPRIO)).should be_nil + end + + if defined?(Process::RLIMIT_RTTIME) + it "coerces :RTTIME into RLIMIT_RTTIME" do + Process.setrlimit(:RTTIME, *Process.getrlimit(Process::RLIMIT_RTTIME)).should be_nil + end + end + + it "coerces :SIGPENDING into RLIMIT_SIGPENDING" do + Process.setrlimit(:SIGPENDING, *Process.getrlimit(Process::RLIMIT_SIGPENDING)).should be_nil + end + + it "coerces :MSGQUEUE into RLIMIT_MSGQUEUE" do + Process.setrlimit(:MSGQUEUE, *Process.getrlimit(Process::RLIMIT_MSGQUEUE)).should be_nil + end + + it "coerces :NICE into RLIMIT_NICE" do + Process.setrlimit(:NICE, *Process.getrlimit(Process::RLIMIT_NICE)).should be_nil + end + end + + it "raises ArgumentError when passed an unknown resource" do + lambda { Process.setrlimit(:FOO, 1, 1) }.should raise_error(ArgumentError) + end + end + + context "when passed a String" do + platform_is_not :openbsd do + it "coerces 'AS' into RLIMIT_AS" do + Process.setrlimit("AS", *Process.getrlimit(Process::RLIMIT_AS)).should be_nil + end + end + + it "coerces 'CORE' into RLIMIT_CORE" do + Process.setrlimit("CORE", *Process.getrlimit(Process::RLIMIT_CORE)).should be_nil + end + + it "coerces 'CPU' into RLIMIT_CPU" do + Process.setrlimit("CPU", *Process.getrlimit(Process::RLIMIT_CPU)).should be_nil + end + + it "coerces 'DATA' into RLIMIT_DATA" do + Process.setrlimit("DATA", *Process.getrlimit(Process::RLIMIT_DATA)).should be_nil + end + + it "coerces 'FSIZE' into RLIMIT_FSIZE" do + Process.setrlimit("FSIZE", *Process.getrlimit(Process::RLIMIT_FSIZE)).should be_nil + end + + it "coerces 'NOFILE' into RLIMIT_NOFILE" do + Process.setrlimit("NOFILE", *Process.getrlimit(Process::RLIMIT_NOFILE)).should be_nil + end + + it "coerces 'STACK' into RLIMIT_STACK" do + Process.setrlimit("STACK", *Process.getrlimit(Process::RLIMIT_STACK)).should be_nil + end + + platform_is_not :solaris do + platform_is_not :aix do + it "coerces 'MEMLOCK' into RLIMIT_MEMLOCK" do + Process.setrlimit("MEMLOCK", *Process.getrlimit(Process::RLIMIT_MEMLOCK)).should be_nil + end + end + + it "coerces 'NPROC' into RLIMIT_NPROC" do + Process.setrlimit("NPROC", *Process.getrlimit(Process::RLIMIT_NPROC)).should be_nil + end + + it "coerces 'RSS' into RLIMIT_RSS" do + Process.setrlimit("RSS", *Process.getrlimit(Process::RLIMIT_RSS)).should be_nil + end + end + + platform_is :netbsd, :freebsd do + it "coerces 'SBSIZE' into RLIMIT_SBSIZE" do + Process.setrlimit("SBSIZE", *Process.getrlimit(Process::RLIMIT_SBSIZE)).should be_nil + end + end + + platform_is :linux do + it "coerces 'RTPRIO' into RLIMIT_RTPRIO" do + Process.setrlimit("RTPRIO", *Process.getrlimit(Process::RLIMIT_RTPRIO)).should be_nil + end + + if defined?(Process::RLIMIT_RTTIME) + it "coerces 'RTTIME' into RLIMIT_RTTIME" do + Process.setrlimit("RTTIME", *Process.getrlimit(Process::RLIMIT_RTTIME)).should be_nil + end + end + + it "coerces 'SIGPENDING' into RLIMIT_SIGPENDING" do + Process.setrlimit("SIGPENDING", *Process.getrlimit(Process::RLIMIT_SIGPENDING)).should be_nil + end + + it "coerces 'MSGQUEUE' into RLIMIT_MSGQUEUE" do + Process.setrlimit("MSGQUEUE", *Process.getrlimit(Process::RLIMIT_MSGQUEUE)).should be_nil + end + + it "coerces 'NICE' into RLIMIT_NICE" do + Process.setrlimit("NICE", *Process.getrlimit(Process::RLIMIT_NICE)).should be_nil + end + end + + it "raises ArgumentError when passed an unknown resource" do + lambda { Process.setrlimit("FOO", 1, 1) }.should raise_error(ArgumentError) + end + end + + context "when passed on Object" do + before do + @resource = Process::RLIMIT_CORE + @limit, @max = Process.getrlimit @resource + end + + it "calls #to_str to convert to a String" do + obj = mock("process getrlimit string") + obj.should_receive(:to_str).and_return("CORE") + obj.should_not_receive(:to_int) + + Process.setrlimit(obj, @limit, @max).should be_nil + end + + it "calls #to_int if #to_str does not return a String" do + obj = mock("process getrlimit string") + obj.should_receive(:to_str).and_return(nil) + obj.should_receive(:to_int).and_return(@resource) + + Process.setrlimit(obj, @limit, @max).should be_nil + end + end + end +end diff --git a/spec/rubyspec/core/process/setsid_spec.rb b/spec/rubyspec/core/process/setsid_spec.rb new file mode 100644 index 0000000000..abb75f8225 --- /dev/null +++ b/spec/rubyspec/core/process/setsid_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.setsid" do + with_feature :fork do + it "establishes this process as a new session and process group leader" do + read, write = IO.pipe + read2, write2 = IO.pipe + pid = Process.fork { + begin + read.close + write2.close + pgid = Process.setsid + write << pgid + write.close + read2.gets + rescue Exception => e + write << e << e.backtrace + end + Process.exit! + } + write.close + read2.close + pgid_child = Integer(read.gets) + read.close + platform_is_not :aix do + # AIX does not allow Process.getsid(pid) + # if pid is in a different session. + pgid = Process.getsid(pid) + pgid_child.should == pgid + end + write2.close + Process.wait pid + + pgid_child.should_not == Process.getsid + end + end +end diff --git a/spec/rubyspec/core/process/spawn_spec.rb b/spec/rubyspec/core/process/spawn_spec.rb new file mode 100644 index 0000000000..31d14054ed --- /dev/null +++ b/spec/rubyspec/core/process/spawn_spec.rb @@ -0,0 +1,620 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +newline = "\n" +platform_is :windows do + newline = "\r\n" +end + +describe :process_spawn_does_not_close_std_streams, shared: true do + platform_is_not :windows do + it "does not close STDIN" do + code = "STDOUT.puts STDIN.read(0).inspect" + cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" + ruby_exe(cmd, args: "> #{@output}") + File.binread(@output).should == %[""#{newline}] + end + + it "does not close STDOUT" do + code = "STDOUT.puts 'hello'" + cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" + ruby_exe(cmd, args: "> #{@output}") + File.binread(@output).should == "hello#{newline}" + end + + it "does not close STDERR" do + code = "STDERR.puts 'hello'" + cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" + ruby_exe(cmd, args: "2> #{@output}") + File.binread(@output).should == "hello#{newline}" + end + end +end + +describe "Process.spawn" do + before :each do + @name = tmp("process_spawn.txt") + end + + after :each do + rm_r @name + end + + it "executes the given command" do + lambda { Process.wait Process.spawn("echo spawn") }.should output_to_fd("spawn\n") + end + + it "returns the process ID of the new process as a Fixnum" do + pid = Process.spawn(ruby_cmd("exit")) + Process.wait pid + pid.should be_an_instance_of(Fixnum) + end + + it "returns immediately" do + start = Time.now + pid = Process.spawn(ruby_cmd("sleep 10")) + (Time.now - start).should < 5 + Process.kill :KILL, pid + Process.wait pid + end + + # argv processing + + describe "with a single argument" do + platform_is_not :windows do + it "subjects the specified command to shell expansion" do + lambda { Process.wait Process.spawn("echo *") }.should_not output_to_fd("*\n") + end + + it "creates an argument array with shell parsing semantics for whitespace" do + lambda { Process.wait Process.spawn("echo a b c d") }.should output_to_fd("a b c d\n") + end + end + + platform_is :windows do + # There is no shell expansion on Windows + it "does not subject the specified command to shell expansion on Windows" do + lambda { Process.wait Process.spawn("echo *") }.should output_to_fd("*\n") + end + + it "does not create an argument array with shell parsing semantics for whitespace on Windows" do + lambda { Process.wait Process.spawn("echo a b c d") }.should output_to_fd("a b c d\n") + end + end + + it "calls #to_str to convert the argument to a String" do + o = mock("to_str") + o.should_receive(:to_str).and_return("echo foo") + lambda { Process.wait Process.spawn(o) }.should output_to_fd("foo\n") + end + + it "raises an ArgumentError if the command includes a null byte" do + lambda { Process.spawn "\000" }.should raise_error(ArgumentError) + end + + it "raises a TypeError if the argument does not respond to #to_str" do + lambda { Process.spawn :echo }.should raise_error(TypeError) + end + end + + describe "with multiple arguments" do + it "does not subject the arguments to shell expansion" do + lambda { Process.wait Process.spawn("echo", "*") }.should output_to_fd("*\n") + end + + it "preserves whitespace in passed arguments" do + out = "a b c d\n" + platform_is :windows do + # The echo command on Windows takes quotes literally + out = "\"a b c d\"\n" + end + lambda { Process.wait Process.spawn("echo", "a b c d") }.should output_to_fd(out) + end + + it "calls #to_str to convert the arguments to Strings" do + o = mock("to_str") + o.should_receive(:to_str).and_return("foo") + lambda { Process.wait Process.spawn("echo", o) }.should output_to_fd("foo\n") + end + + it "raises an ArgumentError if an argument includes a null byte" do + lambda { Process.spawn "echo", "\000" }.should raise_error(ArgumentError) + end + + it "raises a TypeError if an argument does not respond to #to_str" do + lambda { Process.spawn "echo", :foo }.should raise_error(TypeError) + end + end + + describe "with a command array" do + it "uses the first element as the command name and the second as the argv[0] value" do + platform_is_not :windows do + lambda { Process.wait Process.spawn(["/bin/sh", "argv_zero"], "-c", "echo $0") }.should output_to_fd("argv_zero\n") + end + platform_is :windows do + lambda { Process.wait Process.spawn(["cmd.exe", "/C"], "/C", "echo", "argv_zero") }.should output_to_fd("argv_zero\n") + end + end + + it "does not subject the arguments to shell expansion" do + lambda { Process.wait Process.spawn(["echo", "echo"], "*") }.should output_to_fd("*\n") + end + + it "preserves whitespace in passed arguments" do + out = "a b c d\n" + platform_is :windows do + # The echo command on Windows takes quotes literally + out = "\"a b c d\"\n" + end + lambda { Process.wait Process.spawn(["echo", "echo"], "a b c d") }.should output_to_fd(out) + end + + it "calls #to_ary to convert the argument to an Array" do + o = mock("to_ary") + platform_is_not :windows do + o.should_receive(:to_ary).and_return(["/bin/sh", "argv_zero"]) + lambda { Process.wait Process.spawn(o, "-c", "echo $0") }.should output_to_fd("argv_zero\n") + end + platform_is :windows do + o.should_receive(:to_ary).and_return(["cmd.exe", "/C"]) + lambda { Process.wait Process.spawn(o, "/C", "echo", "argv_zero") }.should output_to_fd("argv_zero\n") + end + end + + it "calls #to_str to convert the first element to a String" do + o = mock("to_str") + o.should_receive(:to_str).and_return("echo") + lambda { Process.wait Process.spawn([o, "echo"], "foo") }.should output_to_fd("foo\n") + end + + it "calls #to_str to convert the second element to a String" do + o = mock("to_str") + o.should_receive(:to_str).and_return("echo") + lambda { Process.wait Process.spawn(["echo", o], "foo") }.should output_to_fd("foo\n") + end + + it "raises an ArgumentError if the Array does not have exactly two elements" do + lambda { Process.spawn([]) }.should raise_error(ArgumentError) + lambda { Process.spawn([:a]) }.should raise_error(ArgumentError) + lambda { Process.spawn([:a, :b, :c]) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the Strings in the Array include a null byte" do + lambda { Process.spawn ["\000", "echo"] }.should raise_error(ArgumentError) + lambda { Process.spawn ["echo", "\000"] }.should raise_error(ArgumentError) + end + + it "raises a TypeError if an element in the Array does not respond to #to_str" do + lambda { Process.spawn ["echo", :echo] }.should raise_error(TypeError) + lambda { Process.spawn [:echo, "echo"] }.should raise_error(TypeError) + end + end + + # env handling + + after :each do + ENV.delete("FOO") + end + + it "sets environment variables in the child environment" do + Process.wait Process.spawn({"FOO" => "BAR"}, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + File.read(@name).should == "BAR" + end + + it "unsets environment variables whose value is nil" do + ENV["FOO"] = "BAR" + Process.wait Process.spawn({"FOO" => nil}, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + File.read(@name).should == "" + end + + it "calls #to_hash to convert the environment" do + o = mock("to_hash") + o.should_receive(:to_hash).and_return({"FOO" => "BAR"}) + Process.wait Process.spawn(o, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + File.read(@name).should == "BAR" + end + + it "calls #to_str to convert the environment keys" do + o = mock("to_str") + o.should_receive(:to_str).and_return("FOO") + Process.wait Process.spawn({o => "BAR"}, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + File.read(@name).should == "BAR" + end + + it "calls #to_str to convert the environment values" do + o = mock("to_str") + o.should_receive(:to_str).and_return("BAR") + Process.wait Process.spawn({"FOO" => o}, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + File.read(@name).should == "BAR" + end + + it "raises an ArgumentError if an environment key includes an equals sign" do + lambda do + Process.spawn({"FOO=" => "BAR"}, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + end.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if an environment key includes a null byte" do + lambda do + Process.spawn({"\000" => "BAR"}, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + end.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if an environment value includes a null byte" do + lambda do + Process.spawn({"FOO" => "\000"}, ruby_cmd(fixture(__FILE__, "env.rb"), args: @name)) + end.should raise_error(ArgumentError) + end + + # :unsetenv_others + + before :each do + @minimal_env = { + "PATH" => ENV["PATH"], + "HOME" => ENV["HOME"] + } + @common_env_spawn_args = [@minimal_env, ruby_cmd(fixture(__FILE__, "env.rb"), options: "--disable-gems", args: @name)] + end + + platform_is_not :windows do + it "unsets other environment variables when given a true :unsetenv_others option" do + ENV["FOO"] = "BAR" + Process.wait Process.spawn(*@common_env_spawn_args, unsetenv_others: true) + $?.success?.should be_true + File.read(@name).should == "" + end + end + + it "does not unset other environment variables when given a false :unsetenv_others option" do + ENV["FOO"] = "BAR" + Process.wait Process.spawn(*@common_env_spawn_args, unsetenv_others: false) + $?.success?.should be_true + File.read(@name).should == "BAR" + end + + platform_is_not :windows do + it "does not unset environment variables included in the environment hash" do + env = @minimal_env.merge({"FOO" => "BAR"}) + Process.wait Process.spawn(env, ruby_cmd(fixture(__FILE__, "env.rb"), options: "--disable-gems", args: @name), unsetenv_others: true) + $?.success?.should be_true + File.read(@name).should == "BAR" + end + end + + # :pgroup + + platform_is_not :windows do + it "joins the current process group by default" do + lambda do + Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)")) + end.should output_to_fd(Process.getpgid(Process.pid).to_s) + end + + it "joins the current process if pgroup: false" do + lambda do + Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: false) + end.should output_to_fd(Process.getpgid(Process.pid).to_s) + end + + it "joins the current process if pgroup: nil" do + lambda do + Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: nil) + end.should output_to_fd(Process.getpgid(Process.pid).to_s) + end + + it "joins a new process group if pgroup: true" do + process = lambda do + Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: true) + end + + process.should_not output_to_fd(Process.getpgid(Process.pid).to_s) + process.should output_to_fd(/\d+/) + end + + it "joins a new process group if pgroup: 0" do + process = lambda do + Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: 0) + end + + process.should_not output_to_fd(Process.getpgid(Process.pid).to_s) + process.should output_to_fd(/\d+/) + end + + it "joins the specified process group if pgroup: pgid" do + pgid = Process.getpgid(Process.pid) + lambda do + Process.wait Process.spawn(ruby_cmd("print Process.getpgid(Process.pid)"), pgroup: pgid) + end.should output_to_fd(pgid.to_s) + end + + it "raises an ArgumentError if given a negative :pgroup option" do + lambda { Process.spawn("echo", pgroup: -1) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if given a symbol as :pgroup option" do + lambda { Process.spawn("echo", pgroup: :true) }.should raise_error(TypeError) + end + end + + platform_is :windows do + it "raises an ArgumentError if given :pgroup option" do + lambda { Process.spawn("echo", pgroup: false) }.should raise_error(ArgumentError) + end + end + + # :rlimit_core + # :rlimit_cpu + # :rlimit_data + + # :chdir + + it "uses the current working directory as its working directory" do + lambda do + Process.wait Process.spawn(ruby_cmd("print Dir.pwd")) + end.should output_to_fd(Dir.pwd) + end + + describe "when passed :chdir" do + before do + @dir = tmp("spawn_chdir", false) + Dir.mkdir @dir + end + + after do + rm_r @dir + end + + it "changes to the directory passed for :chdir" do + lambda do + Process.wait Process.spawn(ruby_cmd("print Dir.pwd"), chdir: @dir) + end.should output_to_fd(@dir) + end + + it "calls #to_path to convert the :chdir value" do + dir = mock("spawn_to_path") + dir.should_receive(:to_path).and_return(@dir) + + lambda do + Process.wait Process.spawn(ruby_cmd("print Dir.pwd"), chdir: dir) + end.should output_to_fd(@dir) + end + end + + # :umask + + it "uses the current umask by default" do + lambda do + Process.wait Process.spawn(ruby_cmd("print File.umask")) + end.should output_to_fd(File.umask.to_s) + end + + platform_is_not :windows do + it "sets the umask if given the :umask option" do + lambda do + Process.wait Process.spawn(ruby_cmd("print File.umask"), umask: 146) + end.should output_to_fd("146") + end + end + + # redirection + + it "redirects STDOUT to the given file descriptior if out: Fixnum" do + File.open(@name, 'w') do |file| + lambda do + Process.wait Process.spawn(ruby_cmd(fixture(__FILE__, "print.rb")), out: file.fileno) + end.should output_to_fd("glark", file) + end + end + + it "redirects STDOUT to the given file if out: IO" do + File.open(@name, 'w') do |file| + lambda do + Process.wait Process.spawn(ruby_cmd(fixture(__FILE__, "print.rb")), out: file) + end.should output_to_fd("glark", file) + end + end + + it "redirects STDOUT to the given file if out: String" do + Process.wait Process.spawn(ruby_cmd(fixture(__FILE__, "print.rb")), out: @name) + File.read(@name).should == "glark" + end + + it "redirects STDOUT to the given file if out: [String name, String mode]" do + Process.wait Process.spawn(ruby_cmd(fixture(__FILE__, "print.rb")), out: [@name, 'w']) + File.read(@name).should == "glark" + end + + it "redirects STDERR to the given file descriptior if err: Fixnum" do + File.open(@name, 'w') do |file| + lambda do + Process.wait Process.spawn(ruby_cmd("STDERR.print :glark"), err: file.fileno) + end.should output_to_fd("glark", file) + end + end + + it "redirects STDERR to the given file descriptor if err: IO" do + File.open(@name, 'w') do |file| + lambda do + Process.wait Process.spawn(ruby_cmd("STDERR.print :glark"), err: file) + end.should output_to_fd("glark", file) + end + end + + it "redirects STDERR to the given file if err: String" do + Process.wait Process.spawn(ruby_cmd("STDERR.print :glark"), err: @name) + File.read(@name).should == "glark" + end + + it "redirects STDERR to child STDOUT if :err => [:child, :out]" do + File.open(@name, 'w') do |file| + lambda do + Process.wait Process.spawn(ruby_cmd("STDERR.print :glark"), :out => file, :err => [:child, :out]) + end.should output_to_fd("glark", file) + end + end + + it "redirects both STDERR and STDOUT to the given file descriptior" do + File.open(@name, 'w') do |file| + lambda do + Process.wait Process.spawn(ruby_cmd("print(:glark); STDOUT.flush; STDERR.print(:bang)"), + [:out, :err] => file.fileno) + end.should output_to_fd("glarkbang", file) + end + end + + it "redirects both STDERR and STDOUT to the given IO" do + File.open(@name, 'w') do |file| + lambda do + Process.wait Process.spawn(ruby_cmd("print(:glark); STDOUT.flush; STDERR.print(:bang)"), + [:out, :err] => file) + end.should output_to_fd("glarkbang", file) + end + end + + it "redirects both STDERR and STDOUT at the time to the given name" do + touch @name + Process.wait Process.spawn(ruby_cmd("print(:glark); STDOUT.flush; STDERR.print(:bang)"), [:out, :err] => @name) + File.read(@name).should == "glarkbang" + end + + context "when passed close_others: true" do + before :each do + @output = tmp("spawn_close_others_true") + @options = { close_others: true } + end + + after :each do + rm_r @output + end + + it "closes file descriptors >= 3 in the child process" do + IO.pipe do |r, w| + begin + pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) + w.close + lambda { r.read_nonblock(1) }.should raise_error(EOFError) + ensure + rm_r @name + Process.wait(pid) if pid + end + end + end + + it_should_behave_like :process_spawn_does_not_close_std_streams + end + + context "when passed close_others: false" do + before :each do + @output = tmp("spawn_close_others_false") + @options = { close_others: false } + end + + after :each do + rm_r @output + end + + it "closes file descriptors >= 3 in the child process because they are set close_on_exec by default" do + IO.pipe do |r, w| + begin + pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) + w.close + lambda { r.read_nonblock(1) }.should raise_error(EOFError) + ensure + rm_r @name + Process.wait(pid) if pid + end + end + end + + platform_is_not :windows do + it "does not close file descriptors >= 3 in the child process if fds are set close_on_exec=false" do + IO.pipe do |r, w| + r.close_on_exec = false + w.close_on_exec = false + begin + pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) + w.close + lambda { r.read_nonblock(1) }.should raise_error(Errno::EAGAIN) + ensure + rm_r @name + Process.wait(pid) if pid + end + end + end + end + + it_should_behave_like :process_spawn_does_not_close_std_streams + end + + # error handling + + it "raises an ArgumentError if passed no command arguments" do + lambda { Process.spawn }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed env or options but no command arguments" do + lambda { Process.spawn({}) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed env and options but no command arguments" do + lambda { Process.spawn({}, {}) }.should raise_error(ArgumentError) + end + + it "raises an Errno::ENOENT for an empty string" do + lambda { Process.spawn "" }.should raise_error(Errno::ENOENT) + end + + it "raises an Errno::ENOENT if the command does not exist" do + lambda { Process.spawn "nonesuch" }.should raise_error(Errno::ENOENT) + end + + unless File.executable?(__FILE__) # Some FS (e.g. vboxfs) locate all files executable + platform_is_not :windows do + it "raises an Errno::EACCES when the file does not have execute permissions" do + lambda { Process.spawn __FILE__ }.should raise_error(Errno::EACCES) + end + end + + platform_is :windows do + it "raises Errno::ENOEXEC when the file is not an executable file" do + lambda { Process.spawn __FILE__ }.should raise_error(Errno::ENOEXEC) + end + end + end + + it "raises an Errno::EACCES when passed a directory" do + lambda { Process.spawn File.dirname(__FILE__) }.should raise_error(Errno::EACCES) + end + + it "raises an ArgumentError when passed a string key in options" do + lambda { Process.spawn("echo", "chdir" => Dir.pwd) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed an unknown option key" do + lambda { Process.spawn("echo", nonesuch: :foo) }.should raise_error(ArgumentError) + end + + platform_is_not :windows do + describe "with Integer option keys" do + before :each do + @name = tmp("spawn_fd_map.txt") + @io = new_io @name, "w+" + @io.sync = true + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value" do + child_fd = @io.fileno + 1 + args = ruby_cmd(fixture(__FILE__, "map_fd.rb"), args: [child_fd.to_s]) + pid = Process.spawn(*args, { child_fd => @io }) + Process.waitpid pid + @io.rewind + + @io.read.should == "writing to fd: #{child_fd}" + end + end + end +end diff --git a/spec/rubyspec/core/process/status/bit_and_spec.rb b/spec/rubyspec/core/process/status/bit_and_spec.rb new file mode 100644 index 0000000000..963d2c6c26 --- /dev/null +++ b/spec/rubyspec/core/process/status/bit_and_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#&" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/coredump_spec.rb b/spec/rubyspec/core/process/status/coredump_spec.rb new file mode 100644 index 0000000000..8988cff9e1 --- /dev/null +++ b/spec/rubyspec/core/process/status/coredump_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#coredump?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/equal_value_spec.rb b/spec/rubyspec/core/process/status/equal_value_spec.rb new file mode 100644 index 0000000000..1eaaf82273 --- /dev/null +++ b/spec/rubyspec/core/process/status/equal_value_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#==" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/exited_spec.rb b/spec/rubyspec/core/process/status/exited_spec.rb new file mode 100644 index 0000000000..79863360c5 --- /dev/null +++ b/spec/rubyspec/core/process/status/exited_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#exited?" do + + describe "for a child that exited normally" do + + before :each do + ruby_exe("exit(0)") + end + + it "returns true" do + $?.exited?.should be_true + end + end + + + describe "for a terminated child" do + + before :each do + ruby_exe("Process.kill(:KILL, $$); exit(42)") + end + + platform_is_not :windows do + it "returns false" do + $?.exited?.should be_false + end + end + + platform_is :windows do + it "always returns true" do + $?.exited?.should be_true + end + end + + end + +end diff --git a/spec/rubyspec/core/process/status/exitstatus_spec.rb b/spec/rubyspec/core/process/status/exitstatus_spec.rb new file mode 100644 index 0000000000..57baf77724 --- /dev/null +++ b/spec/rubyspec/core/process/status/exitstatus_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#exitstatus" do + + before :each do + ruby_exe("exit(42)") + end + + it "returns the process exit code" do + $?.exitstatus.should == 42 + end + +end diff --git a/spec/rubyspec/core/process/status/inspect_spec.rb b/spec/rubyspec/core/process/status/inspect_spec.rb new file mode 100644 index 0000000000..f3e7d8c9ab --- /dev/null +++ b/spec/rubyspec/core/process/status/inspect_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#inspect" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/pid_spec.rb b/spec/rubyspec/core/process/status/pid_spec.rb new file mode 100644 index 0000000000..00f2a45820 --- /dev/null +++ b/spec/rubyspec/core/process/status/pid_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#pid" do + + before :each do + @pid = ruby_exe("print $$").to_i + end + + it "returns the pid of the process" do + $?.pid.should == @pid + end + +end diff --git a/spec/rubyspec/core/process/status/right_shift_spec.rb b/spec/rubyspec/core/process/status/right_shift_spec.rb new file mode 100644 index 0000000000..5786d4163c --- /dev/null +++ b/spec/rubyspec/core/process/status/right_shift_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#>>" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/signaled_spec.rb b/spec/rubyspec/core/process/status/signaled_spec.rb new file mode 100644 index 0000000000..0f80a525c9 --- /dev/null +++ b/spec/rubyspec/core/process/status/signaled_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#signaled?" do + + describe "for a cleanly exited child" do + + before :each do + ruby_exe("exit(0)") + end + + it "returns false" do + $?.signaled?.should be_false + end + end + + describe "for a terminated child" do + + before :each do + ruby_exe("Process.kill(:KILL, $$); exit(42)") + end + + platform_is_not :windows do + it "returns true" do + $?.signaled?.should be_true + end + end + + platform_is :windows do + it "always returns false" do + $?.signaled?.should be_false + end + end + + end +end diff --git a/spec/rubyspec/core/process/status/stopped_spec.rb b/spec/rubyspec/core/process/status/stopped_spec.rb new file mode 100644 index 0000000000..e984cefaf7 --- /dev/null +++ b/spec/rubyspec/core/process/status/stopped_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#stopped?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/stopsig_spec.rb b/spec/rubyspec/core/process/status/stopsig_spec.rb new file mode 100644 index 0000000000..95fc5b0e77 --- /dev/null +++ b/spec/rubyspec/core/process/status/stopsig_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#stopsig" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/success_spec.rb b/spec/rubyspec/core/process/status/success_spec.rb new file mode 100644 index 0000000000..e589d3f819 --- /dev/null +++ b/spec/rubyspec/core/process/status/success_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#success?" do + + describe "for a child that exited normally" do + + before :each do + ruby_exe("exit(0)") + end + + it "returns true" do + $?.success?.should be_true + end + end + + describe "for a child that exited with a non zero status" do + + before :each do + ruby_exe("exit(42)") + end + + it "returns false" do + $?.success?.should be_false + end + end + + describe "for a child that was terminated" do + + before :each do + ruby_exe("Process.kill(:KILL, $$); exit(42)") + end + + platform_is_not :windows do + + it "returns nil" do + $?.success?.should be_nil + end + + end + + platform_is :windows do + + it "always returns true" do + $?.success?.should be_true + end + + end + + end + +end diff --git a/spec/rubyspec/core/process/status/termsig_spec.rb b/spec/rubyspec/core/process/status/termsig_spec.rb new file mode 100644 index 0000000000..d4f55e2521 --- /dev/null +++ b/spec/rubyspec/core/process/status/termsig_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#termsig" do + + describe "for a child that exited normally" do + + before :each do + ruby_exe("exit(0)") + end + + it "returns true" do + $?.termsig.should be_nil + end + end + + describe "for a child that was sent a signal" do + + before :each do + ruby_exe("Process.kill(:KILL, $$); exit(42)") + end + + platform_is_not :windows do + + it "returns the signal" do + $?.termsig.should == Signal.list["KILL"] + end + + end + + platform_is :windows do + + it "always returns nil" do + $?.termsig.should be_nil + end + + end + + end +end diff --git a/spec/rubyspec/core/process/status/to_i_spec.rb b/spec/rubyspec/core/process/status/to_i_spec.rb new file mode 100644 index 0000000000..c45724552e --- /dev/null +++ b/spec/rubyspec/core/process/status/to_i_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#to_i" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/to_int_spec.rb b/spec/rubyspec/core/process/status/to_int_spec.rb new file mode 100644 index 0000000000..8c988d7e19 --- /dev/null +++ b/spec/rubyspec/core/process/status/to_int_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#to_int" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/status/to_s_spec.rb b/spec/rubyspec/core/process/status/to_s_spec.rb new file mode 100644 index 0000000000..ac87f4712a --- /dev/null +++ b/spec/rubyspec/core/process/status/to_s_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Status#to_s" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/getegid_spec.rb b/spec/rubyspec/core/process/sys/getegid_spec.rb new file mode 100644 index 0000000000..c21b890519 --- /dev/null +++ b/spec/rubyspec/core/process/sys/getegid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.getegid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/geteuid_spec.rb b/spec/rubyspec/core/process/sys/geteuid_spec.rb new file mode 100644 index 0000000000..85c8d6e1bb --- /dev/null +++ b/spec/rubyspec/core/process/sys/geteuid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.geteuid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/getgid_spec.rb b/spec/rubyspec/core/process/sys/getgid_spec.rb new file mode 100644 index 0000000000..945d3340f7 --- /dev/null +++ b/spec/rubyspec/core/process/sys/getgid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.getgid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/getuid_spec.rb b/spec/rubyspec/core/process/sys/getuid_spec.rb new file mode 100644 index 0000000000..ead6e3044f --- /dev/null +++ b/spec/rubyspec/core/process/sys/getuid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.getuid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/issetugid_spec.rb b/spec/rubyspec/core/process/sys/issetugid_spec.rb new file mode 100644 index 0000000000..2919c351a7 --- /dev/null +++ b/spec/rubyspec/core/process/sys/issetugid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.issetugid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setegid_spec.rb b/spec/rubyspec/core/process/sys/setegid_spec.rb new file mode 100644 index 0000000000..edc0d59da4 --- /dev/null +++ b/spec/rubyspec/core/process/sys/setegid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setegid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/seteuid_spec.rb b/spec/rubyspec/core/process/sys/seteuid_spec.rb new file mode 100644 index 0000000000..70cc78bec4 --- /dev/null +++ b/spec/rubyspec/core/process/sys/seteuid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.seteuid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setgid_spec.rb b/spec/rubyspec/core/process/sys/setgid_spec.rb new file mode 100644 index 0000000000..25272b1eec --- /dev/null +++ b/spec/rubyspec/core/process/sys/setgid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setgid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setregid_spec.rb b/spec/rubyspec/core/process/sys/setregid_spec.rb new file mode 100644 index 0000000000..18a5834c80 --- /dev/null +++ b/spec/rubyspec/core/process/sys/setregid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setregid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setresgid_spec.rb b/spec/rubyspec/core/process/sys/setresgid_spec.rb new file mode 100644 index 0000000000..9f1736b460 --- /dev/null +++ b/spec/rubyspec/core/process/sys/setresgid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setresgid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setresuid_spec.rb b/spec/rubyspec/core/process/sys/setresuid_spec.rb new file mode 100644 index 0000000000..94c892bfdf --- /dev/null +++ b/spec/rubyspec/core/process/sys/setresuid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setresuid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setreuid_spec.rb b/spec/rubyspec/core/process/sys/setreuid_spec.rb new file mode 100644 index 0000000000..72a8e61e7a --- /dev/null +++ b/spec/rubyspec/core/process/sys/setreuid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setreuid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setrgid_spec.rb b/spec/rubyspec/core/process/sys/setrgid_spec.rb new file mode 100644 index 0000000000..ae820c98b8 --- /dev/null +++ b/spec/rubyspec/core/process/sys/setrgid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setrgid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setruid_spec.rb b/spec/rubyspec/core/process/sys/setruid_spec.rb new file mode 100644 index 0000000000..4f40f6666a --- /dev/null +++ b/spec/rubyspec/core/process/sys/setruid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setruid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/sys/setuid_spec.rb b/spec/rubyspec/core/process/sys/setuid_spec.rb new file mode 100644 index 0000000000..13bf072ad1 --- /dev/null +++ b/spec/rubyspec/core/process/sys/setuid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::Sys.setuid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/times_spec.rb b/spec/rubyspec/core/process/times_spec.rb new file mode 100644 index 0000000000..01a5595ef9 --- /dev/null +++ b/spec/rubyspec/core/process/times_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.times" do + it "returns a Struct::Tms" do + Process.times.should be_kind_of(Struct::Tms) + end + + it "returns current cpu times" do + t = Process.times + + # Do busy work for a wall-clock interval. + start = Time.now + 1 until (Time.now - start) > 0.5 + + # Ensure times is larger. NOTE that there is no + # guarantee of an upper bound since anything may be + # happening at the OS level, so we ONLY check that at + # least an interval has elapsed. Also, we are assuming + # there is a correlation between wall clock time and + # process time. In practice, there is an observed + # discrepancy often 10% or greater. In other words, + # this is a very fuzzy test. + t2 = Process.times + diff = (t2.utime + t2.stime) - (t.utime + t.stime) + diff.should > 0 + end +end diff --git a/spec/rubyspec/core/process/uid/change_privilege_spec.rb b/spec/rubyspec/core/process/uid/change_privilege_spec.rb new file mode 100644 index 0000000000..91a38bfcdf --- /dev/null +++ b/spec/rubyspec/core/process/uid/change_privilege_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.change_privilege" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid/eid_spec.rb b/spec/rubyspec/core/process/uid/eid_spec.rb new file mode 100644 index 0000000000..39fcd13d93 --- /dev/null +++ b/spec/rubyspec/core/process/uid/eid_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.eid" do + it "needs to be reviewed for spec completeness" +end + +describe "Process::UID.eid=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid/grant_privilege_spec.rb b/spec/rubyspec/core/process/uid/grant_privilege_spec.rb new file mode 100644 index 0000000000..c4d3443de8 --- /dev/null +++ b/spec/rubyspec/core/process/uid/grant_privilege_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.grant_privilege" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid/re_exchange_spec.rb b/spec/rubyspec/core/process/uid/re_exchange_spec.rb new file mode 100644 index 0000000000..2f9b0d6a87 --- /dev/null +++ b/spec/rubyspec/core/process/uid/re_exchange_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.re_exchange" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid/re_exchangeable_spec.rb b/spec/rubyspec/core/process/uid/re_exchangeable_spec.rb new file mode 100644 index 0000000000..63f45fa662 --- /dev/null +++ b/spec/rubyspec/core/process/uid/re_exchangeable_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.re_exchangeable?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid/rid_spec.rb b/spec/rubyspec/core/process/uid/rid_spec.rb new file mode 100644 index 0000000000..cdfe08e3be --- /dev/null +++ b/spec/rubyspec/core/process/uid/rid_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.rid" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid/sid_available_spec.rb b/spec/rubyspec/core/process/uid/sid_available_spec.rb new file mode 100644 index 0000000000..5d51366dd4 --- /dev/null +++ b/spec/rubyspec/core/process/uid/sid_available_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.sid_available?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid/switch_spec.rb b/spec/rubyspec/core/process/uid/switch_spec.rb new file mode 100644 index 0000000000..6747ee4f43 --- /dev/null +++ b/spec/rubyspec/core/process/uid/switch_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Process::UID.switch" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/uid_spec.rb b/spec/rubyspec/core/process/uid_spec.rb new file mode 100644 index 0000000000..1b561f47e0 --- /dev/null +++ b/spec/rubyspec/core/process/uid_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.uid" do + platform_is_not :windows do + it "returns the correct uid for the user executing this process" do + current_uid_according_to_unix = `id -ur`.to_i + Process.uid.should == current_uid_according_to_unix + end + end + + it "also goes by Process::UID.rid" do + Process::UID.rid.should == Process.uid + end + + it "also goes by Process::Sys.getuid" do + Process::Sys.getuid.should == Process.uid + end +end + +describe "Process.uid=" do + + platform_is_not :windows do + it "raises TypeError if not passed an Integer" do + lambda { Process.uid = Object.new }.should raise_error(TypeError) + end + + as_user do + it "raises Errno::ERPERM if run by a non privileged user trying to set the superuser id" do + lambda { (Process.uid = 0)}.should raise_error(Errno::EPERM) + end + + it "raises Errno::ERPERM if run by a non privileged user trying to set the superuser id from username" do + lambda { Process.uid = "root" }.should raise_error(Errno::EPERM) + end + end + + as_superuser do + describe "if run by a superuser" do + with_feature :fork do + it "sets the real user id for the current process" do + read, write = IO.pipe + pid = Process.fork do + begin + read.close + Process.uid = 1 + write << Process.uid + write.close + rescue Exception => e + write << e << e.backtrace + end + Process.exit! + end + write.close + uid = read.gets + uid.should == "1" + Process.wait pid + end + + it "sets the real user id if preceded by Process.euid=id" do + read, write = IO.pipe + pid = Process.fork do + begin + read.close + Process.euid = 1 + Process.uid = 1 + write << Process.uid + write.close + rescue Exception => e + write << e << e.backtrace + end + Process.exit! + end + write.close + uid = read.gets + uid.should == "1" + Process.wait pid + end + end + end + end + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/wait2_spec.rb b/spec/rubyspec/core/process/wait2_spec.rb new file mode 100644 index 0000000000..8e1ac763f1 --- /dev/null +++ b/spec/rubyspec/core/process/wait2_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.wait2" do + before :all do + # HACK: this kludge is temporarily necessary because some + # misbehaving spec somewhere else does not clear processes + begin + leaked = Process.waitall + puts "leaked before wait2 specs: #{leaked}" unless leaked.empty? + rescue NotImplementedError + end + end + + platform_is_not :windows do + it "returns the pid and status of child process" do + pidf = Process.fork { Process.exit! 99 } + results = Process.wait2 + results.size.should == 2 + pidw, status = results + pidf.should == pidw + status.exitstatus.should == 99 + end + end + + it "raises a StandardError if no child processes exist" do + lambda { Process.wait2 }.should raise_error(Errno::ECHILD) + lambda { Process.wait2 }.should raise_error(StandardError) + end +end diff --git a/spec/rubyspec/core/process/wait_spec.rb b/spec/rubyspec/core/process/wait_spec.rb new file mode 100644 index 0000000000..dca43140fd --- /dev/null +++ b/spec/rubyspec/core/process/wait_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.wait" do + before :all do + begin + leaked = Process.waitall + puts "leaked before wait specs: #{leaked}" unless leaked.empty? + rescue NotImplementedError + end + end + + it "raises an Errno::ECHILD if there are no child processes" do + lambda { Process.wait }.should raise_error(Errno::ECHILD) + end + + platform_is_not :windows do + it "returns its childs pid" do + pid = Process.spawn(ruby_cmd('exit')) + Process.wait.should == pid + end + + it "sets $? to a Process::Status" do + pid = Process.spawn(ruby_cmd('exit')) + Process.wait + $?.should be_kind_of(Process::Status) + $?.pid.should == pid + end + + it "waits for any child process if no pid is given" do + pid = Process.spawn(ruby_cmd('exit')) + Process.wait.should == pid + lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH) + end + + it "waits for a specific child if a pid is given" do + pid1 = Process.spawn(ruby_cmd('exit')) + pid2 = Process.spawn(ruby_cmd('exit')) + Process.wait(pid2).should == pid2 + Process.wait(pid1).should == pid1 + lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH) + lambda { Process.kill(0, pid2) }.should raise_error(Errno::ESRCH) + end + + it "coerces the pid to an Integer" do + pid1 = Process.spawn(ruby_cmd('exit')) + Process.wait(mock_int(pid1)).should == pid1 + lambda { Process.kill(0, pid1) }.should raise_error(Errno::ESRCH) + end + + # This spec is probably system-dependent. + it "waits for a child whose process group ID is that of the calling process" do + pid1 = Process.spawn(ruby_cmd('exit'), pgroup: true) + pid2 = Process.spawn(ruby_cmd('exit')) + + Process.wait(0).should == pid2 + Process.wait.should == pid1 + end + + # This spec is probably system-dependent. + it "doesn't block if no child is available when WNOHANG is used" do + read, write = IO.pipe + pid = Process.fork do + read.close + Signal.trap("TERM") { Process.exit! } + write << 1 + write.close + sleep + end + + Process.wait(pid, Process::WNOHANG).should be_nil + + # wait for the child to setup its TERM handler + write.close + read.read(1) + read.close + + Process.kill("TERM", pid) + Process.wait.should == pid + end + + it "always accepts flags=0" do + pid = Process.spawn(ruby_cmd('exit')) + Process.wait(-1, 0).should == pid + lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH) + end + end +end diff --git a/spec/rubyspec/core/process/waitall_spec.rb b/spec/rubyspec/core/process/waitall_spec.rb new file mode 100644 index 0000000000..e1fc38d2bc --- /dev/null +++ b/spec/rubyspec/core/process/waitall_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.waitall" do + before :all do + begin + Process.waitall + rescue NotImplementedError + end + end + + it "returns an empty array when there are no children" do + Process.waitall.should == [] + end + + it "takes no arguments" do + lambda { Process.waitall(0) }.should raise_error(ArgumentError) + end + + platform_is_not :windows do + it "waits for all children" do + pids = [] + pids << Process.fork { Process.exit! 2 } + pids << Process.fork { Process.exit! 1 } + pids << Process.fork { Process.exit! 0 } + Process.waitall + pids.each { |pid| + lambda { Process.kill(0, pid) }.should raise_error(Errno::ESRCH) + } + end + + it "returns an array of pid/status pairs" do + pids = [] + pids << Process.fork { Process.exit! 2 } + pids << Process.fork { Process.exit! 1 } + pids << Process.fork { Process.exit! 0 } + a = Process.waitall + a.should be_kind_of(Array) + a.size.should == 3 + pids.each { |pid| + pid_status = a.assoc(pid) + pid_status.should be_kind_of(Array) + pid_status.size.should == 2 + pid_status.first.should == pid + pid_status.last.should be_kind_of(Process::Status) + } + end + end +end diff --git a/spec/rubyspec/core/process/waitpid2_spec.rb b/spec/rubyspec/core/process/waitpid2_spec.rb new file mode 100644 index 0000000000..303c59fda3 --- /dev/null +++ b/spec/rubyspec/core/process/waitpid2_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.waitpid2" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/process/waitpid_spec.rb b/spec/rubyspec/core/process/waitpid_spec.rb new file mode 100644 index 0000000000..c95e8d59dd --- /dev/null +++ b/spec/rubyspec/core/process/waitpid_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Process.waitpid" do + it "needs to be reviewed for spec completeness" + + it "returns nil when the process has not yet completed and WNOHANG is specified" do + pid = spawn("sleep 5") + begin + Process.waitpid(pid, Process::WNOHANG).should == nil + Process.kill("KILL", pid) + ensure + Process.wait(pid) + end + end +end diff --git a/spec/rubyspec/core/random/bytes_spec.rb b/spec/rubyspec/core/random/bytes_spec.rb new file mode 100644 index 0000000000..2434a4e72e --- /dev/null +++ b/spec/rubyspec/core/random/bytes_spec.rb @@ -0,0 +1,39 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Random#bytes" do + it "returns a String" do + Random.new.bytes(1).should be_an_instance_of(String) + end + + it "returns a String of the length given as argument" do + Random.new.bytes(15).length.should == 15 + end + + it "returns an ASCII-8BIT String" do + Random.new.bytes(15).encoding.should == Encoding::ASCII_8BIT + end + + it "returns the same output for a given seed" do + Random.new(33).bytes(2).should == Random.new(33).bytes(2) + end + + # Should double check this is official spec + it "returns the same numeric output for a given seed accross all implementations and platforms" do + rnd = Random.new(33) + rnd.bytes(2).should == "\x14\\" + rnd.bytes(1000) # skip some + rnd.bytes(2).should == "\xA1p" + end + + it "returns the same numeric output for a given huge seed accross all implementations and platforms" do + rnd = Random.new(bignum_value ** 4) + rnd.bytes(2).should == "_\x91" + rnd.bytes(1000) # skip some + rnd.bytes(2).should == "\x17\x12" + end + + it "returns a random binary String" do + Random.new.bytes(12).should_not == Random.new.bytes(12) + end +end diff --git a/spec/rubyspec/core/random/equal_value_spec.rb b/spec/rubyspec/core/random/equal_value_spec.rb new file mode 100644 index 0000000000..738f549f1d --- /dev/null +++ b/spec/rubyspec/core/random/equal_value_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Random#==" do + it "returns true if the two objects have the same state" do + a = Random.new(42) + b = Random.new(42) + a.send(:state).should == b.send(:state) + a.should == b + end + + it "returns false if the two objects have different state" do + a = Random.new + b = Random.new + a.send(:state).should_not == b.send(:state) + a.should_not == b + end + + it "returns true if the two objects have the same seed" do + a = Random.new(42) + b = Random.new(42.5) + a.seed.should == b.seed + a.should == b + end + + it "returns false if the two objects have a different seed" do + a = Random.new(42) + b = Random.new(41) + a.seed.should_not == b.seed + a.should_not == b + end + + it "returns false if the other object is not a Random" do + a = Random.new(42) + a.should_not == 42 + a.should_not == [a] + end +end diff --git a/spec/rubyspec/core/random/new_seed_spec.rb b/spec/rubyspec/core/random/new_seed_spec.rb new file mode 100644 index 0000000000..a8efaee2b1 --- /dev/null +++ b/spec/rubyspec/core/random/new_seed_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Random.new_seed" do + it "returns a Bignum" do + Random.new_seed.should be_an_instance_of(Bignum) + end + + it "returns an arbitrary seed value each time" do + bigs = 200.times.map { Random.new_seed } + bigs.uniq.size.should == 200 + end + + it "is not affected by Kernel#srand" do + begin + srand 25 + a = Random.new_seed + srand 25 + b = Random.new_seed + a.should_not == b + ensure + srand Random.new_seed + end + end +end diff --git a/spec/rubyspec/core/random/new_spec.rb b/spec/rubyspec/core/random/new_spec.rb new file mode 100644 index 0000000000..8160f44d79 --- /dev/null +++ b/spec/rubyspec/core/random/new_spec.rb @@ -0,0 +1,37 @@ +describe "Random.new" do + it "returns a new instance of Random" do + Random.new.should be_an_instance_of(Random) + end + + it "uses a random seed value if none is supplied" do + Random.new.seed.should be_an_instance_of(Bignum) + end + + it "returns Random instances initialized with different seeds" do + first = Random.new + second = Random.new + (0..20).map { first.rand } .should_not == (0..20).map { second.rand } + end + + it "accepts an Integer seed value as an argument" do + Random.new(2).seed.should == 2 + end + + it "accepts (and truncates) a Float seed value as an argument" do + Random.new(3.4).seed.should == 3 + end + + it "accepts (and converts to Integer) a Rational seed value as an argument" do + Random.new(Rational(20,2)).seed.should == 10 + end + + it "accepts (and converts to Integer) a Complex (without imaginary part) seed value as an argument" do + Random.new(Complex(20)).seed.should == 20 + end + + it "raises a RangeError if passed a Complex (with imaginary part) seed value as an argument" do + lambda do + Random.new(Complex(20,2)) + end.should raise_error(RangeError) + end +end diff --git a/spec/rubyspec/core/random/rand_spec.rb b/spec/rubyspec/core/random/rand_spec.rb new file mode 100644 index 0000000000..2f10ed080a --- /dev/null +++ b/spec/rubyspec/core/random/rand_spec.rb @@ -0,0 +1,216 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Random.rand" do + it "returns a Float if no max argument is passed" do + Random.rand.should be_kind_of(Float) + end + + it "returns a Float >= 0 if no max argument is passed" do + floats = 200.times.map { Random.rand } + floats.min.should >= 0 + end + + it "returns a Float < 1 if no max argument is passed" do + floats = 200.times.map { Random.rand } + floats.max.should < 1 + end + + it "returns the same sequence for a given seed if no max argument is passed" do + Random.srand 33 + floats_a = 20.times.map { Random.rand } + Random.srand 33 + floats_b = 20.times.map { Random.rand } + floats_a.should == floats_b + end + + it "returns an Integer if an Integer argument is passed" do + Random.rand(20).should be_kind_of(Integer) + end + + it "returns an Integer >= 0 if an Integer argument is passed" do + ints = 200.times.map { Random.rand(34) } + ints.min.should >= 0 + end + + it "returns an Integer < the max argument if an Integer argument is passed" do + ints = 200.times.map { Random.rand(55) } + ints.max.should < 55 + end + + it "returns the same sequence for a given seed if an Integer argument is passed" do + Random.srand 33 + floats_a = 20.times.map { Random.rand(90) } + Random.srand 33 + floats_b = 20.times.map { Random.rand(90) } + floats_a.should == floats_b + end + + it "coerces arguments to Integers with #to_int" do + obj = mock_numeric('int') + obj.should_receive(:to_int).and_return(99) + Random.rand(obj).should be_kind_of(Integer) + end +end + +describe "Random#rand with Fixnum" do + it "returns an Integer" do + Random.new.rand(20).should be_an_instance_of(Fixnum) + end + + it "returns a Fixnum greater than or equal to 0" do + prng = Random.new + ints = 20.times.map { prng.rand(5) } + ints.min.should >= 0 + end + + it "returns a Fixnum less than the argument" do + prng = Random.new + ints = 20.times.map { prng.rand(5) } + ints.max.should <= 4 + end + + it "returns the same sequence for a given seed" do + prng = Random.new 33 + a = 20.times.map { prng.rand(90) } + prng = Random.new 33 + b = 20.times.map { prng.rand(90) } + a.should == b + end + + it "eventually returns all possible values" do + prng = Random.new 33 + 100.times.map{ prng.rand(10) }.uniq.sort.should == (0...10).to_a + end + + it "raises an ArgumentError when the argument is 0" do + lambda do + Random.new.rand(0) + end.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the argument is negative" do + lambda do + Random.new.rand(-12) + end.should raise_error(ArgumentError) + end +end + +describe "Random#rand with Bignum" do + it "typically returns a Bignum" do + rnd = Random.new(1) + 10.times.map{ rnd.rand(bignum_value*2) }.max.should be_an_instance_of(Bignum) + end + + it "returns a Bignum greater than or equal to 0" do + prng = Random.new + bigs = 20.times.map { prng.rand(bignum_value) } + bigs.min.should >= 0 + end + + it "returns a Bignum less than the argument" do + prng = Random.new + bigs = 20.times.map { prng.rand(bignum_value) } + bigs.max.should < bignum_value + end + + it "returns the same sequence for a given seed" do + prng = Random.new 33 + a = 20.times.map { prng.rand(bignum_value) } + prng = Random.new 33 + b = 20.times.map { prng.rand(bignum_value) } + a.should == b + end + + it "raises an ArgumentError when the argument is negative" do + lambda do + Random.new.rand(-bignum_value) + end.should raise_error(ArgumentError) + end +end + +describe "Random#rand with Float" do + it "returns a Float" do + Random.new.rand(20.43).should be_an_instance_of(Float) + end + + it "returns a Float greater than or equal to 0.0" do + prng = Random.new + floats = 20.times.map { prng.rand(5.2) } + floats.min.should >= 0.0 + end + + it "returns a Float less than the argument" do + prng = Random.new + floats = 20.times.map { prng.rand(4.30) } + floats.max.should < 4.30 + end + + it "returns the same sequence for a given seed" do + prng = Random.new 33 + a = 20.times.map { prng.rand(89.2928) } + prng = Random.new 33 + b = 20.times.map { prng.rand(89.2928) } + a.should == b + end + + it "raises an ArgumentError when the argument is negative" do + lambda do + Random.new.rand(-1.234567) + end.should raise_error(ArgumentError) + end +end + +describe "Random#rand with Range" do + it "returns an element from the Range" do + Random.new.rand(20..43).should be_an_instance_of(Fixnum) + end + + it "returns an object that is a member of the Range" do + prng = Random.new + r = 20..30 + 20.times { r.member?(prng.rand(r)).should be_true } + end + + it "works with inclusive ranges" do + prng = Random.new + r = 3..5 + 40.times.map { prng.rand(r) }.uniq.sort.should == [3,4,5] + end + + it "works with exclusive ranges" do + prng = Random.new + r = 3...5 + 20.times.map { prng.rand(r) }.uniq.sort.should == [3,4] + end + + it "returns the same sequence for a given seed" do + prng = Random.new 33 + a = 20.times.map { prng.rand(76890.028..800000.00) } + prng = Random.new 33 + b = 20.times.map { prng.rand(76890.028..800000.00) } + a.should == b + end + + it "eventually returns all possible values" do + prng = Random.new 33 + 100.times.map{ prng.rand(10..20) }.uniq.sort.should == (10..20).to_a + 100.times.map{ prng.rand(10...20) }.uniq.sort.should == (10...20).to_a + end + + it "considers Integers as Floats if one end point is a float" do + Random.new(42).rand(0.0..1).should be_kind_of(Float) + Random.new(42).rand(0..1.0).should be_kind_of(Float) + end + + it "raises an ArgumentError when the startpoint lacks #+ and #- methods" do + lambda do + Random.new.rand(Object.new..67) + end.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the endpoint lacks #+ and #- methods" do + lambda do + Random.new.rand(68..Object.new) + end.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/random/raw_seed_spec.rb b/spec/rubyspec/core/random/raw_seed_spec.rb new file mode 100644 index 0000000000..881cceada9 --- /dev/null +++ b/spec/rubyspec/core/random/raw_seed_spec.rb @@ -0,0 +1,9 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/urandom', __FILE__) + +ruby_version_is "2.5" do + describe "Random.urandom" do + it_behaves_like :random_urandom, :urandom + end +end diff --git a/spec/rubyspec/core/random/seed_spec.rb b/spec/rubyspec/core/random/seed_spec.rb new file mode 100644 index 0000000000..5acb068efe --- /dev/null +++ b/spec/rubyspec/core/random/seed_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Random#seed" do + it "returns an Integer" do + Random.new.seed.should be_kind_of(Integer) + end + + it "returns an arbitrary seed if the constructor was called without arguments" do + Random.new.seed.should_not == Random.new.seed + end + + it "returns the same generated seed when repeatedly called on the same object" do + prng = Random.new + prng.seed.should == prng.seed + end + + it "returns the seed given in the constructor" do + prng = Random.new(36788) + prng.seed.should == prng.seed + prng.seed.should == 36788 + end + + it "returns the given seed coerced with #to_int" do + obj = mock_numeric('int') + obj.should_receive(:to_int).and_return(34) + prng = Random.new(obj) + prng.seed.should == 34 + end +end diff --git a/spec/rubyspec/core/random/shared/urandom.rb b/spec/rubyspec/core/random/shared/urandom.rb new file mode 100644 index 0000000000..f50d30c9de --- /dev/null +++ b/spec/rubyspec/core/random/shared/urandom.rb @@ -0,0 +1,23 @@ +describe :random_urandom, shared: true do + it "returns a String" do + Random.send(@method, 1).should be_an_instance_of(String) + end + + it "returns a String of the length given as argument" do + Random.send(@method, 15).length.should == 15 + end + + it "raises an ArgumentError on a negative size" do + lambda { + Random.send(@method, -1) + }.should raise_error(ArgumentError) + end + + it "returns an ASCII-8BIT String" do + Random.send(@method, 15).encoding.should == Encoding::ASCII_8BIT + end + + it "returns a random binary String" do + Random.send(@method, 12).should_not == Random.send(@method, 12) + end +end diff --git a/spec/rubyspec/core/random/srand_spec.rb b/spec/rubyspec/core/random/srand_spec.rb new file mode 100644 index 0000000000..8ce863f5a9 --- /dev/null +++ b/spec/rubyspec/core/random/srand_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Random.srand" do + it "returns an arbitrary seed if .srand wasn't called previously with an argument and no argument is supplied this time" do + Random.srand # Reset to random seed in case .srand was called previously + Random.srand.should_not == Random.srand + end + + it "returns the previous argument to .srand if one was given and no argument is supplied" do + Random.srand 34 + Random.srand.should == 34 + end + + it "returns an arbitrary seed if .srand wasn't called previously with an argument and 0 is supplied this time" do + Random.srand # Reset to random seed in case .srand was called previously + Random.srand(0).should_not == Random.srand(0) + end + + it "returns the previous argument to .srand if one was given and 0 is supplied" do + Random.srand 34 + Random.srand(0).should == 34 + end + + it "seeds Random.rand such that its return value is deterministic" do + Random.srand 176542 + a = 20.times.map { Random.rand } + Random.srand 176542 + b = 20.times.map { Random.rand } + a.should == b + end + + it "seeds Kernel.rand such that its return value is deterministic" do + Random.srand 176542 + a = 20.times.map { Kernel.rand } + Random.srand 176542 + b = 20.times.map { Kernel.rand } + a.should == b + end +end diff --git a/spec/rubyspec/core/random/urandom_spec.rb b/spec/rubyspec/core/random/urandom_spec.rb new file mode 100644 index 0000000000..1cc0103e35 --- /dev/null +++ b/spec/rubyspec/core/random/urandom_spec.rb @@ -0,0 +1,9 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/urandom', __FILE__) + +ruby_version_is "2.3"..."2.5" do + describe "Random.raw_seed" do + it_behaves_like :random_urandom, :raw_seed + end +end diff --git a/spec/rubyspec/core/range/begin_spec.rb b/spec/rubyspec/core/range/begin_spec.rb new file mode 100644 index 0000000000..090ae0a24b --- /dev/null +++ b/spec/rubyspec/core/range/begin_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/begin', __FILE__) + +describe "Range#begin" do + it_behaves_like(:range_begin, :begin) +end diff --git a/spec/rubyspec/core/range/bsearch_spec.rb b/spec/rubyspec/core/range/bsearch_spec.rb new file mode 100644 index 0000000000..a10dcea61e --- /dev/null +++ b/spec/rubyspec/core/range/bsearch_spec.rb @@ -0,0 +1,137 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Range#bsearch" do + it "returns an Enumerator when not passed a block" do + (0..1).bsearch.should be_an_instance_of(Enumerator) + end + + it_behaves_like :enumeratorized_with_unknown_size, :bsearch, (1..3) + + it "raises a TypeError if the block returns an Object" do + lambda { (0..1).bsearch { Object.new } }.should raise_error(TypeError) + end + + it "raises a TypeError if the block returns a String" do + lambda { (0..1).bsearch { "1" } }.should raise_error(TypeError) + end + + it "raises a TypeError if the Range has Object values" do + value = mock("range bsearch") + r = Range.new value, value + + lambda { r.bsearch { true } }.should raise_error(TypeError) + end + + it "raises a TypeError if the Range has String values" do + lambda { ("a".."e").bsearch { true } }.should raise_error(TypeError) + end + + context "with Integer values" do + context "with a block returning true or false" do + it "returns nil if the block returns false for every element" do + (0...3).bsearch { |x| x > 3 }.should be_nil + end + + it "returns nil if the block returns nil for every element" do + (0..3).bsearch { |x| nil }.should be_nil + end + + it "returns minimum element if the block returns true for every element" do + (-2..4).bsearch { |x| x < 4 }.should == -2 + end + + it "returns the smallest element for which block returns true" do + (0..4).bsearch { |x| x >= 2 }.should == 2 + (-1..4).bsearch { |x| x >= 1 }.should == 1 + end + + it "returns the last element if the block returns true for the last element" do + (0..4).bsearch { |x| x >= 4 }.should == 4 + (0...4).bsearch { |x| x >= 3 }.should == 3 + end + end + + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns less than zero for every element" do + (0..3).bsearch { |x| x <=> 5 }.should be_nil + end + + it "returns nil if the block returns greater than zero for every element" do + (0..3).bsearch { |x| x <=> -1 }.should be_nil + + end + + it "returns nil if the block never returns zero" do + (0..3).bsearch { |x| x < 2 ? 1 : -1 }.should be_nil + end + + it "accepts (+/-)Float::INFINITY from the block" do + (0..4).bsearch { |x| Float::INFINITY }.should be_nil + (0..4).bsearch { |x| -Float::INFINITY }.should be_nil + end + + it "returns an element at an index for which block returns 0.0" do + result = (0..4).bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end + + it "returns an element at an index for which block returns 0" do + result = (0..4).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + [1, 2].should include(result) + end + end + end + + context "with Float values" do + context "with a block returning true or false" do + it "returns nil if the block returns false for every element" do + (0.1...2.3).bsearch { |x| x > 3 }.should be_nil + end + + it "returns nil if the block returns nil for every element" do + (-0.0..2.3).bsearch { |x| nil }.should be_nil + end + + it "returns minimum element if the block returns true for every element" do + (-0.2..4.8).bsearch { |x| x < 4 }.should == -0.2 + end + + it "returns the smallest element for which block returns true" do + (0..4.2).bsearch { |x| x >= 2 }.should == 2 + (-1.2..4.3).bsearch { |x| x >= 1 }.should == 1 + end + end + + context "with a block returning negative, zero, positive numbers" do + it "returns nil if the block returns less than zero for every element" do + (-2.0..3.2).bsearch { |x| x <=> 5 }.should be_nil + end + + it "returns nil if the block returns greater than zero for every element" do + (0.3..3.0).bsearch { |x| x <=> -1 }.should be_nil + + end + + it "returns nil if the block never returns zero" do + (0.2..2.3).bsearch { |x| x < 2 ? 1 : -1 }.should be_nil + end + + it "accepts (+/-)Float::INFINITY from the block" do + (0.1..4.5).bsearch { |x| Float::INFINITY }.should be_nil + (-5.0..4.0).bsearch { |x| -Float::INFINITY }.should be_nil + end + + it "returns an element at an index for which block returns 0.0" do + result = (0.0..4.0).bsearch { |x| x < 2 ? 1.0 : x > 2 ? -1.0 : 0.0 } + result.should == 2 + end + + it "returns an element at an index for which block returns 0" do + result = (0.1..4.9).bsearch { |x| x < 1 ? 1 : x > 3 ? -1 : 0 } + result.should >= 1 + result.should <= 2 + end + end + end +end diff --git a/spec/rubyspec/core/range/case_compare_spec.rb b/spec/rubyspec/core/range/case_compare_spec.rb new file mode 100644 index 0000000000..cada3b7cd5 --- /dev/null +++ b/spec/rubyspec/core/range/case_compare_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/cover_and_include', __FILE__) +require File.expand_path('../shared/cover', __FILE__) + +describe "Range#===" do + it "returns the result of calling #include? on self" do + range = 0...10 + range.should_receive(:include?).with(2).and_return(:true) + (range === 2).should == :true + end +end diff --git a/spec/rubyspec/core/range/cover_spec.rb b/spec/rubyspec/core/range/cover_spec.rb new file mode 100644 index 0000000000..9c8b914d6d --- /dev/null +++ b/spec/rubyspec/core/range/cover_spec.rb @@ -0,0 +1,9 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/cover_and_include', __FILE__) +require File.expand_path('../shared/cover', __FILE__) + +describe "Range#cover?" do + it_behaves_like :range_cover_and_include, :cover? + it_behaves_like :range_cover, :cover? +end diff --git a/spec/rubyspec/core/range/dup_spec.rb b/spec/rubyspec/core/range/dup_spec.rb new file mode 100644 index 0000000000..cdb792c97f --- /dev/null +++ b/spec/rubyspec/core/range/dup_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#dup" do + it "duplicates the range" do + copy = (1..3).dup + copy.begin.should == 1 + copy.end.should == 3 + copy.exclude_end?.should == false + + copy = ("a"..."z").dup + copy.begin.should == "a" + copy.end.should == "z" + copy.exclude_end?.should == true + end +end diff --git a/spec/rubyspec/core/range/each_spec.rb b/spec/rubyspec/core/range/each_spec.rb new file mode 100644 index 0000000000..4520f3cde6 --- /dev/null +++ b/spec/rubyspec/core/range/each_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Range#each" do + it "passes each element to the given block by using #succ" do + a = [] + (-5..5).each { |i| a << i } + a.should == [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] + + a = [] + ('A'..'D').each { |i| a << i } + a.should == ['A','B','C','D'] + + a = [] + ('A'...'D').each { |i| a << i } + a.should == ['A','B','C'] + + a = [] + (0xfffd...0xffff).each { |i| a << i } + a.should == [0xfffd, 0xfffe] + + y = mock('y') + x = mock('x') + x.should_receive(:<=>).with(y).any_number_of_times.and_return(-1) + x.should_receive(:<=>).with(x).any_number_of_times.and_return(0) + x.should_receive(:succ).any_number_of_times.and_return(y) + y.should_receive(:<=>).with(x).any_number_of_times.and_return(1) + y.should_receive(:<=>).with(y).any_number_of_times.and_return(0) + + a = [] + (x..y).each { |i| a << i } + a.should == [x, y] + end + + it "raises a TypeError if the first element does not respond to #succ" do + lambda { (0.5..2.4).each { |i| i } }.should raise_error(TypeError) + + b = mock('x') + (a = mock('1')).should_receive(:<=>).with(b).and_return(1) + + lambda { (a..b).each { |i| i } }.should raise_error(TypeError) + end + + it "returns self" do + range = 1..10 + range.each{}.should equal(range) + end + + it "returns an enumerator when no block given" do + enum = (1..3).each + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == [1, 2, 3] + end + + it "raises a TypeError if the first element is a Time object" do + t = Time.now + lambda { (t..t+1).each { |i| i } }.should raise_error(TypeError) + end + + it "passes each Symbol element by using #succ" do + (:aa..:ac).each.to_a.should == [:aa, :ab, :ac] + (:aa...:ac).each.to_a.should == [:aa, :ab] + end + + it_behaves_like :enumeratorized_with_origin_size, :each, (1..3) +end diff --git a/spec/rubyspec/core/range/end_spec.rb b/spec/rubyspec/core/range/end_spec.rb new file mode 100644 index 0000000000..128f44c309 --- /dev/null +++ b/spec/rubyspec/core/range/end_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/end', __FILE__) + +describe "Range#end" do + it_behaves_like(:range_end, :end) +end diff --git a/spec/rubyspec/core/range/eql_spec.rb b/spec/rubyspec/core/range/eql_spec.rb new file mode 100644 index 0000000000..2d495ee175 --- /dev/null +++ b/spec/rubyspec/core/range/eql_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Range#eql?" do + it_behaves_like(:range_eql, :eql?) + + it "returns false if the endpoints are not eql?" do + (0..1).send(@method, 0..1.0).should == false + end +end diff --git a/spec/rubyspec/core/range/equal_value_spec.rb b/spec/rubyspec/core/range/equal_value_spec.rb new file mode 100644 index 0000000000..488fe73c55 --- /dev/null +++ b/spec/rubyspec/core/range/equal_value_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Range#==" do + it_behaves_like(:range_eql, :==) + + it "returns true if the endpoints are ==" do + (0..1).send(@method, 0..1.0).should == true + end +end diff --git a/spec/rubyspec/core/range/exclude_end_spec.rb b/spec/rubyspec/core/range/exclude_end_spec.rb new file mode 100644 index 0000000000..5bbdaa25a7 --- /dev/null +++ b/spec/rubyspec/core/range/exclude_end_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#exclude_end?" do + it "returns false if the range does not exclude the end value" do + (-2..2).exclude_end?.should == false + ('A'..'B').exclude_end?.should == false + (0.5..2.4).exclude_end?.should == false + (0xfffd..0xffff).exclude_end?.should == false + Range.new(0, 1).exclude_end?.should == false + end + + it "returns true if the range excludes the end value" do + (0...5).exclude_end?.should == true + ('A'...'B').exclude_end?.should == true + (0.5...2.4).exclude_end?.should == true + (0xfffd...0xffff).exclude_end?.should == true + Range.new(0, 1, true).exclude_end?.should == true + end +end diff --git a/spec/rubyspec/core/range/first_spec.rb b/spec/rubyspec/core/range/first_spec.rb new file mode 100644 index 0000000000..7cd3781d34 --- /dev/null +++ b/spec/rubyspec/core/range/first_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/begin', __FILE__) + +describe "Range#first" do + it_behaves_like(:range_begin, :first) + + it "returns the specified number of elements from the beginning" do + (0..2).first(2).should == [0, 1] + end + + it "returns an empty array for an empty Range" do + (0...0).first(2).should == [] + end + + it "returns an empty array when passed zero" do + (0..2).first(0).should == [] + end + + it "returns all elements in the range when count exceeds the number of elements" do + (0..2).first(4).should == [0, 1, 2] + end + + it "raises an ArgumentError when count is negative" do + lambda { (0..2).first(-1) }.should raise_error(ArgumentError) + end + + it "calls #to_int to convert the argument" do + obj = mock_int(2) + (3..7).first(obj).should == [3, 4] + end + + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return("1") + lambda { (2..3).first(obj) }.should raise_error(TypeError) + end + + it "truncates the value when passed a Float" do + (2..9).first(2.8).should == [2, 3] + end + + it "raises a TypeError when passed nil" do + lambda { (2..3).first(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { (2..3).first("1") }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/range/fixtures/classes.rb b/spec/rubyspec/core/range/fixtures/classes.rb new file mode 100644 index 0000000000..de46d7d4a9 --- /dev/null +++ b/spec/rubyspec/core/range/fixtures/classes.rb @@ -0,0 +1,65 @@ +module RangeSpecs + class TenfoldSucc + include Comparable + + attr_reader :n + + def initialize(n) + @n = n + end + + def <=>(other) + @n <=> other.n + end + + def succ + self.class.new(@n * 10) + end + end + + # Custom Range classes Xs and Ys + class Custom + include Comparable + attr_reader :length + + def initialize(n) + @length = n + end + + def eql?(other) + inspect.eql? other.inspect + end + alias :== :eql? + + def inspect + 'custom' + end + + def <=>(other) + @length <=> other.length + end + end + + class Xs < Custom # represent a string of 'x's + def succ + Xs.new(@length + 1) + end + + def inspect + 'x' * @length + end + end + + class Ys < Custom # represent a string of 'y's + def succ + Ys.new(@length + 1) + end + + def inspect + 'y' * @length + end + end + + class MyRange < Range + end +end diff --git a/spec/rubyspec/core/range/hash_spec.rb b/spec/rubyspec/core/range/hash_spec.rb new file mode 100644 index 0000000000..dcac523487 --- /dev/null +++ b/spec/rubyspec/core/range/hash_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#hash" do + it "is provided" do + (0..1).respond_to?(:hash).should == true + ('A'..'Z').respond_to?(:hash).should == true + (0xfffd..0xffff).respond_to?(:hash).should == true + (0.5..2.4).respond_to?(:hash).should == true + end + + it "generates the same hash values for Ranges with the same start, end and exclude_end? values" do + (0..1).hash.should == (0..1).hash + (0...10).hash.should == (0...10).hash + (0..10).hash.should_not == (0...10).hash + end + + it "generates a Fixnum for the hash value" do + (0..0).hash.should be_an_instance_of(Fixnum) + (0..1).hash.should be_an_instance_of(Fixnum) + (0...10).hash.should be_an_instance_of(Fixnum) + (0..10).hash.should be_an_instance_of(Fixnum) + end + +end diff --git a/spec/rubyspec/core/range/include_spec.rb b/spec/rubyspec/core/range/include_spec.rb new file mode 100644 index 0000000000..2d7450f8ae --- /dev/null +++ b/spec/rubyspec/core/range/include_spec.rb @@ -0,0 +1,10 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/cover_and_include', __FILE__) +require File.expand_path('../shared/include', __FILE__) +require File.expand_path('../shared/cover', __FILE__) + +describe "Range#include?" do + it_behaves_like :range_cover_and_include, :include? + it_behaves_like :range_include, :include? +end diff --git a/spec/rubyspec/core/range/initialize_spec.rb b/spec/rubyspec/core/range/initialize_spec.rb new file mode 100644 index 0000000000..457f048f8a --- /dev/null +++ b/spec/rubyspec/core/range/initialize_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#initialize" do + before do + @range = Range.allocate + end + + it "is private" do + Range.should have_private_instance_method("initialize") + end + + it "initializes correctly the Range object when given 2 arguments" do + lambda { @range.send(:initialize, 0, 1) }.should_not raise_error + end + + it "initializes correctly the Range object when given 3 arguments" do + lambda { @range.send(:initialize, 0, 1, true) }.should_not raise_error + end + + it "raises an ArgumentError if passed without or with only one argument" do + lambda { @range.send(:initialize) }.should raise_error(ArgumentError) + lambda { @range.send(:initialize, 1) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed with four or more arguments" do + lambda { @range.send(:initialize, 1, 3, 5, 7) }.should raise_error(ArgumentError) + lambda { @range.send(:initialize, 1, 3, 5, 7, 9) }.should raise_error(ArgumentError) + end + + it "raises a NameError if called on an already initialized Range" do + lambda { (0..1).send(:initialize, 1, 3) }.should raise_error(NameError) + lambda { (0..1).send(:initialize, 1, 3, true) }.should raise_error(NameError) + end + + it "raises an ArgumentError if arguments don't respond to <=>" do + o1 = Object.new + o2 = Object.new + + lambda { @range.send(:initialize, o1, o2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/range/inspect_spec.rb b/spec/rubyspec/core/range/inspect_spec.rb new file mode 100644 index 0000000000..8f5de933d3 --- /dev/null +++ b/spec/rubyspec/core/range/inspect_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#inspect" do + it "provides a printable form, using #inspect to convert the start and end objects" do + ('A'..'Z').inspect.should == '"A".."Z"' + ('A'...'Z').inspect.should == '"A"..."Z"' + + (0..21).inspect.should == "0..21" + (-8..0).inspect.should == "-8..0" + (-411..959).inspect.should == "-411..959" + (0xfff..0xfffff).inspect.should == "4095..1048575" + (0.5..2.4).inspect.should == "0.5..2.4" + end + + it "returns a tainted string if either end is tainted" do + (("a".taint)..."c").inspect.tainted?.should be_true + ("a"...("c".taint)).inspect.tainted?.should be_true + ("a"..."c").taint.inspect.tainted?.should be_true + end + + it "returns a untrusted string if either end is untrusted" do + (("a".untrust)..."c").inspect.untrusted?.should be_true + ("a"...("c".untrust)).inspect.untrusted?.should be_true + ("a"..."c").untrust.inspect.untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/range/last_spec.rb b/spec/rubyspec/core/range/last_spec.rb new file mode 100644 index 0000000000..581e04f785 --- /dev/null +++ b/spec/rubyspec/core/range/last_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/end', __FILE__) + +describe "Range#last" do + it_behaves_like(:range_end, :last) + + it "returns the specified number of elements from the end" do + (1..5).last(3).should == [3, 4, 5] + end + + it "returns an empty array for an empty Range" do + (0...0).last(2).should == [] + end + + it "returns an empty array when passed zero" do + (0..2).last(0).should == [] + end + + it "returns all elements in the range when count exceeds the number of elements" do + (2..4).last(5).should == [2, 3, 4] + end + + it "raises an ArgumentError when count is negative" do + lambda { (0..2).last(-1) }.should raise_error(ArgumentError) + end + + it "calls #to_int to convert the argument" do + obj = mock_int(2) + (3..7).last(obj).should == [6, 7] + end + + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return("1") + lambda { (2..3).last(obj) }.should raise_error(TypeError) + end + + it "truncates the value when passed a Float" do + (2..9).last(2.8).should == [8, 9] + end + + it "raises a TypeError when passed nil" do + lambda { (2..3).last(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { (2..3).last("1") }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/range/max_spec.rb b/spec/rubyspec/core/range/max_spec.rb new file mode 100644 index 0000000000..66e40bce1a --- /dev/null +++ b/spec/rubyspec/core/range/max_spec.rb @@ -0,0 +1,82 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#max" do + it "returns the maximum value in the range when called with no arguments" do + (1..10).max.should == 10 + (1...10).max.should == 9 + (0...2**64).max.should == 18446744073709551615 + ('f'..'l').max.should == 'l' + ('a'...'f').max.should == 'e' + end + + it "returns the maximum value in the Float range when called with no arguments" do + (303.20..908.1111).max.should == 908.1111 + end + + it "raises TypeError when called on an exclusive range and a non Integer value" do + lambda { (303.20...908.1111).max }.should raise_error(TypeError) + end + + it "returns nil when the endpoint is less than the start point" do + (100..10).max.should be_nil + ('z'..'l').max.should be_nil + end + + it "returns nil when the endpoint equals the start point and the range is exclusive" do + (5...5).max.should be_nil + end + + it "returns the endpoint when the endpoint equals the start point and the range is inclusive" do + (5..5).max.should equal(5) + end + + it "returns nil when the endpoint is less than the start point in a Float range" do + (3003.20..908.1111).max.should be_nil + end + + it "returns end point when the range is Time..Time(included end point)" do + time_start = Time.now + time_end = Time.now + 1.0 + (time_start..time_end).max.should equal(time_end) + end + + it "raises TypeError when called on a Time...Time(excluded end point)" do + time_start = Time.now + time_end = Time.now + 1.0 + lambda { (time_start...time_end).max }.should raise_error(TypeError) + end +end + +describe "Range#max given a block" do + it "passes each pair of values in the range to the block" do + acc = [] + (1..10).max {|a,b| acc << [a,b]; a } + acc.flatten! + (1..10).each do |value| + acc.include?(value).should be_true + end + end + + it "passes each pair of elements to the block in reversed order" do + acc = [] + (1..5).max {|a,b| acc << [a,b]; a } + acc.should == [[2,1],[3,2], [4,3], [5, 4]] + end + + it "calls #> and #< on the return value of the block" do + obj = mock('obj') + obj.should_receive(:>).exactly(2).times + obj.should_receive(:<).exactly(2).times + (1..3).max {|a,b| obj } + end + + it "returns the element the block determines to be the maximum" do + (1..3).max {|a,b| -3 }.should == 1 + end + + it "returns nil when the endpoint is less than the start point" do + (100..10).max {|x,y| x <=> y}.should be_nil + ('z'..'l').max {|x,y| x <=> y}.should be_nil + (5...5).max {|x,y| x <=> y}.should be_nil + end +end diff --git a/spec/rubyspec/core/range/member_spec.rb b/spec/rubyspec/core/range/member_spec.rb new file mode 100644 index 0000000000..afcca9bf19 --- /dev/null +++ b/spec/rubyspec/core/range/member_spec.rb @@ -0,0 +1,10 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/cover_and_include', __FILE__) +require File.expand_path('../shared/include', __FILE__) +require File.expand_path('../shared/cover', __FILE__) + +describe "Range#member?" do + it_behaves_like :range_cover_and_include, :member? + it_behaves_like :range_include, :member? +end diff --git a/spec/rubyspec/core/range/min_spec.rb b/spec/rubyspec/core/range/min_spec.rb new file mode 100644 index 0000000000..b6eebd8c35 --- /dev/null +++ b/spec/rubyspec/core/range/min_spec.rb @@ -0,0 +1,75 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#min" do + it "returns the minimum value in the range when called with no arguments" do + (1..10).min.should == 1 + ('f'..'l').min.should == 'f' + end + + it "returns the minimum value in the Float range when called with no arguments" do + (303.20..908.1111).min.should == 303.20 + end + + it "returns nil when the start point is greater than the endpoint" do + (100..10).min.should be_nil + ('z'..'l').min.should be_nil + end + + it "returns nil when the endpoint equals the start point and the range is exclusive" do + (7...7).min.should be_nil + end + + it "returns the start point when the endpoint equals the start point and the range is inclusive" do + (7..7).min.should equal(7) + end + + it "returns nil when the start point is greater than the endpoint in a Float range" do + (3003.20..908.1111).min.should be_nil + end + + it "returns start point when the range is Time..Time(included end point)" do + time_start = Time.now + time_end = Time.now + 1.0 + (time_start..time_end).min.should equal(time_start) + end + + it "returns start point when the range is Time...Time(excluded end point)" do + time_start = Time.now + time_end = Time.now + 1.0 + (time_start...time_end).min.should equal(time_start) + end +end + +describe "Range#min given a block" do + it "passes each pair of values in the range to the block" do + acc = [] + (1..10).min {|a,b| acc << [a,b]; a } + acc.flatten! + (1..10).each do |value| + acc.include?(value).should be_true + end + end + + it "passes each pair of elements to the block where the first argument is the current element, and the last is the first element" do + acc = [] + (1..5).min {|a,b| acc << [a,b]; a } + acc.should == [[2, 1], [3, 1], [4, 1], [5, 1]] + end + + it "calls #> and #< on the return value of the block" do + obj = mock('obj') + obj.should_receive(:>).exactly(2).times + obj.should_receive(:<).exactly(2).times + (1..3).min {|a,b| obj } + end + + it "returns the element the block determines to be the minimum" do + (1..3).min {|a,b| -3 }.should == 3 + end + + it "returns nil when the start point is greater than the endpoint" do + (100..10).min {|x,y| x <=> y}.should be_nil + ('z'..'l').min {|x,y| x <=> y}.should be_nil + (7...7).min {|x,y| x <=> y}.should be_nil + end +end diff --git a/spec/rubyspec/core/range/new_spec.rb b/spec/rubyspec/core/range/new_spec.rb new file mode 100644 index 0000000000..f0d24e4aeb --- /dev/null +++ b/spec/rubyspec/core/range/new_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range.new" do + it "constructs a range using the given start and end" do + range = Range.new('a', 'c') + range.should == ('a'..'c') + + range.first.should == 'a' + range.last.should == 'c' + end + + it "includes the end object when the third parameter is omitted or false" do + Range.new('a', 'c').to_a.should == ['a', 'b', 'c'] + Range.new(1, 3).to_a.should == [1, 2, 3] + + Range.new('a', 'c', false).to_a.should == ['a', 'b', 'c'] + Range.new(1, 3, false).to_a.should == [1, 2, 3] + + Range.new('a', 'c', true).to_a.should == ['a', 'b'] + Range.new(1, 3, 1).to_a.should == [1, 2] + + Range.new(1, 3, mock('[1,2]')).to_a.should == [1, 2] + Range.new(1, 3, :test).to_a.should == [1, 2] + end + + it "raises an ArgumentError when the given start and end can't be compared by using #<=>" do + lambda { Range.new(1, mock('x')) }.should raise_error(ArgumentError) + lambda { Range.new(mock('x'), mock('y')) }.should raise_error(ArgumentError) + + b = mock('x') + (a = mock('nil')).should_receive(:<=>).with(b).and_return(nil) + lambda { Range.new(a, b) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/range/range_spec.rb b/spec/rubyspec/core/range/range_spec.rb new file mode 100644 index 0000000000..ca6da29e47 --- /dev/null +++ b/spec/rubyspec/core/range/range_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range" do + it "includes Enumerable" do + Range.include?(Enumerable).should == true + end +end diff --git a/spec/rubyspec/core/range/shared/begin.rb b/spec/rubyspec/core/range/shared/begin.rb new file mode 100644 index 0000000000..f660e3faf9 --- /dev/null +++ b/spec/rubyspec/core/range/shared/begin.rb @@ -0,0 +1,10 @@ +describe :range_begin, shared: true do + it "returns the first element of self" do + (-1..1).send(@method).should == -1 + (0..1).send(@method).should == 0 + (0xffff...0xfffff).send(@method).should == 65535 + ('Q'..'T').send(@method).should == 'Q' + ('Q'...'T').send(@method).should == 'Q' + (0.5..2.4).send(@method).should == 0.5 + end +end diff --git a/spec/rubyspec/core/range/shared/cover.rb b/spec/rubyspec/core/range/shared/cover.rb new file mode 100644 index 0000000000..1d9c008a87 --- /dev/null +++ b/spec/rubyspec/core/range/shared/cover.rb @@ -0,0 +1,93 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :range_cover, shared: true do + it "uses the range element's <=> to make the comparison" do + a = mock('a') + a.should_receive(:<=>).twice.and_return(-1,-1) + (a..'z').send(@method, 'b').should be_true + end + + it "uses a continuous inclusion test" do + ('a'..'f').send(@method, 'aa').should be_true + ('a'..'f').send(@method, 'babe').should be_true + ('a'..'f').send(@method, 'baby').should be_true + ('a'..'f').send(@method, 'ga').should be_false + (-10..-2).send(@method, -2.5).should be_true + end + + describe "on string elements" do + it "returns true if other is matched by element.succ" do + ('a'..'c').send(@method, 'b').should be_true + ('a'...'c').send(@method, 'b').should be_true + end + + it "returns true if other is not matched by element.succ" do + ('a'..'c').send(@method, 'bc').should be_true + ('a'...'c').send(@method, 'bc').should be_true + end + end + + describe "with weird succ" do + describe "when included end value" do + before :each do + @range = RangeSpecs::TenfoldSucc.new(1)..RangeSpecs::TenfoldSucc.new(99) + end + + it "returns false if other is less than first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + end + + it "returns true if other is equal as first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + end + + it "returns true if other is matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + end + + it "returns true if other is not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_true + end + + it "returns true if other is equal as last element but not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_true + end + + it "returns false if other is greater than last element but matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + end + end + + describe "when excluded end value" do + before :each do + @range = RangeSpecs::TenfoldSucc.new(1)...RangeSpecs::TenfoldSucc.new(99) + end + + it "returns false if other is less than first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + end + + it "returns true if other is equal as first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + end + + it "returns true if other is matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + end + + it "returns true if other is not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_true + end + + it "returns false if other is equal as last element but not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false + end + + it "returns false if other is greater than last element but matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + end + end + end +end diff --git a/spec/rubyspec/core/range/shared/cover_and_include.rb b/spec/rubyspec/core/range/shared/cover_and_include.rb new file mode 100644 index 0000000000..4222424571 --- /dev/null +++ b/spec/rubyspec/core/range/shared/cover_and_include.rb @@ -0,0 +1,66 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :range_cover_and_include, shared: true do + it "returns true if other is an element of self" do + (0..5).send(@method, 2).should == true + (-5..5).send(@method, 0).should == true + (-1...1).send(@method, 10.5).should == false + (-10..-2).send(@method, -2.5).should == true + ('C'..'X').send(@method, 'M').should == true + ('C'..'X').send(@method, 'A').should == false + ('B'...'W').send(@method, 'W').should == false + ('B'...'W').send(@method, 'Q').should == true + (0xffff..0xfffff).send(@method, 0xffffd).should == true + (0xffff..0xfffff).send(@method, 0xfffd).should == false + (0.5..2.4).send(@method, 2).should == true + (0.5..2.4).send(@method, 2.5).should == false + (0.5..2.4).send(@method, 2.4).should == true + (0.5...2.4).send(@method, 2.4).should == false + end + + it "compares values using <=>" do + rng = (1..5) + m = mock("int") + m.should_receive(:coerce).and_return([1, 2]) + m.should_receive(:<=>).and_return(1) + + rng.send(@method, m).should be_false + end + + it "raises an ArgumentError without exactly one argument" do + lambda{ (1..2).send(@method) }.should raise_error(ArgumentError) + lambda{ (1..2).send(@method, 1, 2) }.should raise_error(ArgumentError) + end + + it "returns true if argument is equal to the first value of the range" do + (0..5).send(@method, 0).should be_true + ('f'..'s').send(@method, 'f').should be_true + end + + it "returns true if argument is equal to the last value of the range" do + (0..5).send(@method, 5).should be_true + (0...5).send(@method, 4).should be_true + ('f'..'s').send(@method, 's').should be_true + end + + it "returns true if argument is less than the last value of the range and greater than the first value" do + (20..30).send(@method, 28).should be_true + ('e'..'h').send(@method, 'g').should be_true + ("\u{999}".."\u{9999}").send @method, "\u{9995}" + end + + it "returns true if argument is sole element in the range" do + (30..30).send(@method, 30).should be_true + end + + it "returns false if range is empty" do + (30...30).send(@method, 30).should be_false + (30...30).send(@method, nil).should be_false + end + + it "returns false if the range does not contain the argument" do + ('A'..'C').send(@method, 20.9).should be_false + ('A'...'C').send(@method, 'C').should be_false + end +end diff --git a/spec/rubyspec/core/range/shared/end.rb b/spec/rubyspec/core/range/shared/end.rb new file mode 100644 index 0000000000..b26394fe31 --- /dev/null +++ b/spec/rubyspec/core/range/shared/end.rb @@ -0,0 +1,10 @@ +describe :range_end, shared: true do + it "end returns the last element of self" do + (-1..1).send(@method).should == 1 + (0..1).send(@method).should == 1 + ("A".."Q").send(@method).should == "Q" + ("A"..."Q").send(@method).should == "Q" + (0xffff...0xfffff).send(@method).should == 1048575 + (0.5..2.4).send(@method).should == 2.4 + end +end diff --git a/spec/rubyspec/core/range/shared/equal_value.rb b/spec/rubyspec/core/range/shared/equal_value.rb new file mode 100644 index 0000000000..0bdcf65c3f --- /dev/null +++ b/spec/rubyspec/core/range/shared/equal_value.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :range_eql, shared: true do + it "returns true if other has same begin, end, and exclude_end? values" do + (0..2).send(@method, 0..2).should == true + ('G'..'M').send(@method,'G'..'M').should == true + (0.5..2.4).send(@method, 0.5..2.4).should == true + (5..10).send(@method, Range.new(5,10)).should == true + ('D'..'V').send(@method, Range.new('D','V')).should == true + (0.5..2.4).send(@method, Range.new(0.5, 2.4)).should == true + (0xffff..0xfffff).send(@method, 0xffff..0xfffff).should == true + (0xffff..0xfffff).send(@method, Range.new(0xffff,0xfffff)).should == true + + a = RangeSpecs::Xs.new(3)..RangeSpecs::Xs.new(5) + b = Range.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5)) + a.send(@method, b).should == true + end + + it "returns false if one of the attributes differs" do + ('Q'..'X').send(@method, 'A'..'C').should == false + ('Q'...'X').send(@method, 'Q'..'W').should == false + ('Q'..'X').send(@method, 'Q'...'X').should == false + (0.5..2.4).send(@method, 0.5...2.4).should == false + (1482..1911).send(@method, 1482...1911).should == false + (0xffff..0xfffff).send(@method, 0xffff...0xfffff).should == false + + a = RangeSpecs::Xs.new(3)..RangeSpecs::Xs.new(5) + b = Range.new(RangeSpecs::Ys.new(3), RangeSpecs::Ys.new(5)) + a.send(@method, b).should == false + end + + it "returns false if other is not a Range" do + (1..10).send(@method, 1).should == false + (1..10).send(@method, 'a').should == false + (1..10).send(@method, mock('x')).should == false + end + + it "returns true for subclasses of Range" do + Range.new(1, 2).send(@method, RangeSpecs::MyRange.new(1, 2)).should == true + + a = Range.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5)) + b = RangeSpecs::MyRange.new(RangeSpecs::Xs.new(3), RangeSpecs::Xs.new(5)) + a.send(@method, b).should == true + end +end diff --git a/spec/rubyspec/core/range/shared/include.rb b/spec/rubyspec/core/range/shared/include.rb new file mode 100644 index 0000000000..44fd86f067 --- /dev/null +++ b/spec/rubyspec/core/range/shared/include.rb @@ -0,0 +1,91 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :range_include, shared: true do + describe "on string elements" do + it "returns true if other is matched by element.succ" do + ('a'..'c').send(@method, 'b').should be_true + ('a'...'c').send(@method, 'b').should be_true + end + + it "returns false if other is not matched by element.succ" do + ('a'..'c').send(@method, 'bc').should be_false + ('a'...'c').send(@method, 'bc').should be_false + end + end + + describe "with weird succ" do + describe "when included end value" do + before :each do + @range = RangeSpecs::TenfoldSucc.new(1)..RangeSpecs::TenfoldSucc.new(99) + end + + it "returns false if other is less than first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + end + + it "returns true if other is equal as first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + end + + it "returns true if other is matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + end + + it "returns false if other is not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_false + end + + it "returns false if other is equal as last element but not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false + end + + it "returns false if other is greater than last element but matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + end + end + + describe "when excluded end value" do + before :each do + @range = RangeSpecs::TenfoldSucc.new(1)...RangeSpecs::TenfoldSucc.new(99) + end + + it "returns false if other is less than first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(0)).should be_false + end + + it "returns true if other is equal as first element" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(1)).should be_true + end + + it "returns true if other is matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(10)).should be_true + end + + it "returns false if other is not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(2)).should be_false + end + + it "returns false if other is equal as last element but not matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(99)).should be_false + end + + it "returns false if other is greater than last element but matched by element.succ" do + @range.send(@method, RangeSpecs::TenfoldSucc.new(100)).should be_false + end + end + end + + describe "with Time endpoints" do + it "uses cover? logic" do + now = Time.now + range = (now..(now + 60)) + + range.include?(now).should == true + range.include?(now - 1).should == false + range.include?(now + 60).should == true + range.include?(now + 61).should == false + end + end +end diff --git a/spec/rubyspec/core/range/size_spec.rb b/spec/rubyspec/core/range/size_spec.rb new file mode 100644 index 0000000000..ecf0ecf606 --- /dev/null +++ b/spec/rubyspec/core/range/size_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#size" do + it "returns the number of elements in the range" do + (1..16).size.should == 16 + (1...16).size.should == 15 + + (1.0..16.0).size.should == 16 + (1.0...16.0).size.should == 15 + (1.0..15.9).size.should == 15 + (1.1..16.0).size.should == 15 + (1.1..15.9).size.should == 15 + end + + it "returns 0 if last is less than first" do + (16..0).size.should == 0 + (16.0..0.0).size.should == 0 + (Float::INFINITY..0).size.should == 0 + end + + it 'returns Float::INFINITY for increasing, infinite ranges' do + (0..Float::INFINITY).size.should == Float::INFINITY + (-Float::INFINITY..0).size.should == Float::INFINITY + (-Float::INFINITY..Float::INFINITY).size.should == Float::INFINITY + end + + it "returns nil if first and last are not Numeric" do + (:a..:z).size.should be_nil + ('a'..'z').size.should be_nil + end +end diff --git a/spec/rubyspec/core/range/step_spec.rb b/spec/rubyspec/core/range/step_spec.rb new file mode 100644 index 0000000000..b5bce66861 --- /dev/null +++ b/spec/rubyspec/core/range/step_spec.rb @@ -0,0 +1,347 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#step" do + before :each do + ScratchPad.record [] + end + + it "returns an enumerator when no block is given" do + enum = (1..10).step(4) + enum.should be_an_instance_of(Enumerator) + enum.to_a.should eql([1, 5, 9]) + end + + it "returns self" do + r = 1..2 + r.step { }.should equal(r) + end + + it "raises TypeError if step" do + obj = mock("mock") + lambda { (1..10).step(obj) { } }.should raise_error(TypeError) + end + + it "calls #to_int to coerce step to an Integer" do + obj = mock("Range#step") + obj.should_receive(:to_int).and_return(1) + + (1..2).step(obj) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([1, 2]) + end + + it "raises a TypeError if step does not respond to #to_int" do + obj = mock("Range#step non-integer") + + lambda { (1..2).step(obj) { } }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("Range#step non-integer") + obj.should_receive(:to_int).and_return("1") + + lambda { (1..2).step(obj) { } }.should raise_error(TypeError) + end + + it "coerces the argument to integer by invoking to_int" do + (obj = mock("2")).should_receive(:to_int).and_return(2) + res = [] + (1..10).step(obj) {|x| res << x} + res.should == [1, 3, 5, 7, 9] + end + + it "raises a TypeError if the first element does not respond to #succ" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) + + lambda { (obj..obj).step { |x| x } }.should raise_error(TypeError) + end + + it "raises an ArgumentError if step is 0" do + lambda { (-1..1).step(0) { |x| x } }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if step is 0.0" do + lambda { (-1..1).step(0.0) { |x| x } }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if step is negative" do + lambda { (-1..1).step(-2) { |x| x } }.should raise_error(ArgumentError) + end + + describe "with inclusive end" do + describe "and Integer values" do + it "yields Integer values incremented by 1 and less than or equal to end when not passed a step" do + (-2..2).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2, -1, 0, 1, 2]) + end + + it "yields Integer values incremented by an Integer step" do + (-5..5).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5, -3, -1, 1, 3, 5]) + end + + it "yields Float values incremented by a Float step" do + (-2..2).step(1.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -0.5, 1.0]) + end + end + + describe "and Float values" do + it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do + (-2.0..2.0).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0]) + end + + it "yields Float values incremented by an Integer step" do + (-5.0..5.0).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) + end + + it "yields Float values incremented by a Float step" do + (-1.0..1.0).step(0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0]) + end + + it "returns Float values of 'step * n + begin <= end'" do + (1.0..6.4).step(1.8) { |x| ScratchPad << x } + (1.0..12.7).step(1.3) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 6.4, 1.0, 2.3, 3.6, + 4.9, 6.2, 7.5, 8.8, 10.1, 11.4, 12.7]) + end + end + + describe "and Integer, Float values" do + it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do + (-2..2.0).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0]) + end + + it "yields Float values incremented by an Integer step" do + (-5..5.0).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) + end + + it "yields Float values incremented by a Float step" do + (-1..1.0).step(0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0]) + end + end + + describe "and Float, Integer values" do + it "yields Float values incremented by 1 and less than or equal to end when not passed a step" do + (-2.0..2).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0, 2.0]) + end + + it "yields Float values incremented by an Integer step" do + (-5.0..5).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0, 5.0]) + end + + it "yields Float values incremented by a Float step" do + (-1.0..1).step(0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5, 1.0]) + end + end + + describe "and String values" do + it "yields String values incremented by #succ and less than or equal to end when not passed a step" do + ("A".."E").step { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "B", "C", "D", "E"] + end + + it "yields String values incremented by #succ called Integer step times" do + ("A".."G").step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "C", "E", "G"] + end + + it "raises a TypeError when passed a Float step" do + lambda { ("A".."G").step(2.0) { } }.should raise_error(TypeError) + end + + it "calls #succ on begin and each element returned by #succ" do + obj = mock("Range#step String start") + obj.should_receive(:<=>).exactly(3).times.and_return(-1, -1, -1, 0) + obj.should_receive(:succ).exactly(2).times.and_return(obj) + + (obj..obj).step { |x| ScratchPad << x } + ScratchPad.recorded.should == [obj, obj, obj] + end + end + end + + describe "with exclusive end" do + describe "and Integer values" do + it "yields Integer values incremented by 1 and less than end when not passed a step" do + (-2...2).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2, -1, 0, 1]) + end + + it "yields Integer values incremented by an Integer step" do + (-5...5).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5, -3, -1, 1, 3]) + end + + it "yields Float values incremented by a Float step" do + (-2...2).step(1.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -0.5, 1.0]) + end + end + + describe "and Float values" do + it "yields Float values incremented by 1 and less than end when not passed a step" do + (-2.0...2.0).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) + end + + it "yields Float values incremented by an Integer step" do + (-5.0...5.0).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) + end + + it "yields Float values incremented by a Float step" do + (-1.0...1.0).step(0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) + end + + it "returns Float values of 'step * n + begin < end'" do + (1.0...6.4).step(1.8) { |x| ScratchPad << x } + (1.0...55.6).step(18.2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 1.0, 19.2, 37.4]) + end + end + + describe "and Integer, Float values" do + it "yields Float values incremented by 1 and less than end when not passed a step" do + (-2...2.0).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) + end + + it "yields Float values incremented by an Integer step" do + (-5...5.0).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) + end + + it "yields an Float and then Float values incremented by a Float step" do + (-1...1.0).step(0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) + end + end + + describe "and Float, Integer values" do + it "yields Float values incremented by 1 and less than end when not passed a step" do + (-2.0...2).step { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-2.0, -1.0, 0.0, 1.0]) + end + + it "yields Float values incremented by an Integer step" do + (-5.0...5).step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-5.0, -3.0, -1.0, 1.0, 3.0]) + end + + it "yields Float values incremented by a Float step" do + (-1.0...1).step(0.5) { |x| ScratchPad << x } + ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5]) + end + end + + describe "and String values" do + it "yields String values incremented by #succ and less than or equal to end when not passed a step" do + ("A"..."E").step { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "B", "C", "D"] + end + + it "yields String values incremented by #succ called Integer step times" do + ("A"..."G").step(2) { |x| ScratchPad << x } + ScratchPad.recorded.should == ["A", "C", "E"] + end + + it "raises a TypeError when passed a Float step" do + lambda { ("A"..."G").step(2.0) { } }.should raise_error(TypeError) + end + end + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "raises a TypeError if step does not respond to #to_int" do + obj = mock("Range#step non-integer") + enum = (1..2).step(obj) + lambda { enum.size }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_int does not return an Integer" do + obj = mock("Range#step non-integer") + obj.should_receive(:to_int).and_return("1") + enum = (1..2).step(obj) + + lambda { enum.size }.should raise_error(TypeError) + end + + it "raises an ArgumentError if step is 0" do + enum = (-1..1).step(0) + lambda { enum.size }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if step is 0.0" do + enum = (-1..1).step(0.0) + lambda { enum.size }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if step is negative" do + enum = (-1..1).step(-2) + lambda { enum.size }.should raise_error(ArgumentError) + end + + it "returns the ceil of range size divided by the number of steps" do + (1..10).step(4).size.should == 3 + (1..10).step(3).size.should == 4 + (1..10).step(2).size.should == 5 + (1..10).step(1).size.should == 10 + (-5..5).step(2).size.should == 6 + (1...10).step(4).size.should == 3 + (1...10).step(3).size.should == 3 + (1...10).step(2).size.should == 5 + (1...10).step(1).size.should == 9 + (-5...5).step(2).size.should == 5 + end + + it "returns the correct number of steps when one of the arguments is a float" do + (-1..1.0).step(0.5).size.should == 5 + (-1.0...1.0).step(0.5).size.should == 4 + end + + it "returns the range size when there's no step_size" do + (-2..2).step.size.should == 5 + (-2.0..2.0).step.size.should == 5 + (-2..2.0).step.size.should == 5 + (-2.0..2).step.size.should == 5 + (1.0..6.4).step(1.8).size.should == 4 + (1.0..12.7).step(1.3).size.should == 10 + (-2...2).step.size.should == 4 + (-2.0...2.0).step.size.should == 4 + (-2...2.0).step.size.should == 4 + (-2.0...2).step.size.should == 4 + (1.0...6.4).step(1.8).size.should == 3 + (1.0...55.6).step(18.2).size.should == 3 + end + + it "returns nil with begin and end are String" do + ("A".."E").step(2).size.should == nil + ("A"..."E").step(2).size.should == nil + ("A".."E").step.size.should == nil + ("A"..."E").step.size.should == nil + end + + it "return nil and not raises a TypeError if the first element does not respond to #succ" do + obj = mock("Range#step non-comparable") + obj.should_receive(:<=>).with(obj).and_return(1) + enum = (obj..obj).step + lambda { enum.size }.should_not raise_error + enum.size.should == nil + end + end + end + end +end diff --git a/spec/rubyspec/core/range/to_a_spec.rb b/spec/rubyspec/core/range/to_a_spec.rb new file mode 100644 index 0000000000..d1b838295b --- /dev/null +++ b/spec/rubyspec/core/range/to_a_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#to_a" do + it "converts self to an array" do + (-5..5).to_a.should == [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] + ('A'..'D').to_a.should == ['A','B','C','D'] + ('A'...'D').to_a.should == ['A','B','C'] + (0xfffd...0xffff).to_a.should == [0xfffd,0xfffe] + lambda { (0.5..2.4).to_a }.should raise_error(TypeError) + end + + it "returns empty array for descending-ordered" do + (5..-5).to_a.should == [] + ('D'..'A').to_a.should == [] + ('D'...'A').to_a.should == [] + (0xffff...0xfffd).to_a.should == [] + end + + it "works with Ranges of Symbols" do + (:A..:z).to_a.size.should == 58 + end +end diff --git a/spec/rubyspec/core/range/to_s_spec.rb b/spec/rubyspec/core/range/to_s_spec.rb new file mode 100644 index 0000000000..b59849fc57 --- /dev/null +++ b/spec/rubyspec/core/range/to_s_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Range#to_s" do + it "provides a printable form of self" do + (0..21).to_s.should == "0..21" + (-8..0).to_s.should == "-8..0" + (-411..959).to_s.should == "-411..959" + ('A'..'Z').to_s.should == 'A..Z' + ('A'...'Z').to_s.should == 'A...Z' + (0xfff..0xfffff).to_s.should == "4095..1048575" + (0.5..2.4).to_s.should == "0.5..2.4" + end + + it "returns a tainted string if either end is tainted" do + (("a".taint)..."c").to_s.tainted?.should be_true + ("a"...("c".taint)).to_s.tainted?.should be_true + ("a"..."c").taint.to_s.tainted?.should be_true + end + + it "returns a untrusted string if either end is untrusted" do + (("a".untrust)..."c").to_s.untrusted?.should be_true + ("a"...("c".untrust)).to_s.untrusted?.should be_true + ("a"..."c").untrust.to_s.untrusted?.should be_true + end +end diff --git a/spec/rubyspec/core/rational/abs_spec.rb b/spec/rubyspec/core/rational/abs_spec.rb new file mode 100644 index 0000000000..b04cbd2020 --- /dev/null +++ b/spec/rubyspec/core/rational/abs_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/abs', __FILE__) + +describe "Rational#abs" do + it_behaves_like(:rational_abs, :abs) +end diff --git a/spec/rubyspec/core/rational/ceil_spec.rb b/spec/rubyspec/core/rational/ceil_spec.rb new file mode 100644 index 0000000000..e7aca07d33 --- /dev/null +++ b/spec/rubyspec/core/rational/ceil_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/ceil', __FILE__) + +describe "Rational#ceil" do + it_behaves_like(:rational_ceil, :ceil) +end diff --git a/spec/rubyspec/core/rational/coerce_spec.rb b/spec/rubyspec/core/rational/coerce_spec.rb new file mode 100644 index 0000000000..2cfe1db415 --- /dev/null +++ b/spec/rubyspec/core/rational/coerce_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/coerce', __FILE__) + +describe "Rational#coerce" do + it_behaves_like(:rational_coerce, :coerce) +end diff --git a/spec/rubyspec/core/rational/comparison_spec.rb b/spec/rubyspec/core/rational/comparison_spec.rb new file mode 100644 index 0000000000..47e3ab043d --- /dev/null +++ b/spec/rubyspec/core/rational/comparison_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../shared/rational/comparison', __FILE__) + +describe "Rational#<=> when passed a Rational object" do + it_behaves_like(:rational_cmp_rat, :<=>) +end + +describe "Rational#<=> when passed a Integer object" do + it_behaves_like(:rational_cmp_int, :<=>) +end + +describe "Rational#<=> when passed a Float object" do + it_behaves_like(:rational_cmp_float, :<=>) +end + +describe "Rational#<=> when passed an Object that responds to #coerce" do + it_behaves_like(:rational_cmp_coerce, :<=>) +end + +describe "Rational#<=> when passed a non-Numeric Object that doesn't respond to #coerce" do + it_behaves_like(:rational_cmp_other, :<=>) +end diff --git a/spec/rubyspec/core/rational/denominator_spec.rb b/spec/rubyspec/core/rational/denominator_spec.rb new file mode 100644 index 0000000000..ac2795ded1 --- /dev/null +++ b/spec/rubyspec/core/rational/denominator_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/denominator', __FILE__) + +describe "Rational#denominator" do + it_behaves_like(:rational_denominator, :denominator) +end diff --git a/spec/rubyspec/core/rational/div_spec.rb b/spec/rubyspec/core/rational/div_spec.rb new file mode 100644 index 0000000000..1fd304b646 --- /dev/null +++ b/spec/rubyspec/core/rational/div_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../shared/rational/div', __FILE__) + +describe "Rational#div" do + it_behaves_like(:rational_div, :div) +end + +describe "Rational#div passed a Rational" do + it_behaves_like(:rational_div_rat, :div) +end + +describe "Rational#div passed an Integer" do + it_behaves_like(:rational_div_int, :div) +end + +describe "Rational#div passed a Float" do + it_behaves_like(:rational_div_float, :div) +end diff --git a/spec/rubyspec/core/rational/divide_spec.rb b/spec/rubyspec/core/rational/divide_spec.rb new file mode 100644 index 0000000000..75e447878c --- /dev/null +++ b/spec/rubyspec/core/rational/divide_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../shared/rational/divide', __FILE__) + +describe "Rational#/" do + it_behaves_like(:rational_divide, :/) +end + +describe "Rational#/ when passed an Integer" do + it_behaves_like(:rational_divide_int, :/) +end + +describe "Rational#/ when passed a Rational" do + it_behaves_like(:rational_divide_rat, :/) +end + +describe "Rational#/ when passed a Float" do + it_behaves_like(:rational_divide_float, :/) +end diff --git a/spec/rubyspec/core/rational/divmod_spec.rb b/spec/rubyspec/core/rational/divmod_spec.rb new file mode 100644 index 0000000000..ee03c9498a --- /dev/null +++ b/spec/rubyspec/core/rational/divmod_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../shared/rational/divmod', __FILE__) + +describe "Rational#divmod when passed a Rational" do + it_behaves_like(:rational_divmod_rat, :divmod) +end + +describe "Rational#divmod when passed an Integer" do + it_behaves_like(:rational_divmod_int, :divmod) +end + +describe "Rational#divmod when passed a Float" do + it_behaves_like(:rational_divmod_float, :divmod) +end diff --git a/spec/rubyspec/core/rational/equal_value_spec.rb b/spec/rubyspec/core/rational/equal_value_spec.rb new file mode 100644 index 0000000000..708eccf6d1 --- /dev/null +++ b/spec/rubyspec/core/rational/equal_value_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../shared/rational/equal_value', __FILE__) + +describe "Rational#==" do + it_behaves_like(:rational_equal_value, :==) +end + +describe "Rational#== when passed a Rational" do + it_behaves_like(:rational_equal_value_rat, :==) +end + +describe "Rational#== when passed a Float" do + it_behaves_like(:rational_equal_value_float, :==) +end + +describe "Rational#== when passed an Integer" do + it_behaves_like(:rational_equal_value_int, :==) +end diff --git a/spec/rubyspec/core/rational/exponent_spec.rb b/spec/rubyspec/core/rational/exponent_spec.rb new file mode 100644 index 0000000000..e4810d1b41 --- /dev/null +++ b/spec/rubyspec/core/rational/exponent_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/exponent', __FILE__) + +describe "Rational#**" do + it_behaves_like(:rational_exponent, :**) +end diff --git a/spec/rubyspec/core/rational/fdiv_spec.rb b/spec/rubyspec/core/rational/fdiv_spec.rb new file mode 100644 index 0000000000..15b4ed0619 --- /dev/null +++ b/spec/rubyspec/core/rational/fdiv_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/fdiv', __FILE__) + +describe "Rational#fdiv" do + it_behaves_like(:rational_fdiv, :fdiv) +end diff --git a/spec/rubyspec/core/rational/floor_spec.rb b/spec/rubyspec/core/rational/floor_spec.rb new file mode 100644 index 0000000000..485abcf58f --- /dev/null +++ b/spec/rubyspec/core/rational/floor_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/floor', __FILE__) + +describe "Rational#floor" do + it_behaves_like(:rational_floor, :floor) +end diff --git a/spec/rubyspec/core/rational/hash_spec.rb b/spec/rubyspec/core/rational/hash_spec.rb new file mode 100644 index 0000000000..a17572d57e --- /dev/null +++ b/spec/rubyspec/core/rational/hash_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/hash', __FILE__) + +describe "Rational#hash" do + it_behaves_like(:rational_hash, :hash) +end diff --git a/spec/rubyspec/core/rational/inspect_spec.rb b/spec/rubyspec/core/rational/inspect_spec.rb new file mode 100644 index 0000000000..452cf9c5ae --- /dev/null +++ b/spec/rubyspec/core/rational/inspect_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/inspect', __FILE__) + +describe "Rational#inspect" do + it_behaves_like(:rational_inspect, :inspect) +end diff --git a/spec/rubyspec/core/rational/integer_spec.rb b/spec/rubyspec/core/rational/integer_spec.rb new file mode 100644 index 0000000000..451e42ee14 --- /dev/null +++ b/spec/rubyspec/core/rational/integer_spec.rb @@ -0,0 +1,9 @@ +describe "Rational#integer?" do + it "returns false for a rational with a numerator and no denominator" do + Rational(20).integer?.should be_false + end + + it "returns false for a rational with a numerator and a denominator" do + Rational(20,3).integer?.should be_false + end +end diff --git a/spec/rubyspec/core/rational/magnitude_spec.rb b/spec/rubyspec/core/rational/magnitude_spec.rb new file mode 100644 index 0000000000..288b9a3fa3 --- /dev/null +++ b/spec/rubyspec/core/rational/magnitude_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/abs', __FILE__) + +describe "Rational#abs" do + it_behaves_like(:rational_abs, :magnitude) +end diff --git a/spec/rubyspec/core/rational/marshal_dump_spec.rb b/spec/rubyspec/core/rational/marshal_dump_spec.rb new file mode 100644 index 0000000000..46e9267ebd --- /dev/null +++ b/spec/rubyspec/core/rational/marshal_dump_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Rational#marshal_dump" do + it "is a private method" do + Rational.should have_private_instance_method(:marshal_dump, false) + end + + it "dumps numerator and denominator" do + Rational(1, 2).send(:marshal_dump).should == [1, 2] + end +end diff --git a/spec/rubyspec/core/rational/minus_spec.rb b/spec/rubyspec/core/rational/minus_spec.rb new file mode 100644 index 0000000000..9e28cb1c4f --- /dev/null +++ b/spec/rubyspec/core/rational/minus_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/minus', __FILE__) + +describe "Rational#-" do + it_behaves_like(:rational_minus, :-) +end diff --git a/spec/rubyspec/core/rational/modulo_spec.rb b/spec/rubyspec/core/rational/modulo_spec.rb new file mode 100644 index 0000000000..81e7c13797 --- /dev/null +++ b/spec/rubyspec/core/rational/modulo_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/modulo', __FILE__) + +describe "Rational#%" do + it_behaves_like(:rational_modulo, :%) +end diff --git a/spec/rubyspec/core/rational/multiply_spec.rb b/spec/rubyspec/core/rational/multiply_spec.rb new file mode 100644 index 0000000000..c45491fde3 --- /dev/null +++ b/spec/rubyspec/core/rational/multiply_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../shared/rational/multiply', __FILE__) + +describe "Rational#*" do + it_behaves_like(:rational_multiply, :*) +end + +describe "Rational#* passed a Rational" do + it_behaves_like(:rational_multiply_rat, :*) +end + +describe "Rational#* passed a Float" do + it_behaves_like(:rational_multiply_float, :*) +end + +describe "Rational#* passed an Integer" do + it_behaves_like(:rational_multiply_int, :*) +end diff --git a/spec/rubyspec/core/rational/numerator_spec.rb b/spec/rubyspec/core/rational/numerator_spec.rb new file mode 100644 index 0000000000..ac6591291d --- /dev/null +++ b/spec/rubyspec/core/rational/numerator_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/numerator', __FILE__) + +describe "Rational#numerator" do + it_behaves_like(:rational_numerator, :numerator) +end diff --git a/spec/rubyspec/core/rational/plus_spec.rb b/spec/rubyspec/core/rational/plus_spec.rb new file mode 100644 index 0000000000..b82c44bbad --- /dev/null +++ b/spec/rubyspec/core/rational/plus_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../shared/rational/plus', __FILE__) + +describe "Rational#+" do + it_behaves_like(:rational_plus, :+) +end + +describe "Rational#+ with a Rational" do + it_behaves_like(:rational_plus_rat, :+) +end +describe "Rational#+ with a Float" do + it_behaves_like(:rational_plus_float, :+) +end + +describe "Rational#+ with an Integer" do + it_behaves_like(:rational_plus_int, :+) +end diff --git a/spec/rubyspec/core/rational/quo_spec.rb b/spec/rubyspec/core/rational/quo_spec.rb new file mode 100644 index 0000000000..f4d04d5f6f --- /dev/null +++ b/spec/rubyspec/core/rational/quo_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/divide', __FILE__) + +describe "Rational#quo" do + it_behaves_like(:rational_divide, :quo) +end diff --git a/spec/rubyspec/core/rational/rational_spec.rb b/spec/rubyspec/core/rational/rational_spec.rb new file mode 100644 index 0000000000..fc3108c11b --- /dev/null +++ b/spec/rubyspec/core/rational/rational_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Rational" do + it "includes Comparable" do + Rational.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/rational/rationalize_spec.rb b/spec/rubyspec/core/rational/rationalize_spec.rb new file mode 100644 index 0000000000..053e245cff --- /dev/null +++ b/spec/rubyspec/core/rational/rationalize_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Rational#rationalize" do + it "returns self with no argument" do + Rational(12,3).rationalize.should == Rational(12,3) + Rational(-45,7).rationalize.should == Rational(-45,7) + end + + # FIXME: These specs need reviewing by somebody familiar with the + # algorithm used by #rationalize + it "simplifies self to the degree specified by a Rational argument" do + r = Rational(5404319552844595,18014398509481984) + r.rationalize(Rational(1,10)).should == Rational(1,3) + r.rationalize(Rational(-1,10)).should == Rational(1,3) + + r = Rational(-5404319552844595,18014398509481984) + r.rationalize(Rational(1,10)).should == Rational(-1,3) + r.rationalize(Rational(-1,10)).should == Rational(-1,3) + + end + + it "simplifies self to the degree specified by a Float argument" do + r = Rational(5404319552844595,18014398509481984) + r.rationalize(0.05).should == Rational(1,3) + r.rationalize(0.001).should == Rational(3, 10) + + r = Rational(-5404319552844595,18014398509481984) + r.rationalize(0.05).should == Rational(-1,3) + r.rationalize(0.001).should == Rational(-3,10) + end + + it "raises ArgumentError when passed more than one argument" do + lambda { Rational(1,1).rationalize(0.1, 0.1) }.should raise_error(ArgumentError) + lambda { Rational(1,1).rationalize(0.1, 0.1, 2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/rational/remainder_spec.rb b/spec/rubyspec/core/rational/remainder_spec.rb new file mode 100644 index 0000000000..1de276548f --- /dev/null +++ b/spec/rubyspec/core/rational/remainder_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/remainder', __FILE__) + +describe "Rational#remainder" do + it_behaves_like(:rational_remainder, :remainder) +end diff --git a/spec/rubyspec/core/rational/round_spec.rb b/spec/rubyspec/core/rational/round_spec.rb new file mode 100644 index 0000000000..2b432f3234 --- /dev/null +++ b/spec/rubyspec/core/rational/round_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/round', __FILE__) + +describe "Rational#round" do + it_behaves_like(:rational_round, :round) +end diff --git a/spec/rubyspec/core/rational/to_f_spec.rb b/spec/rubyspec/core/rational/to_f_spec.rb new file mode 100644 index 0000000000..92ff2444b4 --- /dev/null +++ b/spec/rubyspec/core/rational/to_f_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/to_f', __FILE__) + +describe "Rational#to_f" do + it_behaves_like(:rational_to_f, :to_f) +end diff --git a/spec/rubyspec/core/rational/to_i_spec.rb b/spec/rubyspec/core/rational/to_i_spec.rb new file mode 100644 index 0000000000..fda6b06e96 --- /dev/null +++ b/spec/rubyspec/core/rational/to_i_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/to_i', __FILE__) + +describe "Rational#to_i" do + it_behaves_like(:rational_to_i, :to_i) +end diff --git a/spec/rubyspec/core/rational/to_r_spec.rb b/spec/rubyspec/core/rational/to_r_spec.rb new file mode 100644 index 0000000000..22c8b4532c --- /dev/null +++ b/spec/rubyspec/core/rational/to_r_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../shared/rational/to_r', __FILE__) + +describe "Rational#to_r" do + it_behaves_like(:rational_to_r, :to_r) + + it "raises TypeError trying to convert BasicObject" do + obj = BasicObject.new + lambda { Rational(obj) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/rational/to_s_spec.rb b/spec/rubyspec/core/rational/to_s_spec.rb new file mode 100644 index 0000000000..7d1702b8fa --- /dev/null +++ b/spec/rubyspec/core/rational/to_s_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/to_s', __FILE__) + +describe "Rational#to_s" do + it_behaves_like(:rational_to_s, :to_s) +end diff --git a/spec/rubyspec/core/rational/truncate_spec.rb b/spec/rubyspec/core/rational/truncate_spec.rb new file mode 100644 index 0000000000..000db73357 --- /dev/null +++ b/spec/rubyspec/core/rational/truncate_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../shared/rational/truncate', __FILE__) + +describe "Rational#truncate" do + it_behaves_like(:rational_truncate, :truncate) +end diff --git a/spec/rubyspec/core/rational/zero_spec.rb b/spec/rubyspec/core/rational/zero_spec.rb new file mode 100644 index 0000000000..e6dd751922 --- /dev/null +++ b/spec/rubyspec/core/rational/zero_spec.rb @@ -0,0 +1,13 @@ +describe "Rational#zero?" do + it "returns true if the numerator is 0" do + Rational(0,26).zero?.should be_true + end + + it "returns true if the numerator is 0.0" do + Rational(0.0,26).zero?.should be_true + end + + it "returns false if the numerator isn't 0" do + Rational(26).zero?.should be_false + end +end diff --git a/spec/rubyspec/core/regexp/case_compare_spec.rb b/spec/rubyspec/core/regexp/case_compare_spec.rb new file mode 100644 index 0000000000..84ac957d12 --- /dev/null +++ b/spec/rubyspec/core/regexp/case_compare_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#===" do + it "is true if there is a match" do + (/abc/ === "aabcc").should be_true + end + + it "is false if there is no match" do + (/abc/ === "xyz").should be_false + end + + it "returns true if it matches a Symbol" do + (/a/ === :a).should be_true + end + + it "returns false if it does not match a Symbol" do + (/a/ === :b).should be_false + end + + # mirroring https://github.com/ruby/ruby/blob/trunk/test/ruby/test_regexp.rb + it "returns false if the other value cannot be coerced to a string" do + (/abc/ === nil).should be_false + (/abc/ === /abc/).should be_false + end +end diff --git a/spec/rubyspec/core/regexp/casefold_spec.rb b/spec/rubyspec/core/regexp/casefold_spec.rb new file mode 100644 index 0000000000..55efcaa765 --- /dev/null +++ b/spec/rubyspec/core/regexp/casefold_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#casefold?" do + it "returns the value of the case-insensitive flag" do + /abc/i.casefold?.should == true + /xyz/.casefold?.should == false + end +end diff --git a/spec/rubyspec/core/regexp/compile_spec.rb b/spec/rubyspec/core/regexp/compile_spec.rb new file mode 100644 index 0000000000..530816e07c --- /dev/null +++ b/spec/rubyspec/core/regexp/compile_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/new_ascii', __FILE__) +require File.expand_path('../shared/new_ascii_8bit', __FILE__) + +describe "Regexp.compile" do + it_behaves_like :regexp_new_ascii, :compile + it_behaves_like :regexp_new_ascii_8bit, :compile +end + +describe "Regexp.compile given a String" do + it_behaves_like :regexp_new_string_ascii, :compile + it_behaves_like :regexp_new_string_ascii_8bit, :compile +end + +describe "Regexp.compile given a Regexp" do + it_behaves_like :regexp_new_regexp_ascii, :compile + it_behaves_like :regexp_new_regexp_ascii_8bit, :compile +end diff --git a/spec/rubyspec/core/regexp/encoding_spec.rb b/spec/rubyspec/core/regexp/encoding_spec.rb new file mode 100644 index 0000000000..6ce1ada337 --- /dev/null +++ b/spec/rubyspec/core/regexp/encoding_spec.rb @@ -0,0 +1,58 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#encoding" do + it "returns an Encoding object" do + /glar/.encoding.should be_an_instance_of(Encoding) + end + + it "defaults to US-ASCII if the Regexp contains only US-ASCII character" do + /ASCII/.encoding.should == Encoding::US_ASCII + end + + it "returns US_ASCII if the 'n' modifier is supplied and only US-ASCII characters are present" do + /ASCII/n.encoding.should == Encoding::US_ASCII + end + + it "returns ASCII-8BIT if the 'n' modifier is supplied and non-US-ASCII characters are present" do + /#{}\xc2\xa1/n.encoding.should == Encoding::ASCII_8BIT + end + + it "defaults to UTF-8 if \\u escapes appear" do + /\u{9879}/.encoding.should == Encoding::UTF_8 + end + + it "defaults to UTF-8 if a literal UTF-8 character appears" do + /¥/.encoding.should == Encoding::UTF_8 + end + + it "returns UTF-8 if the 'u' modifier is supplied" do + /ASCII/u.encoding.should == Encoding::UTF_8 + end + + it "returns Windows-31J if the 's' modifier is supplied" do + /ASCII/s.encoding.should == Encoding::Windows_31J + end + + it "returns EUC_JP if the 'e' modifier is supplied" do + /ASCII/e.encoding.should == Encoding::EUC_JP + end + + it "upgrades the encoding to that of an embedded String" do + str = "文字化け".encode('euc-jp') + /#{str}/.encoding.should == Encoding::EUC_JP + end + + it "ignores the encoding and uses US-ASCII if the string has only ASCII characters" do + str = "abc".encode('euc-jp') + str.encoding.should == Encoding::EUC_JP + /#{str}/.encoding.should == Encoding::US_ASCII + end + + it "ignores the default_internal encoding" do + old_internal = Encoding.default_internal + Encoding.default_internal = Encoding::EUC_JP + /foo/.encoding.should_not == Encoding::EUC_JP + Encoding.default_internal = old_internal + end +end diff --git a/spec/rubyspec/core/regexp/eql_spec.rb b/spec/rubyspec/core/regexp/eql_spec.rb new file mode 100644 index 0000000000..7288ff45fe --- /dev/null +++ b/spec/rubyspec/core/regexp/eql_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Regexp#eql?" do + it_behaves_like :regexp_eql, :eql? +end diff --git a/spec/rubyspec/core/regexp/equal_value_spec.rb b/spec/rubyspec/core/regexp/equal_value_spec.rb new file mode 100644 index 0000000000..4ddaec8e4e --- /dev/null +++ b/spec/rubyspec/core/regexp/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Regexp#==" do + it_behaves_like :regexp_eql, :== +end diff --git a/spec/rubyspec/core/regexp/escape_spec.rb b/spec/rubyspec/core/regexp/escape_spec.rb new file mode 100644 index 0000000000..56b6cc5059 --- /dev/null +++ b/spec/rubyspec/core/regexp/escape_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quote', __FILE__) + +describe "Regexp.escape" do + it_behaves_like :regexp_quote, :escape +end diff --git a/spec/rubyspec/core/regexp/fixed_encoding_spec.rb b/spec/rubyspec/core/regexp/fixed_encoding_spec.rb new file mode 100644 index 0000000000..8a11de9575 --- /dev/null +++ b/spec/rubyspec/core/regexp/fixed_encoding_spec.rb @@ -0,0 +1,36 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#fixed_encoding?" do + it "returns false by default" do + /needle/.fixed_encoding?.should be_false + end + + it "returns false if the 'n' modifier was supplied to the Regexp" do + /needle/n.fixed_encoding?.should be_false + end + + it "returns true if the 'u' modifier was supplied to the Regexp" do + /needle/u.fixed_encoding?.should be_true + end + + it "returns true if the 's' modifier was supplied to the Regexp" do + /needle/s.fixed_encoding?.should be_true + end + + it "returns true if the 'e' modifier was supplied to the Regexp" do + /needle/e.fixed_encoding?.should be_true + end + + it "returns true if the Regexp contains a \\u escape" do + /needle \u{8768}/.fixed_encoding?.should be_true + end + + it "returns true if the Regexp contains a UTF-8 literal" do + /文字化け/.fixed_encoding?.should be_true + end + + it "returns true if the Regexp was created with the Regexp::FIXEDENCODING option" do + Regexp.new("", Regexp::FIXEDENCODING).fixed_encoding?.should be_true + end +end diff --git a/spec/rubyspec/core/regexp/hash_spec.rb b/spec/rubyspec/core/regexp/hash_spec.rb new file mode 100644 index 0000000000..77b79b1ce5 --- /dev/null +++ b/spec/rubyspec/core/regexp/hash_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#hash" do + it "is provided" do + Regexp.new('').respond_to?(:hash).should == true + end + + it "is based on the text and options of Regexp" do + (/cat/.hash == /dog/.hash).should == false + (/dog/m.hash == /dog/m.hash).should == true + not_supported_on :opal do + (/cat/ix.hash == /cat/ixn.hash).should == true + (/cat/.hash == /cat/ix.hash).should == false + end + end + + it "returns the same value for two Regexps differing only in the /n option" do + (//.hash == //n.hash).should == true + end +end diff --git a/spec/rubyspec/core/regexp/initialize_spec.rb b/spec/rubyspec/core/regexp/initialize_spec.rb new file mode 100644 index 0000000000..3c32f97a31 --- /dev/null +++ b/spec/rubyspec/core/regexp/initialize_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#initialize" do + it "is a private method" do + Regexp.should have_private_method(:initialize) + end + + it "raises a SecurityError on a Regexp literal" do + lambda { //.send(:initialize, "") }.should raise_error(SecurityError) + end + + it "raises a TypeError on an initialized non-literal Regexp" do + lambda { Regexp.new("").send(:initialize, "") }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/regexp/inspect_spec.rb b/spec/rubyspec/core/regexp/inspect_spec.rb new file mode 100644 index 0000000000..a2855cae5b --- /dev/null +++ b/spec/rubyspec/core/regexp/inspect_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#inspect" do + it "returns a formatted string that would eval to the same regexp" do + not_supported_on :opal do + /ab+c/ix.inspect.should == "/ab+c/ix" + /a(.)+s/n.inspect.should =~ %r|/a(.)+s/n?| # Default 'n' may not appear + end + # 1.9 doesn't round-trip the encoding flags, such as 'u'. This is + # seemingly by design. + /a(.)+s/m.inspect.should == "/a(.)+s/m" # But a specified one does + end + + it "returns options in the order 'mixn'" do + //nixm.inspect.should == "//mixn" + end + + it "does not include the 'o' option" do + //o.inspect.should == "//" + end + + it "does not include a character set code" do + //u.inspect.should == "//" + //s.inspect.should == "//" + //e.inspect.should == "//" + end + + it "correctly escapes forward slashes /" do + Regexp.new("/foo/bar").inspect.should == "/\\/foo\\/bar/" + Regexp.new("/foo/bar[/]").inspect.should == "/\\/foo\\/bar[\\/]/" + end + + it "doesn't over escape forward slashes" do + /\/foo\/bar/.inspect.should == '/\/foo\/bar/' + end + + it "escapes 2 slashes in a row properly" do + Regexp.new("//").inspect.should == '/\/\//' + end + + it "does not over escape" do + Regexp.new('\\\/').inspect.should == "/\\\\\\//" + end +end diff --git a/spec/rubyspec/core/regexp/last_match_spec.rb b/spec/rubyspec/core/regexp/last_match_spec.rb new file mode 100644 index 0000000000..4673554f91 --- /dev/null +++ b/spec/rubyspec/core/regexp/last_match_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp.last_match" do + it "returns MatchData instance when not passed arguments" do + /c(.)t/ =~ 'cat' + + Regexp.last_match.should be_kind_of(MatchData) + end + + it "returns the nth field in this MatchData when passed a Fixnum" do + /c(.)t/ =~ 'cat' + Regexp.last_match(1).should == 'a' + end +end diff --git a/spec/rubyspec/core/regexp/match_spec.rb b/spec/rubyspec/core/regexp/match_spec.rb new file mode 100644 index 0000000000..872c3ff59e --- /dev/null +++ b/spec/rubyspec/core/regexp/match_spec.rb @@ -0,0 +1,148 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe :regexp_match, shared: true do + it "returns nil if there is no match" do + /xyz/.send(@method,"abxyc").should be_nil + end + + it "returns nil if the object is nil" do + /\w+/.send(@method, nil).should be_nil + end +end + +describe "Regexp#=~" do + it_behaves_like(:regexp_match, :=~) + + it "returns the index of the first character of the matching region" do + (/(.)(.)(.)/ =~ "abc").should == 0 + end + + it "returns the index too, when argument is a Symbol" do + (/(.)(.)(.)/ =~ :abc).should == 0 + end +end + +describe "Regexp#match" do + it_behaves_like(:regexp_match, :match) + + it "returns a MatchData object" do + /(.)(.)(.)/.match("abc").should be_kind_of(MatchData) + end + + it "returns a MatchData object, when argument is a Symbol" do + /(.)(.)(.)/.match(:abc).should be_kind_of(MatchData) + end + + it "raises a TypeError on an uninitialized Regexp" do + lambda { Regexp.allocate.match('foo') }.should raise_error(TypeError) + end + + describe "with [string, position]" do + describe "when given a positive position" do + it "matches the input at a given position" do + /(.).(.)/.match("01234", 1).captures.should == ["1", "3"] + end + + with_feature :encoding do + it "uses the start as a character offset" do + /(.).(.)/.match("零一二三四", 1).captures.should == ["一", "三"] + end + + it "raises an ArgumentError for an invalid encoding" do + x96 = ([150].pack('C')).force_encoding('utf-8') + lambda { /(.).(.)/.match("Hello, #{x96} world!", 1) }.should raise_error(ArgumentError) + end + end + end + + describe "when given a negative position" do + it "matches the input at a given position" do + /(.).(.)/.match("01234", -4).captures.should == ["1", "3"] + end + + with_feature :encoding do + it "uses the start as a character offset" do + /(.).(.)/.match("零一二三四", -4).captures.should == ["一", "三"] + end + + it "raises an ArgumentError for an invalid encoding" do + x96 = ([150].pack('C')).force_encoding('utf-8') + lambda { /(.).(.)/.match("Hello, #{x96} world!", -1) }.should raise_error(ArgumentError) + end + end + end + + describe "when passed a block" do + it "yields the MatchData" do + /./.match("abc") {|m| ScratchPad.record m } + ScratchPad.recorded.should be_kind_of(MatchData) + end + + it "returns the block result" do + /./.match("abc") { :result }.should == :result + end + + it "does not yield if there is no match" do + ScratchPad.record [] + /a/.match("b") {|m| ScratchPad << m } + ScratchPad.recorded.should == [] + end + end + end + + it "resets $~ if passed nil" do + # set $~ + /./.match("a") + $~.should be_kind_of(MatchData) + + /1/.match(nil) + $~.should be_nil + end + + it "raises TypeError when the given argument cannot be coarce to String" do + f = 1 + lambda { /foo/.match(f)[0] }.should raise_error(TypeError) + end + + it "raises TypeError when the given argument is an Exception" do + f = Exception.new("foo") + lambda { /foo/.match(f)[0] }.should raise_error(TypeError) + end +end + +ruby_version_is "2.4" do + describe "Regexp#match?" do + before :each do + # Resetting Regexp.last_match + /DONTMATCH/.match '' + end + + context "when matches the given value" do + it "returns true but does not set Regexp.last_match" do + /string/i.match?('string').should be_true + Regexp.last_match.should be_nil + end + end + + it "returns false when does not match the given value" do + /STRING/.match?('string').should be_false + end + + it "takes matching position as the 2nd argument" do + /str/i.match?('string', 0).should be_true + /str/i.match?('string', 1).should be_false + end + + it "returns false when given nil" do + /./.match?(nil).should be_false + end + end +end + +describe "Regexp#~" do + it "matches against the contents of $_" do + $_ = "input data" + (~ /at/).should == 7 + end +end diff --git a/spec/rubyspec/core/regexp/named_captures_spec.rb b/spec/rubyspec/core/regexp/named_captures_spec.rb new file mode 100644 index 0000000000..c495d00a35 --- /dev/null +++ b/spec/rubyspec/core/regexp/named_captures_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#named_captures" do + it "returns a Hash" do + /foo/.named_captures.should be_an_instance_of(Hash) + end + + it "returns an empty Hash when there are no capture groups" do + /foo/.named_captures.should == {} + end + + it "sets the keys of the Hash to the names of the capture groups" do + rex = /this (?is) [aA] (?pate?rn)/ + rex.named_captures.keys.should == ['is','pat'] + end + + it "sets the values of the Hash to Arrays" do + rex = /this (?is) [aA] (?pate?rn)/ + rex.named_captures.values.each do |value| + value.should be_an_instance_of(Array) + end + end + + it "sets each element of the Array to the corresponding group's index" do + rex = /this (?is) [aA] (?pate?rn)/ + rex.named_captures['is'].should == [1] + rex.named_captures['pat'].should == [2] + end + + it "works with duplicate capture group names" do + rex = /this (?is) [aA] (?pate?(?rn))/ + rex.named_captures['is'].should == [1,3] + rex.named_captures['pat'].should == [2] + end +end diff --git a/spec/rubyspec/core/regexp/names_spec.rb b/spec/rubyspec/core/regexp/names_spec.rb new file mode 100644 index 0000000000..bbd994d993 --- /dev/null +++ b/spec/rubyspec/core/regexp/names_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#names" do + it "returns an Array" do + /foo/.names.should be_an_instance_of(Array) + end + + it "returns an empty Array if there are no named captures" do + /needle/.names.should == [] + end + + it "returns each named capture as a String" do + /n(?ee)d(?le)/.names.each do |name| + name.should be_an_instance_of(String) + end + end + + it "returns all of the named captures" do + /n(?ee)d(?le)/.names.should == ['cap', 'ture'] + end + + it "works with nested named captures" do + /n(?eed(?le))/.names.should == ['cap', 'ture'] + end + + it "returns each capture name only once" do + /n(?ee)d(?le)/.names.should == ['cap'] + end +end diff --git a/spec/rubyspec/core/regexp/new_spec.rb b/spec/rubyspec/core/regexp/new_spec.rb new file mode 100644 index 0000000000..c9581e661f --- /dev/null +++ b/spec/rubyspec/core/regexp/new_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/new_ascii', __FILE__) +require File.expand_path('../shared/new_ascii_8bit', __FILE__) + +describe "Regexp.new" do + it_behaves_like :regexp_new_ascii, :new + it_behaves_like :regexp_new_ascii_8bit, :new +end + +describe "Regexp.new given a String" do + it_behaves_like :regexp_new_string_ascii, :new + it_behaves_like :regexp_new_string_ascii_8bit, :new +end + +describe "Regexp.new given a Regexp" do + it_behaves_like :regexp_new_regexp_ascii, :new + it_behaves_like :regexp_new_regexp_ascii_8bit, :new +end + +describe "Regexp.new given a Fixnum" do + it "raises a TypeError" do + lambda { Regexp.new(1) }.should raise_error(TypeError) + end +end + +describe "Regexp.new given a Float" do + it "raises a TypeError" do + lambda { Regexp.new(1.0) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/regexp/options_spec.rb b/spec/rubyspec/core/regexp/options_spec.rb new file mode 100644 index 0000000000..10aeeac919 --- /dev/null +++ b/spec/rubyspec/core/regexp/options_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#options" do + it "returns a Fixnum bitvector of regexp options for the Regexp object" do + /cat/.options.should be_kind_of(Fixnum) + not_supported_on :opal do + /cat/ix.options.should be_kind_of(Fixnum) + end + end + + it "allows checking for presence of a certain option with bitwise &" do + (/cat/.options & Regexp::IGNORECASE).should == 0 + (/cat/i.options & Regexp::IGNORECASE).should_not == 0 + (/cat/.options & Regexp::MULTILINE).should == 0 + (/cat/m.options & Regexp::MULTILINE).should_not == 0 + not_supported_on :opal do + (/cat/.options & Regexp::EXTENDED).should == 0 + (/cat/x.options & Regexp::EXTENDED).should_not == 0 + (/cat/mx.options & Regexp::MULTILINE).should_not == 0 + (/cat/mx.options & Regexp::EXTENDED).should_not == 0 + (/cat/xi.options & Regexp::IGNORECASE).should_not == 0 + (/cat/xi.options & Regexp::EXTENDED).should_not == 0 + end + end + + it "returns 0 for a Regexp literal without options" do + //.options.should == 0 + /abc/.options.should == 0 + end + + it "raises a TypeError on an uninitialized Regexp" do + lambda { Regexp.allocate.options }.should raise_error(TypeError) + end + + it "includes Regexp::FIXEDENCODING for a Regexp literal with the 'u' option" do + (//u.options & Regexp::FIXEDENCODING).should_not == 0 + end + + it "includes Regexp::FIXEDENCODING for a Regexp literal with the 'e' option" do + (//e.options & Regexp::FIXEDENCODING).should_not == 0 + end + + it "includes Regexp::FIXEDENCODING for a Regexp literal with the 's' option" do + (//s.options & Regexp::FIXEDENCODING).should_not == 0 + end + + it "does not include Regexp::FIXEDENCODING for a Regexp literal with the 'n' option" do + (//n.options & Regexp::FIXEDENCODING).should == 0 + end + + it "includes Regexp::NOENCODING for a Regexp literal with the 'n' option" do + (//n.options & Regexp::NOENCODING).should_not == 0 + end +end diff --git a/spec/rubyspec/core/regexp/quote_spec.rb b/spec/rubyspec/core/regexp/quote_spec.rb new file mode 100644 index 0000000000..a38903ef15 --- /dev/null +++ b/spec/rubyspec/core/regexp/quote_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quote', __FILE__) + +describe "Regexp.quote" do + it_behaves_like :regexp_quote, :quote +end diff --git a/spec/rubyspec/core/regexp/shared/equal_value.rb b/spec/rubyspec/core/regexp/shared/equal_value.rb new file mode 100644 index 0000000000..803988de9e --- /dev/null +++ b/spec/rubyspec/core/regexp/shared/equal_value.rb @@ -0,0 +1,31 @@ +describe :regexp_eql, shared: true do + it "is true if self and other have the same pattern" do + /abc/.send(@method, /abc/).should == true + /abc/.send(@method, /abd/).should == false + end + + not_supported_on :opal do + it "is true if self and other have the same character set code" do + /abc/.send(@method, /abc/x).should == false + /abc/x.send(@method, /abc/x).should == true + /abc/u.send(@method, /abc/n).should == false + /abc/u.send(@method, /abc/u).should == true + /abc/n.send(@method, /abc/n).should == true + end + end + + it "is true if other has the same #casefold? values" do + /abc/.send(@method, /abc/i).should == false + /abc/i.send(@method, /abc/i).should == true + end + + not_supported_on :opal do + it "is true if self does not specify /n option and other does" do + //.send(@method, //n).should == true + end + + it "is true if self specifies /n option and other does not" do + //n.send(@method, //).should == true + end + end +end diff --git a/spec/rubyspec/core/regexp/shared/new_ascii.rb b/spec/rubyspec/core/regexp/shared/new_ascii.rb new file mode 100644 index 0000000000..98c458312e --- /dev/null +++ b/spec/rubyspec/core/regexp/shared/new_ascii.rb @@ -0,0 +1,464 @@ +# -*- encoding: binary -*- +describe :regexp_new_ascii, shared: true do + it "requires one argument and creates a new regular expression object" do + Regexp.send(@method, '').is_a?(Regexp).should == true + end + + it "works by default for subclasses with overridden #initialize" do + class RegexpSpecsSubclass < Regexp + def initialize(*args) + super + @args = args + end + + attr_accessor :args + end + + class RegexpSpecsSubclassTwo < Regexp; end + + RegexpSpecsSubclass.send(@method, "hi").should be_kind_of(RegexpSpecsSubclass) + RegexpSpecsSubclass.send(@method, "hi").args.first.should == "hi" + + RegexpSpecsSubclassTwo.send(@method, "hi").should be_kind_of(RegexpSpecsSubclassTwo) + end +end + +describe :regexp_new_string_ascii, shared: true do + it "uses the String argument as an unescaped literal to construct a Regexp object" do + Regexp.send(@method, "^hi{2,3}fo.o$").should == /^hi{2,3}fo.o$/ + end + + it "raises a RegexpError when passed an incorrect regexp" do + lambda { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError) + end + + it "does not set Regexp options if only given one argument" do + r = Regexp.send(@method, 'Hi') + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "does not set Regexp options if second argument is nil or false" do + r = Regexp.send(@method, 'Hi', nil) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + + r = Regexp.send(@method, 'Hi', false) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "sets options from second argument if it is one of the Fixnum option constants" do + r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE) + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + + r = Regexp.send(@method, 'Hi', Regexp::MULTILINE) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should_not == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + + not_supported_on :opal do + r = Regexp.send(@method, 'Hi', Regexp::EXTENDED) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + (r.options & Regexp::EXTENDED).should_not == 1 + end + end + + it "accepts a Fixnum of two or more options ORed together as the second argument" do + r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE | Regexp::EXTENDED) + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should == 0 + (r.options & Regexp::EXTENDED).should_not == 0 + end + + it "treats any non-Fixnum, non-nil, non-false second argument as IGNORECASE" do + r = Regexp.send(@method, 'Hi', Object.new) + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do + lambda { + Regexp.send(@method, 'Hi', nil, 'e').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'euc').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'E').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'EUC').encoding.should == Encoding::US_ASCII + }.should complain(/encoding option is ignored/) + end + + it "ignores the third argument if it is 's' or 'sjis' (case-insensitive)" do + lambda { + Regexp.send(@method, 'Hi', nil, 's').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'sjis').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'S').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'SJIS').encoding.should == Encoding::US_ASCII + }.should complain(/encoding option is ignored/) + end + + it "ignores the third argument if it is 'u' or 'utf8' (case-insensitive)" do + lambda { + Regexp.send(@method, 'Hi', nil, 'u').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'utf8').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'U').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'UTF8').encoding.should == Encoding::US_ASCII + }.should complain(/encoding option is ignored/) + end + + it "uses US_ASCII encoding if third argument is 'n' or 'none' (case insensitive) and only ascii characters" do + Regexp.send(@method, 'Hi', nil, 'n').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'none').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'N').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'NONE').encoding.should == Encoding::US_ASCII + end + + it "uses ASCII_8BIT encoding if third argument is 'n' or 'none' (case insensitive) and non-ascii characters" do + a = "(?:[\x8E\xA1-\xFE])" + str = "\A(?:#{a}|x*)\z" + + Regexp.send(@method, str, nil, 'N').encoding.should == Encoding::ASCII_8BIT + Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::ASCII_8BIT + Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::ASCII_8BIT + Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::ASCII_8BIT + end + + describe "with escaped characters" do + it "raises a Regexp error if there is a trailing backslash" do + lambda { Regexp.send(@method, "\\") }.should raise_error(RegexpError) + end + + it "does not raise a Regexp error if there is an escaped trailing backslash" do + lambda { Regexp.send(@method, "\\\\") }.should_not raise_error(RegexpError) + end + + it "accepts a backspace followed by a character" do + Regexp.send(@method, "\\N").should == /#{"\x5c"+"N"}/ + end + + it "accepts a one-digit octal value" do + Regexp.send(@method, "\0").should == /#{"\x00"}/ + end + + it "accepts a two-digit octal value" do + Regexp.send(@method, "\11").should == /#{"\x09"}/ + end + + it "accepts a one-digit hexadecimal value" do + Regexp.send(@method, "\x9n").should == /#{"\x09n"}/ + end + + it "accepts a two-digit hexadecimal value" do + Regexp.send(@method, "\x23").should == /#{"\x23"}/ + end + + it "interprets a digit following a two-digit hexadecimal value as a character" do + Regexp.send(@method, "\x420").should == /#{"\x420"}/ + end + + it "raises a RegexpError if \\x is not followed by any hexadecimal digits" do + lambda { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError) + end + + it "accepts an escaped string interpolation" do + Regexp.send(@method, "\#{abc}").should == /#{"\#{abc}"}/ + end + + it "accepts '\\n'" do + Regexp.send(@method, "\n").should == /#{"\x0a"}/ + end + + it "accepts '\\t'" do + Regexp.send(@method, "\t").should == /#{"\x09"}/ + end + + it "accepts '\\r'" do + Regexp.send(@method, "\r").should == /#{"\x0d"}/ + end + + it "accepts '\\f'" do + Regexp.send(@method, "\f").should == /#{"\x0c"}/ + end + + it "accepts '\\v'" do + Regexp.send(@method, "\v").should == /#{"\x0b"}/ + end + + it "accepts '\\a'" do + Regexp.send(@method, "\a").should == /#{"\x07"}/ + end + + it "accepts '\\e'" do + Regexp.send(@method, "\e").should == /#{"\x1b"}/ + end + + it "accepts '\\C-\\n'" do + Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/ + end + + it "accepts '\\C-\\t'" do + Regexp.send(@method, "\C-\t").should == /#{"\x09"}/ + end + + it "accepts '\\C-\\r'" do + Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/ + end + + it "accepts '\\C-\\f'" do + Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/ + end + + it "accepts '\\C-\\v'" do + Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/ + end + + it "accepts '\\C-\\a'" do + Regexp.send(@method, "\C-\a").should == /#{"\x07"}/ + end + + it "accepts '\\C-\\e'" do + Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/ + end + + it "accepts '\\c\\n'" do + Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/ + end + + it "accepts '\\c\\t'" do + Regexp.send(@method, "\C-\t").should == /#{"\x09"}/ + end + + it "accepts '\\c\\r'" do + Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/ + end + + it "accepts '\\c\\f'" do + Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/ + end + + it "accepts '\\c\\v'" do + Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/ + end + + it "accepts '\\c\\a'" do + Regexp.send(@method, "\C-\a").should == /#{"\x07"}/ + end + + it "accepts '\\c\\e'" do + Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/ + end + + it "accepts multiple consecutive '\\' characters" do + Regexp.send(@method, "\\\\\\N").should == /#{"\\\\\\"+"N"}/ + end + + it "accepts characters and escaped octal digits" do + Regexp.send(@method, "abc\076").should == /#{"abc\x3e"}/ + end + + it "accepts escaped octal digits and characters" do + Regexp.send(@method, "\076abc").should == /#{"\x3eabc"}/ + end + + it "accepts characters and escaped hexadecimal digits" do + Regexp.send(@method, "abc\x42").should == /#{"abc\x42"}/ + end + + it "accepts escaped hexadecimal digits and characters" do + Regexp.send(@method, "\x3eabc").should == /#{"\x3eabc"}/ + end + + it "accepts escaped hexadecimal and octal digits" do + Regexp.send(@method, "\061\x42").should == /#{"\x31\x42"}/ + end + + it "accepts \\u{H} for a single Unicode codepoint" do + Regexp.send(@method, "\u{f}").should == /#{"\x0f"}/ + end + + it "accepts \\u{HH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{7f}").should == /#{"\x7f"}/ + end + + it "accepts \\u{HHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{07f}").should == /#{"\x7f"}/ + end + + it "accepts \\u{HHHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{0000}").should == /#{"\x00"}/ + end + + it "accepts \\u{HHHHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{00001}").should == /#{"\x01"}/ + end + + it "accepts \\u{HHHHHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{000000}").should == /#{"\x00"}/ + end + + it "accepts characters followed by \\u{HHHH}" do + Regexp.send(@method, "abc\u{3042}").should == /#{"abc\u3042"}/ + end + + it "accepts \\u{HHHH} followed by characters" do + Regexp.send(@method, "\u{3042}abc").should == /#{"\u3042abc"}/ + end + + it "accepts escaped hexadecimal digits followed by \\u{HHHH}" do + Regexp.send(@method, "\x42\u{3042}").should == /#{"\x42\u3042"}/ + end + + it "accepts escaped octal digits followed by \\u{HHHH}" do + Regexp.send(@method, "\056\u{3042}").should == /#{"\x2e\u3042"}/ + end + + it "accepts a combination of escaped octal and hexadecimal digits and \\u{HHHH}" do + Regexp.send(@method, "\056\x42\u{3042}\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/ + end + + it "accepts \\uHHHH for a single Unicode codepoint" do + Regexp.send(@method, "\u3042").should == /#{"\u3042"}/ + end + + it "accepts characters followed by \\uHHHH" do + Regexp.send(@method, "abc\u3042").should == /#{"abc\u3042"}/ + end + + it "accepts \\uHHHH followed by characters" do + Regexp.send(@method, "\u3042abc").should == /#{"\u3042abc"}/ + end + + it "accepts escaped hexadecimal digits followed by \\uHHHH" do + Regexp.send(@method, "\x42\u3042").should == /#{"\x42\u3042"}/ + end + + it "accepts escaped octal digits followed by \\uHHHH" do + Regexp.send(@method, "\056\u3042").should == /#{"\x2e\u3042"}/ + end + + it "accepts a combination of escaped octal and hexadecimal digits and \\uHHHH" do + Regexp.send(@method, "\056\x42\u3042\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/ + end + + it "raises a RegexpError if less than four digits are given for \\uHHHH" do + lambda { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError) + end + + it "raises a RegexpError if the \\u{} escape is empty" do + lambda { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError) + end + + it "raises a RegexpError if more than six hexadecimal digits are given" do + lambda { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError) + end + + it "returns a Regexp with US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do + Regexp.send(@method, "abc").encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with source String having US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do + Regexp.send(@method, "abc").source.encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do + Regexp.send(@method, "\u{61}").encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with source String having US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do + Regexp.send(@method, "\u{61}").source.encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do + Regexp.send(@method, "\u{ff}").encoding.should == Encoding::UTF_8 + end + + it "returns a Regexp with source String having UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do + Regexp.send(@method, "\u{ff}").source.encoding.should == Encoding::UTF_8 + end + + it "returns a Regexp with the input String's encoding" do + str = "\x82\xa0".force_encoding(Encoding::Shift_JIS) + Regexp.send(@method, str).encoding.should == Encoding::Shift_JIS + end + + it "returns a Regexp with source String having the input String's encoding" do + str = "\x82\xa0".force_encoding(Encoding::Shift_JIS) + Regexp.send(@method, str).source.encoding.should == Encoding::Shift_JIS + end + end +end + +describe :regexp_new_regexp_ascii, shared: true do + it "uses the argument as a literal to construct a Regexp object" do + Regexp.send(@method, /^hi{2,3}fo.o$/).should == /^hi{2,3}fo.o$/ + end + + it "preserves any options given in the Regexp literal" do + (Regexp.send(@method, /Hi/i).options & Regexp::IGNORECASE).should_not == 0 + (Regexp.send(@method, /Hi/m).options & Regexp::MULTILINE).should_not == 0 + not_supported_on :opal do + (Regexp.send(@method, /Hi/x).options & Regexp::EXTENDED).should_not == 0 + end + + not_supported_on :opal do + r = Regexp.send @method, /Hi/imx + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should_not == 0 + (r.options & Regexp::EXTENDED).should_not == 0 + end + + r = Regexp.send @method, /Hi/ + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "does not honour options given as additional arguments" do + r = nil + lambda { + r = Regexp.send @method, /hi/, Regexp::IGNORECASE + }.should complain(/flags ignored/) + (r.options & Regexp::IGNORECASE).should == 0 + end + + not_supported_on :opal do + it "sets the encoding to UTF-8 if the Regexp literal has the 'u' option" do + Regexp.send(@method, /Hi/u).encoding.should == Encoding::UTF_8 + end + + it "sets the encoding to EUC-JP if the Regexp literal has the 'e' option" do + Regexp.send(@method, /Hi/e).encoding.should == Encoding::EUC_JP + end + + it "sets the encoding to Windows-31J if the Regexp literal has the 's' option" do + Regexp.send(@method, /Hi/s).encoding.should == Encoding::Windows_31J + end + + it "sets the encoding to US-ASCII if the Regexp literal has the 'n' option and the source String is ASCII only" do + Regexp.send(@method, /Hi/n).encoding.should == Encoding::US_ASCII + end + + it "sets the encoding to source String's encoding if the Regexp literal has the 'n' option and the source String is not ASCII only" do + Regexp.send(@method, Regexp.new("\\xff", nil, 'n')).encoding.should == Encoding::ASCII_8BIT + end + end +end diff --git a/spec/rubyspec/core/regexp/shared/new_ascii_8bit.rb b/spec/rubyspec/core/regexp/shared/new_ascii_8bit.rb new file mode 100644 index 0000000000..5110a08380 --- /dev/null +++ b/spec/rubyspec/core/regexp/shared/new_ascii_8bit.rb @@ -0,0 +1,553 @@ +# -*- encoding: ascii-8bit -*- + +describe :regexp_new_ascii_8bit, shared: true do + it "requires one argument and creates a new regular expression object" do + Regexp.send(@method, '').is_a?(Regexp).should == true + end + + it "works by default for subclasses with overridden #initialize" do + class RegexpSpecsSubclass < Regexp + def initialize(*args) + super + @args = args + end + + attr_accessor :args + end + + class RegexpSpecsSubclassTwo < Regexp; end + + RegexpSpecsSubclass.send(@method, "hi").should be_kind_of(RegexpSpecsSubclass) + RegexpSpecsSubclass.send(@method, "hi").args.first.should == "hi" + + RegexpSpecsSubclassTwo.send(@method, "hi").should be_kind_of(RegexpSpecsSubclassTwo) + end +end + +describe :regexp_new_string_ascii_8bit, shared: true do + it "uses the String argument as an unescaped literal to construct a Regexp object" do + Regexp.send(@method, "^hi{2,3}fo.o$").should == /^hi{2,3}fo.o$/ + end + + it "raises a RegexpError when passed an incorrect regexp" do + lambda { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError) + end + + it "does not set Regexp options if only given one argument" do + r = Regexp.send(@method, 'Hi') + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "does not set Regexp options if second argument is nil or false" do + r = Regexp.send(@method, 'Hi', nil) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + + r = Regexp.send(@method, 'Hi', false) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "sets options from second argument if it is one of the Fixnum option constants" do + r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE) + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + + r = Regexp.send(@method, 'Hi', Regexp::MULTILINE) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should_not == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + + not_supported_on :opal do + r = Regexp.send(@method, 'Hi', Regexp::EXTENDED) + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + (r.options & Regexp::EXTENDED).should_not == 1 + end + end + + it "accepts a Fixnum of two or more options ORed together as the second argument" do + r = Regexp.send(@method, 'Hi', Regexp::IGNORECASE | Regexp::EXTENDED) + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should == 0 + (r.options & Regexp::EXTENDED).should_not == 0 + end + + it "treats any non-Fixnum, non-nil, non-false second argument as IGNORECASE" do + r = Regexp.send(@method, 'Hi', Object.new) + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "ignores the third argument if it is 'e' or 'euc' (case-insensitive)" do + lambda { + Regexp.send(@method, 'Hi', nil, 'e').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'euc').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'E').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'EUC').encoding.should == Encoding::US_ASCII + }.should complain(/encoding option is ignored/) + end + + it "ignores the third argument if it is 's' or 'sjis' (case-insensitive)" do + lambda { + Regexp.send(@method, 'Hi', nil, 's').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'sjis').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'S').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'SJIS').encoding.should == Encoding::US_ASCII + }.should complain(/encoding option is ignored/) + end + + it "ignores the third argument if it is 'u' or 'utf8' (case-insensitive)" do + lambda { + Regexp.send(@method, 'Hi', nil, 'u').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'utf8').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'U').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'UTF8').encoding.should == Encoding::US_ASCII + }.should complain(/encoding option is ignored/) + end + + it "uses US_ASCII encoding if third argument is 'n' or 'none' (case insensitive) and only ascii characters" do + Regexp.send(@method, 'Hi', nil, 'n').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'none').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'N').encoding.should == Encoding::US_ASCII + Regexp.send(@method, 'Hi', nil, 'NONE').encoding.should == Encoding::US_ASCII + end + + it "uses ASCII_8BIT encoding if third argument is 'n' or 'none' (case insensitive) and non-ascii characters" do + a = "(?:[\x8E\xA1-\xFE])" + str = "\A(?:#{a}|x*)\z" + + Regexp.send(@method, str, nil, 'N').encoding.should == Encoding::ASCII_8BIT + Regexp.send(@method, str, nil, 'n').encoding.should == Encoding::ASCII_8BIT + Regexp.send(@method, str, nil, 'none').encoding.should == Encoding::ASCII_8BIT + Regexp.send(@method, str, nil, 'NONE').encoding.should == Encoding::ASCII_8BIT + end + + describe "with escaped characters" do + it "raises a Regexp error if there is a trailing backslash" do + lambda { Regexp.send(@method, "\\") }.should raise_error(RegexpError) + end + + it "accepts a backspace followed by a character" do + Regexp.send(@method, "\\N").should == /#{"\x5c"+"N"}/ + end + + it "accepts a one-digit octal value" do + Regexp.send(@method, "\0").should == /#{"\x00"}/ + end + + it "accepts a two-digit octal value" do + Regexp.send(@method, "\11").should == /#{"\x09"}/ + end + + it "accepts a three-digit octal value" do + Regexp.send(@method, "\315").should == /#{"\xcd"}/ + end + + it "interprets a digit following a three-digit octal value as a character" do + Regexp.send(@method, "\3762").should == /#{"\xfe2"}/ + end + + it "accepts a one-digit hexadecimal value" do + Regexp.send(@method, "\x9n").should == /#{"\x09n"}/ + end + + it "accepts a two-digit hexadecimal value" do + Regexp.send(@method, "\x23").should == /#{"\x23"}/ + end + + it "interprets a digit following a two-digit hexadecimal value as a character" do + Regexp.send(@method, "\x420").should == /#{"\x420"}/ + end + + it "raises a RegexpError if \\x is not followed by any hexadecimal digits" do + lambda { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError) + end + + it "accepts an escaped string interpolation" do + Regexp.send(@method, "\#{abc}").should == /#{"\#{abc}"}/ + end + + it "accepts '\\n'" do + Regexp.send(@method, "\n").should == /#{"\x0a"}/ + end + + it "accepts '\\t'" do + Regexp.send(@method, "\t").should == /#{"\x09"}/ + end + + it "accepts '\\r'" do + Regexp.send(@method, "\r").should == /#{"\x0d"}/ + end + + it "accepts '\\f'" do + Regexp.send(@method, "\f").should == /#{"\x0c"}/ + end + + it "accepts '\\v'" do + Regexp.send(@method, "\v").should == /#{"\x0b"}/ + end + + it "accepts '\\a'" do + Regexp.send(@method, "\a").should == /#{"\x07"}/ + end + + it "accepts '\\e'" do + Regexp.send(@method, "\e").should == /#{"\x1b"}/ + end + + it "accepts '\\C-\\n'" do + Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/ + end + + it "accepts '\\C-\\t'" do + Regexp.send(@method, "\C-\t").should == /#{"\x09"}/ + end + + it "accepts '\\C-\\r'" do + Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/ + end + + it "accepts '\\C-\\f'" do + Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/ + end + + it "accepts '\\C-\\v'" do + Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/ + end + + it "accepts '\\C-\\a'" do + Regexp.send(@method, "\C-\a").should == /#{"\x07"}/ + end + + it "accepts '\\C-\\e'" do + Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/ + end + + it "accepts '\\c\\n'" do + Regexp.send(@method, "\C-\n").should == /#{"\x0a"}/ + end + + it "accepts '\\c\\t'" do + Regexp.send(@method, "\C-\t").should == /#{"\x09"}/ + end + + it "accepts '\\c\\r'" do + Regexp.send(@method, "\C-\r").should == /#{"\x0d"}/ + end + + it "accepts '\\c\\f'" do + Regexp.send(@method, "\C-\f").should == /#{"\x0c"}/ + end + + it "accepts '\\c\\v'" do + Regexp.send(@method, "\C-\v").should == /#{"\x0b"}/ + end + + it "accepts '\\c\\a'" do + Regexp.send(@method, "\C-\a").should == /#{"\x07"}/ + end + + it "accepts '\\c\\e'" do + Regexp.send(@method, "\C-\e").should == /#{"\x1b"}/ + end + + it "accepts '\\M-\\n'" do + Regexp.send(@method, "\M-\n").should == /#{"\x8a"}/ + end + + it "accepts '\\M-\\t'" do + Regexp.send(@method, "\M-\t").should == /#{"\x89"}/ + end + + it "accepts '\\M-\\r'" do + Regexp.send(@method, "\M-\r").should == /#{"\x8d"}/ + end + + it "accepts '\\M-\\f'" do + Regexp.send(@method, "\M-\f").should == /#{"\x8c"}/ + end + + it "accepts '\\M-\\v'" do + Regexp.send(@method, "\M-\v").should == /#{"\x8b"}/ + end + + it "accepts '\\M-\\a'" do + Regexp.send(@method, "\M-\a").should == /#{"\x87"}/ + end + + it "accepts '\\M-\\e'" do + Regexp.send(@method, "\M-\e").should == /#{"\x9b"}/ + end + + it "accepts '\\M-\\C-\\n'" do + Regexp.send(@method, "\M-\n").should == /#{"\x8a"}/ + end + + it "accepts '\\M-\\C-\\t'" do + Regexp.send(@method, "\M-\t").should == /#{"\x89"}/ + end + + it "accepts '\\M-\\C-\\r'" do + Regexp.send(@method, "\M-\r").should == /#{"\x8d"}/ + end + + it "accepts '\\M-\\C-\\f'" do + Regexp.send(@method, "\M-\f").should == /#{"\x8c"}/ + end + + it "accepts '\\M-\\C-\\v'" do + Regexp.send(@method, "\M-\v").should == /#{"\x8b"}/ + end + + it "accepts '\\M-\\C-\\a'" do + Regexp.send(@method, "\M-\a").should == /#{"\x87"}/ + end + + it "accepts '\\M-\\C-\\e'" do + Regexp.send(@method, "\M-\e").should == /#{"\x9b"}/ + end + + it "accepts '\\M-\\c\\n'" do + Regexp.send(@method, "\M-\n").should == /#{"\x8a"}/ + end + + it "accepts '\\M-\\c\\t'" do + Regexp.send(@method, "\M-\t").should == /#{"\x89"}/ + end + + it "accepts '\\M-\\c\\r'" do + Regexp.send(@method, "\M-\r").should == /#{"\x8d"}/ + end + + it "accepts '\\M-\\c\\f'" do + Regexp.send(@method, "\M-\f").should == /#{"\x8c"}/ + end + + it "accepts '\\M-\\c\\v'" do + Regexp.send(@method, "\M-\v").should == /#{"\x8b"}/ + end + + it "accepts '\\M-\\c\\a'" do + Regexp.send(@method, "\M-\a").should == /#{"\x87"}/ + end + + it "accepts '\\M-\\c\\e'" do + Regexp.send(@method, "\M-\e").should == /#{"\x9b"}/ + end + + it "accepts multiple consecutive '\\' characters" do + Regexp.send(@method, "\\\\\\N").should == /#{"\\\\\\"+"N"}/ + end + + it "accepts characters and escaped octal digits" do + Regexp.send(@method, "abc\076").should == /#{"abc\x3e"}/ + end + + it "accepts escaped octal digits and characters" do + Regexp.send(@method, "\076abc").should == /#{"\x3eabc"}/ + end + + it "accepts characters and escaped hexadecimal digits" do + Regexp.send(@method, "abc\x42").should == /#{"abc\x42"}/ + end + + it "accepts escaped hexadecimal digits and characters" do + Regexp.send(@method, "\x3eabc").should == /#{"\x3eabc"}/ + end + + it "accepts escaped hexadecimal and octal digits" do + Regexp.send(@method, "\061\x42").should == /#{"\x31\x42"}/ + end + + it "accepts \\u{H} for a single Unicode codepoint" do + Regexp.send(@method, "\u{f}").should == /#{"\x0f"}/ + end + + it "accepts \\u{HH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{7f}").should == /#{"\x7f"}/ + end + + it "accepts \\u{HHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{07f}").should == /#{"\x7f"}/ + end + + it "accepts \\u{HHHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{0000}").should == /#{"\x00"}/ + end + + it "accepts \\u{HHHHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{00001}").should == /#{"\x01"}/ + end + + it "accepts \\u{HHHHHH} for a single Unicode codepoint" do + Regexp.send(@method, "\u{000000}").should == /#{"\x00"}/ + end + + it "accepts characters followed by \\u{HHHH}" do + Regexp.send(@method, "abc\u{3042}").should == /#{"abc\u3042"}/ + end + + it "accepts \\u{HHHH} followed by characters" do + Regexp.send(@method, "\u{3042}abc").should == /#{"\u3042abc"}/ + end + + it "accepts escaped hexadecimal digits followed by \\u{HHHH}" do + Regexp.send(@method, "\x42\u{3042}").should == /#{"\x42\u3042"}/ + end + + it "accepts escaped octal digits followed by \\u{HHHH}" do + Regexp.send(@method, "\056\u{3042}").should == /#{"\x2e\u3042"}/ + end + + it "accepts a combination of escaped octal and hexadecimal digits and \\u{HHHH}" do + Regexp.send(@method, "\056\x42\u{3042}\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/ + end + + it "accepts \\uHHHH for a single Unicode codepoint" do + Regexp.send(@method, "\u3042").should == /#{"\u3042"}/ + end + + it "accepts characters followed by \\uHHHH" do + Regexp.send(@method, "abc\u3042").should == /#{"abc\u3042"}/ + end + + it "accepts \\uHHHH followed by characters" do + Regexp.send(@method, "\u3042abc").should == /#{"\u3042abc"}/ + end + + it "accepts escaped hexadecimal digits followed by \\uHHHH" do + Regexp.send(@method, "\x42\u3042").should == /#{"\x42\u3042"}/ + end + + it "accepts escaped octal digits followed by \\uHHHH" do + Regexp.send(@method, "\056\u3042").should == /#{"\x2e\u3042"}/ + end + + it "accepts a combination of escaped octal and hexadecimal digits and \\uHHHH" do + Regexp.send(@method, "\056\x42\u3042\x52\076").should == /#{"\x2e\x42\u3042\x52\x3e"}/ + end + + it "raises a RegexpError if less than four digits are given for \\uHHHH" do + lambda { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError) + end + + it "raises a RegexpError if the \\u{} escape is empty" do + lambda { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError) + end + + it "raises a RegexpError if more than six hexadecimal digits are given" do + lambda { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError) + end + + it "returns a Regexp with US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do + Regexp.send(@method, "abc").encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with source String having US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do + Regexp.send(@method, "abc").source.encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do + Regexp.send(@method, "\u{61}").encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with source String having US-ASCII encoding if UTF-8 escape sequences using only 7-bit ASCII are present" do + Regexp.send(@method, "\u{61}").source.encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do + Regexp.send(@method, "\u{ff}").encoding.should == Encoding::UTF_8 + end + + it "returns a Regexp with source String having UTF-8 encoding if any UTF-8 escape sequences outside 7-bit ASCII are present" do + Regexp.send(@method, "\u{ff}").source.encoding.should == Encoding::UTF_8 + end + + it "returns a Regexp with the input String's encoding" do + str = "\x82\xa0".force_encoding(Encoding::Shift_JIS) + Regexp.send(@method, str).encoding.should == Encoding::Shift_JIS + end + + it "returns a Regexp with source String having the input String's encoding" do + str = "\x82\xa0".force_encoding(Encoding::Shift_JIS) + Regexp.send(@method, str).source.encoding.should == Encoding::Shift_JIS + end + end +end + +describe :regexp_new_regexp_ascii_8bit, shared: true do + it "uses the argument as a literal to construct a Regexp object" do + Regexp.send(@method, /^hi{2,3}fo.o$/).should == /^hi{2,3}fo.o$/ + end + + it "preserves any options given in the Regexp literal" do + (Regexp.send(@method, /Hi/i).options & Regexp::IGNORECASE).should_not == 0 + (Regexp.send(@method, /Hi/m).options & Regexp::MULTILINE).should_not == 0 + not_supported_on :opal do + (Regexp.send(@method, /Hi/x).options & Regexp::EXTENDED).should_not == 0 + end + + not_supported_on :opal do + r = Regexp.send @method, /Hi/imx + (r.options & Regexp::IGNORECASE).should_not == 0 + (r.options & Regexp::MULTILINE).should_not == 0 + (r.options & Regexp::EXTENDED).should_not == 0 + end + + r = Regexp.send @method, /Hi/ + (r.options & Regexp::IGNORECASE).should == 0 + (r.options & Regexp::MULTILINE).should == 0 + not_supported_on :opal do + (r.options & Regexp::EXTENDED).should == 0 + end + end + + it "does not honour options given as additional arguments" do + r = nil + lambda { + r = Regexp.send @method, /hi/, Regexp::IGNORECASE + }.should complain(/flags ignored/) + (r.options & Regexp::IGNORECASE).should == 0 + end + + not_supported_on :opal do + it "sets the encoding to UTF-8 if the Regexp literal has the 'u' option" do + Regexp.send(@method, /Hi/u).encoding.should == Encoding::UTF_8 + end + + it "sets the encoding to EUC-JP if the Regexp literal has the 'e' option" do + Regexp.send(@method, /Hi/e).encoding.should == Encoding::EUC_JP + end + + it "sets the encoding to Windows-31J if the Regexp literal has the 's' option" do + Regexp.send(@method, /Hi/s).encoding.should == Encoding::Windows_31J + end + + it "sets the encoding to US-ASCII if the Regexp literal has the 'n' option and the source String is ASCII only" do + Regexp.send(@method, /Hi/n).encoding.should == Encoding::US_ASCII + end + + it "sets the encoding to source String's encoding if the Regexp literal has the 'n' option and the source String is not ASCII only" do + Regexp.send(@method, Regexp.new("\\xff", nil, 'n')).encoding.should == Encoding::ASCII_8BIT + end + end +end diff --git a/spec/rubyspec/core/regexp/shared/quote.rb b/spec/rubyspec/core/regexp/shared/quote.rb new file mode 100644 index 0000000000..016cb0b164 --- /dev/null +++ b/spec/rubyspec/core/regexp/shared/quote.rb @@ -0,0 +1,31 @@ +# -*- encoding: ascii-8bit -*- + +describe :regexp_quote, shared: true do + it "escapes any characters with special meaning in a regular expression" do + Regexp.send(@method, '\*?{}.+^[]()- ').should == '\\\\\*\?\{\}\.\+\^\[\]\(\)\-\\ ' + Regexp.send(@method, "\*?{}.+^[]()- ").should == '\\*\\?\\{\\}\\.\\+\\^\\[\\]\\(\\)\\-\\ ' + Regexp.send(@method, '\n\r\f\t').should == '\\\\n\\\\r\\\\f\\\\t' + Regexp.send(@method, "\n\r\f\t").should == '\\n\\r\\f\\t' + end + + it "works with symbols" do + Regexp.send(@method, :symbol).should == 'symbol' + end + + it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do + str = "abc".force_encoding("euc-jp") + Regexp.send(@method, str).encoding.should == Encoding::US_ASCII + end + + it "sets the encoding of the result to the encoding of the String if any non-US-ASCII characters are present in an input String with valid encoding" do + str = "ありがとう".force_encoding("utf-8") + str.valid_encoding?.should be_true + Regexp.send(@method, str).encoding.should == Encoding::UTF_8 + end + + it "sets the encoding of the result to ASCII-8BIT if any non-US-ASCII characters are present in an input String with invalid encoding" do + str = "\xff".force_encoding "us-ascii" + str.valid_encoding?.should be_false + Regexp.send(@method, "\xff").encoding.should == Encoding::ASCII_8BIT + end +end diff --git a/spec/rubyspec/core/regexp/source_spec.rb b/spec/rubyspec/core/regexp/source_spec.rb new file mode 100644 index 0000000000..3960a09395 --- /dev/null +++ b/spec/rubyspec/core/regexp/source_spec.rb @@ -0,0 +1,29 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#source" do + it "returns the original string of the pattern" do + not_supported_on :opal do + /ab+c/ix.source.should == "ab+c" + end + /x(.)xz/.source.should == "x(.)xz" + end + + it "will remove escape characters" do + /foo\/bar/.source.should == "foo/bar" + end + + not_supported_on :opal do + it "has US-ASCII encoding when created from an ASCII-only \\u{} literal" do + re = /[\u{20}-\u{7E}]/ + re.source.encoding.should equal(Encoding::US_ASCII) + end + end + + not_supported_on :opal do + it "has UTF-8 encoding when created from a non-ASCII-only \\u{} literal" do + re = /[\u{20}-\u{7EE}]/ + re.source.encoding.should equal(Encoding::UTF_8) + end + end +end diff --git a/spec/rubyspec/core/regexp/to_s_spec.rb b/spec/rubyspec/core/regexp/to_s_spec.rb new file mode 100644 index 0000000000..a23fd78975 --- /dev/null +++ b/spec/rubyspec/core/regexp/to_s_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp#to_s" do + not_supported_on :opal do + it "displays options if included" do + /abc/mxi.to_s.should == "(?mix:abc)" + end + end + + it "shows non-included options after a - sign" do + /abc/i.to_s.should == "(?i-mx:abc)" + end + + it "shows all options as excluded if none are selected" do + /abc/.to_s.should == "(?-mix:abc)" + end + + it "shows the pattern after the options" do + not_supported_on :opal do + /ab+c/mix.to_s.should == "(?mix:ab+c)" + end + /xyz/.to_s.should == "(?-mix:xyz)" + end + + not_supported_on :opal do + it "displays groups with options" do + /(?ix:foo)(?m:bar)/.to_s.should == "(?-mix:(?ix:foo)(?m:bar))" + /(?ix:foo)bar/m.to_s.should == "(?m-ix:(?ix:foo)bar)" + end + + it "displays single group with same options as main regex as the main regex" do + /(?i:nothing outside this group)/.to_s.should == "(?i-mx:nothing outside this group)" + end + end + + not_supported_on :opal do + it "deals properly with uncaptured groups" do + /whatever(?:0d)/ix.to_s.should == "(?ix-m:whatever(?:0d))" + end + end + + it "deals properly with the two types of lookahead groups" do + /(?=5)/.to_s.should == "(?-mix:(?=5))" + /(?!5)/.to_s.should == "(?-mix:(?!5))" + end + + it "returns a string in (?xxx:yyy) notation" do + not_supported_on :opal do + /ab+c/ix.to_s.should == "(?ix-m:ab+c)" + /jis/s.to_s.should == "(?-mix:jis)" + /(?i:.)/.to_s.should == "(?i-mx:.)" + end + /(?:.)/.to_s.should == "(?-mix:.)" + end + + not_supported_on :opal do + it "handles abusive option groups" do + /(?mmmmix-miiiix:)/.to_s.should == '(?-mix:)' + end + end + +end diff --git a/spec/rubyspec/core/regexp/try_convert_spec.rb b/spec/rubyspec/core/regexp/try_convert_spec.rb new file mode 100644 index 0000000000..e782fc07fb --- /dev/null +++ b/spec/rubyspec/core/regexp/try_convert_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp.try_convert" do + not_supported_on :opal do + it "returns the argument if given a Regexp" do + Regexp.try_convert(/foo/s).should == /foo/s + end + end + + it "returns nil if given an argument that can't be converted to a Regexp" do + ['', 'glark', [], Object.new, :pat].each do |arg| + Regexp.try_convert(arg).should be_nil + end + end + + it "tries to coerce the argument by calling #to_regexp" do + rex = mock('regexp') + rex.should_receive(:to_regexp).and_return(/(p(a)t[e]rn)/) + Regexp.try_convert(rex).should == /(p(a)t[e]rn)/ + end +end diff --git a/spec/rubyspec/core/regexp/union_spec.rb b/spec/rubyspec/core/regexp/union_spec.rb new file mode 100644 index 0000000000..0f62747753 --- /dev/null +++ b/spec/rubyspec/core/regexp/union_spec.rb @@ -0,0 +1,149 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Regexp.union" do + it "returns /(?!)/ when passed no arguments" do + Regexp.union.should == /(?!)/ + end + + it "returns a regular expression that will match passed arguments" do + Regexp.union("penzance").should == /penzance/ + Regexp.union("skiing", "sledding").should == /skiing|sledding/ + not_supported_on :opal do + Regexp.union(/dogs/, /cats/i).should == /(?-mix:dogs)|(?i-mx:cats)/ + end + end + + it "quotes any string arguments" do + Regexp.union("n", ".").should == /n|\./ + end + + it "returns a Regexp with the encoding of an ASCII-incompatible String argument" do + Regexp.union("a".encode("UTF-16LE")).encoding.should == Encoding::UTF_16LE + end + + it "returns a Regexp with the encoding of a String containing non-ASCII-compatible characters" do + Regexp.union("\u00A9".encode("ISO-8859-1")).encoding.should == Encoding::ISO_8859_1 + end + + it "returns a Regexp with US-ASCII encoding if all arguments are ASCII-only" do + Regexp.union("a".encode("UTF-8"), "b".encode("SJIS")).encoding.should == Encoding::US_ASCII + end + + it "returns a Regexp with the encoding of multiple non-conflicting ASCII-incompatible String arguments" do + Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-16LE")).encoding.should == Encoding::UTF_16LE + end + + it "returns a Regexp with the encoding of multiple non-conflicting Strings containing non-ASCII-compatible characters" do + Regexp.union("\u00A9".encode("ISO-8859-1"), "\u00B0".encode("ISO-8859-1")).encoding.should == Encoding::ISO_8859_1 + end + + it "returns a Regexp with the encoding of a String containing non-ASCII-compatible characters and another ASCII-only String" do + Regexp.union("\u00A9".encode("ISO-8859-1"), "a".encode("UTF-8")).encoding.should == Encoding::ISO_8859_1 + end + + it "returns a Regexp with UTF-8 if one part is UTF-8" do + Regexp.union(/probl[éeè]me/i, /help/i).encoding.should == Encoding::UTF_8 + end + + it "returns a Regexp if an array of string with special characters is passed" do + Regexp.union(["+","-"]).should == /\+|\-/ + end + + it "raises ArgumentError if the arguments include conflicting ASCII-incompatible Strings" do + lambda { + Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-16BE")) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include conflicting ASCII-incompatible Regexps" do + lambda { + Regexp.union(Regexp.new("a".encode("UTF-16LE")), + Regexp.new("b".encode("UTF-16BE"))) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include conflicting fixed encoding Regexps" do + lambda { + Regexp.union(Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING), + Regexp.new("b".encode("US-ASCII"), Regexp::FIXEDENCODING)) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include a fixed encoding Regexp and a String containing non-ASCII-compatible characters in a different encoding" do + lambda { + Regexp.union(Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING), + "\u00A9".encode("ISO-8859-1")) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include a String containing non-ASCII-compatible characters and a fixed encoding Regexp in a different encoding" do + lambda { + Regexp.union("\u00A9".encode("ISO-8859-1"), + Regexp.new("a".encode("UTF-8"), Regexp::FIXEDENCODING)) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible String and an ASCII-only String" do + lambda { + Regexp.union("a".encode("UTF-16LE"), "b".encode("UTF-8")) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and an ASCII-only String" do + lambda { + Regexp.union(Regexp.new("a".encode("UTF-16LE")), "b".encode("UTF-8")) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible String and an ASCII-only Regexp" do + lambda { + Regexp.union("a".encode("UTF-16LE"), Regexp.new("b".encode("UTF-8"))) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and an ASCII-only Regexp" do + lambda { + Regexp.union(Regexp.new("a".encode("UTF-16LE")), Regexp.new("b".encode("UTF-8"))) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible String and a String containing non-ASCII-compatible characters in a different encoding" do + lambda { + Regexp.union("a".encode("UTF-16LE"), "\u00A9".encode("ISO-8859-1")) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and a String containing non-ASCII-compatible characters in a different encoding" do + lambda { + Regexp.union(Regexp.new("a".encode("UTF-16LE")), "\u00A9".encode("ISO-8859-1")) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible String and a Regexp containing non-ASCII-compatible characters in a different encoding" do + lambda { + Regexp.union("a".encode("UTF-16LE"), Regexp.new("\u00A9".encode("ISO-8859-1"))) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the arguments include an ASCII-incompatible Regexp and a Regexp containing non-ASCII-compatible characters in a different encoding" do + lambda { + Regexp.union(Regexp.new("a".encode("UTF-16LE")), Regexp.new("\u00A9".encode("ISO-8859-1"))) + }.should raise_error(ArgumentError) + end + + it "uses to_str to convert arguments (if not Regexp)" do + obj = mock('pattern') + obj.should_receive(:to_str).and_return('foo') + Regexp.union(obj, "bar").should == /foo|bar/ + end + + it "accepts a single array of patterns as arguments" do + Regexp.union(["skiing", "sledding"]).should == /skiing|sledding/ + not_supported_on :opal do + Regexp.union([/dogs/, /cats/i]).should == /(?-mix:dogs)|(?i-mx:cats)/ + end + lambda{Regexp.union(["skiing", "sledding"], [/dogs/, /cats/i])}.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/signal/list_spec.rb b/spec/rubyspec/core/signal/list_spec.rb new file mode 100644 index 0000000000..510b671337 --- /dev/null +++ b/spec/rubyspec/core/signal/list_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Signal.list" do + RUBY_SIGNALS = %w{ + EXIT + HUP + INT + QUIT + ILL + TRAP + IOT + ABRT + EMT + FPE + KILL + BUS + SEGV + SYS + PIPE + ALRM + TERM + URG + STOP + TSTP + CONT + CHLD + CLD + TTIN + TTOU + IO + XCPU + XFSZ + VTALRM + PROF + WINCH + USR1 + USR2 + LOST + MSG + PWR + POLL + DANGER + MIGRATE + PRE + GRANT + RETRACT + SOUND + INFO + } + + it "doesn't contain other signals than the known list" do + (Signal.list.keys - RUBY_SIGNALS).should == [] + end + + if Signal.list["CHLD"] + it "redefines CLD with CHLD if defined" do + Signal.list["CLD"].should == Signal.list["CHLD"] + end + end + + it "includes the EXIT key with a value of zero" do + Signal.list["EXIT"].should == 0 + end +end diff --git a/spec/rubyspec/core/signal/signame_spec.rb b/spec/rubyspec/core/signal/signame_spec.rb new file mode 100644 index 0000000000..1874a67933 --- /dev/null +++ b/spec/rubyspec/core/signal/signame_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Signal.signame" do + it "takes a signal name with a well known signal number" do + Signal.signame(0).should == "EXIT" + end + + ruby_version_is "2.0"..."2.3" do + it "raises an ArgumentError if the argument is an invalid signal number" do + lambda { Signal.signame(-1) }.should raise_error(ArgumentError) + end + end + + ruby_version_is "2.3" do + it "returns nil if the argument is an invalid signal number" do + Signal.signame(-1).should == nil + end + end + + it "raises a TypeError when the passed argument can't be coerced to Integer" do + lambda { Signal.signame("hello") }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/signal/trap_spec.rb b/spec/rubyspec/core/signal/trap_spec.rb new file mode 100644 index 0000000000..787de1735c --- /dev/null +++ b/spec/rubyspec/core/signal/trap_spec.rb @@ -0,0 +1,135 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +platform_is_not :windows do + describe "Signal.trap" do + before :each do + ScratchPad.clear + + @proc = lambda { ScratchPad.record :proc_trap } + @saved_trap = Signal.trap(:HUP, @proc) + end + + after :each do + Signal.trap(:HUP, @saved_trap) if @saved_trap + end + + it "returns the previous handler" do + Signal.trap(:HUP, @saved_trap).should equal(@proc) + end + + it "accepts a block in place of a proc/command argument" do + done = false + + Signal.trap(:HUP) do + ScratchPad.record :block_trap + done = true + end + + Process.kill :HUP, Process.pid + Thread.pass until done + + ScratchPad.recorded.should == :block_trap + end + + it "is possible to create a new Thread when the handler runs" do + done = false + + Signal.trap(:HUP) do + thr = Thread.new { } + thr.join + ScratchPad.record(thr.group == Thread.main.group) + + done = true + end + + Process.kill :HUP, Process.pid + Thread.pass until done + + ScratchPad.recorded.should be_true + end + + it "ignores the signal when passed nil" do + Signal.trap :HUP, nil + Signal.trap(:HUP, @saved_trap).should be_nil + end + + it "accepts 'DEFAULT' as a symbol in place of a proc" do + Signal.trap :HUP, :DEFAULT + Signal.trap(:HUP, :DEFAULT).should == "DEFAULT" + end + + it "accepts 'SIG_DFL' as a symbol in place of a proc" do + Signal.trap :HUP, :SIG_DFL + Signal.trap(:HUP, :SIG_DFL).should == "DEFAULT" + end + + it "accepts 'SIG_IGN' as a symbol in place of a proc" do + Signal.trap :HUP, :SIG_IGN + Signal.trap(:HUP, :SIG_IGN).should == "IGNORE" + end + + it "accepts 'IGNORE' as a symbol in place of a proc" do + Signal.trap :HUP, :IGNORE + Signal.trap(:HUP, :IGNORE).should == "IGNORE" + end + + it "accepts long names as Strings" do + Signal.trap "SIGHUP", @proc + Signal.trap("SIGHUP", @saved_trap).should equal(@proc) + end + + it "acceps short names as Strings" do + Signal.trap "HUP", @proc + Signal.trap("HUP", @saved_trap).should equal(@proc) + end + + it "accepts long names as Symbols" do + Signal.trap :SIGHUP, @proc + Signal.trap(:SIGHUP, @saved_trap).should equal(@proc) + end + + it "accepts short names as Symbols" do + Signal.trap :HUP, @proc + Signal.trap(:HUP, @saved_trap).should equal(@proc) + end + + it "accepts 'SIG_DFL' in place of a proc" do + Signal.trap :HUP, "SIG_DFL" + Signal.trap(:HUP, @saved_trap).should == "DEFAULT" + end + + it "accepts 'DEFAULT' in place of a proc" do + Signal.trap :HUP, "DEFAULT" + Signal.trap(:HUP, @saved_trap).should == "DEFAULT" + end + + it "accepts 'SIG_IGN' in place of a proc" do + Signal.trap :HUP, "SIG_IGN" + Signal.trap(:HUP, "SIG_IGN").should == "IGNORE" + end + + it "accepts 'IGNORE' in place of a proc" do + Signal.trap :HUP, "IGNORE" + Signal.trap(:HUP, "IGNORE").should == "IGNORE" + end + end +end + +describe "Signal.trap" do + describe "the special EXIT signal code" do + it "accepts the EXIT code" do + code = "trap(:EXIT, proc { print 1 })" + ruby_exe(code).should == "1" + end + + it "runs the proc before at_exit handlers" do + code = "at_exit {print 1}; trap(:EXIT, proc {print 2}); at_exit {print 3}" + ruby_exe(code).should == "231" + end + + it "can unset the handler" do + code = "trap(:EXIT, proc { print 1 }); trap(:EXIT, 'DEFAULT')" + ruby_exe(code).should == "" + end + end +end diff --git a/spec/rubyspec/core/string/allocate_spec.rb b/spec/rubyspec/core/string/allocate_spec.rb new file mode 100644 index 0000000000..9048815c5d --- /dev/null +++ b/spec/rubyspec/core/string/allocate_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String.allocate" do + it "returns an instance of String" do + str = String.allocate + str.should be_an_instance_of(String) + end + + it "returns a fully-formed String" do + str = String.allocate + str.size.should == 0 + str << "more" + str.should == "more" + end + + it "returns a binary String" do + String.new.encoding.should == Encoding::BINARY + end +end diff --git a/spec/rubyspec/core/string/append_spec.rb b/spec/rubyspec/core/string/append_spec.rb new file mode 100644 index 0000000000..87b2dca725 --- /dev/null +++ b/spec/rubyspec/core/string/append_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/concat', __FILE__) + +describe "String#<<" do + it_behaves_like :string_concat, :<< + it_behaves_like :string_concat_encoding, :<< +end diff --git a/spec/rubyspec/core/string/ascii_only_spec.rb b/spec/rubyspec/core/string/ascii_only_spec.rb new file mode 100644 index 0000000000..1bf9cfa4a1 --- /dev/null +++ b/spec/rubyspec/core/string/ascii_only_spec.rb @@ -0,0 +1,85 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +with_feature :encoding do + describe "String#ascii_only?" do + describe "with ASCII only characters" do + it "returns true if the encoding is UTF-8" do + [ ["hello", true], + ["hello".encode('UTF-8'), true], + ["hello".force_encoding('UTF-8'), true], + ].should be_computed_by(:ascii_only?) + end + + it "returns true if the encoding is US-ASCII" do + "hello".force_encoding(Encoding::US_ASCII).ascii_only?.should be_true + "hello".encode(Encoding::US_ASCII).ascii_only?.should be_true + end + + it "returns true for all single-character UTF-8 Strings" do + 0.upto(127) do |n| + n.chr.ascii_only?.should be_true + end + end + end + + describe "with non-ASCII only characters" do + it "returns false if the encoding is ASCII-8BIT" do + chr = 128.chr + chr.encoding.should == Encoding::ASCII_8BIT + chr.ascii_only?.should be_false + end + + it "returns false if the String contains any non-ASCII characters" do + [ ["\u{6666}", false], + ["hello, \u{6666}", false], + ["\u{6666}".encode('UTF-8'), false], + ["\u{6666}".force_encoding('UTF-8'), false], + ].should be_computed_by(:ascii_only?) + end + + it "returns false if the encoding is US-ASCII" do + [ ["\u{6666}".force_encoding(Encoding::US_ASCII), false], + ["hello, \u{6666}".force_encoding(Encoding::US_ASCII), false], + ].should be_computed_by(:ascii_only?) + end + end + + it "returns true for the empty String with an ASCII-compatible encoding" do + "".ascii_only?.should be_true + "".encode('UTF-8').ascii_only?.should be_true + end + + it "returns false for the empty String with a non-ASCII-compatible encoding" do + "".force_encoding('UTF-16LE').ascii_only?.should be_false + "".encode('UTF-16BE').ascii_only?.should be_false + end + + it "returns false for a non-empty String with non-ASCII-compatible encoding" do + "\x78\x00".force_encoding("UTF-16LE").ascii_only?.should be_false + end + + it "returns false when interpolating non ascii strings" do + base = "EU currency is" + base.force_encoding(Encoding::US_ASCII) + euro = "\u20AC" + interp = "#{base} #{euro}" + euro.ascii_only?.should be_false + base.ascii_only?.should be_true + interp.ascii_only?.should be_false + end + + it "returns false after appending non ASCII characters to an empty String" do + ("" << "λ").ascii_only?.should be_false + end + + it "returns false when concatenating an ASCII and non-ASCII String" do + "".concat("λ").ascii_only?.should be_false + end + + it "returns false when replacing an ASCII String with a non-ASCII String" do + "".replace("λ").ascii_only?.should be_false + end + end +end diff --git a/spec/rubyspec/core/string/b_spec.rb b/spec/rubyspec/core/string/b_spec.rb new file mode 100644 index 0000000000..89fcedf7c4 --- /dev/null +++ b/spec/rubyspec/core/string/b_spec.rb @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#b" do + with_feature :encoding do + it "returns an ASCII-8BIT encoded string" do + "Hello".b.should == "Hello".force_encoding(Encoding::ASCII_8BIT) + "こんちには".b.should == "こんちには".force_encoding(Encoding::ASCII_8BIT) + end + + it "returns new string without modifying self" do + str = "こんちには" + str.b.should_not equal(str) + str.should == "こんちには" + end + + it "copies own tainted/untrusted status to the returning value" do + utf_8 = "こんちには".taint.untrust + ret = utf_8.b + ret.tainted?.should be_true + ret.untrusted?.should be_true + end + end +end diff --git a/spec/rubyspec/core/string/bytes_spec.rb b/spec/rubyspec/core/string/bytes_spec.rb new file mode 100644 index 0000000000..b1f5ba412f --- /dev/null +++ b/spec/rubyspec/core/string/bytes_spec.rb @@ -0,0 +1,57 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#bytes" do + before :each do + @utf8 = "東京" + @ascii = 'Tokyo' + @utf8_ascii = @utf8 + @ascii + end + + it "returns an Array when no block is given" do + @utf8.bytes.should be_an_instance_of(Array) + end + + it "yields each byte to a block if one is given, returning self" do + bytes = [] + @utf8.bytes {|b| bytes << b}.should == @utf8 + bytes.should == @utf8.bytes.to_a + end + + it "returns #bytesize bytes" do + @utf8_ascii.bytes.to_a.size.should == @utf8_ascii.bytesize + end + + it "returns bytes as Fixnums" do + @ascii.bytes.to_a.each {|b| b.should be_an_instance_of(Fixnum)} + @utf8_ascii.bytes { |b| b.should be_an_instance_of(Fixnum) } + end + + it "agrees with #unpack('C*')" do + @utf8_ascii.bytes.to_a.should == @utf8_ascii.unpack("C*") + end + + it "yields/returns no bytes for the empty string" do + ''.bytes.to_a.should == [] + end +end + +with_feature :encoding do + describe "String#bytes" do + before :each do + @utf8 = "東京" + @ascii = 'Tokyo' + @utf8_ascii = @utf8 + @ascii + end + + it "agrees with #getbyte" do + @utf8_ascii.bytes.to_a.each_with_index do |byte,index| + byte.should == @utf8_ascii.getbyte(index) + end + end + + it "is unaffected by #force_encoding" do + @utf8.force_encoding('ASCII').bytes.to_a.should == @utf8.bytes.to_a + end + end +end diff --git a/spec/rubyspec/core/string/bytesize_spec.rb b/spec/rubyspec/core/string/bytesize_spec.rb new file mode 100644 index 0000000000..51da179da8 --- /dev/null +++ b/spec/rubyspec/core/string/bytesize_spec.rb @@ -0,0 +1,37 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +with_feature :encoding do + describe "#String#bytesize" do + it "needs to be reviewed for spec completeness" + + it "returns the length of self in bytes" do + "hello".bytesize.should == 5 + " ".bytesize.should == 1 + end + + it "works with strings containing single UTF-8 characters" do + "\u{6666}".bytesize.should == 3 + end + + it "works with pseudo-ASCII strings containing single UTF-8 characters" do + "\u{6666}".force_encoding('ASCII').bytesize.should == 3 + end + + it "works with strings containing UTF-8 characters" do + "c \u{6666}".force_encoding('UTF-8').bytesize.should == 5 + "c \u{6666}".bytesize.should == 5 + end + + it "works with pseudo-ASCII strings containing UTF-8 characters" do + "c \u{6666}".force_encoding('ASCII').bytesize.should == 5 + end + + it "returns 0 for the empty string" do + "".bytesize.should == 0 + "".force_encoding('ASCII').bytesize.should == 0 + "".force_encoding('UTF-8').bytesize.should == 0 + end + end +end diff --git a/spec/rubyspec/core/string/byteslice_spec.rb b/spec/rubyspec/core/string/byteslice_spec.rb new file mode 100644 index 0000000000..263810971e --- /dev/null +++ b/spec/rubyspec/core/string/byteslice_spec.rb @@ -0,0 +1,29 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/slice.rb', __FILE__) + +describe "String#byteslice" do + it "needs to reviewed for spec completeness" + + it_behaves_like :string_slice, :byteslice +end + +describe "String#byteslice with index, length" do + it_behaves_like :string_slice_index_length, :byteslice +end + +describe "String#byteslice with Range" do + it_behaves_like :string_slice_range, :byteslice +end + +with_feature :encoding do + describe "String#byteslice on on non ASCII strings" do + it "returns byteslice of unicode strings" do + "\u3042".byteslice(1).should == "\x81".force_encoding("UTF-8") + "\u3042".byteslice(1, 2).should == "\x81\x82".force_encoding("UTF-8") + "\u3042".byteslice(1..2).should == "\x81\x82".force_encoding("UTF-8") + "\u3042".byteslice(-1).should == "\x82".force_encoding("UTF-8") + end + end +end diff --git a/spec/rubyspec/core/string/capitalize_spec.rb b/spec/rubyspec/core/string/capitalize_spec.rb new file mode 100644 index 0000000000..b70509da99 --- /dev/null +++ b/spec/rubyspec/core/string/capitalize_spec.rb @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#capitalize" do + it "returns a copy of self with the first character converted to uppercase and the remainder to lowercase" do + "".capitalize.should == "" + "h".capitalize.should == "H" + "H".capitalize.should == "H" + "hello".capitalize.should == "Hello" + "HELLO".capitalize.should == "Hello" + "123ABC".capitalize.should == "123abc" + end + + it "taints resulting string when self is tainted" do + "".taint.capitalize.tainted?.should == true + "hello".taint.capitalize.tainted?.should == true + end + + ruby_version_is ''...'2.4' do + it "is locale insensitive (only upcases a-z and only downcases A-Z)" do + "ÄÖÜ".capitalize.should == "ÄÖÜ" + "ärger".capitalize.should == "ärger" + "BÄR".capitalize.should == "BÄr" + end + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(StringSpecs::MyString) + end +end + +describe "String#capitalize!" do + it "capitalizes self in place" do + a = "hello" + a.capitalize!.should equal(a) + a.should == "Hello" + end + + it "returns nil when no changes are made" do + a = "Hello" + a.capitalize!.should == nil + a.should == "Hello" + + "".capitalize!.should == nil + "H".capitalize!.should == nil + end + + it "raises a RuntimeError when self is frozen" do + ["", "Hello", "hello"].each do |a| + a.freeze + lambda { a.capitalize! }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/string/case_compare_spec.rb b/spec/rubyspec/core/string/case_compare_spec.rb new file mode 100644 index 0000000000..930f5bea90 --- /dev/null +++ b/spec/rubyspec/core/string/case_compare_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "String#===" do + it_behaves_like(:string_eql_value, :===) + it_behaves_like(:string_equal_value, :===) +end diff --git a/spec/rubyspec/core/string/casecmp_spec.rb b/spec/rubyspec/core/string/casecmp_spec.rb new file mode 100644 index 0000000000..c5503a18bf --- /dev/null +++ b/spec/rubyspec/core/string/casecmp_spec.rb @@ -0,0 +1,111 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#casecmp independent of case" do + it "returns -1 when less than other" do + "a".casecmp("b").should == -1 + "A".casecmp("b").should == -1 + end + + it "returns 0 when equal to other" do + "a".casecmp("a").should == 0 + "A".casecmp("a").should == 0 + end + + it "returns 1 when greater than other" do + "b".casecmp("a").should == 1 + "B".casecmp("a").should == 1 + end + + it "tries to convert other to string using to_str" do + other = mock('abc') + def other.to_str() "abc" end + "abc".casecmp(other).should == 0 + end + + it "raises a TypeError if other can't be converted to a string" do + lambda { "abc".casecmp(mock('abc')) }.should raise_error(TypeError) + end + + describe "in UTF-8 mode" do + describe "for non-ASCII characters" do + before :each do + @upper_a_tilde = "\xc3\x83" + @lower_a_tilde = "\xc3\xa3" + @upper_a_umlaut = "\xc3\x84" + @lower_a_umlaut = "\xc3\xa4" + end + + it "returns -1 when numerically less than other" do + @upper_a_tilde.casecmp(@lower_a_tilde).should == -1 + @upper_a_tilde.casecmp(@upper_a_umlaut).should == -1 + end + + it "returns 0 when numerically equal to other" do + @upper_a_tilde.casecmp(@upper_a_tilde).should == 0 + end + + it "returns 1 when numerically greater than other" do + @lower_a_umlaut.casecmp(@upper_a_umlaut).should == 1 + @lower_a_umlaut.casecmp(@lower_a_tilde).should == 1 + end + end + + describe "for ASCII characters" do + it "returns -1 when less than other" do + "a".casecmp("b").should == -1 + "A".casecmp("b").should == -1 + end + + it "returns 0 when equal to other" do + "a".casecmp("a").should == 0 + "A".casecmp("a").should == 0 + end + + it "returns 1 when greater than other" do + "b".casecmp("a").should == 1 + "B".casecmp("a").should == 1 + end + end + end + + describe "for non-ASCII characters" do + before :each do + @upper_a_tilde = "\xc3" + @lower_a_tilde = "\xe3" + end + + it "returns -1 when numerically less than other" do + @upper_a_tilde.casecmp(@lower_a_tilde).should == -1 + end + + it "returns 0 when equal to other" do + @upper_a_tilde.casecmp("\xc3").should == 0 + end + + it "returns 1 when numerically greater than other" do + @lower_a_tilde.casecmp(@upper_a_tilde).should == 1 + end + end + + describe "when comparing a subclass instance" do + it "returns -1 when less than other" do + b = StringSpecs::MyString.new "b" + "a".casecmp(b).should == -1 + "A".casecmp(b).should == -1 + end + + it "returns 0 when equal to other" do + a = StringSpecs::MyString.new "a" + "a".casecmp(a).should == 0 + "A".casecmp(a).should == 0 + end + + it "returns 1 when greater than other" do + a = StringSpecs::MyString.new "a" + "b".casecmp(a).should == 1 + "B".casecmp(a).should == 1 + end + end +end diff --git a/spec/rubyspec/core/string/center_spec.rb b/spec/rubyspec/core/string/center_spec.rb new file mode 100644 index 0000000000..4a40ef88bf --- /dev/null +++ b/spec/rubyspec/core/string/center_spec.rb @@ -0,0 +1,133 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#center with length, padding" do + it "returns a new string of specified length with self centered and padded with padstr" do + "one".center(9, '.').should == "...one..." + "hello".center(20, '123').should == "1231231hello12312312" + "middle".center(13, '-').should == "---middle----" + + "".center(1, "abcd").should == "a" + "".center(2, "abcd").should == "aa" + "".center(3, "abcd").should == "aab" + "".center(4, "abcd").should == "abab" + "".center(6, "xy").should == "xyxxyx" + "".center(11, "12345").should == "12345123451" + + "|".center(2, "abcd").should == "|a" + "|".center(3, "abcd").should == "a|a" + "|".center(4, "abcd").should == "a|ab" + "|".center(5, "abcd").should == "ab|ab" + "|".center(6, "xy").should == "xy|xyx" + "|".center(7, "xy").should == "xyx|xyx" + "|".center(11, "12345").should == "12345|12345" + "|".center(12, "12345").should == "12345|123451" + + "||".center(3, "abcd").should == "||a" + "||".center(4, "abcd").should == "a||a" + "||".center(5, "abcd").should == "a||ab" + "||".center(6, "abcd").should == "ab||ab" + "||".center(8, "xy").should == "xyx||xyx" + "||".center(12, "12345").should == "12345||12345" + "||".center(13, "12345").should == "12345||123451" + end + + it "pads with whitespace if no padstr is given" do + "two".center(5).should == " two " + "hello".center(20).should == " hello " + end + + it "returns self if it's longer than or as long as the specified length" do + "".center(0).should == "" + "".center(-1).should == "" + "hello".center(4).should == "hello" + "hello".center(-1).should == "hello" + "this".center(3).should == "this" + "radiology".center(8, '-').should == "radiology" + end + + it "taints result when self or padstr is tainted" do + "x".taint.center(4).tainted?.should == true + "x".taint.center(0).tainted?.should == true + "".taint.center(0).tainted?.should == true + "x".taint.center(4, "*").tainted?.should == true + "x".center(4, "*".taint).tainted?.should == true + end + + it "calls #to_int to convert length to an integer" do + "_".center(3.8, "^").should == "^_^" + + obj = mock('3') + obj.should_receive(:to_int).and_return(3) + + "_".center(obj, "o").should == "o_o" + end + + it "raises a TypeError when length can't be converted to an integer" do + lambda { "hello".center("x") }.should raise_error(TypeError) + lambda { "hello".center("x", "y") }.should raise_error(TypeError) + lambda { "hello".center([]) }.should raise_error(TypeError) + lambda { "hello".center(mock('x')) }.should raise_error(TypeError) + end + + it "calls #to_str to convert padstr to a String" do + padstr = mock('123') + padstr.should_receive(:to_str).and_return("123") + + "hello".center(20, padstr).should == "1231231hello12312312" + end + + it "raises a TypeError when padstr can't be converted to a string" do + lambda { "hello".center(20, 100) }.should raise_error(TypeError) + lambda { "hello".center(20, []) }.should raise_error(TypeError) + lambda { "hello".center(20, mock('x')) }.should raise_error(TypeError) + end + + it "raises an ArgumentError if padstr is empty" do + lambda { "hello".center(10, "") }.should raise_error(ArgumentError) + lambda { "hello".center(0, "") }.should raise_error(ArgumentError) + end + + it "returns subclass instances when called on subclasses" do + StringSpecs::MyString.new("").center(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + + "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end + + it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do + "hello".center(4, 'X'.taint).tainted?.should be_false + "hello".center(5, 'X'.taint).tainted?.should be_false + "hello".center(6, 'X'.taint).tainted?.should be_true + end + + with_feature :encoding do + describe "with width" do + it "returns a String in the same encoding as the original" do + str = "abc".force_encoding Encoding::IBM437 + result = str.center 6 + result.should == " abc " + result.encoding.should equal(Encoding::IBM437) + end + end + + describe "with width, pattern" do + it "returns a String in the compatible encoding" do + str = "abc".force_encoding Encoding::IBM437 + result = str.center 6, "あ" + result.should == "あabcああ" + result.encoding.should equal(Encoding::UTF_8) + end + + it "raises an Encoding::CompatibilityError if the encodings are incompatible" do + pat = "ア".encode Encoding::EUC_JP + lambda do + "あれ".center 5, pat + end.should raise_error(Encoding::CompatibilityError) + end + end + end +end diff --git a/spec/rubyspec/core/string/chars_spec.rb b/spec/rubyspec/core/string/chars_spec.rb new file mode 100644 index 0000000000..75cef89be1 --- /dev/null +++ b/spec/rubyspec/core/string/chars_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/chars', __FILE__) +require File.expand_path('../shared/each_char_without_block', __FILE__) + +describe "String#chars" do + it_behaves_like(:string_chars, :chars) + + it "returns an array when no block given" do + ary = "hello".send(@method) + ary.should == ['h', 'e', 'l', 'l', 'o'] + end +end diff --git a/spec/rubyspec/core/string/chomp_spec.rb b/spec/rubyspec/core/string/chomp_spec.rb new file mode 100644 index 0000000000..5daa8c5a40 --- /dev/null +++ b/spec/rubyspec/core/string/chomp_spec.rb @@ -0,0 +1,387 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#chomp" do + describe "when passed no argument" do + before do + # Ensure that $/ is set to the default value + @dollar_slash, $/ = $/, "\n" + end + + after do + $/ = @dollar_slash + end + + it "does not modify a String with no trailing carriage return or newline" do + "abc".chomp.should == "abc" + end + + it "returns a copy of the String when it is not modified" do + str = "abc" + str.chomp.should_not equal(str) + end + + it "removes one trailing newline" do + "abc\n\n".chomp.should == "abc\n" + end + + it "removes one trailing carriage return" do + "abc\r\r".chomp.should == "abc\r" + end + + it "removes one trailing carrige return, newline pair" do + "abc\r\n\r\n".chomp.should == "abc\r\n" + end + + it "returns an empty String when self is empty" do + "".chomp.should == "" + end + + it "taints the result if self is tainted" do + "abc".taint.chomp.tainted?.should be_true + end + + it "returns subclass instances when called on a subclass" do + str = StringSpecs::MyString.new("hello\n").chomp + str.should be_an_instance_of(StringSpecs::MyString) + end + + it "removes trailing characters that match $/ when it has been assigned a value" do + $/ = "cdef" + "abcdef".chomp.should == "ab" + end + end + + describe "when passed nil" do + it "does not modify the String" do + "abc\r\n".chomp(nil).should == "abc\r\n" + end + + it "returns a copy of the String" do + str = "abc" + str.chomp(nil).should_not equal(str) + end + + it "taints the result if self is tainted" do + "abc".taint.chomp(nil).tainted?.should be_true + end + + it "returns an empty String when self is empty" do + "".chomp(nil).should == "" + end + end + + describe "when passed ''" do + it "removes a final newline" do + "abc\n".chomp("").should == "abc" + end + + it "removes a final carriage return, newline" do + "abc\r\n".chomp("").should == "abc" + end + + it "does not remove a final carriage return" do + "abc\r".chomp("").should == "abc\r" + end + + it "removes more than one trailing newlines" do + "abc\n\n\n".chomp("").should == "abc" + end + + it "removes more than one trailing carriage return, newline pairs" do + "abc\r\n\r\n\r\n".chomp("").should == "abc" + end + + it "taints the result if self is tainted" do + "abc".taint.chomp("").tainted?.should be_true + end + + it "returns an empty String when self is empty" do + "".chomp("").should == "" + end + end + + describe "when passed '\\n'" do + it "removes one trailing newline" do + "abc\n\n".chomp("\n").should == "abc\n" + end + + it "removes one trailing carriage return" do + "abc\r\r".chomp("\n").should == "abc\r" + end + + it "removes one trailing carrige return, newline pair" do + "abc\r\n\r\n".chomp("\n").should == "abc\r\n" + end + + it "taints the result if self is tainted" do + "abc".taint.chomp("\n").tainted?.should be_true + end + + it "returns an empty String when self is empty" do + "".chomp("\n").should == "" + end + end + + describe "when passed an Object" do + it "calls #to_str to convert to a String" do + arg = mock("string chomp") + arg.should_receive(:to_str).and_return("bc") + "abc".chomp(arg).should == "a" + end + + it "raises a TypeError if #to_str does not return a String" do + arg = mock("string chomp") + arg.should_receive(:to_str).and_return(1) + lambda { "abc".chomp(arg) }.should raise_error(TypeError) + end + end + + describe "when passed a String" do + it "removes the trailing characters if they match the argument" do + "abcabc".chomp("abc").should == "abc" + end + + it "does not modify the String if the argument does not match the trailing characters" do + "abc".chomp("def").should == "abc" + end + + it "returns an empty String when self is empty" do + "".chomp("abc").should == "" + end + + it "taints the result if self is tainted" do + "abc".taint.chomp("abc").tainted?.should be_true + end + + it "does not taint the result when the argument is tainted" do + "abc".chomp("abc".taint).tainted?.should be_false + end + end +end + +describe "String#chomp!" do + describe "when passed no argument" do + before do + # Ensure that $/ is set to the default value + @dollar_slash, $/ = $/, "\n" + end + + after do + $/ = @dollar_slash + end + + it "modifies self" do + str = "abc\n" + str.chomp!.should equal(str) + end + + it "returns nil if self is not modified" do + "abc".chomp!.should be_nil + end + + it "removes one trailing newline" do + "abc\n\n".chomp!.should == "abc\n" + end + + it "removes one trailing carriage return" do + "abc\r\r".chomp!.should == "abc\r" + end + + it "removes one trailing carrige return, newline pair" do + "abc\r\n\r\n".chomp!.should == "abc\r\n" + end + + it "returns nil when self is empty" do + "".chomp!.should be_nil + end + + it "taints the result if self is tainted" do + "abc\n".taint.chomp!.tainted?.should be_true + end + + it "returns subclass instances when called on a subclass" do + str = StringSpecs::MyString.new("hello\n").chomp! + str.should be_an_instance_of(StringSpecs::MyString) + end + + it "removes trailing characters that match $/ when it has been assigned a value" do + $/ = "cdef" + "abcdef".chomp!.should == "ab" + end + end + + describe "when passed nil" do + it "returns nil" do + "abc\r\n".chomp!(nil).should be_nil + end + + it "returns nil when self is empty" do + "".chomp!(nil).should be_nil + end + end + + describe "when passed ''" do + it "removes a final newline" do + "abc\n".chomp!("").should == "abc" + end + + it "removes a final carriage return, newline" do + "abc\r\n".chomp!("").should == "abc" + end + + it "does not remove a final carriage return" do + "abc\r".chomp!("").should be_nil + end + + it "removes more than one trailing newlines" do + "abc\n\n\n".chomp!("").should == "abc" + end + + it "removes more than one trailing carriage return, newline pairs" do + "abc\r\n\r\n\r\n".chomp!("").should == "abc" + end + + it "taints the result if self is tainted" do + "abc\n".taint.chomp!("").tainted?.should be_true + end + + it "returns nil when self is empty" do + "".chomp!("").should be_nil + end + end + + describe "when passed '\\n'" do + it "removes one trailing newline" do + "abc\n\n".chomp!("\n").should == "abc\n" + end + + it "removes one trailing carriage return" do + "abc\r\r".chomp!("\n").should == "abc\r" + end + + it "removes one trailing carrige return, newline pair" do + "abc\r\n\r\n".chomp!("\n").should == "abc\r\n" + end + + it "taints the result if self is tainted" do + "abc\n".taint.chomp!("\n").tainted?.should be_true + end + + it "returns nil when self is empty" do + "".chomp!("\n").should be_nil + end + end + + describe "when passed an Object" do + it "calls #to_str to convert to a String" do + arg = mock("string chomp") + arg.should_receive(:to_str).and_return("bc") + "abc".chomp!(arg).should == "a" + end + + it "raises a TypeError if #to_str does not return a String" do + arg = mock("string chomp") + arg.should_receive(:to_str).and_return(1) + lambda { "abc".chomp!(arg) }.should raise_error(TypeError) + end + end + + describe "when passed a String" do + it "removes the trailing characters if they match the argument" do + "abcabc".chomp!("abc").should == "abc" + end + + it "returns nil if the argument does not match the trailing characters" do + "abc".chomp!("def").should be_nil + end + + it "returns nil when self is empty" do + "".chomp!("abc").should be_nil + end + + it "taints the result if self is tainted" do + "abc".taint.chomp!("abc").tainted?.should be_true + end + + it "does not taint the result when the argument is tainted" do + "abc".chomp!("abc".taint).tainted?.should be_false + end + end + + it "raises a RuntimeError on a frozen instance when it is modified" do + a = "string\n\r" + a.freeze + + lambda { a.chomp! }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on a frozen instance when it would not be modified" do + a = "string\n\r" + a.freeze + lambda { a.chomp!(nil) }.should raise_error(RuntimeError) + lambda { a.chomp!("x") }.should raise_error(RuntimeError) + end +end + +with_feature :encoding do + describe "String#chomp" do + before :each do + @before_separator = $/ + end + + after :each do + $/ = @before_separator + end + + it "does not modify a multi-byte character" do + "あれ".chomp.should == "あれ" + end + + it "removes the final carriage return, newline from a multibyte String" do + "あれ\r\n".chomp.should == "あれ" + end + + it "removes the final carriage return, newline from a non-ASCII String" do + str = "abc\r\n".encode "utf-32be" + str.chomp.should == "abc".encode("utf-32be") + end + + it "removes the final carriage return, newline from a non-ASCII String when the record separator is changed" do + $/ = "\n".encode("utf-8") + str = "abc\r\n".encode "utf-32be" + str.chomp.should == "abc".encode("utf-32be") + end + end + + describe "String#chomp!" do + before :each do + @before_separator = $/ + end + + after :each do + $/ = @before_separator + end + + it "returns nil when the String is not modified" do + "あれ".chomp!.should be_nil + end + + it "removes the final carriage return, newline from a multibyte String" do + "あれ\r\n".chomp!.should == "あれ" + end + + it "removes the final carriage return, newline from a non-ASCII String" do + str = "abc\r\n".encode "utf-32be" + str.chomp!.should == "abc".encode("utf-32be") + end + + it "removes the final carriage return, newline from a non-ASCII String when the record separator is changed" do + $/ = "\n".encode("utf-8") + str = "abc\r\n".encode "utf-32be" + str.chomp!.should == "abc".encode("utf-32be") + end + end +end diff --git a/spec/rubyspec/core/string/chop_spec.rb b/spec/rubyspec/core/string/chop_spec.rb new file mode 100644 index 0000000000..4e9a39f866 --- /dev/null +++ b/spec/rubyspec/core/string/chop_spec.rb @@ -0,0 +1,128 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#chop" do + it "removes the final character" do + "abc".chop.should == "ab" + end + + it "removes the final carriage return" do + "abc\r".chop.should == "abc" + end + + it "removes the final newline" do + "abc\n".chop.should == "abc" + end + + it "removes the final carriage return, newline" do + "abc\r\n".chop.should == "abc" + end + + it "removes the carrige return, newline if they are the only characters" do + "\r\n".chop.should == "" + end + + it "does not remove more than the final carriage return, newline" do + "abc\r\n\r\n".chop.should == "abc\r\n" + end + + with_feature :encoding do + it "removes a multi-byte character" do + "あれ".chop.should == "あ" + end + + it "removes the final carriage return, newline from a multibyte String" do + "あれ\r\n".chop.should == "あれ" + end + + it "removes the final carriage return, newline from a non-ASCII String" do + str = "abc\r\n".encode "utf-32be" + str.chop.should == "abc".encode("utf-32be") + end + end + + it "returns an empty string when applied to an empty string" do + "".chop.should == "" + end + + it "returns a new string when applied to an empty string" do + s = "" + s.chop.should_not equal(s) + end + + it "taints result when self is tainted" do + "hello".taint.chop.tainted?.should == true + "".taint.chop.tainted?.should == true + end + + it "untrusts result when self is untrusted" do + "hello".untrust.chop.untrusted?.should == true + "".untrust.chop.untrusted?.should == true + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(StringSpecs::MyString) + end +end + +describe "String#chop!" do + it "removes the final character" do + "abc".chop!.should == "ab" + end + + it "removes the final carriage return" do + "abc\r".chop!.should == "abc" + end + + it "removes the final newline" do + "abc\n".chop!.should == "abc" + end + + it "removes the final carriage return, newline" do + "abc\r\n".chop!.should == "abc" + end + + it "removes the carrige return, newline if they are the only characters" do + "\r\n".chop!.should == "" + end + + it "does not remove more than the final carriage return, newline" do + "abc\r\n\r\n".chop!.should == "abc\r\n" + end + + with_feature :encoding do + it "removes a multi-byte character" do + "あれ".chop!.should == "あ" + end + + it "removes the final carriage return, newline from a multibyte String" do + "あれ\r\n".chop!.should == "あれ" + end + + it "removes the final carriage return, newline from a non-ASCII String" do + str = "abc\r\n".encode "utf-32be" + str.chop!.should == "abc".encode("utf-32be") + end + end + + it "returns self if modifications were made" do + str = "hello" + str.chop!.should equal(str) + end + + it "returns nil when called on an empty string" do + "".chop!.should be_nil + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda { "string\n\r".freeze.chop! }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on a frozen instance that would not be modified" do + a = "" + a.freeze + lambda { a.chop! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/chr_spec.rb b/spec/rubyspec/core/string/chr_spec.rb new file mode 100644 index 0000000000..c7834b78b7 --- /dev/null +++ b/spec/rubyspec/core/string/chr_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "String#chr" do + it "returns a copy of self" do + s = 'e' + s.object_id.should_not == s.chr.object_id + end + + it "returns a String" do + 'glark'.chr.should be_an_instance_of(String) + end + + it "returns an empty String if self is an empty String" do + "".chr.should == "" + end + + it "returns a 1-character String" do + "glark".chr.size.should == 1 + end + + it "returns the character at the start of the String" do + "Goodbye, world".chr.should == "G" + end + + it "returns a String in the same encoding as self" do + "\x24".encode(Encoding::US_ASCII).chr.encoding.should == Encoding::US_ASCII + end + + it "understands multi-byte characters" do + s = "\u{9879}" + s.bytesize.should == 3 + s.chr.should == s + end + + it "understands Strings that contain a mixture of character widths" do + three = "\u{8082}" + three.bytesize.should == 3 + four = "\u{77082}" + four.bytesize.should == 4 + "#{three}#{four}".chr.should == three + end + end +end diff --git a/spec/rubyspec/core/string/clear_spec.rb b/spec/rubyspec/core/string/clear_spec.rb new file mode 100644 index 0000000000..6a8b6018d0 --- /dev/null +++ b/spec/rubyspec/core/string/clear_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "String#clear" do + before :each do + @s = "Jolene" + end + + it "sets self equal to the empty String" do + @s.clear + @s.should == "" + end + + it "returns self after emptying it" do + cleared = @s.clear + cleared.should == "" + cleared.object_id.should == @s.object_id + end + + it "preserves its encoding" do + @s.encode!(Encoding::SHIFT_JIS) + @s.encoding.should == Encoding::SHIFT_JIS + @s.clear.encoding.should == Encoding::SHIFT_JIS + @s.encoding.should == Encoding::SHIFT_JIS + end + + it "works with multibyte Strings" do + s = "\u{9765}\u{876}" + s.clear + s.should == "" + end + + it "raises a RuntimeError if self is frozen" do + @s.freeze + lambda { @s.clear }.should raise_error(RuntimeError) + lambda { "".freeze.clear }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/string/clone_spec.rb b/spec/rubyspec/core/string/clone_spec.rb new file mode 100644 index 0000000000..3940858e53 --- /dev/null +++ b/spec/rubyspec/core/string/clone_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "String#clone" do + before :each do + ScratchPad.clear + @obj = StringSpecs::InitializeString.new "string" + end + + it "calls #initialize_copy on the new instance" do + clone = @obj.clone + ScratchPad.recorded.should_not == @obj.object_id + ScratchPad.recorded.should == clone.object_id + end + + it "copies instance variables" do + clone = @obj.clone + clone.ivar.should == 1 + end + + it "copies singleton methods" do + def @obj.special() :the_one end + clone = @obj.clone + clone.special.should == :the_one + end + + it "copies modules included in the singleton class" do + class << @obj + include StringSpecs::StringModule + end + + clone = @obj.clone + clone.repr.should == 1 + end + + it "copies constants defined in the singleton class" do + class << @obj + CLONE = :clone + end + + clone = @obj.clone + (class << clone; CLONE; end).should == :clone + end + + it "copies frozen state" do + @obj.freeze.clone.frozen?.should be_true + "".freeze.clone.frozen?.should be_true + end + + it "does not modify the original string when changing cloned string" do + orig = "string"[0..100] + clone = orig.clone + orig[0] = 'x' + orig.should == "xtring" + clone.should == "string" + end +end + diff --git a/spec/rubyspec/core/string/codepoints_spec.rb b/spec/rubyspec/core/string/codepoints_spec.rb new file mode 100644 index 0000000000..6304513583 --- /dev/null +++ b/spec/rubyspec/core/string/codepoints_spec.rb @@ -0,0 +1,20 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/codepoints', __FILE__) +require File.expand_path('../shared/each_codepoint_without_block', __FILE__) + +with_feature :encoding do + describe "String#codepoints" do + it_behaves_like(:string_codepoints, :codepoints) + + it "returns an Array when no block is given" do + "abc".send(@method).should == [?a.ord, ?b.ord, ?c.ord] + end + + it "raises an ArgumentError when no block is given if self has an invalid encoding" do + s = "\xDF".force_encoding(Encoding::UTF_8) + s.valid_encoding?.should be_false + lambda {s.send(@method)}.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/string/comparison_spec.rb b/spec/rubyspec/core/string/comparison_spec.rb new file mode 100644 index 0000000000..5a0e7fabc3 --- /dev/null +++ b/spec/rubyspec/core/string/comparison_spec.rb @@ -0,0 +1,108 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#<=> with String" do + it "compares individual characters based on their ascii value" do + ascii_order = Array.new(256) { |x| x.chr } + sort_order = ascii_order.sort + sort_order.should == ascii_order + end + + it "returns -1 when self is less than other" do + ("this" <=> "those").should == -1 + end + + it "returns 0 when self is equal to other" do + ("yep" <=> "yep").should == 0 + end + + it "returns 1 when self is greater than other" do + ("yoddle" <=> "griddle").should == 1 + end + + it "considers string that comes lexicographically first to be less if strings have same size" do + ("aba" <=> "abc").should == -1 + ("abc" <=> "aba").should == 1 + end + + it "doesn't consider shorter string to be less if longer string starts with shorter one" do + ("abc" <=> "abcd").should == -1 + ("abcd" <=> "abc").should == 1 + end + + it "compares shorter string with corresponding number of first chars of longer string" do + ("abx" <=> "abcd").should == 1 + ("abcd" <=> "abx").should == -1 + end + + it "ignores subclass differences" do + a = "hello" + b = StringSpecs::MyString.new("hello") + + (a <=> b).should == 0 + (b <=> a).should == 0 + end + + it "returns 0 if self and other are bytewise identical and have the same encoding" do + ("ÄÖÜ" <=> "ÄÖÜ").should == 0 + end + + it "returns 0 if self and other are bytewise identical and have the same encoding" do + ("ÄÖÜ" <=> "ÄÖÜ").should == 0 + end + + it "returns -1 if self is bytewise less than other" do + ("ÄÖÛ" <=> "ÄÖÜ").should == -1 + end + + it "returns 1 if self is bytewise greater than other" do + ("ÄÖÜ" <=> "ÄÖÛ").should == 1 + end + + it "ignores encoding difference" do + ("ÄÖÛ".force_encoding("utf-8") <=> "ÄÖÜ".force_encoding("iso-8859-1")).should == -1 + ("ÄÖÜ".force_encoding("utf-8") <=> "ÄÖÛ".force_encoding("iso-8859-1")).should == 1 + end + + it "returns 0 with identical ASCII-compatible bytes of different encodings" do + ("abc".force_encoding("utf-8") <=> "abc".force_encoding("iso-8859-1")).should == 0 + end + + it "compares the indices of the encodings when the strings have identical non-ASCII-compatible bytes" do + xff_1 = [0xFF].pack('C').force_encoding("utf-8") + xff_2 = [0xFF].pack('C').force_encoding("iso-8859-1") + (xff_1 <=> xff_2).should == -1 + (xff_2 <=> xff_1).should == 1 + end +end + +# Note: This is inconsistent with Array#<=> which calls #to_ary instead of +# just using it as an indicator. +describe "String#<=>" do + it "returns nil if its argument provides neither #to_str nor #<=>" do + ("abc" <=> mock('x')).should be_nil + end + + it "uses the result of calling #to_str for comparison when #to_str is defined" do + obj = mock('x') + obj.should_receive(:to_str).and_return("aaa") + + ("abc" <=> obj).should == 1 + end + + it "uses the result of calling #<=> on its argument when #<=> is defined but #to_str is not" do + obj = mock('x') + obj.should_receive(:<=>).and_return(-1) + + ("abc" <=> obj).should == 1 + end + + it "returns nil if argument also uses an inverse comparison for <=>" do + obj = mock('x') + def obj.<=>(other); other <=> self; end + obj.should_receive(:<=>).once + + ("abc" <=> obj).should be_nil + end +end diff --git a/spec/rubyspec/core/string/concat_spec.rb b/spec/rubyspec/core/string/concat_spec.rb new file mode 100644 index 0000000000..939be5ed0a --- /dev/null +++ b/spec/rubyspec/core/string/concat_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/concat', __FILE__) + +describe "String#concat" do + it_behaves_like :string_concat, :concat + it_behaves_like :string_concat_encoding, :concat + + ruby_version_is "2.4" do + it "takes multiple arguments" do + str = "hello " + str.concat "wo", "", "rld" + str.should == "hello world" + end + + it "concatenates the initial value when given arguments contain 2 self" do + str = "hello" + str.concat str, str + str.should == "hellohellohello" + end + + it "returns self when given no arguments" do + str = "hello" + str.concat.should equal(str) + str.should == "hello" + end + end +end diff --git a/spec/rubyspec/core/string/count_spec.rb b/spec/rubyspec/core/string/count_spec.rb new file mode 100644 index 0000000000..3afb79a9fc --- /dev/null +++ b/spec/rubyspec/core/string/count_spec.rb @@ -0,0 +1,105 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#count" do + it "counts occurrences of chars from the intersection of the specified sets" do + s = "hello\nworld\x00\x00" + + s.count(s).should == s.size + s.count("lo").should == 5 + s.count("eo").should == 3 + s.count("l").should == 3 + s.count("\n").should == 1 + s.count("\x00").should == 2 + + s.count("").should == 0 + "".count("").should == 0 + + s.count("l", "lo").should == s.count("l") + s.count("l", "lo", "o").should == s.count("") + s.count("helo", "hel", "h").should == s.count("h") + s.count("helo", "", "x").should == 0 + end + + it "raises an ArgumentError when given no arguments" do + lambda { "hell yeah".count }.should raise_error(ArgumentError) + end + + it "negates sets starting with ^" do + s = "^hello\nworld\x00\x00" + + s.count("^").should == 1 # no negation, counts ^ + + s.count("^leh").should == 9 + s.count("^o").should == 12 + + s.count("helo", "^el").should == s.count("ho") + s.count("aeiou", "^e").should == s.count("aiou") + + "^_^".count("^^").should == 1 + "oa^_^o".count("a^").should == 3 + end + + it "counts all chars in a sequence" do + s = "hel-[()]-lo012^" + + s.count("\x00-\xFF").should == s.size + s.count("ej-m").should == 3 + s.count("e-h").should == 2 + + # no sequences + s.count("-").should == 2 + s.count("e-").should == s.count("e") + s.count("-") + s.count("-h").should == s.count("h") + s.count("-") + + s.count("---").should == s.count("-") + + # see an ASCII table for reference + s.count("--2").should == s.count("-./012") + s.count("(--").should == s.count("()*+,-") + s.count("A-a").should == s.count("A-Z[\\]^_`a") + + # negated sequences + s.count("^e-h").should == s.size - s.count("e-h") + s.count("^^-^").should == s.size - s.count("^") + s.count("^---").should == s.size - s.count("-") + + "abcdefgh".count("a-ce-fh").should == 6 + "abcdefgh".count("he-fa-c").should == 6 + "abcdefgh".count("e-fha-c").should == 6 + + "abcde".count("ac-e").should == 4 + "abcde".count("^ac-e").should == 1 + end + + it "raises if the given sequences are invalid" do + s = "hel-[()]-lo012^" + + lambda { s.count("h-e") }.should raise_error(ArgumentError) + lambda { s.count("^h-e") }.should raise_error(ArgumentError) + end + + it 'returns the number of occurrences of a multi-byte character' do + str = "\u{2605}" + str.count(str).should == 1 + "asd#{str}zzz#{str}ggg".count(str).should == 2 + end + + it "calls #to_str to convert each set arg to a String" do + other_string = mock('lo') + other_string.should_receive(:to_str).and_return("lo") + + other_string2 = mock('o') + other_string2.should_receive(:to_str).and_return("o") + + s = "hello world" + s.count(other_string, other_string2).should == s.count("o") + end + + it "raises a TypeError when a set arg can't be converted to a string" do + lambda { "hello world".count(100) }.should raise_error(TypeError) + lambda { "hello world".count([]) }.should raise_error(TypeError) + lambda { "hello world".count(mock('x')) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/string/crypt_spec.rb b/spec/rubyspec/core/string/crypt_spec.rb new file mode 100644 index 0000000000..d3bf5ec732 --- /dev/null +++ b/spec/rubyspec/core/string/crypt_spec.rb @@ -0,0 +1,75 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#crypt" do + # Note: MRI's documentation just says that the C stdlib function crypt() is + # called. + # + # I'm not sure if crypt() is guaranteed to produce the same result across + # different platforms. It seems that there is one standard UNIX implementation + # of crypt(), but that alternative implementations are possible. See + # http://www.unix.org.ua/orelly/networking/puis/ch08_06.htm + it "returns a cryptographic hash of self by applying the UNIX crypt algorithm with the specified salt" do + "".crypt("aa").should == "aaQSqAReePlq6" + "nutmeg".crypt("Mi").should == "MiqkFWCm1fNJI" + "ellen1".crypt("ri").should == "ri79kNd7V6.Sk" + "Sharon".crypt("./").should == "./UY9Q7TvYJDg" + "norahs".crypt("am").should == "amfIADT2iqjA." + "norahs".crypt("7a").should == "7azfT5tIdyh0I" + + # Only uses first 8 chars of string + "01234567".crypt("aa").should == "aa4c4gpuvCkSE" + "012345678".crypt("aa").should == "aa4c4gpuvCkSE" + "0123456789".crypt("aa").should == "aa4c4gpuvCkSE" + + # Only uses first 2 chars of salt + "hello world".crypt("aa").should == "aayPz4hyPS1wI" + "hello world".crypt("aab").should == "aayPz4hyPS1wI" + "hello world".crypt("aabc").should == "aayPz4hyPS1wI" + end + + it "raises an ArgumentError when the salt is shorter than two characters" do + lambda { "hello".crypt("") }.should raise_error(ArgumentError) + lambda { "hello".crypt("f") }.should raise_error(ArgumentError) + lambda { "hello".crypt("\x00\x00") }.should raise_error(ArgumentError) + lambda { "hello".crypt("\x00a") }.should raise_error(ArgumentError) + lambda { "hello".crypt("a\x00") }.should raise_error(ArgumentError) + end + + ruby_version_is "2.3" do + it "raises an ArgumentError when the string contains NUL character" do + lambda { "poison\0null".crypt("aa") }.should raise_error(ArgumentError) + end + end + + it "calls #to_str to converts the salt arg to a String" do + obj = mock('aa') + obj.should_receive(:to_str).and_return("aa") + + "".crypt(obj).should == "aaQSqAReePlq6" + end + + it "raises a type error when the salt arg can't be converted to a string" do + lambda { "".crypt(5) }.should raise_error(TypeError) + lambda { "".crypt(mock('x')) }.should raise_error(TypeError) + end + + it "taints the result if either salt or self is tainted" do + tainted_salt = "aa" + tainted_str = "hello" + + tainted_salt.taint + tainted_str.taint + + "hello".crypt("aa").tainted?.should == false + tainted_str.crypt("aa").tainted?.should == true + "hello".crypt(tainted_salt).tainted?.should == true + tainted_str.crypt(tainted_salt).tainted?.should == true + end + + it "doesn't return subclass instances" do + StringSpecs::MyString.new("hello").crypt("aa").should be_an_instance_of(String) + "hello".crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String) + StringSpecs::MyString.new("hello").crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String) + end +end diff --git a/spec/rubyspec/core/string/delete_spec.rb b/spec/rubyspec/core/string/delete_spec.rb new file mode 100644 index 0000000000..536d4a95af --- /dev/null +++ b/spec/rubyspec/core/string/delete_spec.rb @@ -0,0 +1,119 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#delete" do + it "returns a new string with the chars from the intersection of sets removed" do + s = "hello" + s.delete("lo").should == "he" + s.should == "hello" + + "hello".delete("l", "lo").should == "heo" + + "hell yeah".delete("").should == "hell yeah" + end + + it "raises an ArgumentError when given no arguments" do + lambda { "hell yeah".delete }.should raise_error(ArgumentError) + end + + it "negates sets starting with ^" do + "hello".delete("aeiou", "^e").should == "hell" + "hello".delete("^leh").should == "hell" + "hello".delete("^o").should == "o" + "hello".delete("^").should == "hello" + "^_^".delete("^^").should == "^^" + "oa^_^o".delete("a^").should == "o_o" + end + + it "deletes all chars in a sequence" do + "hello".delete("ej-m").should == "ho" + "hello".delete("e-h").should == "llo" + "hel-lo".delete("e-").should == "hllo" + "hel-lo".delete("-h").should == "ello" + "hel-lo".delete("---").should == "hello" + "hel-012".delete("--2").should == "hel" + "hel-()".delete("(--").should == "hel" + "hello".delete("^e-h").should == "he" + "hello^".delete("^^-^").should == "^" + "hel--lo".delete("^---").should == "--" + + "abcdefgh".delete("a-ce-fh").should == "dg" + "abcdefgh".delete("he-fa-c").should == "dg" + "abcdefgh".delete("e-fha-c").should == "dg" + + "abcde".delete("ac-e").should == "b" + "abcde".delete("^ac-e").should == "acde" + + "ABCabc[]".delete("A-a").should == "bc" + end + + it "deletes multibyte characters" do + "四月".delete("月").should == "四" + '哥哥我倒'.delete('哥').should == "我倒" + end + + it "respects backslash for escaping a -" do + 'Non-Authoritative Information'.delete(' \-\'').should == + 'NonAuthoritativeInformation' + end + + it "raises if the given ranges are invalid" do + not_supported_on :opal do + xFF = [0xFF].pack('C') + range = "\x00 - #{xFF}".force_encoding('utf-8') + lambda { "hello".delete(range).should == "" }.should raise_error(ArgumentError) + end + lambda { "hello".delete("h-e") }.should raise_error(ArgumentError) + lambda { "hello".delete("^h-e") }.should raise_error(ArgumentError) + end + + it "taints result when self is tainted" do + "hello".taint.delete("e").tainted?.should == true + "hello".taint.delete("a-z").tainted?.should == true + + "hello".delete("e".taint).tainted?.should == false + end + + it "tries to convert each set arg to a string using to_str" do + other_string = mock('lo') + other_string.should_receive(:to_str).and_return("lo") + + other_string2 = mock('o') + other_string2.should_receive(:to_str).and_return("o") + + "hello world".delete(other_string, other_string2).should == "hell wrld" + end + + it "raises a TypeError when one set arg can't be converted to a string" do + lambda { "hello world".delete(100) }.should raise_error(TypeError) + lambda { "hello world".delete([]) }.should raise_error(TypeError) + lambda { "hello world".delete(mock('x')) }.should raise_error(TypeError) + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(StringSpecs::MyString) + end +end + +describe "String#delete!" do + it "modifies self in place and returns self" do + a = "hello" + a.delete!("aeiou", "^e").should equal(a) + a.should == "hell" + end + + it "returns nil if no modifications were made" do + a = "hello" + a.delete!("z").should == nil + a.should == "hello" + end + + it "raises a RuntimeError when self is frozen" do + a = "hello" + a.freeze + + lambda { a.delete!("") }.should raise_error(RuntimeError) + lambda { a.delete!("aeiou", "^e") }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/downcase_spec.rb b/spec/rubyspec/core/string/downcase_spec.rb new file mode 100644 index 0000000000..d31b6633df --- /dev/null +++ b/spec/rubyspec/core/string/downcase_spec.rb @@ -0,0 +1,59 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#downcase" do + it "returns a copy of self with all uppercase letters downcased" do + "hELLO".downcase.should == "hello" + "hello".downcase.should == "hello" + end + + ruby_version_is ''...'2.4' do + it "is locale insensitive (only replaces A-Z)" do + "ÄÖÜ".downcase.should == "ÄÖÜ" + + str = Array.new(256) { |c| c.chr }.join + expected = Array.new(256) do |i| + c = i.chr + c.between?("A", "Z") ? c.downcase : c + end.join + + str.downcase.should == expected + end + end + + it "taints result when self is tainted" do + "".taint.downcase.tainted?.should == true + "x".taint.downcase.tainted?.should == true + "X".taint.downcase.tainted?.should == true + end + + it "returns a subclass instance for subclasses" do + StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(StringSpecs::MyString) + end +end + +describe "String#downcase!" do + it "modifies self in place" do + a = "HeLlO" + a.downcase!.should equal(a) + a.should == "hello" + end + + it "returns nil if no modifications were made" do + a = "hello" + a.downcase!.should == nil + a.should == "hello" + end + + it "raises a RuntimeError when self is frozen" do + lambda { "HeLlo".freeze.downcase! }.should raise_error(RuntimeError) + lambda { "hello".freeze.downcase! }.should raise_error(RuntimeError) + end + + with_feature :encoding do + it "sets the result String encoding to the source String encoding" do + "ABC".downcase.encoding.should equal(Encoding::UTF_8) + end + end +end diff --git a/spec/rubyspec/core/string/dump_spec.rb b/spec/rubyspec/core/string/dump_spec.rb new file mode 100644 index 0000000000..db743ad08f --- /dev/null +++ b/spec/rubyspec/core/string/dump_spec.rb @@ -0,0 +1,424 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "String#dump" do + it "taints the result if self is tainted" do + "foo".taint.dump.tainted?.should == true + "foo\n".taint.dump.tainted?.should == true + end + + it "untrusts the result if self is untrusted" do + "foo".untrust.dump.untrusted?.should == true + "foo\n".untrust.dump.untrusted?.should == true + end + + it "returns a subclass instance" do + StringSpecs::MyString.new.dump.should be_an_instance_of(StringSpecs::MyString) + end + + it "returns a string with special characters replaced with \\ notation" do + [ ["\a", '"\\a"'], + ["\b", '"\\b"'], + ["\t", '"\\t"'], + ["\n", '"\\n"'], + ["\v", '"\\v"'], + ["\f", '"\\f"'], + ["\r", '"\\r"'], + ["\e", '"\\e"'] + ].should be_computed_by(:dump) + end + + it "returns a string with \" and \\ escaped with a backslash" do + [ ["\"", '"\\""'], + ["\\", '"\\\\"'] + ].should be_computed_by(:dump) + end + + it "returns a string with \\# when # is followed by $, @, {" do + [ ["\#$", '"\\#$"'], + ["\#@", '"\\#@"'], + ["\#{", '"\\#{"'] + ].should be_computed_by(:dump) + end + + it "returns a string with # not escaped when followed by any other character" do + [ ["#", '"#"'], + ["#1", '"#1"'] + ].should be_computed_by(:dump) + end + + it "returns a string with printable non-alphanumeric characters unescaped" do + [ [" ", '" "'], + ["!", '"!"'], + ["$", '"$"'], + ["%", '"%"'], + ["&", '"&"'], + ["'", '"\'"'], + ["(", '"("'], + [")", '")"'], + ["*", '"*"'], + ["+", '"+"'], + [",", '","'], + ["-", '"-"'], + [".", '"."'], + ["/", '"/"'], + [":", '":"'], + [";", '";"'], + ["<", '"<"'], + ["=", '"="'], + [">", '">"'], + ["?", '"?"'], + ["@", '"@"'], + ["[", '"["'], + ["]", '"]"'], + ["^", '"^"'], + ["_", '"_"'], + ["`", '"`"'], + ["{", '"{"'], + ["|", '"|"'], + ["}", '"}"'], + ["~", '"~"'] + ].should be_computed_by(:dump) + end + + it "returns a string with numeric characters unescaped" do + [ ["0", '"0"'], + ["1", '"1"'], + ["2", '"2"'], + ["3", '"3"'], + ["4", '"4"'], + ["5", '"5"'], + ["6", '"6"'], + ["7", '"7"'], + ["8", '"8"'], + ["9", '"9"'], + ].should be_computed_by(:dump) + end + + it "returns a string with upper-case alpha characters unescaped" do + [ ["A", '"A"'], + ["B", '"B"'], + ["C", '"C"'], + ["D", '"D"'], + ["E", '"E"'], + ["F", '"F"'], + ["G", '"G"'], + ["H", '"H"'], + ["I", '"I"'], + ["J", '"J"'], + ["K", '"K"'], + ["L", '"L"'], + ["M", '"M"'], + ["N", '"N"'], + ["O", '"O"'], + ["P", '"P"'], + ["Q", '"Q"'], + ["R", '"R"'], + ["S", '"S"'], + ["T", '"T"'], + ["U", '"U"'], + ["V", '"V"'], + ["W", '"W"'], + ["X", '"X"'], + ["Y", '"Y"'], + ["Z", '"Z"'] + ].should be_computed_by(:dump) + end + + it "returns a string with lower-case alpha characters unescaped" do + [ ["a", '"a"'], + ["b", '"b"'], + ["c", '"c"'], + ["d", '"d"'], + ["e", '"e"'], + ["f", '"f"'], + ["g", '"g"'], + ["h", '"h"'], + ["i", '"i"'], + ["j", '"j"'], + ["k", '"k"'], + ["l", '"l"'], + ["m", '"m"'], + ["n", '"n"'], + ["o", '"o"'], + ["p", '"p"'], + ["q", '"q"'], + ["r", '"r"'], + ["s", '"s"'], + ["t", '"t"'], + ["u", '"u"'], + ["v", '"v"'], + ["w", '"w"'], + ["x", '"x"'], + ["y", '"y"'], + ["z", '"z"'] + ].should be_computed_by(:dump) + end + + it "returns a string with non-printing ASCII characters replaced by \\x notation" do + # Avoid the file encoding by computing the string with #chr. + [ [0000.chr, '"\\x00"'], + [0001.chr, '"\\x01"'], + [0002.chr, '"\\x02"'], + [0003.chr, '"\\x03"'], + [0004.chr, '"\\x04"'], + [0005.chr, '"\\x05"'], + [0006.chr, '"\\x06"'], + [0016.chr, '"\\x0E"'], + [0017.chr, '"\\x0F"'], + [0020.chr, '"\\x10"'], + [0021.chr, '"\\x11"'], + [0022.chr, '"\\x12"'], + [0023.chr, '"\\x13"'], + [0024.chr, '"\\x14"'], + [0025.chr, '"\\x15"'], + [0026.chr, '"\\x16"'], + [0027.chr, '"\\x17"'], + [0030.chr, '"\\x18"'], + [0031.chr, '"\\x19"'], + [0032.chr, '"\\x1A"'], + [0034.chr, '"\\x1C"'], + [0035.chr, '"\\x1D"'], + [0036.chr, '"\\x1E"'], + [0037.chr, '"\\x1F"'], + [0177.chr, '"\\x7F"'], + [0200.chr, '"\\x80"'], + [0201.chr, '"\\x81"'], + [0202.chr, '"\\x82"'], + [0203.chr, '"\\x83"'], + [0204.chr, '"\\x84"'], + [0205.chr, '"\\x85"'], + [0206.chr, '"\\x86"'], + [0207.chr, '"\\x87"'], + [0210.chr, '"\\x88"'], + [0211.chr, '"\\x89"'], + [0212.chr, '"\\x8A"'], + [0213.chr, '"\\x8B"'], + [0214.chr, '"\\x8C"'], + [0215.chr, '"\\x8D"'], + [0216.chr, '"\\x8E"'], + [0217.chr, '"\\x8F"'], + [0220.chr, '"\\x90"'], + [0221.chr, '"\\x91"'], + [0222.chr, '"\\x92"'], + [0223.chr, '"\\x93"'], + [0224.chr, '"\\x94"'], + [0225.chr, '"\\x95"'], + [0226.chr, '"\\x96"'], + [0227.chr, '"\\x97"'], + [0230.chr, '"\\x98"'], + [0231.chr, '"\\x99"'], + [0232.chr, '"\\x9A"'], + [0233.chr, '"\\x9B"'], + [0234.chr, '"\\x9C"'], + [0235.chr, '"\\x9D"'], + [0236.chr, '"\\x9E"'], + [0237.chr, '"\\x9F"'], + [0240.chr, '"\\xA0"'], + [0241.chr, '"\\xA1"'], + [0242.chr, '"\\xA2"'], + [0243.chr, '"\\xA3"'], + [0244.chr, '"\\xA4"'], + [0245.chr, '"\\xA5"'], + [0246.chr, '"\\xA6"'], + [0247.chr, '"\\xA7"'], + [0250.chr, '"\\xA8"'], + [0251.chr, '"\\xA9"'], + [0252.chr, '"\\xAA"'], + [0253.chr, '"\\xAB"'], + [0254.chr, '"\\xAC"'], + [0255.chr, '"\\xAD"'], + [0256.chr, '"\\xAE"'], + [0257.chr, '"\\xAF"'], + [0260.chr, '"\\xB0"'], + [0261.chr, '"\\xB1"'], + [0262.chr, '"\\xB2"'], + [0263.chr, '"\\xB3"'], + [0264.chr, '"\\xB4"'], + [0265.chr, '"\\xB5"'], + [0266.chr, '"\\xB6"'], + [0267.chr, '"\\xB7"'], + [0270.chr, '"\\xB8"'], + [0271.chr, '"\\xB9"'], + [0272.chr, '"\\xBA"'], + [0273.chr, '"\\xBB"'], + [0274.chr, '"\\xBC"'], + [0275.chr, '"\\xBD"'], + [0276.chr, '"\\xBE"'], + [0277.chr, '"\\xBF"'], + [0300.chr, '"\\xC0"'], + [0301.chr, '"\\xC1"'], + [0302.chr, '"\\xC2"'], + [0303.chr, '"\\xC3"'], + [0304.chr, '"\\xC4"'], + [0305.chr, '"\\xC5"'], + [0306.chr, '"\\xC6"'], + [0307.chr, '"\\xC7"'], + [0310.chr, '"\\xC8"'], + [0311.chr, '"\\xC9"'], + [0312.chr, '"\\xCA"'], + [0313.chr, '"\\xCB"'], + [0314.chr, '"\\xCC"'], + [0315.chr, '"\\xCD"'], + [0316.chr, '"\\xCE"'], + [0317.chr, '"\\xCF"'], + [0320.chr, '"\\xD0"'], + [0321.chr, '"\\xD1"'], + [0322.chr, '"\\xD2"'], + [0323.chr, '"\\xD3"'], + [0324.chr, '"\\xD4"'], + [0325.chr, '"\\xD5"'], + [0326.chr, '"\\xD6"'], + [0327.chr, '"\\xD7"'], + [0330.chr, '"\\xD8"'], + [0331.chr, '"\\xD9"'], + [0332.chr, '"\\xDA"'], + [0333.chr, '"\\xDB"'], + [0334.chr, '"\\xDC"'], + [0335.chr, '"\\xDD"'], + [0336.chr, '"\\xDE"'], + [0337.chr, '"\\xDF"'], + [0340.chr, '"\\xE0"'], + [0341.chr, '"\\xE1"'], + [0342.chr, '"\\xE2"'], + [0343.chr, '"\\xE3"'], + [0344.chr, '"\\xE4"'], + [0345.chr, '"\\xE5"'], + [0346.chr, '"\\xE6"'], + [0347.chr, '"\\xE7"'], + [0350.chr, '"\\xE8"'], + [0351.chr, '"\\xE9"'], + [0352.chr, '"\\xEA"'], + [0353.chr, '"\\xEB"'], + [0354.chr, '"\\xEC"'], + [0355.chr, '"\\xED"'], + [0356.chr, '"\\xEE"'], + [0357.chr, '"\\xEF"'], + [0360.chr, '"\\xF0"'], + [0361.chr, '"\\xF1"'], + [0362.chr, '"\\xF2"'], + [0363.chr, '"\\xF3"'], + [0364.chr, '"\\xF4"'], + [0365.chr, '"\\xF5"'], + [0366.chr, '"\\xF6"'], + [0367.chr, '"\\xF7"'], + [0370.chr, '"\\xF8"'], + [0371.chr, '"\\xF9"'], + [0372.chr, '"\\xFA"'], + [0373.chr, '"\\xFB"'], + [0374.chr, '"\\xFC"'], + [0375.chr, '"\\xFD"'], + [0376.chr, '"\\xFE"'], + [0377.chr, '"\\xFF"'] + ].should be_computed_by(:dump) + end + + it "returns a string with non-printing single-byte UTF-8 characters replaced by \\x notation" do + [ [0000.chr('utf-8'), '"\x00"'], + [0001.chr('utf-8'), '"\x01"'], + [0002.chr('utf-8'), '"\x02"'], + [0003.chr('utf-8'), '"\x03"'], + [0004.chr('utf-8'), '"\x04"'], + [0005.chr('utf-8'), '"\x05"'], + [0006.chr('utf-8'), '"\x06"'], + [0016.chr('utf-8'), '"\x0E"'], + [0017.chr('utf-8'), '"\x0F"'], + [0020.chr('utf-8'), '"\x10"'], + [0021.chr('utf-8'), '"\x11"'], + [0022.chr('utf-8'), '"\x12"'], + [0023.chr('utf-8'), '"\x13"'], + [0024.chr('utf-8'), '"\x14"'], + [0025.chr('utf-8'), '"\x15"'], + [0026.chr('utf-8'), '"\x16"'], + [0027.chr('utf-8'), '"\x17"'], + [0030.chr('utf-8'), '"\x18"'], + [0031.chr('utf-8'), '"\x19"'], + [0032.chr('utf-8'), '"\x1A"'], + [0034.chr('utf-8'), '"\x1C"'], + [0035.chr('utf-8'), '"\x1D"'], + [0036.chr('utf-8'), '"\x1E"'], + [0037.chr('utf-8'), '"\x1F"'], + [0177.chr('utf-8'), '"\x7F"'] + ].should be_computed_by(:dump) + end + + ruby_version_is ''...'2.4' do + it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with lower-case hex digits" do + [ [0200.chr('utf-8'), '"\u{80}"'], + [0201.chr('utf-8'), '"\u{81}"'], + [0202.chr('utf-8'), '"\u{82}"'], + [0203.chr('utf-8'), '"\u{83}"'], + [0204.chr('utf-8'), '"\u{84}"'], + [0206.chr('utf-8'), '"\u{86}"'], + [0207.chr('utf-8'), '"\u{87}"'], + [0210.chr('utf-8'), '"\u{88}"'], + [0211.chr('utf-8'), '"\u{89}"'], + [0212.chr('utf-8'), '"\u{8a}"'], + [0213.chr('utf-8'), '"\u{8b}"'], + [0214.chr('utf-8'), '"\u{8c}"'], + [0215.chr('utf-8'), '"\u{8d}"'], + [0216.chr('utf-8'), '"\u{8e}"'], + [0217.chr('utf-8'), '"\u{8f}"'], + [0220.chr('utf-8'), '"\u{90}"'], + [0221.chr('utf-8'), '"\u{91}"'], + [0222.chr('utf-8'), '"\u{92}"'], + [0223.chr('utf-8'), '"\u{93}"'], + [0224.chr('utf-8'), '"\u{94}"'], + [0225.chr('utf-8'), '"\u{95}"'], + [0226.chr('utf-8'), '"\u{96}"'], + [0227.chr('utf-8'), '"\u{97}"'], + [0230.chr('utf-8'), '"\u{98}"'], + [0231.chr('utf-8'), '"\u{99}"'], + [0232.chr('utf-8'), '"\u{9a}"'], + [0233.chr('utf-8'), '"\u{9b}"'], + [0234.chr('utf-8'), '"\u{9c}"'], + [0235.chr('utf-8'), '"\u{9d}"'], + [0236.chr('utf-8'), '"\u{9e}"'], + [0237.chr('utf-8'), '"\u{9f}"'], + ].should be_computed_by(:dump) + end + end + + ruby_version_is '2.4' do + it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with lower-case hex digits" do + [ [0200.chr('utf-8'), '"\u0080"'], + [0201.chr('utf-8'), '"\u0081"'], + [0202.chr('utf-8'), '"\u0082"'], + [0203.chr('utf-8'), '"\u0083"'], + [0204.chr('utf-8'), '"\u0084"'], + [0206.chr('utf-8'), '"\u0086"'], + [0207.chr('utf-8'), '"\u0087"'], + [0210.chr('utf-8'), '"\u0088"'], + [0211.chr('utf-8'), '"\u0089"'], + [0212.chr('utf-8'), '"\u008A"'], + [0213.chr('utf-8'), '"\u008B"'], + [0214.chr('utf-8'), '"\u008C"'], + [0215.chr('utf-8'), '"\u008D"'], + [0216.chr('utf-8'), '"\u008E"'], + [0217.chr('utf-8'), '"\u008F"'], + [0220.chr('utf-8'), '"\u0090"'], + [0221.chr('utf-8'), '"\u0091"'], + [0222.chr('utf-8'), '"\u0092"'], + [0223.chr('utf-8'), '"\u0093"'], + [0224.chr('utf-8'), '"\u0094"'], + [0225.chr('utf-8'), '"\u0095"'], + [0226.chr('utf-8'), '"\u0096"'], + [0227.chr('utf-8'), '"\u0097"'], + [0230.chr('utf-8'), '"\u0098"'], + [0231.chr('utf-8'), '"\u0099"'], + [0232.chr('utf-8'), '"\u009A"'], + [0233.chr('utf-8'), '"\u009B"'], + [0234.chr('utf-8'), '"\u009C"'], + [0235.chr('utf-8'), '"\u009D"'], + [0236.chr('utf-8'), '"\u009E"'], + [0237.chr('utf-8'), '"\u009F"'], + ].should be_computed_by(:dump) + end + end + + it "includes .force_encoding(name) if the encoding isn't ASCII compatible" do + "\u{876}".encode('utf-16be').dump.should == "\"\\bv\".force_encoding(\"UTF-16BE\")" + "\u{876}".encode('utf-16le').dump.should == "\"v\\b\".force_encoding(\"UTF-16LE\")" + end +end diff --git a/spec/rubyspec/core/string/dup_spec.rb b/spec/rubyspec/core/string/dup_spec.rb new file mode 100644 index 0000000000..81fb1308cc --- /dev/null +++ b/spec/rubyspec/core/string/dup_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "String#dup" do + before :each do + ScratchPad.clear + @obj = StringSpecs::InitializeString.new "string" + end + + it "calls #initialize_copy on the new instance" do + dup = @obj.dup + ScratchPad.recorded.should_not == @obj.object_id + ScratchPad.recorded.should == dup.object_id + end + + it "copies instance variables" do + dup = @obj.dup + dup.ivar.should == 1 + end + + it "does not copy singleton methods" do + def @obj.special() :the_one end + dup = @obj.dup + lambda { dup.special }.should raise_error(NameError) + end + + it "does not copy modules included in the singleton class" do + class << @obj + include StringSpecs::StringModule + end + + dup = @obj.dup + lambda { dup.repr }.should raise_error(NameError) + end + + it "does not copy constants defined in the singleton class" do + class << @obj + CLONE = :clone + end + + dup = @obj.dup + lambda { class << dup; CLONE; end }.should raise_error(NameError) + end + + it "does not modify the original string when changing dupped string" do + orig = "string"[0..100] + dup = orig.dup + orig[0] = 'x' + orig.should == "xtring" + dup.should == "string" + end +end diff --git a/spec/rubyspec/core/string/each_byte_spec.rb b/spec/rubyspec/core/string/each_byte_spec.rb new file mode 100644 index 0000000000..2282cd6d7a --- /dev/null +++ b/spec/rubyspec/core/string/each_byte_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#each_byte" do + it "passes each byte in self to the given block" do + a = [] + "hello\x00".each_byte { |c| a << c } + a.should == [104, 101, 108, 108, 111, 0] + end + + it "keeps iterating from the old position (to new string end) when self changes" do + r = "" + s = "hello world" + s.each_byte do |c| + r << c + s.insert(0, "<>") if r.size < 3 + end + r.should == "h><>hello world" + + r = "" + s = "hello world" + s.each_byte { |c| s.slice!(-1); r << c } + r.should == "hello " + + r = "" + s = "hello world" + s.each_byte { |c| s.slice!(0); r << c } + r.should == "hlowrd" + + r = "" + s = "hello world" + s.each_byte { |c| s.slice!(0..-1); r << c } + r.should == "h" + end + + it "returns self" do + s = "hello" + (s.each_byte {}).should equal(s) + end + + describe "when no block is given" do + it "returns an enumerator" do + enum = "hello".each_byte + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == [104, 101, 108, 108, 111] + end + + describe "returned enumerator" do + describe "size" do + it "should return the bytesize of the string" do + str = "hello" + str.each_byte.size.should == str.bytesize + str = "ola" + str.each_byte.size.should == str.bytesize + str = "\303\207\342\210\202\303\251\306\222g" + str.each_byte.size.should == str.bytesize + end + end + end + end +end diff --git a/spec/rubyspec/core/string/each_char_spec.rb b/spec/rubyspec/core/string/each_char_spec.rb new file mode 100644 index 0000000000..3233c7609d --- /dev/null +++ b/spec/rubyspec/core/string/each_char_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../shared/chars', __FILE__) +require File.expand_path('../shared/each_char_without_block', __FILE__) + +describe "String#each_char" do + it_behaves_like(:string_chars, :each_char) + it_behaves_like(:string_each_char_without_block, :each_char) +end diff --git a/spec/rubyspec/core/string/each_codepoint_spec.rb b/spec/rubyspec/core/string/each_codepoint_spec.rb new file mode 100644 index 0000000000..4e910f44b5 --- /dev/null +++ b/spec/rubyspec/core/string/each_codepoint_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/codepoints', __FILE__) +require File.expand_path('../shared/each_codepoint_without_block', __FILE__) + +with_feature :encoding do + describe "String#each_codepoint" do + it_behaves_like(:string_codepoints, :each_codepoint) + it_behaves_like(:string_each_codepoint_without_block, :each_codepoint) + end +end diff --git a/spec/rubyspec/core/string/each_line_spec.rb b/spec/rubyspec/core/string/each_line_spec.rb new file mode 100644 index 0000000000..865ae264d6 --- /dev/null +++ b/spec/rubyspec/core/string/each_line_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_line', __FILE__) +require File.expand_path('../shared/each_line_without_block', __FILE__) + +describe "String#each_line" do + it_behaves_like(:string_each_line, :each_line) + it_behaves_like(:string_each_line_without_block, :each_line) +end diff --git a/spec/rubyspec/core/string/element_reference_spec.rb b/spec/rubyspec/core/string/element_reference_spec.rb new file mode 100644 index 0000000000..785bbdaf80 --- /dev/null +++ b/spec/rubyspec/core/string/element_reference_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/slice.rb', __FILE__) + +describe "String#[]" do + it_behaves_like :string_slice, :[] +end + +describe "String#[] with index, length" do + it_behaves_like :string_slice_index_length, :[] +end + +describe "String#[] with Range" do + it_behaves_like :string_slice_range, :[] +end + +describe "String#[] with Regexp" do + it_behaves_like :string_slice_regexp, :[] +end + +describe "String#[] with Regexp, index" do + it_behaves_like :string_slice_regexp_index, :[] +end + +describe "String#[] with Regexp, group" do + it_behaves_like :string_slice_regexp_group, :[] +end + +describe "String#[] with String" do + it_behaves_like :string_slice_string, :[] +end + +describe "String#[] with Symbol" do + it_behaves_like :string_slice_symbol, :[] +end diff --git a/spec/rubyspec/core/string/element_set_spec.rb b/spec/rubyspec/core/string/element_set_spec.rb new file mode 100644 index 0000000000..fea03607f2 --- /dev/null +++ b/spec/rubyspec/core/string/element_set_spec.rb @@ -0,0 +1,612 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +# TODO: Add missing String#[]= specs: +# String#[re, idx] = obj + +describe "String#[]= with Fixnum index" do + it "replaces the char at idx with other_str" do + a = "hello" + a[0] = "bam" + a.should == "bamello" + a[-2] = "" + a.should == "bamelo" + end + + it "taints self if other_str is tainted" do + a = "hello" + a[0] = "".taint + a.tainted?.should == true + + a = "hello" + a[0] = "x".taint + a.tainted?.should == true + end + + it "raises an IndexError without changing self if idx is outside of self" do + str = "hello" + + lambda { str[20] = "bam" }.should raise_error(IndexError) + str.should == "hello" + + lambda { str[-20] = "bam" }.should raise_error(IndexError) + str.should == "hello" + + lambda { ""[-1] = "bam" }.should raise_error(IndexError) + end + + # Behaviour verfieid correct by matz in + # http://redmine.ruby-lang.org/issues/show/1750 + it "allows assignment to the zero'th element of an empty String" do + str = "" + str[0] = "bam" + str.should == "bam" + end + + it "raises IndexError if the string index doesn't match a position in the string" do + str = "hello" + lambda { str['y'] = "bam" }.should raise_error(IndexError) + str.should == "hello" + end + + it "raises a RuntimeError when self is frozen" do + a = "hello" + a.freeze + + lambda { a[0] = "bam" }.should raise_error(RuntimeError) + end + + it "calls to_int on index" do + str = "hello" + str[0.5] = "hi " + str.should == "hi ello" + + obj = mock('-1') + obj.should_receive(:to_int).and_return(-1) + str[obj] = "!" + str.should == "hi ell!" + end + + it "calls #to_str to convert other to a String" do + other_str = mock('-test-') + other_str.should_receive(:to_str).and_return("-test-") + + a = "abc" + a[1] = other_str + a.should == "a-test-c" + end + + it "raises a TypeError if other_str can't be converted to a String" do + lambda { "test"[1] = [] }.should raise_error(TypeError) + lambda { "test"[1] = mock('x') }.should raise_error(TypeError) + lambda { "test"[1] = nil }.should raise_error(TypeError) + end + + with_feature :encoding do + it "raises a TypeError if passed a Fixnum replacement" do + lambda { "abc"[1] = 65 }.should raise_error(TypeError) + end + + it "raises an IndexError if the index is greater than character size" do + lambda { "あれ"[4] = "a" }.should raise_error(IndexError) + end + + it "calls #to_int to convert the index" do + index = mock("string element set") + index.should_receive(:to_int).and_return(1) + + str = "あれ" + str[index] = "a" + str.should == "あa" + end + + it "raises a TypeError if #to_int does not return an Fixnum" do + index = mock("string element set") + index.should_receive(:to_int).and_return('1') + + lambda { "abc"[index] = "d" }.should raise_error(TypeError) + end + + it "raises an IndexError if #to_int returns a value out of range" do + index = mock("string element set") + index.should_receive(:to_int).and_return(4) + + lambda { "ab"[index] = "c" }.should raise_error(IndexError) + end + + it "replaces a character with a multibyte character" do + str = "ありがとu" + str[4] = "う" + str.should == "ありがとう" + end + + it "replaces a multibyte character with a character" do + str = "ありがとう" + str[4] = "u" + str.should == "ありがとu" + end + + it "replaces a multibyte character with a multibyte character" do + str = "ありがとお" + str[4] = "う" + str.should == "ありがとう" + end + + it "encodes the String in an encoding compatible with the replacement" do + str = " ".force_encoding Encoding::US_ASCII + rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT + str[0] = rep + str.encoding.should equal(Encoding::ASCII_8BIT) + end + + it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do + str = "あれ" + rep = "が".encode Encoding::EUC_JP + lambda { str[0] = rep }.should raise_error(Encoding::CompatibilityError) + end + end +end + +describe "String#[]= with String index" do + it "replaces fewer characters with more characters" do + str = "abcde" + str["cd"] = "ghi" + str.should == "abghie" + end + + it "replaces more characters with fewer characters" do + str = "abcde" + str["bcd"] = "f" + str.should == "afe" + end + + it "replaces characters with no characters" do + str = "abcde" + str["cd"] = "" + str.should == "abe" + end + + it "raises an IndexError if the search String is not found" do + str = "abcde" + lambda { str["g"] = "h" }.should raise_error(IndexError) + end + + with_feature :encoding do + it "replaces characters with a multibyte character" do + str = "ありgaとう" + str["ga"] = "が" + str.should == "ありがとう" + end + + it "replaces multibyte characters with characters" do + str = "ありがとう" + str["が"] = "ga" + str.should == "ありgaとう" + end + + it "replaces multibyte characters with multibyte characters" do + str = "ありがとう" + str["が"] = "か" + str.should == "ありかとう" + end + + it "encodes the String in an encoding compatible with the replacement" do + str = " ".force_encoding Encoding::US_ASCII + rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT + str[" "] = rep + str.encoding.should equal(Encoding::ASCII_8BIT) + end + + it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do + str = "あれ" + rep = "が".encode Encoding::EUC_JP + lambda { str["れ"] = rep }.should raise_error(Encoding::CompatibilityError) + end + end +end + +describe "String#[]= with a Regexp index" do + it "replaces the matched text with the rhs" do + str = "hello" + str[/lo/] = "x" + str.should == "helx" + end + + it "raises IndexError if the regexp index doesn't match a position in the string" do + str = "hello" + lambda { str[/y/] = "bam" }.should raise_error(IndexError) + str.should == "hello" + end + + it "calls #to_str to convert the replacement" do + rep = mock("string element set regexp") + rep.should_receive(:to_str).and_return("b") + + str = "abc" + str[/ab/] = rep + str.should == "bc" + end + + it "checks the match before calling #to_str to convert the replacement" do + rep = mock("string element set regexp") + rep.should_not_receive(:to_str) + + lambda { "abc"[/def/] = rep }.should raise_error(IndexError) + end + + describe "with 3 arguments" do + it "calls #to_int to convert the second object" do + ref = mock("string element set regexp ref") + ref.should_receive(:to_int).and_return(1) + + str = "abc" + str[/a(b)/, ref] = "x" + str.should == "axc" + end + + it "raises a TypeError if #to_int does not return a Fixnum" do + ref = mock("string element set regexp ref") + ref.should_receive(:to_int).and_return(nil) + + lambda { "abc"[/a(b)/, ref] = "x" }.should raise_error(TypeError) + end + + it "uses the 2nd of 3 arguments as which capture should be replaced" do + str = "aaa bbb ccc" + str[/a (bbb) c/, 1] = "ddd" + str.should == "aaa ddd ccc" + end + + it "allows the specified capture to be negative and count from the end" do + str = "abcd" + str[/(a)(b)(c)(d)/, -2] = "e" + str.should == "abed" + end + + it "checks the match index before calling #to_str to convert the replacement" do + rep = mock("string element set regexp") + rep.should_not_receive(:to_str) + + lambda { "abc"[/a(b)/, 2] = rep }.should raise_error(IndexError) + end + + it "raises IndexError if the specified capture isn't available" do + str = "aaa bbb ccc" + lambda { str[/a (bbb) c/, 2] = "ddd" }.should raise_error(IndexError) + lambda { str[/a (bbb) c/, -2] = "ddd" }.should raise_error(IndexError) + end + + describe "when the optional capture does not match" do + it "raises an IndexError before setting the replacement" do + str1 = "a b c" + str2 = str1.dup + lambda { str2[/a (b) (Z)?/, 2] = "d" }.should raise_error(IndexError) + str2.should == str1 + end + end + end + + with_feature :encoding do + it "replaces characters with a multibyte character" do + str = "ありgaとう" + str[/ga/] = "が" + str.should == "ありがとう" + end + + it "replaces multibyte characters with characters" do + str = "ありがとう" + str[/が/] = "ga" + str.should == "ありgaとう" + end + + it "replaces multibyte characters with multibyte characters" do + str = "ありがとう" + str[/が/] = "か" + str.should == "ありかとう" + end + + it "encodes the String in an encoding compatible with the replacement" do + str = " ".force_encoding Encoding::US_ASCII + rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT + str[/ /] = rep + str.encoding.should equal(Encoding::ASCII_8BIT) + end + + it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do + str = "あれ" + rep = "が".encode Encoding::EUC_JP + lambda { str[/れ/] = rep }.should raise_error(Encoding::CompatibilityError) + end + end +end + +describe "String#[]= with a Range index" do + describe "with an empty replacement" do + it "does not replace a character with a zero-index, zero exclude-end range" do + str = "abc" + str[0...0] = "" + str.should == "abc" + end + + it "does not replace a character with a zero exclude-end range" do + str = "abc" + str[1...1] = "" + str.should == "abc" + end + + it "replaces a character with zero-index, zero non-exclude-end range" do + str = "abc" + str[0..0] = "" + str.should == "bc" + end + + it "replaces a character with a zero non-exclude-end range" do + str = "abc" + str[1..1] = "" + str.should == "ac" + end + end + + it "replaces the contents with a shorter String" do + str = "abcde" + str[0..-1] = "hg" + str.should == "hg" + end + + it "replaces the contents with a longer String" do + str = "abc" + str[0...4] = "uvwxyz" + str.should == "uvwxyz" + end + + it "replaces a partial string" do + str = "abcde" + str[1..3] = "B" + str.should == "aBe" + end + + it "raises a RangeError if negative Range begin is out of range" do + lambda { "abc"[-4..-2] = "x" }.should raise_error(RangeError) + end + + it "raises a RangeError if positive Range begin is greater than String size" do + lambda { "abc"[4..2] = "x" }.should raise_error(RangeError) + end + + it "uses the Range end as an index rather than a count" do + str = "abcdefg" + str[-5..3] = "xyz" + str.should == "abxyzefg" + end + + it "treats a negative out-of-range Range end with a positive Range begin as a zero count" do + str = "abc" + str[1..-4] = "x" + str.should == "axbc" + end + + it "treats a negative out-of-range Range end with a negative Range begin as a zero count" do + str = "abcd" + str[-1..-4] = "x" + str.should == "abcxd" + end + + with_feature :encoding do + it "replaces characters with a multibyte character" do + str = "ありgaとう" + str[2..3] = "が" + str.should == "ありがとう" + end + + it "replaces multibyte characters with characters" do + str = "ありがとう" + str[2...3] = "ga" + str.should == "ありgaとう" + end + + it "replaces multibyte characters by negative indexes" do + str = "ありがとう" + str[-3...-2] = "ga" + str.should == "ありgaとう" + end + + it "replaces multibyte characters with multibyte characters" do + str = "ありがとう" + str[2..2] = "か" + str.should == "ありかとう" + end + + it "deletes a multibyte character" do + str = "ありとう" + str[2..3] = "" + str.should == "あり" + end + + it "inserts a multibyte character" do + str = "ありとう" + str[2...2] = "が" + str.should == "ありがとう" + end + + it "encodes the String in an encoding compatible with the replacement" do + str = " ".force_encoding Encoding::US_ASCII + rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT + str[0..1] = rep + str.encoding.should equal(Encoding::ASCII_8BIT) + end + + it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do + str = "あれ" + rep = "が".encode Encoding::EUC_JP + lambda { str[0..1] = rep }.should raise_error(Encoding::CompatibilityError) + end + end +end + +describe "String#[]= with Fixnum index, count" do + it "starts at idx and overwrites count characters before inserting the rest of other_str" do + a = "hello" + a[0, 2] = "xx" + a.should == "xxllo" + a = "hello" + a[0, 2] = "jello" + a.should == "jellollo" + end + + it "counts negative idx values from end of the string" do + a = "hello" + a[-1, 0] = "bob" + a.should == "hellbobo" + a = "hello" + a[-5, 0] = "bob" + a.should == "bobhello" + end + + it "overwrites and deletes characters if count is more than the length of other_str" do + a = "hello" + a[0, 4] = "x" + a.should == "xo" + a = "hello" + a[0, 5] = "x" + a.should == "x" + end + + it "deletes characters if other_str is an empty string" do + a = "hello" + a[0, 2] = "" + a.should == "llo" + end + + it "deletes characters up to the maximum length of the existing string" do + a = "hello" + a[0, 6] = "x" + a.should == "x" + a = "hello" + a[0, 100] = "" + a.should == "" + end + + it "appends other_str to the end of the string if idx == the length of the string" do + a = "hello" + a[5, 0] = "bob" + a.should == "hellobob" + end + + it "taints self if other_str is tainted" do + a = "hello" + a[0, 0] = "".taint + a.tainted?.should == true + + a = "hello" + a[1, 4] = "x".taint + a.tainted?.should == true + end + + it "calls #to_int to convert the index and count objects" do + index = mock("string element set index") + index.should_receive(:to_int).and_return(-4) + + count = mock("string element set count") + count.should_receive(:to_int).and_return(2) + + str = "abcde" + str[index, count] = "xyz" + str.should == "axyzde" + end + + it "raises a TypeError if #to_int for index does not return an Integer" do + index = mock("string element set index") + index.should_receive(:to_int).and_return("1") + + lambda { "abc"[index, 2] = "xyz" }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_int for count does not return an Integer" do + count = mock("string element set count") + count.should_receive(:to_int).and_return("1") + + lambda { "abc"[1, count] = "xyz" }.should raise_error(TypeError) + end + + it "calls #to_str to convert the replacement object" do + r = mock("string element set replacement") + r.should_receive(:to_str).and_return("xyz") + + str = "abcde" + str[2, 2] = r + str.should == "abxyze" + end + + it "raises a TypeError of #to_str does not return a String" do + r = mock("string element set replacement") + r.should_receive(:to_str).and_return(nil) + + lambda { "abc"[1, 1] = r }.should raise_error(TypeError) + end + + it "raises an IndexError if |idx| is greater than the length of the string" do + lambda { "hello"[6, 0] = "bob" }.should raise_error(IndexError) + lambda { "hello"[-6, 0] = "bob" }.should raise_error(IndexError) + end + + it "raises an IndexError if count < 0" do + lambda { "hello"[0, -1] = "bob" }.should raise_error(IndexError) + lambda { "hello"[1, -1] = "bob" }.should raise_error(IndexError) + end + + it "raises a TypeError if other_str is a type other than String" do + lambda { "hello"[0, 2] = nil }.should raise_error(TypeError) + lambda { "hello"[0, 2] = [] }.should raise_error(TypeError) + lambda { "hello"[0, 2] = 33 }.should raise_error(TypeError) + end + + with_feature :encoding do + it "replaces characters with a multibyte character" do + str = "ありgaとう" + str[2, 2] = "が" + str.should == "ありがとう" + end + + it "replaces multibyte characters with characters" do + str = "ありがとう" + str[2, 1] = "ga" + str.should == "ありgaとう" + end + + it "replaces multibyte characters with multibyte characters" do + str = "ありがとう" + str[2, 1] = "か" + str.should == "ありかとう" + end + + it "deletes a multibyte character" do + str = "ありとう" + str[2, 2] = "" + str.should == "あり" + end + + it "inserts a multibyte character" do + str = "ありとう" + str[2, 0] = "が" + str.should == "ありがとう" + end + + it "raises an IndexError if the character index is out of range of a multibyte String" do + lambda { "あれ"[3, 0] = "り" }.should raise_error(IndexError) + end + + it "encodes the String in an encoding compatible with the replacement" do + str = " ".force_encoding Encoding::US_ASCII + rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT + str[0, 1] = rep + str.encoding.should equal(Encoding::ASCII_8BIT) + end + + it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do + str = "あれ" + rep = "が".encode Encoding::EUC_JP + lambda { str[0, 1] = rep }.should raise_error(Encoding::CompatibilityError) + end + end +end diff --git a/spec/rubyspec/core/string/empty_spec.rb b/spec/rubyspec/core/string/empty_spec.rb new file mode 100644 index 0000000000..c13e1145e4 --- /dev/null +++ b/spec/rubyspec/core/string/empty_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#empty?" do + it "returns true if the string has a length of zero" do + "hello".empty?.should == false + " ".empty?.should == false + "\x00".empty?.should == false + "".empty?.should == true + StringSpecs::MyString.new("").empty?.should == true + end +end diff --git a/spec/rubyspec/core/string/encode_spec.rb b/spec/rubyspec/core/string/encode_spec.rb new file mode 100644 index 0000000000..0ec592acd3 --- /dev/null +++ b/spec/rubyspec/core/string/encode_spec.rb @@ -0,0 +1,159 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/encode', __FILE__) + +with_feature :encoding do + describe "String#encode" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it_behaves_like :string_encode, :encode + + describe "when passed no options" do + it "returns a copy when Encoding.default_internal is nil" do + Encoding.default_internal = nil + str = "あ" + str.encode.should_not equal(str) + end + + it "returns a copy for a ASCII-only String when Encoding.default_internal is nil" do + Encoding.default_internal = nil + str = "abc" + str.encode.should_not equal(str) + end + + it "encodes an ascii substring of a binary string to UTF-8" do + x82 = [0x82].pack('C') + str = "#{x82}foo".force_encoding("ascii-8bit")[1..-1].encode("utf-8") + str.should == "foo".force_encoding("utf-8") + str.encoding.should equal(Encoding::UTF_8) + end + end + + describe "when passed to encoding" do + it "returns a copy when passed the same encoding as the String" do + str = "あ" + str.encode(Encoding::UTF_8).should_not equal(str) + end + + it "round trips a String" do + str = "abc def".force_encoding Encoding::US_ASCII + str.encode("utf-32be").encode("ascii").should == "abc def" + end + end + + describe "when passed options" do + it "returns a copy when Encoding.default_internal is nil" do + Encoding.default_internal = nil + str = "あ" + str.encode(invalid: :replace).should_not equal(str) + end + + it "normalizes newlines" do + "\r\nfoo".encode(universal_newline: true).should == "\nfoo" + + "\rfoo".encode(universal_newline: true).should == "\nfoo" + end + end + + describe "when passed to, from" do + it "returns a copy in the destination encoding when both encodings are the same" do + str = "あ" + str.force_encoding("ascii-8bit") + encoded = str.encode("utf-8", "utf-8") + + encoded.should_not equal(str) + encoded.encoding.should == Encoding::UTF_8 + end + + it "returns the transcoded string" do + str = "\x00\x00\x00\x1F" + str.encode(Encoding::UTF_8, Encoding::UTF_16BE).should == "\u0000\u001f" + end + end + + describe "when passed to, options" do + it "returns a copy when the destination encoding is the same as the String encoding" do + str = "あ" + str.encode(Encoding::UTF_8, undef: :replace).should_not equal(str) + end + end + + describe "when passed to, from, options" do + it "returns a copy when both encodings are the same" do + str = "あ" + str.encode("utf-8", "utf-8", invalid: :replace).should_not equal(str) + end + end + end + + describe "String#encode!" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + it_behaves_like :string_encode, :encode! + + it "raises a RuntimeError when called on a frozen String" do + lambda { "foo".freeze.encode!("euc-jp") }.should raise_error(RuntimeError) + end + + # http://redmine.ruby-lang.org/issues/show/1836 + it "raises a RuntimeError when called on a frozen String when it's a no-op" do + lambda { "foo".freeze.encode!("utf-8") }.should raise_error(RuntimeError) + end + + describe "when passed no options" do + it "returns self when Encoding.default_internal is nil" do + Encoding.default_internal = nil + str = "あ" + str.encode!.should equal(str) + end + + it "returns self for a ASCII-only String when Encoding.default_internal is nil" do + Encoding.default_internal = nil + str = "abc" + str.encode!.should equal(str) + end + end + + describe "when passed options" do + it "returns self for ASCII-only String when Encoding.default_internal is nil" do + Encoding.default_internal = nil + str = "abc" + str.encode!(invalid: :replace).should equal(str) + end + end + + describe "when passed to encoding" do + it "returns self" do + str = "abc" + result = str.encode!(Encoding::BINARY) + result.encoding.should equal(Encoding::BINARY) + result.should equal(str) + end + end + + describe "when passed to, from" do + it "returns self" do + str = "ああ" + result = str.encode!("euc-jp", "utf-8") + result.encoding.should equal(Encoding::EUC_JP) + result.should equal(str) + end + end + end +end diff --git a/spec/rubyspec/core/string/encoding_spec.rb b/spec/rubyspec/core/string/encoding_spec.rb new file mode 100644 index 0000000000..a07032b041 --- /dev/null +++ b/spec/rubyspec/core/string/encoding_spec.rb @@ -0,0 +1,189 @@ +# -*- encoding: us-ascii -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/iso-8859-9-encoding', __FILE__) + +with_feature :encoding do + describe "String#encoding" do + it "returns an Encoding object" do + String.new.encoding.should be_an_instance_of(Encoding) + end + + it "is equal to the source encoding by default" do + s = StringSpecs::ISO88599Encoding.new + s.cedilla.encoding.should == s.source_encoding + end + + it "returns the given encoding if #force_encoding has been called" do + "a".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + end + + it "returns the given encoding if #encode!has been called" do + "a".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + end + end + + describe "String#encoding for US-ASCII Strings" do + it "returns US-ASCII if self is US-ASCII" do + "a".encoding.should == Encoding::US_ASCII + end + + it "returns US-ASCII if self is US-ASCII only, despite the default internal encoding being different" do + default_internal = Encoding.default_internal + Encoding.default_internal = Encoding::UTF_8 + "a".encoding.should == Encoding::US_ASCII + Encoding.default_internal = default_internal + end + + it "returns US-ASCII if self is US-ASCII only, despite the default external encoding being different" do + default_external = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + "a".encoding.should == Encoding::US_ASCII + Encoding.default_external = default_external + end + + it "returns US-ASCII if self is US-ASCII only, despite the default internal and external encodings being different" do + default_internal = Encoding.default_internal + default_external = Encoding.default_external + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::UTF_8 + "a".encoding.should == Encoding::US_ASCII + Encoding.default_external = default_external + Encoding.default_internal = default_internal + end + + it "returns US-ASCII if self is US-ASCII only, despite the default encodings being different" do + default_internal = Encoding.default_internal + default_external = Encoding.default_external + Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::UTF_8 + "a".encoding.should == Encoding::US_ASCII + Encoding.default_external = default_external + Encoding.default_internal = default_internal + end + + end + + describe "String#encoding for Strings with \\u escapes" do + it "returns UTF-8" do + "\u{4040}".encoding.should == Encoding::UTF_8 + end + + it "returns US-ASCII if self is US-ASCII only" do + s = "\u{40}" + s.ascii_only?.should be_true + s.encoding.should == Encoding::US_ASCII + end + + it "returns UTF-8 if self isn't US-ASCII only" do + s = "\u{4076}\u{619}" + s.ascii_only?.should be_false + s.encoding.should == Encoding::UTF_8 + end + + it "is not affected by the default internal encoding" do + default_internal = Encoding.default_internal + Encoding.default_internal = Encoding::ISO_8859_15 + "\u{5050}".encoding.should == Encoding::UTF_8 + "\u{50}".encoding.should == Encoding::US_ASCII + Encoding.default_internal = default_internal + end + + it "is not affected by the default external encoding" do + default_external = Encoding.default_external + Encoding.default_external = Encoding::SHIFT_JIS + "\u{50}".encoding.should == Encoding::US_ASCII + "\u{5050}".encoding.should == Encoding::UTF_8 + Encoding.default_external = default_external + end + + it "is not affected by both the default internal and external encoding being set at the same time" do + default_internal = Encoding.default_internal + default_external = Encoding.default_external + Encoding.default_internal = Encoding::EUC_JP + Encoding.default_external = Encoding::SHIFT_JIS + "\u{50}".encoding.should == Encoding::US_ASCII + "\u{507}".encoding.should == Encoding::UTF_8 + Encoding.default_external = default_external + Encoding.default_internal = default_internal + end + + it "returns the given encoding if #force_encoding has been called" do + "\u{20}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + "\u{2020}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + end + + it "returns the given encoding if #encode!has been called" do + "\u{20}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + "\u{2020}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + end + end + + describe "String#encoding for Strings with \\x escapes" do + + it "returns US-ASCII if self is US-ASCII only" do + s = "\x61" + s.ascii_only?.should be_true + s.encoding.should == Encoding::US_ASCII + end + + it "returns ASCII-8BIT when an escape creates a byte with the 8th bit set if the source encoding is US-ASCII" do + __ENCODING__.should == Encoding::US_ASCII + str = " " + str.encoding.should == Encoding::US_ASCII + str += [0xDF].pack('C') + str.ascii_only?.should be_false + str.encoding.should == Encoding::ASCII_8BIT + end + + # TODO: Deal with case when the byte in question isn't valid in the source + # encoding? + it "returns the source encoding when an escape creates a byte with the 8th bit set if the source encoding isn't US-ASCII" do + fixture = StringSpecs::ISO88599Encoding.new + fixture.source_encoding.should == Encoding::ISO8859_9 + fixture.x_escape.ascii_only?.should be_false + fixture.x_escape.encoding.should == Encoding::ISO8859_9 + end + + it "is not affected by the default internal encoding" do + default_internal = Encoding.default_internal + Encoding.default_internal = Encoding::ISO_8859_15 + "\x50".encoding.should == Encoding::US_ASCII + "\x50".encoding.should == Encoding::US_ASCII + Encoding.default_internal = default_internal + end + + it "is not affected by the default external encoding" do + default_external = Encoding.default_external + Encoding.default_external = Encoding::SHIFT_JIS + "\x50".encoding.should == Encoding::US_ASCII + [0xD4].pack('C').encoding.should == Encoding::ASCII_8BIT + Encoding.default_external = default_external + end + + it "is not affected by both the default internal and external encoding being set at the same time" do + default_internal = Encoding.default_internal + default_external = Encoding.default_external + Encoding.default_internal = Encoding::EUC_JP + Encoding.default_external = Encoding::SHIFT_JIS + x50 = "\x50" + x50.encoding.should == Encoding::US_ASCII + [0xD4].pack('C').encoding.should == Encoding::ASCII_8BIT + Encoding.default_external = default_external + Encoding.default_internal = default_internal + end + + it "returns the given encoding if #force_encoding has been called" do + x50 = "\x50" + x50.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + xD4 = [212].pack('C') + xD4.force_encoding(Encoding::ISO_8859_9).encoding.should == Encoding::ISO_8859_9 + end + + it "returns the given encoding if #encode!has been called" do + x50 = "\x50" + x50.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS + x00 = "x\00" + x00.encode!(Encoding::UTF_8).encoding.should == Encoding::UTF_8 + end + end +end diff --git a/spec/rubyspec/core/string/end_with_spec.rb b/spec/rubyspec/core/string/end_with_spec.rb new file mode 100644 index 0000000000..2c3ff07272 --- /dev/null +++ b/spec/rubyspec/core/string/end_with_spec.rb @@ -0,0 +1,50 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#end_with?" do + it "returns true only if ends match" do + s = "hello" + s.end_with?('o').should be_true + s.end_with?('llo').should be_true + end + + it 'returns false if the end does not match' do + s = 'hello' + s.end_with?('ll').should be_false + end + + it "returns true if the search string is empty" do + "hello".end_with?("").should be_true + "".end_with?("").should be_true + end + + it "returns true only if any ending match" do + "hello".end_with?('x', 'y', 'llo', 'z').should be_true + end + + it "converts its argument using :to_str" do + s = "hello" + find = mock('o') + find.should_receive(:to_str).and_return("o") + s.end_with?(find).should be_true + end + + it "ignores arguments not convertible to string" do + "hello".end_with?().should be_false + lambda { "hello".end_with?(1) }.should raise_error(TypeError) + lambda { "hello".end_with?(["o"]) }.should raise_error(TypeError) + lambda { "hello".end_with?(1, nil, "o") }.should raise_error(TypeError) + end + + it "uses only the needed arguments" do + find = mock('h') + find.should_not_receive(:to_str) + "hello".end_with?("o",find).should be_true + end + + it "works for multibyte strings" do + "céréale".end_with?("réale").should be_true + end + +end diff --git a/spec/rubyspec/core/string/eql_spec.rb b/spec/rubyspec/core/string/eql_spec.rb new file mode 100644 index 0000000000..df094e122f --- /dev/null +++ b/spec/rubyspec/core/string/eql_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql', __FILE__) + +describe "String#eql?" do + it_behaves_like(:string_eql_value, :eql?) + + describe "when given a non-String" do + it "returns false" do + 'hello'.should_not eql(5) + not_supported_on :opal do + 'hello'.should_not eql(:hello) + end + 'hello'.should_not eql(mock('x')) + end + + it "does not try to call #to_str on the given argument" do + (obj = mock('x')).should_not_receive(:to_str) + 'hello'.should_not eql(obj) + end + end +end diff --git a/spec/rubyspec/core/string/equal_value_spec.rb b/spec/rubyspec/core/string/equal_value_spec.rb new file mode 100644 index 0000000000..bf252d6d30 --- /dev/null +++ b/spec/rubyspec/core/string/equal_value_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "String#==" do + it_behaves_like(:string_eql_value, :==) + it_behaves_like(:string_equal_value, :==) +end diff --git a/spec/rubyspec/core/string/fixtures/classes.rb b/spec/rubyspec/core/string/fixtures/classes.rb new file mode 100644 index 0000000000..6af106f9d3 --- /dev/null +++ b/spec/rubyspec/core/string/fixtures/classes.rb @@ -0,0 +1,49 @@ +class Object + # This helper is defined here rather than in MSpec because + # it is only used in #unpack specs. + def unpack_format(count=nil, repeat=nil) + format = "#{instance_variable_get(:@method)}#{count}" + format *= repeat if repeat + format + end +end + +module StringSpecs + class MyString < String; end + class MyArray < Array; end + class MyRange < Range; end + + class SubString < String + attr_reader :special + + def initialize(str=nil) + @special = str + end + end + + class InitializeString < String + attr_reader :ivar + + def initialize(other) + super + @ivar = 1 + end + + def initialize_copy(other) + ScratchPad.record object_id + end + end + + module StringModule + def repr + 1 + end + end + + class StringWithRaisingConstructor < String + def initialize(str) + raise ArgumentError.new('constructor was called') unless str == 'silly:string' + self.replace(str) + end + end +end diff --git a/spec/rubyspec/core/string/fixtures/freeze_magic_comment.rb b/spec/rubyspec/core/string/fixtures/freeze_magic_comment.rb new file mode 100644 index 0000000000..2b87a16328 --- /dev/null +++ b/spec/rubyspec/core/string/fixtures/freeze_magic_comment.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +print (+ 'frozen string').frozen? ? 'immutable' : 'mutable' diff --git a/spec/rubyspec/core/string/fixtures/iso-8859-9-encoding.rb b/spec/rubyspec/core/string/fixtures/iso-8859-9-encoding.rb new file mode 100644 index 0000000000..61a691ff78 --- /dev/null +++ b/spec/rubyspec/core/string/fixtures/iso-8859-9-encoding.rb @@ -0,0 +1,9 @@ +# -*- encoding: iso-8859-9 -*- +module StringSpecs + class ISO88599Encoding + def source_encoding; __ENCODING__; end + def x_escape; [0xDF].pack('C').force_encoding("iso-8859-9"); end + def ascii_only; "glark"; end + def cedilla; "Ş"; end + end +end diff --git a/spec/rubyspec/core/string/fixtures/utf-8-encoding.rb b/spec/rubyspec/core/string/fixtures/utf-8-encoding.rb new file mode 100644 index 0000000000..fd243ec522 --- /dev/null +++ b/spec/rubyspec/core/string/fixtures/utf-8-encoding.rb @@ -0,0 +1,7 @@ +# -*- encoding: utf-8 -*- +module StringSpecs + class UTF8Encoding + def self.source_encoding; __ENCODING__; end + def self.egrave; "é"; end + end +end diff --git a/spec/rubyspec/core/string/force_encoding_spec.rb b/spec/rubyspec/core/string/force_encoding_spec.rb new file mode 100644 index 0000000000..d163c75ac3 --- /dev/null +++ b/spec/rubyspec/core/string/force_encoding_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "String#force_encoding" do + it "accepts a String as the name of an Encoding" do + "abc".force_encoding('shift_jis').encoding.should == Encoding::Shift_JIS + end + + it "accepts an Encoding instance" do + "abc".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::Shift_JIS + end + + it "calls #to_str to convert an object to an encoding name" do + obj = mock("force_encoding") + obj.should_receive(:to_str).and_return("utf-8") + + "abc".force_encoding(obj).encoding.should == Encoding::UTF_8 + end + + it "raises a TypeError if #to_str does not return a String" do + obj = mock("force_encoding") + obj.should_receive(:to_str).and_return(1) + + lambda { "abc".force_encoding(obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed nil" do + lambda { "abc".force_encoding(nil) }.should raise_error(TypeError) + end + + it "returns self" do + str = "abc" + str.force_encoding('utf-8').should equal(str) + end + + it "sets the encoding even if the String contents are invalid in that encoding" do + str = "\u{9765}" + str.force_encoding('euc-jp') + str.encoding.should == Encoding::EUC_JP + str.valid_encoding?.should be_false + end + + it "does not transcode self" do + str = "\u{8612}" + str.dup.force_encoding('utf-16le').should_not == str.encode('utf-16le') + end + + it "raises a RuntimeError if self is frozen" do + str = "abcd".freeze + lambda { str.force_encoding(str.encoding) }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/string/freeze_spec.rb b/spec/rubyspec/core/string/freeze_spec.rb new file mode 100644 index 0000000000..bd7c2fbc73 --- /dev/null +++ b/spec/rubyspec/core/string/freeze_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#freeze" do + + it "produces the same object whenever called on an instance of a literal in the source" do + ids = Array.new(2) { "abc".freeze.object_id } + ids.first.should == ids.last + end + + it "doesn't produce the same object for different instances of literals in the source" do + "abc".object_id.should_not == "abc".object_id + end + + it "being a special form doesn't change the value of defined?" do + defined?("abc".freeze).should == "method" + end + +end diff --git a/spec/rubyspec/core/string/getbyte_spec.rb b/spec/rubyspec/core/string/getbyte_spec.rb new file mode 100644 index 0000000000..b46ab1ab64 --- /dev/null +++ b/spec/rubyspec/core/string/getbyte_spec.rb @@ -0,0 +1,69 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#getbyte" do + it "returns an Integer if given a valid index" do + "a".getbyte(0).should be_kind_of(Integer) + end + + it "starts indexing at 0" do + "b".getbyte(0).should == 98 + + # copy-on-write case + _str1, str2 = "fooXbar".split("X") + str2.getbyte(0).should == 98 + end + + it "counts from the end of the String if given a negative argument" do + "glark".getbyte(-1).should == "glark".getbyte(4) + + # copy-on-write case + _str1, str2 = "fooXbar".split("X") + str2.getbyte(-1).should == 114 + end + + it "returns an Integer between 0 and 255" do + "\x00".getbyte(0).should == 0 + [0xFF].pack('C').getbyte(0).should == 255 + 256.chr('utf-8').getbyte(0).should == 196 + 256.chr('utf-8').getbyte(1).should == 128 + end + + it "regards a multi-byte character as having multiple bytes" do + chr = "\u{998}" + chr.bytesize.should == 3 + chr.getbyte(0).should == 224 + chr.getbyte(1).should == 166 + chr.getbyte(2).should == 152 + end + + it "mirrors the output of #bytes" do + xDE = [0xDE].pack('C').force_encoding('utf-8') + str = "UTF-8 (\u{9865}} characters and hex escapes (#{xDE})" + str.bytes.to_a.each_with_index do |byte, index| + str.getbyte(index).should == byte + end + end + + it "interprets bytes relative to the String's encoding" do + str = "\u{333}" + str.encode('utf-8').getbyte(0).should_not == str.encode('utf-16le').getbyte(0) + end + + it "returns nil for out-of-bound indexes" do + "g".getbyte(1).should be_nil + end + + it "regards the empty String as containing no bytes" do + "".getbyte(0).should be_nil + end + + it "raises an ArgumentError unless given one argument" do + lambda { "glark".getbyte }.should raise_error(ArgumentError) + lambda { "food".getbyte(0,0) }.should raise_error(ArgumentError) + end + + it "raises a TypeError unless its argument can be coerced into an Integer" do + lambda { "a".getbyte('a') }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/string/gsub_spec.rb b/spec/rubyspec/core/string/gsub_spec.rb new file mode 100644 index 0000000000..026b037b6c --- /dev/null +++ b/spec/rubyspec/core/string/gsub_spec.rb @@ -0,0 +1,696 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe :string_gsub_named_capture, shared: true do + it "replaces \\k named backreferences with the regexp's corresponding capture" do + str = "hello" + + str.gsub(/(?[aeiou])/, '<\k>').should == "hll" + str.gsub(/(?.)/, '\k\k').should == "hheelllloo" + end +end + +describe "String#gsub with pattern and replacement" do + it "inserts the replacement around every character when the pattern collapses" do + "hello".gsub(//, ".").should == ".h.e.l.l.o." + end + + it "respects unicode when the pattern collapses" do + str = "こにちわ" + reg = %r!! + + str.gsub(reg, ".").should == ".こ.に.ち.わ." + end + + it "doesn't freak out when replacing ^" do + "Text\n".gsub(/^/, ' ').should == " Text\n" + "Text\nFoo".gsub(/^/, ' ').should == " Text\n Foo" + end + + it "returns a copy of self with all occurrences of pattern replaced with replacement" do + "hello".gsub(/[aeiou]/, '*').should == "h*ll*" + + str = "hello homely world. hah!" + str.gsub(/\Ah\S+\s*/, "huh? ").should == "huh? homely world. hah!" + + str = "¿por qué?" + str.gsub(/([a-z\d]*)/, "*").should == "*¿** **é*?*" + end + + it "ignores a block if supplied" do + "food".gsub(/f/, "g") { "w" }.should == "good" + end + + it "supports \\G which matches at the beginning of the remaining (non-matched) string" do + str = "hello homely world. hah!" + str.gsub(/\Gh\S+\s*/, "huh? ").should == "huh? huh? world. hah!" + end + + it "supports /i for ignoring case" do + str = "Hello. How happy are you?" + str.gsub(/h/i, "j").should == "jello. jow jappy are you?" + str.gsub(/H/i, "j").should == "jello. jow jappy are you?" + end + + it "doesn't interpret regexp metacharacters if pattern is a string" do + "12345".gsub('\d', 'a').should == "12345" + '\d'.gsub('\d', 'a').should == "a" + end + + it "replaces \\1 sequences with the regexp's corresponding capture" do + str = "hello" + + str.gsub(/([aeiou])/, '<\1>').should == "hll" + str.gsub(/(.)/, '\1\1').should == "hheelllloo" + + str.gsub(/.(.?)/, '<\0>(\1)').should == "(e)(l)()" + + str.gsub(/.(.)+/, '\1').should == "o" + + str = "ABCDEFGHIJKLabcdefghijkl" + re = /#{"(.)" * 12}/ + str.gsub(re, '\1').should == "Aa" + str.gsub(re, '\9').should == "Ii" + # Only the first 9 captures can be accessed in MRI + str.gsub(re, '\10').should == "A0a0" + end + + it "treats \\1 sequences without corresponding captures as empty strings" do + str = "hello!" + + str.gsub("", '<\1>').should == "<>h<>e<>l<>l<>o<>!<>" + str.gsub("h", '<\1>').should == "<>ello!" + + str.gsub(//, '<\1>').should == "<>h<>e<>l<>l<>o<>!<>" + str.gsub(/./, '\1\2\3').should == "" + str.gsub(/.(.{20})?/, '\1').should == "" + end + + it "replaces \\& and \\0 with the complete match" do + str = "hello!" + + str.gsub("", '<\0>').should == "<>h<>e<>l<>l<>o<>!<>" + str.gsub("", '<\&>').should == "<>h<>e<>l<>l<>o<>!<>" + str.gsub("he", '<\0>').should == "llo!" + str.gsub("he", '<\&>').should == "llo!" + str.gsub("l", '<\0>').should == "heo!" + str.gsub("l", '<\&>').should == "heo!" + + str.gsub(//, '<\0>').should == "<>h<>e<>l<>l<>o<>!<>" + str.gsub(//, '<\&>').should == "<>h<>e<>l<>l<>o<>!<>" + str.gsub(/../, '<\0>').should == "" + str.gsub(/../, '<\&>').should == "" + str.gsub(/(.)./, '<\0>').should == "" + end + + it "replaces \\` with everything before the current match" do + str = "hello!" + + str.gsub("", '<\`>').should == "<>hello!" + str.gsub("h", '<\`>').should == "<>ello!" + str.gsub("l", '<\`>').should == "heo!" + str.gsub("!", '<\`>').should == "hello" + + str.gsub(//, '<\`>').should == "<>hello!" + str.gsub(/../, '<\`>').should == "<>" + end + + it "replaces \\' with everything after the current match" do + str = "hello!" + + str.gsub("", '<\\\'>').should == "hello!<>" + str.gsub("h", '<\\\'>').should == "ello!" + str.gsub("ll", '<\\\'>').should == "heo!" + str.gsub("!", '<\\\'>').should == "hello<>" + + str.gsub(//, '<\\\'>').should == "hello!<>" + str.gsub(/../, '<\\\'>').should == "<>" + end + + it "replaces \\+ with the last paren that actually matched" do + str = "hello!" + + str.gsub(/(.)(.)/, '\+').should == "el!" + str.gsub(/(.)(.)+/, '\+').should == "!" + str.gsub(/(.)()/, '\+').should == "" + str.gsub(/(.)(.{20})?/, '<\+>').should == "" + + str = "ABCDEFGHIJKLabcdefghijkl" + re = /#{"(.)" * 12}/ + str.gsub(re, '\+').should == "Ll" + end + + it "treats \\+ as an empty string if there was no captures" do + "hello!".gsub(/./, '\+').should == "" + end + + it "maps \\\\ in replacement to \\" do + "hello".gsub(/./, '\\\\').should == '\\' * 5 + end + + it "leaves unknown \\x escapes in replacement untouched" do + "hello".gsub(/./, '\\x').should == '\\x' * 5 + "hello".gsub(/./, '\\y').should == '\\y' * 5 + end + + it "leaves \\ at the end of replacement untouched" do + "hello".gsub(/./, 'hah\\').should == 'hah\\' * 5 + end + + it_behaves_like :string_gsub_named_capture, :gsub + + it "taints the result if the original string or replacement is tainted" do + hello = "hello" + hello_t = "hello" + a = "a" + a_t = "a" + empty = "" + empty_t = "" + + hello_t.taint; a_t.taint; empty_t.taint + + hello_t.gsub(/./, a).tainted?.should == true + hello_t.gsub(/./, empty).tainted?.should == true + + hello.gsub(/./, a_t).tainted?.should == true + hello.gsub(/./, empty_t).tainted?.should == true + hello.gsub(//, empty_t).tainted?.should == true + + hello.gsub(//.taint, "foo").tainted?.should == false + end + + it "handles pattern collapse" do + str = "こにちわ" + reg = %r!! + str.gsub(reg, ".").should == ".こ.に.ち.わ." + end + + it "untrusts the result if the original string or replacement is untrusted" do + hello = "hello" + hello_t = "hello" + a = "a" + a_t = "a" + empty = "" + empty_t = "" + + hello_t.untrust; a_t.untrust; empty_t.untrust + + hello_t.gsub(/./, a).untrusted?.should == true + hello_t.gsub(/./, empty).untrusted?.should == true + + hello.gsub(/./, a_t).untrusted?.should == true + hello.gsub(/./, empty_t).untrusted?.should == true + hello.gsub(//, empty_t).untrusted?.should == true + + hello.gsub(//.untrust, "foo").untrusted?.should == false + end + + it "tries to convert pattern to a string using to_str" do + pattern = mock('.') + def pattern.to_str() "." end + + "hello.".gsub(pattern, "!").should == "hello!" + end + + it "raises a TypeError when pattern can't be converted to a string" do + lambda { "hello".gsub([], "x") }.should raise_error(TypeError) + lambda { "hello".gsub(Object.new, "x") }.should raise_error(TypeError) + lambda { "hello".gsub(nil, "x") }.should raise_error(TypeError) + end + + it "tries to convert replacement to a string using to_str" do + replacement = mock('hello_replacement') + def replacement.to_str() "hello_replacement" end + + "hello".gsub(/hello/, replacement).should == "hello_replacement" + end + + it "raises a TypeError when replacement can't be converted to a string" do + lambda { "hello".gsub(/[aeiou]/, []) }.should raise_error(TypeError) + lambda { "hello".gsub(/[aeiou]/, Object.new) }.should raise_error(TypeError) + lambda { "hello".gsub(/[aeiou]/, nil) }.should raise_error(TypeError) + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(StringSpecs::MyString) + end + + # Note: $~ cannot be tested because mspec messes with it + + it "sets $~ to MatchData of last match and nil when there's none" do + 'hello.'.gsub('hello', 'x') + $~[0].should == 'hello' + + 'hello.'.gsub('not', 'x') + $~.should == nil + + 'hello.'.gsub(/.(.)/, 'x') + $~[0].should == 'o.' + + 'hello.'.gsub(/not/, 'x') + $~.should == nil + end +end + +describe "String#gsub with pattern and Hash" do + it "returns a copy of self with all occurrences of pattern replaced with the value of the corresponding hash key" do + "hello".gsub(/./, 'l' => 'L').should == "LL" + "hello!".gsub(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said' + "hello".gsub('l', 'l' => 'el').should == 'heelelo' + end + + it "ignores keys that don't correspond to matches" do + "hello".gsub(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow" + end + + it "returns an empty string if the pattern matches but the hash specifies no replacements" do + "hello".gsub(/./, 'z' => 'L').should == "" + end + + it "ignores non-String keys" do + "tattoo".gsub(/(tt)/, 'tt' => 'b', tt: 'z').should == "taboo" + end + + it "uses a key's value as many times as needed" do + "food".gsub(/o/, 'o' => '0').should == "f00d" + end + + it "uses the hash's default value for missing keys" do + hsh = {} + hsh.default='?' + hsh['o'] = '0' + "food".gsub(/./, hsh).should == "?00?" + end + + it "coerces the hash values with #to_s" do + hsh = {} + hsh.default=[] + hsh['o'] = 0 + obj = mock('!') + obj.should_receive(:to_s).and_return('!') + hsh['!'] = obj + "food!".gsub(/./, hsh).should == "[]00[]!" + end + + it "uses the hash's value set from default_proc for missing keys" do + hsh = {} + hsh.default_proc = lambda { |k,v| 'lamb' } + "food!".gsub(/./, hsh).should == "lamblamblamblamblamb" + end + + it "sets $~ to MatchData of last match and nil when there's none for access from outside" do + 'hello.'.gsub('l', 'l' => 'L') + $~.begin(0).should == 3 + $~[0].should == 'l' + + 'hello.'.gsub('not', 'ot' => 'to') + $~.should == nil + + 'hello.'.gsub(/.(.)/, 'o' => ' hole') + $~[0].should == 'o.' + + 'hello.'.gsub(/not/, 'z' => 'glark') + $~.should == nil + end + + it "doesn't interpolate special sequences like \\1 for the block's return value" do + repl = '\& \0 \1 \` \\\' \+ \\\\ foo' + "hello".gsub(/(.+)/, 'hello' => repl ).should == repl + end + + it "untrusts the result if the original string is untrusted" do + str = "Ghana".untrust + str.gsub(/[Aa]na/, 'ana' => '').untrusted?.should be_true + end + + it "untrusts the result if a hash value is untrusted" do + str = "Ghana" + str.gsub(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true + end + + it "taints the result if the original string is tainted" do + str = "Ghana".taint + str.gsub(/[Aa]na/, 'ana' => '').tainted?.should be_true + end + + it "taints the result if a hash value is tainted" do + str = "Ghana" + str.gsub(/a$/, 'a' => 'di'.taint).tainted?.should be_true + end + +end + +describe "String#gsub! with pattern and Hash" do + + it "returns self with all occurrences of pattern replaced with the value of the corresponding hash key" do + "hello".gsub!(/./, 'l' => 'L').should == "LL" + "hello!".gsub!(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said' + "hello".gsub!('l', 'l' => 'el').should == 'heelelo' + end + + it "ignores keys that don't correspond to matches" do + "hello".gsub!(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow" + end + + it "replaces self with an empty string if the pattern matches but the hash specifies no replacements" do + "hello".gsub!(/./, 'z' => 'L').should == "" + end + + it "ignores non-String keys" do + "hello".gsub!(/(ll)/, 'll' => 'r', ll: 'z').should == "hero" + end + + it "uses a key's value as many times as needed" do + "food".gsub!(/o/, 'o' => '0').should == "f00d" + end + + it "uses the hash's default value for missing keys" do + hsh = {} + hsh.default='?' + hsh['o'] = '0' + "food".gsub!(/./, hsh).should == "?00?" + end + + it "coerces the hash values with #to_s" do + hsh = {} + hsh.default=[] + hsh['o'] = 0 + obj = mock('!') + obj.should_receive(:to_s).and_return('!') + hsh['!'] = obj + "food!".gsub!(/./, hsh).should == "[]00[]!" + end + + it "uses the hash's value set from default_proc for missing keys" do + hsh = {} + hsh.default_proc = lambda { |k,v| 'lamb' } + "food!".gsub!(/./, hsh).should == "lamblamblamblamblamb" + end + + it "sets $~ to MatchData of last match and nil when there's none for access from outside" do + 'hello.'.gsub!('l', 'l' => 'L') + $~.begin(0).should == 3 + $~[0].should == 'l' + + 'hello.'.gsub!('not', 'ot' => 'to') + $~.should == nil + + 'hello.'.gsub!(/.(.)/, 'o' => ' hole') + $~[0].should == 'o.' + + 'hello.'.gsub!(/not/, 'z' => 'glark') + $~.should == nil + end + + it "doesn't interpolate special sequences like \\1 for the block's return value" do + repl = '\& \0 \1 \` \\\' \+ \\\\ foo' + "hello".gsub!(/(.+)/, 'hello' => repl ).should == repl + end + + it "keeps untrusted state" do + str = "Ghana".untrust + str.gsub!(/[Aa]na/, 'ana' => '').untrusted?.should be_true + end + + it "untrusts self if a hash value is untrusted" do + str = "Ghana" + str.gsub!(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true + end + + it "keeps tainted state" do + str = "Ghana".taint + str.gsub!(/[Aa]na/, 'ana' => '').tainted?.should be_true + end + + it "taints self if a hash value is tainted" do + str = "Ghana" + str.gsub!(/a$/, 'a' => 'di'.taint).tainted?.should be_true + end + +end + +describe "String#gsub with pattern and block" do + it "returns a copy of self with all occurrences of pattern replaced with the block's return value" do + "hello".gsub(/./) { |s| s.succ + ' ' }.should == "i f m m p " + "hello!".gsub(/(.)(.)/) { |*a| a.inspect }.should == '["he"]["ll"]["o!"]' + "hello".gsub('l') { 'x'}.should == 'hexxo' + end + + it "sets $~ for access from the block" do + str = "hello" + str.gsub(/([aeiou])/) { "<#{$~[1]}>" }.should == "hll" + str.gsub(/([aeiou])/) { "<#{$1}>" }.should == "hll" + str.gsub("l") { "<#{$~[0]}>" }.should == "heo" + + offsets = [] + + str.gsub(/([aeiou])/) do + md = $~ + md.string.should == str + offsets << md.offset(0) + str + end.should == "hhellollhello" + + offsets.should == [[1, 2], [4, 5]] + end + + it "restores $~ after leaving the block" do + [/./, "l"].each do |pattern| + old_md = nil + "hello".gsub(pattern) do + old_md = $~ + "ok".match(/./) + "x" + end + + $~[0].should == old_md[0] + $~.string.should == "hello" + end + end + + it "sets $~ to MatchData of last match and nil when there's none for access from outside" do + 'hello.'.gsub('l') { 'x' } + $~.begin(0).should == 3 + $~[0].should == 'l' + + 'hello.'.gsub('not') { 'x' } + $~.should == nil + + 'hello.'.gsub(/.(.)/) { 'x' } + $~[0].should == 'o.' + + 'hello.'.gsub(/not/) { 'x' } + $~.should == nil + end + + it "doesn't interpolate special sequences like \\1 for the block's return value" do + repl = '\& \0 \1 \` \\\' \+ \\\\ foo' + "hello".gsub(/(.+)/) { repl }.should == repl + end + + it "converts the block's return value to a string using to_s" do + replacement = mock('hello_replacement') + def replacement.to_s() "hello_replacement" end + + "hello".gsub(/hello/) { replacement }.should == "hello_replacement" + + obj = mock('ok') + def obj.to_s() "ok" end + + "hello".gsub(/.+/) { obj }.should == "ok" + end + + it "untrusts the result if the original string or replacement is untrusted" do + hello = "hello" + hello_t = "hello" + a = "a" + a_t = "a" + empty = "" + empty_t = "" + + hello_t.untrust; a_t.untrust; empty_t.untrust + + hello_t.gsub(/./) { a }.untrusted?.should == true + hello_t.gsub(/./) { empty }.untrusted?.should == true + + hello.gsub(/./) { a_t }.untrusted?.should == true + hello.gsub(/./) { empty_t }.untrusted?.should == true + hello.gsub(//) { empty_t }.untrusted?.should == true + + hello.gsub(//.untrust) { "foo" }.untrusted?.should == false + end + + it "uses the compatible encoding if they are compatible" do + s = "hello" + s2 = "#{195.chr}#{192.chr}#{195.chr}" + + s.gsub(/l/) { |bar| 195.chr }.encoding.should == Encoding::ASCII_8BIT + s2.gsub("#{192.chr}") { |bar| "hello" }.encoding.should == Encoding::ASCII_8BIT + end + + it "raises an Encoding::CompatibilityError if the encodings are not compatible" do + s = "hllëllo" + s2 = "hellö" + + lambda { s.gsub(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError) + lambda { s2.gsub(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError) + end + + it "replaces the incompatible part properly even if the encodings are not compatible" do + s = "hllëllo" + + s.gsub(/ë/) { |bar| "Русский".force_encoding("iso-8859-5") }.encoding.should == Encoding::ISO_8859_5 + end + + not_supported_on :opal do + it "raises an ArgumentError if encoding is not valid" do + x92 = [0x92].pack('C').force_encoding('utf-8') + lambda { "a#{x92}b".gsub(/[^\x00-\x7f]/u, '') }.should raise_error(ArgumentError) + end + end +end + +describe "String#gsub with pattern and without replacement and block" do + it "returns an enumerator" do + enum = "abca".gsub(/a/) + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == ["a", "a"] + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + "abca".gsub(/a/).size.should == nil + end + end + end +end + +describe "String#gsub! with pattern and replacement" do + it "modifies self in place and returns self" do + a = "hello" + a.gsub!(/[aeiou]/, '*').should equal(a) + a.should == "h*ll*" + end + + it "modifies self in place with multi-byte characters and returns self" do + a = "¿por qué?" + a.gsub!(/([a-z\d]*)/, "*").should equal(a) + a.should == "*¿** **é*?*" + end + + it "taints self if replacement is tainted" do + a = "hello" + a.gsub!(/./.taint, "foo").tainted?.should == false + a.gsub!(/./, "foo".taint).tainted?.should == true + end + + it "untrusts self if replacement is untrusted" do + a = "hello" + a.gsub!(/./.untrust, "foo").untrusted?.should == false + a.gsub!(/./, "foo".untrust).untrusted?.should == true + end + + it "returns nil if no modifications were made" do + a = "hello" + a.gsub!(/z/, '*').should == nil + a.gsub!(/z/, 'z').should == nil + a.should == "hello" + end + + # See [ruby-core:23666] + it "raises a RuntimeError when self is frozen" do + s = "hello" + s.freeze + + lambda { s.gsub!(/ROAR/, "x") }.should raise_error(RuntimeError) + lambda { s.gsub!(/e/, "e") }.should raise_error(RuntimeError) + lambda { s.gsub!(/[aeiou]/, '*') }.should raise_error(RuntimeError) + end +end + +describe "String#gsub! with pattern and block" do + it "modifies self in place and returns self" do + a = "hello" + a.gsub!(/[aeiou]/) { '*' }.should equal(a) + a.should == "h*ll*" + end + + it "taints self if block's result is tainted" do + a = "hello" + a.gsub!(/./.taint) { "foo" }.tainted?.should == false + a.gsub!(/./) { "foo".taint }.tainted?.should == true + end + + it "untrusts self if block's result is untrusted" do + a = "hello" + a.gsub!(/./.untrust) { "foo" }.untrusted?.should == false + a.gsub!(/./) { "foo".untrust }.untrusted?.should == true + end + + it "returns nil if no modifications were made" do + a = "hello" + a.gsub!(/z/) { '*' }.should == nil + a.gsub!(/z/) { 'z' }.should == nil + a.should == "hello" + end + + # See [ruby-core:23663] + it "raises a RuntimeError when self is frozen" do + s = "hello" + s.freeze + + lambda { s.gsub!(/ROAR/) { "x" } }.should raise_error(RuntimeError) + lambda { s.gsub!(/e/) { "e" } }.should raise_error(RuntimeError) + lambda { s.gsub!(/[aeiou]/) { '*' } }.should raise_error(RuntimeError) + end + + it "uses the compatible encoding if they are compatible" do + s = "hello" + s2 = "#{195.chr}#{192.chr}#{195.chr}" + + s.gsub!(/l/) { |bar| 195.chr }.encoding.should == Encoding::ASCII_8BIT + s2.gsub!("#{192.chr}") { |bar| "hello" }.encoding.should == Encoding::ASCII_8BIT + end + + it "raises an Encoding::CompatibilityError if the encodings are not compatible" do + s = "hllëllo" + s2 = "hellö" + + lambda { s.gsub!(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError) + lambda { s2.gsub!(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError) + end + + it "replaces the incompatible part properly even if the encodings are not compatible" do + s = "hllëllo" + + s.gsub!(/ë/) { |bar| "Русский".force_encoding("iso-8859-5") }.encoding.should == Encoding::ISO_8859_5 + end + + not_supported_on :opal do + it "raises an ArgumentError if encoding is not valid" do + x92 = [0x92].pack('C').force_encoding('utf-8') + lambda { "a#{x92}b".gsub!(/[^\x00-\x7f]/u, '') }.should raise_error(ArgumentError) + end + end +end + +describe "String#gsub! with pattern and without replacement and block" do + it "returns an enumerator" do + enum = "abca".gsub!(/a/) + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == ["a", "a"] + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + "abca".gsub!(/a/).size.should == nil + end + end + end +end diff --git a/spec/rubyspec/core/string/hash_spec.rb b/spec/rubyspec/core/string/hash_spec.rb new file mode 100644 index 0000000000..255168cebd --- /dev/null +++ b/spec/rubyspec/core/string/hash_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#hash" do + it "returns a hash based on a string's length and content" do + "abc".hash.should == "abc".hash + "abc".hash.should_not == "cba".hash + end +end diff --git a/spec/rubyspec/core/string/hex_spec.rb b/spec/rubyspec/core/string/hex_spec.rb new file mode 100644 index 0000000000..8a9257c310 --- /dev/null +++ b/spec/rubyspec/core/string/hex_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +# TODO: Move actual results to String#to_int() and spec in terms of it +describe "String#hex" do + it "treats leading characters of self as a string of hex digits" do + "0a".hex.should == 10 + "0o".hex.should == 0 + "0x".hex.should == 0 + "A_BAD_BABE".hex.should == 0xABADBABE + "0b1010".hex.should == "b1010".hex + "0d500".hex.should == "d500".hex + "abcdefG".hex.should == 0xabcdef + end + + it "does not accept a sequence of underscores as part of a number" do + "a__b".hex.should == 0xa + "a____b".hex.should == 0xa + "a___f".hex.should == 0xa + end + + it "takes an optional sign" do + "-1234".hex.should == -4660 + "+1234".hex.should == 4660 + end + + it "takes an optional 0x" do + "0x0a".hex.should == 10 + "0a".hex.should == 10 + end + + it "requires that the sign is in front of the 0x if present" do + "-0x1".hex.should == -1 + "0x-1".hex.should == 0 + end + + it "returns 0 on error" do + "".hex.should == 0 + "+-5".hex.should == 0 + "wombat".hex.should == 0 + "0x0x42".hex.should == 0 + end + + it "returns 0 if sequence begins with underscore" do + "_a".hex.should == 0 + "___b".hex.should == 0 + "___0xc".hex.should == 0 + end +end diff --git a/spec/rubyspec/core/string/include_spec.rb b/spec/rubyspec/core/string/include_spec.rb new file mode 100644 index 0000000000..8da12a9862 --- /dev/null +++ b/spec/rubyspec/core/string/include_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#include? with String" do + it "returns true if self contains other_str" do + "hello".include?("lo").should == true + "hello".include?("ol").should == false + end + + it "ignores subclass differences" do + "hello".include?(StringSpecs::MyString.new("lo")).should == true + StringSpecs::MyString.new("hello").include?("lo").should == true + StringSpecs::MyString.new("hello").include?(StringSpecs::MyString.new("lo")).should == true + end + + it "tries to convert other to string using to_str" do + other = mock('lo') + other.should_receive(:to_str).and_return("lo") + + "hello".include?(other).should == true + end + + it "raises a TypeError if other can't be converted to string" do + lambda { "hello".include?([]) }.should raise_error(TypeError) + lambda { "hello".include?('h'.ord) }.should raise_error(TypeError) + lambda { "hello".include?(mock('x')) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/string/index_spec.rb b/spec/rubyspec/core/string/index_spec.rb new file mode 100644 index 0000000000..37d11a0fd7 --- /dev/null +++ b/spec/rubyspec/core/string/index_spec.rb @@ -0,0 +1,315 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#index" do + it "raises a TypeError if passed nil" do + lambda { "abc".index nil }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a boolean" do + lambda { "abc".index true }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a Symbol" do + lambda { "abc".index :a }.should raise_error(TypeError) + end + + it "calls #to_str to convert the first argument" do + char = mock("string index char") + char.should_receive(:to_str).and_return("b") + "abc".index(char).should == 1 + end + + it "calls #to_int to convert the second argument" do + offset = mock("string index offset") + offset.should_receive(:to_int).and_return(1) + "abc".index("c", offset).should == 2 + end + + it "raises a TypeError if passed a Fixnum" do + lambda { "abc".index 97 }.should raise_error(TypeError) + end +end + +describe "String#index with String" do + it "behaves the same as String#index(char) for one-character strings" do + "blablabla hello cruel world...!".split("").uniq.each do |str| + chr = str[0] + str.index(str).should == str.index(chr) + + 0.upto(str.size + 1) do |start| + str.index(str, start).should == str.index(chr, start) + end + + (-str.size - 1).upto(-1) do |start| + str.index(str, start).should == str.index(chr, start) + end + end + end + + it "returns the index of the first occurrence of the given substring" do + "blablabla".index("").should == 0 + "blablabla".index("b").should == 0 + "blablabla".index("bla").should == 0 + "blablabla".index("blabla").should == 0 + "blablabla".index("blablabla").should == 0 + + "blablabla".index("l").should == 1 + "blablabla".index("la").should == 1 + "blablabla".index("labla").should == 1 + "blablabla".index("lablabla").should == 1 + + "blablabla".index("a").should == 2 + "blablabla".index("abla").should == 2 + "blablabla".index("ablabla").should == 2 + end + + it "doesn't set $~" do + $~ = nil + + 'hello.'.index('ll') + $~.should == nil + end + + it "ignores string subclasses" do + "blablabla".index(StringSpecs::MyString.new("bla")).should == 0 + StringSpecs::MyString.new("blablabla").index("bla").should == 0 + StringSpecs::MyString.new("blablabla").index(StringSpecs::MyString.new("bla")).should == 0 + end + + it "starts the search at the given offset" do + "blablabla".index("bl", 0).should == 0 + "blablabla".index("bl", 1).should == 3 + "blablabla".index("bl", 2).should == 3 + "blablabla".index("bl", 3).should == 3 + + "blablabla".index("bla", 0).should == 0 + "blablabla".index("bla", 1).should == 3 + "blablabla".index("bla", 2).should == 3 + "blablabla".index("bla", 3).should == 3 + + "blablabla".index("blab", 0).should == 0 + "blablabla".index("blab", 1).should == 3 + "blablabla".index("blab", 2).should == 3 + "blablabla".index("blab", 3).should == 3 + + "blablabla".index("la", 1).should == 1 + "blablabla".index("la", 2).should == 4 + "blablabla".index("la", 3).should == 4 + "blablabla".index("la", 4).should == 4 + + "blablabla".index("lab", 1).should == 1 + "blablabla".index("lab", 2).should == 4 + "blablabla".index("lab", 3).should == 4 + "blablabla".index("lab", 4).should == 4 + + "blablabla".index("ab", 2).should == 2 + "blablabla".index("ab", 3).should == 5 + "blablabla".index("ab", 4).should == 5 + "blablabla".index("ab", 5).should == 5 + + "blablabla".index("", 0).should == 0 + "blablabla".index("", 1).should == 1 + "blablabla".index("", 2).should == 2 + "blablabla".index("", 7).should == 7 + "blablabla".index("", 8).should == 8 + "blablabla".index("", 9).should == 9 + end + + it "starts the search at offset + self.length if offset is negative" do + str = "blablabla" + + ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle| + (-str.length .. -1).each do |offset| + str.index(needle, offset).should == + str.index(needle, offset + str.length) + end + end + end + + it "returns nil if the substring isn't found" do + "blablabla".index("B").should == nil + "blablabla".index("z").should == nil + "blablabla".index("BLA").should == nil + "blablabla".index("blablablabla").should == nil + "blablabla".index("", 10).should == nil + + "hello".index("he", 1).should == nil + "hello".index("he", 2).should == nil + end + + with_feature :encoding do + it "returns the character index of a multibyte character" do + "ありがとう".index("が").should == 2 + end + + it "returns the character index after offset" do + "われわれ".index("わ", 1).should == 2 + end + + it "returns the character index after a partial first match" do + " notation" do + [ ["\a", '"\\a"'], + ["\b", '"\\b"'], + ["\t", '"\\t"'], + ["\n", '"\\n"'], + ["\v", '"\\v"'], + ["\f", '"\\f"'], + ["\r", '"\\r"'], + ["\e", '"\\e"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with \" and \\ escaped with a backslash" do + [ ["\"", '"\\""'], + ["\\", '"\\\\"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with \\# when # is followed by $, @, {" do + [ ["\#$", '"\\#$"'], + ["\#@", '"\\#@"'], + ["\#{", '"\\#{"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with # not escaped when followed by any other character" do + [ ["#", '"#"'], + ["#1", '"#1"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with printable non-alphanumeric characters unescaped" do + [ [" ", '" "'], + ["!", '"!"'], + ["$", '"$"'], + ["%", '"%"'], + ["&", '"&"'], + ["'", '"\'"'], + ["(", '"("'], + [")", '")"'], + ["*", '"*"'], + ["+", '"+"'], + [",", '","'], + ["-", '"-"'], + [".", '"."'], + ["/", '"/"'], + [":", '":"'], + [";", '";"'], + ["<", '"<"'], + ["=", '"="'], + [">", '">"'], + ["?", '"?"'], + ["@", '"@"'], + ["[", '"["'], + ["]", '"]"'], + ["^", '"^"'], + ["_", '"_"'], + ["`", '"`"'], + ["{", '"{"'], + ["|", '"|"'], + ["}", '"}"'], + ["~", '"~"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with numeric characters unescaped" do + [ ["0", '"0"'], + ["1", '"1"'], + ["2", '"2"'], + ["3", '"3"'], + ["4", '"4"'], + ["5", '"5"'], + ["6", '"6"'], + ["7", '"7"'], + ["8", '"8"'], + ["9", '"9"'], + ].should be_computed_by(:inspect) + end + + it "returns a string with upper-case alpha characters unescaped" do + [ ["A", '"A"'], + ["B", '"B"'], + ["C", '"C"'], + ["D", '"D"'], + ["E", '"E"'], + ["F", '"F"'], + ["G", '"G"'], + ["H", '"H"'], + ["I", '"I"'], + ["J", '"J"'], + ["K", '"K"'], + ["L", '"L"'], + ["M", '"M"'], + ["N", '"N"'], + ["O", '"O"'], + ["P", '"P"'], + ["Q", '"Q"'], + ["R", '"R"'], + ["S", '"S"'], + ["T", '"T"'], + ["U", '"U"'], + ["V", '"V"'], + ["W", '"W"'], + ["X", '"X"'], + ["Y", '"Y"'], + ["Z", '"Z"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with lower-case alpha characters unescaped" do + [ ["a", '"a"'], + ["b", '"b"'], + ["c", '"c"'], + ["d", '"d"'], + ["e", '"e"'], + ["f", '"f"'], + ["g", '"g"'], + ["h", '"h"'], + ["i", '"i"'], + ["j", '"j"'], + ["k", '"k"'], + ["l", '"l"'], + ["m", '"m"'], + ["n", '"n"'], + ["o", '"o"'], + ["p", '"p"'], + ["q", '"q"'], + ["r", '"r"'], + ["s", '"s"'], + ["t", '"t"'], + ["u", '"u"'], + ["v", '"v"'], + ["w", '"w"'], + ["x", '"x"'], + ["y", '"y"'], + ["z", '"z"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with non-printing characters replaced by \\x notation" do + # Avoid the file encoding by computing the string with #chr. + [ [0001.chr, '"\\x01"'], + [0002.chr, '"\\x02"'], + [0003.chr, '"\\x03"'], + [0004.chr, '"\\x04"'], + [0005.chr, '"\\x05"'], + [0006.chr, '"\\x06"'], + [0016.chr, '"\\x0E"'], + [0017.chr, '"\\x0F"'], + [0020.chr, '"\\x10"'], + [0021.chr, '"\\x11"'], + [0022.chr, '"\\x12"'], + [0023.chr, '"\\x13"'], + [0024.chr, '"\\x14"'], + [0025.chr, '"\\x15"'], + [0026.chr, '"\\x16"'], + [0027.chr, '"\\x17"'], + [0030.chr, '"\\x18"'], + [0031.chr, '"\\x19"'], + [0032.chr, '"\\x1A"'], + [0034.chr, '"\\x1C"'], + [0035.chr, '"\\x1D"'], + [0036.chr, '"\\x1E"'], + [0037.chr, '"\\x1F"'], + [0177.chr, '"\\x7F"'], + [0200.chr, '"\\x80"'], + [0201.chr, '"\\x81"'], + [0202.chr, '"\\x82"'], + [0203.chr, '"\\x83"'], + [0204.chr, '"\\x84"'], + [0205.chr, '"\\x85"'], + [0206.chr, '"\\x86"'], + [0207.chr, '"\\x87"'], + [0210.chr, '"\\x88"'], + [0211.chr, '"\\x89"'], + [0212.chr, '"\\x8A"'], + [0213.chr, '"\\x8B"'], + [0214.chr, '"\\x8C"'], + [0215.chr, '"\\x8D"'], + [0216.chr, '"\\x8E"'], + [0217.chr, '"\\x8F"'], + [0220.chr, '"\\x90"'], + [0221.chr, '"\\x91"'], + [0222.chr, '"\\x92"'], + [0223.chr, '"\\x93"'], + [0224.chr, '"\\x94"'], + [0225.chr, '"\\x95"'], + [0226.chr, '"\\x96"'], + [0227.chr, '"\\x97"'], + [0230.chr, '"\\x98"'], + [0231.chr, '"\\x99"'], + [0232.chr, '"\\x9A"'], + [0233.chr, '"\\x9B"'], + [0234.chr, '"\\x9C"'], + [0235.chr, '"\\x9D"'], + [0236.chr, '"\\x9E"'], + [0237.chr, '"\\x9F"'], + [0240.chr, '"\\xA0"'], + [0241.chr, '"\\xA1"'], + [0242.chr, '"\\xA2"'], + [0243.chr, '"\\xA3"'], + [0244.chr, '"\\xA4"'], + [0245.chr, '"\\xA5"'], + [0246.chr, '"\\xA6"'], + [0247.chr, '"\\xA7"'], + [0250.chr, '"\\xA8"'], + [0251.chr, '"\\xA9"'], + [0252.chr, '"\\xAA"'], + [0253.chr, '"\\xAB"'], + [0254.chr, '"\\xAC"'], + [0255.chr, '"\\xAD"'], + [0256.chr, '"\\xAE"'], + [0257.chr, '"\\xAF"'], + [0260.chr, '"\\xB0"'], + [0261.chr, '"\\xB1"'], + [0262.chr, '"\\xB2"'], + [0263.chr, '"\\xB3"'], + [0264.chr, '"\\xB4"'], + [0265.chr, '"\\xB5"'], + [0266.chr, '"\\xB6"'], + [0267.chr, '"\\xB7"'], + [0270.chr, '"\\xB8"'], + [0271.chr, '"\\xB9"'], + [0272.chr, '"\\xBA"'], + [0273.chr, '"\\xBB"'], + [0274.chr, '"\\xBC"'], + [0275.chr, '"\\xBD"'], + [0276.chr, '"\\xBE"'], + [0277.chr, '"\\xBF"'], + [0300.chr, '"\\xC0"'], + [0301.chr, '"\\xC1"'], + [0302.chr, '"\\xC2"'], + [0303.chr, '"\\xC3"'], + [0304.chr, '"\\xC4"'], + [0305.chr, '"\\xC5"'], + [0306.chr, '"\\xC6"'], + [0307.chr, '"\\xC7"'], + [0310.chr, '"\\xC8"'], + [0311.chr, '"\\xC9"'], + [0312.chr, '"\\xCA"'], + [0313.chr, '"\\xCB"'], + [0314.chr, '"\\xCC"'], + [0315.chr, '"\\xCD"'], + [0316.chr, '"\\xCE"'], + [0317.chr, '"\\xCF"'], + [0320.chr, '"\\xD0"'], + [0321.chr, '"\\xD1"'], + [0322.chr, '"\\xD2"'], + [0323.chr, '"\\xD3"'], + [0324.chr, '"\\xD4"'], + [0325.chr, '"\\xD5"'], + [0326.chr, '"\\xD6"'], + [0327.chr, '"\\xD7"'], + [0330.chr, '"\\xD8"'], + [0331.chr, '"\\xD9"'], + [0332.chr, '"\\xDA"'], + [0333.chr, '"\\xDB"'], + [0334.chr, '"\\xDC"'], + [0335.chr, '"\\xDD"'], + [0336.chr, '"\\xDE"'], + [0337.chr, '"\\xDF"'], + [0340.chr, '"\\xE0"'], + [0341.chr, '"\\xE1"'], + [0342.chr, '"\\xE2"'], + [0343.chr, '"\\xE3"'], + [0344.chr, '"\\xE4"'], + [0345.chr, '"\\xE5"'], + [0346.chr, '"\\xE6"'], + [0347.chr, '"\\xE7"'], + [0350.chr, '"\\xE8"'], + [0351.chr, '"\\xE9"'], + [0352.chr, '"\\xEA"'], + [0353.chr, '"\\xEB"'], + [0354.chr, '"\\xEC"'], + [0355.chr, '"\\xED"'], + [0356.chr, '"\\xEE"'], + [0357.chr, '"\\xEF"'], + [0360.chr, '"\\xF0"'], + [0361.chr, '"\\xF1"'], + [0362.chr, '"\\xF2"'], + [0363.chr, '"\\xF3"'], + [0364.chr, '"\\xF4"'], + [0365.chr, '"\\xF5"'], + [0366.chr, '"\\xF6"'], + [0367.chr, '"\\xF7"'], + [0370.chr, '"\\xF8"'], + [0371.chr, '"\\xF9"'], + [0372.chr, '"\\xFA"'], + [0373.chr, '"\\xFB"'], + [0374.chr, '"\\xFC"'], + [0375.chr, '"\\xFD"'], + [0376.chr, '"\\xFE"'], + [0377.chr, '"\\xFF"'] + ].should be_computed_by(:inspect) + end + + it "returns a string with a NUL character replaced by \\x notation" do + 0.chr.inspect.should == '"\\x00"' + end + + describe "when default external is UTF-8" do + before :each do + @extenc, Encoding.default_external = Encoding.default_external, Encoding::UTF_8 + end + + after :each do + Encoding.default_external = @extenc + end + + it "returns a string with non-printing characters replaced by \\u notation for Unicode strings" do + [ [0001.chr('utf-8'), '"\u0001"'], + [0002.chr('utf-8'), '"\u0002"'], + [0003.chr('utf-8'), '"\u0003"'], + [0004.chr('utf-8'), '"\u0004"'], + [0005.chr('utf-8'), '"\u0005"'], + [0006.chr('utf-8'), '"\u0006"'], + [0016.chr('utf-8'), '"\u000E"'], + [0017.chr('utf-8'), '"\u000F"'], + [0020.chr('utf-8'), '"\u0010"'], + [0021.chr('utf-8'), '"\u0011"'], + [0022.chr('utf-8'), '"\u0012"'], + [0023.chr('utf-8'), '"\u0013"'], + [0024.chr('utf-8'), '"\u0014"'], + [0025.chr('utf-8'), '"\u0015"'], + [0026.chr('utf-8'), '"\u0016"'], + [0027.chr('utf-8'), '"\u0017"'], + [0030.chr('utf-8'), '"\u0018"'], + [0031.chr('utf-8'), '"\u0019"'], + [0032.chr('utf-8'), '"\u001A"'], + [0034.chr('utf-8'), '"\u001C"'], + [0035.chr('utf-8'), '"\u001D"'], + [0036.chr('utf-8'), '"\u001E"'], + [0037.chr('utf-8'), '"\u001F"'], + [0177.chr('utf-8'), '"\u007F"'], + [0200.chr('utf-8'), '"\u0080"'], + [0201.chr('utf-8'), '"\u0081"'], + [0202.chr('utf-8'), '"\u0082"'], + [0203.chr('utf-8'), '"\u0083"'], + [0204.chr('utf-8'), '"\u0084"'], + [0206.chr('utf-8'), '"\u0086"'], + [0207.chr('utf-8'), '"\u0087"'], + [0210.chr('utf-8'), '"\u0088"'], + [0211.chr('utf-8'), '"\u0089"'], + [0212.chr('utf-8'), '"\u008A"'], + [0213.chr('utf-8'), '"\u008B"'], + [0214.chr('utf-8'), '"\u008C"'], + [0215.chr('utf-8'), '"\u008D"'], + [0216.chr('utf-8'), '"\u008E"'], + [0217.chr('utf-8'), '"\u008F"'], + [0220.chr('utf-8'), '"\u0090"'], + [0221.chr('utf-8'), '"\u0091"'], + [0222.chr('utf-8'), '"\u0092"'], + [0223.chr('utf-8'), '"\u0093"'], + [0224.chr('utf-8'), '"\u0094"'], + [0225.chr('utf-8'), '"\u0095"'], + [0226.chr('utf-8'), '"\u0096"'], + [0227.chr('utf-8'), '"\u0097"'], + [0230.chr('utf-8'), '"\u0098"'], + [0231.chr('utf-8'), '"\u0099"'], + [0232.chr('utf-8'), '"\u009A"'], + [0233.chr('utf-8'), '"\u009B"'], + [0234.chr('utf-8'), '"\u009C"'], + [0235.chr('utf-8'), '"\u009D"'], + [0236.chr('utf-8'), '"\u009E"'], + [0237.chr('utf-8'), '"\u009F"'], + ].should be_computed_by(:inspect) + end + + it "returns a string with a NUL character replaced by \\u notation" do + 0.chr('utf-8').inspect.should == '"\\u0000"' + end + + it "returns a string with extended characters for Unicode strings" do + [ [0240.chr('utf-8'), '" "'], + [0241.chr('utf-8'), '"¡"'], + [0242.chr('utf-8'), '"¢"'], + [0243.chr('utf-8'), '"£"'], + [0244.chr('utf-8'), '"¤"'], + [0245.chr('utf-8'), '"¥"'], + [0246.chr('utf-8'), '"¦"'], + [0247.chr('utf-8'), '"§"'], + [0250.chr('utf-8'), '"¨"'], + [0251.chr('utf-8'), '"©"'], + [0252.chr('utf-8'), '"ª"'], + [0253.chr('utf-8'), '"«"'], + [0254.chr('utf-8'), '"¬"'], + [0255.chr('utf-8'), '"­"'], + [0256.chr('utf-8'), '"®"'], + [0257.chr('utf-8'), '"¯"'], + [0260.chr('utf-8'), '"°"'], + [0261.chr('utf-8'), '"±"'], + [0262.chr('utf-8'), '"²"'], + [0263.chr('utf-8'), '"³"'], + [0264.chr('utf-8'), '"´"'], + [0265.chr('utf-8'), '"µ"'], + [0266.chr('utf-8'), '"¶"'], + [0267.chr('utf-8'), '"·"'], + [0270.chr('utf-8'), '"¸"'], + [0271.chr('utf-8'), '"¹"'], + [0272.chr('utf-8'), '"º"'], + [0273.chr('utf-8'), '"»"'], + [0274.chr('utf-8'), '"¼"'], + [0275.chr('utf-8'), '"½"'], + [0276.chr('utf-8'), '"¾"'], + [0277.chr('utf-8'), '"¿"'], + [0300.chr('utf-8'), '"À"'], + [0301.chr('utf-8'), '"Á"'], + [0302.chr('utf-8'), '"Â"'], + [0303.chr('utf-8'), '"Ã"'], + [0304.chr('utf-8'), '"Ä"'], + [0305.chr('utf-8'), '"Å"'], + [0306.chr('utf-8'), '"Æ"'], + [0307.chr('utf-8'), '"Ç"'], + [0310.chr('utf-8'), '"È"'], + [0311.chr('utf-8'), '"É"'], + [0312.chr('utf-8'), '"Ê"'], + [0313.chr('utf-8'), '"Ë"'], + [0314.chr('utf-8'), '"Ì"'], + [0315.chr('utf-8'), '"Í"'], + [0316.chr('utf-8'), '"Î"'], + [0317.chr('utf-8'), '"Ï"'], + [0320.chr('utf-8'), '"Ð"'], + [0321.chr('utf-8'), '"Ñ"'], + [0322.chr('utf-8'), '"Ò"'], + [0323.chr('utf-8'), '"Ó"'], + [0324.chr('utf-8'), '"Ô"'], + [0325.chr('utf-8'), '"Õ"'], + [0326.chr('utf-8'), '"Ö"'], + [0327.chr('utf-8'), '"×"'], + [0330.chr('utf-8'), '"Ø"'], + [0331.chr('utf-8'), '"Ù"'], + [0332.chr('utf-8'), '"Ú"'], + [0333.chr('utf-8'), '"Û"'], + [0334.chr('utf-8'), '"Ü"'], + [0335.chr('utf-8'), '"Ý"'], + [0336.chr('utf-8'), '"Þ"'], + [0337.chr('utf-8'), '"ß"'], + [0340.chr('utf-8'), '"à"'], + [0341.chr('utf-8'), '"á"'], + [0342.chr('utf-8'), '"â"'], + [0343.chr('utf-8'), '"ã"'], + [0344.chr('utf-8'), '"ä"'], + [0345.chr('utf-8'), '"å"'], + [0346.chr('utf-8'), '"æ"'], + [0347.chr('utf-8'), '"ç"'], + [0350.chr('utf-8'), '"è"'], + [0351.chr('utf-8'), '"é"'], + [0352.chr('utf-8'), '"ê"'], + [0353.chr('utf-8'), '"ë"'], + [0354.chr('utf-8'), '"ì"'], + [0355.chr('utf-8'), '"í"'], + [0356.chr('utf-8'), '"î"'], + [0357.chr('utf-8'), '"ï"'], + [0360.chr('utf-8'), '"ð"'], + [0361.chr('utf-8'), '"ñ"'], + [0362.chr('utf-8'), '"ò"'], + [0363.chr('utf-8'), '"ó"'], + [0364.chr('utf-8'), '"ô"'], + [0365.chr('utf-8'), '"õ"'], + [0366.chr('utf-8'), '"ö"'], + [0367.chr('utf-8'), '"÷"'], + [0370.chr('utf-8'), '"ø"'], + [0371.chr('utf-8'), '"ù"'], + [0372.chr('utf-8'), '"ú"'], + [0373.chr('utf-8'), '"û"'], + [0374.chr('utf-8'), '"ü"'], + [0375.chr('utf-8'), '"ý"'], + [0376.chr('utf-8'), '"þ"'], + [0377.chr('utf-8'), '"ÿ"'] + ].should be_computed_by(:inspect) + end + end +end diff --git a/spec/rubyspec/core/string/intern_spec.rb b/spec/rubyspec/core/string/intern_spec.rb new file mode 100644 index 0000000000..71f3633920 --- /dev/null +++ b/spec/rubyspec/core/string/intern_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/to_sym.rb', __FILE__) + +describe "String#intern" do + it_behaves_like(:string_to_sym, :intern) +end diff --git a/spec/rubyspec/core/string/length_spec.rb b/spec/rubyspec/core/string/length_spec.rb new file mode 100644 index 0000000000..5f51f6bc01 --- /dev/null +++ b/spec/rubyspec/core/string/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "String#length" do + it_behaves_like(:string_length, :length) +end diff --git a/spec/rubyspec/core/string/lines_spec.rb b/spec/rubyspec/core/string/lines_spec.rb new file mode 100644 index 0000000000..6aa47ea728 --- /dev/null +++ b/spec/rubyspec/core/string/lines_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_line', __FILE__) +require File.expand_path('../shared/each_line_without_block', __FILE__) + +describe "String#lines" do + it_behaves_like(:string_each_line, :lines) + + it "returns an array when no block given" do + ary = "hello world".send(@method, ' ') + ary.should == ["hello ", "world"] + end +end diff --git a/spec/rubyspec/core/string/ljust_spec.rb b/spec/rubyspec/core/string/ljust_spec.rb new file mode 100644 index 0000000000..f66fb0c573 --- /dev/null +++ b/spec/rubyspec/core/string/ljust_spec.rb @@ -0,0 +1,116 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#ljust with length, padding" do + it "returns a new string of specified length with self left justified and padded with padstr" do + "hello".ljust(20, '1234').should == "hello123412341234123" + + "".ljust(1, "abcd").should == "a" + "".ljust(2, "abcd").should == "ab" + "".ljust(3, "abcd").should == "abc" + "".ljust(4, "abcd").should == "abcd" + "".ljust(6, "abcd").should == "abcdab" + + "OK".ljust(3, "abcd").should == "OKa" + "OK".ljust(4, "abcd").should == "OKab" + "OK".ljust(6, "abcd").should == "OKabcd" + "OK".ljust(8, "abcd").should == "OKabcdab" + end + + it "pads with whitespace if no padstr is given" do + "hello".ljust(20).should == "hello " + end + + it "returns self if it's longer than or as long as the specified length" do + "".ljust(0).should == "" + "".ljust(-1).should == "" + "hello".ljust(4).should == "hello" + "hello".ljust(-1).should == "hello" + "this".ljust(3).should == "this" + "radiology".ljust(8, '-').should == "radiology" + end + + it "taints result when self or padstr is tainted" do + "x".taint.ljust(4).tainted?.should == true + "x".taint.ljust(0).tainted?.should == true + "".taint.ljust(0).tainted?.should == true + "x".taint.ljust(4, "*").tainted?.should == true + "x".ljust(4, "*".taint).tainted?.should == true + end + + it "tries to convert length to an integer using to_int" do + "^".ljust(3.8, "_^").should == "^_^" + + obj = mock('3') + obj.should_receive(:to_int).and_return(3) + + "o".ljust(obj, "_o").should == "o_o" + end + + it "raises a TypeError when length can't be converted to an integer" do + lambda { "hello".ljust("x") }.should raise_error(TypeError) + lambda { "hello".ljust("x", "y") }.should raise_error(TypeError) + lambda { "hello".ljust([]) }.should raise_error(TypeError) + lambda { "hello".ljust(mock('x')) }.should raise_error(TypeError) + end + + it "tries to convert padstr to a string using to_str" do + padstr = mock('123') + padstr.should_receive(:to_str).and_return("123") + + "hello".ljust(10, padstr).should == "hello12312" + end + + it "raises a TypeError when padstr can't be converted" do + lambda { "hello".ljust(20, []) }.should raise_error(TypeError) + lambda { "hello".ljust(20, Object.new)}.should raise_error(TypeError) + lambda { "hello".ljust(20, mock('x')) }.should raise_error(TypeError) + end + + it "raises an ArgumentError when padstr is empty" do + lambda { "hello".ljust(10, '') }.should raise_error(ArgumentError) + end + + it "returns subclass instances when called on subclasses" do + StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + + "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end + + it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do + "hello".ljust(4, 'X'.taint).tainted?.should be_false + "hello".ljust(5, 'X'.taint).tainted?.should be_false + "hello".ljust(6, 'X'.taint).tainted?.should be_true + end + + with_feature :encoding do + describe "with width" do + it "returns a String in the same encoding as the original" do + str = "abc".force_encoding Encoding::IBM437 + result = str.ljust 5 + result.should == "abc " + result.encoding.should equal(Encoding::IBM437) + end + end + + describe "with width, pattern" do + it "returns a String in the compatible encoding" do + str = "abc".force_encoding Encoding::IBM437 + result = str.ljust 5, "あ" + result.should == "abcああ" + result.encoding.should equal(Encoding::UTF_8) + end + + it "raises an Encoding::CompatibilityError if the encodings are incompatible" do + pat = "ア".encode Encoding::EUC_JP + lambda do + "あれ".ljust 5, pat + end.should raise_error(Encoding::CompatibilityError) + end + end + end +end diff --git a/spec/rubyspec/core/string/lstrip_spec.rb b/spec/rubyspec/core/string/lstrip_spec.rb new file mode 100644 index 0000000000..7ef94be567 --- /dev/null +++ b/spec/rubyspec/core/string/lstrip_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#lstrip" do + it "returns a copy of self with leading whitespace removed" do + " hello ".lstrip.should == "hello " + " hello world ".lstrip.should == "hello world " + "\n\r\t\n\v\r hello world ".lstrip.should == "hello world " + "hello".lstrip.should == "hello" + "\000 \000hello\000 \000".lstrip.should == "\000 \000hello\000 \000" + end + + it "does not strip leading \\0" do + "\x00hello".lstrip.should == "\x00hello" + end + + it "taints the result when self is tainted" do + "".taint.lstrip.tainted?.should == true + "ok".taint.lstrip.tainted?.should == true + " ok".taint.lstrip.tainted?.should == true + end +end + +describe "String#lstrip!" do + it "modifies self in place and returns self" do + a = " hello " + a.lstrip!.should equal(a) + a.should == "hello " + + a = "\000 \000hello\000 \000" + a.lstrip! + a.should == "\000 \000hello\000 \000" + end + + it "returns nil if no modifications were made" do + a = "hello" + a.lstrip!.should == nil + a.should == "hello" + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda { " hello ".freeze.lstrip! }.should raise_error(RuntimeError) + end + + # see [ruby-core:23657] + it "raises a RuntimeError on a frozen instance that would not be modified" do + lambda { "hello".freeze.lstrip! }.should raise_error(RuntimeError) + lambda { "".freeze.lstrip! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/match_spec.rb b/spec/rubyspec/core/string/match_spec.rb new file mode 100644 index 0000000000..94e28e7297 --- /dev/null +++ b/spec/rubyspec/core/string/match_spec.rb @@ -0,0 +1,175 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe :string_match_escaped_literal, shared: true do + not_supported_on :opal do + it "matches a literal Regexp that uses ASCII-only UTF-8 escape sequences" do + "a b".match(/([\u{20}-\u{7e}])/)[0].should == "a" + end + end +end + +describe "String#=~" do + it "behaves the same way as index() when given a regexp" do + ("rudder" =~ /udder/).should == "rudder".index(/udder/) + ("boat" =~ /[^fl]oat/).should == "boat".index(/[^fl]oat/) + ("bean" =~ /bag/).should == "bean".index(/bag/) + ("true" =~ /false/).should == "true".index(/false/) + end + + it "raises a TypeError if a obj is a string" do + lambda { "some string" =~ "another string" }.should raise_error(TypeError) + lambda { "a" =~ StringSpecs::MyString.new("b") }.should raise_error(TypeError) + end + + it "invokes obj.=~ with self if obj is neither a string nor regexp" do + str = "w00t" + obj = mock('x') + + obj.should_receive(:=~).with(str).any_number_of_times.and_return(true) + str.should =~ obj + + obj = mock('y') + obj.should_receive(:=~).with(str).any_number_of_times.and_return(false) + str.should_not =~ obj + end + + it "sets $~ to MatchData when there is a match and nil when there's none" do + 'hello' =~ /./ + $~[0].should == 'h' + + 'hello' =~ /not/ + $~.should == nil + end + + with_feature :encoding do + it "returns the character index of a found match" do + ("こにちわ" =~ /に/).should == 1 + end + end + +end + +describe "String#match" do + it "matches the pattern against self" do + 'hello'.match(/(.)\1/)[0].should == 'll' + end + + it_behaves_like :string_match_escaped_literal, :match + + describe "with [pattern, position]" do + describe "when given a positive position" do + it "matches the pattern against self starting at an optional index" do + "01234".match(/(.).(.)/, 1).captures.should == ["1", "3"] + end + + with_feature :encoding do + it "uses the start as a character offset" do + "零一二三四".match(/(.).(.)/, 1).captures.should == ["一", "三"] + end + end + end + + describe "when given a negative position" do + it "matches the pattern against self starting at an optional index" do + "01234".match(/(.).(.)/, -4).captures.should == ["1", "3"] + end + + with_feature :encoding do + it "uses the start as a character offset" do + "零一二三四".match(/(.).(.)/, -4).captures.should == ["一", "三"] + end + end + end + end + + describe "when passed a block" do + it "yields the MatchData" do + "abc".match(/./) {|m| ScratchPad.record m } + ScratchPad.recorded.should be_kind_of(MatchData) + end + + it "returns the block result" do + "abc".match(/./) { :result }.should == :result + end + + it "does not yield if there is no match" do + ScratchPad.record [] + "b".match(/a/) {|m| ScratchPad << m } + ScratchPad.recorded.should == [] + end + end + + it "tries to convert pattern to a string via to_str" do + obj = mock('.') + def obj.to_str() "." end + "hello".match(obj)[0].should == "h" + + obj = mock('.') + def obj.respond_to?(type, *) true end + def obj.method_missing(*args) "." end + "hello".match(obj)[0].should == "h" + end + + it "raises a TypeError if pattern is not a regexp or a string" do + lambda { 'hello'.match(10) }.should raise_error(TypeError) + not_supported_on :opal do + lambda { 'hello'.match(:ell) }.should raise_error(TypeError) + end + end + + it "converts string patterns to regexps without escaping" do + 'hello'.match('(.)\1')[0].should == 'll' + end + + it "returns nil if there's no match" do + 'hello'.match('xx').should == nil + end + + it "matches \\G at the start of the string" do + 'hello'.match(/\Gh/)[0].should == 'h' + 'hello'.match(/\Go/).should == nil + end + + it "sets $~ to MatchData of match or nil when there is none" do + 'hello'.match(/./) + $~[0].should == 'h' + Regexp.last_match[0].should == 'h' + + 'hello'.match(/X/) + $~.should == nil + Regexp.last_match.should == nil + end + + it "calls match on the regular expression" do + regexp = /./ + regexp.should_receive(:match).and_return(:foo) + 'hello'.match(regexp).should == :foo + end +end + +ruby_version_is "2.4" do + describe "String#match?" do + before :each do + # Resetting Regexp.last_match + /DONTMATCH/.match '' + end + + context "when matches the given regex" do + it "returns true but does not set Regexp.last_match" do + 'string'.match?(/string/i).should be_true + Regexp.last_match.should be_nil + end + end + + it "returns false when does not match the given regex" do + 'string'.match?(/STRING/).should be_false + end + + it "takes matching position as the 2nd argument" do + 'string'.match?(/str/i, 0).should be_true + 'string'.match?(/str/i, 1).should be_false + end + end +end diff --git a/spec/rubyspec/core/string/modulo_spec.rb b/spec/rubyspec/core/string/modulo_spec.rb new file mode 100644 index 0000000000..249b15bf1e --- /dev/null +++ b/spec/rubyspec/core/string/modulo_spec.rb @@ -0,0 +1,780 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#%" do + it "formats multiple expressions" do + ("%b %x %d %s" % [10, 10, 10, 10]).should == "1010 a 10 10" + end + + it "formats expressions mid string" do + ("hello %s!" % "world").should == "hello world!" + end + + it "formats %% into %" do + ("%d%% %s" % [10, "of chickens!"]).should == "10% of chickens!" + end + + it "formats single % character at the end as literal %" do + ("%" % []).should == "%" + ("foo%" % []).should == "foo%" + end + + it "formats single % character before a newline as literal %" do + ("%\n" % []).should == "%\n" + ("foo%\n" % []).should == "foo%\n" + ("%\n.3f" % 1.2).should == "%\n.3f" + end + + it "formats single % character before a NUL as literal %" do + ("%\0" % []).should == "%\0" + ("foo%\0" % []).should == "foo%\0" + ("%\0.3f" % 1.2).should == "%\0.3f" + end + + it "raises an error if single % appears anywhere else" do + lambda { (" % " % []) }.should raise_error(ArgumentError) + lambda { ("foo%quux" % []) }.should raise_error(ArgumentError) + end + + it "raises an error if NULL or \\n appear anywhere else in the format string" do + begin + old_debug, $DEBUG = $DEBUG, false + + lambda { "%.\n3f" % 1.2 }.should raise_error(ArgumentError) + lambda { "%.3\nf" % 1.2 }.should raise_error(ArgumentError) + lambda { "%.\03f" % 1.2 }.should raise_error(ArgumentError) + lambda { "%.3\0f" % 1.2 }.should raise_error(ArgumentError) + ensure + $DEBUG = old_debug + end + end + + it "ignores unused arguments when $DEBUG is false" do + begin + old_debug = $DEBUG + $DEBUG = false + + ("" % [1, 2, 3]).should == "" + ("%s" % [1, 2, 3]).should == "1" + ensure + $DEBUG = old_debug + end + end + + it "raises an ArgumentError for unused arguments when $DEBUG is true" do + begin + old_debug = $DEBUG + $DEBUG = true + s = $stderr + $stderr = IOStub.new + + lambda { "" % [1, 2, 3] }.should raise_error(ArgumentError) + lambda { "%s" % [1, 2, 3] }.should raise_error(ArgumentError) + ensure + $DEBUG = old_debug + $stderr = s + end + end + + it "always allows unused arguments when positional argument style is used" do + begin + old_debug = $DEBUG + $DEBUG = false + + ("%2$s" % [1, 2, 3]).should == "2" + $DEBUG = true + ("%2$s" % [1, 2, 3]).should == "2" + ensure + $DEBUG = old_debug + end + end + + it "replaces trailing absolute argument specifier without type with percent sign" do + ("hello %1$" % "foo").should == "hello %" + end + + it "raises an ArgumentError when given invalid argument specifiers" do + lambda { "%1" % [] }.should raise_error(ArgumentError) + lambda { "%+" % [] }.should raise_error(ArgumentError) + lambda { "%-" % [] }.should raise_error(ArgumentError) + lambda { "%#" % [] }.should raise_error(ArgumentError) + lambda { "%0" % [] }.should raise_error(ArgumentError) + lambda { "%*" % [] }.should raise_error(ArgumentError) + lambda { "%." % [] }.should raise_error(ArgumentError) + lambda { "%_" % [] }.should raise_error(ArgumentError) + lambda { "%0$s" % "x" }.should raise_error(ArgumentError) + lambda { "%*0$s" % [5, "x"] }.should raise_error(ArgumentError) + lambda { "%*1$.*0$1$s" % [1, 2, 3] }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when multiple positional argument tokens are given for one format specifier" do + lambda { "%1$1$s" % "foo" }.should raise_error(ArgumentError) + end + + it "respects positional arguments and precision tokens given for one format specifier" do + ("%2$1d" % [1, 0]).should == "0" + ("%2$1d" % [0, 1]).should == "1" + + ("%2$.2f" % [1, 0]).should == "0.00" + ("%2$.2f" % [0, 1]).should == "1.00" + end + + it "allows more than one digit of position" do + ("%50$d" % (0..100).to_a).should == "49" + end + + it "raises an ArgumentError when multiple width star tokens are given for one format specifier" do + lambda { "%**s" % [5, 5, 5] }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when a width star token is seen after a width token" do + lambda { "%5*s" % [5, 5] }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when multiple precision tokens are given" do + lambda { "%.5.5s" % 5 }.should raise_error(ArgumentError) + lambda { "%.5.*s" % [5, 5] }.should raise_error(ArgumentError) + lambda { "%.*.5s" % [5, 5] }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when there are less arguments than format specifiers" do + ("foo" % []).should == "foo" + lambda { "%s" % [] }.should raise_error(ArgumentError) + lambda { "%s %s" % [1] }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when absolute and relative argument numbers are mixed" do + lambda { "%s %1$s" % "foo" }.should raise_error(ArgumentError) + lambda { "%1$s %s" % "foo" }.should raise_error(ArgumentError) + + lambda { "%s %2$s" % ["foo", "bar"] }.should raise_error(ArgumentError) + lambda { "%2$s %s" % ["foo", "bar"] }.should raise_error(ArgumentError) + + lambda { "%*2$s" % [5, 5, 5] }.should raise_error(ArgumentError) + lambda { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError) + lambda { "%*2$.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError) + lambda { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError) + end + + it "allows reuse of the one argument multiple via absolute argument numbers" do + ("%1$s %1$s" % "foo").should == "foo foo" + ("%1$s %2$s %1$s %2$s" % ["foo", "bar"]).should == "foo bar foo bar" + end + + it "always interprets an array argument as a list of argument parameters" do + lambda { "%p" % [] }.should raise_error(ArgumentError) + ("%p" % [1]).should == "1" + ("%p %p" % [1, 2]).should == "1 2" + end + + it "always interprets an array subclass argument as a list of argument parameters" do + lambda { "%p" % StringSpecs::MyArray[] }.should raise_error(ArgumentError) + ("%p" % StringSpecs::MyArray[1]).should == "1" + ("%p %p" % StringSpecs::MyArray[1, 2]).should == "1 2" + end + + it "allows positional arguments for width star and precision star arguments" do + ("%*1$.*2$3$d" % [10, 5, 1]).should == " 00001" + end + + it "allows negative width to imply '-' flag" do + ("%*1$.*2$3$d" % [-10, 5, 1]).should == "00001 " + ("%-*1$.*2$3$d" % [10, 5, 1]).should == "00001 " + ("%-*1$.*2$3$d" % [-10, 5, 1]).should == "00001 " + end + + it "ignores negative precision" do + ("%*1$.*2$3$d" % [10, -5, 1]).should == " 1" + end + + it "allows a star to take an argument number to use as the width" do + ("%1$*2$s" % ["a", 8]).should == " a" + ("%1$*10$s" % ["a",0,0,0,0,0,0,0,0,8]).should == " a" + end + + it "calls to_int on width star and precision star tokens" do + w = mock('10') + w.should_receive(:to_int).and_return(10) + + p = mock('5') + p.should_receive(:to_int).and_return(5) + + ("%*.*f" % [w, p, 1]).should == " 1.00000" + + + w = mock('10') + w.should_receive(:to_int).and_return(10) + + p = mock('5') + p.should_receive(:to_int).and_return(5) + + ("%*.*d" % [w, p, 1]).should == " 00001" + end + + it "does not call #to_a to convert the argument" do + x = mock("string modulo to_a") + x.should_not_receive(:to_a) + x.should_receive(:to_s).and_return("x") + + ("%s" % x).should == "x" + end + + it "calls #to_ary to convert the argument" do + x = mock("string modulo to_ary") + x.should_not_receive(:to_s) + x.should_receive(:to_ary).and_return(["x"]) + + ("%s" % x).should == "x" + end + + it "wraps the object in an Array if #to_ary returns nil" do + x = mock("string modulo to_ary") + x.should_receive(:to_ary).and_return(nil) + x.should_receive(:to_s).and_return("x") + + ("%s" % x).should == "x" + end + + it "raises a TypeError if #to_ary does not return an Array" do + x = mock("string modulo to_ary") + x.should_receive(:to_ary).and_return("x") + + lambda { "%s" % x }.should raise_error(TypeError) + end + + it "tries to convert the argument to Array by calling #to_ary" do + obj = mock('[1,2]') + def obj.to_ary() [1, 2] end + def obj.to_s() "obj" end + ("%s %s" % obj).should == "1 2" + ("%s" % obj).should == "1" + end + + it "doesn't return subclass instances when called on a subclass" do + universal = mock('0') + def universal.to_int() 0 end + def universal.to_str() "0" end + def universal.to_f() 0.0 end + + [ + "", "foo", + "%b", "%B", "%c", "%d", "%e", "%E", + "%f", "%g", "%G", "%i", "%o", "%p", + "%s", "%u", "%x", "%X" + ].each do |format| + (StringSpecs::MyString.new(format) % universal).should be_an_instance_of(String) + end + end + + it "always taints the result when the format string is tainted" do + universal = mock('0') + def universal.to_int() 0 end + def universal.to_str() "0" end + def universal.to_f() 0.0 end + + [ + "", "foo", + "%b", "%B", "%c", "%d", "%e", "%E", + "%f", "%g", "%G", "%i", "%o", "%p", + "%s", "%u", "%x", "%X" + ].each do |format| + subcls_format = StringSpecs::MyString.new(format) + subcls_format.taint + format.taint + + (format % universal).tainted?.should == true + (subcls_format % universal).tainted?.should == true + end + end + + it "supports binary formats using %b for positive numbers" do + ("%b" % 10).should == "1010" + ("% b" % 10).should == " 1010" + ("%1$b" % [10, 20]).should == "1010" + ("%#b" % 10).should == "0b1010" + ("%+b" % 10).should == "+1010" + ("%-9b" % 10).should == "1010 " + ("%05b" % 10).should == "01010" + ("%*b" % [10, 6]).should == " 110" + ("%*b" % [-10, 6]).should == "110 " + ("%.4b" % 2).should == "0010" + ("%.32b" % 2147483648).should == "10000000000000000000000000000000" + end + + it "supports binary formats using %b for negative numbers" do + ("%b" % -5).should == "..1011" + ("%0b" % -5).should == "..1011" + ("%.1b" % -5).should == "..1011" + ("%.7b" % -5).should == "..11011" + ("%.10b" % -5).should == "..11111011" + ("% b" % -5).should == "-101" + ("%+b" % -5).should == "-101" + not_supported_on :opal do + ("%b" % -(2 ** 64 + 5)).should == + "..101111111111111111111111111111111111111111111111111111111111111011" + end + end + + it "supports binary formats using %B with same behaviour as %b except for using 0B instead of 0b for #" do + ("%B" % 10).should == ("%b" % 10) + ("% B" % 10).should == ("% b" % 10) + ("%1$B" % [10, 20]).should == ("%1$b" % [10, 20]) + ("%+B" % 10).should == ("%+b" % 10) + ("%-9B" % 10).should == ("%-9b" % 10) + ("%05B" % 10).should == ("%05b" % 10) + ("%*B" % [10, 6]).should == ("%*b" % [10, 6]) + ("%*B" % [-10, 6]).should == ("%*b" % [-10, 6]) + + ("%B" % -5).should == ("%b" % -5) + ("%0B" % -5).should == ("%0b" % -5) + ("%.1B" % -5).should == ("%.1b" % -5) + ("%.7B" % -5).should == ("%.7b" % -5) + ("%.10B" % -5).should == ("%.10b" % -5) + ("% B" % -5).should == ("% b" % -5) + ("%+B" % -5).should == ("%+b" % -5) + not_supported_on :opal do + ("%B" % -(2 ** 64 + 5)).should == ("%b" % -(2 ** 64 + 5)) + end + + ("%#B" % 10).should == "0B1010" + end + + it "supports character formats using %c" do + ("%c" % 10).should == "\n" + ("%2$c" % [10, 11, 14]).should == "\v" + ("%-4c" % 10).should == "\n " + ("%*c" % [10, 3]).should == " \003" + ("%c" % 42).should == "*" + + lambda { "%c" % Object }.should raise_error(TypeError) + end + + it "supports single character strings as argument for %c" do + ("%c" % 'A').should == "A" + end + + it "raises an exception for multiple character strings as argument for %c" do + lambda { "%c" % 'AA' }.should raise_error(ArgumentError) + end + + it "calls to_str on argument for %c formats" do + obj = mock('A') + obj.should_receive(:to_str).and_return('A') + + ("%c" % obj).should == "A" + end + + it "calls #to_ary on argument for %c formats" do + obj = mock('65') + obj.should_receive(:to_ary).and_return([65]) + ("%c" % obj).should == ("%c" % [65]) + end + + it "calls #to_int on argument for %c formats, if the argument does not respond to #to_ary" do + obj = mock('65') + obj.should_receive(:to_int).and_return(65) + + ("%c" % obj).should == ("%c" % 65) + end + + %w(d i).each do |f| + format = "%" + f + + it "supports integer formats using #{format}" do + ("%#{f}" % 10).should == "10" + ("% #{f}" % 10).should == " 10" + ("%1$#{f}" % [10, 20]).should == "10" + ("%+#{f}" % 10).should == "+10" + ("%-7#{f}" % 10).should == "10 " + ("%04#{f}" % 10).should == "0010" + ("%*#{f}" % [10, 4]).should == " 4" + ("%6.4#{f}" % 123).should == " 0123" + end + + it "supports negative integers using #{format}" do + ("%#{f}" % -5).should == "-5" + ("%3#{f}" % -5).should == " -5" + ("%03#{f}" % -5).should == "-05" + ("%+03#{f}" % -5).should == "-05" + ("%+.2#{f}" % -5).should == "-05" + ("%-3#{f}" % -5).should == "-5 " + ("%6.4#{f}" % -123).should == " -0123" + end + + it "supports negative integers using #{format}, giving priority to `-`" do + ("%-03#{f}" % -5).should == "-5 " + ("%+-03#{f}" % -5).should == "-5 " + end + end + + it "supports float formats using %e" do + ("%e" % 10).should == "1.000000e+01" + ("% e" % 10).should == " 1.000000e+01" + ("%1$e" % 10).should == "1.000000e+01" + ("%#e" % 10).should == "1.000000e+01" + ("%+e" % 10).should == "+1.000000e+01" + ("%-7e" % 10).should == "1.000000e+01" + ("%05e" % 10).should == "1.000000e+01" + ("%*e" % [10, 9]).should == "9.000000e+00" + end + + it "supports float formats using %e, but Inf, -Inf, and NaN are not floats" do + ("%e" % 1e1020).should == "Inf" + ("%e" % -1e1020).should == "-Inf" + ("%e" % -Float::NAN).should == "NaN" + ("%e" % Float::NAN).should == "NaN" + end + + it "supports float formats using %E, but Inf, -Inf, and NaN are not floats" do + ("%E" % 1e1020).should == "Inf" + ("%E" % -1e1020).should == "-Inf" + ("%-10E" % 1e1020).should == "Inf " + ("%10E" % 1e1020).should == " Inf" + ("%+E" % 1e1020).should == "+Inf" + ("% E" % 1e1020).should == " Inf" + ("%E" % Float::NAN).should == "NaN" + ("%E" % -Float::NAN).should == "NaN" + end + + it "supports float formats using %E" do + ("%E" % 10).should == "1.000000E+01" + ("% E" % 10).should == " 1.000000E+01" + ("%1$E" % 10).should == "1.000000E+01" + ("%#E" % 10).should == "1.000000E+01" + ("%+E" % 10).should == "+1.000000E+01" + ("%-7E" % 10).should == "1.000000E+01" + ("%05E" % 10).should == "1.000000E+01" + ("%*E" % [10, 9]).should == "9.000000E+00" + end + + it "pads with spaces for %E with Inf, -Inf, and NaN" do + ("%010E" % -1e1020).should == " -Inf" + ("%010E" % 1e1020).should == " Inf" + ("%010E" % Float::NAN).should == " NaN" + end + + it "supports float formats using %f" do + ("%f" % 10).should == "10.000000" + ("% f" % 10).should == " 10.000000" + ("%1$f" % 10).should == "10.000000" + ("%#f" % 10).should == "10.000000" + ("%#0.3f" % 10).should == "10.000" + ("%+f" % 10).should == "+10.000000" + ("%-7f" % 10).should == "10.000000" + ("%05f" % 10).should == "10.000000" + ("%0.5f" % 10).should == "10.00000" + ("%*f" % [10, 9]).should == " 9.000000" + end + + it "supports float formats using %g" do + ("%g" % 10).should == "10" + ("% g" % 10).should == " 10" + ("%1$g" % 10).should == "10" + ("%#g" % 10).should == "10.0000" + ("%#.3g" % 10).should == "10.0" + ("%+g" % 10).should == "+10" + ("%-7g" % 10).should == "10 " + ("%05g" % 10).should == "00010" + ("%g" % 10**10).should == "1e+10" + ("%*g" % [10, 9]).should == " 9" + end + + it "supports float formats using %G" do + ("%G" % 10).should == "10" + ("% G" % 10).should == " 10" + ("%1$G" % 10).should == "10" + ("%#G" % 10).should == "10.0000" + ("%#.3G" % 10).should == "10.0" + ("%+G" % 10).should == "+10" + ("%-7G" % 10).should == "10 " + ("%05G" % 10).should == "00010" + ("%G" % 10**10).should == "1E+10" + ("%*G" % [10, 9]).should == " 9" + end + + it "supports octal formats using %o for positive numbers" do + ("%o" % 10).should == "12" + ("% o" % 10).should == " 12" + ("%1$o" % [10, 20]).should == "12" + ("%#o" % 10).should == "012" + ("%+o" % 10).should == "+12" + ("%-9o" % 10).should == "12 " + ("%05o" % 10).should == "00012" + ("%*o" % [10, 6]).should == " 6" + end + + it "supports octal formats using %o for negative numbers" do + # These are incredibly wrong. -05 == -5, not 7177777...whatever + ("%o" % -5).should == "..73" + ("%0o" % -5).should == "..73" + ("%.4o" % 20).should == "0024" + ("%.1o" % -5).should == "..73" + ("%.7o" % -5).should == "..77773" + ("%.10o" % -5).should == "..77777773" + + ("% o" % -26).should == "-32" + ("%+o" % -26).should == "-32" + not_supported_on :opal do + ("%o" % -(2 ** 64 + 5)).should == "..75777777777777777777773" + end + end + + it "supports inspect formats using %p" do + ("%p" % 10).should == "10" + ("%1$p" % [10, 5]).should == "10" + ("%-22p" % 10).should == "10 " + ("%*p" % [10, 10]).should == " 10" + ("%p" % {capture: 1}).should == "{:capture=>1}" + ("%p" % "str").should == "\"str\"" + end + + it "calls inspect on arguments for %p format" do + obj = mock('obj') + def obj.inspect() "obj" end + ("%p" % obj).should == "obj" + + # undef is not working + # obj = mock('obj') + # class << obj; undef :inspect; end + # def obj.method_missing(*args) "obj" end + # ("%p" % obj).should == "obj" + end + + it "taints result for %p when argument.inspect is tainted" do + obj = mock('x') + def obj.inspect() "x".taint end + + ("%p" % obj).tainted?.should == true + + obj = mock('x'); obj.taint + def obj.inspect() "x" end + + ("%p" % obj).tainted?.should == false + end + + it "supports string formats using %s" do + ("%s" % "hello").should == "hello" + ("%s" % "").should == "" + ("%s" % 10).should == "10" + ("%1$s" % [10, 8]).should == "10" + ("%-5s" % 10).should == "10 " + ("%*s" % [10, 9]).should == " 9" + end + + it "respects a space padding request not as part of the width" do + x = "% -5s" % ["foo"] + x.should == "foo " + end + + it "calls to_s on non-String arguments for %s format" do + obj = mock('obj') + def obj.to_s() "obj" end + + ("%s" % obj).should == "obj" + + # undef doesn't work + # obj = mock('obj') + # class << obj; undef :to_s; end + # def obj.method_missing(*args) "obj" end + # + # ("%s" % obj).should == "obj" + end + + it "taints result for %s when argument is tainted" do + ("%s" % "x".taint).tainted?.should == true + ("%s" % mock('x').taint).tainted?.should == true + end + + # MRI crashes on this one. + # See http://groups.google.com/group/ruby-core-google/t/c285c18cd94c216d + it "raises an ArgumentError for huge precisions for %s" do + block = lambda { "%.25555555555555555555555555555555555555s" % "hello world" } + block.should raise_error(ArgumentError) + end + + # Note: %u has been changed to an alias for %d in 1.9. + it "supports unsigned formats using %u" do + ("%u" % 10).should == "10" + ("% u" % 10).should == " 10" + ("%1$u" % [10, 20]).should == "10" + ("%+u" % 10).should == "+10" + ("%-7u" % 10).should == "10 " + ("%04u" % 10).should == "0010" + ("%*u" % [10, 4]).should == " 4" + end + + it "formats negative values with a leading sign using %u" do + ("% u" % -26).should == "-26" + ("%+u" % -26).should == "-26" + end + + it "supports negative bignums with %u or %d" do + ("%u" % -(2 ** 64 + 5)).should == "-18446744073709551621" + ("%d" % -(2 ** 64 + 5)).should == "-18446744073709551621" + end + + it "supports hex formats using %x for positive numbers" do + ("%x" % 10).should == "a" + ("% x" % 10).should == " a" + ("%1$x" % [10, 20]).should == "a" + ("%#x" % 10).should == "0xa" + ("%+x" % 10).should == "+a" + ("%-9x" % 10).should == "a " + ("%05x" % 10).should == "0000a" + ("%*x" % [10, 6]).should == " 6" + ("%.4x" % 20).should == "0014" + ("%x" % 0xFFFFFFFF).should == "ffffffff" + end + + it "supports hex formats using %x for negative numbers" do + ("%x" % -5).should == "..fb" + ("%0x" % -5).should == "..fb" + ("%.1x" % -5).should == "..fb" + ("%.7x" % -5).should == "..ffffb" + ("%.10x" % -5).should == "..fffffffb" + ("% x" % -26).should == "-1a" + ("%+x" % -26).should == "-1a" + not_supported_on :opal do + ("%x" % -(2 ** 64 + 5)).should == "..fefffffffffffffffb" + end + end + + it "supports hex formats using %X for positive numbers" do + ("%X" % 10).should == "A" + ("% X" % 10).should == " A" + ("%1$X" % [10, 20]).should == "A" + ("%#X" % 10).should == "0XA" + ("%+X" % 10).should == "+A" + ("%-9X" % 10).should == "A " + ("%05X" % 10).should == "0000A" + ("%*X" % [10, 6]).should == " 6" + ("%X" % 0xFFFFFFFF).should == "FFFFFFFF" + end + + it "supports hex formats using %X for negative numbers" do + ("%X" % -5).should == "..FB" + ("%0X" % -5).should == "..FB" + ("%.1X" % -5).should == "..FB" + ("%.7X" % -5).should == "..FFFFB" + ("%.10X" % -5).should == "..FFFFFFFB" + ("% X" % -26).should == "-1A" + ("%+X" % -26).should == "-1A" + not_supported_on :opal do + ("%X" % -(2 ** 64 + 5)).should == "..FEFFFFFFFFFFFFFFFB" + end + end + + it "formats zero without prefix using %#x" do + ("%#x" % 0).should == "0" + end + + it "formats zero without prefix using %#X" do + ("%#X" % 0).should == "0" + end + + %w(b d i o u x X).each do |f| + format = "%" + f + + it "behaves as if calling Kernel#Integer for #{format} argument, if it does not respond to #to_ary" do + (format % "10").should == (format % Kernel.Integer("10")) + (format % "0x42").should == (format % Kernel.Integer("0x42")) + (format % "0b1101").should == (format % Kernel.Integer("0b1101")) + (format % "0b1101_0000").should == (format % Kernel.Integer("0b1101_0000")) + (format % "0777").should == (format % Kernel.Integer("0777")) + lambda { + # see [ruby-core:14139] for more details + (format % "0777").should == (format % Kernel.Integer("0777")) + }.should_not raise_error(ArgumentError) + + lambda { format % "0__7_7_7" }.should raise_error(ArgumentError) + + lambda { format % "" }.should raise_error(ArgumentError) + lambda { format % "x" }.should raise_error(ArgumentError) + lambda { format % "5x" }.should raise_error(ArgumentError) + lambda { format % "08" }.should raise_error(ArgumentError) + lambda { format % "0b2" }.should raise_error(ArgumentError) + lambda { format % "123__456" }.should raise_error(ArgumentError) + + obj = mock('5') + obj.should_receive(:to_i).and_return(5) + (format % obj).should == (format % 5) + + obj = mock('6') + obj.stub!(:to_i).and_return(5) + obj.should_receive(:to_int).and_return(6) + (format % obj).should == (format % 6) + end + end + + %w(e E f g G).each do |f| + format = "%" + f + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('3.14') + obj.should_receive(:to_ary).and_return([3.14]) + (format % obj).should == (format % [3.14]) + end + + it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument does not respond to #to_ary" do + (format % 10).should == (format % 10.0) + (format % "-10.4e-20").should == (format % -10.4e-20) + (format % ".5").should == (format % 0.5) + (format % "-.5").should == (format % -0.5) + # Something's strange with this spec: + # it works just fine in individual mode, but not when run as part of a group + (format % "10_1_0.5_5_5").should == (format % 1010.555) + + (format % "0777").should == (format % 777) + + lambda { format % "" }.should raise_error(ArgumentError) + lambda { format % "x" }.should raise_error(ArgumentError) + lambda { format % "." }.should raise_error(ArgumentError) + lambda { format % "10." }.should raise_error(ArgumentError) + lambda { format % "5x" }.should raise_error(ArgumentError) + lambda { format % "0b1" }.should raise_error(ArgumentError) + lambda { format % "10e10.5" }.should raise_error(ArgumentError) + lambda { format % "10__10" }.should raise_error(ArgumentError) + lambda { format % "10.10__10" }.should raise_error(ArgumentError) + + obj = mock('5.0') + obj.should_receive(:to_f).and_return(5.0) + (format % obj).should == (format % 5.0) + end + + it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument is hexadecimal string" do + (format % "0xA").should == (format % 0xA) + end + + it "doesn't taint the result for #{format} when argument is tainted" do + (format % "5".taint).tainted?.should == false + end + end + + describe "when format string contains %{} sections" do + it "replaces %{} sections with values from passed-in hash" do + ("%{foo}bar" % {foo: 'oof'}).should == "oofbar" + end + + it "raises KeyError if key is missing from passed-in hash" do + lambda {"%{foo}" % {}}.should raise_error(KeyError) + end + + it "should raise ArgumentError if no hash given" do + lambda {"%{foo}" % []}.should raise_error(ArgumentError) + end + end + + describe "when format string contains %<> formats" do + it "uses the named argument for the format's value" do + ("%d" % {foo: 1}).should == "1" + end + + it "raises KeyError if key is missing from passed-in hash" do + lambda {"%d" % {}}.should raise_error(KeyError) + end + + it "should raise ArgumentError if no hash given" do + lambda {"%" % []}.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/string/multiply_spec.rb b/spec/rubyspec/core/string/multiply_spec.rb new file mode 100644 index 0000000000..d932ebeb8e --- /dev/null +++ b/spec/rubyspec/core/string/multiply_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../../../shared/string/times', __FILE__) + +describe "String#*" do + it_behaves_like :string_times, :*, ->(str, times) { str * times } +end diff --git a/spec/rubyspec/core/string/new_spec.rb b/spec/rubyspec/core/string/new_spec.rb new file mode 100644 index 0000000000..0a246f6f53 --- /dev/null +++ b/spec/rubyspec/core/string/new_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "String.new" do + it "returns an instance of String" do + str = String.new + str.should be_an_instance_of(String) + end + + ruby_version_is "2.3" do + it "accepts an encoding argument" do + xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding 'utf-8' + str = String.new(xA4xA2, encoding: 'euc-jp') + str.encoding.should == Encoding::EUC_JP + end + end + + it "returns a fully-formed String" do + str = String.new + str.size.should == 0 + str << "more" + str.should == "more" + end + + it "returns a new string given a string argument" do + str1 = "test" + str = String.new(str1) + str.should be_an_instance_of(String) + str.should == str1 + str << "more" + str.should == "testmore" + end + + it "returns an instance of a subclass" do + a = StringSpecs::MyString.new("blah") + a.should be_an_instance_of(StringSpecs::MyString) + a.should == "blah" + end + + it "is called on subclasses" do + s = StringSpecs::SubString.new + s.special.should == nil + s.should == "" + + s = StringSpecs::SubString.new "subclass" + s.special.should == "subclass" + s.should == "" + end + + it "raises TypeError on inconvertible object" do + lambda { String.new 5 }.should raise_error(TypeError) + lambda { String.new nil }.should raise_error(TypeError) + end + + it "returns a binary String" do + String.new.encoding.should == Encoding::BINARY + end +end diff --git a/spec/rubyspec/core/string/next_spec.rb b/spec/rubyspec/core/string/next_spec.rb new file mode 100644 index 0000000000..6b4a98d993 --- /dev/null +++ b/spec/rubyspec/core/string/next_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/succ.rb', __FILE__) + +describe "String#next" do + it_behaves_like(:string_succ, :next) +end + +describe "String#next!" do + it_behaves_like(:string_succ_bang, :"next!") +end diff --git a/spec/rubyspec/core/string/oct_spec.rb b/spec/rubyspec/core/string/oct_spec.rb new file mode 100644 index 0000000000..7cdbabc436 --- /dev/null +++ b/spec/rubyspec/core/string/oct_spec.rb @@ -0,0 +1,88 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +# Note: We can't completely spec this in terms of to_int() because hex() +# allows the base to be changed by a base specifier in the string. +# See http://groups.google.com/group/ruby-core-google/browse_frm/thread/b53e9c2003425703 +describe "String#oct" do + it "treats numeric digits as base-8 digits by default" do + "0".oct.should == 0 + "77".oct.should == 077 + "077".oct.should == 077 + end + + it "accepts numbers formatted as binary" do + "0b1010".oct.should == 0b1010 + end + + it "accepts numbers formatted as hexadecimal" do + "0xFF".oct.should == 0xFF + end + + it "accepts numbers formatted as decimal" do + "0d500".oct.should == 500 + end + + describe "with a leading minus sign" do + it "treats numeric digits as base-8 digits by default" do + "-12348".oct.should == -01234 + end + + it "accepts numbers formatted as binary" do + "-0b0101".oct.should == -0b0101 + end + + it "accepts numbers formatted as hexadecimal" do + "-0xEE".oct.should == -0xEE + end + + it "accepts numbers formatted as decimal" do + "-0d500".oct.should == -500 + end + end + + describe "with a leading plus sign" do + it "treats numeric digits as base-8 digits by default" do + "+12348".oct.should == 01234 + end + + it "accepts numbers formatted as binary" do + "+0b1010".oct.should == 0b1010 + end + + it "accepts numbers formatted as hexadecimal" do + "+0xFF".oct.should == 0xFF + end + + it "accepts numbers formatted as decimal" do + "+0d500".oct.should == 500 + end + end + + it "accepts a single underscore separating digits" do + "755_333".oct.should == 0755_333 + end + + it "does not accept a sequence of underscores as part of a number" do + "7__3".oct.should == 07 + "7___3".oct.should == 07 + "7__5".oct.should == 07 + end + + it "ignores characters that are incorrect for the base-8 digits" do + "0o".oct.should == 0 + "5678".oct.should == 0567 + end + + it "returns 0 if no characters can be interpreted as a base-8 number" do + "".oct.should == 0 + "+-5".oct.should == 0 + "wombat".oct.should == 0 + end + + it "returns 0 for strings with leading underscores" do + "_7".oct.should == 0 + "_07".oct.should == 0 + " _7".oct.should == 0 + end +end diff --git a/spec/rubyspec/core/string/ord_spec.rb b/spec/rubyspec/core/string/ord_spec.rb new file mode 100644 index 0000000000..64466fde58 --- /dev/null +++ b/spec/rubyspec/core/string/ord_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "String#ord" do + it "returns a Fixnum" do + 'a'.ord.should be_an_instance_of(Fixnum) + end + + it "returns the codepoint of the first character in the String" do + 'a'.ord.should == 97 + end + + + it "ignores subsequent characters" do + "\u{287}a".ord.should == "\u{287}".ord + end + + it "understands multibyte characters" do + "\u{9879}".ord.should == 39033 + end + + it "is equivalent to #codepoints.first" do + "\u{981}\u{982}".ord.should == "\u{981}\u{982}".codepoints.first + end + + it "raises an ArgumentError if called on an empty String" do + lambda { ''.ord }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/string/partition_spec.rb b/spec/rubyspec/core/string/partition_spec.rb new file mode 100644 index 0000000000..04f49db1b1 --- /dev/null +++ b/spec/rubyspec/core/string/partition_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#partition with String" do + it "returns an array of substrings based on splitting on the given string" do + "hello world".partition("o").should == ["hell", "o", " world"] + end + + it "always returns 3 elements" do + "hello".partition("x").should == ["hello", "", ""] + "hello".partition("hello").should == ["", "hello", ""] + end + + it "accepts regexp" do + "hello!".partition(/l./).should == ["he", "ll", "o!"] + end + + it "sets global vars if regexp used" do + "hello!".partition(/(.l)(.o)/) + $1.should == "el" + $2.should == "lo" + end + + it "converts its argument using :to_str" do + find = mock('l') + find.should_receive(:to_str).and_return("l") + "hello".partition(find).should == ["he","l","lo"] + end + + it "raises an error if not convertible to string" do + lambda{ "hello".partition(5) }.should raise_error(TypeError) + lambda{ "hello".partition(nil) }.should raise_error(TypeError) + end + + it "takes precedence over a given block" do + "hello world".partition("o") { true }.should == ["hell", "o", " world"] + end +end diff --git a/spec/rubyspec/core/string/plus_spec.rb b/spec/rubyspec/core/string/plus_spec.rb new file mode 100644 index 0000000000..addc8873eb --- /dev/null +++ b/spec/rubyspec/core/string/plus_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/concat', __FILE__) + +describe "String#+" do + it "returns a new string containing the given string concatenated to self" do + ("" + "").should == "" + ("" + "Hello").should == "Hello" + ("Hello" + "").should == "Hello" + ("Ruby !" + "= Rubinius").should == "Ruby != Rubinius" + end + + it "converts any non-String argument with #to_str" do + c = mock 'str' + c.should_receive(:to_str).any_number_of_times.and_return(' + 1 = 2') + + ("1" + c).should == '1 + 1 = 2' + end + + it "raises a TypeError when given any object that fails #to_str" do + lambda { "" + Object.new }.should raise_error(TypeError) + lambda { "" + 65 }.should raise_error(TypeError) + end + + it "doesn't return subclass instances" do + (StringSpecs::MyString.new("hello") + "").should be_an_instance_of(String) + (StringSpecs::MyString.new("hello") + "foo").should be_an_instance_of(String) + (StringSpecs::MyString.new("hello") + StringSpecs::MyString.new("foo")).should be_an_instance_of(String) + (StringSpecs::MyString.new("hello") + StringSpecs::MyString.new("")).should be_an_instance_of(String) + (StringSpecs::MyString.new("") + StringSpecs::MyString.new("")).should be_an_instance_of(String) + ("hello" + StringSpecs::MyString.new("foo")).should be_an_instance_of(String) + ("hello" + StringSpecs::MyString.new("")).should be_an_instance_of(String) + end + + it "taints the result when self or other is tainted" do + strs = ["", "OK", StringSpecs::MyString.new(""), StringSpecs::MyString.new("OK")] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + strs.each do |other| + (str + other).tainted?.should == (str.tainted? | other.tainted?) + end + end + end + + it_behaves_like :string_concat_encoding, :+ +end diff --git a/spec/rubyspec/core/string/prepend_spec.rb b/spec/rubyspec/core/string/prepend_spec.rb new file mode 100644 index 0000000000..17e97fd844 --- /dev/null +++ b/spec/rubyspec/core/string/prepend_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#prepend" do + it "prepends the given argument to self and returns self" do + str = "world" + str.prepend("hello ").should equal(str) + str.should == "hello world" + end + + it "converts the given argument to a String using to_str" do + obj = mock("hello") + obj.should_receive(:to_str).and_return("hello") + a = " world!".prepend(obj) + a.should == "hello world!" + end + + it "raises a TypeError if the given argument can't be converted to a String" do + lambda { "hello ".prepend [] }.should raise_error(TypeError) + lambda { 'hello '.prepend mock('x') }.should raise_error(TypeError) + end + + it "raises a RuntimeError when self is frozen" do + a = "hello" + a.freeze + + lambda { a.prepend "" }.should raise_error(RuntimeError) + lambda { a.prepend "test" }.should raise_error(RuntimeError) + end + + it "works when given a subclass instance" do + a = " world" + a.prepend StringSpecs::MyString.new("hello") + a.should == "hello world" + end + + it "taints self if other is tainted" do + x = "x" + x.prepend("".taint).tainted?.should be_true + + x = "x" + x.prepend("y".taint).tainted?.should be_true + end + + ruby_version_is "2.4" do + it "takes multiple arguments" do + str = " world" + str.prepend "he", "", "llo" + str.should == "hello world" + end + + it "prepends the initial value when given arguments contain 2 self" do + str = "hello" + str.prepend str, str + str.should == "hellohellohello" + end + + it "returns self when given no arguments" do + str = "hello" + str.prepend.should equal(str) + str.should == "hello" + end + end +end diff --git a/spec/rubyspec/core/string/replace_spec.rb b/spec/rubyspec/core/string/replace_spec.rb new file mode 100644 index 0000000000..0f59a9a1ab --- /dev/null +++ b/spec/rubyspec/core/string/replace_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/replace', __FILE__) + +describe "String#replace" do + it_behaves_like :string_replace, :replace +end diff --git a/spec/rubyspec/core/string/reverse_spec.rb b/spec/rubyspec/core/string/reverse_spec.rb new file mode 100644 index 0000000000..c37e815ba3 --- /dev/null +++ b/spec/rubyspec/core/string/reverse_spec.rb @@ -0,0 +1,52 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#reverse" do + it "returns a new string with the characters of self in reverse order" do + "stressed".reverse.should == "desserts" + "m".reverse.should == "m" + "".reverse.should == "" + end + + it "taints the result if self is tainted" do + "".taint.reverse.tainted?.should == true + "m".taint.reverse.tainted?.should == true + end + + with_feature :encoding do + it "reverses a string with multi byte characters" do + "微軟正黑體".reverse.should == "體黑正軟微" + end + end + +end + +describe "String#reverse!" do + it "reverses self in place and always returns self" do + a = "stressed" + a.reverse!.should equal(a) + a.should == "desserts" + + "".reverse!.should == "" + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda { "anna".freeze.reverse! }.should raise_error(RuntimeError) + lambda { "hello".freeze.reverse! }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on a frozen instance that would not be modified" do + lambda { "".freeze.reverse! }.should raise_error(RuntimeError) + end + + with_feature :encoding do + it "reverses a string with multi byte characters" do + str = "微軟正黑體" + str.reverse! + str.should == "體黑正軟微" + end + end +end diff --git a/spec/rubyspec/core/string/rindex_spec.rb b/spec/rubyspec/core/string/rindex_spec.rb new file mode 100644 index 0000000000..7085914189 --- /dev/null +++ b/spec/rubyspec/core/string/rindex_spec.rb @@ -0,0 +1,368 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../fixtures/utf-8-encoding.rb', __FILE__) + +describe "String#rindex with object" do + it "raises a TypeError if obj isn't a String, Fixnum or Regexp" do + not_supported_on :opal do + lambda { "hello".rindex(:sym) }.should raise_error(TypeError) + end + lambda { "hello".rindex(mock('x')) }.should raise_error(TypeError) + end + + it "doesn't try to convert obj to an integer via to_int" do + obj = mock('x') + obj.should_not_receive(:to_int) + lambda { "hello".rindex(obj) }.should raise_error(TypeError) + end + + it "tries to convert obj to a string via to_str" do + obj = mock('lo') + def obj.to_str() "lo" end + "hello".rindex(obj).should == "hello".rindex("lo") + + obj = mock('o') + def obj.respond_to?(arg, *) true end + def obj.method_missing(*args) "o" end + "hello".rindex(obj).should == "hello".rindex("o") + end +end + +describe "String#rindex with String" do + it "behaves the same as String#rindex(char) for one-character strings" do + "blablabla hello cruel world...!".split("").uniq.each do |str| + chr = str[0] + str.rindex(str).should == str.rindex(chr) + + 0.upto(str.size + 1) do |start| + str.rindex(str, start).should == str.rindex(chr, start) + end + + (-str.size - 1).upto(-1) do |start| + str.rindex(str, start).should == str.rindex(chr, start) + end + end + end + + it "behaves the same as String#rindex(?char) for one-character strings" do + "blablabla hello cruel world...!".split("").uniq.each do |str| + chr = str[0] =~ / / ? str[0] : eval("?#{str[0]}") + str.rindex(str).should == str.rindex(chr) + + 0.upto(str.size + 1) do |start| + str.rindex(str, start).should == str.rindex(chr, start) + end + + (-str.size - 1).upto(-1) do |start| + str.rindex(str, start).should == str.rindex(chr, start) + end + end + end + + it "returns the index of the last occurrence of the given substring" do + "blablabla".rindex("").should == 9 + "blablabla".rindex("a").should == 8 + "blablabla".rindex("la").should == 7 + "blablabla".rindex("bla").should == 6 + "blablabla".rindex("abla").should == 5 + "blablabla".rindex("labla").should == 4 + "blablabla".rindex("blabla").should == 3 + "blablabla".rindex("ablabla").should == 2 + "blablabla".rindex("lablabla").should == 1 + "blablabla".rindex("blablabla").should == 0 + + "blablabla".rindex("l").should == 7 + "blablabla".rindex("bl").should == 6 + "blablabla".rindex("abl").should == 5 + "blablabla".rindex("labl").should == 4 + "blablabla".rindex("blabl").should == 3 + "blablabla".rindex("ablabl").should == 2 + "blablabla".rindex("lablabl").should == 1 + "blablabla".rindex("blablabl").should == 0 + + "blablabla".rindex("b").should == 6 + "blablabla".rindex("ab").should == 5 + "blablabla".rindex("lab").should == 4 + "blablabla".rindex("blab").should == 3 + "blablabla".rindex("ablab").should == 2 + "blablabla".rindex("lablab").should == 1 + "blablabla".rindex("blablab").should == 0 + end + + it "doesn't set $~" do + $~ = nil + + 'hello.'.rindex('ll') + $~.should == nil + end + + it "ignores string subclasses" do + "blablabla".rindex(StringSpecs::MyString.new("bla")).should == 6 + StringSpecs::MyString.new("blablabla").rindex("bla").should == 6 + StringSpecs::MyString.new("blablabla").rindex(StringSpecs::MyString.new("bla")).should == 6 + end + + it "starts the search at the given offset" do + "blablabla".rindex("bl", 0).should == 0 + "blablabla".rindex("bl", 1).should == 0 + "blablabla".rindex("bl", 2).should == 0 + "blablabla".rindex("bl", 3).should == 3 + + "blablabla".rindex("bla", 0).should == 0 + "blablabla".rindex("bla", 1).should == 0 + "blablabla".rindex("bla", 2).should == 0 + "blablabla".rindex("bla", 3).should == 3 + + "blablabla".rindex("blab", 0).should == 0 + "blablabla".rindex("blab", 1).should == 0 + "blablabla".rindex("blab", 2).should == 0 + "blablabla".rindex("blab", 3).should == 3 + "blablabla".rindex("blab", 6).should == 3 + "blablablax".rindex("blab", 6).should == 3 + + "blablabla".rindex("la", 1).should == 1 + "blablabla".rindex("la", 2).should == 1 + "blablabla".rindex("la", 3).should == 1 + "blablabla".rindex("la", 4).should == 4 + + "blablabla".rindex("lab", 1).should == 1 + "blablabla".rindex("lab", 2).should == 1 + "blablabla".rindex("lab", 3).should == 1 + "blablabla".rindex("lab", 4).should == 4 + + "blablabla".rindex("ab", 2).should == 2 + "blablabla".rindex("ab", 3).should == 2 + "blablabla".rindex("ab", 4).should == 2 + "blablabla".rindex("ab", 5).should == 5 + + "blablabla".rindex("", 0).should == 0 + "blablabla".rindex("", 1).should == 1 + "blablabla".rindex("", 2).should == 2 + "blablabla".rindex("", 7).should == 7 + "blablabla".rindex("", 8).should == 8 + "blablabla".rindex("", 9).should == 9 + "blablabla".rindex("", 10).should == 9 + end + + it "starts the search at offset + self.length if offset is negative" do + str = "blablabla" + + ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle| + (-str.length .. -1).each do |offset| + str.rindex(needle, offset).should == + str.rindex(needle, offset + str.length) + end + end + end + + it "returns nil if the substring isn't found" do + "blablabla".rindex("B").should == nil + "blablabla".rindex("z").should == nil + "blablabla".rindex("BLA").should == nil + "blablabla".rindex("blablablabla").should == nil + + "hello".rindex("lo", 0).should == nil + "hello".rindex("lo", 1).should == nil + "hello".rindex("lo", 2).should == nil + + "hello".rindex("llo", 0).should == nil + "hello".rindex("llo", 1).should == nil + + "hello".rindex("el", 0).should == nil + "hello".rindex("ello", 0).should == nil + + "hello".rindex("", -6).should == nil + "hello".rindex("", -7).should == nil + + "hello".rindex("h", -6).should == nil + end + + it "tries to convert start_offset to an integer via to_int" do + obj = mock('5') + def obj.to_int() 5 end + "str".rindex("st", obj).should == 0 + + obj = mock('5') + def obj.respond_to?(arg, *) true end + def obj.method_missing(*args) 5 end + "str".rindex("st", obj).should == 0 + end + + it "raises a TypeError when given offset is nil" do + lambda { "str".rindex("st", nil) }.should raise_error(TypeError) + end +end + +describe "String#rindex with Regexp" do + it "behaves the same as String#rindex(string) for escaped string regexps" do + ["blablabla", "hello cruel world...!"].each do |str| + ["", "b", "bla", "lab", "o c", "d."].each do |needle| + regexp = Regexp.new(Regexp.escape(needle)) + str.rindex(regexp).should == str.rindex(needle) + + 0.upto(str.size + 1) do |start| + str.rindex(regexp, start).should == str.rindex(needle, start) + end + + (-str.size - 1).upto(-1) do |start| + str.rindex(regexp, start).should == str.rindex(needle, start) + end + end + end + end + + it "returns the index of the first match from the end of string of regexp" do + "blablabla".rindex(/bla/).should == 6 + "blablabla".rindex(/BLA/i).should == 6 + + "blablabla".rindex(/.{0}/).should == 9 + "blablabla".rindex(/.{1}/).should == 8 + "blablabla".rindex(/.{2}/).should == 7 + "blablabla".rindex(/.{6}/).should == 3 + "blablabla".rindex(/.{9}/).should == 0 + + "blablabla".rindex(/.*/).should == 9 + "blablabla".rindex(/.+/).should == 8 + + "blablabla".rindex(/bla|a/).should == 8 + + not_supported_on :opal do + "blablabla".rindex(/\A/).should == 0 + "blablabla".rindex(/\Z/).should == 9 + "blablabla".rindex(/\z/).should == 9 + "blablabla\n".rindex(/\Z/).should == 10 + "blablabla\n".rindex(/\z/).should == 10 + end + + "blablabla".rindex(/^/).should == 0 + not_supported_on :opal do + "\nblablabla".rindex(/^/).should == 1 + "b\nlablabla".rindex(/^/).should == 2 + end + "blablabla".rindex(/$/).should == 9 + + "blablabla".rindex(/.l./).should == 6 + end + + it "sets $~ to MatchData of match and nil when there's none" do + 'hello.'.rindex(/.(.)/) + $~[0].should == 'o.' + + 'hello.'.rindex(/not/) + $~.should == nil + end + + it "starts the search at the given offset" do + "blablabla".rindex(/.{0}/, 5).should == 5 + "blablabla".rindex(/.{1}/, 5).should == 5 + "blablabla".rindex(/.{2}/, 5).should == 5 + "blablabla".rindex(/.{3}/, 5).should == 5 + "blablabla".rindex(/.{4}/, 5).should == 5 + + "blablabla".rindex(/.{0}/, 3).should == 3 + "blablabla".rindex(/.{1}/, 3).should == 3 + "blablabla".rindex(/.{2}/, 3).should == 3 + "blablabla".rindex(/.{5}/, 3).should == 3 + "blablabla".rindex(/.{6}/, 3).should == 3 + + "blablabla".rindex(/.l./, 0).should == 0 + "blablabla".rindex(/.l./, 1).should == 0 + "blablabla".rindex(/.l./, 2).should == 0 + "blablabla".rindex(/.l./, 3).should == 3 + + "blablablax".rindex(/.x/, 10).should == 8 + "blablablax".rindex(/.x/, 9).should == 8 + "blablablax".rindex(/.x/, 8).should == 8 + + "blablablax".rindex(/..x/, 10).should == 7 + "blablablax".rindex(/..x/, 9).should == 7 + "blablablax".rindex(/..x/, 8).should == 7 + "blablablax".rindex(/..x/, 7).should == 7 + + not_supported_on :opal do + "blablabla\n".rindex(/\Z/, 9).should == 9 + end + end + + it "starts the search at offset + self.length if offset is negative" do + str = "blablabla" + + ["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle| + (-str.length .. -1).each do |offset| + str.rindex(needle, offset).should == + str.rindex(needle, offset + str.length) + end + end + end + + it "returns nil if the substring isn't found" do + "blablabla".rindex(/BLA/).should == nil + "blablabla".rindex(/.{10}/).should == nil + "blablablax".rindex(/.x/, 7).should == nil + "blablablax".rindex(/..x/, 6).should == nil + + not_supported_on :opal do + "blablabla".rindex(/\Z/, 5).should == nil + "blablabla".rindex(/\z/, 5).should == nil + "blablabla\n".rindex(/\z/, 9).should == nil + end + end + + not_supported_on :opal do + it "supports \\G which matches at the given start offset" do + "helloYOU.".rindex(/YOU\G/, 8).should == 5 + "helloYOU.".rindex(/YOU\G/).should == nil + + idx = "helloYOUall!".index("YOU") + re = /YOU.+\G.+/ + # The # marks where \G will match. + [ + ["helloYOU#all.", nil], + ["helloYOUa#ll.", idx], + ["helloYOUal#l.", idx], + ["helloYOUall#.", idx], + ["helloYOUall.#", nil] + ].each do |i| + start = i[0].index("#") + str = i[0].delete("#") + + str.rindex(re, start).should == i[1] + end + end + end + + it "tries to convert start_offset to an integer via to_int" do + obj = mock('5') + def obj.to_int() 5 end + "str".rindex(/../, obj).should == 1 + + obj = mock('5') + def obj.respond_to?(arg, *) true end + def obj.method_missing(*args); 5; end + "str".rindex(/../, obj).should == 1 + end + + it "raises a TypeError when given offset is nil" do + lambda { "str".rindex(/../, nil) }.should raise_error(TypeError) + end + + with_feature :encoding do + it "returns the reverse character index of a multibyte character" do + "ありがりがとう".rindex("が").should == 4 + "ありがりがとう".rindex(/が/).should == 4 + end + + it "returns the character index before the finish" do + "ありがりがとう".rindex("が", 3).should == 2 + "ありがりがとう".rindex(/が/, 3).should == 2 + end + + it "raises an Encoding::CompatibilityError if the encodings are incompatible" do + re = Regexp.new "れ".encode(Encoding::EUC_JP) + lambda do + "あれ".rindex re + end.should raise_error(Encoding::CompatibilityError) + end + end +end diff --git a/spec/rubyspec/core/string/rjust_spec.rb b/spec/rubyspec/core/string/rjust_spec.rb new file mode 100644 index 0000000000..85cb1fe213 --- /dev/null +++ b/spec/rubyspec/core/string/rjust_spec.rb @@ -0,0 +1,116 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#rjust with length, padding" do + it "returns a new string of specified length with self right justified and padded with padstr" do + "hello".rjust(20, '1234').should == "123412341234123hello" + + "".rjust(1, "abcd").should == "a" + "".rjust(2, "abcd").should == "ab" + "".rjust(3, "abcd").should == "abc" + "".rjust(4, "abcd").should == "abcd" + "".rjust(6, "abcd").should == "abcdab" + + "OK".rjust(3, "abcd").should == "aOK" + "OK".rjust(4, "abcd").should == "abOK" + "OK".rjust(6, "abcd").should == "abcdOK" + "OK".rjust(8, "abcd").should == "abcdabOK" + end + + it "pads with whitespace if no padstr is given" do + "hello".rjust(20).should == " hello" + end + + it "returns self if it's longer than or as long as the specified length" do + "".rjust(0).should == "" + "".rjust(-1).should == "" + "hello".rjust(4).should == "hello" + "hello".rjust(-1).should == "hello" + "this".rjust(3).should == "this" + "radiology".rjust(8, '-').should == "radiology" + end + + it "taints result when self or padstr is tainted" do + "x".taint.rjust(4).tainted?.should == true + "x".taint.rjust(0).tainted?.should == true + "".taint.rjust(0).tainted?.should == true + "x".taint.rjust(4, "*").tainted?.should == true + "x".rjust(4, "*".taint).tainted?.should == true + end + + it "tries to convert length to an integer using to_int" do + "^".rjust(3.8, "^_").should == "^_^" + + obj = mock('3') + obj.should_receive(:to_int).and_return(3) + + "o".rjust(obj, "o_").should == "o_o" + end + + it "raises a TypeError when length can't be converted to an integer" do + lambda { "hello".rjust("x") }.should raise_error(TypeError) + lambda { "hello".rjust("x", "y") }.should raise_error(TypeError) + lambda { "hello".rjust([]) }.should raise_error(TypeError) + lambda { "hello".rjust(mock('x')) }.should raise_error(TypeError) + end + + it "tries to convert padstr to a string using to_str" do + padstr = mock('123') + padstr.should_receive(:to_str).and_return("123") + + "hello".rjust(10, padstr).should == "12312hello" + end + + it "raises a TypeError when padstr can't be converted" do + lambda { "hello".rjust(20, []) }.should raise_error(TypeError) + lambda { "hello".rjust(20, Object.new)}.should raise_error(TypeError) + lambda { "hello".rjust(20, mock('x')) }.should raise_error(TypeError) + end + + it "raises an ArgumentError when padstr is empty" do + lambda { "hello".rjust(10, '') }.should raise_error(ArgumentError) + end + + it "returns subclass instances when called on subclasses" do + StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + + "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end + + it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do + "hello".rjust(4, 'X'.taint).tainted?.should be_false + "hello".rjust(5, 'X'.taint).tainted?.should be_false + "hello".rjust(6, 'X'.taint).tainted?.should be_true + end + + with_feature :encoding do + describe "with width" do + it "returns a String in the same encoding as the original" do + str = "abc".force_encoding Encoding::IBM437 + result = str.rjust 5 + result.should == " abc" + result.encoding.should equal(Encoding::IBM437) + end + end + + describe "with width, pattern" do + it "returns a String in the compatible encoding" do + str = "abc".force_encoding Encoding::IBM437 + result = str.rjust 5, "あ" + result.should == "ああabc" + result.encoding.should equal(Encoding::UTF_8) + end + + it "raises an Encoding::CompatibilityError if the encodings are incompatible" do + pat = "ア".encode Encoding::EUC_JP + lambda do + "あれ".rjust 5, pat + end.should raise_error(Encoding::CompatibilityError) + end + end + end +end diff --git a/spec/rubyspec/core/string/rpartition_spec.rb b/spec/rubyspec/core/string/rpartition_spec.rb new file mode 100644 index 0000000000..c58d96f298 --- /dev/null +++ b/spec/rubyspec/core/string/rpartition_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#rpartition with String" do + it "returns an array of substrings based on splitting on the given string" do + "hello world".rpartition("o").should == ["hello w", "o", "rld"] + end + + it "always returns 3 elements" do + "hello".rpartition("x").should == ["", "", "hello"] + "hello".rpartition("hello").should == ["", "hello", ""] + end + + it "accepts regexp" do + "hello!".rpartition(/l./).should == ["hel", "lo", "!"] + end + + it "affects $~" do + matched_string = "hello!".rpartition(/l./)[1] + matched_string.should == $~[0] + end + + it "converts its argument using :to_str" do + find = mock('l') + find.should_receive(:to_str).and_return("l") + "hello".rpartition(find).should == ["hel","l","o"] + end + + it "raises an error if not convertible to string" do + lambda{ "hello".rpartition(5) }.should raise_error(TypeError) + lambda{ "hello".rpartition(nil) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/string/rstrip_spec.rb b/spec/rubyspec/core/string/rstrip_spec.rb new file mode 100644 index 0000000000..9dd686ce55 --- /dev/null +++ b/spec/rubyspec/core/string/rstrip_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#rstrip" do + it "returns a copy of self with trailing whitespace removed" do + " hello ".rstrip.should == " hello" + " hello world ".rstrip.should == " hello world" + " hello world \n\r\t\n\v\r".rstrip.should == " hello world" + "hello".rstrip.should == "hello" + "hello\x00".rstrip.should == "hello" + end + + it "returns a copy of self with all trailing whitespace and NULL bytes removed" do + "\x00 \x00hello\x00 \x00".rstrip.should == "\x00 \x00hello" + end + + it "taints the result when self is tainted" do + "".taint.rstrip.tainted?.should == true + "ok".taint.rstrip.tainted?.should == true + "ok ".taint.rstrip.tainted?.should == true + end +end + +describe "String#rstrip!" do + it "modifies self in place and returns self" do + a = " hello " + a.rstrip!.should equal(a) + a.should == " hello" + end + + it "modifies self removing trailing NULL bytes and whitespace" do + a = "\x00 \x00hello\x00 \x00" + a.rstrip! + a.should == "\x00 \x00hello" + end + + it "returns nil if no modifications were made" do + a = "hello" + a.rstrip!.should == nil + a.should == "hello" + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda { " hello ".freeze.rstrip! }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on a frozen instance that would not be modified" do + lambda { "hello".freeze.rstrip! }.should raise_error(RuntimeError) + lambda { "".freeze.rstrip! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/scan_spec.rb b/spec/rubyspec/core/string/scan_spec.rb new file mode 100644 index 0000000000..f09daa1b74 --- /dev/null +++ b/spec/rubyspec/core/string/scan_spec.rb @@ -0,0 +1,192 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#scan" do + it "returns an array containing all matches" do + "cruel world".scan(/\w+/).should == ["cruel", "world"] + "cruel world".scan(/.../).should == ["cru", "el ", "wor"] + + # Edge case + "hello".scan(//).should == ["", "", "", "", "", ""] + "".scan(//).should == [""] + end + + it "respects unicode when the pattern collapses to nothing" do + str = "こにちわ" + reg = %r!! + str.scan(reg).should == ["", "", "", "", ""] + end + + it "stores groups as arrays in the returned arrays" do + "hello".scan(/()/).should == [[""]] * 6 + "hello".scan(/()()/).should == [["", ""]] * 6 + "cruel world".scan(/(...)/).should == [["cru"], ["el "], ["wor"]] + "cruel world".scan(/(..)(..)/).should == [["cr", "ue"], ["l ", "wo"]] + end + + it "scans for occurrences of the string if pattern is a string" do + "one two one two".scan('one').should == ["one", "one"] + "hello.".scan('.').should == ['.'] + end + + it "sets $~ to MatchData of last match and nil when there's none" do + 'hello.'.scan(/.(.)/) + $~[0].should == 'o.' + + 'hello.'.scan(/not/) + $~.should == nil + + 'hello.'.scan('l') + $~.begin(0).should == 3 + $~[0].should == 'l' + + 'hello.'.scan('not') + $~.should == nil + end + + it "supports \\G which matches the end of the previous match / string start for first match" do + "one two one two".scan(/\G\w+/).should == ["one"] + "one two one two".scan(/\G\w+\s*/).should == ["one ", "two ", "one ", "two"] + "one two one two".scan(/\G\s*\w+/).should == ["one", " two", " one", " two"] + end + + it "tries to convert pattern to a string via to_str" do + obj = mock('o') + obj.should_receive(:to_str).and_return("o") + "o_o".scan(obj).should == ["o", "o"] + end + + it "raises a TypeError if pattern isn't a Regexp and can't be converted to a String" do + lambda { "cruel world".scan(5) }.should raise_error(TypeError) + not_supported_on :opal do + lambda { "cruel world".scan(:test) }.should raise_error(TypeError) + end + lambda { "cruel world".scan(mock('x')) }.should raise_error(TypeError) + end + + it "taints the results if the String argument is tainted" do + a = "hello hello hello".scan("hello".taint) + a.each { |m| m.tainted?.should be_true } + end + + it "taints the results when passed a String argument if self is tainted" do + a = "hello hello hello".taint.scan("hello") + a.each { |m| m.tainted?.should be_true } + end + + it "taints the results if the Regexp argument is tainted" do + a = "hello".scan(/./.taint) + a.each { |m| m.tainted?.should be_true } + end + + it "taints the results when passed a Regexp argument if self is tainted" do + a = "hello".taint.scan(/./) + a.each { |m| m.tainted?.should be_true } + end +end + +describe "String#scan with pattern and block" do + it "returns self" do + s = "foo" + s.scan(/./) {}.should equal(s) + s.scan(/roar/) {}.should equal(s) + end + + it "passes each match to the block as one argument: an array" do + a = [] + "cruel world".scan(/\w+/) { |*w| a << w } + a.should == [["cruel"], ["world"]] + end + + it "passes groups to the block as one argument: an array" do + a = [] + "cruel world".scan(/(..)(..)/) { |w| a << w } + a.should == [["cr", "ue"], ["l ", "wo"]] + end + + it "sets $~ for access from the block" do + str = "hello" + + matches = [] + offsets = [] + + str.scan(/([aeiou])/) do + md = $~ + md.string.should == str + matches << md.to_a + offsets << md.offset(0) + str + end + + matches.should == [["e", "e"], ["o", "o"]] + offsets.should == [[1, 2], [4, 5]] + + matches = [] + offsets = [] + + str.scan("l") do + md = $~ + md.string.should == str + matches << md.to_a + offsets << md.offset(0) + str + end + + matches.should == [["l"], ["l"]] + offsets.should == [[2, 3], [3, 4]] + end + + it "restores $~ after leaving the block" do + [/./, "l"].each do |pattern| + old_md = nil + "hello".scan(pattern) do + old_md = $~ + "ok".match(/./) + "x" + end + + $~[0].should == old_md[0] + $~.string.should == "hello" + end + end + + it "sets $~ to MatchData of last match and nil when there's none for access from outside" do + 'hello.'.scan('l') { 'x' } + $~.begin(0).should == 3 + $~[0].should == 'l' + + 'hello.'.scan('not') { 'x' } + $~.should == nil + + 'hello.'.scan(/.(.)/) { 'x' } + $~[0].should == 'o.' + + 'hello.'.scan(/not/) { 'x' } + $~.should == nil + end + + it "taints the results if the String argument is tainted" do + "hello hello hello".scan("hello".taint).each { |m| m.tainted?.should be_true } + end + + it "taints the results when passed a String argument if self is tainted" do + "hello hello hello".taint.scan("hello").each { |m| m.tainted?.should be_true } + end + + it "taints the results if the Regexp argument is tainted" do + "hello".scan(/./.taint).each { |m| m.tainted?.should be_true } + end + + it "taints the results when passed a Regexp argument if self is tainted" do + "hello".taint.scan(/./).each { |m| m.tainted?.should be_true } + end + + it "passes block arguments as individual arguments when blocks are provided" do + "a b c\na b c\na b c".scan(/(\w*) (\w*) (\w*)/) do |first,second,third| + first.should == 'a'; + second.should == 'b'; + third.should == 'c'; + end + end +end diff --git a/spec/rubyspec/core/string/scrub_spec.rb b/spec/rubyspec/core/string/scrub_spec.rb new file mode 100644 index 0000000000..815eb0fbb7 --- /dev/null +++ b/spec/rubyspec/core/string/scrub_spec.rb @@ -0,0 +1,101 @@ +# -*- encoding: utf-8 -*- +require File.expand_path("../../../spec_helper", __FILE__) + +describe "String#scrub with a default replacement" do + it "returns self for valid strings" do + input = "foo" + + input.scrub.should == input + end + + it "replaces invalid byte sequences" do + x81 = [0x81].pack('C').force_encoding('utf-8') + "abc\u3042#{x81}".scrub.should == "abc\u3042\uFFFD" + end + + it "returns a copy of self when the input encoding is BINARY" do + input = "foo".encode('BINARY') + + input.scrub.should == "foo" + end + + + it "replaces invalid byte sequences when using ASCII as the input encoding" do + xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8' + input = "abc\u3042#{xE3x80}".force_encoding('ASCII') + input.scrub.should == "abc?????" + end +end + +describe "String#scrub with a custom replacement" do + it "returns self for valid strings" do + input = "foo" + + input.scrub("*").should == input + end + + it "replaces invalid byte sequences" do + x81 = [0x81].pack('C').force_encoding('utf-8') + "abc\u3042#{x81}".scrub("*").should == "abc\u3042*" + end + + it "replaces an incomplete character at the end with a single replacement" do + xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8' + xE3x80.scrub("*").should == "*" + end + + it "raises ArgumentError for replacements with an invalid encoding" do + x81 = [0x81].pack('C').force_encoding('utf-8') + xE4 = [0xE4].pack('C').force_encoding('utf-8') + block = lambda { "foo#{x81}".scrub(xE4) } + + block.should raise_error(ArgumentError) + end + + it "raises TypeError when a non String replacement is given" do + x81 = [0x81].pack('C').force_encoding('utf-8') + block = lambda { "foo#{x81}".scrub(1) } + + block.should raise_error(TypeError) + end +end + +describe "String#scrub with a block" do + it "returns self for valid strings" do + input = "foo" + + input.scrub { |b| "*" }.should == input + end + + it "replaces invalid byte sequences" do + xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8' + replaced = "abc\u3042#{xE3x80}".scrub { |b| "<#{b.unpack("H*")[0]}>" } + + replaced.should == "abc\u3042" + end + + it "replaces invalid byte sequences using a custom encoding" do + x80x80 = [0x80, 0x80].pack('CC').force_encoding 'utf-8' + replaced = x80x80.scrub do |bad| + bad.encode(Encoding::UTF_8, Encoding::Windows_1252) + end + + replaced.should == "€€" + end +end + +describe "String#scrub!" do + it "modifies self for valid strings" do + x81 = [0x81].pack('C').force_encoding('utf-8') + input = "a#{x81}" + input.scrub! + input.should == "a\uFFFD" + end + + it "accepts blocks" do + x81 = [0x81].pack('C').force_encoding('utf-8') + input = "a#{x81}" + input.scrub! { |b| "" } + input.should == "a" + end +end diff --git a/spec/rubyspec/core/string/setbyte_spec.rb b/spec/rubyspec/core/string/setbyte_spec.rb new file mode 100644 index 0000000000..6373d74be1 --- /dev/null +++ b/spec/rubyspec/core/string/setbyte_spec.rb @@ -0,0 +1,105 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#setbyte" do + it "returns an Integer" do + "a".setbyte(0,1).should be_kind_of(Integer) + end + + it "modifies the receiver" do + str = "glark" + old_id = str.object_id + str.setbyte(0,88) + str.object_id.should == old_id + end + + it "changes the byte at the given index to the new byte" do + str = "a" + str.setbyte(0,98) + str.should == 'b' + + # copy-on-write case + str1, str2 = "fooXbar".split("X") + str2.setbyte(0, 50) + str2.should == "2ar" + str1.should == "foo" + end + + it "allows changing bytes in multi-byte characters" do + str = "\u{915}" + str.setbyte(1,254) + str.getbyte(1).should == 254 + end + + it "can invalidate a String's encoding" do + str = "glark" + str.valid_encoding?.should be_true + str.setbyte(2,253) + str.valid_encoding?.should be_false + end + + it "regards a negative index as counting from the end of the String" do + str = "hedgehog" + str.setbyte(-3, 108) + str.should == "hedgelog" + + # copy-on-write case + str1, str2 = "fooXbar".split("X") + str2.setbyte(-1, 50) + str2.should == "ba2" + str1.should == "foo" + end + + it "raises an IndexError if the index is greater than the String bytesize" do + lambda { "?".setbyte(1, 97) }.should raise_error(IndexError) + end + + it "raises an IndexError if the nexgative index is greater magnitude than the String bytesize" do + lambda { "???".setbyte(-5, 97) }.should raise_error(IndexError) + end + + it "sets a byte at an index greater than String size" do + chr = "\u{998}" + chr.bytesize.should == 3 + chr.setbyte(2, 150) + chr.should == "\xe0\xa6\x96" + end + + it "does not modify the original string when using String.new" do + str1 = "hedgehog" + str2 = String.new(str1) + str2.setbyte(0, 108) + str2.should == "ledgehog" + str2.should_not == "hedgehog" + str1.should == "hedgehog" + str1.should_not == "ledgehog" + end + + it "raises a RuntimeError if self is frozen" do + str = "cold".freeze + str.frozen?.should be_true + lambda { str.setbyte(3,96) }.should raise_error(RuntimeError) + end + + it "raises a TypeError unless the second argument is an Integer" do + lambda { "a".setbyte(0,'a') }.should raise_error(TypeError) + end + + it "calls #to_int to convert the index" do + index = mock("setbyte index") + index.should_receive(:to_int).and_return(1) + + str = "hat" + str.setbyte(index, "i".ord) + str.should == "hit" + end + + it "calls to_int to convert the value" do + value = mock("setbyte value") + value.should_receive(:to_int).and_return("i".ord) + + str = "hat" + str.setbyte(1, value) + str.should == "hit" + end +end diff --git a/spec/rubyspec/core/string/shared/chars.rb b/spec/rubyspec/core/string/shared/chars.rb new file mode 100644 index 0000000000..2f7280a95f --- /dev/null +++ b/spec/rubyspec/core/string/shared/chars.rb @@ -0,0 +1,82 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :string_chars, shared: true do + it "passes each char in self to the given block" do + a = [] + "hello".send(@method) { |c| a << c } + a.should == ['h', 'e', 'l', 'l', 'o'] + end + + it "returns self" do + s = StringSpecs::MyString.new "hello" + s.send(@method){}.should equal(s) + end + + + it "is unicode aware" do + "\303\207\342\210\202\303\251\306\222g".send(@method).to_a.should == + ["\303\207", "\342\210\202", "\303\251", "\306\222", "g"] + end + + with_feature :encoding do + it "returns characters in the same encoding as self" do + "&%".force_encoding('Shift_JIS').send(@method).to_a.all? {|c| c.encoding.name.should == 'Shift_JIS'} + "&%".encode('ASCII-8BIT').send(@method).to_a.all? {|c| c.encoding.name.should == 'ASCII-8BIT'} + end + + it "works with multibyte characters" do + s = "\u{8987}".force_encoding("UTF-8") + s.bytesize.should == 3 + s.send(@method).to_a.should == [s] + end + + it "works if the String's contents is invalid for its encoding" do + xA4 = [0xA4].pack('C') + xA4.force_encoding('UTF-8') + xA4.valid_encoding?.should be_false + xA4.send(@method).to_a.should == [xA4.force_encoding("UTF-8")] + end + + it "returns a different character if the String is transcoded" do + s = "\u{20AC}".force_encoding('UTF-8') + s.encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')] + s.encode('iso-8859-15').send(@method).to_a.should == [ + [0xA4].pack('C').force_encoding('iso-8859-15')] + s.encode('iso-8859-15').encode('UTF-8').send(@method).to_a.should == [ + "\u{20AC}".force_encoding('UTF-8')] + end + + it "uses the String's encoding to determine what characters it contains" do + s = "\u{24B62}" + + s.force_encoding('UTF-8').send(@method).to_a.should == [ + s.force_encoding('UTF-8') + ] + s.force_encoding('BINARY').send(@method).to_a.should == [ + [0xF0].pack('C').force_encoding('BINARY'), + [0xA4].pack('C').force_encoding('BINARY'), + [0xAD].pack('C').force_encoding('BINARY'), + [0xA2].pack('C').force_encoding('BINARY') + ] + s.force_encoding('SJIS').send(@method).to_a.should == [ + [0xF0,0xA4].pack('CC').force_encoding('SJIS'), + [0xAD].pack('C').force_encoding('SJIS'), + [0xA2].pack('C').force_encoding('SJIS') + ] + end + + it "taints resulting strings when self is tainted" do + str = "hello" + + str.send(@method) do |x| + x.tainted?.should == false + end + + str.dup.taint.send(@method) do |x| + x.tainted?.should == true + end + end + end +end diff --git a/spec/rubyspec/core/string/shared/codepoints.rb b/spec/rubyspec/core/string/shared/codepoints.rb new file mode 100644 index 0000000000..1ee13c82f4 --- /dev/null +++ b/spec/rubyspec/core/string/shared/codepoints.rb @@ -0,0 +1,56 @@ +# -*- encoding: binary -*- +describe :string_codepoints, shared: true do + it "raises an ArgumentError when self has an invalid encoding and a method is called on the returned Enumerator" do + s = "\xDF".force_encoding(Encoding::UTF_8) + s.valid_encoding?.should be_false + lambda { s.send(@method).to_a }.should raise_error(ArgumentError) + end + + it "yields each codepoint to the block if one is given" do + codepoints = [] + "abcd".send(@method) do |codepoint| + codepoints << codepoint + end + codepoints.should == [97, 98, 99, 100] + end + + it "raises an ArgumentError if self's encoding is invalid and a block is given" do + s = "\xDF".force_encoding(Encoding::UTF_8) + s.valid_encoding?.should be_false + lambda { s.send(@method) { } }.should raise_error(ArgumentError) + end + + it "returns codepoints as Fixnums" do + "glark\u{20}".send(@method).to_a.each do |codepoint| + codepoint.should be_an_instance_of(Fixnum) + end + end + + it "returns one codepoint for each character" do + s = "\u{9876}\u{28}\u{1987}" + s.send(@method).to_a.size.should == s.chars.to_a.size + end + + it "works for multibyte characters" do + s = "\u{9819}" + s.bytesize.should == 3 + s.send(@method).to_a.should == [38937] + end + + it "returns the codepoint corresponding to the character's position in the String's encoding" do + "\u{787}".send(@method).to_a.should == [1927] + end + + it "round-trips to the original String using Integer#chr" do + s = "\u{13}\u{7711}\u{1010}" + s2 = "" + s.send(@method) {|n| s2 << n.chr(Encoding::UTF_8)} + s.should == s2 + end + + it "is synonomous with #bytes for Strings which are single-byte optimisable" do + s = "(){}".encode('ascii') + s.ascii_only?.should be_true + s.send(@method).to_a.should == s.bytes.to_a + end +end diff --git a/spec/rubyspec/core/string/shared/concat.rb b/spec/rubyspec/core/string/shared/concat.rb new file mode 100644 index 0000000000..7da995fdc7 --- /dev/null +++ b/spec/rubyspec/core/string/shared/concat.rb @@ -0,0 +1,160 @@ +describe :string_concat, shared: true do + it "concatenates the given argument to self and returns self" do + str = 'hello ' + str.send(@method, 'world').should equal(str) + str.should == "hello world" + end + + it "converts the given argument to a String using to_str" do + obj = mock('world!') + obj.should_receive(:to_str).and_return("world!") + a = 'hello '.send(@method, obj) + a.should == 'hello world!' + end + + it "raises a TypeError if the given argument can't be converted to a String" do + lambda { 'hello '.send(@method, []) }.should raise_error(TypeError) + lambda { 'hello '.send(@method, mock('x')) }.should raise_error(TypeError) + end + + it "raises a RuntimeError when self is frozen" do + a = "hello" + a.freeze + + lambda { a.send(@method, "") }.should raise_error(RuntimeError) + lambda { a.send(@method, "test") }.should raise_error(RuntimeError) + end + + it "returns a String when given a subclass instance" do + a = "hello" + a.send(@method, StringSpecs::MyString.new(" world")) + a.should == "hello world" + a.should be_an_instance_of(String) + end + + it "returns an instance of same class when called on a subclass" do + str = StringSpecs::MyString.new("hello") + str.send(@method, " world") + str.should == "hello world" + str.should be_an_instance_of(StringSpecs::MyString) + end + + it "taints self if other is tainted" do + "x".send(@method, "".taint).tainted?.should == true + "x".send(@method, "y".taint).tainted?.should == true + end + + it "untrusts self if other is untrusted" do + "x".send(@method, "".untrust).untrusted?.should == true + "x".send(@method, "y".untrust).untrusted?.should == true + end + + describe "with Integer" do + it "concatencates the argument interpreted as a codepoint" do + b = "".send(@method, 33) + b.should == "!" + + b.encode!(Encoding::UTF_8) + b.send(@method, 0x203D) + b.should == "!\u203D" + end + + # #5855 + it "returns a ASCII-8BIT string if self is US-ASCII and the argument is between 128-255 (inclusive)" do + a = ("".encode(Encoding::US_ASCII).send(@method, 128)) + a.encoding.should == Encoding::ASCII_8BIT + a.should == 128.chr + + a = ("".encode(Encoding::US_ASCII).send(@method, 255)) + a.encoding.should == Encoding::ASCII_8BIT + a.should == 255.chr + end + + it "raises RangeError if the argument is an invalid codepoint for self's encoding" do + lambda { "".encode(Encoding::US_ASCII).send(@method, 256) }.should raise_error(RangeError) + lambda { "".encode(Encoding::EUC_JP).send(@method, 0x81) }.should raise_error(RangeError) + end + + it "raises RangeError if the argument is negative" do + lambda { "".send(@method, -200) }.should raise_error(RangeError) + lambda { "".send(@method, -bignum_value) }.should raise_error(RangeError) + end + + it "doesn't call to_int on its argument" do + x = mock('x') + x.should_not_receive(:to_int) + + lambda { "".send(@method, x) }.should raise_error(TypeError) + end + + it "raises a RuntimeError when self is frozen" do + a = "hello" + a.freeze + + lambda { a.send(@method, 0) }.should raise_error(RuntimeError) + lambda { a.send(@method, 33) }.should raise_error(RuntimeError) + end + end +end + +describe :string_concat_encoding, shared: true do + describe "when self is in an ASCII-incompatible encoding incompatible with the argument's encoding" do + it "uses self's encoding if both are empty" do + "".encode("UTF-16LE").send(@method, "").encoding.should == Encoding::UTF_16LE + end + + it "uses self's encoding if the argument is empty" do + "x".encode("UTF-16LE").send(@method, "").encoding.should == Encoding::UTF_16LE + end + + it "uses the argument's encoding if self is empty" do + "".encode("UTF-16LE").send(@method, "x".encode("UTF-8")).encoding.should == Encoding::UTF_8 + end + + it "raises Encoding::CompatibilityError if neither are empty" do + lambda { "x".encode("UTF-16LE").send(@method, "y".encode("UTF-8")) }.should raise_error(Encoding::CompatibilityError) + end + end + + describe "when the argument is in an ASCII-incompatible encoding incompatible with self's encoding" do + it "uses self's encoding if both are empty" do + "".encode("UTF-8").send(@method, "".encode("UTF-16LE")).encoding.should == Encoding::UTF_8 + end + + it "uses self's encoding if the argument is empty" do + "x".encode("UTF-8").send(@method, "".encode("UTF-16LE")).encoding.should == Encoding::UTF_8 + end + + it "uses the argument's encoding if self is empty" do + "".encode("UTF-8").send(@method, "x".encode("UTF-16LE")).encoding.should == Encoding::UTF_16LE + end + + it "raises Encoding::CompatibilityError if neither are empty" do + lambda { "x".encode("UTF-8").send(@method, "y".encode("UTF-16LE")) }.should raise_error(Encoding::CompatibilityError) + end + end + + describe "when self and the argument are in different ASCII-compatible encodings" do + it "uses self's encoding if both are ASCII-only" do + "abc".encode("UTF-8").send(@method, "123".encode("SHIFT_JIS")).encoding.should == Encoding::UTF_8 + end + + it "uses self's encoding if the argument is ASCII-only" do + "\u00E9".encode("UTF-8").send(@method, "123".encode("ISO-8859-1")).encoding.should == Encoding::UTF_8 + end + + it "uses the argument's encoding if self is ASCII-only" do + "abc".encode("UTF-8").send(@method, "\u00E9".encode("ISO-8859-1")).encoding.should == Encoding::ISO_8859_1 + end + + it "raises Encoding::CompatibilityError if neither are ASCII-only" do + lambda { "\u00E9".encode("UTF-8").send(@method, "\u00E9".encode("ISO-8859-1")) }.should raise_error(Encoding::CompatibilityError) + end + end + + describe "when self is ASCII-8BIT and argument is US-ASCII" do + it "uses ASCII-8BIT encoding" do + "abc".encode("ASCII-8BIT").send(@method, "123".encode("US-ASCII")).encoding.should == Encoding::ASCII_8BIT + end + end +end diff --git a/spec/rubyspec/core/string/shared/each_char_without_block.rb b/spec/rubyspec/core/string/shared/each_char_without_block.rb new file mode 100644 index 0000000000..40808cfd9f --- /dev/null +++ b/spec/rubyspec/core/string/shared/each_char_without_block.rb @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :string_each_char_without_block, shared: true do + describe "when no block is given" do + it "returns an enumerator" do + enum = "hello".send(@method) + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == ['h', 'e', 'l', 'l', 'o'] + end + + describe "returned enumerator" do + describe "size" do + it "should return the size of the string" do + str = "hello" + str.send(@method).size.should == str.size + str = "ola" + str.send(@method).size.should == str.size + str = "\303\207\342\210\202\303\251\306\222g" + str.send(@method).size.should == str.size + end + end + end + end +end diff --git a/spec/rubyspec/core/string/shared/each_codepoint_without_block.rb b/spec/rubyspec/core/string/shared/each_codepoint_without_block.rb new file mode 100644 index 0000000000..92b7f76032 --- /dev/null +++ b/spec/rubyspec/core/string/shared/each_codepoint_without_block.rb @@ -0,0 +1,33 @@ +# -*- encoding: binary -*- +describe :string_each_codepoint_without_block, shared: true do + describe "when no block is given" do + it "returns an Enumerator" do + "".send(@method).should be_an_instance_of(Enumerator) + end + + it "returns an Enumerator even when self has an invalid encoding" do + s = "\xDF".force_encoding(Encoding::UTF_8) + s.valid_encoding?.should be_false + s.send(@method).should be_an_instance_of(Enumerator) + end + + describe "returned Enumerator" do + describe "size" do + it "should return the size of the string" do + str = "hello" + str.send(@method).size.should == str.size + str = "ola" + str.send(@method).size.should == str.size + str = "\303\207\342\210\202\303\251\306\222g" + str.send(@method).size.should == str.size + end + + it "should return the size of the string even when the string has an invalid encoding" do + s = "\xDF".force_encoding(Encoding::UTF_8) + s.valid_encoding?.should be_false + s.send(@method).size.should == 1 + end + end + end + end +end diff --git a/spec/rubyspec/core/string/shared/each_line.rb b/spec/rubyspec/core/string/shared/each_line.rb new file mode 100644 index 0000000000..fe8b76b47b --- /dev/null +++ b/spec/rubyspec/core/string/shared/each_line.rb @@ -0,0 +1,136 @@ +describe :string_each_line, shared: true do + it "splits using default newline separator when none is specified" do + a = [] + "one\ntwo\r\nthree".send(@method) { |s| a << s } + a.should == ["one\n", "two\r\n", "three"] + + b = [] + "hello\n\n\nworld".send(@method) { |s| b << s } + b.should == ["hello\n", "\n", "\n", "world"] + + c = [] + "\n\n\n\n\n".send(@method) {|s| c << s} + c.should == ["\n", "\n", "\n", "\n", "\n"] + end + + it "splits self using the supplied record separator and passes each substring to the block" do + a = [] + "one\ntwo\r\nthree".send(@method, "\n") { |s| a << s } + a.should == ["one\n", "two\r\n", "three"] + + b = [] + "hello\nworld".send(@method, 'l') { |s| b << s } + b.should == [ "hel", "l", "o\nworl", "d" ] + + c = [] + "hello\n\n\nworld".send(@method, "\n") { |s| c << s } + c.should == ["hello\n", "\n", "\n", "world"] + end + + it "taints substrings that are passed to the block if self is tainted" do + "one\ntwo\r\nthree".taint.send(@method) { |s| s.tainted?.should == true } + + "x.y.".send(@method, ".".taint) { |s| s.tainted?.should == false } + end + + it "passes self as a whole to the block if the separator is nil" do + a = [] + "one\ntwo\r\nthree".send(@method, nil) { |s| a << s } + a.should == ["one\ntwo\r\nthree"] + end + + ruby_version_is ''...'2.5' do + it "yields paragraphs (broken by 2 or more successive newlines) when passed ''" do + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s } + a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n"] + + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s } + a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n", "dog"] + end + end + +quarantine! do # Currently fails on Travis + ruby_version_is '2.5' do + it "yields paragraphs (broken by 2 or more successive newlines) when passed ''" do + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s } + a.should == ["hello\nworld\n\n", "and\nuniverse\n\n"] + + a = [] + "hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s } + a.should == ["hello\nworld\n\n", "and\nuniverse\n\n", "dog"] + end + end +end + + describe "uses $/" do + before :each do + @before_separator = $/ + end + + after :each do + $/ = @before_separator + end + + it "as the separator when none is given" do + [ + "", "x", "x\ny", "x\ry", "x\r\ny", "x\n\r\r\ny", + "hello hullo bello" + ].each do |str| + ["", "llo", "\n", "\r", nil].each do |sep| + expected = [] + str.send(@method, sep) { |x| expected << x } + + $/ = sep + + actual = [] + str.send(@method) { |x| actual << x } + + actual.should == expected + end + end + end + end + + it "yields subclass instances for subclasses" do + a = [] + StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class } + a.should == [StringSpecs::MyString, StringSpecs::MyString] + end + + it "returns self" do + s = "hello\nworld" + (s.send(@method) {}).should equal(s) + end + + it "tries to convert the separator to a string using to_str" do + separator = mock('l') + separator.should_receive(:to_str).and_return("l") + + a = [] + "hello\nworld".send(@method, separator) { |s| a << s } + a.should == [ "hel", "l", "o\nworl", "d" ] + end + + it "does not care if the string is modified while substituting" do + str = "hello\nworld." + out = [] + str.send(@method){|x| out << x; str[-1] = '!' }.should == "hello\nworld!" + out.should == ["hello\n", "world."] + end + + it "raises a TypeError when the separator can't be converted to a string" do + lambda { "hello world".send(@method, false) {} }.should raise_error(TypeError) + lambda { "hello world".send(@method, mock('x')) {} }.should raise_error(TypeError) + end + + it "accepts a string separator" do + "hello world".send(@method, ?o).to_a.should == ["hello", " wo", "rld"] + end + + it "raises a TypeError when the separator is a symbol" do + lambda { "hello world".send(@method, :o).to_a }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/string/shared/each_line_without_block.rb b/spec/rubyspec/core/string/shared/each_line_without_block.rb new file mode 100644 index 0000000000..8e08b0390c --- /dev/null +++ b/spec/rubyspec/core/string/shared/each_line_without_block.rb @@ -0,0 +1,17 @@ +describe :string_each_line_without_block, shared: true do + describe "when no block is given" do + it "returns an enumerator" do + enum = "hello world".send(@method, ' ') + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == ["hello ", "world"] + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + "hello world".send(@method, ' ').size.should == nil + end + end + end + end +end diff --git a/spec/rubyspec/core/string/shared/encode.rb b/spec/rubyspec/core/string/shared/encode.rb new file mode 100644 index 0000000000..71d46b1bd3 --- /dev/null +++ b/spec/rubyspec/core/string/shared/encode.rb @@ -0,0 +1,247 @@ +# -*- encoding: utf-8 -*- +describe :string_encode, shared: true do + describe "when passed no options" do + it "transcodes to Encoding.default_internal when set" do + Encoding.default_internal = Encoding::UTF_8 + str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP + str.send(@method).should == "あ" + end + + it "transcodes a 7-bit String despite no generic converting being available" do + lambda do + Encoding::Converter.new Encoding::Emacs_Mule, Encoding::ASCII_8BIT + end.should raise_error(Encoding::ConverterNotFoundError) + + Encoding.default_internal = Encoding::Emacs_Mule + str = "\x79".force_encoding Encoding::ASCII_8BIT + + str.send(@method).should == "y".force_encoding(Encoding::ASCII_8BIT) + end + + it "raises an Encoding::ConverterNotFoundError when no conversion is possible" do + Encoding.default_internal = Encoding::Emacs_Mule + str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT + lambda { str.send(@method) }.should raise_error(Encoding::ConverterNotFoundError) + end + end + + describe "when passed to encoding" do + it "accepts a String argument" do + str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP + str.send(@method, "utf-8").should == "あ" + end + + it "calls #to_str to convert the object to an Encoding" do + enc = mock("string encode encoding") + enc.should_receive(:to_str).and_return("utf-8") + + str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP + str.send(@method, enc).should == "あ" + end + + it "transcodes to the passed encoding" do + str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP + str.send(@method, Encoding::UTF_8).should == "あ" + end + + it "transcodes Japanese multibyte characters" do + str = "あいうえお" + str.send(@method, Encoding::ISO_2022_JP).should == + "\e\x24\x42\x24\x22\x24\x24\x24\x26\x24\x28\x24\x2A\e\x28\x42".force_encoding(Encoding::ISO_2022_JP) + end + + it "transcodes a 7-bit String despite no generic converting being available" do + lambda do + Encoding::Converter.new Encoding::Emacs_Mule, Encoding::ASCII_8BIT + end.should raise_error(Encoding::ConverterNotFoundError) + + str = "\x79".force_encoding Encoding::ASCII_8BIT + str.send(@method, Encoding::Emacs_Mule).should == "y".force_encoding(Encoding::ASCII_8BIT) + end + + it "raises an Encoding::ConverterNotFoundError when no conversion is possible" do + str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT + lambda do + str.send(@method, Encoding::Emacs_Mule) + end.should raise_error(Encoding::ConverterNotFoundError) + end + + it "raises an Encoding::ConverterNotFoundError for an invalid encoding" do + lambda do + "abc".send(@method, "xyz") + end.should raise_error(Encoding::ConverterNotFoundError) + end + end + + describe "when passed options" do + it "does not process transcoding options if not transcoding" do + result = "あ\ufffdあ".send(@method, undef: :replace) + result.should == "あ\ufffdあ" + end + + it "calls #to_hash to convert the object" do + options = mock("string encode options") + options.should_receive(:to_hash).and_return({ undef: :replace }) + + result = "あ\ufffdあ".send(@method, options) + result.should == "あ\ufffdあ" + end + + it "transcodes to Encoding.default_internal when set" do + Encoding.default_internal = Encoding::UTF_8 + str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP + str.send(@method, invalid: :replace).should == "あ" + end + + it "raises an Encoding::ConverterNotFoundError when no conversion is possible despite 'invalid: :replace, undef: :replace'" do + Encoding.default_internal = Encoding::Emacs_Mule + str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT + lambda do + str.send(@method, invalid: :replace, undef: :replace) + end.should raise_error(Encoding::ConverterNotFoundError) + end + + it "replaces invalid characters when replacing Emacs-Mule encoded strings" do + got = [0x80].pack('C').force_encoding('Emacs-Mule').send(@method, invalid: :replace) + + got.should == "?".encode('Emacs-Mule') + end + end + + describe "when passed to, from" do + it "transcodes between the encodings ignoring the String encoding" do + str = "あ" + result = [0xA6, 0xD0, 0x8F, 0xAB, 0xE4, 0x8F, 0xAB, 0xB1].pack('C8') + result.force_encoding Encoding::EUC_JP + str.send(@method, "euc-jp", "ibm437").should == result + end + + it "calls #to_str to convert the from object to an Encoding" do + enc = mock("string encode encoding") + enc.should_receive(:to_str).and_return("ibm437") + + str = "あ" + result = [0xA6, 0xD0, 0x8F, 0xAB, 0xE4, 0x8F, 0xAB, 0xB1].pack('C8') + result.force_encoding Encoding::EUC_JP + + str.send(@method, "euc-jp", enc).should == result + end + end + + describe "when passed to, options" do + it "replaces undefined characters in the destination encoding" do + result = "あ?あ".send(@method, Encoding::EUC_JP, undef: :replace) + # testing for: "\xA4\xA2?\xA4\xA2" + xA4xA2 = [0xA4, 0xA2].pack('CC') + result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp") + end + + it "replaces invalid characters in the destination encoding" do + xFF = [0xFF].pack('C').force_encoding('utf-8') + "ab#{xFF}c".send(@method, Encoding::ISO_8859_1, invalid: :replace).should == "ab?c" + end + + it "calls #to_hash to convert the options object" do + options = mock("string encode options") + options.should_receive(:to_hash).and_return({ undef: :replace }) + + result = "あ?あ".send(@method, Encoding::EUC_JP, options) + xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding('utf-8') + result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp") + end + end + + describe "when passed to, from, options" do + it "replaces undefined characters in the destination encoding" do + str = "あ?あ".force_encoding Encoding::ASCII_8BIT + result = str.send(@method, "euc-jp", "utf-8", undef: :replace) + xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding('utf-8') + result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp") + end + + it "replaces invalid characters in the destination encoding" do + xFF = [0xFF].pack('C').force_encoding('utf-8') + str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT + str.send(@method, "iso-8859-1", "utf-8", invalid: :replace).should == "ab?c" + end + + it "calls #to_str to convert the to object to an encoding" do + to = mock("string encode to encoding") + to.should_receive(:to_str).and_return("iso-8859-1") + + xFF = [0xFF].pack('C').force_encoding('utf-8') + str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT + str.send(@method, to, "utf-8", invalid: :replace).should == "ab?c" + end + + it "calls #to_str to convert the from object to an encoding" do + from = mock("string encode to encoding") + from.should_receive(:to_str).and_return("utf-8") + + xFF = [0xFF].pack('C').force_encoding('utf-8') + str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT + str.send(@method, "iso-8859-1", from, invalid: :replace).should == "ab?c" + end + + it "calls #to_hash to convert the options object" do + options = mock("string encode options") + options.should_receive(:to_hash).and_return({ invalid: :replace }) + + xFF = [0xFF].pack('C').force_encoding('utf-8') + str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT + str.send(@method, "iso-8859-1", "utf-8", options).should == "ab?c" + end + end + + describe "given the xml: :text option" do + it "replaces all instances of '&' with '&'" do + '& and &'.send(@method, "UTF-8", xml: :text).should == '& and &' + end + + it "replaces all instances of '<' with '<'" do + '< and <'.send(@method, "UTF-8", xml: :text).should == '< and <' + end + + it "replaces all instances of '>' with '>'" do + '> and >'.send(@method, "UTF-8", xml: :text).should == '> and >' + end + + it "does not replace '\"'" do + '" and "'.send(@method, "UTF-8", xml: :text).should == '" and "' + end + + it "replaces undefined characters with their upper-case hexadecimal numeric character references" do + 'ürst'.send(@method, Encoding::US_ASCII, xml: :text).should == 'ürst' + end + end + + describe "given the xml: :attr option" do + it "surrounds the encoded text with double-quotes" do + 'abc'.send(@method, "UTF-8", xml: :attr).should == '"abc"' + end + + it "replaces all instances of '&' with '&'" do + '& and &'.send(@method, "UTF-8", xml: :attr).should == '"& and &"' + end + + it "replaces all instances of '<' with '<'" do + '< and <'.send(@method, "UTF-8", xml: :attr).should == '"< and <"' + end + + it "replaces all instances of '>' with '>'" do + '> and >'.send(@method, "UTF-8", xml: :attr).should == '"> and >"' + end + + it "replaces all instances of '\"' with '"'" do + '" and "'.send(@method, "UTF-8", xml: :attr).should == '"" and ""' + end + + it "replaces undefined characters with their upper-case hexadecimal numeric character references" do + 'ürst'.send(@method, Encoding::US_ASCII, xml: :attr).should == '"ürst"' + end + end + + it "raises ArgumentError if the value of the :xml option is not :text or :attr" do + lambda { ''.send(@method, "UTF-8", xml: :other) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/shared/eql.rb b/spec/rubyspec/core/string/shared/eql.rb new file mode 100644 index 0000000000..92dfa91923 --- /dev/null +++ b/spec/rubyspec/core/string/shared/eql.rb @@ -0,0 +1,34 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :string_eql_value, shared: true do + it "returns true if self <=> string returns 0" do + 'hello'.send(@method, 'hello').should be_true + end + + it "returns false if self <=> string does not return 0" do + "more".send(@method, "MORE").should be_false + "less".send(@method, "greater").should be_false + end + + it "ignores encoding difference of compatible string" do + "hello".force_encoding("utf-8").send(@method, "hello".force_encoding("iso-8859-1")).should be_true + end + + it "considers encoding difference of incompatible string" do + "\xff".force_encoding("utf-8").send(@method, "\xff".force_encoding("iso-8859-1")).should be_false + end + + it "considers encoding compatibility" do + "hello".force_encoding("utf-8").send(@method, "hello".force_encoding("utf-32le")).should be_false + end + + it "ignores subclass differences" do + a = "hello" + b = StringSpecs::MyString.new("hello") + + a.send(@method, b).should be_true + b.send(@method, a).should be_true + end +end diff --git a/spec/rubyspec/core/string/shared/equal_value.rb b/spec/rubyspec/core/string/shared/equal_value.rb new file mode 100644 index 0000000000..6df76478c7 --- /dev/null +++ b/spec/rubyspec/core/string/shared/equal_value.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :string_equal_value, shared: true do + it "returns false if obj does not respond to to_str" do + 'hello'.send(@method, 5).should be_false + not_supported_on :opal do + 'hello'.send(@method, :hello).should be_false + end + 'hello'.send(@method, mock('x')).should be_false + end + + it "returns obj == self if obj responds to to_str" do + obj = Object.new + + # String#== merely checks if #to_str is defined. It does + # not call it. + obj.stub!(:to_str) + + # Don't use @method for :== in `obj.should_recerive(:==)` + obj.should_receive(:==).and_return(true) + + 'hello'.send(@method, obj).should be_true + end + + it "is not fooled by NUL characters" do + "abc\0def".send(@method, "abc\0xyz").should be_false + end +end diff --git a/spec/rubyspec/core/string/shared/length.rb b/spec/rubyspec/core/string/shared/length.rb new file mode 100644 index 0000000000..0e6e66ee1c --- /dev/null +++ b/spec/rubyspec/core/string/shared/length.rb @@ -0,0 +1,28 @@ +# encoding: utf-8 + +describe :string_length, shared: true do + it "returns the length of self" do + "".send(@method).should == 0 + "\x00".send(@method).should == 1 + "one".send(@method).should == 3 + "two".send(@method).should == 3 + "three".send(@method).should == 5 + "four".send(@method).should == 4 + end + + with_feature :encoding do + it "returns the length of a string in different encodings" do + utf8_str = 'こにちわ' * 100 + utf8_str.size.should == 400 + utf8_str.encode(Encoding::UTF_32BE).size.should == 400 + utf8_str.encode(Encoding::SHIFT_JIS).size.should == 400 + end + + it "returns the length of the new self after encoding is changed" do + str = 'こにちわ' + str.send(@method) + + str.force_encoding('ASCII-8BIT').send(@method).should == 12 + end + end +end diff --git a/spec/rubyspec/core/string/shared/replace.rb b/spec/rubyspec/core/string/shared/replace.rb new file mode 100644 index 0000000000..9f5446fbbe --- /dev/null +++ b/spec/rubyspec/core/string/shared/replace.rb @@ -0,0 +1,75 @@ +describe :string_replace, shared: true do + it "returns self" do + a = "a" + a.send(@method, "b").should equal(a) + end + + it "replaces the content of self with other" do + a = "some string" + a.send(@method, "another string") + a.should == "another string" + end + + it "taints self if other is tainted" do + a = "" + b = "".taint + a.send(@method, b) + a.tainted?.should == true + end + + it "does not untaint self if other is untainted" do + a = "".taint + b = "" + a.send(@method, b) + a.tainted?.should == true + end + + it "untrusts self if other is untrusted" do + a = "" + b = "".untrust + a.send(@method, b) + a.untrusted?.should == true + end + + it "does not trust self if other is trusted" do + a = "".untrust + b = "" + a.send(@method, b) + a.untrusted?.should == true + end + + it "replaces the encoding of self with that of other" do + a = "".encode("UTF-16LE") + b = "".encode("UTF-8") + a.send(@method, b) + a.encoding.should == Encoding::UTF_8 + end + + it "carries over the encoding invalidity" do + a = "\u{8765}".force_encoding('ascii') + "".send(@method, a).valid_encoding?.should be_false + end + + it "tries to convert other to string using to_str" do + other = mock('x') + other.should_receive(:to_str).and_return("converted to a string") + "hello".send(@method, other).should == "converted to a string" + end + + it "raises a TypeError if other can't be converted to string" do + lambda { "hello".send(@method, 123) }.should raise_error(TypeError) + lambda { "hello".send(@method, []) }.should raise_error(TypeError) + lambda { "hello".send(@method, mock('x')) }.should raise_error(TypeError) + end + + it "raises a RuntimeError on a frozen instance that is modified" do + a = "hello".freeze + lambda { a.send(@method, "world") }.should raise_error(RuntimeError) + end + + # see [ruby-core:23666] + it "raises a RuntimeError on a frozen instance when self-replacing" do + a = "hello".freeze + lambda { a.send(@method, a) }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/shared/slice.rb b/spec/rubyspec/core/string/shared/slice.rb new file mode 100644 index 0000000000..231c50d87f --- /dev/null +++ b/spec/rubyspec/core/string/shared/slice.rb @@ -0,0 +1,533 @@ +describe :string_slice, shared: true do + it "returns the character code of the character at the given index" do + "hello".send(@method, 0).should == ?h + "hello".send(@method, -1).should == ?o + end + + it "returns nil if index is outside of self" do + "hello".send(@method, 20).should == nil + "hello".send(@method, -20).should == nil + + "".send(@method, 0).should == nil + "".send(@method, -1).should == nil + end + + it "calls to_int on the given index" do + "hello".send(@method, 0.5).should == ?h + + obj = mock('1') + obj.should_receive(:to_int).and_return(1) + "hello".send(@method, obj).should == ?e + end + + it "raises a TypeError if the given index is nil" do + lambda { "hello".send(@method, nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if the given index can't be converted to an Integer" do + lambda { "hello".send(@method, mock('x')) }.should raise_error(TypeError) + lambda { "hello".send(@method, {}) }.should raise_error(TypeError) + lambda { "hello".send(@method, []) }.should raise_error(TypeError) + end +end + +describe :string_slice_index_length, shared: true do + it "returns the substring starting at the given index with the given length" do + "hello there".send(@method, 0,0).should == "" + "hello there".send(@method, 0,1).should == "h" + "hello there".send(@method, 0,3).should == "hel" + "hello there".send(@method, 0,6).should == "hello " + "hello there".send(@method, 0,9).should == "hello the" + "hello there".send(@method, 0,12).should == "hello there" + + "hello there".send(@method, 1,0).should == "" + "hello there".send(@method, 1,1).should == "e" + "hello there".send(@method, 1,3).should == "ell" + "hello there".send(@method, 1,6).should == "ello t" + "hello there".send(@method, 1,9).should == "ello ther" + "hello there".send(@method, 1,12).should == "ello there" + + "hello there".send(@method, 3,0).should == "" + "hello there".send(@method, 3,1).should == "l" + "hello there".send(@method, 3,3).should == "lo " + "hello there".send(@method, 3,6).should == "lo the" + "hello there".send(@method, 3,9).should == "lo there" + + "hello there".send(@method, 4,0).should == "" + "hello there".send(@method, 4,3).should == "o t" + "hello there".send(@method, 4,6).should == "o ther" + "hello there".send(@method, 4,9).should == "o there" + + "foo".send(@method, 2,1).should == "o" + "foo".send(@method, 3,0).should == "" + "foo".send(@method, 3,1).should == "" + + "".send(@method, 0,0).should == "" + "".send(@method, 0,1).should == "" + + "x".send(@method, 0,0).should == "" + "x".send(@method, 0,1).should == "x" + "x".send(@method, 1,0).should == "" + "x".send(@method, 1,1).should == "" + + "x".send(@method, -1,0).should == "" + "x".send(@method, -1,1).should == "x" + + "hello there".send(@method, -3,2).should == "er" + end + + it "always taints resulting strings when self is tainted" do + str = "hello world" + str.taint + + str.send(@method, 0,0).tainted?.should == true + str.send(@method, 0,1).tainted?.should == true + str.send(@method, 2,1).tainted?.should == true + end + + it "returns nil if the offset falls outside of self" do + "hello there".send(@method, 20,3).should == nil + "hello there".send(@method, -20,3).should == nil + + "".send(@method, 1,0).should == nil + "".send(@method, 1,1).should == nil + + "".send(@method, -1,0).should == nil + "".send(@method, -1,1).should == nil + + "x".send(@method, 2,0).should == nil + "x".send(@method, 2,1).should == nil + + "x".send(@method, -2,0).should == nil + "x".send(@method, -2,1).should == nil + end + + it "returns nil if the length is negative" do + "hello there".send(@method, 4,-3).should == nil + "hello there".send(@method, -4,-3).should == nil + end + + it "calls to_int on the given index and the given length" do + "hello".send(@method, 0.5, 1).should == "h" + "hello".send(@method, 0.5, 2.5).should == "he" + "hello".send(@method, 1, 2.5).should == "el" + + obj = mock('2') + obj.should_receive(:to_int).exactly(4).times.and_return(2) + + "hello".send(@method, obj, 1).should == "l" + "hello".send(@method, obj, obj).should == "ll" + "hello".send(@method, 0, obj).should == "he" + end + + it "raises a TypeError when idx or length can't be converted to an integer" do + lambda { "hello".send(@method, mock('x'), 0) }.should raise_error(TypeError) + lambda { "hello".send(@method, 0, mock('x')) }.should raise_error(TypeError) + + # I'm deliberately including this here. + # It means that str.send(@method, other, idx) isn't supported. + lambda { "hello".send(@method, "", 0) }.should raise_error(TypeError) + end + + it "raises a TypeError when the given index or the given length is nil" do + lambda { "hello".send(@method, 1, nil) }.should raise_error(TypeError) + lambda { "hello".send(@method, nil, 1) }.should raise_error(TypeError) + lambda { "hello".send(@method, nil, nil) }.should raise_error(TypeError) + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, 0,0).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 0,4).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 1,4).should be_an_instance_of(StringSpecs::MyString) + end + + it "handles repeated application" do + "hello world".send(@method, 6, 5).send(@method, 0, 1).should == 'w' + "hello world".send(@method, 6, 5).send(@method, 0, 5).should == 'world' + + "hello world".send(@method, 6, 5).send(@method, 1, 1).should == 'o' + "hello world".send(@method, 6, 5).send(@method, 1, 4).should == 'orld' + + "hello world".send(@method, 6, 5).send(@method, 4, 1).should == 'd' + "hello world".send(@method, 6, 5).send(@method, 5, 0).should == '' + + "hello world".send(@method, 6, 0).send(@method, -1, 0).should == nil + "hello world".send(@method, 6, 0).send(@method, 1, 1).should == nil + end +end + +describe :string_slice_range, shared: true do + it "returns the substring given by the offsets of the range" do + "hello there".send(@method, 1..1).should == "e" + "hello there".send(@method, 1..3).should == "ell" + "hello there".send(@method, 1...3).should == "el" + "hello there".send(@method, -4..-2).should == "her" + "hello there".send(@method, -4...-2).should == "he" + "hello there".send(@method, 5..-1).should == " there" + "hello there".send(@method, 5...-1).should == " ther" + + "".send(@method, 0..0).should == "" + + "x".send(@method, 0..0).should == "x" + "x".send(@method, 0..1).should == "x" + "x".send(@method, 0...1).should == "x" + "x".send(@method, 0..-1).should == "x" + + "x".send(@method, 1..1).should == "" + "x".send(@method, 1..-1).should == "" + end + + it "returns nil if the beginning of the range falls outside of self" do + "hello there".send(@method, 12..-1).should == nil + "hello there".send(@method, 20..25).should == nil + "hello there".send(@method, 20..1).should == nil + "hello there".send(@method, -20..1).should == nil + "hello there".send(@method, -20..-1).should == nil + + "".send(@method, -1..-1).should == nil + "".send(@method, -1...-1).should == nil + "".send(@method, -1..0).should == nil + "".send(@method, -1...0).should == nil + end + + it "returns an empty string if range.begin is inside self and > real end" do + "hello there".send(@method, 1...1).should == "" + "hello there".send(@method, 4..2).should == "" + "hello".send(@method, 4..-4).should == "" + "hello there".send(@method, -5..-6).should == "" + "hello there".send(@method, -2..-4).should == "" + "hello there".send(@method, -5..-6).should == "" + "hello there".send(@method, -5..2).should == "" + + "".send(@method, 0...0).should == "" + "".send(@method, 0..-1).should == "" + "".send(@method, 0...-1).should == "" + + "x".send(@method, 0...0).should == "" + "x".send(@method, 0...-1).should == "" + "x".send(@method, 1...1).should == "" + "x".send(@method, 1...-1).should == "" + end + + it "always taints resulting strings when self is tainted" do + str = "hello world" + str.taint + + str.send(@method, 0..0).tainted?.should == true + str.send(@method, 0...0).tainted?.should == true + str.send(@method, 0..1).tainted?.should == true + str.send(@method, 0...1).tainted?.should == true + str.send(@method, 2..3).tainted?.should == true + str.send(@method, 2..0).tainted?.should == true + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, 0...0).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 0..4).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 1..4).should be_an_instance_of(StringSpecs::MyString) + end + + it "calls to_int on range arguments" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + from.should_receive(:<=>).twice.and_return(0) + + from.should_receive(:to_int).twice.and_return(1) + to.should_receive(:to_int).twice.and_return(-2) + + "hello there".send(@method, from..to).should == "ello ther" + "hello there".send(@method, from...to).should == "ello the" + end + + it "works with Range subclasses" do + a = "GOOD" + range_incl = StringSpecs::MyRange.new(1, 2) + range_excl = StringSpecs::MyRange.new(-3, -1, true) + + a.send(@method, range_incl).should == "OO" + a.send(@method, range_excl).should == "OO" + end + + it "handles repeated application" do + "hello world".send(@method, 6..11).send(@method, 0..0).should == 'w' + "hello world".send(@method, 6..11).send(@method, 0..4).should == 'world' + + "hello world".send(@method, 6..11).send(@method, 1..1).should == 'o' + "hello world".send(@method, 6..11).send(@method, 1..4).should == 'orld' + + "hello world".send(@method, 6..11).send(@method, 4..4).should == 'd' + "hello world".send(@method, 6..11).send(@method, 5..4).should == '' + + "hello world".send(@method, 6..5).send(@method, -1..-1).should == nil + "hello world".send(@method, 6..5).send(@method, 1..1).should == nil + end +end + +describe :string_slice_regexp, shared: true do + it "returns the matching portion of self" do + "hello there".send(@method, /[aeiou](.)\1/).should == "ell" + "".send(@method, //).should == "" + end + + it "returns nil if there is no match" do + "hello there".send(@method, /xyz/).should == nil + end + + not_supported_on :opal do + it "always taints resulting strings when self or regexp is tainted" do + strs = ["hello world"] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + str.send(@method, //).tainted?.should == str.tainted? + str.send(@method, /hello/).tainted?.should == str.tainted? + + tainted_re = /./ + tainted_re.taint + + str.send(@method, tainted_re).tainted?.should == true + end + end + + it "returns an untrusted string if the regexp is untrusted" do + "hello".send(@method, /./.untrust).untrusted?.should be_true + end + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, //).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, /../).should be_an_instance_of(StringSpecs::MyString) + end + + it "sets $~ to MatchData when there is a match and nil when there's none" do + 'hello'.send(@method, /./) + $~[0].should == 'h' + + 'hello'.send(@method, /not/) + $~.should == nil + end +end + +describe :string_slice_regexp_index, shared: true do + it "returns the capture for the given index" do + "hello there".send(@method, /[aeiou](.)\1/, 0).should == "ell" + "hello there".send(@method, /[aeiou](.)\1/, 1).should == "l" + "hello there".send(@method, /[aeiou](.)\1/, -1).should == "l" + + "har".send(@method, /(.)(.)(.)/, 0).should == "har" + "har".send(@method, /(.)(.)(.)/, 1).should == "h" + "har".send(@method, /(.)(.)(.)/, 2).should == "a" + "har".send(@method, /(.)(.)(.)/, 3).should == "r" + "har".send(@method, /(.)(.)(.)/, -1).should == "r" + "har".send(@method, /(.)(.)(.)/, -2).should == "a" + "har".send(@method, /(.)(.)(.)/, -3).should == "h" + end + + it "always taints resulting strings when self or regexp is tainted" do + strs = ["hello world"] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + str.send(@method, //, 0).tainted?.should == str.tainted? + str.send(@method, /hello/, 0).tainted?.should == str.tainted? + + str.send(@method, /(.)(.)(.)/, 0).tainted?.should == str.tainted? + str.send(@method, /(.)(.)(.)/, 1).tainted?.should == str.tainted? + str.send(@method, /(.)(.)(.)/, -1).tainted?.should == str.tainted? + str.send(@method, /(.)(.)(.)/, -2).tainted?.should == str.tainted? + + tainted_re = /(.)(.)(.)/ + tainted_re.taint + + str.send(@method, tainted_re, 0).tainted?.should == true + str.send(@method, tainted_re, 1).tainted?.should == true + str.send(@method, tainted_re, -1).tainted?.should == true + end + end + + not_supported_on :opal do + it "returns an untrusted string if the regexp is untrusted" do + "hello".send(@method, /(.)/.untrust, 1).untrusted?.should be_true + end + end + + it "returns nil if there is no match" do + "hello there".send(@method, /(what?)/, 1).should == nil + end + + it "returns nil if there is no capture for the given index" do + "hello there".send(@method, /[aeiou](.)\1/, 2).should == nil + # You can't refer to 0 using negative indices + "hello there".send(@method, /[aeiou](.)\1/, -2).should == nil + end + + it "calls to_int on the given index" do + obj = mock('2') + obj.should_receive(:to_int).and_return(2) + + "har".send(@method, /(.)(.)(.)/, 1.5).should == "h" + "har".send(@method, /(.)(.)(.)/, obj).should == "a" + end + + it "raises a TypeError when the given index can't be converted to Integer" do + lambda { "hello".send(@method, /(.)(.)(.)/, mock('x')) }.should raise_error(TypeError) + lambda { "hello".send(@method, /(.)(.)(.)/, {}) }.should raise_error(TypeError) + lambda { "hello".send(@method, /(.)(.)(.)/, []) }.should raise_error(TypeError) + end + + it "raises a TypeError when the given index is nil" do + lambda { "hello".send(@method, /(.)(.)(.)/, nil) }.should raise_error(TypeError) + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, /(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, /(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString) + end + + it "sets $~ to MatchData when there is a match and nil when there's none" do + 'hello'.send(@method, /.(.)/, 0) + $~[0].should == 'he' + + 'hello'.send(@method, /.(.)/, 1) + $~[1].should == 'e' + + 'hello'.send(@method, /not/, 0) + $~.should == nil + end +end + +describe :string_slice_string, shared: true do + it "returns other_str if it occurs in self" do + s = "lo" + "hello there".send(@method, s).should == s + end + + it "taints resulting strings when other is tainted" do + strs = ["", "hello world", "hello"] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + strs.each do |other| + r = str.send(@method, other) + + r.tainted?.should == !r.nil? & other.tainted? + end + end + end + + it "doesn't set $~" do + $~ = nil + + 'hello'.send(@method, 'll') + $~.should == nil + end + + it "returns nil if there is no match" do + "hello there".send(@method, "bye").should == nil + end + + it "doesn't call to_str on its argument" do + o = mock('x') + o.should_not_receive(:to_str) + + lambda { "hello".send(@method, o) }.should raise_error(TypeError) + end + + it "returns a subclass instance when given a subclass instance" do + s = StringSpecs::MyString.new("el") + r = "hello".send(@method, s) + r.should == "el" + r.should be_an_instance_of(StringSpecs::MyString) + end +end + +describe :string_slice_regexp_group, shared: true do + not_supported_on :opal do + it "returns the capture for the given name" do + "hello there".send(@method, /(?[aeiou](.))/, 'g').should == "el" + "hello there".send(@method, /[aeiou](?.)/, 'g').should == "l" + + "har".send(@method, /(?(.)(.)(.))/, 'g').should == "har" + "har".send(@method, /(?.)(.)(.)/, 'h').should == "h" + "har".send(@method, /(.)(?.)(.)/, 'a').should == "a" + "har".send(@method, /(.)(.)(?.)/, 'r').should == "r" + "har".send(@method, /(?.)(?.)(?.)/, 'r').should == "r" + end + + it "returns the last capture for duplicate names" do + "hello there".send(@method, /(?h)(?.)/, 'g').should == "e" + "hello there".send(@method, /(?h)(?.)(?.)/, 'g').should == "e" + end + + it "returns the innermost capture for nested duplicate names" do + "hello there".send(@method, /(?h(?.))/, 'g').should == "e" + end + + it "always taints resulting strings when self or regexp is tainted" do + strs = ["hello world"] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + str.send(@method, /(?hello)/, 'hi').tainted?.should == str.tainted? + + str.send(@method, /(?(.)(.)(.))/, 'g').tainted?.should == str.tainted? + str.send(@method, /(?.)(.)(.)/, 'h').tainted?.should == str.tainted? + str.send(@method, /(.)(?.)(.)/, 'a').tainted?.should == str.tainted? + str.send(@method, /(.)(.)(?.)/, 'r').tainted?.should == str.tainted? + str.send(@method, /(?.)(?.)(?.)/, 'r').tainted?.should == str.tainted? + + tainted_re = /(?.)(?.)(?.)/ + tainted_re.taint + + str.send(@method, tainted_re, 'a').tainted?.should be_true + str.send(@method, tainted_re, 'b').tainted?.should be_true + str.send(@method, tainted_re, 'c').tainted?.should be_true + end + end + + it "returns nil if there is no match" do + "hello there".send(@method, /(?what?)/, 'whut').should be_nil + end + + it "raises an IndexError if there is no capture for the given name" do + lambda do + "hello there".send(@method, /[aeiou](.)\1/, 'non') + end.should raise_error(IndexError) + end + + it "raises a TypeError when the given name is not a String" do + lambda { "hello".send(@method, /(?.)/, mock('x')) }.should raise_error(TypeError) + lambda { "hello".send(@method, /(?.)/, {}) }.should raise_error(TypeError) + lambda { "hello".send(@method, /(?.)/, []) }.should raise_error(TypeError) + end + + it "raises an IndexError when given the empty String as a group name" do + lambda { "hello".send(@method, /(?)/, '') }.should raise_error(IndexError) + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, /(?.)/, 'q').should be_an_instance_of(StringSpecs::MyString) + end + + it "sets $~ to MatchData when there is a match and nil when there's none" do + 'hello'.send(@method, /(?.(.))/, 'hi') + $~[0].should == 'he' + + 'hello'.send(@method, /(?not)/, 'non') + $~.should be_nil + end + end +end + +describe :string_slice_symbol, shared: true do + it "raises TypeError" do + lambda { 'hello'.send(@method, :hello) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/string/shared/succ.rb b/spec/rubyspec/core/string/shared/succ.rb new file mode 100644 index 0000000000..4854cb7146 --- /dev/null +++ b/spec/rubyspec/core/string/shared/succ.rb @@ -0,0 +1,88 @@ +# -*- encoding: binary -*- +describe :string_succ, shared: true do + it "returns an empty string for empty strings" do + "".send(@method).should == "" + end + + it "returns the successor by increasing the rightmost alphanumeric (digit => digit, letter => letter with same case)" do + "abcd".send(@method).should == "abce" + "THX1138".send(@method).should == "THX1139" + + "<>".send(@method).should == "<>" + "==A??".send(@method).should == "==B??" + end + + it "increases non-alphanumerics (via ascii rules) if there are no alphanumerics" do + "***".send(@method).should == "**+" + "**`".send(@method).should == "**a" + end + + it "increases the next best alphanumeric (jumping over non-alphanumerics) if there is a carry" do + "dz".send(@method).should == "ea" + "HZ".send(@method).should == "IA" + "49".send(@method).should == "50" + + "izz".send(@method).should == "jaa" + "IZZ".send(@method).should == "JAA" + "699".send(@method).should == "700" + + "6Z99z99Z".send(@method).should == "7A00a00A" + + "1999zzz".send(@method).should == "2000aaa" + "NZ/[]ZZZ9999".send(@method).should == "OA/[]AAA0000" + end + + it "increases the next best character if there is a carry for non-alphanumerics" do + "(\xFF".send(@method).should == ")\x00" + "`\xFF".send(@method).should == "a\x00" + "<\xFF\xFF".send(@method).should == "=\x00\x00" + end + + it "adds an additional character (just left to the last increased one) if there is a carry and no character left to increase" do + "z".send(@method).should == "aa" + "Z".send(@method).should == "AA" + "9".send(@method).should == "10" + + "zz".send(@method).should == "aaa" + "ZZ".send(@method).should == "AAA" + "99".send(@method).should == "100" + + "9Z99z99Z".send(@method).should == "10A00a00A" + + "ZZZ9999".send(@method).should == "AAAA0000" + "/[]9999".send(@method).should == "/[]10000" + "/[]ZZZ9999".send(@method).should == "/[]AAAA0000" + "Z/[]ZZZ9999".send(@method).should == "AA/[]AAA0000" + + # non-alphanumeric cases + "\xFF".send(@method).should == "\x01\x00" + "\xFF\xFF".send(@method).should == "\x01\x00\x00" + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").send(@method).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(StringSpecs::MyString) + end + + it "taints the result if self is tainted" do + ["", "a", "z", "Z", "9", "\xFF", "\xFF\xFF"].each do |s| + s.taint.send(@method).tainted?.should == true + end + end +end + +describe :string_succ_bang, shared: true do + it "is equivalent to succ, but modifies self in place (still returns self)" do + ["", "abcd", "THX1138"].each do |s| + r = s.dup.send(@method) + s.send(@method).should equal(s) + s.should == r + end + end + + it "raises a RuntimeError if self is frozen" do + lambda { "".freeze.send(@method) }.should raise_error(RuntimeError) + lambda { "abcd".freeze.send(@method) }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/shared/to_a.rb b/spec/rubyspec/core/string/shared/to_a.rb new file mode 100644 index 0000000000..bad3ea6584 --- /dev/null +++ b/spec/rubyspec/core/string/shared/to_a.rb @@ -0,0 +1,9 @@ +describe :string_to_a, shared: true do + it "returns an empty array for empty strings" do + "".send(@method).should == [] + end + + it "returns an array containing the string for non-empty strings" do + "hello".send(@method).should == ["hello"] + end +end diff --git a/spec/rubyspec/core/string/shared/to_s.rb b/spec/rubyspec/core/string/shared/to_s.rb new file mode 100644 index 0000000000..a5a13e4f26 --- /dev/null +++ b/spec/rubyspec/core/string/shared/to_s.rb @@ -0,0 +1,18 @@ +describe :string_to_s, shared: true do + it "returns self when self.class == String" do + a = "a string" + a.should equal(a.send(@method)) + end + + it "returns a new instance of String when called on a subclass" do + a = StringSpecs::MyString.new("a string") + s = a.send(@method) + s.should == "a string" + s.should be_an_instance_of(String) + end + + it "taints the result when self is tainted" do + "x".taint.send(@method).tainted?.should == true + StringSpecs::MyString.new("x").taint.send(@method).tainted?.should == true + end +end diff --git a/spec/rubyspec/core/string/shared/to_sym.rb b/spec/rubyspec/core/string/shared/to_sym.rb new file mode 100644 index 0000000000..501247078d --- /dev/null +++ b/spec/rubyspec/core/string/shared/to_sym.rb @@ -0,0 +1,24 @@ +describe :string_to_sym, shared: true do + it "returns the symbol corresponding to self" do + "Koala".send(@method).should == :Koala + 'cat'.send(@method).should == :cat + '@cat'.send(@method).should == :@cat + 'cat and dog'.send(@method).should == :"cat and dog" + "abc=".send(@method).should == :abc= + end + + it "does not special case +(binary) and -(binary)" do + "+(binary)".send(@method).should == :"+(binary)" + "-(binary)".send(@method).should == :"-(binary)" + end + + it "does not special case certain operators" do + [ ["!@", :"!@"], + ["~@", :"~@"], + ["!(unary)", :"!(unary)"], + ["~(unary)", :"~(unary)"], + ["+(unary)", :"+(unary)"], + ["-(unary)", :"-(unary)"] + ].should be_computed_by(@method) + end +end diff --git a/spec/rubyspec/core/string/size_spec.rb b/spec/rubyspec/core/string/size_spec.rb new file mode 100644 index 0000000000..b3172453ea --- /dev/null +++ b/spec/rubyspec/core/string/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "String#size" do + it_behaves_like(:string_length, :size) +end diff --git a/spec/rubyspec/core/string/slice_spec.rb b/spec/rubyspec/core/string/slice_spec.rb new file mode 100644 index 0000000000..8018cc2140 --- /dev/null +++ b/spec/rubyspec/core/string/slice_spec.rb @@ -0,0 +1,476 @@ +# -*- encoding: utf-8 -*- + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/slice.rb', __FILE__) + +describe "String#slice" do + it_behaves_like :string_slice, :slice +end + +describe "String#slice with index, length" do + it_behaves_like :string_slice_index_length, :slice +end + +describe "String#slice with Range" do + it_behaves_like :string_slice_range, :slice +end + +describe "String#slice with Regexp" do + it_behaves_like :string_slice_regexp, :slice +end + +describe "String#slice with Regexp, index" do + it_behaves_like :string_slice_regexp_index, :slice +end + +describe "String#slice with Regexp, group" do + it_behaves_like :string_slice_regexp_group, :slice +end + +describe "String#slice with String" do + it_behaves_like :string_slice_string, :slice +end + +describe "String#slice with Symbol" do + it_behaves_like :string_slice_symbol, :slice +end + +describe "String#slice! with index" do + it "deletes and return the char at the given position" do + a = "hello" + a.slice!(1).should == ?e + a.should == "hllo" + a.slice!(-1).should == ?o + a.should == "hll" + end + + it "returns nil if idx is outside of self" do + a = "hello" + a.slice!(20).should == nil + a.should == "hello" + a.slice!(-20).should == nil + a.should == "hello" + end + + it "raises a RuntimeError if self is frozen" do + lambda { "hello".freeze.slice!(1) }.should raise_error(RuntimeError) + lambda { "hello".freeze.slice!(10) }.should raise_error(RuntimeError) + lambda { "".freeze.slice!(0) }.should raise_error(RuntimeError) + end + + it "calls to_int on index" do + "hello".slice!(0.5).should == ?h + + obj = mock('1') + obj.should_receive(:to_int).at_least(1).and_return(1) + "hello".slice!(obj).should == ?e + + obj = mock('1') + obj.should_receive(:respond_to?).at_least(1).with(:to_int, true).and_return(true) + obj.should_receive(:method_missing).at_least(1).with(:to_int).and_return(1) + "hello".slice!(obj).should == ?e + end + + with_feature :encoding do + + it "returns the character given by the character index" do + "hellö there".send(@method, 1).should == "e" + "hellö there".send(@method, 4).should == "ö" + "hellö there".send(@method, 6).should == "t" + end + + end +end + +describe "String#slice! with index, length" do + it "deletes and returns the substring at idx and the given length" do + a = "hello" + a.slice!(1, 2).should == "el" + a.should == "hlo" + + a.slice!(1, 0).should == "" + a.should == "hlo" + + a.slice!(-2, 4).should == "lo" + a.should == "h" + end + + it "always taints resulting strings when self is tainted" do + str = "hello world" + str.taint + + str.slice!(0, 0).tainted?.should == true + str.slice!(2, 1).tainted?.should == true + end + + it "returns nil if the given position is out of self" do + a = "hello" + a.slice(10, 3).should == nil + a.should == "hello" + + a.slice(-10, 20).should == nil + a.should == "hello" + end + + it "returns nil if the length is negative" do + a = "hello" + a.slice(4, -3).should == nil + a.should == "hello" + end + + it "raises a RuntimeError if self is frozen" do + lambda { "hello".freeze.slice!(1, 2) }.should raise_error(RuntimeError) + lambda { "hello".freeze.slice!(10, 3) }.should raise_error(RuntimeError) + lambda { "hello".freeze.slice!(-10, 3)}.should raise_error(RuntimeError) + lambda { "hello".freeze.slice!(4, -3) }.should raise_error(RuntimeError) + lambda { "hello".freeze.slice!(10, 3) }.should raise_error(RuntimeError) + lambda { "hello".freeze.slice!(-10, 3)}.should raise_error(RuntimeError) + lambda { "hello".freeze.slice!(4, -3) }.should raise_error(RuntimeError) + end + + it "calls to_int on idx and length" do + "hello".slice!(0.5, 2.5).should == "he" + + obj = mock('2') + def obj.to_int() 2 end + "hello".slice!(obj, obj).should == "ll" + + obj = mock('2') + def obj.respond_to?(name, *) name == :to_int; end + def obj.method_missing(name, *) name == :to_int ? 2 : super; end + "hello".slice!(obj, obj).should == "ll" + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(0, 0).should be_an_instance_of(StringSpecs::MyString) + s.slice!(0, 4).should be_an_instance_of(StringSpecs::MyString) + end + + with_feature :encoding do + + it "returns the substring given by the character offsets" do + "hellö there".send(@method, 1,0).should == "" + "hellö there".send(@method, 1,3).should == "ell" + "hellö there".send(@method, 1,6).should == "ellö t" + "hellö there".send(@method, 1,9).should == "ellö ther" + end + + it "treats invalid bytes as single bytes" do + xE6xCB = [0xE6,0xCB].pack('CC').force_encoding('utf-8') + "a#{xE6xCB}b".send(@method, 1, 2).should == xE6xCB + end + end +end + +describe "String#slice! Range" do + it "deletes and return the substring given by the offsets of the range" do + a = "hello" + a.slice!(1..3).should == "ell" + a.should == "ho" + a.slice!(0..0).should == "h" + a.should == "o" + a.slice!(0...0).should == "" + a.should == "o" + + # Edge Case? + "hello".slice!(-3..-9).should == "" + end + + it "returns nil if the given range is out of self" do + a = "hello" + a.slice!(-6..-9).should == nil + a.should == "hello" + + b = "hello" + b.slice!(10..20).should == nil + b.should == "hello" + end + + it "always taints resulting strings when self is tainted" do + str = "hello world" + str.taint + + str.slice!(0..0).tainted?.should == true + str.slice!(2..3).tainted?.should == true + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(0...0).should be_an_instance_of(StringSpecs::MyString) + s.slice!(0..4).should be_an_instance_of(StringSpecs::MyString) + end + + it "calls to_int on range arguments" do + from = mock('from') + to = mock('to') + + # So we can construct a range out of them... + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.to_int() 1 end + def to.to_int() -2 end + + "hello there".slice!(from..to).should == "ello ther" + + from = mock('from') + to = mock('to') + + def from.<=>(o) 0 end + def to.<=>(o) 0 end + + def from.respond_to?(name, *) name == :to_int; end + def from.method_missing(name) name == :to_int ? 1 : super; end + def to.respond_to?(name, *) name == :to_int; end + def to.method_missing(name) name == :to_int ? -2 : super; end + + "hello there".slice!(from..to).should == "ello ther" + end + + it "works with Range subclasses" do + a = "GOOD" + range_incl = StringSpecs::MyRange.new(1, 2) + + a.slice!(range_incl).should == "OO" + end + + with_feature :encoding do + + it "returns the substring given by the character offsets of the range" do + "hellö there".send(@method, 1..1).should == "e" + "hellö there".send(@method, 1..3).should == "ell" + "hellö there".send(@method, 1...3).should == "el" + "hellö there".send(@method, -4..-2).should == "her" + "hellö there".send(@method, -4...-2).should == "he" + "hellö there".send(@method, 5..-1).should == " there" + "hellö there".send(@method, 5...-1).should == " ther" + end + + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda { "hello".freeze.slice!(1..3) }.should raise_error(RuntimeError) + end + + # see redmine #1551 + it "raises a RuntimeError on a frozen instance that would not be modified" do + lambda { "hello".freeze.slice!(10..20)}.should raise_error(RuntimeError) + end +end + +describe "String#slice! with Regexp" do + it "deletes and returns the first match from self" do + s = "this is a string" + s.slice!(/s.*t/).should == 's is a st' + s.should == 'thiring' + + c = "hello hello" + c.slice!(/llo/).should == "llo" + c.should == "he hello" + end + + it "returns nil if there was no match" do + s = "this is a string" + s.slice!(/zzz/).should == nil + s.should == "this is a string" + end + + it "always taints resulting strings when self or regexp is tainted" do + strs = ["hello world"] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + str = str.dup + str.slice!(//).tainted?.should == str.tainted? + str.slice!(/hello/).tainted?.should == str.tainted? + + tainted_re = /./ + tainted_re.taint + + str.slice!(tainted_re).tainted?.should == true + end + end + + it "doesn't taint self when regexp is tainted" do + s = "hello" + s.slice!(/./.taint) + s.tainted?.should == false + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(//).should be_an_instance_of(StringSpecs::MyString) + s.slice!(/../).should be_an_instance_of(StringSpecs::MyString) + end + + with_feature :encoding do + it "returns the matching portion of self with a multi byte character" do + "hëllo there".send(@method, /[ë](.)\1/).should == "ëll" + "".send(@method, //).should == "" + end + end + + it "sets $~ to MatchData when there is a match and nil when there's none" do + 'hello'.slice!(/./) + $~[0].should == 'h' + + 'hello'.slice!(/not/) + $~.should == nil + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError on a frozen instance that would not be modified" do + lambda { "this is a string".freeze.slice!(/zzz/) }.should raise_error(RuntimeError) + end +end + +describe "String#slice! with Regexp, index" do + it "deletes and returns the capture for idx from self" do + str = "hello there" + str.slice!(/[aeiou](.)\1/, 0).should == "ell" + str.should == "ho there" + str.slice!(/(t)h/, 1).should == "t" + str.should == "ho here" + end + + it "always taints resulting strings when self or regexp is tainted" do + strs = ["hello world"] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + str = str.dup + str.slice!(//, 0).tainted?.should == str.tainted? + str.slice!(/hello/, 0).tainted?.should == str.tainted? + + tainted_re = /(.)(.)(.)/ + tainted_re.taint + + str.slice!(tainted_re, 1).tainted?.should == true + end + end + + it "doesn't taint self when regexp is tainted" do + s = "hello" + s.slice!(/(.)(.)/.taint, 1) + s.tainted?.should == false + end + + it "returns nil if there was no match" do + s = "this is a string" + s.slice!(/x(zzz)/, 1).should == nil + s.should == "this is a string" + end + + it "returns nil if there is no capture for idx" do + "hello there".slice!(/[aeiou](.)\1/, 2).should == nil + # You can't refer to 0 using negative indices + "hello there".slice!(/[aeiou](.)\1/, -2).should == nil + end + + it "accepts a Float for capture index" do + "har".slice!(/(.)(.)(.)/, 1.5).should == "h" + end + + it "calls #to_int to convert an Object to capture index" do + obj = mock('2') + obj.should_receive(:to_int).at_least(1).times.and_return(2) + + "har".slice!(/(.)(.)(.)/, obj).should == "a" + end + + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(/(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString) + s.slice!(/(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString) + end + + with_feature :encoding do + it "returns the encoding aware capture for the given index" do + "hår".send(@method, /(.)(.)(.)/, 0).should == "hår" + "hår".send(@method, /(.)(.)(.)/, 1).should == "h" + "hår".send(@method, /(.)(.)(.)/, 2).should == "å" + "hår".send(@method, /(.)(.)(.)/, 3).should == "r" + "hår".send(@method, /(.)(.)(.)/, -1).should == "r" + "hår".send(@method, /(.)(.)(.)/, -2).should == "å" + "hår".send(@method, /(.)(.)(.)/, -3).should == "h" + end + end + + it "sets $~ to MatchData when there is a match and nil when there's none" do + 'hello'[/.(.)/, 0] + $~[0].should == 'he' + + 'hello'[/.(.)/, 1] + $~[1].should == 'e' + + 'hello'[/not/, 0] + $~.should == nil + end + + it "raises a RuntimeError if self is frozen" do + lambda { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(RuntimeError) + lambda { "this is a string".freeze.slice!(/zzz/, 0)}.should raise_error(RuntimeError) + lambda { "this is a string".freeze.slice!(/(.)/, 2)}.should raise_error(RuntimeError) + end +end + +describe "String#slice! with String" do + it "removes and returns the first occurrence of other_str from self" do + c = "hello hello" + c.slice!('llo').should == "llo" + c.should == "he hello" + end + + it "taints resulting strings when other is tainted" do + strs = ["", "hello world", "hello"] + strs += strs.map { |s| s.dup.taint } + + strs.each do |str| + str = str.dup + strs.each do |other| + other = other.dup + r = str.slice!(other) + + r.tainted?.should == !r.nil? & other.tainted? + end + end + end + + it "doesn't set $~" do + $~ = nil + + 'hello'.slice!('ll') + $~.should == nil + end + + it "returns nil if self does not contain other" do + a = "hello" + a.slice!('zzz').should == nil + a.should == "hello" + end + + it "doesn't call to_str on its argument" do + o = mock('x') + o.should_not_receive(:to_str) + + lambda { "hello".slice!(o) }.should raise_error(TypeError) + end + + it "returns a subclass instance when given a subclass instance" do + s = StringSpecs::MyString.new("el") + r = "hello".slice!(s) + r.should == "el" + r.should be_an_instance_of(StringSpecs::MyString) + end + + it "raises a RuntimeError if self is frozen" do + lambda { "hello hello".freeze.slice!('llo') }.should raise_error(RuntimeError) + lambda { "this is a string".freeze.slice!('zzz')}.should raise_error(RuntimeError) + lambda { "this is a string".freeze.slice!('zzz')}.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/split_spec.rb b/spec/rubyspec/core/string/split_spec.rb new file mode 100644 index 0000000000..7d7d383f9c --- /dev/null +++ b/spec/rubyspec/core/string/split_spec.rb @@ -0,0 +1,401 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#split with String" do + with_feature :encoding do + it "throws an ArgumentError if the pattern is not a valid string" do + str = 'проверка' + broken_str = 'проверка' + broken_str.force_encoding('binary') + broken_str.chop! + broken_str.force_encoding('utf-8') + lambda { str.split(broken_str) }.should raise_error(ArgumentError) + end + + it "splits on multibyte characters" do + "ありがりがとう".split("が").should == ["あり", "り", "とう"] + end + end + + it "returns an array of substrings based on splitting on the given string" do + "mellow yellow".split("ello").should == ["m", "w y", "w"] + end + + it "suppresses trailing empty fields when limit isn't given or 0" do + "1,2,,3,4,,".split(',').should == ["1", "2", "", "3", "4"] + "1,2,,3,4,,".split(',', 0).should == ["1", "2", "", "3", "4"] + " a b c\nd ".split(" ").should == ["", "a", "b", "c\nd"] + "hai".split("hai").should == [] + ",".split(",").should == [] + ",".split(",", 0).should == [] + end + + it "returns an array with one entry if limit is 1: the original string" do + "hai".split("hai", 1).should == ["hai"] + "x.y.z".split(".", 1).should == ["x.y.z"] + "hello world ".split(" ", 1).should == ["hello world "] + "hi!".split("", 1).should == ["hi!"] + end + + it "returns at most limit fields when limit > 1" do + "hai".split("hai", 2).should == ["", ""] + + "1,2".split(",", 3).should == ["1", "2"] + + "1,2,,3,4,,".split(',', 2).should == ["1", "2,,3,4,,"] + "1,2,,3,4,,".split(',', 3).should == ["1", "2", ",3,4,,"] + "1,2,,3,4,,".split(',', 4).should == ["1", "2", "", "3,4,,"] + "1,2,,3,4,,".split(',', 5).should == ["1", "2", "", "3", "4,,"] + "1,2,,3,4,,".split(',', 6).should == ["1", "2", "", "3", "4", ","] + + "x".split('x', 2).should == ["", ""] + "xx".split('x', 2).should == ["", "x"] + "xx".split('x', 3).should == ["", "", ""] + "xxx".split('x', 2).should == ["", "xx"] + "xxx".split('x', 3).should == ["", "", "x"] + "xxx".split('x', 4).should == ["", "", "", ""] + end + + it "doesn't suppress or limit fields when limit is negative" do + "1,2,,3,4,,".split(',', -1).should == ["1", "2", "", "3", "4", "", ""] + "1,2,,3,4,,".split(',', -5).should == ["1", "2", "", "3", "4", "", ""] + " a b c\nd ".split(" ", -1).should == ["", "a", "b", "c\nd", ""] + ",".split(",", -1).should == ["", ""] + end + + it "defaults to $; when string isn't given or nil" do + begin + old_fs = $; + + [",", ":", "", "XY", nil].each do |fs| + $; = fs + + ["x,y,z,,,", "1:2:", "aXYbXYcXY", ""].each do |str| + expected = str.split(fs || " ") + + str.split(nil).should == expected + str.split.should == expected + + str.split(nil, -1).should == str.split(fs || " ", -1) + str.split(nil, 0).should == str.split(fs || " ", 0) + str.split(nil, 2).should == str.split(fs || " ", 2) + end + end + ensure + $; = old_fs + end + end + + it "ignores leading and continuous whitespace when string is a single space" do + " now's the time ".split(' ').should == ["now's", "the", "time"] + " now's the time ".split(' ', -1).should == ["now's", "the", "time", ""] + " now's the time ".split(' ', 3).should == ["now's", "the", "time "] + + "\t\n a\t\tb \n\r\r\nc\v\vd\v ".split(' ').should == ["a", "b", "c", "d"] + "a\x00a b".split(' ').should == ["a\x00a", "b"] + end + + describe "when limit is zero" do + it "ignores leading and continuous whitespace when string is a single space" do + " now's the time ".split(' ', 0).should == ["now's", "the", "time"] + end + end + + it "splits between characters when its argument is an empty string" do + "hi!".split("").should == ["h", "i", "!"] + "hi!".split("", -1).should == ["h", "i", "!", ""] + "hi!".split("", 0).should == ["h", "i", "!"] + "hi!".split("", 1).should == ["hi!"] + "hi!".split("", 2).should == ["h", "i!"] + "hi!".split("", 3).should == ["h", "i", "!"] + "hi!".split("", 4).should == ["h", "i", "!", ""] + "hi!".split("", 5).should == ["h", "i", "!", ""] + end + + it "tries converting its pattern argument to a string via to_str" do + obj = mock('::') + obj.should_receive(:to_str).and_return("::") + + "hello::world".split(obj).should == ["hello", "world"] + end + + it "tries converting limit to an integer via to_int" do + obj = mock('2') + obj.should_receive(:to_int).and_return(2) + + "1.2.3.4".split(".", obj).should == ["1", "2.3.4"] + end + + it "doesn't set $~" do + $~ = nil + "x.y.z".split(".") + $~.should == nil + end + + it "returns the original string if no matches are found" do + "foo".split("bar").should == ["foo"] + "foo".split("bar", -1).should == ["foo"] + "foo".split("bar", 0).should == ["foo"] + "foo".split("bar", 1).should == ["foo"] + "foo".split("bar", 2).should == ["foo"] + "foo".split("bar", 3).should == ["foo"] + end + + it "returns subclass instances based on self" do + ["", "x.y.z.", " x y "].each do |str| + ["", ".", " "].each do |pat| + [-1, 0, 1, 2].each do |limit| + StringSpecs::MyString.new(str).split(pat, limit).each do |x| + x.should be_an_instance_of(StringSpecs::MyString) + end + + str.split(StringSpecs::MyString.new(pat), limit).each do |x| + x.should be_an_instance_of(String) + end + end + end + end + end + + it "does not call constructor on created subclass instances" do + # can't call should_not_receive on an object that doesn't yet exist + # so failure here is signalled by exception, not expectation failure + + s = StringSpecs::StringWithRaisingConstructor.new('silly:string') + s.split(':').first.should == 'silly' + end + + it "taints the resulting strings if self is tainted" do + ["", "x.y.z.", " x y "].each do |str| + ["", ".", " "].each do |pat| + [-1, 0, 1, 2].each do |limit| + str.dup.taint.split(pat).each do |x| + x.tainted?.should == true + end + + str.split(pat.dup.taint).each do |x| + x.tainted?.should == false + end + end + end + end + end +end + +describe "String#split with Regexp" do + it "divides self on regexp matches" do + " now's the time".split(/ /).should == ["", "now's", "", "the", "time"] + " x\ny ".split(/ /).should == ["", "x\ny"] + "1, 2.34,56, 7".split(/,\s*/).should == ["1", "2.34", "56", "7"] + "1x2X3".split(/x/i).should == ["1", "2", "3"] + end + + it "treats negative limits as no limit" do + "".split(%r!/+!, -1).should == [] + end + + it "suppresses trailing empty fields when limit isn't given or 0" do + "1,2,,3,4,,".split(/,/).should == ["1", "2", "", "3", "4"] + "1,2,,3,4,,".split(/,/, 0).should == ["1", "2", "", "3", "4"] + " a b c\nd ".split(/\s+/).should == ["", "a", "b", "c", "d"] + "hai".split(/hai/).should == [] + ",".split(/,/).should == [] + ",".split(/,/, 0).should == [] + end + + it "returns an array with one entry if limit is 1: the original string" do + "hai".split(/hai/, 1).should == ["hai"] + "xAyBzC".split(/[A-Z]/, 1).should == ["xAyBzC"] + "hello world ".split(/\s+/, 1).should == ["hello world "] + "hi!".split(//, 1).should == ["hi!"] + end + + it "returns at most limit fields when limit > 1" do + "hai".split(/hai/, 2).should == ["", ""] + + "1,2".split(/,/, 3).should == ["1", "2"] + + "1,2,,3,4,,".split(/,/, 2).should == ["1", "2,,3,4,,"] + "1,2,,3,4,,".split(/,/, 3).should == ["1", "2", ",3,4,,"] + "1,2,,3,4,,".split(/,/, 4).should == ["1", "2", "", "3,4,,"] + "1,2,,3,4,,".split(/,/, 5).should == ["1", "2", "", "3", "4,,"] + "1,2,,3,4,,".split(/,/, 6).should == ["1", "2", "", "3", "4", ","] + + "x".split(/x/, 2).should == ["", ""] + "xx".split(/x/, 2).should == ["", "x"] + "xx".split(/x/, 3).should == ["", "", ""] + "xxx".split(/x/, 2).should == ["", "xx"] + "xxx".split(/x/, 3).should == ["", "", "x"] + "xxx".split(/x/, 4).should == ["", "", "", ""] + end + + it "doesn't suppress or limit fields when limit is negative" do + "1,2,,3,4,,".split(/,/, -1).should == ["1", "2", "", "3", "4", "", ""] + "1,2,,3,4,,".split(/,/, -5).should == ["1", "2", "", "3", "4", "", ""] + " a b c\nd ".split(/\s+/, -1).should == ["", "a", "b", "c", "d", ""] + ",".split(/,/, -1).should == ["", ""] + end + + it "defaults to $; when regexp isn't given or nil" do + begin + old_fs = $; + + [/,/, /:/, //, /XY/, /./].each do |fs| + $; = fs + + ["x,y,z,,,", "1:2:", "aXYbXYcXY", ""].each do |str| + expected = str.split(fs) + + str.split(nil).should == expected + str.split.should == expected + + str.split(nil, -1).should == str.split(fs, -1) + str.split(nil, 0).should == str.split(fs, 0) + str.split(nil, 2).should == str.split(fs, 2) + end + end + ensure + $; = old_fs + end + end + + it "splits between characters when regexp matches a zero-length string" do + "hello".split(//).should == ["h", "e", "l", "l", "o"] + "hello".split(//, -1).should == ["h", "e", "l", "l", "o", ""] + "hello".split(//, 0).should == ["h", "e", "l", "l", "o"] + "hello".split(//, 1).should == ["hello"] + "hello".split(//, 2).should == ["h", "ello"] + "hello".split(//, 5).should == ["h", "e", "l", "l", "o"] + "hello".split(//, 6).should == ["h", "e", "l", "l", "o", ""] + "hello".split(//, 7).should == ["h", "e", "l", "l", "o", ""] + + "hi mom".split(/\s*/).should == ["h", "i", "m", "o", "m"] + + "AABCCBAA".split(/(?=B)/).should == ["AA", "BCC", "BAA"] + "AABCCBAA".split(/(?=B)/, -1).should == ["AA", "BCC", "BAA"] + "AABCCBAA".split(/(?=B)/, 2).should == ["AA", "BCCBAA"] + end + + it "respects unicode when splitting between characters" do + str = "こにちわ" + reg = %r!! + ary = str.split(reg) + ary.size.should == 4 + ary.should == ["こ", "に", "ち", "わ"] + end + + it "respects the encoding of the regexp when splitting between characters" do + str = "\303\202" + ary = str.split(//u) + ary.size.should == 1 + ary.should == ["\303\202"] + end + + it "includes all captures in the result array" do + "hello".split(/(el)/).should == ["h", "el", "lo"] + "hi!".split(/()/).should == ["h", "", "i", "", "!"] + "hi!".split(/()/, -1).should == ["h", "", "i", "", "!", "", ""] + "hello".split(/((el))()/).should == ["h", "el", "el", "", "lo"] + "AabB".split(/([a-z])+/).should == ["A", "b", "B"] + end + + it "does not include non-matching captures in the result array" do + "hello".split(/(el)|(xx)/).should == ["h", "el", "lo"] + end + + it "tries converting limit to an integer via to_int" do + obj = mock('2') + obj.should_receive(:to_int).and_return(2) + + "1.2.3.4".split(".", obj).should == ["1", "2.3.4"] + end + + it "returns a type error if limit can't be converted to an integer" do + lambda {"1.2.3.4".split(".", "three")}.should raise_error(TypeError) + lambda {"1.2.3.4".split(".", nil) }.should raise_error(TypeError) + end + + it "doesn't set $~" do + $~ = nil + "x:y:z".split(/:/) + $~.should == nil + end + + it "returns the original string if no matches are found" do + "foo".split(/bar/).should == ["foo"] + "foo".split(/bar/, -1).should == ["foo"] + "foo".split(/bar/, 0).should == ["foo"] + "foo".split(/bar/, 1).should == ["foo"] + "foo".split(/bar/, 2).should == ["foo"] + "foo".split(/bar/, 3).should == ["foo"] + end + + it "returns subclass instances based on self" do + ["", "x:y:z:", " x y "].each do |str| + [//, /:/, /\s+/].each do |pat| + [-1, 0, 1, 2].each do |limit| + StringSpecs::MyString.new(str).split(pat, limit).each do |x| + x.should be_an_instance_of(StringSpecs::MyString) + end + end + end + end + end + + it "does not call constructor on created subclass instances" do + # can't call should_not_receive on an object that doesn't yet exist + # so failure here is signalled by exception, not expectation failure + + s = StringSpecs::StringWithRaisingConstructor.new('silly:string') + s.split(/:/).first.should == 'silly' + end + + it "taints the resulting strings if self is tainted" do + ["", "x:y:z:", " x y "].each do |str| + [//, /:/, /\s+/].each do |pat| + [-1, 0, 1, 2].each do |limit| + str.dup.taint.split(pat, limit).each do |x| + # See the spec below for why the conditional is here + x.tainted?.should be_true unless x.empty? + end + end + end + end + end + + it "taints an empty string if self is tainted" do + ":".taint.split(//, -1).last.tainted?.should be_true + end + + it "doesn't taints the resulting strings if the Regexp is tainted" do + ["", "x:y:z:", " x y "].each do |str| + [//, /:/, /\s+/].each do |pat| + [-1, 0, 1, 2].each do |limit| + str.split(pat.dup.taint, limit).each do |x| + x.tainted?.should be_false + end + end + end + end + end + + it "retains the encoding of the source string" do + ary = "а б в".split + encodings = ary.map { |s| s.encoding } + encodings.should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8] + end + + + it "splits a string on each character for a multibyte encoding and empty split" do + "That's why efficiency could not be helped".split("").size.should == 39 + end + + it "returns an ArgumentError if an invalid UTF-8 string is supplied" do + broken_str = 'проверка' # in russian, means "test" + broken_str.force_encoding('binary') + broken_str.chop! + broken_str.force_encoding('utf-8') + lambda{ broken_str.split(/\r\n|\r|\n/) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/squeeze_spec.rb b/spec/rubyspec/core/string/squeeze_spec.rb new file mode 100644 index 0000000000..d6b3fb6de6 --- /dev/null +++ b/spec/rubyspec/core/string/squeeze_spec.rb @@ -0,0 +1,113 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +# TODO: rewrite all these specs + +describe "String#squeeze" do + it "returns new string where runs of the same character are replaced by a single character when no args are given" do + "yellow moon".squeeze.should == "yelow mon" + end + + it "only squeezes chars that are in the intersection of all sets given" do + "woot squeeze cheese".squeeze("eost", "queo").should == "wot squeze chese" + " now is the".squeeze(" ").should == " now is the" + end + + it "negates sets starting with ^" do + s = "<>" + s.squeeze("beko", "^e").should == s.squeeze("bko") + s.squeeze("^").should == s.squeeze("o") + s.squeeze("^o").should == s.squeeze("") + s.squeeze("^").should == s + "^__^".squeeze("^^").should == "^_^" + "((^^__^^))".squeeze("_^").should == "((^_^))" + end + + it "squeezes all chars in a sequence" do + s = "--subbookkeeper--" + s.squeeze("\x00-\xFF").should == s.squeeze + s.squeeze("bk-o").should == s.squeeze("bklmno") + s.squeeze("b-e").should == s.squeeze("bcde") + s.squeeze("e-").should == "-subbookkeper-" + s.squeeze("-e").should == "-subbookkeper-" + s.squeeze("---").should == "-subbookkeeper-" + "ook--001122".squeeze("--2").should == "ook-012" + "ook--(())".squeeze("(--").should == "ook-()" + s.squeeze("^b-e").should == "-subbokeeper-" + "^^__^^".squeeze("^^-^").should == "^^_^^" + "^^--^^".squeeze("^---").should == "^--^" + + s.squeeze("b-dk-o-").should == "-subokeeper-" + s.squeeze("-b-dk-o").should == "-subokeeper-" + s.squeeze("b-d-k-o").should == "-subokeeper-" + + s.squeeze("bc-e").should == "--subookkeper--" + s.squeeze("^bc-e").should == "-subbokeeper-" + + "AABBCCaabbcc[[]]".squeeze("A-a").should == "ABCabbcc[]" + end + + it "raises an ArgumentError when the parameter is out of sequence" do + s = "--subbookkeeper--" + lambda { s.squeeze("e-b") }.should raise_error(ArgumentError) + lambda { s.squeeze("^e-b") }.should raise_error(ArgumentError) + end + + it "taints the result when self is tainted" do + "hello".taint.squeeze("e").tainted?.should == true + "hello".taint.squeeze("a-z").tainted?.should == true + + "hello".squeeze("e".taint).tainted?.should == false + "hello".squeeze("l".taint).tainted?.should == false + end + + it "tries to convert each set arg to a string using to_str" do + other_string = mock('lo') + other_string.should_receive(:to_str).and_return("lo") + + other_string2 = mock('o') + other_string2.should_receive(:to_str).and_return("o") + + "hello room".squeeze(other_string, other_string2).should == "hello rom" + end + + it "raises a TypeError when one set arg can't be converted to a string" do + lambda { "hello world".squeeze([]) }.should raise_error(TypeError) + lambda { "hello world".squeeze(Object.new)}.should raise_error(TypeError) + lambda { "hello world".squeeze(mock('x')) }.should raise_error(TypeError) + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(StringSpecs::MyString) + end +end + +describe "String#squeeze!" do + it "modifies self in place and returns self" do + a = "yellow moon" + a.squeeze!.should equal(a) + a.should == "yelow mon" + end + + it "returns nil if no modifications were made" do + a = "squeeze" + a.squeeze!("u", "sq").should == nil + a.squeeze!("q").should == nil + a.should == "squeeze" + end + + it "raises an ArgumentError when the parameter is out of sequence" do + s = "--subbookkeeper--" + lambda { s.squeeze!("e-b") }.should raise_error(ArgumentError) + lambda { s.squeeze!("^e-b") }.should raise_error(ArgumentError) + end + + it "raises a RuntimeError when self is frozen" do + a = "yellow moon" + a.freeze + + lambda { a.squeeze!("") }.should raise_error(RuntimeError) + lambda { a.squeeze! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/start_with_spec.rb b/spec/rubyspec/core/string/start_with_spec.rb new file mode 100644 index 0000000000..b85081037d --- /dev/null +++ b/spec/rubyspec/core/string/start_with_spec.rb @@ -0,0 +1,45 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#start_with?" do + it "returns true only if beginning match" do + s = "hello" + s.start_with?('h').should be_true + s.start_with?('hel').should be_true + s.start_with?('el').should be_false + end + + it "returns true only if any beginning match" do + "hello".start_with?('x', 'y', 'he', 'z').should be_true + end + + it "returns true if the search string is empty" do + "hello".start_with?("").should be_true + "".start_with?("").should be_true + end + + it "converts its argument using :to_str" do + s = "hello" + find = mock('h') + find.should_receive(:to_str).and_return("h") + s.start_with?(find).should be_true + end + + it "ignores arguments not convertible to string" do + "hello".start_with?().should be_false + lambda { "hello".start_with?(1) }.should raise_error(TypeError) + lambda { "hello".start_with?(["h"]) }.should raise_error(TypeError) + lambda { "hello".start_with?(1, nil, "h").should }.should raise_error(TypeError) + end + + it "uses only the needed arguments" do + find = mock('h') + find.should_not_receive(:to_str) + "hello".start_with?("h",find).should be_true + end + + it "works for multibyte strings" do + "céréale".start_with?("cér").should be_true + end +end diff --git a/spec/rubyspec/core/string/string_spec.rb b/spec/rubyspec/core/string/string_spec.rb new file mode 100644 index 0000000000..37a858acae --- /dev/null +++ b/spec/rubyspec/core/string/string_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String" do + it "includes Comparable" do + String.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/string/strip_spec.rb b/spec/rubyspec/core/string/strip_spec.rb new file mode 100644 index 0000000000..747fd8cdf2 --- /dev/null +++ b/spec/rubyspec/core/string/strip_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#strip" do + it "returns a new string with leading and trailing whitespace removed" do + " hello ".strip.should == "hello" + " hello world ".strip.should == "hello world" + "\tgoodbye\r\v\n".strip.should == "goodbye" + "\x00 goodbye \x00".strip.should == "\x00 goodbye" + end + + it "returns a copy of self with trailing NULL bytes and whitespace" do + " \x00 goodbye \x00 ".strip.should == "\x00 goodbye" + end + + it "taints the result when self is tainted" do + "".taint.strip.tainted?.should == true + "ok".taint.strip.tainted?.should == true + " ok ".taint.strip.tainted?.should == true + end +end + +describe "String#strip!" do + it "modifies self in place and returns self" do + a = " hello " + a.strip!.should equal(a) + a.should == "hello" + + a = "\tgoodbye\r\v\n" + a.strip! + a.should == "goodbye" + + a = "\000 goodbye \000" + a.strip! + a.should == "\000 goodbye" + + end + + it "returns nil if no modifications where made" do + a = "hello" + a.strip!.should == nil + a.should == "hello" + end + + it "modifies self removing trailing NULL bytes and whitespace" do + a = " \x00 goodbye \x00 " + a.strip! + a.should == "\x00 goodbye" + end + + it "raises a RuntimeError on a frozen instance that is modified" do + lambda { " hello ".freeze.strip! }.should raise_error(RuntimeError) + end + + # see #1552 + it "raises a RuntimeError on a frozen instance that would not be modified" do + lambda {"hello".freeze.strip! }.should raise_error(RuntimeError) + lambda {"".freeze.strip! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/sub_spec.rb b/spec/rubyspec/core/string/sub_spec.rb new file mode 100644 index 0000000000..deaa7e27f1 --- /dev/null +++ b/spec/rubyspec/core/string/sub_spec.rb @@ -0,0 +1,571 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#sub with pattern, replacement" do + it "returns a copy of self when no modification is made" do + a = "hello" + b = a.sub(/w.*$/, "*") + + b.should_not equal(a) + b.should == "hello" + end + + it "returns a copy of self with all occurrences of pattern replaced with replacement" do + "hello".sub(/[aeiou]/, '*').should == "h*llo" + "hello".sub(//, ".").should == ".hello" + end + + it "ignores a block if supplied" do + "food".sub(/f/, "g") { "w" }.should == "good" + end + + it "supports \\G which matches at the beginning of the string" do + "hello world!".sub(/\Ghello/, "hi").should == "hi world!" + end + + it "supports /i for ignoring case" do + "Hello".sub(/h/i, "j").should == "jello" + "hello".sub(/H/i, "j").should == "jello" + end + + it "doesn't interpret regexp metacharacters if pattern is a string" do + "12345".sub('\d', 'a').should == "12345" + '\d'.sub('\d', 'a').should == "a" + end + + it "replaces \\1 sequences with the regexp's corresponding capture" do + str = "hello" + + str.sub(/([aeiou])/, '<\1>').should == "hllo" + str.sub(/(.)/, '\1\1').should == "hhello" + + str.sub(/.(.?)/, '<\0>(\1)').should == "(e)llo" + + str.sub(/.(.)+/, '\1').should == "o" + + str = "ABCDEFGHIJKL" + re = /#{"(.)" * 12}/ + str.sub(re, '\1').should == "A" + str.sub(re, '\9').should == "I" + # Only the first 9 captures can be accessed in MRI + str.sub(re, '\10').should == "A0" + end + + it "treats \\1 sequences without corresponding captures as empty strings" do + str = "hello!" + + str.sub("", '<\1>').should == "<>hello!" + str.sub("h", '<\1>').should == "<>ello!" + + str.sub(//, '<\1>').should == "<>hello!" + str.sub(/./, '\1\2\3').should == "ello!" + str.sub(/.(.{20})?/, '\1').should == "ello!" + end + + it "replaces \\& and \\0 with the complete match" do + str = "hello!" + + str.sub("", '<\0>').should == "<>hello!" + str.sub("", '<\&>').should == "<>hello!" + str.sub("he", '<\0>').should == "llo!" + str.sub("he", '<\&>').should == "llo!" + str.sub("l", '<\0>').should == "helo!" + str.sub("l", '<\&>').should == "helo!" + + str.sub(//, '<\0>').should == "<>hello!" + str.sub(//, '<\&>').should == "<>hello!" + str.sub(/../, '<\0>').should == "llo!" + str.sub(/../, '<\&>').should == "llo!" + str.sub(/(.)./, '<\0>').should == "llo!" + end + + it "replaces \\` with everything before the current match" do + str = "hello!" + + str.sub("", '<\`>').should == "<>hello!" + str.sub("h", '<\`>').should == "<>ello!" + str.sub("l", '<\`>').should == "helo!" + str.sub("!", '<\`>').should == "hello" + + str.sub(//, '<\`>').should == "<>hello!" + str.sub(/..o/, '<\`>').should == "he!" + end + + it "replaces \\' with everything after the current match" do + str = "hello!" + + str.sub("", '<\\\'>').should == "hello!" + str.sub("h", '<\\\'>').should == "ello!" + str.sub("ll", '<\\\'>').should == "heo!" + str.sub("!", '<\\\'>').should == "hello<>" + + str.sub(//, '<\\\'>').should == "hello!" + str.sub(/../, '<\\\'>').should == "llo!" + end + + it "replaces \\\\\\+ with \\\\+" do + "x".sub(/x/, '\\\+').should == "\\+" + end + + it "replaces \\+ with the last paren that actually matched" do + str = "hello!" + + str.sub(/(.)(.)/, '\+').should == "ello!" + str.sub(/(.)(.)+/, '\+').should == "!" + str.sub(/(.)()/, '\+').should == "ello!" + str.sub(/(.)(.{20})?/, '<\+>').should == "ello!" + + str = "ABCDEFGHIJKL" + re = /#{"(.)" * 12}/ + str.sub(re, '\+').should == "L" + end + + it "treats \\+ as an empty string if there was no captures" do + "hello!".sub(/./, '\+').should == "ello!" + end + + it "maps \\\\ in replacement to \\" do + "hello".sub(/./, '\\\\').should == '\\ello' + end + + it "leaves unknown \\x escapes in replacement untouched" do + "hello".sub(/./, '\\x').should == '\\xello' + "hello".sub(/./, '\\y').should == '\\yello' + end + + it "leaves \\ at the end of replacement untouched" do + "hello".sub(/./, 'hah\\').should == 'hah\\ello' + end + + it "taints the result if the original string or replacement is tainted" do + hello = "hello" + hello_t = "hello" + a = "a" + a_t = "a" + empty = "" + empty_t = "" + + hello_t.taint; a_t.taint; empty_t.taint + + hello_t.sub(/./, a).tainted?.should == true + hello_t.sub(/./, empty).tainted?.should == true + + hello.sub(/./, a_t).tainted?.should == true + hello.sub(/./, empty_t).tainted?.should == true + hello.sub(//, empty_t).tainted?.should == true + + hello.sub(//.taint, "foo").tainted?.should == false + end + + it "tries to convert pattern to a string using to_str" do + pattern = mock('.') + pattern.should_receive(:to_str).and_return(".") + + "hello.".sub(pattern, "!").should == "hello!" + end + + not_supported_on :opal do + it "raises a TypeError when pattern is a Symbol" do + lambda { "hello".sub(:woot, "x") }.should raise_error(TypeError) + end + end + + it "raises a TypeError when pattern is an Array" do + lambda { "hello".sub([], "x") }.should raise_error(TypeError) + end + + it "raises a TypeError when pattern can't be converted to a string" do + lambda { "hello".sub(Object.new, nil) }.should raise_error(TypeError) + end + + it "tries to convert replacement to a string using to_str" do + replacement = mock('hello_replacement') + replacement.should_receive(:to_str).and_return("hello_replacement") + + "hello".sub(/hello/, replacement).should == "hello_replacement" + end + + it "raises a TypeError when replacement can't be converted to a string" do + lambda { "hello".sub(/[aeiou]/, []) }.should raise_error(TypeError) + lambda { "hello".sub(/[aeiou]/, 99) }.should raise_error(TypeError) + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(StringSpecs::MyString) + end + + it "sets $~ to MatchData of match and nil when there's none" do + 'hello.'.sub('hello', 'x') + $~[0].should == 'hello' + + 'hello.'.sub('not', 'x') + $~.should == nil + + 'hello.'.sub(/.(.)/, 'x') + $~[0].should == 'he' + + 'hello.'.sub(/not/, 'x') + $~.should == nil + end + + it "replaces \\\\\\1 with \\1" do + "ababa".sub(/(b)/, '\\\1').should == "a\\1aba" + end + + it "replaces \\\\\\\\1 with \\1" do + "ababa".sub(/(b)/, '\\\\1').should == "a\\1aba" + end + + it "replaces \\\\\\\\\\1 with \\" do + "ababa".sub(/(b)/, '\\\\\1').should == "a\\baba" + end + +end + +describe "String#sub with pattern and block" do + it "returns a copy of self with the first occurrences of pattern replaced with the block's return value" do + "hi".sub(/./) { |s| s + ' ' }.should == "h i" + "hi!".sub(/(.)(.)/) { |*a| a.inspect }.should == '["hi"]!' + end + + it "sets $~ for access from the block" do + str = "hello" + str.sub(/([aeiou])/) { "<#{$~[1]}>" }.should == "hllo" + str.sub(/([aeiou])/) { "<#{$1}>" }.should == "hllo" + str.sub("l") { "<#{$~[0]}>" }.should == "helo" + + offsets = [] + + str.sub(/([aeiou])/) do + md = $~ + md.string.should == str + offsets << md.offset(0) + str + end.should == "hhellollo" + + offsets.should == [[1, 2]] + end + + it "sets $~ to MatchData of last match and nil when there's none for access from outside" do + 'hello.'.sub('l') { 'x' } + $~.begin(0).should == 2 + $~[0].should == 'l' + + 'hello.'.sub('not') { 'x' } + $~.should == nil + + 'hello.'.sub(/.(.)/) { 'x' } + $~[0].should == 'he' + + 'hello.'.sub(/not/) { 'x' } + $~.should == nil + end + + it "doesn't raise a RuntimeError if the string is modified while substituting" do + str = "hello" + str.sub(//) { str[0] = 'x' }.should == "xhello" + str.should == "xello" + end + + it "doesn't interpolate special sequences like \\1 for the block's return value" do + repl = '\& \0 \1 \` \\\' \+ \\\\ foo' + "hello".sub(/(.+)/) { repl }.should == repl + end + + it "converts the block's return value to a string using to_s" do + obj = mock('hello_replacement') + obj.should_receive(:to_s).and_return("hello_replacement") + "hello".sub(/hello/) { obj }.should == "hello_replacement" + + obj = mock('ok') + obj.should_receive(:to_s).and_return("ok") + "hello".sub(/.+/) { obj }.should == "ok" + end + + it "taints the result if the original string or replacement is tainted" do + hello = "hello" + hello_t = "hello" + a = "a" + a_t = "a" + empty = "" + empty_t = "" + + hello_t.taint; a_t.taint; empty_t.taint + + hello_t.sub(/./) { a }.tainted?.should == true + hello_t.sub(/./) { empty }.tainted?.should == true + + hello.sub(/./) { a_t }.tainted?.should == true + hello.sub(/./) { empty_t }.tainted?.should == true + hello.sub(//) { empty_t }.tainted?.should == true + + hello.sub(//.taint) { "foo" }.tainted?.should == false + end +end + +describe "String#sub! with pattern, replacement" do + it "modifies self in place and returns self" do + a = "hello" + a.sub!(/[aeiou]/, '*').should equal(a) + a.should == "h*llo" + end + + it "taints self if replacement is tainted" do + a = "hello" + a.sub!(/./.taint, "foo").tainted?.should == false + a.sub!(/./, "foo".taint).tainted?.should == true + end + + it "returns nil if no modifications were made" do + a = "hello" + a.sub!(/z/, '*').should == nil + a.sub!(/z/, 'z').should == nil + a.should == "hello" + end + + it "raises a RuntimeError when self is frozen" do + s = "hello" + s.freeze + + lambda { s.sub!(/ROAR/, "x") }.should raise_error(RuntimeError) + lambda { s.sub!(/e/, "e") }.should raise_error(RuntimeError) + lambda { s.sub!(/[aeiou]/, '*') }.should raise_error(RuntimeError) + end +end + +describe "String#sub! with pattern and block" do + it "modifies self in place and returns self" do + a = "hello" + a.sub!(/[aeiou]/) { '*' }.should equal(a) + a.should == "h*llo" + end + + it "sets $~ for access from the block" do + str = "hello" + str.dup.sub!(/([aeiou])/) { "<#{$~[1]}>" }.should == "hllo" + str.dup.sub!(/([aeiou])/) { "<#{$1}>" }.should == "hllo" + str.dup.sub!("l") { "<#{$~[0]}>" }.should == "helo" + + offsets = [] + + str.dup.sub!(/([aeiou])/) do + md = $~ + md.string.should == str + offsets << md.offset(0) + str + end.should == "hhellollo" + + offsets.should == [[1, 2]] + end + + it "taints self if block's result is tainted" do + a = "hello" + a.sub!(/./.taint) { "foo" }.tainted?.should == false + a.sub!(/./) { "foo".taint }.tainted?.should == true + end + + it "returns nil if no modifications were made" do + a = "hello" + a.sub!(/z/) { '*' }.should == nil + a.sub!(/z/) { 'z' }.should == nil + a.should == "hello" + end + + it "raises a RuntimeError if the string is modified while substituting" do + str = "hello" + lambda { str.sub!(//) { str << 'x' } }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when self is frozen" do + s = "hello" + s.freeze + + lambda { s.sub!(/ROAR/) { "x" } }.should raise_error(RuntimeError) + lambda { s.sub!(/e/) { "e" } }.should raise_error(RuntimeError) + lambda { s.sub!(/[aeiou]/) { '*' } }.should raise_error(RuntimeError) + end +end + +describe "String#sub with pattern and Hash" do + + it "returns a copy of self with the first occurrence of pattern replaced with the value of the corresponding hash key" do + "hello".sub(/./, 'l' => 'L').should == "ello" + "hello!".sub(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she llo!' + "hello".sub('l', 'l' => 'el').should == 'heello' + end + + it "removes keys that don't correspond to matches" do + "hello".sub(/./, 'z' => 'b', 'o' => 'ow').should == "ello" + end + + it "ignores non-String keys" do + "tattoo".sub(/(tt)/, 'tt' => 'b', tt: 'z').should == "taboo" + end + + it "uses a key's value only a single time" do + "food".sub(/o/, 'o' => '0').should == "f0od" + end + + it "uses the hash's default value for missing keys" do + hsh = {} + hsh.default='?' + hsh['o'] = '0' + "food".sub(/./, hsh).should == "?ood" + end + + it "coerces the hash values with #to_s" do + hsh = {} + hsh.default=[] + hsh['o'] = 0 + obj = mock('!') + obj.should_receive(:to_s).and_return('!') + hsh['f'] = obj + "food!".sub(/./, hsh).should == "!ood!" + end + + it "uses the hash's value set from default_proc for missing keys" do + hsh = {} + hsh.default_proc = lambda { |k,v| 'lamb' } + "food!".sub(/./, hsh).should == "lambood!" + end + + it "sets $~ to MatchData of first match and nil when there's none for access from outside" do + 'hello.'.sub('l', 'l' => 'L') + $~.begin(0).should == 2 + $~[0].should == 'l' + + 'hello.'.sub('not', 'ot' => 'to') + $~.should == nil + + 'hello.'.sub(/.(.)/, 'o' => ' hole') + $~[0].should == 'he' + + 'hello.'.sub(/not/, 'z' => 'glark') + $~.should == nil + end + + it "doesn't interpolate special sequences like \\1 for the block's return value" do + repl = '\& \0 \1 \` \\\' \+ \\\\ foo' + "hello".sub(/(.+)/, 'hello' => repl ).should == repl + end + + it "untrusts the result if the original string is untrusted" do + str = "Ghana".untrust + str.sub(/[Aa]na/, 'ana' => '').untrusted?.should be_true + end + + it "untrusts the result if a hash value is untrusted" do + str = "Ghana" + str.sub(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true + end + + it "taints the result if the original string is tainted" do + str = "Ghana".taint + str.sub(/[Aa]na/, 'ana' => '').tainted?.should be_true + end + + it "taints the result if a hash value is tainted" do + str = "Ghana" + str.sub(/a$/, 'a' => 'di'.taint).tainted?.should be_true + end + +end + +describe "String#sub! with pattern and Hash" do + + it "returns self with the first occurrence of pattern replaced with the value of the corresponding hash key" do + "hello".sub!(/./, 'l' => 'L').should == "ello" + "hello!".sub!(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she llo!' + "hello".sub!('l', 'l' => 'el').should == 'heello' + end + + it "removes keys that don't correspond to matches" do + "hello".sub!(/./, 'z' => 'b', 'o' => 'ow').should == "ello" + end + + it "ignores non-String keys" do + "hello".sub!(/(ll)/, 'll' => 'r', ll: 'z').should == "hero" + end + + it "uses a key's value only a single time" do + "food".sub!(/o/, 'o' => '0').should == "f0od" + end + + it "uses the hash's default value for missing keys" do + hsh = {} + hsh.default='?' + hsh['o'] = '0' + "food".sub!(/./, hsh).should == "?ood" + end + + it "coerces the hash values with #to_s" do + hsh = {} + hsh.default=[] + hsh['o'] = 0 + obj = mock('!') + obj.should_receive(:to_s).and_return('!') + hsh['f'] = obj + "food!".sub!(/./, hsh).should == "!ood!" + end + + it "uses the hash's value set from default_proc for missing keys" do + hsh = {} + hsh.default_proc = lambda { |k,v| 'lamb' } + "food!".sub!(/./, hsh).should == "lambood!" + end + + it "sets $~ to MatchData of first match and nil when there's none for access from outside" do + 'hello.'.sub!('l', 'l' => 'L') + $~.begin(0).should == 2 + $~[0].should == 'l' + + 'hello.'.sub!('not', 'ot' => 'to') + $~.should == nil + + 'hello.'.sub!(/.(.)/, 'o' => ' hole') + $~[0].should == 'he' + + 'hello.'.sub!(/not/, 'z' => 'glark') + $~.should == nil + end + + it "doesn't interpolate special sequences like \\1 for the block's return value" do + repl = '\& \0 \1 \` \\\' \+ \\\\ foo' + "hello".sub!(/(.+)/, 'hello' => repl ).should == repl + end + + it "keeps untrusted state" do + str = "Ghana".untrust + str.sub!(/[Aa]na/, 'ana' => '').untrusted?.should be_true + end + + it "untrusts self if a hash value is untrusted" do + str = "Ghana" + str.sub!(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true + end + + it "keeps tainted state" do + str = "Ghana".taint + str.sub!(/[Aa]na/, 'ana' => '').tainted?.should be_true + end + + it "taints self if a hash value is tainted" do + str = "Ghana" + str.sub!(/a$/, 'a' => 'di'.taint).tainted?.should be_true + end +end + +describe "String#sub with pattern and without replacement and block" do + it "raises a ArgumentError" do + lambda { "abca".sub(/a/) }.should raise_error(ArgumentError) + end +end + +describe "String#sub! with pattern and without replacement and block" do + it "raises a ArgumentError" do + lambda { "abca".sub!(/a/) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/succ_spec.rb b/spec/rubyspec/core/string/succ_spec.rb new file mode 100644 index 0000000000..311453702d --- /dev/null +++ b/spec/rubyspec/core/string/succ_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/succ.rb', __FILE__) + +describe "String#succ" do + it_behaves_like(:string_succ, :succ) +end + +describe "String#succ!" do + it_behaves_like(:string_succ_bang, :"succ!") +end diff --git a/spec/rubyspec/core/string/sum_spec.rb b/spec/rubyspec/core/string/sum_spec.rb new file mode 100644 index 0000000000..2d68668f49 --- /dev/null +++ b/spec/rubyspec/core/string/sum_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#sum" do + it "returns a basic n-bit checksum of the characters in self" do + "ruby".sum.should == 450 + "ruby".sum(8).should == 194 + "rubinius".sum(23).should == 881 + end + + it "tries to convert n to an integer using to_int" do + obj = mock('8') + obj.should_receive(:to_int).and_return(8) + + "hello".sum(obj).should == "hello".sum(8) + end + + it "returns sum of the bytes in self if n less or equal to zero" do + "xyz".sum(0).should == 363 + "xyz".sum(-10).should == 363 + end +end diff --git a/spec/rubyspec/core/string/swapcase_spec.rb b/spec/rubyspec/core/string/swapcase_spec.rb new file mode 100644 index 0000000000..0085887ae2 --- /dev/null +++ b/spec/rubyspec/core/string/swapcase_spec.rb @@ -0,0 +1,52 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#swapcase" do + it "returns a new string with all uppercase chars from self converted to lowercase and vice versa" do + "Hello".swapcase.should == "hELLO" + "cYbEr_PuNk11".swapcase.should == "CyBeR_pUnK11" + "+++---111222???".swapcase.should == "+++---111222???" + end + + it "taints resulting string when self is tainted" do + "".taint.swapcase.tainted?.should == true + "hello".taint.swapcase.tainted?.should == true + end + + ruby_version_is ''...'2.4' do + it "is locale insensitive (only upcases a-z and only downcases A-Z)" do + "ÄÖÜ".swapcase.should == "ÄÖÜ" + "ärger".swapcase.should == "äRGER" + "BÄR".swapcase.should == "bÄr" + end + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").swapcase.should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(StringSpecs::MyString) + end +end + +describe "String#swapcase!" do + it "modifies self in place" do + a = "cYbEr_PuNk11" + a.swapcase!.should equal(a) + a.should == "CyBeR_pUnK11" + end + + it "returns nil if no modifications were made" do + a = "+++---111222???" + a.swapcase!.should == nil + a.should == "+++---111222???" + + "".swapcase!.should == nil + end + + it "raises a RuntimeError when self is frozen" do + ["", "hello"].each do |a| + a.freeze + lambda { a.swapcase! }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/core/string/to_c_spec.rb b/spec/rubyspec/core/string/to_c_spec.rb new file mode 100644 index 0000000000..da353e18d5 --- /dev/null +++ b/spec/rubyspec/core/string/to_c_spec.rb @@ -0,0 +1,99 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#to_c" do + it "returns a Complex object" do + '9'.to_c.should be_an_instance_of(Complex) + end + + it "understands integers" do + '20'.to_c.should == Complex(20) + end + + it "understands negative integers" do + '-3'.to_c.should == Complex(-3) + end + + it "understands fractions (numerator/denominator) for the real part" do + '2/3'.to_c.should == Complex(Rational(2, 3)) + end + + it "understands fractions (numerator/denominator) for the imaginary part" do + '4+2/3i'.to_c.should == Complex(4, Rational(2, 3)) + end + + it "understands negative fractions (-numerator/denominator) for the real part" do + '-2/3'.to_c.should == Complex(Rational(-2, 3)) + end + + it "understands negative fractions (-numerator/denominator) for the imaginary part" do + '7-2/3i'.to_c.should == Complex(7, Rational(-2, 3)) + end + + it "understands floats (a.b) for the real part" do + '2.3'.to_c.should == Complex(2.3) + end + + it "understands floats (a.b) for the imaginary part" do + '4+2.3i'.to_c.should == Complex(4, 2.3) + end + + it "understands negative floats (-a.b) for the real part" do + '-2.33'.to_c.should == Complex(-2.33) + end + + it "understands negative floats (-a.b) for the imaginary part" do + '7-28.771i'.to_c.should == Complex(7, -28.771) + end + + it "understands an integer followed by 'i' to mean that integer is the imaginary part" do + '35i'.to_c.should == Complex(0,35) + end + + it "understands a negative integer followed by 'i' to mean that negative integer is the imaginary part" do + '-29i'.to_c.should == Complex(0,-29) + end + + it "understands an 'i' by itself as denoting a complex number with an imaginary part of 1" do + 'i'.to_c.should == Complex(0,1) + end + + it "understands a '-i' by itself as denoting a complex number with an imaginary part of -1" do + '-i'.to_c.should == Complex(0,-1) + end + + it "understands 'a+bi' to mean a complex number with 'a' as the real part, 'b' as the imaginary" do + '79+4i'.to_c.should == Complex(79,4) + end + + it "understands 'a-bi' to mean a complex number with 'a' as the real part, '-b' as the imaginary" do + '79-4i'.to_c.should == Complex(79,-4) + end + + it "understands scientific notation for the real part" do + '2e3+4i'.to_c.should == Complex(2e3,4) + end + + it "understands negative scientific notation for the real part" do + '-2e3+4i'.to_c.should == Complex(-2e3,4) + end + + it "understands scientific notation for the imaginary part" do + '4+2e3i'.to_c.should == Complex(4, 2e3) + end + + it "understands negative scientific notation for the imaginary part" do + '4-2e3i'.to_c.should == Complex(4, -2e3) + end + + it "understands scientific notation for the real and imaginary part in the same String" do + '2e3+2e4i'.to_c.should == Complex(2e3,2e4) + end + + it "understands negative scientific notation for the real and imaginary part in the same String" do + '-2e3-2e4i'.to_c.should == Complex(-2e3,-2e4) + end + + it "returns a complex number with 0 as the real part, 0 as the imaginary part for unrecognised Strings" do + 'ruby'.to_c.should == Complex(0,0) + end +end diff --git a/spec/rubyspec/core/string/to_f_spec.rb b/spec/rubyspec/core/string/to_f_spec.rb new file mode 100644 index 0000000000..8454651ab2 --- /dev/null +++ b/spec/rubyspec/core/string/to_f_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +# src.scan(/[+-]?[\d_]+\.[\d_]+(e[+-]?[\d_]+)?\b|[+-]?[\d_]+e[+-]?[\d_]+\b/i) + +describe "String#to_f" do + it "treats leading characters of self as a floating point number" do + "123.45e1".to_f.should == 1234.5 + "45.67 degrees".to_f.should == 45.67 + "0".to_f.should == 0.0 + "123.45e1".to_f.should == 1234.5 + + ".5".to_f.should == 0.5 + ".5e1".to_f.should == 5.0 + "5e".to_f.should == 5.0 + "5E".to_f.should == 5.0 + end + + it "treats special float value strings as characters" do + "NaN".to_f.should == 0 + "Infinity".to_f.should == 0 + "-Infinity".to_f.should == 0 + end + + it "allows for varying case" do + "123.45e1".to_f.should == 1234.5 + "123.45E1".to_f.should == 1234.5 + end + + it "allows for varying signs" do + "+123.45e1".to_f.should == +123.45e1 + "-123.45e1".to_f.should == -123.45e1 + "123.45e+1".to_f.should == 123.45e+1 + "123.45e-1".to_f.should == 123.45e-1 + "+123.45e+1".to_f.should == +123.45e+1 + "+123.45e-1".to_f.should == +123.45e-1 + "-123.45e+1".to_f.should == -123.45e+1 + "-123.45e-1".to_f.should == -123.45e-1 + end + + it "allows for underscores, even in the decimal side" do + "1_234_567.890_1".to_f.should == 1_234_567.890_1 + end + + it "returns 0 for strings with any non-digit in them" do + "blah".to_f.should == 0 + "0b5".to_f.should == 0 + "0d5".to_f.should == 0 + "0o5".to_f.should == 0 + "0xx5".to_f.should == 0 + end + + it "returns 0 for strings with leading underscores" do + "_9".to_f.should == 0 + end + + it "takes an optional sign" do + "-45.67 degrees".to_f.should == -45.67 + "+45.67 degrees".to_f.should == 45.67 + "-5_5e-5_0".to_f.should == -55e-50 + "-".to_f.should == 0.0 + (1.0 / "-0".to_f).to_s.should == "-Infinity" + end + + it "returns 0.0 if the conversion fails" do + "bad".to_f.should == 0.0 + "thx1138".to_f.should == 0.0 + end +end diff --git a/spec/rubyspec/core/string/to_i_spec.rb b/spec/rubyspec/core/string/to_i_spec.rb new file mode 100644 index 0000000000..be0f67a46a --- /dev/null +++ b/spec/rubyspec/core/string/to_i_spec.rb @@ -0,0 +1,337 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#to_i" do + it "returns 0 for strings with leading underscores" do + "_123".to_i.should == 0 + end + + it "ignores underscores in between the digits" do + "1_2_3asdf".to_i.should == 123 + end + + it "ignores leading whitespaces" do + [ " 123", " 123", "\r\n\r\n123", "\t\t123", + "\r\n\t\n123", " \t\n\r\t 123"].each do |str| + str.to_i.should == 123 + end + end + + it "ignores subsequent invalid characters" do + "123asdf".to_i.should == 123 + "123#123".to_i.should == 123 + "123 456".to_i.should == 123 + end + + it "returns 0 if self is no valid integer-representation" do + [ "++2", "+-2", "--2" ].each do |str| + str.to_i.should == 0 + end + end + + it "accepts '+' at the beginning of a String" do + "+0d56".to_i.should == 56 + end + + it "interprets leading characters as a number in the given base" do + "100110010010".to_i(2).should == 0b100110010010 + "100110201001".to_i(3).should == 186409 + "103110201001".to_i(4).should == 5064769 + "103110241001".to_i(5).should == 55165126 + "153110241001".to_i(6).should == 697341529 + "153160241001".to_i(7).should == 3521513430 + "153160241701".to_i(8).should == 14390739905 + "853160241701".to_i(9).should == 269716550518 + "853160241791".to_i(10).should == 853160241791 + + "F00D_BE_1337".to_i(16).should == 0xF00D_BE_1337 + "-hello_world".to_i(32).should == -18306744 + "abcXYZ".to_i(36).should == 623741435 + + ("z" * 24).to_i(36).should == 22452257707354557240087211123792674815 + + "5e10".to_i.should == 5 + end + + it "auto-detects base 8 via leading 0 when base = 0" do + "01778".to_i(0).should == 0177 + "-01778".to_i(0).should == -0177 + end + + it "auto-detects base 2 via 0b when base = 0" do + "0b112".to_i(0).should == 0b11 + "-0b112".to_i(0).should == -0b11 + end + + it "auto-detects base 10 via 0d when base = 0" do + "0d19A".to_i(0).should == 19 + "-0d19A".to_i(0).should == -19 + end + + it "auto-detects base 8 via 0o when base = 0" do + "0o178".to_i(0).should == 0o17 + "-0o178".to_i(0).should == -0o17 + end + + it "auto-detects base 16 via 0x when base = 0" do + "0xFAZ".to_i(0).should == 0xFA + "-0xFAZ".to_i(0).should == -0xFA + end + + it "auto-detects base 10 with no base specifier when base = 0" do + "1234567890ABC".to_i(0).should == 1234567890 + "-1234567890ABC".to_i(0).should == -1234567890 + end + + it "doesn't handle foreign base specifiers when base is > 0" do + [2, 3, 4, 8, 10].each do |base| + "0111".to_i(base).should == "111".to_i(base) + + "0b11".to_i(base).should == (base == 2 ? 0b11 : 0) + "0d11".to_i(base).should == (base == 10 ? 0d11 : 0) + "0o11".to_i(base).should == (base == 8 ? 0o11 : 0) + "0xFA".to_i(base).should == 0 + end + + "0xD00D".to_i(16).should == 0xD00D + + "0b11".to_i(16).should == 0xb11 + "0d11".to_i(16).should == 0xd11 + "0o11".to_i(25).should == 15026 + "0x11".to_i(34).should == 38183 + + "0B11".to_i(16).should == 0xb11 + "0D11".to_i(16).should == 0xd11 + "0O11".to_i(25).should == 15026 + "0X11".to_i(34).should == 38183 + end + + it "tries to convert the base to an integer using to_int" do + obj = mock('8') + obj.should_receive(:to_int).and_return(8) + + "777".to_i(obj).should == 0777 + end + + it "requires that the sign if any appears before the base specifier" do + "0b-1".to_i( 2).should == 0 + "0d-1".to_i(10).should == 0 + "0o-1".to_i( 8).should == 0 + "0x-1".to_i(16).should == 0 + + "0b-1".to_i(2).should == 0 + "0o-1".to_i(8).should == 0 + "0d-1".to_i(10).should == 0 + "0x-1".to_i(16).should == 0 + end + + it "raises an ArgumentError for illegal bases (1, < 0 or > 36)" do + lambda { "".to_i(1) }.should raise_error(ArgumentError) + lambda { "".to_i(-1) }.should raise_error(ArgumentError) + lambda { "".to_i(37) }.should raise_error(ArgumentError) + end + + it "returns a Fixnum for long strings with trailing spaces" do + "0 ".to_i.should == 0 + "0 ".to_i.should be_an_instance_of(Fixnum) + + "10 ".to_i.should == 10 + "10 ".to_i.should be_an_instance_of(Fixnum) + + "-10 ".to_i.should == -10 + "-10 ".to_i.should be_an_instance_of(Fixnum) + end + + it "returns a Fixnum for long strings with leading spaces" do + " 0".to_i.should == 0 + " 0".to_i.should be_an_instance_of(Fixnum) + + " 10".to_i.should == 10 + " 10".to_i.should be_an_instance_of(Fixnum) + + " -10".to_i.should == -10 + " -10".to_i.should be_an_instance_of(Fixnum) + end + + it "returns the correct Bignum for long strings" do + "245789127594125924165923648312749312749327482".to_i.should == 245789127594125924165923648312749312749327482 + "-245789127594125924165923648312749312749327482".to_i.should == -245789127594125924165923648312749312749327482 + end +end + +describe "String#to_i with bases" do + it "parses a String in base 2" do + str = "10" * 50 + str.to_i(2).to_s(2).should == str + end + + it "parses a String in base 3" do + str = "120" * 33 + str.to_i(3).to_s(3).should == str + end + + it "parses a String in base 4" do + str = "1230" * 25 + str.to_i(4).to_s(4).should == str + end + + it "parses a String in base 5" do + str = "12340" * 20 + str.to_i(5).to_s(5).should == str + end + + it "parses a String in base 6" do + str = "123450" * 16 + str.to_i(6).to_s(6).should == str + end + + it "parses a String in base 7" do + str = "1234560" * 14 + str.to_i(7).to_s(7).should == str + end + + it "parses a String in base 8" do + str = "12345670" * 12 + str.to_i(8).to_s(8).should == str + end + + it "parses a String in base 9" do + str = "123456780" * 11 + str.to_i(9).to_s(9).should == str + end + + it "parses a String in base 10" do + str = "1234567890" * 10 + str.to_i(10).to_s(10).should == str + end + + it "parses a String in base 11" do + str = "1234567890a" * 9 + str.to_i(11).to_s(11).should == str + end + + it "parses a String in base 12" do + str = "1234567890ab" * 8 + str.to_i(12).to_s(12).should == str + end + + it "parses a String in base 13" do + str = "1234567890abc" * 7 + str.to_i(13).to_s(13).should == str + end + + it "parses a String in base 14" do + str = "1234567890abcd" * 7 + str.to_i(14).to_s(14).should == str + end + + it "parses a String in base 15" do + str = "1234567890abcde" * 6 + str.to_i(15).to_s(15).should == str + end + + it "parses a String in base 16" do + str = "1234567890abcdef" * 6 + str.to_i(16).to_s(16).should == str + end + + it "parses a String in base 17" do + str = "1234567890abcdefg" * 5 + str.to_i(17).to_s(17).should == str + end + + it "parses a String in base 18" do + str = "1234567890abcdefgh" * 5 + str.to_i(18).to_s(18).should == str + end + + it "parses a String in base 19" do + str = "1234567890abcdefghi" * 5 + str.to_i(19).to_s(19).should == str + end + + it "parses a String in base 20" do + str = "1234567890abcdefghij" * 5 + str.to_i(20).to_s(20).should == str + end + + it "parses a String in base 21" do + str = "1234567890abcdefghijk" * 4 + str.to_i(21).to_s(21).should == str + end + + it "parses a String in base 22" do + str = "1234567890abcdefghijkl" * 4 + str.to_i(22).to_s(22).should == str + end + + it "parses a String in base 23" do + str = "1234567890abcdefghijklm" * 4 + str.to_i(23).to_s(23).should == str + end + + it "parses a String in base 24" do + str = "1234567890abcdefghijklmn" * 4 + str.to_i(24).to_s(24).should == str + end + + it "parses a String in base 25" do + str = "1234567890abcdefghijklmno" * 4 + str.to_i(25).to_s(25).should == str + end + + it "parses a String in base 26" do + str = "1234567890abcdefghijklmnop" * 3 + str.to_i(26).to_s(26).should == str + end + + it "parses a String in base 27" do + str = "1234567890abcdefghijklmnopq" * 3 + str.to_i(27).to_s(27).should == str + end + + it "parses a String in base 28" do + str = "1234567890abcdefghijklmnopqr" * 3 + str.to_i(28).to_s(28).should == str + end + + it "parses a String in base 29" do + str = "1234567890abcdefghijklmnopqrs" * 3 + str.to_i(29).to_s(29).should == str + end + + it "parses a String in base 30" do + str = "1234567890abcdefghijklmnopqrst" * 3 + str.to_i(30).to_s(30).should == str + end + + it "parses a String in base 31" do + str = "1234567890abcdefghijklmnopqrstu" * 3 + str.to_i(31).to_s(31).should == str + end + + it "parses a String in base 32" do + str = "1234567890abcdefghijklmnopqrstuv" * 3 + str.to_i(32).to_s(32).should == str + end + + it "parses a String in base 33" do + str = "1234567890abcdefghijklmnopqrstuvw" * 3 + str.to_i(33).to_s(33).should == str + end + + it "parses a String in base 34" do + str = "1234567890abcdefghijklmnopqrstuvwx" * 2 + str.to_i(34).to_s(34).should == str + end + + it "parses a String in base 35" do + str = "1234567890abcdefghijklmnopqrstuvwxy" * 2 + str.to_i(35).to_s(35).should == str + end + + it "parses a String in base 36" do + str = "1234567890abcdefghijklmnopqrstuvwxyz" * 2 + str.to_i(36).to_s(36).should == str + end +end diff --git a/spec/rubyspec/core/string/to_r_spec.rb b/spec/rubyspec/core/string/to_r_spec.rb new file mode 100644 index 0000000000..7fa16f6f49 --- /dev/null +++ b/spec/rubyspec/core/string/to_r_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#to_r" do + it "returns a Rational object" do + String.new.to_r.should be_an_instance_of(Rational) + end + + it "returns (0/1) for the empty String" do + "".to_r.should == Rational(0, 1) + end + + it "returns (n/1) for a String starting with a decimal _n_" do + "2".to_r.should == Rational(2, 1) + "1765".to_r.should == Rational(1765, 1) + end + + it "ignores trailing characters" do + "2 foo".to_r.should == Rational(2, 1) + "1765, ".to_r.should == Rational(1765, 1) + end + + it "ignores leading spaces" do + " 2".to_r.should == Rational(2, 1) + " 1765, ".to_r.should == Rational(1765, 1) + end + + it "does not ignore arbitrary, non-numeric leading characters" do + "The rational form of 33 is...".to_r.should_not == Rational(33, 1) + "a1765, ".to_r.should_not == Rational(1765, 1) + end + + it "treats leading hypens as minus signs" do + "-20".to_r.should == Rational(-20, 1) + end + + it "does not treat a leading period without a numeric prefix as a decimal point" do + ".9".to_r.should_not == Rational(8106479329266893, 9007199254740992) + end + + it "understands decimal points" do + "3.33".to_r.should == Rational(333, 100) + "-3.33".to_r.should == Rational(-333, 100) + end + + it "ignores underscores between numbers" do + "190_22".to_r.should == Rational(19022, 1) + "-190_22.7".to_r.should == Rational(-190227, 10) + end + + it "understands a forward slash as separating the numerator from the denominator" do + "20/3".to_r.should == Rational(20, 3) + " -19.10/3".to_r.should == Rational(-191, 30) + end + + it "returns (0/1) for Strings it can't parse" do + "glark".to_r.should == Rational(0,1) + end +end diff --git a/spec/rubyspec/core/string/to_s_spec.rb b/spec/rubyspec/core/string/to_s_spec.rb new file mode 100644 index 0000000000..b483b1b138 --- /dev/null +++ b/spec/rubyspec/core/string/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/to_s.rb', __FILE__) + +describe "String#to_s" do + it_behaves_like(:string_to_s, :to_s) +end diff --git a/spec/rubyspec/core/string/to_str_spec.rb b/spec/rubyspec/core/string/to_str_spec.rb new file mode 100644 index 0000000000..fb1260a687 --- /dev/null +++ b/spec/rubyspec/core/string/to_str_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/to_s.rb', __FILE__) + +describe "String#to_str" do + it_behaves_like(:string_to_s, :to_str) +end diff --git a/spec/rubyspec/core/string/to_sym_spec.rb b/spec/rubyspec/core/string/to_sym_spec.rb new file mode 100644 index 0000000000..7659f266cd --- /dev/null +++ b/spec/rubyspec/core/string/to_sym_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) +require File.expand_path('../shared/to_sym.rb', __FILE__) + +describe "String#to_sym" do + it_behaves_like(:string_to_sym, :to_sym) +end diff --git a/spec/rubyspec/core/string/tr_s_spec.rb b/spec/rubyspec/core/string/tr_s_spec.rb new file mode 100644 index 0000000000..ea2ffa71b9 --- /dev/null +++ b/spec/rubyspec/core/string/tr_s_spec.rb @@ -0,0 +1,136 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#tr_s" do + it "returns a string processed according to tr with newly duplicate characters removed" do + "hello".tr_s('l', 'r').should == "hero" + "hello".tr_s('el', '*').should == "h*o" + "hello".tr_s('el', 'hx').should == "hhxo" + "hello".tr_s('o', '.').should == "hell." + end + + it "accepts c1-c2 notation to denote ranges of characters" do + "hello".tr_s('a-y', 'b-z').should == "ifmp" + "123456789".tr_s("2-5", "abcdefg").should == "1abcd6789" + "hello ^--^".tr_s("e-", "__").should == "h_llo ^_^" + "hello ^--^".tr_s("---", "_").should == "hello ^_^" + end + + it "pads to_str with its last char if it is shorter than from_string" do + "this".tr_s("this", "x").should == "x" + end + + it "translates chars not in from_string when it starts with a ^" do + "hello".tr_s('^aeiou', '*').should == "*e*o" + "123456789".tr_s("^345", "abc").should == "c345c" + "abcdefghijk".tr_s("^d-g", "9131").should == "1defg1" + + "hello ^_^".tr_s("a-e^e", ".").should == "h.llo ._." + "hello ^_^".tr_s("^^", ".").should == ".^.^" + "hello ^_^".tr_s("^", "x").should == "hello x_x" + "hello ^-^".tr_s("^-^", "x").should == "x^-^" + "hello ^-^".tr_s("^^-^", "x").should == "x^x^" + "hello ^-^".tr_s("^---", "x").should == "x-x" + "hello ^-^".tr_s("^---l-o", "x").should == "xllox-x" + end + + it "tries to convert from_str and to_str to strings using to_str" do + from_str = mock('ab') + from_str.should_receive(:to_str).and_return("ab") + + to_str = mock('AB') + to_str.should_receive(:to_str).and_return("AB") + + "bla".tr_s(from_str, to_str).should == "BlA" + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(StringSpecs::MyString) + end + + it "taints the result when self is tainted" do + ["h", "hello"].each do |str| + tainted_str = str.dup.taint + + tainted_str.tr_s("e", "a").tainted?.should == true + + str.tr_s("e".taint, "a").tainted?.should == false + str.tr_s("e", "a".taint).tainted?.should == false + end + end + + with_feature :encoding do + # http://redmine.ruby-lang.org/issues/show/1839 + it "can replace a 7-bit ASCII character with a multibyte one" do + a = "uber" + a.encoding.should == Encoding::UTF_8 + b = a.tr_s("u","ü") + b.should == "über" + b.encoding.should == Encoding::UTF_8 + end + + it "can replace multiple 7-bit ASCII characters with a multibyte one" do + a = "uuuber" + a.encoding.should == Encoding::UTF_8 + b = a.tr_s("u","ü") + b.should == "über" + b.encoding.should == Encoding::UTF_8 + end + + it "can replace a multibyte character with a single byte one" do + a = "über" + a.encoding.should == Encoding::UTF_8 + b = a.tr_s("ü","u") + b.should == "uber" + b.encoding.should == Encoding::UTF_8 + end + + it "can replace multiple multibyte characters with a single byte one" do + a = "üüüber" + a.encoding.should == Encoding::UTF_8 + b = a.tr_s("ü","u") + b.should == "uber" + b.encoding.should == Encoding::UTF_8 + end + + it "does not replace a multibyte character where part of the bytes match the tr string" do + str = "椎名深夏" + a = "\u0080\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008E\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009E\u009F" + b = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ" + str.tr_s(a, b).should == "椎名深夏" + end + + end + +end + +describe "String#tr_s!" do + it "modifies self in place" do + s = "hello" + s.tr_s!("l", "r").should == "hero" + s.should == "hero" + end + + it "returns nil if no modification was made" do + s = "hello" + s.tr_s!("za", "yb").should == nil + s.tr_s!("", "").should == nil + s.should == "hello" + end + + it "does not modify self if from_str is empty" do + s = "hello" + s.tr_s!("", "").should == nil + s.should == "hello" + s.tr_s!("", "yb").should == nil + s.should == "hello" + end + + it "raises a RuntimeError if self is frozen" do + s = "hello".freeze + lambda { s.tr_s!("el", "ar") }.should raise_error(RuntimeError) + lambda { s.tr_s!("l", "r") }.should raise_error(RuntimeError) + lambda { s.tr_s!("", "") }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/tr_spec.rb b/spec/rubyspec/core/string/tr_spec.rb new file mode 100644 index 0000000000..16d2d318e1 --- /dev/null +++ b/spec/rubyspec/core/string/tr_spec.rb @@ -0,0 +1,131 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#tr" do + it "returns a new string with the characters from from_string replaced by the ones in to_string" do + "hello".tr('aeiou', '*').should == "h*ll*" + "hello".tr('el', 'ip').should == "hippo" + "Lisp".tr("Lisp", "Ruby").should == "Ruby" + end + + it "accepts c1-c2 notation to denote ranges of characters" do + "hello".tr('a-y', 'b-z').should == "ifmmp" + "123456789".tr("2-5","abcdefg").should == "1abcd6789" + "hello ^-^".tr("e-", "__").should == "h_llo ^_^" + "hello ^-^".tr("---", "_").should == "hello ^_^" + end + + it "pads to_str with its last char if it is shorter than from_string" do + "this".tr("this", "x").should == "xxxx" + "hello".tr("a-z", "A-H.").should == "HE..." + end + + it "raises an ArgumentError a descending range in the replacement as containing just the start character" do + lambda { "hello".tr("a-y", "z-b") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError a descending range in the source as empty" do + lambda { "hello".tr("l-a", "z") }.should raise_error(ArgumentError) + end + + it "translates chars not in from_string when it starts with a ^" do + "hello".tr('^aeiou', '*').should == "*e**o" + "123456789".tr("^345", "abc").should == "cc345cccc" + "abcdefghijk".tr("^d-g", "9131").should == "111defg1111" + + "hello ^_^".tr("a-e^e", ".").should == "h.llo ._." + "hello ^_^".tr("^^", ".").should == "......^.^" + "hello ^_^".tr("^", "x").should == "hello x_x" + "hello ^-^".tr("^-^", "x").should == "xxxxxx^-^" + "hello ^-^".tr("^^-^", "x").should == "xxxxxx^x^" + "hello ^-^".tr("^---", "x").should == "xxxxxxx-x" + "hello ^-^".tr("^---l-o", "x").should == "xxlloxx-x" + end + + it "supports non-injective replacements" do + "hello".tr("helo", "1212").should == "12112" + end + + it "tries to convert from_str and to_str to strings using to_str" do + from_str = mock('ab') + from_str.should_receive(:to_str).and_return("ab") + + to_str = mock('AB') + to_str.should_receive(:to_str).and_return("AB") + + "bla".tr(from_str, to_str).should == "BlA" + end + + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(StringSpecs::MyString) + end + + it "taints the result when self is tainted" do + ["h", "hello"].each do |str| + tainted_str = str.dup.taint + + tainted_str.tr("e", "a").tainted?.should == true + + str.tr("e".taint, "a").tainted?.should == false + str.tr("e", "a".taint).tainted?.should == false + end + end + + with_feature :encoding do + # http://redmine.ruby-lang.org/issues/show/1839 + it "can replace a 7-bit ASCII character with a multibyte one" do + a = "uber" + a.encoding.should == Encoding::UTF_8 + b = a.tr("u","ü") + b.should == "über" + b.encoding.should == Encoding::UTF_8 + end + + it "can replace a multibyte character with a single byte one" do + a = "über" + a.encoding.should == Encoding::UTF_8 + b = a.tr("ü","u") + b.should == "uber" + b.encoding.should == Encoding::UTF_8 + end + + it "does not replace a multibyte character where part of the bytes match the tr string" do + str = "椎名深夏" + a = "\u0080\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008E\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009E\u009F" + b = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ" + str.tr(a, b).should == "椎名深夏" + end + + end +end + +describe "String#tr!" do + it "modifies self in place" do + s = "abcdefghijklmnopqR" + s.tr!("cdefg", "12").should == "ab12222hijklmnopqR" + s.should == "ab12222hijklmnopqR" + end + + it "returns nil if no modification was made" do + s = "hello" + s.tr!("za", "yb").should == nil + s.tr!("", "").should == nil + s.should == "hello" + end + + it "does not modify self if from_str is empty" do + s = "hello" + s.tr!("", "").should == nil + s.should == "hello" + s.tr!("", "yb").should == nil + s.should == "hello" + end + + it "raises a RuntimeError if self is frozen" do + s = "abcdefghijklmnopqR".freeze + lambda { s.tr!("cdefg", "12") }.should raise_error(RuntimeError) + lambda { s.tr!("R", "S") }.should raise_error(RuntimeError) + lambda { s.tr!("", "") }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/try_convert_spec.rb b/spec/rubyspec/core/string/try_convert_spec.rb new file mode 100644 index 0000000000..ce12839c59 --- /dev/null +++ b/spec/rubyspec/core/string/try_convert_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "String.try_convert" do + it "returns the argument if it's a String" do + x = String.new + String.try_convert(x).should equal(x) + end + + it "returns the argument if it's a kind of String" do + x = StringSpecs::MyString.new + String.try_convert(x).should equal(x) + end + + it "returns nil when the argument does not respond to #to_str" do + String.try_convert(Object.new).should be_nil + end + + it "sends #to_str to the argument and returns the result if it's nil" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(nil) + String.try_convert(obj).should be_nil + end + + it "sends #to_str to the argument and returns the result if it's a String" do + x = String.new + obj = mock("to_str") + obj.should_receive(:to_str).and_return(x) + String.try_convert(obj).should equal(x) + end + + it "sends #to_str to the argument and returns the result if it's a kind of String" do + x = StringSpecs::MyString.new + obj = mock("to_str") + obj.should_receive(:to_str).and_return(x) + String.try_convert(obj).should equal(x) + end + + it "sends #to_str to the argument and raises TypeError if it's not a kind of String" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(Object.new) + lambda { String.try_convert obj }.should raise_error(TypeError) + end + + it "does not rescue exceptions raised by #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_raise(RuntimeError) + lambda { String.try_convert obj }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/uminus_spec.rb b/spec/rubyspec/core/string/uminus_spec.rb new file mode 100644 index 0000000000..53e73b7e67 --- /dev/null +++ b/spec/rubyspec/core/string/uminus_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.3" do + describe 'String#-@' do + it 'returns self if the String is frozen' do + input = 'foo'.freeze + output = -input + + output.equal?(input).should == true + output.frozen?.should == true + end + + it 'returns a frozen copy if the String is not frozen' do + input = 'foo' + output = -input + + output.frozen?.should == true + output.should == 'foo' + end + end +end diff --git a/spec/rubyspec/core/string/unicode_normalize_spec.rb b/spec/rubyspec/core/string/unicode_normalize_spec.rb new file mode 100644 index 0000000000..ab7c0e3618 --- /dev/null +++ b/spec/rubyspec/core/string/unicode_normalize_spec.rb @@ -0,0 +1,116 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +# Examples taken from http://www.unicode.org/reports/tr15/#Norm_Forms + +describe "String#unicode_normalize" do + before :each do + @accented_f = "\u1e9b\u0323" + @angstrom = "\u212b" + @ohm = "\u2126" + end + + it "normalizes code points in the string according to the form that is specified" do + @accented_f.unicode_normalize(:nfc).should == "\u1e9b\u0323" + @accented_f.unicode_normalize(:nfd).should == "\u017f\u0323\u0307" + @accented_f.unicode_normalize(:nfkc).should == "\u1e69" + @accented_f.unicode_normalize(:nfkd).should == "\u0073\u0323\u0307" + end + + it "defaults to the nfc normalization form if no forms are specified" do + @accented_f.unicode_normalize.should == "\u1e9b\u0323" + @angstrom.unicode_normalize.should == "\u00c5" + @ohm.unicode_normalize.should == "\u03a9" + end + + # http://unicode.org/faq/normalization.html#6 + context "returns normalized form of string by default" do + it "03D3 (ϓ) GREEK UPSILON WITH ACUTE AND HOOK SYMBOL" do + "\u03D3".unicode_normalize(:nfc).should == "\u03D3" + "\u03D3".unicode_normalize(:nfd).should == "\u03D2\u0301" + "\u03D3".unicode_normalize(:nfkc).should == "\u038E" + "\u03D3".unicode_normalize(:nfkd).should == "\u03A5\u0301" + end + + it "03D4 (ϔ) GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL" do + "\u03D4".unicode_normalize(:nfc).should == "\u03D4" + "\u03D4".unicode_normalize(:nfd).should == "\u03D2\u0308" + "\u03D4".unicode_normalize(:nfkc).should == "\u03AB" + "\u03D4".unicode_normalize(:nfkd).should == "\u03A5\u0308" + end + + it "1E9B (ẛ) LATIN SMALL LETTER LONG S WITH DOT ABOVE" do + "\u1E9B".unicode_normalize(:nfc).should == "\u1E9B" + "\u1E9B".unicode_normalize(:nfd).should == "\u017F\u0307" + "\u1E9B".unicode_normalize(:nfkc).should == "\u1E61" + "\u1E9B".unicode_normalize(:nfkd).should == "\u0073\u0307" + end + end + + it "raises an Encoding::CompatibilityError if string is not in an unicode encoding" do + lambda do + [0xE0].pack('C').force_encoding("ISO-8859-1").unicode_normalize(:nfd) + end.should raise_error(Encoding::CompatibilityError) + end + + it "raises an ArgumentError if the specified form is invalid" do + lambda { + @angstrom.unicode_normalize(:invalid_form) + }.should raise_error(ArgumentError) + end +end + +describe "String#unicode_normalize!" do + it "normalizes code points and modifies the receiving string" do + angstrom = "\u212b" + angstrom.unicode_normalize! + angstrom.should == "\u00c5" + angstrom.should_not == "\u212b" + end + + it "modifies original string (nfc)" do + str = "a\u0300" + str.unicode_normalize!(:nfc) + + str.should_not == "a\u0300" + str.should == "à" + end + + it "modifies self in place (nfd)" do + str = "\u00E0" + str.unicode_normalize!(:nfd) + + str.should_not == "a\u00E0" + str.should == "à" + end + + it "modifies self in place (nfkc)" do + str = "a\u0300" + str.unicode_normalize!(:nfkc) + + str.should_not == "a\u0300" + str.should == "à" + end + + it "modifies self in place (nfkd)" do + str = "\u00E0" + str.unicode_normalize!(:nfkd) + + str.should_not == "a\u00E0" + str.should == "à" + end + + it "raises an Encoding::CompatibilityError if the string is not in an unicode encoding" do + ohm = "\u2126" + lambda { + ohm.force_encoding("ISO-8859-1").unicode_normalize! + }.should raise_error(Encoding::CompatibilityError) + end + + it "raises an ArgumentError if the specified form is invalid" do + ohm = "\u2126" + lambda { + ohm.unicode_normalize!(:invalid_form) + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/unicode_normalized_spec.rb b/spec/rubyspec/core/string/unicode_normalized_spec.rb new file mode 100644 index 0000000000..dc5e2742e4 --- /dev/null +++ b/spec/rubyspec/core/string/unicode_normalized_spec.rb @@ -0,0 +1,74 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "String#unicode_normalized?" do + before :each do + @nfc_normalized_str = "\u1e9b\u0323" + @nfd_normalized_str = "\u017f\u0323\u0307" + @nfkc_normalized_str = "\u1e69" + @nfkd_normalized_str = "\u0073\u0323\u0307" + end + + it "returns true if string is in the specified normalization form" do + @nfc_normalized_str.unicode_normalized?(:nfc).should == true + @nfd_normalized_str.unicode_normalized?(:nfd).should == true + @nfkc_normalized_str.unicode_normalized?(:nfkc).should == true + @nfkd_normalized_str.unicode_normalized?(:nfkd).should == true + end + + it "returns false if string is not in the supplied normalization form" do + @nfd_normalized_str.unicode_normalized?(:nfc).should == false + @nfc_normalized_str.unicode_normalized?(:nfd).should == false + @nfc_normalized_str.unicode_normalized?(:nfkc).should == false + @nfc_normalized_str.unicode_normalized?(:nfkd).should == false + end + + it "defaults to the nfc normalization form if no forms are specified" do + @nfc_normalized_str.unicode_normalized?.should == true + @nfd_normalized_str.unicode_normalized?.should == false + end + + it "returns true if string is empty" do + "".unicode_normalized?.should == true + end + + it "returns true if string does not contain any unicode codepoints" do + "abc".unicode_normalized?.should == true + end + + it "raises an Encoding::CompatibilityError if the string is not in an unicode encoding" do + lambda { @nfc_normalized_str.force_encoding("ISO-8859-1").unicode_normalized? }.should raise_error(Encoding::CompatibilityError) + end + + it "raises an ArgumentError if the specified form is invalid" do + lambda { @nfc_normalized_str.unicode_normalized?(:invalid_form) }.should raise_error(ArgumentError) + end + + it "returns true if str is in Unicode normalization form (nfc)" do + str = "a\u0300" + str.unicode_normalized?(:nfc).should be_false + str.unicode_normalize!(:nfc) + str.unicode_normalized?(:nfc).should be_true + end + + it "returns true if str is in Unicode normalization form (nfd)" do + str = "a\u00E0" + str.unicode_normalized?(:nfd).should be_false + str.unicode_normalize!(:nfd) + str.unicode_normalized?(:nfd).should be_true + end + + it "returns true if str is in Unicode normalization form (nfkc)" do + str = "a\u0300" + str.unicode_normalized?(:nfkc).should be_false + str.unicode_normalize!(:nfkc) + str.unicode_normalized?(:nfkc).should be_true + end + + it "returns true if str is in Unicode normalization form (nfkd)" do + str = "a\u00E0" + str.unicode_normalized?(:nfkd).should be_false + str.unicode_normalize!(:nfkd) + str.unicode_normalized?(:nfkd).should be_true + end +end diff --git a/spec/rubyspec/core/string/unpack/a_spec.rb b/spec/rubyspec/core/string/unpack/a_spec.rb new file mode 100644 index 0000000000..18882c91a6 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/a_spec.rb @@ -0,0 +1,63 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/string', __FILE__) + +describe "String#unpack with format 'A'" do + it_behaves_like :string_unpack_basic, 'A' + it_behaves_like :string_unpack_no_platform, 'A' + it_behaves_like :string_unpack_string, 'A' + it_behaves_like :string_unpack_Aa, 'A' + + it "removes trailing space and NULL bytes from the decoded string" do + [ ["a\x00 b \x00", ["a\x00 b", ""]], + ["a\x00 b \x00 ", ["a\x00 b", ""]], + ["a\x00 b\x00 ", ["a\x00 b", ""]], + ["a\x00 b\x00", ["a\x00 b", ""]], + ["a\x00 b ", ["a\x00 b", ""]] + ].should be_computed_by(:unpack, "A*A") + end + + it "does not remove whitespace other than space" do + [ ["a\x00 b\x00\f", ["a\x00 b\x00\f"]], + ["a\x00 b\x00\n", ["a\x00 b\x00\n"]], + ["a\x00 b\x00\r", ["a\x00 b\x00\r"]], + ["a\x00 b\x00\t", ["a\x00 b\x00\t"]], + ["a\x00 b\x00\v", ["a\x00 b\x00\v"]], + ].should be_computed_by(:unpack, "A*") + end + + it "decodes into raw (ascii) string values" do + str = "str".force_encoding('UTF-8').unpack("A*")[0] + str.encoding.name.should == 'ASCII-8BIT' + end + +end + +describe "String#unpack with format 'a'" do + it_behaves_like :string_unpack_basic, 'a' + it_behaves_like :string_unpack_no_platform, 'a' + it_behaves_like :string_unpack_string, 'a' + it_behaves_like :string_unpack_Aa, 'a' + + it "does not remove trailing whitespace or NULL bytes from the decoded string" do + [ ["a\x00 b \x00", ["a\x00 b \x00"]], + ["a\x00 b \x00 ", ["a\x00 b \x00 "]], + ["a\x00 b\x00 ", ["a\x00 b\x00 "]], + ["a\x00 b\x00", ["a\x00 b\x00"]], + ["a\x00 b ", ["a\x00 b "]], + ["a\x00 b\f", ["a\x00 b\f"]], + ["a\x00 b\n", ["a\x00 b\n"]], + ["a\x00 b\r", ["a\x00 b\r"]], + ["a\x00 b\t", ["a\x00 b\t"]], + ["a\x00 b\v", ["a\x00 b\v"]] + ].should be_computed_by(:unpack, "a*") + end + + it "decodes into raw (ascii) string values" do + str = "".unpack("a*")[0] + str.encoding.name.should == 'ASCII-8BIT' + end + +end diff --git a/spec/rubyspec/core/string/unpack/at_spec.rb b/spec/rubyspec/core/string/unpack/at_spec.rb new file mode 100644 index 0000000000..70cbebd2ba --- /dev/null +++ b/spec/rubyspec/core/string/unpack/at_spec.rb @@ -0,0 +1,29 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format '@'" do + it_behaves_like :string_unpack_basic, '@' + it_behaves_like :string_unpack_no_platform, '@' + + it "moves the read index to the byte specified by the count" do + "\x01\x02\x03\x04".unpack("C3@2C").should == [1, 2, 3, 3] + end + + it "implicitly has a count of zero when count is not specified" do + "\x01\x02\x03\x04".unpack("C2@C").should == [1, 2, 1] + end + + it "has no effect when passed the '*' modifier" do + "\x01\x02\x03\x04".unpack("C2@*C").should == [1, 2, 3] + end + + it "positions the read index one beyond the last readable byte in the String" do + "\x01\x02\x03\x04".unpack("C2@4C").should == [1, 2, nil] + end + + it "raises an ArgumentError if the count exceeds the size of the String" do + lambda { "\x01\x02\x03\x04".unpack("C2@5C") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/unpack/b_spec.rb b/spec/rubyspec/core/string/unpack/b_spec.rb new file mode 100644 index 0000000000..fa632e6526 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/b_spec.rb @@ -0,0 +1,190 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'B'" do + it_behaves_like :string_unpack_basic, 'B' + it_behaves_like :string_unpack_no_platform, 'B' + + it "decodes one bit from each byte for each format character starting with the most significant bit" do + [ ["\x00", "B", ["0"]], + ["\x80", "B", ["1"]], + ["\x0f", "B", ["0"]], + ["\x8f", "B", ["1"]], + ["\x7f", "B", ["0"]], + ["\xff", "B", ["1"]], + ["\x80\x00", "BB", ["1", "0"]], + ["\x8f\x00", "BB", ["1", "0"]], + ["\x80\x0f", "BB", ["1", "0"]], + ["\x80\x8f", "BB", ["1", "1"]], + ["\x80\x80", "BB", ["1", "1"]], + ["\x0f\x80", "BB", ["0", "1"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of bits in the string when passed a count" do + "\x83".unpack("B25").should == ["10000011"] + end + + it "decodes multiple differing bit counts from a single string" do + str = "\xaa\xaa\xaa\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7\xc3\xd4\xaa\x6b\xd7\xaa" + array = str.unpack("B5B6B7B8B9B10B13B14B16B17") + array.should == ["10101", "101010", "1010101", "10101010", "010101011", + "1101010011", "0110101111010", "10101010110101", + "1100001111010100", "10101010011010111"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xd4\xc3\x6b\xd7".unpack("B5B*").should == ["11010", "110000110110101111010111"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xd4\xc3\x6b\xd7".unpack("B*B5").should == ["11010100110000110110101111010111", ""] + end + + it "decodes the number of bits specified by the count modifier" do + [ ["\x00", "B0", [""]], + ["\x80", "B1", ["1"]], + ["\x7f", "B2", ["01"]], + ["\x8f", "B3", ["100"]], + ["\x7f", "B4", ["0111"]], + ["\xff", "B5", ["11111"]], + ["\xf8", "B6", ["111110"]], + ["\x9c", "B7", ["1001110"]], + ["\xbd", "B8", ["10111101"]], + ["\x80\x80", "B9", ["100000001"]], + ["\x80\x70", "B10", ["1000000001"]], + ["\x80\x20", "B11", ["10000000001"]], + ["\x8f\x10", "B12", ["100011110001"]], + ["\x8f\x0f", "B13", ["1000111100001"]], + ["\x80\x0f", "B14", ["10000000000011"]], + ["\x80\x8f", "B15", ["100000001000111"]], + ["\x0f\x81", "B16", ["0000111110000001"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the bits when passed the '*' modifier" do + [ ["", [""]], + ["\x00", ["00000000"]], + ["\x80", ["10000000"]], + ["\x7f", ["01111111"]], + ["\x81", ["10000001"]], + ["\x0f", ["00001111"]], + ["\x80\x80", ["1000000010000000"]], + ["\x8f\x10", ["1000111100010000"]], + ["\x00\x10", ["0000000000010000"]] + ].should be_computed_by(:unpack, "B*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x80", ["1", "", ""]], + ["\x80\x08", ["1", "0", ""]] + ].should be_computed_by(:unpack, "BBB") + end + + it "ignores NULL bytes between directives" do + "\x80\x00".unpack("B\x00B").should == ["1", "0"] + end + + it "ignores spaces between directives" do + "\x80\x00".unpack("B B").should == ["1", "0"] + end +end + +describe "String#unpack with format 'b'" do + it_behaves_like :string_unpack_basic, 'b' + it_behaves_like :string_unpack_no_platform, 'b' + + it "decodes one bit from each byte for each format character starting with the least significant bit" do + [ ["\x00", "b", ["0"]], + ["\x01", "b", ["1"]], + ["\xf0", "b", ["0"]], + ["\xf1", "b", ["1"]], + ["\xfe", "b", ["0"]], + ["\xff", "b", ["1"]], + ["\x01\x00", "bb", ["1", "0"]], + ["\xf1\x00", "bb", ["1", "0"]], + ["\x01\xf0", "bb", ["1", "0"]], + ["\x01\xf1", "bb", ["1", "1"]], + ["\x01\x01", "bb", ["1", "1"]], + ["\xf0\x01", "bb", ["0", "1"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of bits in the string when passed a count" do + "\x83".unpack("b25").should == ["11000001"] + end + + it "decodes multiple differing bit counts from a single string" do + str = "\xaa\xaa\xaa\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7\xc3\xd4\xaa\x6b\xd7\xaa" + array = str.unpack("b5b6b7b8b9b10b13b14b16b17") + array.should == ["01010", "010101", "0101010", "01010101", "101010100", + "0010101111", "1101011011101", "01010101111010", + "1100001100101011", "01010101110101101"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xd4\xc3\x6b\xd7".unpack("b5b*").should == ["00101", "110000111101011011101011"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xd4\xc3\x6b\xd7".unpack("b*b5").should == ["00101011110000111101011011101011", ""] + end + + it "decodes the number of bits specified by the count modifier" do + [ ["\x00", "b0", [""]], + ["\x01", "b1", ["1"]], + ["\xfe", "b2", ["01"]], + ["\xfc", "b3", ["001"]], + ["\xf7", "b4", ["1110"]], + ["\xff", "b5", ["11111"]], + ["\xfe", "b6", ["011111"]], + ["\xce", "b7", ["0111001"]], + ["\xbd", "b8", ["10111101"]], + ["\x01\xff", "b9", ["100000001"]], + ["\x01\xfe", "b10", ["1000000001"]], + ["\x01\xfc", "b11", ["10000000001"]], + ["\xf1\xf8", "b12", ["100011110001"]], + ["\xe1\xf1", "b13", ["1000011110001"]], + ["\x03\xe0", "b14", ["11000000000001"]], + ["\x47\xc0", "b15", ["111000100000001"]], + ["\x81\x0f", "b16", ["1000000111110000"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the bits when passed the '*' modifier" do + [ ["", [""]], + ["\x00", ["00000000"]], + ["\x80", ["00000001"]], + ["\x7f", ["11111110"]], + ["\x81", ["10000001"]], + ["\x0f", ["11110000"]], + ["\x80\x80", ["0000000100000001"]], + ["\x8f\x10", ["1111000100001000"]], + ["\x00\x10", ["0000000000001000"]] + ].should be_computed_by(:unpack, "b*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x01", ["1", "", ""]], + ["\x01\x80", ["1", "0", ""]] + ].should be_computed_by(:unpack, "bbb") + end + + it "ignores NULL bytes between directives" do + "\x01\x00".unpack("b\x00b").should == ["1", "0"] + end + + it "ignores spaces between directives" do + "\x01\x00".unpack("b b").should == ["1", "0"] + end + + it "decodes into US-ASCII string values" do + str = "s".force_encoding('UTF-8').unpack("b*")[0] + str.encoding.name.should == 'US-ASCII' + end + +end diff --git a/spec/rubyspec/core/string/unpack/c_spec.rb b/spec/rubyspec/core/string/unpack/c_spec.rb new file mode 100644 index 0000000000..36de462cac --- /dev/null +++ b/spec/rubyspec/core/string/unpack/c_spec.rb @@ -0,0 +1,63 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe :string_unpack_8bit, shared: true do + it "decodes one byte for a single format character" do + "abc".unpack(unpack_format()).should == [97] + end + + it "decodes two bytes for two format characters" do + "abc".unpack(unpack_format(nil, 2)).should == [97, 98] + end + + it "decodes the number of bytes requested by the count modifier" do + "abc".unpack(unpack_format(2)).should == [97, 98] + end + + it "decodes the remaining bytes when passed the '*' modifier" do + "abc".unpack(unpack_format('*')).should == [97, 98, 99] + end + + it "decodes the remaining bytes when passed the '*' modifer after another directive" do + "abc".unpack(unpack_format()+unpack_format('*')).should == [97, 98, 99] + end + + it "decodes zero bytes when no bytes remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*', 2)).should == [97, 98, 99] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["a", [97, nil, nil]], + ["ab", [97, 98, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "abc".unpack(unpack_format("\000", 2)).should == [97, 98] + end + + it "ignores spaces between directives" do + "abc".unpack(unpack_format(' ', 2)).should == [97, 98] + end +end + +describe "String#unpack with format 'C'" do + it_behaves_like :string_unpack_basic, 'C' + it_behaves_like :string_unpack_8bit, 'C' + + it "decodes a byte with most significant bit set as a positive number" do + "\xff\x80\x82".unpack('C*').should == [255, 128, 130] + end +end + +describe "String#unpack with format 'c'" do + it_behaves_like :string_unpack_basic, 'c' + it_behaves_like :string_unpack_8bit, 'c' + + it "decodes a byte with most significant bit set as a negative number" do + "\xff\x80\x82".unpack('c*').should == [-1, -128, -126] + end +end diff --git a/spec/rubyspec/core/string/unpack/comment_spec.rb b/spec/rubyspec/core/string/unpack/comment_spec.rb new file mode 100644 index 0000000000..884960b337 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/comment_spec.rb @@ -0,0 +1,25 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "String#unpack" do + it "ignores directives text from '#' to the first newline" do + "\x01\x02\x03".unpack("c#this is a comment\nc").should == [1, 2] + end + + it "ignores directives text from '#' to the end if no newline is present" do + "\x01\x02\x03".unpack("c#this is a comment c").should == [1] + end + + it "ignores comments at the start of the directives string" do + "\x01\x02\x03".unpack("#this is a comment\nc").should == [1] + end + + it "ignores the entire directive string if it is a comment" do + "\x01\x02\x03".unpack("#this is a comment c").should == [] + end + + it "ignores multiple comments" do + "\x01\x02\x03".unpack("c#comment\nc#comment\nc#c").should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/core/string/unpack/d_spec.rb b/spec/rubyspec/core/string/unpack/d_spec.rb new file mode 100644 index 0000000000..db4638f8ef --- /dev/null +++ b/spec/rubyspec/core/string/unpack/d_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +little_endian do + describe "String#unpack with format 'D'" do + it_behaves_like :string_unpack_basic, 'D' + it_behaves_like :string_unpack_double_le, 'D' + end + + describe "String#unpack with format 'd'" do + it_behaves_like :string_unpack_basic, 'd' + it_behaves_like :string_unpack_double_le, 'd' + end +end + +big_endian do + describe "String#unpack with format 'D'" do + it_behaves_like :string_unpack_basic, 'D' + it_behaves_like :string_unpack_double_be, 'D' + end + + describe "String#unpack with format 'd'" do + it_behaves_like :string_unpack_basic, 'd' + it_behaves_like :string_unpack_double_be, 'd' + end +end diff --git a/spec/rubyspec/core/string/unpack/e_spec.rb b/spec/rubyspec/core/string/unpack/e_spec.rb new file mode 100644 index 0000000000..cb74c00206 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/e_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "String#unpack with format 'E'" do + it_behaves_like :string_unpack_basic, 'E' + it_behaves_like :string_unpack_double_le, 'E' +end + +describe "String#unpack with format 'e'" do + it_behaves_like :string_unpack_basic, 'e' + it_behaves_like :string_unpack_float_le, 'e' +end diff --git a/spec/rubyspec/core/string/unpack/f_spec.rb b/spec/rubyspec/core/string/unpack/f_spec.rb new file mode 100644 index 0000000000..60dad46703 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/f_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +little_endian do + describe "String#unpack with format 'F'" do + it_behaves_like :string_unpack_basic, 'F' + it_behaves_like :string_unpack_float_le, 'F' + end + + describe "String#unpack with format 'f'" do + it_behaves_like :string_unpack_basic, 'f' + it_behaves_like :string_unpack_float_le, 'f' + end +end + +big_endian do + describe "String#unpack with format 'F'" do + it_behaves_like :string_unpack_basic, 'F' + it_behaves_like :string_unpack_float_be, 'F' + end + + describe "String#unpack with format 'f'" do + it_behaves_like :string_unpack_basic, 'f' + it_behaves_like :string_unpack_float_be, 'f' + end +end diff --git a/spec/rubyspec/core/string/unpack/g_spec.rb b/spec/rubyspec/core/string/unpack/g_spec.rb new file mode 100644 index 0000000000..f5bec1534e --- /dev/null +++ b/spec/rubyspec/core/string/unpack/g_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/float', __FILE__) + +describe "String#unpack with format 'G'" do + it_behaves_like :string_unpack_basic, 'G' + it_behaves_like :string_unpack_double_be, 'G' +end + +describe "String#unpack with format 'g'" do + it_behaves_like :string_unpack_basic, 'g' + it_behaves_like :string_unpack_float_be, 'g' +end diff --git a/spec/rubyspec/core/string/unpack/h_spec.rb b/spec/rubyspec/core/string/unpack/h_spec.rb new file mode 100644 index 0000000000..00d6d68eee --- /dev/null +++ b/spec/rubyspec/core/string/unpack/h_spec.rb @@ -0,0 +1,124 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'H'" do + it_behaves_like :string_unpack_basic, 'H' + it_behaves_like :string_unpack_no_platform, 'H' + + it "decodes one nibble from each byte for each format character starting with the most significant bit" do + [ ["\x8f", "H", ["8"]], + ["\xf8\x0f", "HH", ["f", "0"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of nibbles in the string when passed a count" do + "\xca\xfe".unpack("H5").should == ["cafe"] + end + + it "decodes multiple differing nibble counts from a single string" do + array = "\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7".unpack("HH2H3H4H5") + array.should == ["a", "55", "aad", "c36b", "d7aad"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xaa\x55\xaa\xd4\xc3\x6b".unpack("H3H*").should == ["aa5", "aad4c36b"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xaa\x55\xaa\xd4\xc3\x6b".unpack("H*H3").should == ["aa55aad4c36b", ""] + end + + it "decodes the number of nibbles specified by the count modifier" do + [ ["\xab", "H0", [""]], + ["\x00", "H1", ["0"]], + ["\x01", "H2", ["01"]], + ["\x01\x23", "H3", ["012"]], + ["\x01\x23", "H4", ["0123"]], + ["\x01\x23\x45", "H5", ["01234"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the nibbles when passed the '*' modifier" do + [ ["", [""]], + ["\xab", ["ab"]], + ["\xca\xfe", ["cafe"]], + ].should be_computed_by(:unpack, "H*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x01", ["0", "", ""]], + ["\x01\x80", ["0", "8", ""]] + ].should be_computed_by(:unpack, "HHH") + end + + it "ignores NULL bytes between directives" do + "\x01\x10".unpack("H\x00H").should == ["0", "1"] + end + + it "ignores spaces between directives" do + "\x01\x10".unpack("H H").should == ["0", "1"] + end +end + +describe "String#unpack with format 'h'" do + it_behaves_like :string_unpack_basic, 'h' + it_behaves_like :string_unpack_no_platform, 'h' + + it "decodes one nibble from each byte for each format character starting with the least significant bit" do + [ ["\x8f", "h", ["f"]], + ["\xf8\x0f", "hh", ["8", "f"]] + ].should be_computed_by(:unpack) + end + + it "decodes only the number of nibbles in the string when passed a count" do + "\xac\xef".unpack("h5").should == ["cafe"] + end + + it "decodes multiple differing nibble counts from a single string" do + array = "\xaa\x55\xaa\xd4\xc3\x6b\xd7\xaa\xd7".unpack("hh2h3h4h5") + array.should == ["a", "55", "aa4", "3cb6", "7daa7"] + end + + it "decodes a directive with a '*' modifier after a directive with a count modifier" do + "\xba\x55\xaa\xd4\xc3\x6b".unpack("h3h*").should == ["ab5", "aa4d3cb6"] + end + + it "decodes a directive with a count modifier after a directive with a '*' modifier" do + "\xba\x55\xaa\xd4\xc3\x6b".unpack("h*h3").should == ["ab55aa4d3cb6", ""] + end + + it "decodes the number of nibbles specified by the count modifier" do + [ ["\xab", "h0", [""]], + ["\x00", "h1", ["0"]], + ["\x01", "h2", ["10"]], + ["\x01\x23", "h3", ["103"]], + ["\x01\x23", "h4", ["1032"]], + ["\x01\x23\x45", "h5", ["10325"]] + ].should be_computed_by(:unpack) + end + + it "decodes all the nibbles when passed the '*' modifier" do + [ ["", [""]], + ["\xab", ["ba"]], + ["\xac\xef", ["cafe"]], + ].should be_computed_by(:unpack, "h*") + end + + it "adds an empty string for each element requested beyond the end of the String" do + [ ["", ["", "", ""]], + ["\x01", ["1", "", ""]], + ["\x01\x80", ["1", "0", ""]] + ].should be_computed_by(:unpack, "hhh") + end + + it "ignores NULL bytes between directives" do + "\x01\x10".unpack("h\x00h").should == ["1", "0"] + end + + it "ignores spaces between directives" do + "\x01\x10".unpack("h h").should == ["1", "0"] + end +end diff --git a/spec/rubyspec/core/string/unpack/i_spec.rb b/spec/rubyspec/core/string/unpack/i_spec.rb new file mode 100644 index 0000000000..f3183afe99 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/i_spec.rb @@ -0,0 +1,152 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'I'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'I<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'I<_' + it_behaves_like :string_unpack_32bit_le, 'I_<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I<_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'I'" do + it_behaves_like :string_unpack_32bit_be, 'I>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'I>_' + it_behaves_like :string_unpack_32bit_be, 'I_>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I>_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'I>!' + it_behaves_like :string_unpack_32bit_be, 'I!>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I>!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I!>' + end +end + +describe "String#unpack with format 'i'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'i<' + it_behaves_like :string_unpack_32bit_le_signed, 'i<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'i<_' + it_behaves_like :string_unpack_32bit_le, 'i_<' + it_behaves_like :string_unpack_32bit_le_signed, 'i<_' + it_behaves_like :string_unpack_32bit_le_signed, 'i_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'i'" do + it_behaves_like :string_unpack_32bit_be, 'i>' + it_behaves_like :string_unpack_32bit_be_signed, 'i>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'i>_' + it_behaves_like :string_unpack_32bit_be, 'i_>' + it_behaves_like :string_unpack_32bit_be_signed, 'i>_' + it_behaves_like :string_unpack_32bit_be_signed, 'i_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'i>!' + it_behaves_like :string_unpack_32bit_be, 'i!>' + it_behaves_like :string_unpack_32bit_be_signed, 'i>!' + it_behaves_like :string_unpack_32bit_be_signed, 'i!>' + end +end + +little_endian do + describe "String#unpack with format 'I'" do + it_behaves_like :string_unpack_basic, 'I' + it_behaves_like :string_unpack_32bit_le, 'I' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I' + end + + describe "String#unpack with format 'I' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'I_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I_' + end + + describe "String#unpack with format 'I' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'I!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'I!' + end + + describe "String#unpack with format 'i'" do + it_behaves_like :string_unpack_basic, 'i' + it_behaves_like :string_unpack_32bit_le, 'i' + it_behaves_like :string_unpack_32bit_le_signed, 'i' + end + + describe "String#unpack with format 'i' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'i_' + it_behaves_like :string_unpack_32bit_le_signed, 'i_' + end + + describe "String#unpack with format 'i' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'i!' + it_behaves_like :string_unpack_32bit_le_signed, 'i!' + end +end + +big_endian do + describe "String#unpack with format 'I'" do + it_behaves_like :string_unpack_basic, 'I' + it_behaves_like :string_unpack_32bit_be, 'I' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I' + end + + describe "String#unpack with format 'I' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'I_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I_' + end + + describe "String#unpack with format 'I' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'I!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'I!' + end + + describe "String#unpack with format 'i'" do + it_behaves_like :string_unpack_basic, 'i' + it_behaves_like :string_unpack_32bit_be, 'i' + it_behaves_like :string_unpack_32bit_be_signed, 'i' + end + + describe "String#unpack with format 'i' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'i_' + it_behaves_like :string_unpack_32bit_be_signed, 'i_' + end + + describe "String#unpack with format 'i' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'i!' + it_behaves_like :string_unpack_32bit_be_signed, 'i!' + end +end diff --git a/spec/rubyspec/core/string/unpack/j_spec.rb b/spec/rubyspec/core/string/unpack/j_spec.rb new file mode 100644 index 0000000000..49c460aeb3 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/j_spec.rb @@ -0,0 +1,277 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +ruby_version_is '2.3' do + # To handle the special case of x64-mingw32 + pointer_size = RUBY_PLATFORM =~ /\bx64\b/ ? 64 : 1.size * 8 + + if pointer_size == 64 then + little_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'J_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'J!' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'j_' + it_behaves_like :string_unpack_64bit_le_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'j!' + it_behaves_like :string_unpack_64bit_le_signed, 'j!' + end + end + end + + big_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'J_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'J!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'j_' + it_behaves_like :string_unpack_64bit_be_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'j!' + it_behaves_like :string_unpack_64bit_be_signed, 'j!' + end + end + end + + describe "String#unpack with format 'J'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'J<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'J>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'J<_' + it_behaves_like :string_unpack_64bit_le, 'J_<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J<_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'J_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'J' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'J>_' + it_behaves_like :string_unpack_64bit_be, 'J_>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J>_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'J>!' + it_behaves_like :string_unpack_64bit_be, 'J!>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J>!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'J!>' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'j<' + it_behaves_like :string_unpack_64bit_le_signed, 'j<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'j>' + it_behaves_like :string_unpack_64bit_be_signed, 'j>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'j<_' + it_behaves_like :string_unpack_64bit_le, 'j_<' + it_behaves_like :string_unpack_64bit_le_signed, 'j<_' + it_behaves_like :string_unpack_64bit_le_signed, 'j_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'j' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'j>_' + it_behaves_like :string_unpack_64bit_be, 'j_>' + it_behaves_like :string_unpack_64bit_be_signed, 'j>_' + it_behaves_like :string_unpack_64bit_be_signed, 'j_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'j>!' + it_behaves_like :string_unpack_64bit_be, 'j!>' + it_behaves_like :string_unpack_64bit_be_signed, 'j>!' + it_behaves_like :string_unpack_64bit_be_signed, 'j!>' + end + end + end + + if pointer_size == 32 then + little_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'J_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'J!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'j_' + it_behaves_like :string_unpack_32bit_le_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'j!' + it_behaves_like :string_unpack_32bit_le_signed, 'j!' + end + end + end + + big_endian do + describe "String#unpack with format 'J'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'J_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'J!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J!' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'j_' + it_behaves_like :string_unpack_32bit_be_signed, 'j_' + end + + describe "with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'j!' + it_behaves_like :string_unpack_32bit_be_signed, 'j!' + end + end + end + + describe "String#unpack with format 'J'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'J<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'J>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'J<_' + it_behaves_like :string_unpack_32bit_le, 'J_<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J<_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'J_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'J' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'J>_' + it_behaves_like :string_unpack_32bit_be, 'J_>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J>_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'J>!' + it_behaves_like :string_unpack_32bit_be, 'J!>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J>!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'J!>' + end + end + + describe "String#unpack with format 'j'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'j<' + it_behaves_like :string_unpack_32bit_le_signed, 'j<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'j>' + it_behaves_like :string_unpack_32bit_be_signed, 'j>' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'j<_' + it_behaves_like :string_unpack_32bit_le, 'j_<' + it_behaves_like :string_unpack_32bit_le_signed, 'j<_' + it_behaves_like :string_unpack_32bit_le_signed, 'j_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'j' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'j>_' + it_behaves_like :string_unpack_32bit_be, 'j_>' + it_behaves_like :string_unpack_32bit_be_signed, 'j>_' + it_behaves_like :string_unpack_32bit_be_signed, 'j_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'j>!' + it_behaves_like :string_unpack_32bit_be, 'j!>' + it_behaves_like :string_unpack_32bit_be_signed, 'j>!' + it_behaves_like :string_unpack_32bit_be_signed, 'j!>' + end + end + end +end diff --git a/spec/rubyspec/core/string/unpack/l_spec.rb b/spec/rubyspec/core/string/unpack/l_spec.rb new file mode 100644 index 0000000000..3a7c55f8da --- /dev/null +++ b/spec/rubyspec/core/string/unpack/l_spec.rb @@ -0,0 +1,376 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'L'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'L<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'L>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>' + end + + platform_is wordsize: 32 do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'L<_' + it_behaves_like :string_unpack_32bit_le, 'L_<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L<_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'L' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'L>_' + it_behaves_like :string_unpack_32bit_be, 'L_>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'L>!' + it_behaves_like :string_unpack_32bit_be, 'L!>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L!>' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'L<_' + it_behaves_like :string_unpack_64bit_le, 'L_<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L<_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'L' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'L>_' + it_behaves_like :string_unpack_64bit_be, 'L_>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L>_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'L>!' + it_behaves_like :string_unpack_64bit_be, 'L!>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L>!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L!>' + end + end + + platform_is :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'L<_' + it_behaves_like :string_unpack_32bit_le, 'L_<' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L<_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'L' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'L>_' + it_behaves_like :string_unpack_32bit_be, 'L_>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'L>!' + it_behaves_like :string_unpack_32bit_be, 'L!>' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L>!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L!>' + end + end + end +end + +describe "String#unpack with format 'l'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_32bit_le, 'l<' + it_behaves_like :string_unpack_32bit_le_signed, 'l<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_32bit_be, 'l>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>' + end + + platform_is wordsize: 32 do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'l<_' + it_behaves_like :string_unpack_32bit_le, 'l_<' + it_behaves_like :string_unpack_32bit_le_signed, 'l<_' + it_behaves_like :string_unpack_32bit_le_signed, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'l' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'l>_' + it_behaves_like :string_unpack_32bit_be, 'l_>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>_' + it_behaves_like :string_unpack_32bit_be_signed, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'l>!' + it_behaves_like :string_unpack_32bit_be, 'l!>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>!' + it_behaves_like :string_unpack_32bit_be_signed, 'l!>' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_64bit_le, 'l<_' + it_behaves_like :string_unpack_64bit_le, 'l_<' + it_behaves_like :string_unpack_64bit_le_signed, 'l<_' + it_behaves_like :string_unpack_64bit_le_signed, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_64bit_le, 'l' and '_'" do + it_behaves_like :string_unpack_64bit_be, 'l>_' + it_behaves_like :string_unpack_64bit_be, 'l_>' + it_behaves_like :string_unpack_64bit_be_signed, 'l>_' + it_behaves_like :string_unpack_64bit_be_signed, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_64bit_be, 'l>!' + it_behaves_like :string_unpack_64bit_be, 'l!>' + it_behaves_like :string_unpack_64bit_be_signed, 'l>!' + it_behaves_like :string_unpack_64bit_be_signed, 'l!>' + end + end + + platform_is :mingw32 do + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_32bit_le, 'l<_' + it_behaves_like :string_unpack_32bit_le, 'l_<' + it_behaves_like :string_unpack_32bit_le_signed, 'l<_' + it_behaves_like :string_unpack_32bit_le_signed, 'l_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_32bit_le, 'l' and '_'" do + it_behaves_like :string_unpack_32bit_be, 'l>_' + it_behaves_like :string_unpack_32bit_be, 'l_>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>_' + it_behaves_like :string_unpack_32bit_be_signed, 'l_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_32bit_be, 'l>!' + it_behaves_like :string_unpack_32bit_be, 'l!>' + it_behaves_like :string_unpack_32bit_be_signed, 'l>!' + it_behaves_like :string_unpack_32bit_be_signed, 'l!>' + end + end + end +end + +little_endian do + describe "String#unpack with format 'L'" do + it_behaves_like :string_unpack_basic, 'L' + it_behaves_like :string_unpack_32bit_le, 'L' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L' + end + + describe "String#unpack with format 'l'" do + it_behaves_like :string_unpack_basic, 'l' + it_behaves_like :string_unpack_32bit_le, 'l' + it_behaves_like :string_unpack_32bit_le_signed, 'l' + end + + platform_is wordsize: 32 do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'L_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'L!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'l_' + it_behaves_like :string_unpack_32bit_le_signed, 'l' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'l!' + it_behaves_like :string_unpack_32bit_le_signed, 'l' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'L_' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'L!' + it_behaves_like :string_unpack_64bit_le_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_64bit_le, 'l_' + it_behaves_like :string_unpack_64bit_le_signed, 'l_' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_64bit_le, 'l!' + it_behaves_like :string_unpack_64bit_le_signed, 'l!' + end + end + + platform_is :mingw32 do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'L_' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'L!' + it_behaves_like :string_unpack_32bit_le_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_32bit_le, 'l_' + it_behaves_like :string_unpack_32bit_le_signed, 'l_' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_32bit_le, 'l!' + it_behaves_like :string_unpack_32bit_le_signed, 'l!' + end + end + end +end + +big_endian do + describe "String#unpack with format 'L'" do + it_behaves_like :string_unpack_basic, 'L' + it_behaves_like :string_unpack_32bit_be, 'L' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L' + end + + describe "String#unpack with format 'l'" do + it_behaves_like :string_unpack_basic, 'l' + it_behaves_like :string_unpack_32bit_be, 'l' + it_behaves_like :string_unpack_32bit_be_signed, 'l' + end + + platform_is wordsize: 32 do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'L_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'L!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'l_' + it_behaves_like :string_unpack_32bit_be_signed, 'l' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'l!' + it_behaves_like :string_unpack_32bit_be_signed, 'l' + end + end + + platform_is wordsize: 64 do + platform_is_not :mingw32 do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'L_' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'L!' + it_behaves_like :string_unpack_64bit_be_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_64bit_be, 'l_' + it_behaves_like :string_unpack_64bit_be_signed, 'l_' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_64bit_be, 'l!' + it_behaves_like :string_unpack_64bit_be_signed, 'l!' + end + end + + platform_is :mingw32 do + describe "String#unpack with format 'L' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'L_' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L_' + end + + describe "String#unpack with format 'L' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'L!' + it_behaves_like :string_unpack_32bit_be_unsigned, 'L!' + end + + describe "String#unpack with format 'l' with modifier '_'" do + it_behaves_like :string_unpack_32bit_be, 'l_' + it_behaves_like :string_unpack_32bit_be_signed, 'l_' + end + + describe "String#unpack with format 'l' with modifier '!'" do + it_behaves_like :string_unpack_32bit_be, 'l!' + it_behaves_like :string_unpack_32bit_be_signed, 'l!' + end + end + end +end diff --git a/spec/rubyspec/core/string/unpack/m_spec.rb b/spec/rubyspec/core/string/unpack/m_spec.rb new file mode 100644 index 0000000000..104f282fed --- /dev/null +++ b/spec/rubyspec/core/string/unpack/m_spec.rb @@ -0,0 +1,170 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'M'" do + it_behaves_like :string_unpack_basic, 'M' + it_behaves_like :string_unpack_no_platform, 'M' + + it "decodes an empty string" do + "".unpack("M").should == [""] + end + + it "decodes the complete string ignoring newlines when given a single directive" do + "a=\nb=\nc=\n".unpack("M").should == ["abc"] + end + + it "appends empty string to the array for directives exceeding the input size" do + "a=\nb=\nc=\n".unpack("MMM").should == ["abc", "", ""] + end + + it "ignores the count or '*' modifier and decodes the entire string" do + [ ["a=\nb=\nc=\n", "M238", ["abc"]], + ["a=\nb=\nc=\n", "M*", ["abc"]] + ].should be_computed_by(:unpack) + end + + it "decodes the '=' character" do + "=3D=\n".unpack("M").should == ["="] + end + + it "decodes an embedded space character" do + "a b=\n".unpack("M").should == ["a b"] + end + + it "decodes a space at the end of the pre-encoded string" do + "a =\n".unpack("M").should == ["a "] + end + + it "decodes an embedded tab character" do + "a\tb=\n".unpack("M").should == ["a\tb"] + end + + it "decodes a tab character at the end of the pre-encoded string" do + "a\t=\n".unpack("M").should == ["a\t"] + end + + it "decodes an embedded newline" do + "a\nb=\n".unpack("M").should == ["a\nb"] + end + + it "decodes pre-encoded byte values 33..60" do + [ ["!\"\#$%&'()*+,-./=\n", ["!\"\#$%&'()*+,-./"]], + ["0123456789=\n", ["0123456789"]], + [":;<=\n", [":;<"]] + ].should be_computed_by(:unpack, "M") + end + + it "decodes pre-encoded byte values 62..126" do + [ [">?@=\n", [">?@"]], + ["ABCDEFGHIJKLMNOPQRSTUVWXYZ=\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]], + ["[\\]^_`=\n", ["[\\]^_`"]], + ["abcdefghijklmnopqrstuvwxyz=\n", ["abcdefghijklmnopqrstuvwxyz"]], + ["{|}~=\n", ["{|}~"]] + ].should be_computed_by(:unpack, "M") + end + + it "decodes pre-encoded byte values 0..31 except tab and newline" do + [ ["=00=01=02=03=04=05=06=\n", ["\x00\x01\x02\x03\x04\x05\x06"]], + ["=07=08=0B=0C=0D=\n", ["\a\b\v\f\r"]], + ["=0E=0F=10=11=12=13=14=\n", ["\x0e\x0f\x10\x11\x12\x13\x14"]], + ["=15=16=17=18=19=1A=\n", ["\x15\x16\x17\x18\x19\x1a"]], + ["=1B=\n", ["\e"]], + ["=1C=1D=1E=1F=\n", ["\x1c\x1d\x1e\x1f"]] + ].should be_computed_by(:unpack, "M") + end + + it "decodes pre-encoded byte values 127..255" do + [ ["=7F=80=81=82=83=84=85=86=\n", ["\x7f\x80\x81\x82\x83\x84\x85\x86"]], + ["=87=88=89=8A=8B=8C=8D=8E=\n", ["\x87\x88\x89\x8a\x8b\x8c\x8d\x8e"]], + ["=8F=90=91=92=93=94=95=96=\n", ["\x8f\x90\x91\x92\x93\x94\x95\x96"]], + ["=97=98=99=9A=9B=9C=9D=9E=\n", ["\x97\x98\x99\x9a\x9b\x9c\x9d\x9e"]], + ["=9F=A0=A1=A2=A3=A4=A5=A6=\n", ["\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6"]], + ["=A7=A8=A9=AA=AB=AC=AD=AE=\n", ["\xa7\xa8\xa9\xaa\xab\xac\xad\xae"]], + ["=AF=B0=B1=B2=B3=B4=B5=B6=\n", ["\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6"]], + ["=B7=B8=B9=BA=BB=BC=BD=BE=\n", ["\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe"]], + ["=BF=C0=C1=C2=C3=C4=C5=C6=\n", ["\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"]], + ["=C7=C8=C9=CA=CB=CC=CD=CE=\n", ["\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce"]], + ["=CF=D0=D1=D2=D3=D4=D5=D6=\n", ["\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6"]], + ["=D7=D8=D9=DA=DB=DC=DD=DE=\n", ["\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde"]], + ["=DF=E0=E1=E2=E3=E4=E5=E6=\n", ["\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6"]], + ["=E7=E8=E9=EA=EB=EC=ED=EE=\n", ["\xe7\xe8\xe9\xea\xeb\xec\xed\xee"]], + ["=EF=F0=F1=F2=F3=F4=F5=F6=\n", ["\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6"]], + ["=F7=F8=F9=FA=FB=FC=FD=FE=\n", ["\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe"]], + ["=FF=\n", ["\xff"]] + ].should be_computed_by(:unpack, "M") + end +end + +describe "String#unpack with format 'm'" do + it_behaves_like :string_unpack_basic, 'm' + it_behaves_like :string_unpack_no_platform, 'm' + + it "decodes an empty string" do + "".unpack("m").should == [""] + end + + it "decodes the complete string ignoring newlines when given a single directive" do + "YWJj\nREVG\n".unpack("m").should == ["abcDEF"] + end + + it "ignores the count or '*' modifier and decodes the entire string" do + [ ["YWJj\nREVG\n", "m238", ["abcDEF"]], + ["YWJj\nREVG\n", "m*", ["abcDEF"]] + ].should be_computed_by(:unpack) + end + + it "appends empty string to the array for directives exceeding the input size" do + "YWJj\nREVG\n".unpack("mmm").should == ["abcDEF", "", ""] + end + + it "decodes all pre-encoded ascii byte values" do + [ ["AAECAwQFBg==\n", ["\x00\x01\x02\x03\x04\x05\x06"]], + ["BwgJCgsMDQ==\n", ["\a\b\t\n\v\f\r"]], + ["Dg8QERITFBUW\n", ["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"]], + ["FxgZGhscHR4f\n", ["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"]], + ["ISIjJCUmJygpKissLS4v\n", ["!\"\#$%&'()*+,-./"]], + ["MDEyMzQ1Njc4OQ==\n", ["0123456789"]], + ["Ojs8PT4/QA==\n", [":;<=>?@"]], + ["QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]], + ["W1xdXl9g\n", ["[\\]^_`"]], + ["YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=\n", ["abcdefghijklmnopqrstuvwxyz"]], + ["e3x9fg==\n", ["{|}~"]], + ["f8KAwoHCgsKD\n", ["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"]], + ["woTChcKGwofC\n", ["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"]], + ["iMKJworCi8KM\n", ["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"]], + ["wo3CjsKPwpDC\n", ["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"]], + ["kcKSwpPClMKV\n", ["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"]], + ["wpbCl8KYwpnC\n", ["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"]], + ["msKbwpzCncKe\n", ["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"]], + ["wp/CoMKhwqLC\n", ["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"]], + ["o8KkwqXCpsKn\n", ["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"]], + ["wqjCqcKqwqvC\n", ["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"]], + ["rMKtwq7Cr8Kw\n", ["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"]], + ["wrHCssKzwrTC\n", ["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"]], + ["tcK2wrfCuMK5\n", ["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"]], + ["wrrCu8K8wr3C\n", ["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"]], + ["vsK/w4DDgcOC\n", ["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"]], + ["w4PDhMOFw4bD\n", ["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"]], + ["h8OIw4nDisOL\n", ["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"]], + ["w4zDjcOOw4/D\n", ["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"]], + ["kMORw5LDk8OU\n", ["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"]], + ["w5XDlsOXw5jD\n", ["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"]], + ["mcOaw5vDnMOd\n", ["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"]], + ["w57Dn8Ogw6HD\n", ["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"]], + ["osOjw6TDpcOm\n", ["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"]], + ["w6fDqMOpw6rD\n", ["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"]], + ["q8Osw63DrsOv\n", ["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"]], + ["w7DDscOyw7PD\n", ["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"]], + ["tMO1w7bDt8O4\n", ["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"]], + ["w7nDusO7w7zD\n", ["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"]], + ["vcO+w78=\n", ["\xbd\xc3\xbe\xc3\xbf"]] + ].should be_computed_by(:unpack, "m") + end + + it "produces binary strings" do + "".unpack("m").first.encoding.should == Encoding::BINARY + "Ojs8PT4/QA==\n".unpack("m").first.encoding.should == Encoding::BINARY + end +end diff --git a/spec/rubyspec/core/string/unpack/n_spec.rb b/spec/rubyspec/core/string/unpack/n_spec.rb new file mode 100644 index 0000000000..6e85346338 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/n_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'N'" do + it_behaves_like :string_unpack_basic, 'N' + it_behaves_like :string_unpack_32bit_be, 'N' + it_behaves_like :string_unpack_32bit_be_unsigned, 'N' + it_behaves_like :string_unpack_no_platform, 'N' +end + +describe "String#unpack with format 'n'" do + it_behaves_like :string_unpack_basic, 'n' + it_behaves_like :string_unpack_16bit_be, 'n' + it_behaves_like :string_unpack_16bit_be_unsigned, 'n' + it_behaves_like :string_unpack_no_platform, 'n' +end diff --git a/spec/rubyspec/core/string/unpack/p_spec.rb b/spec/rubyspec/core/string/unpack/p_spec.rb new file mode 100644 index 0000000000..7c9a502a15 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/p_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'P'" do + it_behaves_like :string_unpack_basic, 'P' + + it "returns a random object after consuming a size-of a machine word bytes" do + str = "\0" * 1.size + str.unpack("P").should be_kind_of(Object) + end +end + +describe "String#unpack with format 'p'" do + it_behaves_like :string_unpack_basic, 'p' + + it "returns a random object after consuming a size-of a machine word bytes" do + str = "\0" * 1.size + str.unpack("p").should be_kind_of(Object) + end +end diff --git a/spec/rubyspec/core/string/unpack/percent_spec.rb b/spec/rubyspec/core/string/unpack/percent_spec.rb new file mode 100644 index 0000000000..38cf81b037 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/percent_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "String#unpack with format '%'" do + it "raises an Argument Error" do + lambda { "abc".unpack("%") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/unpack/q_spec.rb b/spec/rubyspec/core/string/unpack/q_spec.rb new file mode 100644 index 0000000000..91e65a9405 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/q_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'Q'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'Q<' + it_behaves_like :string_unpack_64bit_le_unsigned, 'Q<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'Q>' + it_behaves_like :string_unpack_64bit_be_unsigned, 'Q>' + end +end + +describe "String#unpack with format 'q'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_64bit_le, 'q<' + it_behaves_like :string_unpack_64bit_le_signed, 'q<' + end + + describe "with modifier '>'" do + it_behaves_like :string_unpack_64bit_be, 'q>' + it_behaves_like :string_unpack_64bit_be_signed, 'q>' + end +end + +describe "String#unpack with format 'Q'" do + it_behaves_like :string_unpack_basic, 'Q' +end + +describe "String#unpack with format 'q'" do + it_behaves_like :string_unpack_basic, 'q' +end + +little_endian do + describe "String#unpack with format 'Q'" do + it_behaves_like :string_unpack_64bit_le, 'Q' + it_behaves_like :string_unpack_64bit_le_extra, 'Q' + it_behaves_like :string_unpack_64bit_le_unsigned, 'Q' + end + + describe "String#unpack with format 'q'" do + it_behaves_like :string_unpack_64bit_le, 'q' + it_behaves_like :string_unpack_64bit_le_extra, 'q' + it_behaves_like :string_unpack_64bit_le_signed, 'q' + end +end + +big_endian do + describe "String#unpack with format 'Q'" do + it_behaves_like :string_unpack_64bit_be, 'Q' + it_behaves_like :string_unpack_64bit_be_extra, 'Q' + it_behaves_like :string_unpack_64bit_be_unsigned, 'Q' + end + + describe "String#unpack with format 'q'" do + it_behaves_like :string_unpack_64bit_be, 'q' + it_behaves_like :string_unpack_64bit_be_extra, 'q' + it_behaves_like :string_unpack_64bit_be_signed, 'q' + end +end diff --git a/spec/rubyspec/core/string/unpack/s_spec.rb b/spec/rubyspec/core/string/unpack/s_spec.rb new file mode 100644 index 0000000000..c6b079b0a6 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/s_spec.rb @@ -0,0 +1,152 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'S'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_16bit_le, 'S<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_16bit_le, 'S<_' + it_behaves_like :string_unpack_16bit_le, 'S_<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S_<' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S<_' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_16bit_le, 'S'" do + it_behaves_like :string_unpack_16bit_be, 'S>' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_16bit_be, 'S>_' + it_behaves_like :string_unpack_16bit_be, 'S_>' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S>_' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_16bit_be, 'S>!' + it_behaves_like :string_unpack_16bit_be, 'S!>' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S>!' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S!>' + end +end + +describe "String#unpack with format 's'" do + describe "with modifier '<'" do + it_behaves_like :string_unpack_16bit_le, 's<' + it_behaves_like :string_unpack_16bit_le_signed, 's<' + end + + describe "with modifier '<' and '_'" do + it_behaves_like :string_unpack_16bit_le, 's<_' + it_behaves_like :string_unpack_16bit_le, 's_<' + it_behaves_like :string_unpack_16bit_le_signed, 's<_' + it_behaves_like :string_unpack_16bit_le_signed, 's_<' + end + + describe "with modifier '<' and '!'" do + it_behaves_like :string_unpack_16bit_le, 's'" do + it_behaves_like :string_unpack_16bit_be, 's>' + it_behaves_like :string_unpack_16bit_be_signed, 's>' + end + + describe "with modifier '>' and '_'" do + it_behaves_like :string_unpack_16bit_be, 's>_' + it_behaves_like :string_unpack_16bit_be, 's_>' + it_behaves_like :string_unpack_16bit_be_signed, 's>_' + it_behaves_like :string_unpack_16bit_be_signed, 's_>' + end + + describe "with modifier '>' and '!'" do + it_behaves_like :string_unpack_16bit_be, 's>!' + it_behaves_like :string_unpack_16bit_be, 's!>' + it_behaves_like :string_unpack_16bit_be_signed, 's>!' + it_behaves_like :string_unpack_16bit_be_signed, 's!>' + end +end + +little_endian do + describe "String#unpack with format 'S'" do + it_behaves_like :string_unpack_basic, 'S' + it_behaves_like :string_unpack_16bit_le, 'S' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S' + end + + describe "String#unpack with format 'S' with modifier '_'" do + it_behaves_like :string_unpack_16bit_le, 'S_' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S_' + end + + describe "String#unpack with format 'S' with modifier '!'" do + it_behaves_like :string_unpack_16bit_le, 'S!' + it_behaves_like :string_unpack_16bit_le_unsigned, 'S!' + end + + describe "String#unpack with format 's'" do + it_behaves_like :string_unpack_basic, 's' + it_behaves_like :string_unpack_16bit_le, 's' + it_behaves_like :string_unpack_16bit_le_signed, 's' + end + + describe "String#unpack with format 's' with modifier '_'" do + it_behaves_like :string_unpack_16bit_le, 's_' + it_behaves_like :string_unpack_16bit_le_signed, 's_' + end + + describe "String#unpack with format 's' with modifier '!'" do + it_behaves_like :string_unpack_16bit_le, 's!' + it_behaves_like :string_unpack_16bit_le_signed, 's!' + end +end + +big_endian do + describe "String#unpack with format 'S'" do + it_behaves_like :string_unpack_basic, 'S' + it_behaves_like :string_unpack_16bit_be, 'S' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S' + end + + describe "String#unpack with format 'S' with modifier '_'" do + it_behaves_like :string_unpack_16bit_be, 'S_' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S_' + end + + describe "String#unpack with format 'S' with modifier '!'" do + it_behaves_like :string_unpack_16bit_be, 'S!' + it_behaves_like :string_unpack_16bit_be_unsigned, 'S!' + end + + describe "String#unpack with format 's'" do + it_behaves_like :string_unpack_basic, 's' + it_behaves_like :string_unpack_16bit_be, 's' + it_behaves_like :string_unpack_16bit_be_signed, 's' + end + + describe "String#unpack with format 's' with modifier '_'" do + it_behaves_like :string_unpack_16bit_be, 's_' + it_behaves_like :string_unpack_16bit_be_signed, 's_' + end + + describe "String#unpack with format 's' with modifier '!'" do + it_behaves_like :string_unpack_16bit_be, 's!' + it_behaves_like :string_unpack_16bit_be_signed, 's!' + end +end diff --git a/spec/rubyspec/core/string/unpack/shared/basic.rb b/spec/rubyspec/core/string/unpack/shared/basic.rb new file mode 100644 index 0000000000..0ecbf615af --- /dev/null +++ b/spec/rubyspec/core/string/unpack/shared/basic.rb @@ -0,0 +1,29 @@ +describe :string_unpack_basic, shared: true do + it "ignores whitespace in the format string" do + "abc".unpack("a \t\n\v\f\r"+unpack_format).should be_an_instance_of(Array) + end + + it "calls #to_str to coerce the directives string" do + d = mock("unpack directive") + d.should_receive(:to_str).and_return("a"+unpack_format) + "abc".unpack(d).should be_an_instance_of(Array) + end + + it "raises a TypeError when passed nil" do + lambda { "abc".unpack(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed an Integer" do + lambda { "abc".unpack(1) }.should raise_error(TypeError) + end +end + +describe :string_unpack_no_platform, shared: true do + it "raises an ArgumentError when the format modifier is '_'" do + lambda { "abcdefgh".unpack(unpack_format("_")) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the format modifier is '!'" do + lambda { "abcdefgh".unpack(unpack_format("!")) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/unpack/shared/float.rb b/spec/rubyspec/core/string/unpack/shared/float.rb new file mode 100644 index 0000000000..208dc357af --- /dev/null +++ b/spec/rubyspec/core/string/unpack/shared/float.rb @@ -0,0 +1,271 @@ +# -*- encoding: ascii-8bit -*- + +describe :string_unpack_float_le, shared: true do + it "decodes one float for a single format character" do + "\x8f\xc2\xb5?".unpack(unpack_format).should == [1.4199999570846558] + end + + it "decodes a negative float" do + "\xcd\xcc\x08\xc2".unpack(unpack_format).should == [-34.200000762939453] + end + + it "decodes two floats for two format characters" do + array = "\x9a\x999@33\xb3?".unpack(unpack_format(nil, 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "decodes the number of floats requested by the count modifier" do + array = "\x9a\x999@33\xb3?33\x03A".unpack(unpack_format(3)) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier" do + array = "\x9a\x999@33\xb3?33\x03A".unpack(unpack_format("*")) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier after another directive" do + array = "\x9a\x99\xa9@33\x13A".unpack(unpack_format()+unpack_format('*')) + array.should == [5.300000190734863, 9.199999809265137] + end + + it "does not decode a float when fewer bytes than a float remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["abc", [nil, nil, nil]], + ["\x8f\xc2\xb5?abc", [1.4199999570846558, nil, nil]], + ["\x9a\x999@33\xb3?abc", [2.9000000953674316, 1.399999976158142, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x00\x00\x80\x7f".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\x00\x00\x80\xff".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + array = "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "ignores spaces between directives" do + array = "\x9a\x999@33\xb3?".unpack(unpack_format(' ', 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end +end + +describe :string_unpack_float_be, shared: true do + it "decodes one float for a single format character" do + "?\xb5\xc2\x8f".unpack(unpack_format).should == [1.4199999570846558] + end + + it "decodes a negative float" do + "\xc2\x08\xcc\xcd".unpack(unpack_format).should == [-34.200000762939453] + end + + it "decodes two floats for two format characters" do + array = "@9\x99\x9a?\xb333".unpack(unpack_format(nil, 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "decodes the number of floats requested by the count modifier" do + array = "@9\x99\x9a?\xb333A\x0333".unpack(unpack_format(3)) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier" do + array = "@9\x99\x9a?\xb333A\x0333".unpack(unpack_format("*")) + array.should == [2.9000000953674316, 1.399999976158142, 8.199999809265137] + end + + it "decodes the remaining floats when passed the '*' modifier after another directive" do + array = "@\xa9\x99\x9aA\x1333".unpack(unpack_format()+unpack_format('*')) + array.should == [5.300000190734863, 9.199999809265137] + end + + it "does not decode a float when fewer bytes than a float remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["abc", [nil, nil, nil]], + ["?\xb5\xc2\x8fabc", [1.4199999570846558, nil, nil]], + ["@9\x99\x9a?\xb333abc", [2.9000000953674316, 1.399999976158142, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x7f\x80\x00\x00".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\xff\x80\x00\x00".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + array = "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + + it "ignores spaces between directives" do + array = "@9\x99\x9a?\xb333".unpack(unpack_format(' ', 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end +end + +describe :string_unpack_double_le, shared: true do + it "decodes one double for a single format character" do + "\xb8\x1e\x85\xebQ\xb8\xf6?".unpack(unpack_format).should == [1.42] + end + + it "decodes a negative double" do + "\x9a\x99\x99\x99\x99\x19A\xc0".unpack(unpack_format).should == [-34.2] + end + + it "decodes two doubles for two format characters" do + "333333\x07@ffffff\xf6?".unpack(unpack_format(nil, 2)).should == [2.9, 1.4] + end + + it "decodes the number of doubles requested by the count modifier" do + array = "333333\x07@ffffff\xf6?ffffff\x20@".unpack(unpack_format(3)) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier" do + array = "333333\x07@ffffff\xf6?ffffff\x20@".unpack(unpack_format("*")) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier after another directive" do + array = "333333\x15@ffffff\x22@".unpack(unpack_format()+unpack_format('*')) + array.should == [5.3, 9.2] + end + + it "does not decode a double when fewer bytes than a double remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []], + ["\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff", []], + ["\xff\x00\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["\xff\x00\xff\x00\xff\x00\xff", [nil, nil, nil]], + ["\xb8\x1e\x85\xebQ\xb8\xf6?abc", [1.42, nil, nil]], + ["333333\x07@ffffff\xf6?abcd", [2.9, 1.4, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x00\x00\x00\x00\x00\x00\xf0\x7f".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\x00\x00\x00\x00\x00\x00\xf0\xff".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + end + + it "ignores spaces between directives" do + "333333\x07@ffffff\xf6?".unpack(unpack_format(' ', 2)).should == [2.9, 1.4] + end +end + +describe :string_unpack_double_be, shared: true do + it "decodes one double for a single format character" do + "?\xf6\xb8Q\xeb\x85\x1e\xb8".unpack(unpack_format).should == [1.42] + end + + it "decodes a negative double" do + "\xc0A\x19\x99\x99\x99\x99\x9a".unpack(unpack_format).should == [-34.2] + end + + it "decodes two doubles for two format characters" do + "@\x07333333?\xf6ffffff".unpack(unpack_format(nil, 2)).should == [2.9, 1.4] + end + + it "decodes the number of doubles requested by the count modifier" do + array = "@\x07333333?\xf6ffffff@\x20ffffff".unpack(unpack_format(3)) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier" do + array = "@\x07333333?\xf6ffffff@\x20ffffff".unpack(unpack_format("*")) + array.should == [2.9, 1.4, 8.2] + end + + it "decodes the remaining doubles when passed the '*' modifier after another directive" do + array = "@\x15333333@\x22ffffff".unpack(unpack_format()+unpack_format('*')) + array.should == [5.3, 9.2] + end + + it "does not decode a double when fewer bytes than a double remain and the '*' modifier is passed" do + [ ["\xff", []], + ["\xff\x00", []], + ["\xff\x00\xff", []], + ["\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff", []], + ["\xff\x00\xff\x00\xff\x00", []], + ["\xff\x00\xff\x00\xff\x00\xff", []] + ].should be_computed_by(:unpack, unpack_format("*")) + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["abcdefg", [nil, nil, nil]], + ["?\xf6\xb8Q\xeb\x85\x1e\xb8abc", [1.42, nil, nil]], + ["@\x07333333?\xf6ffffffabcd", [2.9, 1.4, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "decodes positive Infinity" do + "\x7f\xf0\x00\x00\x00\x00\x00\x00".unpack(unpack_format).should == [infinity_value] + end + + it "decodes negative Infinity" do + "\xff\xf0\x00\x00\x00\x00\x00\x00".unpack(unpack_format).should == [-infinity_value] + end + + it "decodes NaN" do + # mumble mumble NaN mumble https://bugs.ruby-lang.org/issues/5884 + [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true + end + + it "ignores NULL bytes between directives" do + "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + end + + it "ignores spaces between directives" do + "@\x07333333?\xf6ffffff".unpack(unpack_format(' ', 2)).should == [2.9, 1.4] + end +end diff --git a/spec/rubyspec/core/string/unpack/shared/integer.rb b/spec/rubyspec/core/string/unpack/shared/integer.rb new file mode 100644 index 0000000000..03dfb5c682 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/shared/integer.rb @@ -0,0 +1,339 @@ +# -*- encoding: ascii-8bit -*- + +describe :string_unpack_16bit_le, shared: true do + it "decodes one short for a single format character" do + "ab".unpack(unpack_format).should == [25185] + end + + it "decodes two shorts for two format characters" do + "abcd".unpack(unpack_format(nil, 2)).should == [25185, 25699] + end + + it "decodes the number of shorts requested by the count modifier" do + "abcdef".unpack(unpack_format(3)).should == [25185, 25699, 26213] + end + + it "decodes the remaining shorts when passed the '*' modifier" do + "abcd".unpack(unpack_format('*')).should == [25185, 25699] + end + + it "decodes the remaining shorts when passed the '*' modifier after another directive" do + "abcd".unpack(unpack_format()+unpack_format('*')).should == [25185, 25699] + end + + it "does not decode a short when fewer bytes than a short remain and the '*' modifier is passed" do + "\xff".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["abc", [25185, nil, nil]], + ["abcd", [25185, 25699, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "abcd".unpack(unpack_format("\000", 2)).should == [25185, 25699] + end + + it "ignores spaces between directives" do + "abcd".unpack(unpack_format(' ', 2)).should == [25185, 25699] + end +end + +describe :string_unpack_16bit_le_signed, shared: true do + it "decodes a short with most significant bit set as a negative number" do + "\x00\xff".unpack(unpack_format()).should == [-256] + end +end + +describe :string_unpack_16bit_le_unsigned, shared: true do + it "decodes a short with most significant bit set as a positive number" do + "\x00\xff".unpack(unpack_format()).should == [65280] + end +end + +describe :string_unpack_16bit_be, shared: true do + it "decodes one short for a single format character" do + "ba".unpack(unpack_format).should == [25185] + end + + it "decodes two shorts for two format characters" do + "badc".unpack(unpack_format(nil, 2)).should == [25185, 25699] + end + + it "decodes the number of shorts requested by the count modifier" do + "badcfe".unpack(unpack_format(3)).should == [25185, 25699, 26213] + end + + it "decodes the remaining shorts when passed the '*' modifier" do + "badc".unpack(unpack_format('*')).should == [25185, 25699] + end + + it "decodes the remaining shorts when passed the '*' modifier after another directive" do + "badc".unpack(unpack_format()+unpack_format('*')).should == [25185, 25699] + end + + it "does not decode a short when fewer bytes than a short remain and the '*' modifier is passed" do + "\xff".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["bac", [25185, nil, nil]], + ["badc", [25185, 25699, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "badc".unpack(unpack_format("\000", 2)).should == [25185, 25699] + end + + it "ignores spaces between directives" do + "badc".unpack(unpack_format(' ', 2)).should == [25185, 25699] + end +end + +describe :string_unpack_16bit_be_signed, shared: true do + it "decodes a short with most significant bit set as a negative number" do + "\xff\x00".unpack(unpack_format()).should == [-256] + end +end + +describe :string_unpack_16bit_be_unsigned, shared: true do + it "decodes a short with most significant bit set as a positive number" do + "\xff\x00".unpack(unpack_format()).should == [65280] + end +end + +describe :string_unpack_32bit_le, shared: true do + it "decodes one int for a single format character" do + "abcd".unpack(unpack_format).should == [1684234849] + end + + it "decodes two ints for two format characters" do + "abghefcd".unpack(unpack_format(nil, 2)).should == [1751605857, 1684235877] + end + + it "decodes the number of ints requested by the count modifier" do + "abcedfgh".unpack(unpack_format(2)).should == [1701012065, 1751606884] + end + + it "decodes the remaining ints when passed the '*' modifier" do + "acbdegfh".unpack(unpack_format('*')).should == [1684169569, 1751541605] + end + + it "decodes the remaining ints when passed the '*' modifier after another directive" do + "abcdefgh".unpack(unpack_format()+unpack_format('*')).should == [1684234849, 1751606885] + end + + it "does not decode an int when fewer bytes than an int remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["abcde", [1684234849, nil, nil]], + ["abcdefg", [1684234849, nil, nil]], + ["abcdefgh", [1684234849, 1751606885, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "abcdefgh".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + end + + it "ignores spaces between directives" do + "abcdefgh".unpack(unpack_format(' ', 2)).should == [1684234849, 1751606885] + end +end + +describe :string_unpack_32bit_le_signed, shared: true do + it "decodes an int with most significant bit set as a negative number" do + "\x00\xaa\x00\xff".unpack(unpack_format()).should == [-16733696] + end +end + +describe :string_unpack_32bit_le_unsigned, shared: true do + it "decodes an int with most significant bit set as a positive number" do + "\x00\xaa\x00\xff".unpack(unpack_format()).should == [4278233600] + end +end + +describe :string_unpack_32bit_be, shared: true do + it "decodes one int for a single format character" do + "dcba".unpack(unpack_format).should == [1684234849] + end + + it "decodes two ints for two format characters" do + "hgbadcfe".unpack(unpack_format(nil, 2)).should == [1751605857, 1684235877] + end + + it "decodes the number of ints requested by the count modifier" do + "ecbahgfd".unpack(unpack_format(2)).should == [1701012065, 1751606884] + end + + it "decodes the remaining ints when passed the '*' modifier" do + "dbcahfge".unpack(unpack_format('*')).should == [1684169569, 1751541605] + end + + it "decodes the remaining ints when passed the '*' modifier after another directive" do + "dcbahgfe".unpack(unpack_format()+unpack_format('*')).should == [1684234849, 1751606885] + end + + it "does not decode an int when fewer bytes than an int remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["dcbae", [1684234849, nil, nil]], + ["dcbaefg", [1684234849, nil, nil]], + ["dcbahgfe", [1684234849, 1751606885, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end + + it "ignores NULL bytes between directives" do + "dcbahgfe".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + end + + it "ignores spaces between directives" do + "dcbahgfe".unpack(unpack_format(' ', 2)).should == [1684234849, 1751606885] + end +end + +describe :string_unpack_32bit_be_signed, shared: true do + it "decodes an int with most significant bit set as a negative number" do + "\xff\x00\xaa\x00".unpack(unpack_format()).should == [-16733696] + end +end + +describe :string_unpack_32bit_be_unsigned, shared: true do + it "decodes an int with most significant bit set as a positive number" do + "\xff\x00\xaa\x00".unpack(unpack_format()).should == [4278233600] + end +end + +describe :string_unpack_64bit_le, shared: true do + it "decodes one long for a single format character" do + "abcdefgh".unpack(unpack_format).should == [7523094288207667809] + end + + it "decodes two longs for two format characters" do + array = "abghefcdghefabcd".unpack(unpack_format(nil, 2)) + array.should == [7233738012216484449, 7233733596956420199] + end + + it "decodes the number of longs requested by the count modifier" do + array = "abcedfghefcdghef".unpack(unpack_format(2)) + array.should == [7523094283929477729, 7378418357791581797] + end + + it "decodes the remaining longs when passed the '*' modifier" do + array = "acbdegfhdegfhacb".unpack(unpack_format('*')) + array.should == [7522813912742519649, 7089617339433837924] + end + + it "decodes the remaining longs when passed the '*' modifier after another directive" do + array = "bcahfgedhfgedbca".unpack(unpack_format()+unpack_format('*')) + array.should == [7234302065976107874, 7017560827710891624] + end + + it "does not decode a long when fewer bytes than a long remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "ignores NULL bytes between directives" do + array = "abcdefghabghefcd".unpack(unpack_format("\000", 2)) + array.should == [7523094288207667809, 7233738012216484449] + end + + it "ignores spaces between directives" do + array = "abcdefghabghefcd".unpack(unpack_format(' ', 2)) + array.should == [7523094288207667809, 7233738012216484449] + end +end + +describe :string_unpack_64bit_le_extra, shared: true do + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["abcdefgh", [7523094288207667809, nil, nil]], + ["abcdefghcdefab", [7523094288207667809, nil, nil]], + ["abcdefghcdefabde", [7523094288207667809, 7306072665971057763, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end +end + +describe :string_unpack_64bit_le_signed, shared: true do + it "decodes a long with most significant bit set as a negative number" do + "\x00\xcc\x00\xbb\x00\xaa\x00\xff".unpack(unpack_format()).should == [-71870673923814400] + end +end + +describe :string_unpack_64bit_le_unsigned, shared: true do + it "decodes a long with most significant bit set as a positive number" do + "\x00\xcc\x00\xbb\x00\xaa\x00\xff".unpack(unpack_format()).should == [18374873399785737216] + end +end + +describe :string_unpack_64bit_be, shared: true do + it "decodes one long for a single format character" do + "hgfedcba".unpack(unpack_format).should == [7523094288207667809] + end + + it "decodes two longs for two format characters" do + array = "dcfehgbadcbafehg".unpack(unpack_format(nil, 2)) + array.should == [7233738012216484449, 7233733596956420199] + end + + it "decodes the number of longs requested by the count modifier" do + array = "hgfdecbafehgdcfe".unpack(unpack_format(2)) + array.should == [7523094283929477729, 7378418357791581797] + end + + it "decodes the remaining longs when passed the '*' modifier" do + array = "hfgedbcabcahfged".unpack(unpack_format('*')) + array.should == [7522813912742519649, 7089617339433837924] + end + + it "decodes the remaining longs when passed the '*' modifier after another directive" do + array = "degfhacbacbdegfh".unpack(unpack_format()+unpack_format('*')) + array.should == [7234302065976107874, 7017560827710891624] + end + + it "does not decode a long when fewer bytes than a long remain and the '*' modifier is passed" do + "abc".unpack(unpack_format('*')).should == [] + end + + it "ignores NULL bytes between directives" do + array = "hgfedcbadcfehgba".unpack(unpack_format("\000", 2)) + array.should == [7523094288207667809, 7233738012216484449] + end + + it "ignores spaces between directives" do + array = "hgfedcbadcfehgba".unpack(unpack_format(' ', 2)) + array.should == [7523094288207667809, 7233738012216484449] + end +end + +describe :string_unpack_64bit_be_extra, shared: true do + it "adds nil for each element requested beyond the end of the String" do + [ ["", [nil, nil, nil]], + ["hgfedcba", [7523094288207667809, nil, nil]], + ["hgfedcbacdefab", [7523094288207667809, nil, nil]], + ["hgfedcbaedbafedc", [7523094288207667809, 7306072665971057763, nil]] + ].should be_computed_by(:unpack, unpack_format(3)) + end +end + +describe :string_unpack_64bit_be_signed, shared: true do + it "decodes a long with most significant bit set as a negative number" do + "\xff\x00\xaa\x00\xbb\x00\xcc\x00".unpack(unpack_format()).should == [-71870673923814400] + end +end + +describe :string_unpack_64bit_be_unsigned, shared: true do + it "decodes a long with most significant bit set as a positive number" do + "\xff\x00\xaa\x00\xbb\x00\xcc\x00".unpack(unpack_format()).should == [18374873399785737216] + end +end diff --git a/spec/rubyspec/core/string/unpack/shared/string.rb b/spec/rubyspec/core/string/unpack/shared/string.rb new file mode 100644 index 0000000000..9d85eedf26 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/shared/string.rb @@ -0,0 +1,51 @@ +describe :string_unpack_string, shared: true do + it "returns an empty string if the input is empty" do + "".unpack(unpack_format).should == [""] + end + + it "returns empty strings for repeated formats if the input is empty" do + "".unpack(unpack_format(nil, 3)).should == ["", "", ""] + end + + it "returns an empty string and does not decode any bytes when the count modifier is zero" do + "abc".unpack(unpack_format(0)+unpack_format).should == ["", "a"] + end + + it "implicitly has a count of one when no count is specified" do + "abc".unpack(unpack_format).should == ["a"] + end + + it "decodes the number of bytes specified by the count modifier" do + "abc".unpack(unpack_format(3)).should == ["abc"] + end + + it "decodes the number of bytes specified by the count modifier including whitespace bytes" do + [ ["a bc", ["a b", "c"]], + ["a\fbc", ["a\fb", "c"]], + ["a\nbc", ["a\nb", "c"]], + ["a\rbc", ["a\rb", "c"]], + ["a\tbc", ["a\tb", "c"]], + ["a\vbc", ["a\vb", "c"]] + ].should be_computed_by(:unpack, unpack_format(3)+unpack_format) + end + + it "decodes past whitespace bytes when passed the '*' modifier" do + [ ["a b c", ["a b c"]], + ["a\fb c", ["a\fb c"]], + ["a\nb c", ["a\nb c"]], + ["a\rb c", ["a\rb c"]], + ["a\tb c", ["a\tb c"]], + ["a\vb c", ["a\vb c"]], + ].should be_computed_by(:unpack, unpack_format("*")) + end +end + +describe :string_unpack_Aa, shared: true do + it "decodes the number of bytes specified by the count modifier including NULL bytes" do + "a\x00bc".unpack(unpack_format(3)+unpack_format).should == ["a\x00b", "c"] + end + + it "decodes past NULL bytes when passed the '*' modifier" do + "a\x00b c".unpack(unpack_format("*")).should == ["a\x00b c"] + end +end diff --git a/spec/rubyspec/core/string/unpack/shared/unicode.rb b/spec/rubyspec/core/string/unpack/shared/unicode.rb new file mode 100644 index 0000000000..a2b4e142b2 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/shared/unicode.rb @@ -0,0 +1,60 @@ +# -*- encoding: utf-8 -*- + +describe :string_unpack_unicode, shared: true do + it "decodes Unicode codepoints as ASCII values" do + [ ["\x00", [0]], + ["\x01", [1]], + ["\x08", [8]], + ["\x0f", [15]], + ["\x18", [24]], + ["\x1f", [31]], + ["\x7f", [127]], + ["\xc2\x80", [128]], + ["\xc2\x81", [129]], + ["\xc3\xbf", [255]] + ].should be_computed_by(:unpack, "U") + end + + it "decodes the number of characters specified by the count modifier" do + [ ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U1", [0x80]], + ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U2", [0x80, 0x81]], + ["\xc2\x80\xc2\x81\xc2\x82\xc2\x83", "U3", [0x80, 0x81, 0x82]] + ].should be_computed_by(:unpack) + end + + it "implicitly has a count of one when no count modifier is passed" do + "\xc2\x80\xc2\x81\xc2\x82\xc2\x83".unpack("U1").should == [0x80] + end + + it "decodes all remaining characters when passed the '*' modifier" do + "\xc2\x80\xc2\x81\xc2\x82\xc2\x83".unpack("U*").should == [0x80, 0x81, 0x82, 0x83] + end + + it "decodes UTF-8 BMP codepoints" do + [ ["\xc2\x80", [0x80]], + ["\xdf\xbf", [0x7ff]], + ["\xe0\xa0\x80", [0x800]], + ["\xef\xbf\xbf", [0xffff]] + ].should be_computed_by(:unpack, "U") + end + + it "decodes UTF-8 max codepoints" do + [ ["\xf0\x90\x80\x80", [0x10000]], + ["\xf3\xbf\xbf\xbf", [0xfffff]], + ["\xf4\x80\x80\x80", [0x100000]], + ["\xf4\x8f\xbf\xbf", [0x10ffff]] + ].should be_computed_by(:unpack, "U") + end + + it "does not decode any items for directives exceeding the input string size" do + "\xc2\x80".unpack("UUUU").should == [0x80] + end + + it "ignores NULL bytes between directives" do + "\x01\x02".unpack("U\x00U").should == [1, 2] + end + + it "ignores spaces between directives" do + "\x01\x02".unpack("U U").should == [1, 2] + end +end diff --git a/spec/rubyspec/core/string/unpack/u_spec.rb b/spec/rubyspec/core/string/unpack/u_spec.rb new file mode 100644 index 0000000000..0765da8d96 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/u_spec.rb @@ -0,0 +1,94 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/unicode', __FILE__) + +describe "String#unpack with format 'U'" do + it_behaves_like :string_unpack_basic, 'U' + it_behaves_like :string_unpack_no_platform, 'U' + it_behaves_like :string_unpack_unicode, 'U' + + it "raises ArgumentError on a malformed byte sequence" do + lambda { "\xE3".unpack('U') }.should raise_error(ArgumentError) + end + + it "raises ArgumentError on a malformed byte sequence and doesn't continue when used with the * modifier" do + lambda { "\xE3".unpack('U*') }.should raise_error(ArgumentError) + end +end + +describe "String#unpack with format 'u'" do + it_behaves_like :string_unpack_basic, 'u' + it_behaves_like :string_unpack_no_platform, 'u' + + it "decodes an empty string as an empty string" do + "".unpack("u").should == [""] + end + + it "decodes into raw (ascii) string values" do + str = "".unpack("u")[0] + str.encoding.name.should == 'ASCII-8BIT' + + str = "1".force_encoding('UTF-8').unpack("u")[0] + str.encoding.name.should == 'ASCII-8BIT' + end + + it "decodes the complete string ignoring newlines when given a single directive" do + "#86)C\n#1$5&\n".unpack("u").should == ["abcDEF"] + end + + it "appends empty string to the array for directives exceeding the input size" do + "#86)C\n#1$5&\n".unpack("uuu").should == ["abcDEF", "", ""] + end + + it "ignores the count or '*' modifier and decodes the entire string" do + [ ["#86)C\n#1$5&\n", "u238", ["abcDEF"]], + ["#86)C\n#1$5&\n", "u*", ["abcDEF"]] + ].should be_computed_by(:unpack) + end + + it "decodes all ascii characters" do + [ ["'``$\"`P0%!@``\n", ["\x00\x01\x02\x03\x04\x05\x06"]], + ["'!P@)\"@L,#0``\n", ["\a\b\t\n\v\f\r"]], + [")\#@\\0$1(3%!46\n", ["\x0E\x0F\x10\x11\x12\x13\x14\x15\x16"]], + [")%Q@9&AL<'1X?\n", ["\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f"]], + ["/(2(C)\"4F)R@I*BLL+2XO\n", ["!\"\#$%&'()*+,-./"]], + ["*,\#$R,S0U-C?@"]], + [":04)#1$5&1TA)2DM,34Y/4%%24U155E=865H`\n", ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]], + ["&6UQ=7E]@\n", ["[\\]^_`"]], + [":86)C9&5F9VAI:FML;6YO<'%R7H`\n", ["abcdefghijklmnopqrstuvwxyz"]], + ["$>WQ]?@``\n", ["{|}~"]], + [")?\\*`PH'\"@L*#\n", ["\x7f\xc2\x80\xc2\x81\xc2\x82\xc2\x83"]], + [")PH3\"A<*&PH?\"\n", ["\xc2\x84\xc2\x85\xc2\x86\xc2\x87\xc2"]], + [")B,*)PHK\"B\\*,\n", ["\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c"]], + [")PHW\"CL*/PI#\"\n", ["\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2"]], + [")D<*2PI/\"E,*5\n", ["\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95"]], + [")PI;\"E\\*8PIG\"\n", ["\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2"]], + [")FL*;PIS\"G<*>\n", ["\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e"]], + [")PI_\"H,*APJ+\"\n", ["\xc2\x9f\xc2\xa0\xc2\xa1\xc2\xa2\xc2"]], + [")H\\*DPJ7\"IL*G\n", ["\xa3\xc2\xa4\xc2\xa5\xc2\xa6\xc2\xa7"]], + [")PJC\"J<*JPJO\"\n", ["\xc2\xa8\xc2\xa9\xc2\xaa\xc2\xab\xc2"]], + [")K,*MPJ[\"K\\*P\n", ["\xac\xc2\xad\xc2\xae\xc2\xaf\xc2\xb0"]], + [")PK'\"LL*SPK3\"\n", ["\xc2\xb1\xc2\xb2\xc2\xb3\xc2\xb4\xc2"]], + [")M<*VPK?\"N,*Y\n", ["\xb5\xc2\xb6\xc2\xb7\xc2\xb8\xc2\xb9"]], + [")PKK\"N\\*\\PKW\"\n", ["\xc2\xba\xc2\xbb\xc2\xbc\xc2\xbd\xc2"]], + [")OL*_PX#\#@<.\"\n", ["\xbe\xc2\xbf\xc3\x80\xc3\x81\xc3\x82"]], + [")PX/#A,.%PX;#\n", ["\xc3\x83\xc3\x84\xc3\x85\xc3\x86\xc3"]], + [")A\\.(PXG#BL.+\n", ["\x87\xc3\x88\xc3\x89\xc3\x8a\xc3\x8b"]], + [")PXS#C<..PX_#\n", ["\xc3\x8c\xc3\x8d\xc3\x8e\xc3\x8f\xc3"]], + [")D,.1PY+#D\\.4\n", ["\x90\xc3\x91\xc3\x92\xc3\x93\xc3\x94"]], + [")PY7#EL.7PYC#\n", ["\xc3\x95\xc3\x96\xc3\x97\xc3\x98\xc3"]], + [")F<.:PYO#G,.=\n", ["\x99\xc3\x9a\xc3\x9b\xc3\x9c\xc3\x9d"]], + [")PY[#G\\.@PZ'#\n", ["\xc3\x9e\xc3\x9f\xc3\xa0\xc3\xa1\xc3"]], + [")HL.CPZ3#I<.F\n", ["\xa2\xc3\xa3\xc3\xa4\xc3\xa5\xc3\xa6"]], + [")PZ?#J,.IPZK#\n", ["\xc3\xa7\xc3\xa8\xc3\xa9\xc3\xaa\xc3"]], + [")J\\.LPZW#KL.O\n", ["\xab\xc3\xac\xc3\xad\xc3\xae\xc3\xaf"]], + [")P[##L<.RP[/#\n", ["\xc3\xb0\xc3\xb1\xc3\xb2\xc3\xb3\xc3"]], + [")M,.UP[;#M\\.X\n", ["\xb4\xc3\xb5\xc3\xb6\xc3\xb7\xc3\xb8"]], + [")P[G#NL.[P[S#\n", ["\xc3\xb9\xc3\xba\xc3\xbb\xc3\xbc\xc3"]], + ["%O<.^P[\\`\n", ["\xbd\xc3\xbe\xc3\xbf"]] + ].should be_computed_by(:unpack, "u") + end +end diff --git a/spec/rubyspec/core/string/unpack/v_spec.rb b/spec/rubyspec/core/string/unpack/v_spec.rb new file mode 100644 index 0000000000..33cf23c68b --- /dev/null +++ b/spec/rubyspec/core/string/unpack/v_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/integer', __FILE__) + +describe "String#unpack with format 'V'" do + it_behaves_like :string_unpack_basic, 'V' + it_behaves_like :string_unpack_32bit_le, 'V' + it_behaves_like :string_unpack_32bit_le_unsigned, 'V' + it_behaves_like :string_unpack_no_platform, 'V' +end + +describe "String#unpack with format 'v'" do + it_behaves_like :string_unpack_basic, 'v' + it_behaves_like :string_unpack_16bit_le, 'v' + it_behaves_like :string_unpack_16bit_le_unsigned, 'v' + it_behaves_like :string_unpack_no_platform, 'v' +end diff --git a/spec/rubyspec/core/string/unpack/w_spec.rb b/spec/rubyspec/core/string/unpack/w_spec.rb new file mode 100644 index 0000000000..22f5980a46 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/w_spec.rb @@ -0,0 +1,25 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with directive 'w'" do + it_behaves_like :string_unpack_basic, 'w' + it_behaves_like :string_unpack_no_platform, 'w' + + it "decodes a BER-compressed integer" do + [ ["\x00", [0]], + ["\x01", [1]], + ["\xce\x0f", [9999]], + ["\x84\x80\x80\x80\x80\x80\x80\x80\x80\x00", [2**65]] + ].should be_computed_by(:unpack, "w") + end + + it "ignores NULL bytes between directives" do + "\x01\x02\x03".unpack("w\x00w").should == [1, 2] + end + + it "ignores spaces between directives" do + "\x01\x02\x03".unpack("w w").should == [1, 2] + end +end diff --git a/spec/rubyspec/core/string/unpack/x_spec.rb b/spec/rubyspec/core/string/unpack/x_spec.rb new file mode 100644 index 0000000000..e765472413 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/x_spec.rb @@ -0,0 +1,62 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) + +describe "String#unpack with format 'X'" do + it_behaves_like :string_unpack_basic, 'X' + it_behaves_like :string_unpack_no_platform, 'X' + + it "moves the read index back by the number of bytes specified by count" do + "\x01\x02\x03\x04".unpack("C3X2C").should == [1, 2, 3, 2] + end + + it "does not change the read index when passed a count of zero" do + "\x01\x02\x03\x04".unpack("C3X0C").should == [1, 2, 3, 4] + end + + it "implicitly has a count of one when count is not specified" do + "\x01\x02\x03\x04".unpack("C3XC").should == [1, 2, 3, 3] + end + + it "moves the read index back by the remaining bytes when passed the '*' modifier" do + "abcd".unpack("C3X*C").should == [97, 98, 99, 99] + end + + it "raises an ArgumentError when passed the '*' modifier if the remaining bytes exceed the bytes from the index to the start of the String" do + lambda { "abcd".unpack("CX*C") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the count exceeds the bytes from current index to the start of the String" do + lambda { "\x01\x02\x03\x04".unpack("C3X4C") }.should raise_error(ArgumentError) + end +end + +describe "String#unpack with format 'x'" do + it_behaves_like :string_unpack_basic, 'x' + it_behaves_like :string_unpack_no_platform, 'x' + + it "moves the read index forward by the number of bytes specified by count" do + "\x01\x02\x03\x04".unpack("Cx2C").should == [1, 4] + end + + it "implicitly has a count of one when count is not specified" do + "\x01\x02\x03\x04".unpack("CxC").should == [1, 3] + end + + it "does not change the read index when passed a count of zero" do + "\x01\x02\x03\x04".unpack("Cx0C").should == [1, 2] + end + + it "moves the read index to the end of the string when passed the '*' modifier" do + "\x01\x02\x03\x04".unpack("Cx*C").should == [1, nil] + end + + it "positions the read index one beyond the last readable byte in the String" do + "\x01\x02\x03\x04".unpack("C2x2C").should == [1, 2, nil] + end + + it "raises an ArgumentError if the count exceeds the size of the String" do + lambda { "\x01\x02\x03\x04".unpack("C2x3C") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/string/unpack/z_spec.rb b/spec/rubyspec/core/string/unpack/z_spec.rb new file mode 100644 index 0000000000..7c3d167ac2 --- /dev/null +++ b/spec/rubyspec/core/string/unpack/z_spec.rb @@ -0,0 +1,21 @@ +# -*- encoding: ascii-8bit -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/basic', __FILE__) +require File.expand_path('../shared/string', __FILE__) + +describe "String#unpack with format 'Z'" do + it_behaves_like :string_unpack_basic, 'Z' + it_behaves_like :string_unpack_no_platform, 'Z' + it_behaves_like :string_unpack_string, 'Z' + + it "stops decoding at NULL bytes when passed the '*' modifier" do + "a\x00\x00 b \x00c".unpack('Z*Z*Z*Z*').should == ["a", "", " b ", "c"] + end + + it "decodes the number of bytes specified by the count modifier and truncates the decoded string at the first NULL byte" do + [ ["a\x00 \x00b c", ["a", " "]], + ["\x00a\x00 bc \x00", ["", "c"]] + ].should be_computed_by(:unpack, "Z5Z") + end +end diff --git a/spec/rubyspec/core/string/upcase_spec.rb b/spec/rubyspec/core/string/upcase_spec.rb new file mode 100644 index 0000000000..e23a9829d9 --- /dev/null +++ b/spec/rubyspec/core/string/upcase_spec.rb @@ -0,0 +1,53 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#upcase" do + it "returns a copy of self with all lowercase letters upcased" do + "Hello".upcase.should == "HELLO" + "hello".upcase.should == "HELLO" + end + + ruby_version_is ''...'2.4' do + it "is locale insensitive (only replaces a-z)" do + "äöü".upcase.should == "äöü" + + str = Array.new(256) { |c| c.chr }.join + expected = Array.new(256) do |i| + c = i.chr + c.between?("a", "z") ? c.upcase : c + end.join + + str.upcase.should == expected + end + end + + it "taints result when self is tainted" do + "".taint.upcase.tainted?.should == true + "X".taint.upcase.tainted?.should == true + "x".taint.upcase.tainted?.should == true + end + + it "returns a subclass instance for subclasses" do + StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(StringSpecs::MyString) + end +end + +describe "String#upcase!" do + it "modifies self in place" do + a = "HeLlO" + a.upcase!.should equal(a) + a.should == "HELLO" + end + + it "returns nil if no modifications were made" do + a = "HELLO" + a.upcase!.should == nil + a.should == "HELLO" + end + + it "raises a RuntimeError when self is frozen" do + lambda { "HeLlo".freeze.upcase! }.should raise_error(RuntimeError) + lambda { "HELLO".freeze.upcase! }.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/string/uplus_spec.rb b/spec/rubyspec/core/string/uplus_spec.rb new file mode 100644 index 0000000000..eafa721903 --- /dev/null +++ b/spec/rubyspec/core/string/uplus_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.3" do + describe 'String#+@' do + it 'returns an unfrozen copy of a frozen String' do + input = 'foo'.freeze + output = +input + + output.frozen?.should == false + output.should == 'foo' + end + + it 'returns self if the String is not frozen' do + input = 'foo' + output = +input + + output.equal?(input).should == true + end + + it 'returns mutable copy despite freeze-magic-comment in file' do + ruby_exe(fixture(__FILE__, "freeze_magic_comment.rb")).should == 'mutable' + end + end +end diff --git a/spec/rubyspec/core/string/upto_spec.rb b/spec/rubyspec/core/string/upto_spec.rb new file mode 100644 index 0000000000..6b998eed3c --- /dev/null +++ b/spec/rubyspec/core/string/upto_spec.rb @@ -0,0 +1,98 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes.rb', __FILE__) + +describe "String#upto" do + it "passes successive values, starting at self and ending at other_string, to the block" do + a = [] + "*+".upto("*3") { |s| a << s } + a.should == ["*+", "*,", "*-", "*.", "*/", "*0", "*1", "*2", "*3"] + end + + it "calls the block once even when start eqals stop" do + a = [] + "abc".upto("abc") { |s| a << s } + a.should == ["abc"] + end + + it "doesn't call block with self even if self is less than stop but stop length is less than self length" do + a = [] + "25".upto("5") { |s| a << s } + a.should == [] + end + + it "doesn't call block if stop is less than self and stop length is less than self length" do + a = [] + "25".upto("1") { |s| a << s } + a.should == [] + end + + it "doesn't call the block if self is greater than stop" do + a = [] + "5".upto("2") { |s| a << s } + a.should == [] + end + + it "stops iterating as soon as the current value's character count gets higher than stop's" do + a = [] + "96".upto("AA") { |s| a << s } + a.should == ["96", "97", "98", "99"] + end + + it "returns self" do + "abc".upto("abd") { }.should == "abc" + "5".upto("2") { |i| i }.should == "5" + end + + it "tries to convert other to string using to_str" do + other = mock('abd') + def other.to_str() "abd" end + + a = [] + "abc".upto(other) { |s| a << s } + a.should == ["abc", "abd"] + end + + it "raises a TypeError if other can't be converted to a string" do + lambda { "abc".upto(123) { } }.should raise_error(TypeError) + lambda { "abc".upto(mock('x')){ } }.should raise_error(TypeError) + end + + + it "does not work with symbols" do + lambda { "a".upto(:c).to_a }.should raise_error(TypeError) + end + + it "returns non-alphabetic characters in the ASCII range for single letters" do + "9".upto("A").to_a.should == ["9", ":", ";", "<", "=", ">", "?", "@", "A"] + "Z".upto("a").to_a.should == ["Z", "[", "\\", "]", "^", "_", "`", "a"] + "z".upto("~").to_a.should == ["z", "{", "|", "}", "~"] + end + + it "stops before the last value if exclusive" do + a = [] + "a".upto("d", true) { |s| a << s} + a.should == ["a", "b", "c"] + end + + describe "on sequence of numbers" do + it "calls the block as Integer#upto" do + "8".upto("11").to_a.should == 8.upto(11).map(&:to_s) + end + end + + describe "when no block is given" do + it "returns an enumerator" do + enum = "aaa".upto("baa", true) + enum.should be_an_instance_of(Enumerator) + enum.count.should == 26**2 + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + "a".upto("b").size.should == nil + end + end + end + end +end diff --git a/spec/rubyspec/core/string/valid_encoding_spec.rb b/spec/rubyspec/core/string/valid_encoding_spec.rb new file mode 100644 index 0000000000..ddd0fe52a2 --- /dev/null +++ b/spec/rubyspec/core/string/valid_encoding_spec.rb @@ -0,0 +1,129 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :encoding do + describe "String#valid_encoding?" do + it "returns true if the String's encoding is valid" do + "a".valid_encoding?.should be_true + "\u{8365}\u{221}".valid_encoding?.should be_true + end + + it "returns true if self is valid in the current encoding and other encodings" do + str = "\x77" + str.force_encoding('utf-8').valid_encoding?.should be_true + str.force_encoding('ascii-8bit').valid_encoding?.should be_true + end + + it "returns true for all encodings self is valid in" do + str = "\u{6754}" + str.force_encoding('ASCII-8BIT').valid_encoding?.should be_true + str.force_encoding('UTF-8').valid_encoding?.should be_true + str.force_encoding('US-ASCII').valid_encoding?.should be_false + str.force_encoding('Big5').valid_encoding?.should be_false + str.force_encoding('CP949').valid_encoding?.should be_false + str.force_encoding('Emacs-Mule').valid_encoding?.should be_false + str.force_encoding('EUC-JP').valid_encoding?.should be_false + str.force_encoding('EUC-KR').valid_encoding?.should be_false + str.force_encoding('EUC-TW').valid_encoding?.should be_false + str.force_encoding('GB18030').valid_encoding?.should be_false + str.force_encoding('GBK').valid_encoding?.should be_false + str.force_encoding('ISO-8859-1').valid_encoding?.should be_true + str.force_encoding('ISO-8859-2').valid_encoding?.should be_true + str.force_encoding('ISO-8859-3').valid_encoding?.should be_true + str.force_encoding('ISO-8859-4').valid_encoding?.should be_true + str.force_encoding('ISO-8859-5').valid_encoding?.should be_true + str.force_encoding('ISO-8859-6').valid_encoding?.should be_true + str.force_encoding('ISO-8859-7').valid_encoding?.should be_true + str.force_encoding('ISO-8859-8').valid_encoding?.should be_true + str.force_encoding('ISO-8859-9').valid_encoding?.should be_true + str.force_encoding('ISO-8859-10').valid_encoding?.should be_true + str.force_encoding('ISO-8859-11').valid_encoding?.should be_true + str.force_encoding('ISO-8859-13').valid_encoding?.should be_true + str.force_encoding('ISO-8859-14').valid_encoding?.should be_true + str.force_encoding('ISO-8859-15').valid_encoding?.should be_true + str.force_encoding('ISO-8859-16').valid_encoding?.should be_true + str.force_encoding('KOI8-R').valid_encoding?.should be_true + str.force_encoding('KOI8-U').valid_encoding?.should be_true + str.force_encoding('Shift_JIS').valid_encoding?.should be_false + str.force_encoding('UTF-16BE').valid_encoding?.should be_false + str.force_encoding('UTF-16LE').valid_encoding?.should be_false + str.force_encoding('UTF-32BE').valid_encoding?.should be_false + str.force_encoding('UTF-32LE').valid_encoding?.should be_false + str.force_encoding('Windows-1251').valid_encoding?.should be_true + str.force_encoding('IBM437').valid_encoding?.should be_true + str.force_encoding('IBM737').valid_encoding?.should be_true + str.force_encoding('IBM775').valid_encoding?.should be_true + str.force_encoding('CP850').valid_encoding?.should be_true + str.force_encoding('IBM852').valid_encoding?.should be_true + str.force_encoding('CP852').valid_encoding?.should be_true + str.force_encoding('IBM855').valid_encoding?.should be_true + str.force_encoding('CP855').valid_encoding?.should be_true + str.force_encoding('IBM857').valid_encoding?.should be_true + str.force_encoding('IBM860').valid_encoding?.should be_true + str.force_encoding('IBM861').valid_encoding?.should be_true + str.force_encoding('IBM862').valid_encoding?.should be_true + str.force_encoding('IBM863').valid_encoding?.should be_true + str.force_encoding('IBM864').valid_encoding?.should be_true + str.force_encoding('IBM865').valid_encoding?.should be_true + str.force_encoding('IBM866').valid_encoding?.should be_true + str.force_encoding('IBM869').valid_encoding?.should be_true + str.force_encoding('Windows-1258').valid_encoding?.should be_true + str.force_encoding('GB1988').valid_encoding?.should be_true + str.force_encoding('macCentEuro').valid_encoding?.should be_true + str.force_encoding('macCroatian').valid_encoding?.should be_true + str.force_encoding('macCyrillic').valid_encoding?.should be_true + str.force_encoding('macGreek').valid_encoding?.should be_true + str.force_encoding('macIceland').valid_encoding?.should be_true + str.force_encoding('macRoman').valid_encoding?.should be_true + str.force_encoding('macRomania').valid_encoding?.should be_true + str.force_encoding('macThai').valid_encoding?.should be_true + str.force_encoding('macTurkish').valid_encoding?.should be_true + str.force_encoding('macUkraine').valid_encoding?.should be_true + str.force_encoding('stateless-ISO-2022-JP').valid_encoding?.should be_false + str.force_encoding('eucJP-ms').valid_encoding?.should be_false + str.force_encoding('CP51932').valid_encoding?.should be_false + str.force_encoding('GB2312').valid_encoding?.should be_false + str.force_encoding('GB12345').valid_encoding?.should be_false + str.force_encoding('ISO-2022-JP').valid_encoding?.should be_true + str.force_encoding('ISO-2022-JP-2').valid_encoding?.should be_true + str.force_encoding('CP50221').valid_encoding?.should be_true + str.force_encoding('Windows-1252').valid_encoding?.should be_true + str.force_encoding('Windows-1250').valid_encoding?.should be_true + str.force_encoding('Windows-1256').valid_encoding?.should be_true + str.force_encoding('Windows-1253').valid_encoding?.should be_true + str.force_encoding('Windows-1255').valid_encoding?.should be_true + str.force_encoding('Windows-1254').valid_encoding?.should be_true + str.force_encoding('TIS-620').valid_encoding?.should be_true + str.force_encoding('Windows-874').valid_encoding?.should be_true + str.force_encoding('Windows-1257').valid_encoding?.should be_true + str.force_encoding('Windows-31J').valid_encoding?.should be_false + str.force_encoding('MacJapanese').valid_encoding?.should be_false + str.force_encoding('UTF-7').valid_encoding?.should be_true + str.force_encoding('UTF8-MAC').valid_encoding?.should be_true + end + + it "returns false if self is valid in one encoding, but invalid in the one it's tagged with" do + str = "\u{8765}" + str.valid_encoding?.should be_true + str = str.force_encoding('ascii') + str.valid_encoding?.should be_false + end + + it "returns false if self contains a character invalid in the associated encoding" do + "abc#{[0x80].pack('C')}".force_encoding('ascii').valid_encoding?.should be_false + end + + it "returns false if a valid String had an invalid character appended to it" do + str = "a" + str.valid_encoding?.should be_true + str << [0xDD].pack('C').force_encoding('utf-8') + str.valid_encoding?.should be_false + end + + it "returns true if an invalid string is appended another invalid one but both make a valid string" do + str = [0xD0].pack('C').force_encoding('utf-8') + str.valid_encoding?.should be_false + str << [0xBF].pack('C').force_encoding('utf-8') + str.valid_encoding?.should be_true + end + end +end diff --git a/spec/rubyspec/core/struct/dig_spec.rb b/spec/rubyspec/core/struct/dig_spec.rb new file mode 100644 index 0000000000..c222eec9e4 --- /dev/null +++ b/spec/rubyspec/core/struct/dig_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is "2.3" do + describe "Struct#dig" do + before(:each) do + @klass = Struct.new(:a) + @instance = @klass.new(@klass.new({ b: [1, 2, 3] })) + end + + it "returns the nested value specified by the sequence of keys" do + @instance.dig(:a, :a).should == { b: [1, 2, 3] } + end + + it "returns the nested value specified if the sequence includes an index" do + @instance.dig(:a, :a, :b, 0).should == 1 + end + + it "returns nil if any intermediate step is nil" do + @instance.dig(:b, 0).should == nil + end + + it "raises a TypeError if any intermediate step does not respond to #dig" do + instance = @klass.new(1) + lambda { + instance.dig(:a, 3) + }.should raise_error(TypeError) + end + + it "raises an ArgumentError if no arguments provided" do + lambda { @instance.dig }.should raise_error(ArgumentError) + end + + it "calls #dig on any intermediate step with the rest of the sequence as arguments" do + obj = Object.new + instance = @klass.new(obj) + + def obj.dig(*args) + {dug: args} + end + + instance.dig(:a, :bar, :baz).should == { dug: [:bar, :baz] } + end + end +end diff --git a/spec/rubyspec/core/struct/dup_spec.rb b/spec/rubyspec/core/struct/dup_spec.rb new file mode 100644 index 0000000000..d1da31d6d5 --- /dev/null +++ b/spec/rubyspec/core/struct/dup_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct-based class#dup" do + + # From https://github.com/jruby/jruby/issues/3686 + it "retains an included module in the ancestor chain for the struct's singleton class" do + klass = Struct.new(:foo) + mod = Module.new do + def hello + "hello" + end + end + + klass.extend(mod) + klass_dup = klass.dup + klass_dup.hello.should == "hello" + end + +end diff --git a/spec/rubyspec/core/struct/each_pair_spec.rb b/spec/rubyspec/core/struct/each_pair_spec.rb new file mode 100644 index 0000000000..79a962a6ad --- /dev/null +++ b/spec/rubyspec/core/struct/each_pair_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Struct#each_pair" do + before :each do + @car = StructClasses::Car.new('Ford', 'Ranger', 2001) + end + + it "passes each key value pair to the given block" do + @car.each_pair do |key, value| + value.should == @car[key] + end + end + + context "with a block variable" do + it "passes an array to the given block" do + @car.each_pair.map { |var| var }.should == StructClasses::Car.members.zip(@car.values) + end + end + + it "returns self if passed a block" do + @car.each_pair {}.should equal(@car) + end + + it "returns an Enumerator if not passed a block" do + @car.each_pair.should be_an_instance_of(Enumerator) + end + + it_behaves_like :struct_accessor, :each_pair + it_behaves_like :enumeratorized_with_origin_size, :each_pair, StructClasses::Car.new('Ford', 'Ranger') +end diff --git a/spec/rubyspec/core/struct/each_spec.rb b/spec/rubyspec/core/struct/each_spec.rb new file mode 100644 index 0000000000..86302d91c6 --- /dev/null +++ b/spec/rubyspec/core/struct/each_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Struct#each" do + it "passes each value to the given block" do + car = StructClasses::Car.new('Ford', 'Ranger') + i = -1 + car.each do |value| + value.should == car[i += 1] + end + end + + it "returns self if passed a block" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.each {}.should == car + end + + it "returns an Enumerator if not passed a block" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.each.should be_an_instance_of(Enumerator) + end + + it_behaves_like :struct_accessor, :each + it_behaves_like :enumeratorized_with_origin_size, :each, StructClasses::Car.new('Ford', 'Ranger') +end diff --git a/spec/rubyspec/core/struct/element_reference_spec.rb b/spec/rubyspec/core/struct/element_reference_spec.rb new file mode 100644 index 0000000000..dc51fbfff1 --- /dev/null +++ b/spec/rubyspec/core/struct/element_reference_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct[]" do + it "is a synonym for new" do + StructClasses::Ruby['2.0', 'i686'].should be_kind_of(StructClasses::Ruby) + end +end + +describe "Struct#[]" do + it "returns the attribute referenced" do + car = StructClasses::Car.new('Ford', 'Ranger', 1983) + car['make'].should == 'Ford' + car['model'].should == 'Ranger' + car['year'].should == 1983 + car[:make].should == 'Ford' + car[:model].should == 'Ranger' + car[:year].should == 1983 + car[0].should == 'Ford' + car[1].should == 'Ranger' + car[2].should == 1983 + car[-3].should == 'Ford' + car[-2].should == 'Ranger' + car[-1].should == 1983 + end + + it "fails when it does not know about the requested attribute" do + car = StructClasses::Car.new('Ford', 'Ranger') + lambda { car[3] }.should raise_error(IndexError) + lambda { car[-4] }.should raise_error(IndexError) + lambda { car[:body] }.should raise_error(NameError) + lambda { car['wheels'] }.should raise_error(NameError) + end + + it "fails if passed too many arguments" do + car = StructClasses::Car.new('Ford', 'Ranger') + lambda { car[:make, :model] }.should raise_error(ArgumentError) + end + + it "fails if not passed a string, symbol, or integer" do + car = StructClasses::Car.new('Ford', 'Ranger') + lambda { car[Object.new] }.should raise_error(TypeError) + end + + it "returns attribute names that contain hyphens" do + klass = Struct.new(:'current-state') + tuple = klass.new(0) + tuple['current-state'].should == 0 + tuple[:'current-state'].should == 0 + tuple[0].should == 0 + end +end diff --git a/spec/rubyspec/core/struct/element_set_spec.rb b/spec/rubyspec/core/struct/element_set_spec.rb new file mode 100644 index 0000000000..3d482bdb71 --- /dev/null +++ b/spec/rubyspec/core/struct/element_set_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct#[]=" do + it "assigns the passed value" do + car = StructClasses::Car.new('Ford', 'Ranger') + + car[:model] = 'Escape' + car[:model].should == 'Escape' + + car['model'] = 'Fusion' + car[:model].should == 'Fusion' + + car[1] = 'Excursion' + car[:model].should == 'Excursion' + + car[-1] = '2000-2005' + car[:year].should == '2000-2005' + end + + it "fails when trying to assign attributes which don't exist" do + car = StructClasses::Car.new('Ford', 'Ranger') + + lambda { car[:something] = true }.should raise_error(NameError) + lambda { car[3] = true }.should raise_error(IndexError) + lambda { car[-4] = true }.should raise_error(IndexError) + lambda { car[Object.new] = true }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/struct/eql_spec.rb b/spec/rubyspec/core/struct/eql_spec.rb new file mode 100644 index 0000000000..dfa97811c6 --- /dev/null +++ b/spec/rubyspec/core/struct/eql_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Struct#eql?" do + it_behaves_like(:struct_equal_value, :eql?) + + it "returns false if any corresponding elements are not #eql?" do + car = StructClasses::Car.new("Honda", "Accord", 1998) + similar_car = StructClasses::Car.new("Honda", "Accord", 1998.0) + car.send(@method, similar_car).should be_false + end +end diff --git a/spec/rubyspec/core/struct/equal_value_spec.rb b/spec/rubyspec/core/struct/equal_value_spec.rb new file mode 100644 index 0000000000..a343213417 --- /dev/null +++ b/spec/rubyspec/core/struct/equal_value_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) + +describe "Struct#==" do + it_behaves_like(:struct_equal_value, :==) +end diff --git a/spec/rubyspec/core/struct/fixtures/classes.rb b/spec/rubyspec/core/struct/fixtures/classes.rb new file mode 100644 index 0000000000..6d620f9060 --- /dev/null +++ b/spec/rubyspec/core/struct/fixtures/classes.rb @@ -0,0 +1,26 @@ +module StructClasses + + class Apple < Struct; end + + Ruby = Struct.new(:version, :platform) + + Car = Struct.new(:make, :model, :year) + + class Honda < Car + def initialize(*args) + self.make = "Honda" + super(*args) + end + end + + class SubclassX < Struct + end + + class SubclassX + attr_reader :key + def initialize(*) + @key = :value + super + end + end +end diff --git a/spec/rubyspec/core/struct/hash_spec.rb b/spec/rubyspec/core/struct/hash_spec.rb new file mode 100644 index 0000000000..517d3ab44e --- /dev/null +++ b/spec/rubyspec/core/struct/hash_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) + +describe "Struct#hash" do + + it "returns the same fixnum for structs with the same content" do + [StructClasses::Ruby.new("1.8.6", "PPC"), + StructClasses::Car.new("Hugo", "Foo", "1972")].each do |stc| + stc.hash.should == stc.dup.hash + stc.hash.should be_kind_of(Fixnum) + end + end + + it "returns the same value if structs are #eql?" do + car = StructClasses::Car.new("Honda", "Accord", "1998") + similar_car = StructClasses::Car.new("Honda", "Accord", "1998") + car.should eql(similar_car) + car.hash.should == similar_car.hash + end + + it "allows for overriding methods in an included module" do + mod = Module.new do + def hash + "different" + end + end + s = Struct.new(:arg) do + include mod + end + s.new.hash.should == "different" + end + + it "returns the same hash for recursive structs" do + car = StructClasses::Car.new("Honda", "Accord", "1998") + similar_car = StructClasses::Car.new("Honda", "Accord", "1998") + car[:make] = car + similar_car[:make] = car + car.hash.should == similar_car.hash + # This is because car.eql?(similar_car). + # Objects that are eql? must return the same hash. + # See the Struct#eql? specs + end + + it_behaves_like :struct_accessor, :hash +end diff --git a/spec/rubyspec/core/struct/initialize_spec.rb b/spec/rubyspec/core/struct/initialize_spec.rb new file mode 100644 index 0000000000..59fc5ef9aa --- /dev/null +++ b/spec/rubyspec/core/struct/initialize_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct#initialize" do + + it "is private" do + StructClasses::Car.should have_private_instance_method(:initialize) + end + + it 'allows valid Ruby method names for members' do + valid_method_names = [ + :method1, + :method_1, + :method_1?, + :method_1!, + :a_method + ] + valid_method_names.each do |method_name| + klass = Struct.new(method_name) + instance = klass.new(:value) + instance.send(method_name).should == :value + writer_method = "#{method_name}=".to_sym + result = instance.send(writer_method, :new_value) + result.should == :new_value + instance.send(method_name).should == :new_value + end + end + + it "does nothing when passed a set of fields equal to self" do + car = same_car = StructClasses::Car.new("Honda", "Accord", "1998") + car.instance_eval { initialize("Honda", "Accord", "1998") } + car.should == same_car + end + + it "explicitly sets instance variables to nil when args not provided to initialize" do + car = StructClasses::Honda.new + car.make.should == nil # still nil despite override in Honda#initialize b/c of super order + end + + it "can be overriden" do + StructClasses::SubclassX.new(:y).new.key.should == :value + end +end diff --git a/spec/rubyspec/core/struct/inspect_spec.rb b/spec/rubyspec/core/struct/inspect_spec.rb new file mode 100644 index 0000000000..a85466e1a2 --- /dev/null +++ b/spec/rubyspec/core/struct/inspect_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "Struct#inspect" do + it "returns a string representation of some kind" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.inspect.should == '#' + # ujihisa reported in http://rubyspec.org/issues/show/130 that the + # following example failed under mspec. Prefixing 'Whiskey' with a double + # colon causes it to work. Given that this is an mspec bug, as opposed to + # a problem with a spec, I've used the workaround below. + ::Whiskey = Struct.new(:name, :ounces) + ::Whiskey.new('Jack', 100).inspect.should == '#' + end + + it_behaves_like(:struct_inspect, :inspect) +end diff --git a/spec/rubyspec/core/struct/instance_variables_spec.rb b/spec/rubyspec/core/struct/instance_variables_spec.rb new file mode 100644 index 0000000000..3abb8578a5 --- /dev/null +++ b/spec/rubyspec/core/struct/instance_variables_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct#instance_variables" do + it "returns an empty array if only attributes are defined" do + car = StructClasses::Car.new("Hugo", "Foo", "1972") + car.instance_variables.should == [] + end + + it "returns an array with one name if an instance variable is added" do + car = StructClasses::Car.new("Hugo", "Foo", "1972") + car.instance_variables.should == [] + car.instance_variable_set("@test", 1) + car.instance_variables.should == [:@test] + end +end diff --git a/spec/rubyspec/core/struct/length_spec.rb b/spec/rubyspec/core/struct/length_spec.rb new file mode 100644 index 0000000000..067bb08f88 --- /dev/null +++ b/spec/rubyspec/core/struct/length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) + +describe "Struct#length" do + it "returns the number of attributes" do + StructClasses::Car.new('Cadillac', 'DeVille').length.should == 3 + StructClasses::Car.new.length.should == 3 + end + + it_behaves_like :struct_accessor, :length +end diff --git a/spec/rubyspec/core/struct/members_spec.rb b/spec/rubyspec/core/struct/members_spec.rb new file mode 100644 index 0000000000..702e536a55 --- /dev/null +++ b/spec/rubyspec/core/struct/members_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) + +describe "Struct#members" do + it "returns an array of attribute names" do + StructClasses::Car.new.members.should == [:make, :model, :year] + StructClasses::Car.new('Cadillac').members.should == [:make, :model, :year] + StructClasses::Ruby.members.should == [:version, :platform] + end + + it_behaves_like :struct_accessor, :members +end diff --git a/spec/rubyspec/core/struct/new_spec.rb b/spec/rubyspec/core/struct/new_spec.rb new file mode 100644 index 0000000000..314f749955 --- /dev/null +++ b/spec/rubyspec/core/struct/new_spec.rb @@ -0,0 +1,123 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct.new" do + it "creates a constant in Struct namespace with string as first argument" do + struct = Struct.new('Animal', :name, :legs, :eyeballs) + struct.should == Struct::Animal + struct.name.should == "Struct::Animal" + end + + it "overwrites previously defined constants with string as first argument" do + first = Struct.new('Person', :height, :weight) + first.should == Struct::Person + + second = nil + lambda { + second = Struct.new('Person', :hair, :sex) + }.should complain(/redefining constant/) + second.should == Struct::Person + + first.members.should_not == second.members + end + + it "calls to_str on its first argument (constant name)" do + obj = mock('Foo') + def obj.to_str() "Foo" end + struct = Struct.new(obj) + struct.should == Struct::Foo + struct.name.should == "Struct::Foo" + end + + it "creates a new anonymous class with nil first argument" do + struct = Struct.new(nil, :foo) + struct.new("bar").foo.should == "bar" + struct.should be_kind_of(Class) + struct.name.should be_nil + end + + it "creates a new anonymous class with symbol arguments" do + struct = Struct.new(:make, :model) + struct.should be_kind_of(Class) + struct.name.should == nil + end + + it "does not create a constant with symbol as first argument" do + Struct.new(:Animal2, :name, :legs, :eyeballs) + Struct.const_defined?("Animal2").should be_false + end + + + it "fails with invalid constant name as first argument" do + lambda { Struct.new('animal', :name, :legs, :eyeballs) }.should raise_error(NameError) + end + + it "raises a TypeError if object doesn't respond to to_sym" do + lambda { Struct.new(:animal, mock('giraffe')) }.should raise_error(TypeError) + lambda { Struct.new(:animal, 1.0) }.should raise_error(TypeError) + lambda { Struct.new(:animal, Time.now) }.should raise_error(TypeError) + lambda { Struct.new(:animal, Class) }.should raise_error(TypeError) + lambda { Struct.new(:animal, nil) }.should raise_error(TypeError) + lambda { Struct.new(:animal, true) }.should raise_error(TypeError) + lambda { Struct.new(:animal, ['chris', 'evan']) }.should raise_error(TypeError) + lambda { Struct.new(:animal, { name: 'chris' }) }.should raise_error(TypeError) + end + + it "raises a TypeError if object is not a Symbol" do + obj = mock(':ruby') + def obj.to_sym() :ruby end + lambda { Struct.new(:animal, obj) }.should raise_error(TypeError) + end + + it "processes passed block with instance_eval" do + klass = Struct.new(:something) { @something_else = 'something else entirely!' } + klass.instance_variables.should include(:@something_else) + end + + context "with a block" do + it "allows class to be modified via the block" do + klass = Struct.new(:version) do + def platform + :ruby + end + end + instance = klass.new('2.2') + + instance.version.should == '2.2' + instance.platform.should == :ruby + end + + it "passes same struct class to the block" do + given = nil + klass = Struct.new(:attr) do |block_parameter| + given = block_parameter + end + klass.should equal(given) + end + end + + context "on subclasses" do + it "creates a constant in subclass' namespace" do + struct = StructClasses::Apple.new('Computer', :size) + struct.should == StructClasses::Apple::Computer + end + + it "creates an instance" do + StructClasses::Ruby.new.kind_of?(StructClasses::Ruby).should == true + end + + it "creates reader methods" do + StructClasses::Ruby.new.should have_method(:version) + StructClasses::Ruby.new.should have_method(:platform) + end + + it "creates writer methods" do + StructClasses::Ruby.new.should have_method(:version=) + StructClasses::Ruby.new.should have_method(:platform=) + end + + it "fails with too many arguments" do + lambda { StructClasses::Ruby.new('2.0', 'i686', true) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/core/struct/select_spec.rb b/spec/rubyspec/core/struct/select_spec.rb new file mode 100644 index 0000000000..da80eea0e8 --- /dev/null +++ b/spec/rubyspec/core/struct/select_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) +require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__) + +describe "Struct#select" do + it "raises an ArgumentError if given any non-block arguments" do + lambda { StructClasses::Car.new.select(1) { } }.should raise_error(ArgumentError) + end + + it "returns a new array of elements for which block is true" do + struct = StructClasses::Car.new("Toyota", "Tercel", "2000") + struct.select { |i| i == "2000" }.should == [ "2000" ] + end + + it "returns an instance of Array" do + struct = StructClasses::Car.new("Ford", "Escort", "1995") + struct.select { true }.should be_an_instance_of(Array) + end + + describe "without block" do + it "returns an instance of Enumerator" do + struct = Struct.new(:foo).new + struct.select.should be_an_instance_of(Enumerator) + end + end + + it_behaves_like :struct_accessor, :select + it_behaves_like :enumeratorized_with_origin_size, :select, Struct.new(:foo).new +end diff --git a/spec/rubyspec/core/struct/shared/accessor.rb b/spec/rubyspec/core/struct/shared/accessor.rb new file mode 100644 index 0000000000..dbf5e78f43 --- /dev/null +++ b/spec/rubyspec/core/struct/shared/accessor.rb @@ -0,0 +1,7 @@ +describe :struct_accessor, shared: true do + it "does not override the instance accessor method" do + struct = Struct.new(@method.to_sym) + instance = struct.new 42 + instance.send(@method).should == 42 + end +end diff --git a/spec/rubyspec/core/struct/shared/equal_value.rb b/spec/rubyspec/core/struct/shared/equal_value.rb new file mode 100644 index 0000000000..711862cb44 --- /dev/null +++ b/spec/rubyspec/core/struct/shared/equal_value.rb @@ -0,0 +1,30 @@ +describe :struct_equal_value, shared: true do + it "returns true if the other is the same object" do + car = same_car = StructClasses::Car.new("Honda", "Accord", "1998") + car.send(@method, same_car).should == true + end + + it "returns true if the other has all the same fields" do + car = StructClasses::Car.new("Honda", "Accord", "1998") + similar_car = StructClasses::Car.new("Honda", "Accord", "1998") + car.send(@method, similar_car).should == true + end + + it "returns false if the other is a different object or has different fields" do + car = StructClasses::Car.new("Honda", "Accord", "1998") + different_car = StructClasses::Car.new("Honda", "Accord", "1995") + car.send(@method, different_car).should == false + end + + it "handles recursive structures by returning false if a difference can be found" do + x = StructClasses::Car.new("Honda", "Accord", "1998") + x[:make] = x + stepping = StructClasses::Car.new("Honda", "Accord", "1998") + stone = StructClasses::Car.new(stepping, "Accord", "1998") + stepping[:make] = stone + x.send(@method, stepping).should == true + + stone[:year] = "1999" # introduce a difference + x.send(@method, stepping).should == false + end +end diff --git a/spec/rubyspec/core/struct/shared/inspect.rb b/spec/rubyspec/core/struct/shared/inspect.rb new file mode 100644 index 0000000000..90594a5452 --- /dev/null +++ b/spec/rubyspec/core/struct/shared/inspect.rb @@ -0,0 +1,5 @@ +describe :struct_inspect, shared: true do + it "returns a string representation without the class name for anonymous structs" do + Struct.new(:a).new("").send(@method).should == '#' + end +end diff --git a/spec/rubyspec/core/struct/size_spec.rb b/spec/rubyspec/core/struct/size_spec.rb new file mode 100644 index 0000000000..29b1d2bfba --- /dev/null +++ b/spec/rubyspec/core/struct/size_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) + +describe "Struct#size" do + it "is a synonym for length" do + StructClasses::Car.new.size.should == StructClasses::Car.new.length + end + + it_behaves_like :struct_accessor, :size +end diff --git a/spec/rubyspec/core/struct/struct_spec.rb b/spec/rubyspec/core/struct/struct_spec.rb new file mode 100644 index 0000000000..6c1941aed5 --- /dev/null +++ b/spec/rubyspec/core/struct/struct_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct" do + it "includes Enumerable" do + Struct.include?(Enumerable).should == true + end +end + +describe "Struct anonymous class instance methods" do + it "includes Enumerable" do + StructClasses::Car.include?(Enumerable).should == true + end + + it "reader method should be a synonym for []" do + klass = Struct.new(:clock, :radio) + alarm = klass.new(true) + alarm.clock.should == alarm[:clock] + alarm.radio.should == alarm['radio'] + end + + it "reader method should not interfere with undefined methods" do + car = StructClasses::Car.new('Ford', 'Ranger') + lambda { car.something_weird }.should raise_error(NoMethodError) + end + + it "writer method be a synonym for []=" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.model.should == 'Ranger' + car.model = 'F150' + car.model.should == 'F150' + car[:model].should == 'F150' + car['model'].should == 'F150' + car[1].should == 'F150' + end +end + +describe "Struct subclasses" do + it "can be subclassed" do + compact = Class.new StructClasses::Car + compact.new.class.should == compact + end +end diff --git a/spec/rubyspec/core/struct/tms/cstime_spec.rb b/spec/rubyspec/core/struct/tms/cstime_spec.rb new file mode 100644 index 0000000000..839b02b6e9 --- /dev/null +++ b/spec/rubyspec/core/struct/tms/cstime_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Struct::Tms#cstime" do + it "needs to be reviewed for spec completeness" +end + +describe "Struct::Tms#cstime=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/struct/tms/cutime_spec.rb b/spec/rubyspec/core/struct/tms/cutime_spec.rb new file mode 100644 index 0000000000..235f378fab --- /dev/null +++ b/spec/rubyspec/core/struct/tms/cutime_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Struct::Tms#cutime" do + it "needs to be reviewed for spec completeness" +end + +describe "Struct::Tms#cutime=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/struct/tms/element_reference_spec.rb b/spec/rubyspec/core/struct/tms/element_reference_spec.rb new file mode 100644 index 0000000000..f1735341b4 --- /dev/null +++ b/spec/rubyspec/core/struct/tms/element_reference_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Struct::Tms.[]" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/struct/tms/members_spec.rb b/spec/rubyspec/core/struct/tms/members_spec.rb new file mode 100644 index 0000000000..ddfca83659 --- /dev/null +++ b/spec/rubyspec/core/struct/tms/members_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Struct::Tms.members" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/struct/tms/new_spec.rb b/spec/rubyspec/core/struct/tms/new_spec.rb new file mode 100644 index 0000000000..cf7a501aa2 --- /dev/null +++ b/spec/rubyspec/core/struct/tms/new_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Struct::Tms.new" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/struct/tms/stime_spec.rb b/spec/rubyspec/core/struct/tms/stime_spec.rb new file mode 100644 index 0000000000..f45253cf44 --- /dev/null +++ b/spec/rubyspec/core/struct/tms/stime_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Struct::Tms#stime" do + it "needs to be reviewed for spec completeness" +end + +describe "Struct::Tms#stime=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/struct/tms/utime_spec.rb b/spec/rubyspec/core/struct/tms/utime_spec.rb new file mode 100644 index 0000000000..ea6783a17b --- /dev/null +++ b/spec/rubyspec/core/struct/tms/utime_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Struct::Tms#utime" do + it "needs to be reviewed for spec completeness" +end + +describe "Struct::Tms#utime=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/struct/to_a_spec.rb b/spec/rubyspec/core/struct/to_a_spec.rb new file mode 100644 index 0000000000..f8e9a3658d --- /dev/null +++ b/spec/rubyspec/core/struct/to_a_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/accessor', __FILE__) + +describe "Struct#to_a" do + it "returns the values for this instance as an array" do + StructClasses::Car.new('Geo', 'Metro', 1995).to_a.should == ['Geo', 'Metro', 1995] + StructClasses::Car.new('Ford').to_a.should == ['Ford', nil, nil] + end + + it_behaves_like :struct_accessor, :to_a +end diff --git a/spec/rubyspec/core/struct/to_h_spec.rb b/spec/rubyspec/core/struct/to_h_spec.rb new file mode 100644 index 0000000000..6b8037f950 --- /dev/null +++ b/spec/rubyspec/core/struct/to_h_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct#to_h" do + it "returns a Hash with members as keys" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.to_h.should == {make: "Ford", model: "Ranger", year: nil} + end + + it "returns a Hash that is independent from the struct" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.to_h[:make] = 'Suzuki' + car.make.should == 'Ford' + end +end diff --git a/spec/rubyspec/core/struct/to_s_spec.rb b/spec/rubyspec/core/struct/to_s_spec.rb new file mode 100644 index 0000000000..b9fd413093 --- /dev/null +++ b/spec/rubyspec/core/struct/to_s_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "Struct#to_s" do + it "is a synonym for inspect" do + car = StructClasses::Car.new('Ford', 'Ranger') + car.inspect.should == car.to_s + end + + it_behaves_like(:struct_inspect, :to_s) +end diff --git a/spec/rubyspec/core/struct/values_at_spec.rb b/spec/rubyspec/core/struct/values_at_spec.rb new file mode 100644 index 0000000000..58016e2f2a --- /dev/null +++ b/spec/rubyspec/core/struct/values_at_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct#values_at" do + it "returns an array of values" do + clazz = Struct.new(:name, :director, :year) + movie = clazz.new('Sympathy for Mr. Vengence', 'Chan-wook Park', 2002) + movie.values_at(0, 1).should == ['Sympathy for Mr. Vengence', 'Chan-wook Park'] + movie.values_at(0..2).should == ['Sympathy for Mr. Vengence', 'Chan-wook Park', 2002] + end + + it "fails when passed unsupported types" do + car = StructClasses::Car.new('Ford', 'Ranger') + lambda { car.values_at('make') }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/struct/values_spec.rb b/spec/rubyspec/core/struct/values_spec.rb new file mode 100644 index 0000000000..0e86d33cb5 --- /dev/null +++ b/spec/rubyspec/core/struct/values_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Struct#values" do + it "is a synonym for to_a" do + car = StructClasses::Car.new('Nissan', 'Maxima') + car.values.should == car.to_a + + StructClasses::Car.new.values.should == StructClasses::Car.new.to_a + end +end diff --git a/spec/rubyspec/core/symbol/all_symbols_spec.rb b/spec/rubyspec/core/symbol/all_symbols_spec.rb new file mode 100644 index 0000000000..d18d58ba48 --- /dev/null +++ b/spec/rubyspec/core/symbol/all_symbols_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol.all_symbols" do + it "returns an array containing all the Symbols in the symbol table" do + all_symbols = Symbol.all_symbols + all_symbols.should be_an_instance_of(Array) + all_symbols.all? { |s| s.is_a?(Symbol) ? true : (p s; false) }.should == true + end + + it "returns an Array containing Symbols that have been created" do + symbol = "symbol_specs_#{rand(5_000_000)}".to_sym + Symbol.all_symbols.should include(symbol) + end +end diff --git a/spec/rubyspec/core/symbol/capitalize_spec.rb b/spec/rubyspec/core/symbol/capitalize_spec.rb new file mode 100644 index 0000000000..cf7e8a007f --- /dev/null +++ b/spec/rubyspec/core/symbol/capitalize_spec.rb @@ -0,0 +1,49 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#capitalize" do + it "returns a Symbol" do + :glark.capitalize.should be_an_instance_of(Symbol) + end + + it "converts the first character to uppercase if it is ASCII" do + :lower.capitalize.should == :Lower + end + + it "leaves the first character alone if it is not an alphabetical character" do + :"£1.20".capitalize.should == :"£1.20" + end + + ruby_version_is ''...'2.4' do + it "leaves the first character alone if it is not an alphabetical ASCII character" do + "\u{00DE}c".to_sym.capitalize.should == :"Þc" + "\u{00DF}C".to_sym.capitalize.should == :"ßc" + end + end + + it "converts subsequent uppercase ASCII characters to their lowercase equivalents" do + :lOWER.capitalize.should == :Lower + end + + it "leaves ASCII characters already in the correct case as they were" do + :Title.capitalize.should == :Title + end + + it "works with both upper- and lowercase ASCII characters in the same Symbol" do + :mIxEd.capitalize.should == :Mixed + end + + ruby_version_is ''...'2.4' do + it "leaves uppercase Unicode characters as they were" do + "a\u{00DE}c".to_sym.capitalize.should == :"AÞc" + end + end + + it "leaves lowercase Unicode characters (except in first position) as they were" do + "a\u{00DF}C".to_sym.capitalize.should == :"Aßc" + end + + it "leaves non-alphabetic ASCII characters as they were" do + "Glark?!?".to_sym.capitalize.should == :"Glark?!?" + end +end diff --git a/spec/rubyspec/core/symbol/case_compare_spec.rb b/spec/rubyspec/core/symbol/case_compare_spec.rb new file mode 100644 index 0000000000..5c705c0b04 --- /dev/null +++ b/spec/rubyspec/core/symbol/case_compare_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#===" do + it "returns true when the argument is a Symbol" do + (Symbol === :ruby).should == true + end + + it "returns false when the argument is a String" do + (Symbol === 'ruby').should == false + end +end diff --git a/spec/rubyspec/core/symbol/casecmp_spec.rb b/spec/rubyspec/core/symbol/casecmp_spec.rb new file mode 100644 index 0000000000..942bd15998 --- /dev/null +++ b/spec/rubyspec/core/symbol/casecmp_spec.rb @@ -0,0 +1,74 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#casecmp with Symbol" do + it "compares symbols without regard to case" do + :abcdef.casecmp(:abcde).should == 1 + :aBcDeF.casecmp(:abcdef).should == 0 + :abcdef.casecmp(:abcdefg).should == -1 + :abcdef.casecmp(:ABCDEF).should == 0 + end + + it "doesn't consider non-ascii characters equal that aren't" do + # -- Latin-1 -- + upper_a_tilde = :"\xC3" + upper_a_umlaut = :"\xC4" + lower_a_tilde = :"\xE3" + lower_a_umlaut = :"\xE4" + + lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0 + lower_a_umlaut.casecmp(lower_a_tilde).should_not == 0 + upper_a_tilde.casecmp(upper_a_umlaut).should_not == 0 + upper_a_umlaut.casecmp(upper_a_tilde).should_not == 0 + + # -- UTF-8 -- + upper_a_tilde = :"\xC3\x83" + upper_a_umlaut = :"\xC3\x84" + lower_a_tilde = :"\xC3\xA3" + lower_a_umlaut = :"\xC3\xA4" + + lower_a_tilde.casecmp(lower_a_umlaut).should_not == 0 + lower_a_umlaut.casecmp(lower_a_tilde).should_not == 0 + upper_a_tilde.casecmp(upper_a_umlaut).should_not == 0 + upper_a_umlaut.casecmp(upper_a_tilde).should_not == 0 + end + + it "doesn't do case mapping for non-ascii characters" do + # -- Latin-1 -- + upper_a_tilde = :"\xC3" + upper_a_umlaut = :"\xC4" + lower_a_tilde = :"\xE3" + lower_a_umlaut = :"\xE4" + + upper_a_tilde.casecmp(lower_a_tilde).should == -1 + upper_a_umlaut.casecmp(lower_a_umlaut).should == -1 + lower_a_tilde.casecmp(upper_a_tilde).should == 1 + lower_a_umlaut.casecmp(upper_a_umlaut).should == 1 + + # -- UTF-8 -- + upper_a_tilde = :"\xC3\x83" + upper_a_umlaut = :"\xC3\x84" + lower_a_tilde = :"\xC3\xA3" + lower_a_umlaut = :"\xC3\xA4" + + upper_a_tilde.casecmp(lower_a_tilde).should == -1 + upper_a_umlaut.casecmp(lower_a_umlaut).should == -1 + lower_a_tilde.casecmp(upper_a_tilde).should == 1 + lower_a_umlaut.casecmp(upper_a_umlaut).should == 1 + end +end + +describe "Symbol#casecmp" do + it "returns nil if other is a String" do + :abc.casecmp("abc").should be_nil + end + + it "returns nil if other is a Fixnum" do + :abc.casecmp(1).should be_nil + end + + it "returns nil if other is an object" do + obj = mock("string <=>") + :abc.casecmp(obj).should be_nil + end +end diff --git a/spec/rubyspec/core/symbol/comparison_spec.rb b/spec/rubyspec/core/symbol/comparison_spec.rb new file mode 100644 index 0000000000..91edd1f0d9 --- /dev/null +++ b/spec/rubyspec/core/symbol/comparison_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#<=> with Symbol" do + it "compares individual characters based on their ascii value" do + ascii_order = Array.new(256) { |x| x.chr.to_sym } + sort_order = ascii_order.sort + sort_order.should == ascii_order + end + + it "returns -1 when self is less than other" do + (:this <=> :those).should == -1 + end + + it "returns 0 when self is equal to other" do + (:yep <=> :yep).should == 0 + end + + it "returns 1 when self is greater than other" do + (:yoddle <=> :griddle).should == 1 + end + + it "considers symbol that comes lexicographically first to be less if the symbols have same size" do + (:aba <=> :abc).should == -1 + (:abc <=> :aba).should == 1 + end + + it "doesn't consider shorter string to be less if longer string starts with shorter one" do + (:abc <=> :abcd).should == -1 + (:abcd <=> :abc).should == 1 + end + + it "compares shorter string with corresponding number of first chars of longer string" do + (:abx <=> :abcd).should == 1 + (:abcd <=> :abx).should == -1 + end +end + +describe "Symbol#<=>" do + it "returns nil if other is a String" do + (:abc <=> "abc").should be_nil + end + + it "returns nil if other is a Fixnum" do + (:abc <=> 1).should be_nil + end + + it "returns nil if other is an object" do + obj = mock("string <=>") + (:abc <=> obj).should be_nil + end +end diff --git a/spec/rubyspec/core/symbol/downcase_spec.rb b/spec/rubyspec/core/symbol/downcase_spec.rb new file mode 100644 index 0000000000..0b2422ad3e --- /dev/null +++ b/spec/rubyspec/core/symbol/downcase_spec.rb @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#downcase" do + it "returns a Symbol" do + :glark.downcase.should be_an_instance_of(Symbol) + end + + it "converts uppercase ASCII characters to their lowercase equivalents" do + :lOwEr.downcase.should == :lower + end + + it "leaves lowercase Unicode characters as they were" do + "\u{E0}Bc".to_sym.downcase.should == :"àbc" + end + + ruby_version_is ''...'2.4' do + it "leaves uppercase Unicode characters as they were" do + "\u{DE}Bc".to_sym.downcase.should == :"Þbc" + end + end + + it "leaves non-alphabetic ASCII characters as they were" do + "Glark?!?".to_sym.downcase.should == :"glark?!?" + end +end diff --git a/spec/rubyspec/core/symbol/element_reference_spec.rb b/spec/rubyspec/core/symbol/element_reference_spec.rb new file mode 100644 index 0000000000..4116274ee0 --- /dev/null +++ b/spec/rubyspec/core/symbol/element_reference_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/slice.rb', __FILE__) + +describe "Symbol#[]" do + it_behaves_like(:symbol_slice, :[]) +end diff --git a/spec/rubyspec/core/symbol/empty_spec.rb b/spec/rubyspec/core/symbol/empty_spec.rb new file mode 100644 index 0000000000..7be0007355 --- /dev/null +++ b/spec/rubyspec/core/symbol/empty_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#empty?" do + it "returns true if self is empty" do + :"".empty?.should be_true + end + + it "returns false if self is non-empty" do + :"a".empty?.should be_false + end +end diff --git a/spec/rubyspec/core/symbol/encoding_spec.rb b/spec/rubyspec/core/symbol/encoding_spec.rb new file mode 100644 index 0000000000..b6128562b9 --- /dev/null +++ b/spec/rubyspec/core/symbol/encoding_spec.rb @@ -0,0 +1,23 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#encoding for ASCII symbols" do + it "is US-ASCII" do + :foo.encoding.name.should == "US-ASCII" + end + + it "is US-ASCII after converting to string" do + :foo.to_s.encoding.name.should == "US-ASCII" + end +end + +describe "Symbol#encoding for UTF-8 symbols" do + it "is UTF-8" do + :åäö.encoding.name.should == "UTF-8" + end + + it "is UTF-8 after converting to string" do + :åäö.to_s.encoding.name.should == "UTF-8" + end +end diff --git a/spec/rubyspec/core/symbol/equal_value_spec.rb b/spec/rubyspec/core/symbol/equal_value_spec.rb new file mode 100644 index 0000000000..7c305fab39 --- /dev/null +++ b/spec/rubyspec/core/symbol/equal_value_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#==" do + it "only returns true when the other is exactly the same symbol" do + (:ruby == :ruby).should == true + (:ruby == :"ruby").should == true + (:ruby == :'ruby').should == true + (:@ruby == :@ruby).should == true + + (:ruby == :@ruby).should == false + (:foo == :bar).should == false + (:ruby == 'ruby').should == false + end +end diff --git a/spec/rubyspec/core/symbol/fixtures/classes.rb b/spec/rubyspec/core/symbol/fixtures/classes.rb new file mode 100644 index 0000000000..6552f6ee38 --- /dev/null +++ b/spec/rubyspec/core/symbol/fixtures/classes.rb @@ -0,0 +1,3 @@ +module SymbolSpecs + class MyRange < Range; end +end diff --git a/spec/rubyspec/core/symbol/id2name_spec.rb b/spec/rubyspec/core/symbol/id2name_spec.rb new file mode 100644 index 0000000000..932dd7171d --- /dev/null +++ b/spec/rubyspec/core/symbol/id2name_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/id2name', __FILE__) + +describe "Symbol#id2name" do + it_behaves_like(:symbol_id2name, :id2name) +end diff --git a/spec/rubyspec/core/symbol/inspect_spec.rb b/spec/rubyspec/core/symbol/inspect_spec.rb new file mode 100644 index 0000000000..dead6e34fc --- /dev/null +++ b/spec/rubyspec/core/symbol/inspect_spec.rb @@ -0,0 +1,105 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#inspect" do + symbols = { + fred: ":fred", + :fred? => ":fred?", + :fred! => ":fred!", + :$ruby => ":$ruby", + :@ruby => ":@ruby", + :@@ruby => ":@@ruby", + :"$ruby!" => ":\"$ruby!\"", + :"$ruby?" => ":\"$ruby?\"", + :"@ruby!" => ":\"@ruby!\"", + :"@ruby?" => ":\"@ruby?\"", + :"@@ruby!" => ":\"@@ruby!\"", + :"@@ruby?" => ":\"@@ruby?\"", + + :$-w => ":$-w", + :"$-ww" => ":\"$-ww\"", + :"$+" => ":$+", + :"$~" => ":$~", + :"$:" => ":$:", + :"$?" => ":$?", + :"$<" => ":$<", + :"$_" => ":$_", + :"$/" => ":$/", + :"$'" => ":$'", + :"$\"" => ":$\"", + :"$$" => ":$$", + :"$." => ":$.", + :"$," => ":$,", + :"$`" => ":$`", + :"$!" => ":$!", + :"$;" => ":$;", + :"$\\" => ":$\\", + :"$=" => ":$=", + :"$*" => ":$*", + :"$>" => ":$>", + :"$&" => ":$&", + :"$@" => ":$@", + :"$1234" => ":$1234", + + :-@ => ":-@", + :+@ => ":+@", + :% => ":%", + :& => ":&", + :* => ":*", + :** => ":**", + :"/" => ":/", # lhs quoted for emacs happiness + :< => ":<", + :<= => ":<=", + :<=> => ":<=>", + :== => ":==", + :=== => ":===", + :=~ => ":=~", + :> => ":>", + :>= => ":>=", + :>> => ":>>", + :[] => ":[]", + :[]= => ":[]=", + :"\<\<" => ":\<\<", + :^ => ":^", + :"`" => ":`", # for emacs, and justice! + :~ => ":~", + :| => ":|", + + :"!" => [":\"!\"", ":!" ], + :"!=" => [":\"!=\"", ":!="], + :"!~" => [":\"!~\"", ":!~"], + :"\$" => ":\"$\"", # for justice! + :"&&" => ":\"&&\"", + :"'" => ":\"\'\"", + :"," => ":\",\"", + :"." => ":\".\"", + :".." => ":\"..\"", + :"..." => ":\"...\"", + :":" => ":\":\"", + :"::" => ":\"::\"", + :";" => ":\";\"", + :"=" => ":\"=\"", + :"=>" => ":\"=>\"", + :"\?" => ":\"?\"", # rawr! + :"@" => ":\"@\"", + :"||" => ":\"||\"", + :"|||" => ":\"|||\"", + :"++" => ":\"++\"", + + :"\"" => ":\"\\\"\"", + :"\"\"" => ":\"\\\"\\\"\"", + + :"9" => ":\"9\"", + :"foo bar" => ":\"foo bar\"", + :"*foo" => ":\"*foo\"", + :"foo " => ":\"foo \"", + :" foo" => ":\" foo\"", + :" " => ":\" \"", + } + + symbols.each do |input, expected| + expected = expected[1] if expected.is_a?(Array) + it "returns self as a symbol literal for #{expected}" do + input.inspect.should == expected + end + end +end diff --git a/spec/rubyspec/core/symbol/intern_spec.rb b/spec/rubyspec/core/symbol/intern_spec.rb new file mode 100644 index 0000000000..c1ac5aeac1 --- /dev/null +++ b/spec/rubyspec/core/symbol/intern_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#intern" do + it "returns self" do + :foo.intern.should == :foo + end + + it "returns a Symbol" do + :foo.intern.should be_kind_of(Symbol) + end +end diff --git a/spec/rubyspec/core/symbol/length_spec.rb b/spec/rubyspec/core/symbol/length_spec.rb new file mode 100644 index 0000000000..e7e0700d5a --- /dev/null +++ b/spec/rubyspec/core/symbol/length_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Symbol#length" do + it_behaves_like :symbol_length, :length +end diff --git a/spec/rubyspec/core/symbol/match_spec.rb b/spec/rubyspec/core/symbol/match_spec.rb new file mode 100644 index 0000000000..768e3b00da --- /dev/null +++ b/spec/rubyspec/core/symbol/match_spec.rb @@ -0,0 +1,70 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :symbol_match, shared: true do + it "returns the index of the beginning of the match" do + :abc.send(@method, /b/).should == 1 + end + + it "returns nil if there is no match" do + :a.send(@method, /b/).should be_nil + end + + it "sets the last match pseudo-variables" do + :a.send(@method, /(.)/).should == 0 + $1.should == "a" + end +end + +describe "Symbol#=~" do + it_behaves_like :symbol_match, :=~ +end + +ruby_version_is ""..."2.4" do + describe "Symbol#match" do + it_behaves_like :symbol_match, :match + end +end + +ruby_version_is "2.4" do + describe "Symbol#match" do + it "returns the MatchData" do + result = :abc.match(/b/) + result.should be_kind_of(MatchData) + result[0].should == 'b' + end + + it "returns nil if there is no match" do + :a.match(/b/).should be_nil + end + + it "sets the last match pseudo-variables" do + :a.match(/(.)/)[0].should == 'a' + $1.should == "a" + end + end +end + +ruby_version_is "2.4" do + describe "Symbol#match?" do + before :each do + # Resetting Regexp.last_match + /DONTMATCH/.match '' + end + + context "when matches the given regex" do + it "returns true but does not set Regexp.last_match" do + :string.match?(/string/i).should be_true + Regexp.last_match.should be_nil + end + end + + it "returns false when does not match the given regex" do + :string.match?(/STRING/).should be_false + end + + it "takes matching position as the 2nd argument" do + :string.match?(/str/i, 0).should be_true + :string.match?(/str/i, 1).should be_false + end + end +end diff --git a/spec/rubyspec/core/symbol/next_spec.rb b/spec/rubyspec/core/symbol/next_spec.rb new file mode 100644 index 0000000000..65ffbebd40 --- /dev/null +++ b/spec/rubyspec/core/symbol/next_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/succ', __FILE__) + +describe "Symbol#next" do + it_behaves_like :symbol_succ, :next +end diff --git a/spec/rubyspec/core/symbol/shared/id2name.rb b/spec/rubyspec/core/symbol/shared/id2name.rb new file mode 100644 index 0000000000..47f97bd332 --- /dev/null +++ b/spec/rubyspec/core/symbol/shared/id2name.rb @@ -0,0 +1,9 @@ +describe :symbol_id2name, shared: true do + it "returns the string corresponding to self" do + :rubinius.send(@method).should == "rubinius" + :squash.send(@method).should == "squash" + :[].send(@method).should == "[]" + :@ruby.send(@method).should == "@ruby" + :@@ruby.send(@method).should == "@@ruby" + end +end diff --git a/spec/rubyspec/core/symbol/shared/length.rb b/spec/rubyspec/core/symbol/shared/length.rb new file mode 100644 index 0000000000..692e8c57e3 --- /dev/null +++ b/spec/rubyspec/core/symbol/shared/length.rb @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- + +describe :symbol_length, shared: true do + it "returns 0 for empty name" do + :''.send(@method).should == 0 + end + + it "returns 1 for name formed by a NUL character" do + :"\x00".send(@method).should == 1 + end + + it "returns 3 for name formed by 3 ASCII characters" do + :one.send(@method).should == 3 + end + + it "returns 4 for name formed by 4 ASCII characters" do + :four.send(@method).should == 4 + end + + it "returns 4 for name formed by 1 multibyte and 3 ASCII characters" do + :"\xC3\x9Cber".send(@method).should == 4 + end +end diff --git a/spec/rubyspec/core/symbol/shared/slice.rb b/spec/rubyspec/core/symbol/shared/slice.rb new file mode 100644 index 0000000000..d39b02f319 --- /dev/null +++ b/spec/rubyspec/core/symbol/shared/slice.rb @@ -0,0 +1,278 @@ +require File.expand_path('../../fixtures/classes.rb', __FILE__) + +describe :symbol_slice, shared: true do + describe "with an Integer index" do + it "returns the character code of the element at the index" do + :symbol.send(@method, 1).should == ?y + end + + it "returns nil if the index starts from the end and is greater than the length" do + :symbol.send(@method, -10).should be_nil + end + + it "returns nil if the index is greater than the length" do + :symbol.send(@method, 42).should be_nil + end + end + + describe "with an Integer index and length" do + describe "and a positive index and length" do + it "returns a slice" do + :symbol.send(@method, 1,3).should == "ymb" + end + + it "returns a blank slice if the length is 0" do + :symbol.send(@method, 0,0).should == "" + :symbol.send(@method, 1,0).should == "" + end + + it "returns a slice of all remaining characters if the given length is greater than the actual length" do + :symbol.send(@method, 1,100).should == "ymbol" + end + + it "returns nil if the index is greater than the length" do + :symbol.send(@method, 10,1).should be_nil + end + end + + describe "and a positive index and negative length" do + it "returns nil" do + :symbol.send(@method, 0,-1).should be_nil + :symbol.send(@method, 1,-1).should be_nil + end + end + + describe "and a negative index and positive length" do + it "returns a slice starting from the end upto the length" do + :symbol.send(@method, -3,2).should == "bo" + end + + it "returns a blank slice if the length is 0" do + :symbol.send(@method, -1,0).should == "" + end + + it "returns a slice of all remaining characters if the given length is larger than the actual length" do + :symbol.send(@method, -4,100).should == "mbol" + end + + it "returns nil if the index is past the start" do + :symbol.send(@method, -10,1).should be_nil + end + end + + describe "and a negative index and negative length" do + it "returns nil" do + :symbol.send(@method, -1,-1).should be_nil + end + end + + describe "and a Float length" do + it "converts the length to an Integer" do + :symbol.send(@method, 2,2.5).should == "mb" + end + end + + describe "and a nil length" do + it "raises a TypeError" do + lambda { :symbol.send(@method, 1,nil) }.should raise_error(TypeError) + end + end + + describe "and a length that cannot be converted into an Integer" do + it "raises a TypeError when given an Array" do + lambda { :symbol.send(@method, 1,Array.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Hash" do + lambda { :symbol.send(@method, 1,Hash.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Object" do + lambda { :symbol.send(@method, 1,Object.new) }.should raise_error(TypeError) + end + end + end + + describe "with a Float index" do + it "converts the index to an Integer" do + :symbol.send(@method, 1.5).should == ?y + end + end + + describe "with a nil index" do + it "raises a TypeError" do + lambda { :symbol.send(@method, nil) }.should raise_error(TypeError) + end + end + + describe "with an index that cannot be converted into an Integer" do + it "raises a TypeError when given an Array" do + lambda { :symbol.send(@method, Array.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Hash" do + lambda { :symbol.send(@method, Hash.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Object" do + lambda { :symbol.send(@method, Object.new) }.should raise_error(TypeError) + end + end + + describe "with a Range slice" do + describe "that is within bounds" do + it "returns a slice if both range values begin at the start and are within bounds" do + :symbol.send(@method, 1..4).should == "ymbo" + end + + it "returns a slice if the first range value begins at the start and the last begins at the end" do + :symbol.send(@method, 1..-1).should == "ymbol" + end + + it "returns a slice if the first range value begins at the end and the last begins at the end" do + :symbol.send(@method, -4..-1).should == "mbol" + end + end + + describe "that is out of bounds" do + it "returns nil if the first range value begins past the end" do + :symbol.send(@method, 10..12).should be_nil + end + + it "returns a blank string if the first range value is within bounds and the last range value is not" do + :symbol.send(@method, -2..-10).should == "" + :symbol.send(@method, 2..-10).should == "" + end + + it "returns nil if the first range value starts from the end and is within bounds and the last value starts from the end and is greater than the length" do + :symbol.send(@method, -10..-12).should be_nil + end + + it "returns nil if the first range value starts from the end and is out of bounds and the last value starts from the end and is less than the length" do + :symbol.send(@method, -10..-2).should be_nil + end + end + + describe "with Float values" do + it "converts the first value to an Integer" do + :symbol.send(@method, 0.5..2).should == "sym" + end + + it "converts the last value to an Integer" do + :symbol.send(@method, 0..2.5).should == "sym" + end + end + end + + describe "with a Range subclass slice" do + it "returns a slice" do + range = SymbolSpecs::MyRange.new(1, 4) + :symbol.send(@method, range).should == "ymbo" + end + end + + describe "with a Regex slice" do + describe "without a capture index" do + it "returns a string of the match" do + :symbol.send(@method, /[^bol]+/).should == "sym" + end + + it "returns nil if the expression does not match" do + :symbol.send(@method, /0-9/).should be_nil + end + + it "sets $~ to the MatchData if there is a match" do + :symbol.send(@method, /[^bol]+/) + $~[0].should == "sym" + end + + it "does not set $~ if there if there is not a match" do + :symbol.send(@method, /[0-9]+/) + $~.should be_nil + end + + it "returns a tainted string if the regexp is tainted" do + :symbol.send(@method, /./.taint).tainted?.should be_true + end + + it "returns an untrusted string if the regexp is untrusted" do + :symbol.send(@method, /./.untrust).untrusted?.should be_true + end + end + + describe "with a capture index" do + it "returns a string of the complete match if the capture index is 0" do + :symbol.send(@method, /(sy)(mb)(ol)/, 0).should == "symbol" + end + + it "returns a string for the matched capture at the given index" do + :symbol.send(@method, /(sy)(mb)(ol)/, 1).should == "sy" + :symbol.send(@method, /(sy)(mb)(ol)/, -1).should == "ol" + end + + it "returns nil if there is no capture for the index" do + :symbol.send(@method, /(sy)(mb)(ol)/, 4).should be_nil + :symbol.send(@method, /(sy)(mb)(ol)/, -4).should be_nil + end + + it "converts the index to an Integer" do + :symbol.send(@method, /(sy)(mb)(ol)/, 1.5).should == "sy" + end + + it "returns a tainted string if the regexp is tainted" do + :symbol.send(@method, /(.)/.taint, 1).tainted?.should be_true + end + + it "returns an untrusted string if the regexp is untrusted" do + :symbol.send(@method, /(.)/.untrust, 1).untrusted?.should be_true + end + + describe "and an index that cannot be converted to an Integer" do + it "raises a TypeError when given an Hash" do + lambda { :symbol.send(@method, /(sy)(mb)(ol)/, Hash.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Array" do + lambda { :symbol.send(@method, /(sy)(mb)(ol)/, Array.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when given an Object" do + lambda { :symbol.send(@method, /(sy)(mb)(ol)/, Object.new) }.should raise_error(TypeError) + end + end + + it "raises a TypeError if the index is nil" do + lambda { :symbol.send(@method, /(sy)(mb)(ol)/, nil) }.should raise_error(TypeError) + end + + it "sets $~ to the MatchData if there is a match" do + :symbol.send(@method, /(sy)(mb)(ol)/, 0) + $~[0].should == "symbol" + $~[1].should == "sy" + $~[2].should == "mb" + $~[3].should == "ol" + end + + it "does not set $~ to the MatchData if there is not a match" do + :symbol.send(@method, /0-9/, 0) + $~.should be_nil + end + end + end + + describe "with a String slice" do + it "does not set $~" do + $~ = nil + :symbol.send(@method, "sym") + $~.should be_nil + end + + it "returns a string if there is match" do + :symbol.send(@method, "ymb").should == "ymb" + end + + it "returns nil if there is not a match" do + :symbol.send(@method, "foo").should be_nil + end + end +end diff --git a/spec/rubyspec/core/symbol/shared/succ.rb b/spec/rubyspec/core/symbol/shared/succ.rb new file mode 100644 index 0000000000..0e371490f9 --- /dev/null +++ b/spec/rubyspec/core/symbol/shared/succ.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :symbol_succ, shared: true do + it "returns a successor" do + :abcd.send(@method).should == :abce + :THX1138.send(@method).should == :THX1139 + end + + it "propagates a 'carry'" do + :"1999zzz".send(@method).should == :"2000aaa" + :ZZZ9999.send(@method).should == :AAAA0000 + end + + it "increments non-alphanumeric characters when no alphanumeric characters are present" do + :"<>".send(@method).should == :"<>" + :"***".send(@method).should == :"**+" + end +end diff --git a/spec/rubyspec/core/symbol/size_spec.rb b/spec/rubyspec/core/symbol/size_spec.rb new file mode 100644 index 0000000000..a6dfe092ec --- /dev/null +++ b/spec/rubyspec/core/symbol/size_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Symbol#size" do + it_behaves_like :symbol_length, :size +end diff --git a/spec/rubyspec/core/symbol/slice_spec.rb b/spec/rubyspec/core/symbol/slice_spec.rb new file mode 100644 index 0000000000..3c535ac4b0 --- /dev/null +++ b/spec/rubyspec/core/symbol/slice_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/slice.rb', __FILE__) + +describe "Symbol#slice" do + it_behaves_like(:symbol_slice, :slice) +end diff --git a/spec/rubyspec/core/symbol/succ_spec.rb b/spec/rubyspec/core/symbol/succ_spec.rb new file mode 100644 index 0000000000..21bfb7e4aa --- /dev/null +++ b/spec/rubyspec/core/symbol/succ_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/succ', __FILE__) + +describe "Symbol#succ" do + it_behaves_like :symbol_succ, :succ +end diff --git a/spec/rubyspec/core/symbol/swapcase_spec.rb b/spec/rubyspec/core/symbol/swapcase_spec.rb new file mode 100644 index 0000000000..fdc42ec477 --- /dev/null +++ b/spec/rubyspec/core/symbol/swapcase_spec.rb @@ -0,0 +1,34 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#swapcase" do + it "returns a Symbol" do + :glark.swapcase.should be_an_instance_of(Symbol) + end + + it "converts lowercase ASCII characters to their uppercase equivalents" do + :lower.swapcase.should == :LOWER + end + + it "converts uppercase ASCII characters to their lowercase equivalents" do + :UPPER.swapcase.should == :upper + end + + it "works with both upper- and lowercase ASCII characters in the same Symbol" do + :mIxEd.swapcase.should == :MiXeD + end + + ruby_version_is ''...'2.4' do + it "leaves uppercase Unicode characters as they were" do + "\u{00DE}Bc".to_sym.swapcase.should == :"ÞbC" + end + + it "leaves lowercase Unicode characters as they were" do + "\u{00DF}Bc".to_sym.swapcase.should == :"ßbC" + end + end + + it "leaves non-alphabetic ASCII characters as they were" do + "Glark?!?".to_sym.swapcase.should == :"gLARK?!?" + end +end diff --git a/spec/rubyspec/core/symbol/symbol_spec.rb b/spec/rubyspec/core/symbol/symbol_spec.rb new file mode 100644 index 0000000000..bb95211ba0 --- /dev/null +++ b/spec/rubyspec/core/symbol/symbol_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol" do + it "includes Comparable" do + Symbol.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/symbol/to_proc_spec.rb b/spec/rubyspec/core/symbol/to_proc_spec.rb new file mode 100644 index 0000000000..be625994d9 --- /dev/null +++ b/spec/rubyspec/core/symbol/to_proc_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#to_proc" do + it "returns a new Proc" do + proc = :to_s.to_proc + proc.should be_kind_of(Proc) + end + + it "sends self to arguments passed when calling #call on the Proc" do + obj = mock("Receiving #to_s") + obj.should_receive(:to_s).and_return("Received #to_s") + :to_s.to_proc.call(obj).should == "Received #to_s" + end + + it "raises an ArgumentError when calling #call on the Proc without receiver" do + lambda { :object_id.to_proc.call }.should raise_error(ArgumentError) + end + + it "produces a proc that always returns [[:rest]] for #parameters" do + pr = :to_s.to_proc + pr.parameters.should == [[:rest]] + end +end + +describe "Symbol#to_proc" do + before :all do + @klass = Class.new do + def m + yield + end + + def to_proc + :m.to_proc.call(self) { :value } + end + end + end + + it "passes along the block passed to Proc#call" do + @klass.new.to_proc.should == :value + end +end diff --git a/spec/rubyspec/core/symbol/to_s_spec.rb b/spec/rubyspec/core/symbol/to_s_spec.rb new file mode 100644 index 0000000000..40c13675b3 --- /dev/null +++ b/spec/rubyspec/core/symbol/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/id2name', __FILE__) + +describe "Symbol#to_s" do + it_behaves_like(:symbol_id2name, :to_s) +end diff --git a/spec/rubyspec/core/symbol/to_sym_spec.rb b/spec/rubyspec/core/symbol/to_sym_spec.rb new file mode 100644 index 0000000000..7f26684850 --- /dev/null +++ b/spec/rubyspec/core/symbol/to_sym_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#to_sym" do + it "returns self" do + [:rubinius, :squash, :[], :@ruby, :@@ruby].each do |sym| + sym.to_sym.should == sym + end + end +end diff --git a/spec/rubyspec/core/symbol/upcase_spec.rb b/spec/rubyspec/core/symbol/upcase_spec.rb new file mode 100644 index 0000000000..fce62af100 --- /dev/null +++ b/spec/rubyspec/core/symbol/upcase_spec.rb @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Symbol#upcase" do + it "returns a Symbol" do + :glark.upcase.should be_an_instance_of(Symbol) + end + + it "converts lowercase ASCII characters to their uppercase equivalents" do + :lOwEr.upcase.should == :LOWER + end + + ruby_version_is ''...'2.4' do + it "leaves lowercase Unicode characters as they were" do + "\u{E0}Bc".to_sym.upcase.should == :"àBC" + end + end + + it "leaves non-alphabetic ASCII characters as they were" do + "Glark?!?".to_sym.upcase.should == :"GLARK?!?" + end +end diff --git a/spec/rubyspec/core/systemexit/initialize_spec.rb b/spec/rubyspec/core/systemexit/initialize_spec.rb new file mode 100644 index 0000000000..bf5c8b7798 --- /dev/null +++ b/spec/rubyspec/core/systemexit/initialize_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SystemExit#initialize" do + it "accepts a status" do + s = SystemExit.new 1 + s.status.should == 1 + s.message.should == 'SystemExit' + end + + it "accepts a message" do + s = SystemExit.new 'message' + s.status.should == 0 + s.message.should == 'message' + end + + it "accepts a status and message" do + s = SystemExit.new 10, 'message' + s.status.should == 10 + s.message.should == 'message' + end + + it "sets the status to 0 by default" do + s = SystemExit.new + s.status.should == 0 + end +end + diff --git a/spec/rubyspec/core/systemexit/success_spec.rb b/spec/rubyspec/core/systemexit/success_spec.rb new file mode 100644 index 0000000000..038a0e7e4d --- /dev/null +++ b/spec/rubyspec/core/systemexit/success_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "SystemExit#success?" do + it "returns true when the status is 0" do + s = SystemExit.new 0 + s.success?.should == true + end + + it "returns false when the status is not 0" do + s = SystemExit.new 1 + s.success?.should == false + end +end diff --git a/spec/rubyspec/core/thread/abort_on_exception_spec.rb b/spec/rubyspec/core/thread/abort_on_exception_spec.rb new file mode 100644 index 0000000000..e424b2fd26 --- /dev/null +++ b/spec/rubyspec/core/thread/abort_on_exception_spec.rb @@ -0,0 +1,106 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#abort_on_exception" do + before do + ThreadSpecs.clear_state + @thread = Thread.new { Thread.pass until ThreadSpecs.state == :exit } + end + + after do + ThreadSpecs.state = :exit + @thread.join + end + + it "is false by default" do + @thread.abort_on_exception.should be_false + end + + it "returns true when #abort_on_exception= is passed true" do + @thread.abort_on_exception = true + @thread.abort_on_exception.should be_true + end +end + +describe :thread_abort_on_exception, shared: true do + before do + @thread = Thread.new do + Thread.pass until ThreadSpecs.state == :run + raise RuntimeError, "Thread#abort_on_exception= specs" + end + end + + it "causes the main thread to raise the exception raised in the thread" do + begin + ScratchPad << :before + + @thread.abort_on_exception = true if @object + lambda do + ThreadSpecs.state = :run + # Wait for the main thread to be interrupted + sleep + end.should raise_error(RuntimeError, "Thread#abort_on_exception= specs") + + ScratchPad << :after + rescue Exception => e + ScratchPad << [:rescue, e] + end + + ScratchPad.recorded.should == [:before, :after] + end +end + +describe "Thread#abort_on_exception=" do + describe "when enabled and the thread dies due to an exception" do + before do + ScratchPad.record [] + ThreadSpecs.clear_state + @stderr, $stderr = $stderr, IOStub.new + end + + after do + $stderr = @stderr + end + + it_behaves_like :thread_abort_on_exception, nil, true + end +end + +describe "Thread.abort_on_exception" do + before do + @abort_on_exception = Thread.abort_on_exception + end + + after do + Thread.abort_on_exception = @abort_on_exception + end + + it "is false by default" do + Thread.abort_on_exception.should == false + end + + it "returns true when .abort_on_exception= is passed true" do + Thread.abort_on_exception = true + Thread.abort_on_exception.should be_true + end +end + +describe "Thread.abort_on_exception=" do + describe "when enabled and a non-main thread dies due to an exception" do + before :each do + ScratchPad.record [] + ThreadSpecs.clear_state + @stderr, $stderr = $stderr, IOStub.new + + @abort_on_exception = Thread.abort_on_exception + Thread.abort_on_exception = true + end + + after :each do + Thread.abort_on_exception = @abort_on_exception + $stderr = @stderr + end + + it_behaves_like :thread_abort_on_exception, nil, false + end +end diff --git a/spec/rubyspec/core/thread/add_trace_func_spec.rb b/spec/rubyspec/core/thread/add_trace_func_spec.rb new file mode 100644 index 0000000000..c2010ef317 --- /dev/null +++ b/spec/rubyspec/core/thread/add_trace_func_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread#add_trace_func" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/thread/alive_spec.rb b/spec/rubyspec/core/thread/alive_spec.rb new file mode 100644 index 0000000000..c1459ac693 --- /dev/null +++ b/spec/rubyspec/core/thread/alive_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#alive?" do + it "can check it's own status" do + ThreadSpecs.status_of_current_thread.alive?.should == true + end + + it "describes a running thread" do + ThreadSpecs.status_of_running_thread.alive?.should == true + end + + it "describes a sleeping thread" do + ThreadSpecs.status_of_sleeping_thread.alive?.should == true + end + + it "describes a blocked thread" do + ThreadSpecs.status_of_blocked_thread.alive?.should == true + end + + it "describes a completed thread" do + ThreadSpecs.status_of_completed_thread.alive?.should == false + end + + it "describes a killed thread" do + ThreadSpecs.status_of_killed_thread.alive?.should == false + end + + it "describes a thread with an uncaught exception" do + ThreadSpecs.status_of_thread_with_uncaught_exception.alive?.should == false + end + + it "describes a dying running thread" do + ThreadSpecs.status_of_dying_running_thread.alive?.should == true + end + + it "describes a dying sleeping thread" do + ThreadSpecs.status_of_dying_sleeping_thread.alive?.should == true + end + + it "returns true for a killed but still running thread" do + exit = false + t = Thread.new do + begin + sleep + ensure + Thread.pass until exit + end + end + + ThreadSpecs.spin_until_sleeping(t) + + t.kill + t.alive?.should == true + exit = true + t.join + end +end diff --git a/spec/rubyspec/core/thread/allocate_spec.rb b/spec/rubyspec/core/thread/allocate_spec.rb new file mode 100644 index 0000000000..1db05878ba --- /dev/null +++ b/spec/rubyspec/core/thread/allocate_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread.allocate" do + it "raises a TypeError" do + lambda { + Thread.allocate + }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/absolute_path_spec.rb b/spec/rubyspec/core/thread/backtrace/location/absolute_path_spec.rb new file mode 100644 index 0000000000..6810bdcd78 --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/absolute_path_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe 'Thread::Backtrace::Location#absolute_path' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + end + + it 'returns the absolute path of the call frame' do + @frame.absolute_path.should == File.realpath(__FILE__) + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/base_label_spec.rb b/spec/rubyspec/core/thread/backtrace/location/base_label_spec.rb new file mode 100644 index 0000000000..cba7e3f34c --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/base_label_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe 'Thread::Backtrace::Location#base_label' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + end + + it 'returns the base label of the call frame' do + @frame.base_label.should == '' + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/fixtures/classes.rb b/spec/rubyspec/core/thread/backtrace/location/fixtures/classes.rb new file mode 100644 index 0000000000..3e42d8cf81 --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/fixtures/classes.rb @@ -0,0 +1,17 @@ +module ThreadBacktraceLocationSpecs + MODULE_LOCATION = caller_locations(0) rescue nil + + def self.locations + caller_locations + end + + def self.method_location + caller_locations(0) + end + + def self.block_location + 1.times do + return caller_locations(0) + end + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/fixtures/main.rb b/spec/rubyspec/core/thread/backtrace/location/fixtures/main.rb new file mode 100644 index 0000000000..d2d14ac957 --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/fixtures/main.rb @@ -0,0 +1,5 @@ +def example + caller_locations[0].path +end + +print example diff --git a/spec/rubyspec/core/thread/backtrace/location/inspect_spec.rb b/spec/rubyspec/core/thread/backtrace/location/inspect_spec.rb new file mode 100644 index 0000000000..56d440c04a --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/inspect_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe 'Thread::Backtrace::Location#inspect' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + @line = __LINE__ - 1 + end + + it 'converts the call frame to a String' do + @frame.inspect.should include("#{__FILE__}:#{@line}:in ") + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/label_spec.rb b/spec/rubyspec/core/thread/backtrace/location/label_spec.rb new file mode 100644 index 0000000000..4e67509d0f --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/label_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe 'Thread::Backtrace::Location#label' do + it 'returns the base label of the call frame' do + ThreadBacktraceLocationSpecs.locations[0].label.should include('') + end + + it 'returns the method name for a method location' do + ThreadBacktraceLocationSpecs.method_location[0].label.should == "method_location" + end + + it 'returns the block name for a block location' do + ThreadBacktraceLocationSpecs.block_location[0].label.should == "block in block_location" + end + + it 'returns the module name for a module location' do + ThreadBacktraceLocationSpecs::MODULE_LOCATION[0].label.should include "ThreadBacktraceLocationSpecs" + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/lineno_spec.rb b/spec/rubyspec/core/thread/backtrace/location/lineno_spec.rb new file mode 100644 index 0000000000..7d203008e5 --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/lineno_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe 'Thread::Backtrace::Location#lineno' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + @line = __LINE__ - 1 + end + + it 'returns the absolute path of the call frame' do + @frame.lineno.should == @line + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/path_spec.rb b/spec/rubyspec/core/thread/backtrace/location/path_spec.rb new file mode 100644 index 0000000000..c2f2058990 --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/path_spec.rb @@ -0,0 +1,91 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe 'Thread::Backtrace::Location#path' do + context 'outside a main script' do + it 'returns an absolute path' do + frame = ThreadBacktraceLocationSpecs.locations[0] + + frame.path.should == __FILE__ + end + end + + context 'in a main script' do + before do + @script = fixture(__FILE__, 'main.rb') + end + + context 'when the script is in the working directory' do + before do + @directory = File.dirname(@script) + end + + context 'when using a relative script path' do + it 'returns a path relative to the working directory' do + Dir.chdir(@directory) { + ruby_exe('main.rb') + }.should == 'main.rb' + end + end + + context 'when using an absolute script path' do + it 'returns an absolute path' do + Dir.chdir(@directory) { + ruby_exe(@script) + }.should == @script + end + end + end + + context 'when the script is in a sub directory of the working directory' do + context 'when using a relative script path' do + it 'returns a path relative to the working directory' do + path = 'fixtures/main.rb' + directory = File.dirname(__FILE__) + Dir.chdir(directory) { + ruby_exe(path) + }.should == path + end + end + + context 'when using an absolute script path' do + it 'returns an absolute path' do + ruby_exe(@script).should == @script + end + end + end + + context 'when the script is outside of the working directory' do + before do + @parent_dir = tmp('path_outside_pwd') + @sub_dir = File.join(@parent_dir, 'sub') + @script = File.join(@parent_dir, 'main.rb') + source = fixture(__FILE__, 'main.rb') + + mkdir_p(@sub_dir) + + cp(source, @script) + end + + after do + rm_r(@script) + rm_r(@sub_dir) + rm_r(@parent_dir) + end + + context 'when using a relative script path' do + it 'returns a path relative to the working directory' do + Dir.chdir(@sub_dir) { + ruby_exe('../main.rb') + }.should == '../main.rb' + end + end + + context 'when using an absolute path' do + it 'returns an absolute path' do + ruby_exe(@script).should == @script + end + end + end + end +end diff --git a/spec/rubyspec/core/thread/backtrace/location/to_s_spec.rb b/spec/rubyspec/core/thread/backtrace/location/to_s_spec.rb new file mode 100644 index 0000000000..486d7da4c9 --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace/location/to_s_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe 'Thread::Backtrace::Location#to_s' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + @line = __LINE__ - 1 + end + + it 'converts the call frame to a String' do + @frame.to_s.should include("#{__FILE__}:#{@line}:in ") + end +end diff --git a/spec/rubyspec/core/thread/backtrace_spec.rb b/spec/rubyspec/core/thread/backtrace_spec.rb new file mode 100644 index 0000000000..a20fdee956 --- /dev/null +++ b/spec/rubyspec/core/thread/backtrace_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread#backtrace" do + it "returns the current backtrace of a thread" do + t = Thread.new do + begin + sleep + rescue + end + end + + Thread.pass while t.status && t.status != 'sleep' + + backtrace = t.backtrace + backtrace.should be_kind_of(Array) + backtrace.first.should =~ /`sleep'/ + + t.raise 'finish the thread' + t.join + end + + it "returns nil for dead thread" do + t = Thread.new {} + t.join + t.backtrace.should == nil + end +end diff --git a/spec/rubyspec/core/thread/current_spec.rb b/spec/rubyspec/core/thread/current_spec.rb new file mode 100644 index 0000000000..cc969b71c4 --- /dev/null +++ b/spec/rubyspec/core/thread/current_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread.current" do + it "returns a thread" do + current = Thread.current + current.should be_kind_of(Thread) + end + + it "returns the current thread" do + t = Thread.new { Thread.current } + t.value.should equal(t) + Thread.current.should_not equal(t.value) + end +end diff --git a/spec/rubyspec/core/thread/element_reference_spec.rb b/spec/rubyspec/core/thread/element_reference_spec.rb new file mode 100644 index 0000000000..81b11d2c09 --- /dev/null +++ b/spec/rubyspec/core/thread/element_reference_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#[]" do + it "gives access to thread local values" do + th = Thread.new do + Thread.current[:value] = 5 + end + th.join + th[:value].should == 5 + Thread.current[:value].should == nil + end + + it "is not shared across threads" do + t1 = Thread.new do + Thread.current[:value] = 1 + end + t2 = Thread.new do + Thread.current[:value] = 2 + end + [t1,t2].each {|x| x.join} + t1[:value].should == 1 + t2[:value].should == 2 + end + + it "is accessible using strings or symbols" do + t1 = Thread.new do + Thread.current[:value] = 1 + end + t2 = Thread.new do + Thread.current["value"] = 2 + end + [t1,t2].each {|x| x.join} + t1[:value].should == 1 + t1["value"].should == 1 + t2[:value].should == 2 + t2["value"].should == 2 + end + + it "raises exceptions on the wrong type of keys" do + lambda { Thread.current[nil] }.should raise_error(TypeError) + lambda { Thread.current[5] }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/thread/element_set_spec.rb b/spec/rubyspec/core/thread/element_set_spec.rb new file mode 100644 index 0000000000..47b4d06328 --- /dev/null +++ b/spec/rubyspec/core/thread/element_set_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#[]=" do + after :each do + Thread.current[:value] = nil + end + + it "raises a RuntimeError if the thread is frozen" do + running = false + t = Thread.new do + Thread.pass until running + t.freeze + t[:foo] = "bar" + end + running = true + lambda { t.join }.should raise_error(RuntimeError) + end + + it "raises exceptions on the wrong type of keys" do + lambda { Thread.current[nil] = true }.should raise_error(TypeError) + lambda { Thread.current[5] = true }.should raise_error(TypeError) + end + + it "is not shared across fibers" do + fib = Fiber.new do + Thread.current[:value] = 1 + Fiber.yield + Thread.current[:value].should == 1 + end + fib.resume + Thread.current[:value].should be_nil + Thread.current[:value] = 2 + fib.resume + Thread.current[:value] = 2 + end + + it "stores a local in another thread when in a fiber" do + fib = Fiber.new do + t = Thread.new do + sleep + Thread.current[:value].should == 1 + end + + Thread.pass while t.status and t.status != "sleep" + t[:value] = 1 + t.wakeup + t.join + end + fib.resume + end +end diff --git a/spec/rubyspec/core/thread/exclusive_spec.rb b/spec/rubyspec/core/thread/exclusive_spec.rb new file mode 100644 index 0000000000..66c87f4713 --- /dev/null +++ b/spec/rubyspec/core/thread/exclusive_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread.exclusive" do + before :each do + ScratchPad.clear + end + + it "yields to the block" do + Thread.exclusive { ScratchPad.record true } + ScratchPad.recorded.should == true + end + + it "returns the result of yielding" do + Thread.exclusive { :result }.should == :result + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/thread/exit_spec.rb b/spec/rubyspec/core/thread/exit_spec.rb new file mode 100644 index 0000000000..0fb329e66f --- /dev/null +++ b/spec/rubyspec/core/thread/exit_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/exit', __FILE__) + +describe "Thread#exit!" do + it "needs to be reviewed for spec completeness" +end + +describe "Thread.exit" do + it "causes the current thread to exit" do + thread = Thread.new { Thread.exit; sleep } + thread.join + thread.status.should be_false + end +end diff --git a/spec/rubyspec/core/thread/fixtures/classes.rb b/spec/rubyspec/core/thread/fixtures/classes.rb new file mode 100644 index 0000000000..7f7bb69a61 --- /dev/null +++ b/spec/rubyspec/core/thread/fixtures/classes.rb @@ -0,0 +1,288 @@ +unless defined? Channel + require 'thread' + class Channel < Queue + alias receive shift + end +end + +module ThreadSpecs + + class SubThread < Thread + def initialize(*args) + super { args.first << 1 } + end + end + + class Status + attr_reader :thread, :inspect, :status + def initialize(thread) + @thread = thread + @alive = thread.alive? + @inspect = thread.inspect + @status = thread.status + @stop = thread.stop? + end + + def alive? + @alive + end + + def stop? + @stop + end + end + + # TODO: In the great Thread spec rewrite, abstract this + class << self + attr_accessor :state + end + + def self.clear_state + @state = nil + end + + def self.spin_until_sleeping(t) + Thread.pass while t.status and t.status != "sleep" + end + + def self.sleeping_thread + Thread.new do + begin + sleep + ScratchPad.record :woken + rescue Object => e + ScratchPad.record e + end + end + end + + def self.running_thread + Thread.new do + begin + ThreadSpecs.state = :running + loop { Thread.pass } + ScratchPad.record :woken + rescue Object => e + ScratchPad.record e + end + end + end + + def self.completed_thread + Thread.new {} + end + + def self.status_of_current_thread + Thread.new { Status.new(Thread.current) }.value + end + + def self.status_of_running_thread + t = running_thread + Thread.pass while t.status and t.status != "run" + status = Status.new t + t.kill + t.join + status + end + + def self.status_of_completed_thread + t = completed_thread + t.join + Status.new t + end + + def self.status_of_sleeping_thread + t = sleeping_thread + Thread.pass while t.status and t.status != 'sleep' + status = Status.new t + t.run + t.join + status + end + + def self.status_of_blocked_thread + m = Mutex.new + m.lock + t = Thread.new { m.lock } + Thread.pass while t.status and t.status != 'sleep' + status = Status.new t + m.unlock + t.join + status + end + + def self.status_of_aborting_thread + end + + def self.status_of_killed_thread + t = Thread.new { sleep } + Thread.pass while t.status and t.status != 'sleep' + t.kill + t.join + Status.new t + end + + def self.status_of_thread_with_uncaught_exception + t = Thread.new { raise "error" } + begin + t.join + rescue RuntimeError + end + Status.new t + end + + def self.status_of_dying_running_thread + status = nil + t = dying_thread_ensures { status = Status.new Thread.current } + t.join + status + end + + def self.status_of_dying_sleeping_thread + t = dying_thread_ensures { Thread.stop; } + Thread.pass while t.status and t.status != 'sleep' + status = Status.new t + t.wakeup + t.join + status + end + + def self.dying_thread_ensures(kill_method_name=:kill) + Thread.new do + begin + Thread.current.send(kill_method_name) + ensure + yield + end + end + end + + def self.dying_thread_with_outer_ensure(kill_method_name=:kill) + Thread.new do + begin + begin + Thread.current.send(kill_method_name) + ensure + raise "In dying thread" + end + ensure + yield + end + end + end + + def self.join_dying_thread_with_outer_ensure(kill_method_name=:kill) + t = dying_thread_with_outer_ensure(kill_method_name) { yield } + lambda { t.join }.should raise_error(RuntimeError, "In dying thread") + return t + end + + def self.wakeup_dying_sleeping_thread(kill_method_name=:kill) + t = ThreadSpecs.dying_thread_ensures(kill_method_name) { yield } + Thread.pass while t.status and t.status != 'sleep' + t.wakeup + t.join + end + + def self.critical_is_reset + # Create another thread to verify that it can call Thread.critical= + t = Thread.new do + initial_critical = Thread.critical + Thread.critical = true + Thread.critical = false + initial_critical == false && Thread.critical == false + end + v = t.value + t.join + v + end + + def self.counter + @@counter + end + + def self.counter= c + @@counter = c + end + + def self.increment_counter(incr) + incr.times do + begin + Thread.critical = true + @@counter += 1 + ensure + Thread.critical = false + end + end + end + + def self.critical_thread1 + Thread.critical = true + Thread.current.key?(:thread_specs).should == false + end + + def self.critical_thread2(is_thread_stop) + Thread.current[:thread_specs].should == 101 + Thread.critical.should == !is_thread_stop + unless is_thread_stop + Thread.critical = false + end + end + + def self.main_thread1(critical_thread, is_thread_sleep, is_thread_stop) + # Thread.stop resets Thread.critical. Also, with native threads, the Thread.Stop may not have executed yet + # since the main thread will race with the critical thread + unless is_thread_stop + Thread.critical.should == true + end + critical_thread[:thread_specs] = 101 + if is_thread_sleep or is_thread_stop + # Thread#wakeup calls are not queued up. So we need to ensure that the thread is sleeping before calling wakeup + Thread.pass while critical_thread.status and critical_thread.status != "sleep" + critical_thread.wakeup + end + end + + def self.main_thread2(critical_thread) + Thread.pass # The join below seems to cause a deadlock with CRuby unless Thread.pass is called first + critical_thread.join + Thread.critical.should == false + end + + def self.critical_thread_yields_to_main_thread(is_thread_sleep=false, is_thread_stop=false) + @@after_first_sleep = false + + critical_thread = Thread.new do + Thread.pass while Thread.main.status and Thread.main.status != "sleep" + critical_thread1() + Thread.main.wakeup + yield + Thread.pass while @@after_first_sleep != true # Need to ensure that the next statement does not see the first sleep itself + Thread.pass while Thread.main.status and Thread.main.status != "sleep" + critical_thread2(is_thread_stop) + Thread.main.wakeup + end + + sleep 5 + @@after_first_sleep = true + main_thread1(critical_thread, is_thread_sleep, is_thread_stop) + sleep 5 + main_thread2(critical_thread) + end + + def self.create_critical_thread + Thread.new do + Thread.critical = true + yield + Thread.critical = false + end + end + + def self.create_and_kill_critical_thread(pass_after_kill=false) + ThreadSpecs.create_critical_thread do + Thread.current.kill + Thread.pass if pass_after_kill + ScratchPad.record("status=" + Thread.current.status) + end + end +end diff --git a/spec/rubyspec/core/thread/fork_spec.rb b/spec/rubyspec/core/thread/fork_spec.rb new file mode 100644 index 0000000000..d321230812 --- /dev/null +++ b/spec/rubyspec/core/thread/fork_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/start', __FILE__) + +describe "Thread.fork" do + describe "Thread.start" do + it_behaves_like :thread_start, :fork + end +end diff --git a/spec/rubyspec/core/thread/group_spec.rb b/spec/rubyspec/core/thread/group_spec.rb new file mode 100644 index 0000000000..aecc1422ba --- /dev/null +++ b/spec/rubyspec/core/thread/group_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +describe "Thread#group" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/thread/initialize_spec.rb b/spec/rubyspec/core/thread/initialize_spec.rb new file mode 100644 index 0000000000..b6345f03de --- /dev/null +++ b/spec/rubyspec/core/thread/initialize_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#initialize" do + + describe "already initialized" do + + before do + @t = Thread.new { sleep } + end + + after do + @t.kill + @t.join + end + + it "raises a ThreadError" do + lambda { + @t.instance_eval do + initialize {} + end + }.should raise_error(ThreadError) + end + + end + +end diff --git a/spec/rubyspec/core/thread/inspect_spec.rb b/spec/rubyspec/core/thread/inspect_spec.rb new file mode 100644 index 0000000000..759f6e756c --- /dev/null +++ b/spec/rubyspec/core/thread/inspect_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#inspect" do + it "can check it's own status" do + ThreadSpecs.status_of_current_thread.inspect.should include('run') + end + + it "describes a running thread" do + ThreadSpecs.status_of_running_thread.inspect.should include('run') + end + + it "describes a sleeping thread" do + ThreadSpecs.status_of_sleeping_thread.inspect.should include('sleep') + end + + it "describes a blocked thread" do + ThreadSpecs.status_of_blocked_thread.inspect.should include('sleep') + end + + it "describes a completed thread" do + ThreadSpecs.status_of_completed_thread.inspect.should include('dead') + end + + it "describes a killed thread" do + ThreadSpecs.status_of_killed_thread.inspect.should include('dead') + end + + it "describes a thread with an uncaught exception" do + ThreadSpecs.status_of_thread_with_uncaught_exception.inspect.should include('dead') + end + + it "describes a dying sleeping thread" do + ThreadSpecs.status_of_dying_sleeping_thread.status.should include('sleep') + end + + quarantine! do + it "reports aborting on a killed thread" do + ThreadSpecs.status_of_aborting_thread.inspect.should include('aborting') + end + end +end diff --git a/spec/rubyspec/core/thread/join_spec.rb b/spec/rubyspec/core/thread/join_spec.rb new file mode 100644 index 0000000000..a6dd2da9a3 --- /dev/null +++ b/spec/rubyspec/core/thread/join_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#join" do + it "returns the thread when it is finished" do + t = Thread.new {} + t.join.should equal(t) + end + + it "returns the thread when it is finished when given a timeout" do + t = Thread.new {} + t.join + t.join(0).should equal(t) + end + + it "coerces timeout to a Float if it is not nil" do + t = Thread.new {} + t.join + t.join(0).should equal(t) + t.join(0.0).should equal(t) + t.join(nil).should equal(t) + lambda { t.join(:foo) }.should raise_error TypeError + lambda { t.join("bar") }.should raise_error TypeError + end + + it "returns nil if it is not finished when given a timeout" do + c = Channel.new + t = Thread.new { c.receive } + begin + t.join(0).should == nil + ensure + c << true + end + t.join.should == t + end + + it "accepts a floating point timeout length" do + c = Channel.new + t = Thread.new { c.receive } + begin + t.join(0.01).should == nil + ensure + c << true + end + t.join.should == t + end + + it "raises any exceptions encountered in the thread body" do + t = Thread.new { raise NotImplementedError.new("Just kidding") } + lambda { t.join }.should raise_error(NotImplementedError) + end + + it "returns the dead thread" do + t = Thread.new { Thread.current.kill } + t.join.should equal(t) + end + + it "raises any uncaught exception encountered in ensure block" do + t = ThreadSpecs.dying_thread_ensures { raise NotImplementedError.new("Just kidding") } + lambda { t.join }.should raise_error(NotImplementedError) + end +end diff --git a/spec/rubyspec/core/thread/key_spec.rb b/spec/rubyspec/core/thread/key_spec.rb new file mode 100644 index 0000000000..6cdfc3ca7f --- /dev/null +++ b/spec/rubyspec/core/thread/key_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#key?" do + before :each do + @th = Thread.new do + Thread.current[:oliver] = "a" + end + @th.join + end + + it "tests for existance of thread local variables using symbols or strings" do + @th.key?(:oliver).should == true + @th.key?("oliver").should == true + @th.key?(:stanley).should == false + @th.key?(:stanley.to_s).should == false + end + + it "raises exceptions on the wrong type of keys" do + lambda { Thread.current.key? nil }.should raise_error(TypeError) + lambda { Thread.current.key? 5 }.should raise_error(TypeError) + end + + it "is not shared across fibers" do + fib = Fiber.new do + Thread.current[:val1] = 1 + Fiber.yield + Thread.current.key?(:val1).should be_true + Thread.current.key?(:val2).should be_false + end + Thread.current.key?(:val1).should_not be_true + fib.resume + Thread.current[:val2] = 2 + fib.resume + Thread.current.key?(:val1).should be_false + Thread.current.key?(:val2).should be_true + end + + it "stores a local in another thread when in a fiber" do + fib = Fiber.new do + t = Thread.new do + sleep + Thread.current.key?(:value).should be_true + end + + Thread.pass while t.status and t.status != "sleep" + t[:value] = 1 + t.wakeup + t.join + end + fib.resume + end +end diff --git a/spec/rubyspec/core/thread/keys_spec.rb b/spec/rubyspec/core/thread/keys_spec.rb new file mode 100644 index 0000000000..0fc8184e06 --- /dev/null +++ b/spec/rubyspec/core/thread/keys_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#keys" do + it "returns an array of the names of the thread-local variables as symbols" do + th = Thread.new do + Thread.current["cat"] = 'woof' + Thread.current[:cat] = 'meow' + Thread.current[:dog] = 'woof' + end + th.join + th.keys.sort_by {|x| x.to_s}.should == [:cat,:dog] + end + + it "is not shared across fibers" do + fib = Fiber.new do + Thread.current[:val1] = 1 + Fiber.yield + Thread.current.keys.should include(:val1) + Thread.current.keys.should_not include(:val2) + end + Thread.current.keys.should_not include(:val1) + fib.resume + Thread.current[:val2] = 2 + fib.resume + Thread.current.keys.should include(:val2) + Thread.current.keys.should_not include(:val1) + end + + it "stores a local in another thread when in a fiber" do + fib = Fiber.new do + t = Thread.new do + sleep + Thread.current.keys.should include(:value) + end + + Thread.pass while t.status and t.status != "sleep" + t[:value] = 1 + t.wakeup + t.join + end + fib.resume + end +end diff --git a/spec/rubyspec/core/thread/kill_spec.rb b/spec/rubyspec/core/thread/kill_spec.rb new file mode 100644 index 0000000000..cf71307af5 --- /dev/null +++ b/spec/rubyspec/core/thread/kill_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/exit', __FILE__) + +describe "Thread#kill" do + it_behaves_like :thread_exit, :kill +end + +describe "Thread#kill!" do + it "needs to be reviewed for spec completeness" +end + +describe "Thread.kill" do + it "causes the given thread to exit" do + thread = Thread.new { sleep } + Thread.pass while thread.status and thread.status != "sleep" + Thread.kill(thread).should == thread + thread.join + thread.status.should be_false + end +end diff --git a/spec/rubyspec/core/thread/list_spec.rb b/spec/rubyspec/core/thread/list_spec.rb new file mode 100644 index 0000000000..b8deb98260 --- /dev/null +++ b/spec/rubyspec/core/thread/list_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread.list" do + it "includes the current and main thread" do + Thread.list.should include(Thread.current) + Thread.list.should include(Thread.main) + end + + it "includes threads of non-default thread groups" do + t = Thread.new { sleep } + begin + ThreadGroup.new.add(t) + Thread.list.should include(t) + ensure + t.kill + t.join + end + end + + it "does not include deceased threads" do + t = Thread.new { 1; } + t.join + Thread.list.should_not include(t) + end + + it "includes waiting threads" do + c = Channel.new + t = Thread.new { c.receive } + begin + Thread.pass while t.status and t.status != 'sleep' + Thread.list.should include(t) + ensure + c << nil + t.join + end + end +end + +describe "Thread.list" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/thread/main_spec.rb b/spec/rubyspec/core/thread/main_spec.rb new file mode 100644 index 0000000000..0cada8f59d --- /dev/null +++ b/spec/rubyspec/core/thread/main_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread.main" do + it "returns the main thread" do + Thread.new { @main = Thread.main ; @current = Thread.current}.join + @main.should_not == @current + @main.should == Thread.current + end +end diff --git a/spec/rubyspec/core/thread/name_spec.rb b/spec/rubyspec/core/thread/name_spec.rb new file mode 100644 index 0000000000..0417d7a500 --- /dev/null +++ b/spec/rubyspec/core/thread/name_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +ruby_version_is '2.3' do + describe "Thread#name" do + before :each do + @thread = Thread.new {} + end + + after :each do + @thread.join + end + + it "is nil initially" do + @thread.name.should == nil + end + + it "returns the thread name" do + @thread.name = "thread_name" + @thread.name.should == "thread_name" + end + end + + describe "Thread#name=" do + before :each do + @thread = Thread.new {} + end + + after :each do + @thread.join + end + + it "can be set to a String" do + @thread.name = "new thread name" + @thread.name.should == "new thread name" + end + + it "raises an ArgumentError if the name includes a null byte" do + lambda { + @thread.name = "new thread\0name" + }.should raise_error(ArgumentError) + end + + it "can be reset to nil" do + @thread.name = nil + @thread.name.should == nil + end + + it "calls #to_str to convert name to String" do + name = mock("Thread#name") + name.should_receive(:to_str).and_return("a thread name") + + @thread.name = name + @thread.name.should == "a thread name" + end + end +end diff --git a/spec/rubyspec/core/thread/new_spec.rb b/spec/rubyspec/core/thread/new_spec.rb new file mode 100644 index 0000000000..b1ed5560a1 --- /dev/null +++ b/spec/rubyspec/core/thread/new_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread.new" do + it "creates a thread executing the given block" do + c = Channel.new + Thread.new { c << true }.join + c << false + c.receive.should == true + end + + it "can pass arguments to the thread block" do + arr = [] + a, b, c = 1, 2, 3 + t = Thread.new(a,b,c) {|d,e,f| arr << d << e << f } + t.join + arr.should == [a,b,c] + end + + it "raises an exception when not given a block" do + lambda { Thread.new }.should raise_error(ThreadError) + end + + it "creates a subclass of thread calls super with a block in initialize" do + arr = [] + t = ThreadSpecs::SubThread.new(arr) + t.join + arr.should == [1] + end + + it "calls #initialize and raises an error if super not used" do + c = Class.new(Thread) do + def initialize + end + end + + lambda { + c.new + }.should raise_error(ThreadError) + end + + it "calls and respects #initialize for the block to use" do + c = Class.new(Thread) do + def initialize + ScratchPad.record [:good] + super { ScratchPad << :in_thread } + end + end + + t = c.new + t.join + + ScratchPad.recorded.should == [:good, :in_thread] + end + +end diff --git a/spec/rubyspec/core/thread/pass_spec.rb b/spec/rubyspec/core/thread/pass_spec.rb new file mode 100644 index 0000000000..128de934ac --- /dev/null +++ b/spec/rubyspec/core/thread/pass_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread.pass" do + it "returns nil" do + Thread.pass.should == nil + end +end diff --git a/spec/rubyspec/core/thread/priority_spec.rb b/spec/rubyspec/core/thread/priority_spec.rb new file mode 100644 index 0000000000..b986fb7a0d --- /dev/null +++ b/spec/rubyspec/core/thread/priority_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#priority" do + before do + @current_priority = Thread.current.priority + ThreadSpecs.clear_state + @thread = Thread.new { Thread.pass until ThreadSpecs.state == :exit } + end + + after do + ThreadSpecs.state = :exit + @thread.join + end + + it "inherits the priority of the current thread while running" do + @thread.alive?.should be_true + @thread.priority.should == @current_priority + end + + it "maintain the priority of the current thread after death" do + ThreadSpecs.state = :exit + @thread.join + @thread.alive?.should be_false + @thread.priority.should == @current_priority + end + + it "returns an integer" do + @thread.priority.should be_kind_of(Integer) + end +end + +describe "Thread#priority=" do + before do + ThreadSpecs.clear_state + @thread = Thread.new {} + end + + after do + @thread.join + end + + describe "when set with an integer" do + it "returns an integer" do + value = (@thread.priority = 3) + value.should == 3 + end + + it "clamps the priority to -3..3" do + @thread.priority = 42 + @thread.priority.should == 3 + @thread.priority = -42 + @thread.priority.should == -3 + end + end + + describe "when set with a non-integer" do + it "raises a type error" do + lambda{ @thread.priority = Object.new }.should raise_error(TypeError) + end + end + + it "sets priority even when the thread has died" do + @thread.join + @thread.priority = 3 + @thread.priority.should == 3 + end +end diff --git a/spec/rubyspec/core/thread/raise_spec.rb b/spec/rubyspec/core/thread/raise_spec.rb new file mode 100644 index 0000000000..93e0f048b1 --- /dev/null +++ b/spec/rubyspec/core/thread/raise_spec.rb @@ -0,0 +1,175 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/kernel/raise', __FILE__) + +describe "Thread#raise" do + it "ignores dead threads" do + t = Thread.new { :dead } + Thread.pass while t.alive? + lambda {t.raise("Kill the thread")}.should_not raise_error + lambda {t.value}.should_not raise_error + end +end + +describe "Thread#raise on a sleeping thread" do + before :each do + ScratchPad.clear + @thr = ThreadSpecs.sleeping_thread + Thread.pass while @thr.status and @thr.status != "sleep" + end + + after :each do + @thr.kill + @thr.join + end + + it "raises a RuntimeError if no exception class is given" do + @thr.raise + Thread.pass while @thr.status + ScratchPad.recorded.should be_kind_of(RuntimeError) + end + + it "raises the given exception" do + @thr.raise Exception + Thread.pass while @thr.status + ScratchPad.recorded.should be_kind_of(Exception) + end + + it "raises the given exception with the given message" do + @thr.raise Exception, "get to work" + Thread.pass while @thr.status + ScratchPad.recorded.should be_kind_of(Exception) + ScratchPad.recorded.message.should == "get to work" + end + + it "raises the given exception and the backtrace is the one of the interrupted thread" do + @thr.raise Exception + Thread.pass while @thr.status + ScratchPad.recorded.should be_kind_of(Exception) + ScratchPad.recorded.backtrace[0].should include("sleep") + end + + it "is captured and raised by Thread#value" do + t = Thread.new do + sleep + end + + ThreadSpecs.spin_until_sleeping(t) + + t.raise + lambda { t.value }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when called with no arguments inside rescue" do + t = Thread.new do + begin + 1/0 + rescue ZeroDivisionError + sleep + end + end + begin + raise RangeError + rescue + ThreadSpecs.spin_until_sleeping(t) + t.raise + end + lambda {t.value}.should raise_error(RuntimeError) + end +end + +describe "Thread#raise on a running thread" do + before :each do + ScratchPad.clear + ThreadSpecs.clear_state + + @thr = ThreadSpecs.running_thread + Thread.pass until ThreadSpecs.state == :running + end + + after :each do + @thr.kill + @thr.join + end + + it "raises a RuntimeError if no exception class is given" do + @thr.raise + Thread.pass while @thr.status + ScratchPad.recorded.should be_kind_of(RuntimeError) + end + + it "raises the given exception" do + @thr.raise Exception + Thread.pass while @thr.status + ScratchPad.recorded.should be_kind_of(Exception) + end + + it "raises the given exception with the given message" do + @thr.raise Exception, "get to work" + Thread.pass while @thr.status + ScratchPad.recorded.should be_kind_of(Exception) + ScratchPad.recorded.message.should == "get to work" + end + + it "can go unhandled" do + t = Thread.new do + loop { Thread.pass } + end + + t.raise + lambda {t.value}.should raise_error(RuntimeError) + end + + it "raises the given argument even when there is an active exception" do + raised = false + t = Thread.new do + begin + 1/0 + rescue ZeroDivisionError + raised = true + loop { Thread.pass } + end + end + begin + raise "Create an active exception for the current thread too" + rescue + Thread.pass until raised + t.raise RangeError + lambda {t.value}.should raise_error(RangeError) + end + end + + it "raises a RuntimeError when called with no arguments inside rescue" do + raised = false + t = Thread.new do + begin + 1/0 + rescue ZeroDivisionError + raised = true + loop { } + end + end + begin + raise RangeError + rescue + Thread.pass until raised + t.raise + end + lambda {t.value}.should raise_error(RuntimeError) + end +end + +describe "Thread#raise on same thread" do + it_behaves_like :kernel_raise, :raise, Thread.current + + it "raises a RuntimeError when called with no arguments inside rescue" do + t = Thread.new do + begin + 1/0 + rescue ZeroDivisionError + Thread.current.raise + end + end + lambda {t.value}.should raise_error(RuntimeError) + end +end diff --git a/spec/rubyspec/core/thread/run_spec.rb b/spec/rubyspec/core/thread/run_spec.rb new file mode 100644 index 0000000000..26ed9ed961 --- /dev/null +++ b/spec/rubyspec/core/thread/run_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +require File.expand_path('../shared/wakeup', __FILE__) + +describe "Thread#run" do + it_behaves_like :thread_wakeup, :run +end + diff --git a/spec/rubyspec/core/thread/set_trace_func_spec.rb b/spec/rubyspec/core/thread/set_trace_func_spec.rb new file mode 100644 index 0000000000..6dd5448d79 --- /dev/null +++ b/spec/rubyspec/core/thread/set_trace_func_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread#set_trace_func" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/thread/shared/exit.rb b/spec/rubyspec/core/thread/shared/exit.rb new file mode 100644 index 0000000000..f15da360fd --- /dev/null +++ b/spec/rubyspec/core/thread/shared/exit.rb @@ -0,0 +1,176 @@ +describe :thread_exit, shared: true do + before :each do + ScratchPad.clear + end + + it "kills sleeping thread" do + sleeping_thread = Thread.new do + sleep + ScratchPad.record :after_sleep + end + Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep" + sleeping_thread.send(@method) + sleeping_thread.join + ScratchPad.recorded.should == nil + end + + it "kills current thread" do + thread = Thread.new do + Thread.current.send(@method) + ScratchPad.record :after_sleep + end + thread.join + ScratchPad.recorded.should == nil + end + + it "runs ensure clause" do + thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record :in_ensure_clause } + thread.join + ScratchPad.recorded.should == :in_ensure_clause + end + + it "runs nested ensure clauses" do + ScratchPad.record [] + @outer = Thread.new do + begin + @inner = Thread.new do + begin + sleep + ensure + ScratchPad << :inner_ensure_clause + end + end + sleep + ensure + ScratchPad << :outer_ensure_clause + @inner.send(@method) + @inner.join + end + end + Thread.pass while @outer.status and @outer.status != "sleep" + Thread.pass until @inner + Thread.pass while @inner.status and @inner.status != "sleep" + @outer.send(@method) + @outer.join + ScratchPad.recorded.should include(:inner_ensure_clause) + ScratchPad.recorded.should include(:outer_ensure_clause) + end + + it "does not set $!" do + thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record $! } + thread.join + ScratchPad.recorded.should == nil + end + + it "cannot be rescued" do + thread = Thread.new do + begin + Thread.current.send(@method) + rescue Exception + ScratchPad.record :in_rescue + end + ScratchPad.record :end_of_thread_block + end + + thread.join + ScratchPad.recorded.should == nil + end + + with_feature :fiber do + it "kills the entire thread when a fiber is active" do + t = Thread.new do + Fiber.new do + sleep + end.resume + ScratchPad.record :fiber_resumed + end + Thread.pass while t.status and t.status != "sleep" + t.send(@method) + t.join + ScratchPad.recorded.should == nil + end + end + + # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed + quarantine! do + it "killing dying running does nothing" do + in_ensure_clause = false + exit_loop = true + t = ThreadSpecs.dying_thread_ensures do + in_ensure_clause = true + loop { if exit_loop then break end } + ScratchPad.record :after_stop + end + + Thread.pass until in_ensure_clause == true + 10.times { t.send(@method); Thread.pass } + exit_loop = true + t.join + ScratchPad.recorded.should == :after_stop + end + end + + quarantine! do + + it "propogates inner exception to Thread.join if there is an outer ensure clause" do + thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { } + lambda { thread.join }.should raise_error(RuntimeError, "In dying thread") + end + + it "runs all outer ensure clauses even if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record :in_outer_ensure_clause } + ScratchPad.recorded.should == :in_outer_ensure_clause + end + + it "sets $! in outer ensure clause if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record $! } + ScratchPad.recorded.to_s.should == "In dying thread" + end + end + + it "can be rescued by outer rescue clause when inner ensure clause raises exception" do + thread = Thread.new do + begin + begin + Thread.current.send(@method) + ensure + raise "In dying thread" + end + rescue Exception + ScratchPad.record $! + end + :end_of_thread_block + end + + thread.value.should == :end_of_thread_block + ScratchPad.recorded.to_s.should == "In dying thread" + end + + it "is deferred if ensure clause does Thread.stop" do + ThreadSpecs.wakeup_dying_sleeping_thread(@method) { Thread.stop; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + + # Hangs on 1.8.6.114 OS X, possibly also on Linux + quarantine! do + it "is deferred if ensure clause sleeps" do + ThreadSpecs.wakeup_dying_sleeping_thread(@method) { sleep; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + end + + # This case occurred in JRuby where native threads are used to provide + # the same behavior as MRI green threads. Key to this issue was the fact + # that the thread which called #exit in its block was also being explicitly + # sent #join from outside the thread. The 100.times provides a certain + # probability that the deadlock will occur. It was sufficient to reliably + # reproduce the deadlock in JRuby. + it "does not deadlock when called from within the thread while being joined from without" do + 100.times do + t = Thread.new { Thread.stop; Thread.current.send(@method) } + Thread.pass while t.status and t.status != "sleep" + t.wakeup.should == t + t.join.should == t + end + end +end diff --git a/spec/rubyspec/core/thread/shared/start.rb b/spec/rubyspec/core/thread/shared/start.rb new file mode 100644 index 0000000000..80ce063a0e --- /dev/null +++ b/spec/rubyspec/core/thread/shared/start.rb @@ -0,0 +1,41 @@ +describe :thread_start, shared: true do + before :each do + ScratchPad.clear + end + + it "raises an ArgumentError if not passed a block" do + lambda { + Thread.send(@method) + }.should raise_error(ArgumentError) + end + + it "spawns a new Thread running the block" do + run = false + t = Thread.send(@method) { run = true } + t.should be_kind_of(Thread) + t.join + + run.should be_true + end + + it "respects Thread subclasses" do + c = Class.new(Thread) + t = c.send(@method) { } + t.should be_kind_of(c) + + t.join + end + + it "does not call #initialize" do + c = Class.new(Thread) do + def initialize + ScratchPad.record :bad + end + end + + t = c.send(@method) { } + t.join + + ScratchPad.recorded.should == nil + end +end diff --git a/spec/rubyspec/core/thread/shared/wakeup.rb b/spec/rubyspec/core/thread/shared/wakeup.rb new file mode 100644 index 0000000000..71838d88e5 --- /dev/null +++ b/spec/rubyspec/core/thread/shared/wakeup.rb @@ -0,0 +1,61 @@ +describe :thread_wakeup, shared: true do + it "can interrupt Kernel#sleep" do + exit_loop = false + after_sleep1 = false + after_sleep2 = false + + t = Thread.new do + while true + break if exit_loop == true + Thread.pass + end + + sleep + after_sleep1 = true + + sleep + after_sleep2 = true + end + + 10.times { t.send(@method); Thread.pass } + t.status.should_not == "sleep" + + exit_loop = true + + 10.times { sleep 0.1 if t.status and t.status != "sleep" } + after_sleep1.should == false # t should be blocked on the first sleep + t.send(@method) + + 10.times { sleep 0.1 if after_sleep1 != true } + 10.times { sleep 0.1 if t.status and t.status != "sleep" } + after_sleep2.should == false # t should be blocked on the second sleep + t.send(@method) + + t.join + end + + it "does not result in a deadlock" do + t = Thread.new do + 100.times { Thread.stop } + end + + while t.status + begin + t.send(@method) + rescue ThreadError + # The thread might die right after. + t.status.should == false + end + Thread.pass + end + + t.status.should == false + t.join + end + + it "raises a ThreadError when trying to wake up a dead thread" do + t = Thread.new { 1 } + t.join + lambda { t.send @method }.should raise_error(ThreadError) + end +end diff --git a/spec/rubyspec/core/thread/start_spec.rb b/spec/rubyspec/core/thread/start_spec.rb new file mode 100644 index 0000000000..932e782382 --- /dev/null +++ b/spec/rubyspec/core/thread/start_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/start', __FILE__) + +describe "Thread.start" do + describe "Thread.start" do + it_behaves_like :thread_start, :start + end +end diff --git a/spec/rubyspec/core/thread/status_spec.rb b/spec/rubyspec/core/thread/status_spec.rb new file mode 100644 index 0000000000..00d7a03ab8 --- /dev/null +++ b/spec/rubyspec/core/thread/status_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#status" do + it "can check it's own status" do + ThreadSpecs.status_of_current_thread.status.should == 'run' + end + + it "describes a running thread" do + ThreadSpecs.status_of_running_thread.status.should == 'run' + end + + it "describes a sleeping thread" do + ThreadSpecs.status_of_sleeping_thread.status.should == 'sleep' + end + + it "describes a blocked thread" do + ThreadSpecs.status_of_blocked_thread.status.should == 'sleep' + end + + it "describes a completed thread" do + ThreadSpecs.status_of_completed_thread.status.should == false + end + + it "describes a killed thread" do + ThreadSpecs.status_of_killed_thread.status.should == false + end + + it "describes a thread with an uncaught exception" do + ThreadSpecs.status_of_thread_with_uncaught_exception.status.should == nil + end + + it "describes a dying sleeping thread" do + ThreadSpecs.status_of_dying_sleeping_thread.status.should == 'sleep' + end + + quarantine! do + it "reports aborting on a killed thread" do + ThreadSpecs.status_of_aborting_thread.status.should == 'aborting' + end + end +end diff --git a/spec/rubyspec/core/thread/stop_spec.rb b/spec/rubyspec/core/thread/stop_spec.rb new file mode 100644 index 0000000000..79be623906 --- /dev/null +++ b/spec/rubyspec/core/thread/stop_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread.stop" do + it "causes the current thread to sleep indefinitely" do + t = Thread.new { Thread.stop; 5 } + Thread.pass while t.status and t.status != 'sleep' + t.status.should == 'sleep' + t.run + t.value.should == 5 + end +end + +describe "Thread#stop?" do + it "can check it's own status" do + ThreadSpecs.status_of_current_thread.stop?.should == false + end + + it "describes a running thread" do + ThreadSpecs.status_of_running_thread.stop?.should == false + end + + it "describes a sleeping thread" do + ThreadSpecs.status_of_sleeping_thread.stop?.should == true + end + + it "describes a blocked thread" do + ThreadSpecs.status_of_blocked_thread.stop?.should == true + end + + it "describes a completed thread" do + ThreadSpecs.status_of_completed_thread.stop?.should == true + end + + it "describes a killed thread" do + ThreadSpecs.status_of_killed_thread.stop?.should == true + end + + it "describes a thread with an uncaught exception" do + ThreadSpecs.status_of_thread_with_uncaught_exception.stop?.should == true + end + + it "describes a dying running thread" do + ThreadSpecs.status_of_dying_running_thread.stop?.should == false + end + + it "describes a dying sleeping thread" do + ThreadSpecs.status_of_dying_sleeping_thread.stop?.should == true + end + + quarantine! do + it "reports aborting on a killed thread" do + ThreadSpecs.status_of_aborting_thread.stop?.should == false + end + end +end diff --git a/spec/rubyspec/core/thread/terminate_spec.rb b/spec/rubyspec/core/thread/terminate_spec.rb new file mode 100644 index 0000000000..9ac4a5b6f8 --- /dev/null +++ b/spec/rubyspec/core/thread/terminate_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/exit', __FILE__) + +describe "Thread#terminate" do + it_behaves_like :thread_exit, :terminate +end + +describe "Thread#terminate!" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/thread/thread_variable_get_spec.rb b/spec/rubyspec/core/thread/thread_variable_get_spec.rb new file mode 100644 index 0000000000..0e02c30fad --- /dev/null +++ b/spec/rubyspec/core/thread/thread_variable_get_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread#thread_variable_get" do + before :each do + @t = Thread.new { } + end + + after :each do + @t.join + end + + it "returns nil if the variable is not set" do + @t.thread_variable_get(:a).should be_nil + end + + it "returns the value previously set by #[]=" do + @t.thread_variable_set :a, 49 + @t.thread_variable_get(:a).should == 49 + end + + it "returns a value private to self" do + @t.thread_variable_set :thread_variable_get_spec, 82 + Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil + end +end diff --git a/spec/rubyspec/core/thread/thread_variable_set_spec.rb b/spec/rubyspec/core/thread/thread_variable_set_spec.rb new file mode 100644 index 0000000000..0f55341132 --- /dev/null +++ b/spec/rubyspec/core/thread/thread_variable_set_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread#thread_variable_set" do + before :each do + @t = Thread.new { } + end + + after :each do + @t.join + end + + it "returns the value set" do + (@t.thread_variable_set :a, 2).should == 2 + end + + it "sets a value that will be returned by #thread_variable_get" do + @t.thread_variable_set :a, 49 + @t.thread_variable_get(:a).should == 49 + end + + it "sets a value private to self" do + @t.thread_variable_set :thread_variable_get_spec, 82 + @t.thread_variable_get(:thread_variable_get_spec).should == 82 + Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil + end +end diff --git a/spec/rubyspec/core/thread/thread_variable_spec.rb b/spec/rubyspec/core/thread/thread_variable_spec.rb new file mode 100644 index 0000000000..b409b3abfc --- /dev/null +++ b/spec/rubyspec/core/thread/thread_variable_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread#thread_variable?" do + before :each do + @t = Thread.new { } + end + + after :each do + @t.join + end + + it "returns false if the thread variables do not contain 'key'" do + @t.thread_variable_set :a, 2 + @t.thread_variable?(:b).should be_false + end + + it "returns true if the thread variables contain 'key'" do + @t.thread_variable_set :a, 2 + @t.thread_variable?(:a).should be_true + end +end diff --git a/spec/rubyspec/core/thread/thread_variables_spec.rb b/spec/rubyspec/core/thread/thread_variables_spec.rb new file mode 100644 index 0000000000..538c85c5e4 --- /dev/null +++ b/spec/rubyspec/core/thread/thread_variables_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Thread#thread_variables" do + before :each do + @t = Thread.new { } + end + + after :each do + @t.join + end + + it "returns the keys of all the values set" do + @t.thread_variable_set :a, 2 + @t.thread_variable_set :b, 4 + @t.thread_variable_set :c, 6 + @t.thread_variables.sort.should == [:a, :b, :c] + end + + it "sets a value private to self" do + @t.thread_variable_set :thread_variables_spec_a, 82 + @t.thread_variable_set :thread_variables_spec_b, 82 + Thread.current.thread_variables.should == [] + end +end diff --git a/spec/rubyspec/core/thread/value_spec.rb b/spec/rubyspec/core/thread/value_spec.rb new file mode 100644 index 0000000000..82c0cbf762 --- /dev/null +++ b/spec/rubyspec/core/thread/value_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Thread#value" do + it "returns the result of the block" do + Thread.new { 3 }.value.should == 3 + end + + it "re-raises an error for an uncaught exception" do + t = Thread.new { raise "Hello" } + lambda { t.value }.should raise_error(RuntimeError, "Hello") + end + + it "is nil for a killed thread" do + t = Thread.new { Thread.current.exit } + t.value.should == nil + end +end diff --git a/spec/rubyspec/core/thread/wakeup_spec.rb b/spec/rubyspec/core/thread/wakeup_spec.rb new file mode 100644 index 0000000000..5197a03a35 --- /dev/null +++ b/spec/rubyspec/core/thread/wakeup_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/wakeup', __FILE__) + +describe "Thread#wakeup" do + it_behaves_like :thread_wakeup, :wakeup +end diff --git a/spec/rubyspec/core/threadgroup/add_spec.rb b/spec/rubyspec/core/threadgroup/add_spec.rb new file mode 100644 index 0000000000..3b88d3460e --- /dev/null +++ b/spec/rubyspec/core/threadgroup/add_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "ThreadGroup#add" do + before :each do + @chan1,@chan2 = Channel.new,Channel.new + @thread = Thread.new { @chan1 << :go; @chan2.receive } + @chan1.receive + end + + after :each do + @chan2 << :done + @thread.join + end + + it "adds the given thread to a group and returns self" do + @thread.group.should_not == nil + + tg = ThreadGroup.new + tg.add(@thread).should == tg + @thread.group.should == tg + tg.list.include?(@thread).should == true + end + + it "removes itself from any other threadgroup" do + tg1 = ThreadGroup.new + tg2 = ThreadGroup.new + + tg1.add(@thread) + @thread.group.should == tg1 + tg2.add(@thread) + @thread.group.should == tg2 + tg2.list.include?(@thread).should == true + tg1.list.include?(@thread).should == false + end +end diff --git a/spec/rubyspec/core/threadgroup/default_spec.rb b/spec/rubyspec/core/threadgroup/default_spec.rb new file mode 100644 index 0000000000..d72b86ed39 --- /dev/null +++ b/spec/rubyspec/core/threadgroup/default_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ThreadGroup::Default" do + it "is a ThreadGroup instance" do + ThreadGroup::Default.should be_kind_of(ThreadGroup) + end + + it "is the ThreadGroup of the main thread" do + ThreadGroup::Default.should == Thread.main.group + end +end diff --git a/spec/rubyspec/core/threadgroup/enclose_spec.rb b/spec/rubyspec/core/threadgroup/enclose_spec.rb new file mode 100644 index 0000000000..5827ddb6aa --- /dev/null +++ b/spec/rubyspec/core/threadgroup/enclose_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "ThreadGroup#enclose" do + before :each do + @chan1,@chan2 = Channel.new,Channel.new + @thread = Thread.new { @chan1 << :go; @chan2.receive } + @chan1.receive + end + + after :each do + @chan2 << :done + @thread.join + end + + it "raises a ThreadError if attempting to move a Thread from an enclosed ThreadGroup" do + thread_group = ThreadGroup.new + default_group = @thread.group + thread_group.add(@thread) + thread_group.enclose + lambda do + default_group.add(@thread) + end.should raise_error(ThreadError) + end +end diff --git a/spec/rubyspec/core/threadgroup/enclosed_spec.rb b/spec/rubyspec/core/threadgroup/enclosed_spec.rb new file mode 100644 index 0000000000..2c1c79f24a --- /dev/null +++ b/spec/rubyspec/core/threadgroup/enclosed_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ThreadGroup#enclosed?" do + it "returns false when a ThreadGroup has not been enclosed (default state)" do + thread_group = ThreadGroup.new + thread_group.enclosed?.should be_false + end + + it "returns true when a ThreadGroup is enclosed" do + thread_group = ThreadGroup.new + thread_group.enclose + thread_group.enclosed?.should be_true + end +end diff --git a/spec/rubyspec/core/threadgroup/fixtures/classes.rb b/spec/rubyspec/core/threadgroup/fixtures/classes.rb new file mode 100644 index 0000000000..7dfe5e92d1 --- /dev/null +++ b/spec/rubyspec/core/threadgroup/fixtures/classes.rb @@ -0,0 +1,6 @@ +unless defined? Channel + require 'thread' + class Channel < Queue + alias receive shift + end +end diff --git a/spec/rubyspec/core/threadgroup/list_spec.rb b/spec/rubyspec/core/threadgroup/list_spec.rb new file mode 100644 index 0000000000..aa7b3f73fa --- /dev/null +++ b/spec/rubyspec/core/threadgroup/list_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "ThreadGroup#list" do + it "returns the list of threads in the group" do + chan = Channel.new + th1 = Thread.new { chan << :go; sleep } + chan.receive.should == :go + tg = ThreadGroup.new + tg.add(th1) + tg.list.should include(th1) + + th2 = Thread.new { chan << :go; sleep } + chan.receive.should == :go + + tg.add(th2) + (tg.list & [th1, th2]).should include(th1, th2) + + Thread.pass while th1.status and th1.status != 'sleep' + Thread.pass while th2.status and th2.status != 'sleep' + th1.run; th1.join + th2.run; th2.join + end +end diff --git a/spec/rubyspec/core/time/_dump_spec.rb b/spec/rubyspec/core/time/_dump_spec.rb new file mode 100644 index 0000000000..bec16dab69 --- /dev/null +++ b/spec/rubyspec/core/time/_dump_spec.rb @@ -0,0 +1,56 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#_dump" do + before :each do + @local = Time.at(946812800) + @t = Time.at(946812800) + @t = @t.gmtime + @s = @t.send(:_dump) + end + + it "is a private method" do + Time.should have_private_instance_method(:_dump, false) + end + + # http://redmine.ruby-lang.org/issues/show/627 + it "preserves the GMT flag" do + @t.gmt?.should == true + dump = @t.send(:_dump).unpack("VV").first + ((dump >> 30) & 0x1).should == 1 + + @local.gmt?.should == false + dump = @local.send(:_dump).unpack("VV").first + ((dump >> 30) & 0x1).should == 0 + end + + it "dumps a Time object to a bytestring" do + @s.should be_an_instance_of(String) + @s.should == [3222863947, 2235564032].pack("VV") + end + + it "dumps an array with a date as first element" do + high = 1 << 31 | + (@t.gmt? ? 1 : 0) << 30 | + (@t.year - 1900) << 14 | + (@t.mon - 1) << 10 | + @t.mday << 5 | + @t.hour + + high.should == @s.unpack("VV").first + end + + it "dumps an array with a time as second element" do + low = @t.min << 26 | + @t.sec << 20 | + @t.usec + low.should == @s.unpack("VV").last + end + + it "dumps like MRI's marshaled time format" do + t = Time.utc(2000, 1, 15, 20, 1, 1, 203).localtime + + t.send(:_dump).should == "\364\001\031\200\313\000\020\004" + end +end + diff --git a/spec/rubyspec/core/time/_load_spec.rb b/spec/rubyspec/core/time/_load_spec.rb new file mode 100644 index 0000000000..12fcb219ed --- /dev/null +++ b/spec/rubyspec/core/time/_load_spec.rb @@ -0,0 +1,54 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time._load" do + it "is a private method" do + Time.should have_private_method(:_load, false) + end + + # http://redmine.ruby-lang.org/issues/show/627 + it "loads a time object in the new format" do + t = Time.local(2000, 1, 15, 20, 1, 1) + t = t.gmtime + + high = 1 << 31 | + (t.gmt? ? 1 : 0) << 30 | + (t.year - 1900) << 14 | + (t.mon - 1) << 10 | + t.mday << 5 | + t.hour + + low = t.min << 26 | + t.sec << 20 | + t.usec + + Time.send(:_load, [high, low].pack("VV")).should == t + end + + it "loads a time object in the old UNIX timestamp based format" do + t = Time.local(2000, 1, 15, 20, 1, 1, 203) + timestamp = t.to_i + + high = timestamp & ((1 << 31) - 1) + + low = t.usec + + Time.send(:_load, [high, low].pack("VV")).should == t + end + + it "loads MRI's marshaled time format" do + t = Marshal.load("\004\bu:\tTime\r\320\246\e\200\320\001\r\347") + t.utc + + t.to_s.should == "2010-10-22 16:57:48 UTC" + end + + with_feature :encoding do + it "treats the data as binary data" do + data = "\x04\bu:\tTime\r\fM\x1C\xC0\x00\x00\xD0\xBE" + data.force_encoding Encoding::UTF_8 + t = Marshal.load(data) + t.to_s.should == "2013-04-08 12:47:45 UTC" + end + end +end diff --git a/spec/rubyspec/core/time/asctime_spec.rb b/spec/rubyspec/core/time/asctime_spec.rb new file mode 100644 index 0000000000..3303e06f21 --- /dev/null +++ b/spec/rubyspec/core/time/asctime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/asctime', __FILE__) + +describe "Time#asctime" do + it_behaves_like(:time_asctime, :asctime) +end diff --git a/spec/rubyspec/core/time/at_spec.rb b/spec/rubyspec/core/time/at_spec.rb new file mode 100644 index 0000000000..40c729316e --- /dev/null +++ b/spec/rubyspec/core/time/at_spec.rb @@ -0,0 +1,145 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time.at" do + describe "passed Numeric" do + it "returns a Time object representing the given number of Integer seconds since 1970-01-01 00:00:00 UTC" do + Time.at(1184027924).getgm.asctime.should == "Tue Jul 10 00:38:44 2007" + end + + it "returns a Time object representing the given number of Float seconds since 1970-01-01 00:00:00 UTC" do + t = Time.at(10.5) + t.usec.should == 500000.0 + t.should_not == Time.at(10) + end + + it "returns a non-UTC Time" do + Time.at(1184027924).utc?.should == false + end + + it "returns a subclass instance on a Time subclass" do + c = Class.new(Time) + t = c.at(0) + t.should be_an_instance_of(c) + end + + it "roundtrips a Rational produced by #to_r" do + t = Time.now() + t2 = Time.at(t.to_r) + + t2.should == t + t2.usec.should == t.usec + t2.nsec.should == t.nsec + end + + describe "passed BigDecimal" do + it "doesn't round input value" do + require 'bigdecimal' + Time.at(BigDecimal.new('1.1')).to_f.should == 1.1 + end + end + end + + describe "passed Time" do + it "creates a new time object with the value given by time" do + t = Time.now + Time.at(t).inspect.should == t.inspect + end + + it "creates a dup time object with the value given by time" do + t1 = Time.new + t2 = Time.at(t1) + t1.object_id.should_not == t2.object_id + end + + it "returns a UTC time if the argument is UTC" do + t = Time.now.getgm + Time.at(t).utc?.should == true + end + + it "returns a non-UTC time if the argument is non-UTC" do + t = Time.now + Time.at(t).utc?.should == false + end + + it "returns a subclass instance" do + c = Class.new(Time) + t = c.at(Time.now) + t.should be_an_instance_of(c) + end + end + + describe "passed non-Time, non-Numeric" do + it "raises a TypeError with a String argument" do + lambda { Time.at("0") }.should raise_error(TypeError) + end + + it "raises a TypeError with a nil argument" do + lambda { Time.at(nil) }.should raise_error(TypeError) + end + + describe "with an argument that responds to #to_int" do + it "coerces using #to_int" do + o = mock('integer') + o.should_receive(:to_int).and_return(0) + Time.at(o).should == Time.at(0) + end + end + + describe "with an argument that responds to #to_r" do + it "coerces using #to_r" do + o = mock_numeric('rational') + o.should_receive(:to_r).and_return(Rational(5, 2)) + Time.at(o).should == Time.at(Rational(5, 2)) + end + end + end + + describe "passed [Integer, Numeric]" do + it "returns a Time object representing the given number of seconds and Integer microseconds since 1970-01-01 00:00:00 UTC" do + t = Time.at(10, 500000) + t.tv_sec.should == 10 + t.tv_usec.should == 500000 + end + + it "returns a Time object representing the given number of seconds and Float microseconds since 1970-01-01 00:00:00 UTC" do + t = Time.at(10, 500.500) + t.tv_sec.should == 10 + t.tv_nsec.should == 500500 + end + end + + describe "with a second argument that responds to #to_int" do + it "coerces using #to_int" do + o = mock('integer') + o.should_receive(:to_int).and_return(10) + Time.at(0, o).should == Time.at(0, 10) + end + end + + describe "with a second argument that responds to #to_r" do + it "coerces using #to_r" do + o = mock_numeric('rational') + o.should_receive(:to_r).and_return(Rational(5, 2)) + Time.at(0, o).should == Time.at(0, Rational(5, 2)) + end + end + + describe "passed [Integer, nil]" do + it "raises a TypeError" do + lambda { Time.at(0, nil) }.should raise_error(TypeError) + end + end + + describe "passed [Integer, String]" do + it "raises a TypeError" do + lambda { Time.at(0, "0") }.should raise_error(TypeError) + end + end + + describe "passed [Time, Integer]" do + # #8173 + it "raises a TypeError" do + lambda { Time.at(Time.now, 500000) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/core/time/comparison_spec.rb b/spec/rubyspec/core/time/comparison_spec.rb new file mode 100644 index 0000000000..c5a5b83d28 --- /dev/null +++ b/spec/rubyspec/core/time/comparison_spec.rb @@ -0,0 +1,94 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#<=>" do + it "returns 1 if the first argument is a point in time after the second argument" do + (Time.now <=> Time.at(0)).should == 1 + end + + it "returns 1 if the first argument is a point in time after the second argument (down to a millisecond)" do + (Time.at(0, 1000) <=> Time.at(0, 0)).should == 1 + (Time.at(1202778512, 1000) <=> Time.at(1202778512, 999)).should == 1 + end + + it "returns 1 if the first argument is a point in time after the second argument (down to a microsecond)" do + (Time.at(0, 100) <=> Time.at(0, 0)).should == 1 + (Time.at(1202778512, 100) <=> Time.at(1202778512, 99)).should == 1 + end + + it "returns 0 if time is the same as other" do + (Time.at(1202778513) <=> Time.at(1202778513)).should == 0 + (Time.at(100, 100) <=> Time.at(100, 100)).should == 0 + end + + it "returns -1 if the first argument is a point in time before the second argument" do + (Time.at(0) <=> Time.now).should == -1 + (Time.at(100, 100) <=> Time.at(101, 100)).should == -1 + end + + it "returns -1 if the first argument is a point in time before the second argument (down to a millisecond)" do + (Time.at(0, 0) <=> Time.at(0, 1000)).should == -1 + end + + it "returns -1 if the first argument is a point in time before the second argument (down to a microsecond)" do + (Time.at(0, 0) <=> Time.at(0, 100)).should == -1 + end + + it "returns 1 if the first argument is a fraction of a microsecond after the second argument" do + (Time.at(100, Rational(1,1000)) <=> Time.at(100, 0)).should == 1 + end + + it "returns 0 if time is the same as other, including fractional microseconds" do + (Time.at(100, Rational(1,1000)) <=> Time.at(100, Rational(1,1000))).should == 0 + end + + it "returns -1 if the first argument is a fraction of a microsecond before the second argument" do + (Time.at(100, 0) <=> Time.at(100, Rational(1,1000))).should == -1 + end + + describe "given a non-Time argument" do + it "returns nil if argument <=> self returns nil" do + t = Time.now + obj = mock('time') + obj.should_receive(:<=>).with(t).and_return(nil) + (t <=> obj).should == nil + end + + it "returns -1 if argument <=> self is greater than 0" do + t = Time.now + r = mock('r') + r.should_receive(:>).with(0).and_return(true) + obj = mock('time') + obj.should_receive(:<=>).with(t).and_return(r) + (t <=> obj).should == -1 + end + + it "returns 1 if argument <=> self is not greater than 0 and is less than 0" do + t = Time.now + r = mock('r') + r.should_receive(:>).with(0).and_return(false) + r.should_receive(:<).with(0).and_return(true) + obj = mock('time') + obj.should_receive(:<=>).with(t).and_return(r) + (t <=> obj).should == 1 + end + + it "returns 0 if argument <=> self is neither greater than 0 nor less than 0" do + t = Time.now + r = mock('r') + r.should_receive(:>).with(0).and_return(false) + r.should_receive(:<).with(0).and_return(false) + obj = mock('time') + obj.should_receive(:<=>).with(t).and_return(r) + (t <=> obj).should == 0 + end + + it "returns nil if argument also uses an inverse comparison for <=>" do + t = Time.now + r = mock('r') + def r.<=>(other); other <=> self; end + r.should_receive(:<=>).once + + (t <=> r).should be_nil + end + end +end diff --git a/spec/rubyspec/core/time/ctime_spec.rb b/spec/rubyspec/core/time/ctime_spec.rb new file mode 100644 index 0000000000..cf9c1ee850 --- /dev/null +++ b/spec/rubyspec/core/time/ctime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/asctime', __FILE__) + +describe "Time#ctime" do + it_behaves_like(:time_asctime, :ctime) +end diff --git a/spec/rubyspec/core/time/day_spec.rb b/spec/rubyspec/core/time/day_spec.rb new file mode 100644 index 0000000000..8e77446070 --- /dev/null +++ b/spec/rubyspec/core/time/day_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/day', __FILE__) + +describe "Time#day" do + it_behaves_like(:time_day, :day) +end diff --git a/spec/rubyspec/core/time/dst_spec.rb b/spec/rubyspec/core/time/dst_spec.rb new file mode 100644 index 0000000000..05a0a213c5 --- /dev/null +++ b/spec/rubyspec/core/time/dst_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/isdst', __FILE__) + +describe "Time#dst?" do + it_behaves_like(:time_isdst, :dst?) +end diff --git a/spec/rubyspec/core/time/dup_spec.rb b/spec/rubyspec/core/time/dup_spec.rb new file mode 100644 index 0000000000..16aab04200 --- /dev/null +++ b/spec/rubyspec/core/time/dup_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#dup" do + it "returns a Time object that represents the same time" do + t = Time.at(100) + t.dup.tv_sec.should == t.tv_sec + end + + it "copies the gmt state flag" do + Time.now.gmtime.dup.gmt?.should == true + end + + it "returns an independent Time object" do + t = Time.now + t2 = t.dup + t.gmtime + + t2.gmt?.should == false + end + + it "returns a subclass instance" do + c = Class.new(Time) + t = c.now + + t.should be_an_instance_of(c) + t.dup.should be_an_instance_of(c) + end + + it "returns a clone of Time instance" do + c = Time.dup + t = c.now + + t.should be_an_instance_of(c) + t.should_not be_an_instance_of(Time) + + t.dup.should be_an_instance_of(c) + t.dup.should_not be_an_instance_of(Time) + end +end diff --git a/spec/rubyspec/core/time/eql_spec.rb b/spec/rubyspec/core/time/eql_spec.rb new file mode 100644 index 0000000000..af96c96cc3 --- /dev/null +++ b/spec/rubyspec/core/time/eql_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#eql?" do + it "returns true if self and other have the same whole number of seconds" do + Time.at(100).should eql(Time.at(100)) + end + + it "returns false if self and other have differing whole numbers of seconds" do + Time.at(100).should_not eql(Time.at(99)) + end + + it "returns true if self and other have the same number of microseconds" do + Time.at(100, 100).should eql(Time.at(100, 100)) + end + + it "returns false if self and other have differing numbers of microseconds" do + Time.at(100, 100).should_not eql(Time.at(100, 99)) + end + + it "returns false if self and other have differing fractional microseconds" do + Time.at(100, Rational(100,1000)).should_not eql(Time.at(100, Rational(99,1000))) + end + + it "returns false when given a non-time value" do + Time.at(100, 100).should_not eql("100") + Time.at(100, 100).should_not eql(100) + Time.at(100, 100).should_not eql(100.1) + end +end diff --git a/spec/rubyspec/core/time/fixtures/classes.rb b/spec/rubyspec/core/time/fixtures/classes.rb new file mode 100644 index 0000000000..328f9160f6 --- /dev/null +++ b/spec/rubyspec/core/time/fixtures/classes.rb @@ -0,0 +1,12 @@ +module TimeSpecs + + class SubTime < Time; end + + class MethodHolder + class << self + define_method(:now, &Time.method(:now)) + define_method(:new, &Time.method(:new)) + end + end + +end diff --git a/spec/rubyspec/core/time/friday_spec.rb b/spec/rubyspec/core/time/friday_spec.rb new file mode 100644 index 0000000000..d38a261080 --- /dev/null +++ b/spec/rubyspec/core/time/friday_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#friday?" do + it "returns true if time represents Friday" do + Time.local(2000, 1, 7).friday?.should == true + end + + it "returns false if time doesn't represent Friday" do + Time.local(2000, 1, 1).friday?.should == false + end +end diff --git a/spec/rubyspec/core/time/getgm_spec.rb b/spec/rubyspec/core/time/getgm_spec.rb new file mode 100644 index 0000000000..f091b5c493 --- /dev/null +++ b/spec/rubyspec/core/time/getgm_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/getgm', __FILE__) + +describe "Time#getgm" do + it_behaves_like(:time_getgm, :getgm) +end diff --git a/spec/rubyspec/core/time/getlocal_spec.rb b/spec/rubyspec/core/time/getlocal_spec.rb new file mode 100644 index 0000000000..a94d7f751b --- /dev/null +++ b/spec/rubyspec/core/time/getlocal_spec.rb @@ -0,0 +1,98 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#getlocal" do + it "returns a new time which is the local representation of time" do + # Testing with America/Regina here because it doesn't have DST. + with_timezone("CST", -6) do + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime.should == Time.local(2007, 1, 9, 6, 0, 0) + end + end + + it "returns a Time with UTC offset specified as an Integer number of seconds" do + t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(3630) + t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630) + t.utc_offset.should == 3630 + end + + platform_is_not :windows do + it "returns a new time with the correct utc_offset according to the set timezone" do + t = Time.new(2005, 2, 27, 22, 50, 0, -3600) + t.utc_offset.should == -3600 + + with_timezone("America/New_York") do + t.getlocal.utc_offset.should == -18000 + end + end + end + + describe "with an argument that responds to #to_int" do + it "coerces using #to_int" do + o = mock('integer') + o.should_receive(:to_int).and_return(3630) + t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o) + t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630) + t.utc_offset.should == 3630 + end + end + + it "returns a Time with a UTC offset of the specified number of Rational seconds" do + t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(Rational(7201, 2)) + t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2)) + t.utc_offset.should eql(Rational(7201, 2)) + end + + describe "with an argument that responds to #to_r" do + it "coerces using #to_r" do + o = mock_numeric('rational') + o.should_receive(:to_r).and_return(Rational(7201, 2)) + t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o) + t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2)) + t.utc_offset.should eql(Rational(7201, 2)) + end + end + + it "returns a Time with a UTC offset specified as +HH:MM" do + t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("+01:00") + t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600) + t.utc_offset.should == 3600 + end + + it "returns a Time with a UTC offset specified as -HH:MM" do + t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal("-01:00") + t.should == Time.new(2007, 1, 9, 11, 0, 0, -3600) + t.utc_offset.should == -3600 + end + + describe "with an argument that responds to #to_str" do + it "coerces using #to_str" do + o = mock('string') + o.should_receive(:to_str).and_return("+01:00") + t = Time.gm(2007, 1, 9, 12, 0, 0).getlocal(o) + t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600) + t.utc_offset.should == 3600 + end + end + + it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do + t = Time.now + lambda { t.getlocal("3600") }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do + t = Time.now + lambda { t.getlocal("-01:00".encode("UTF-16LE")) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the argument represents a value less than or equal to -86400 seconds" do + t = Time.new + t.getlocal(-86400 + 1).utc_offset.should == (-86400 + 1) + lambda { t.getlocal(-86400) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do + t = Time.new + t.getlocal(86400 - 1).utc_offset.should == (86400 - 1) + lambda { t.getlocal(86400) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/time/getutc_spec.rb b/spec/rubyspec/core/time/getutc_spec.rb new file mode 100644 index 0000000000..a6e74cfb98 --- /dev/null +++ b/spec/rubyspec/core/time/getutc_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/getgm', __FILE__) + +describe "Time#getutc" do + it_behaves_like(:time_getgm, :getutc) +end diff --git a/spec/rubyspec/core/time/gm_spec.rb b/spec/rubyspec/core/time/gm_spec.rb new file mode 100644 index 0000000000..a6f2858216 --- /dev/null +++ b/spec/rubyspec/core/time/gm_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gm', __FILE__) +require File.expand_path('../shared/time_params', __FILE__) + +describe "Time.gm" do + it_behaves_like(:time_gm, :gm) + it_behaves_like(:time_params, :gm) + it_behaves_like(:time_params_10_arg, :gm) + it_behaves_like(:time_params_microseconds, :gm) +end diff --git a/spec/rubyspec/core/time/gmt_offset_spec.rb b/spec/rubyspec/core/time/gmt_offset_spec.rb new file mode 100644 index 0000000000..b7613eed2f --- /dev/null +++ b/spec/rubyspec/core/time/gmt_offset_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gmt_offset', __FILE__) + +describe "Time#gmt_offset" do + it_behaves_like(:time_gmt_offset, :gmt_offset) +end diff --git a/spec/rubyspec/core/time/gmt_spec.rb b/spec/rubyspec/core/time/gmt_spec.rb new file mode 100644 index 0000000000..78ebcd0f5e --- /dev/null +++ b/spec/rubyspec/core/time/gmt_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#gmt?" do + it "returns true if time represents a time in UTC (GMT)" do + Time.now.gmt?.should == false + Time.now.gmtime.gmt?.should == true + end +end diff --git a/spec/rubyspec/core/time/gmtime_spec.rb b/spec/rubyspec/core/time/gmtime_spec.rb new file mode 100644 index 0000000000..49a1f10479 --- /dev/null +++ b/spec/rubyspec/core/time/gmtime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gmtime', __FILE__) + +describe "Time#gmtime" do + it_behaves_like(:time_gmtime, :gmtime) +end diff --git a/spec/rubyspec/core/time/gmtoff_spec.rb b/spec/rubyspec/core/time/gmtoff_spec.rb new file mode 100644 index 0000000000..505b5d0c1b --- /dev/null +++ b/spec/rubyspec/core/time/gmtoff_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gmt_offset', __FILE__) + +describe "Time#gmtoff" do + it_behaves_like(:time_gmt_offset, :gmtoff) +end diff --git a/spec/rubyspec/core/time/hash_spec.rb b/spec/rubyspec/core/time/hash_spec.rb new file mode 100644 index 0000000000..77014c5dc8 --- /dev/null +++ b/spec/rubyspec/core/time/hash_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#hash" do + it "returns a Fixnum" do + Time.at(100).hash.should be_an_instance_of(Fixnum) + end + + it "is stable" do + Time.at(1234).hash.should == Time.at(1234).hash + end +end diff --git a/spec/rubyspec/core/time/hour_spec.rb b/spec/rubyspec/core/time/hour_spec.rb new file mode 100644 index 0000000000..65a2ae6ad7 --- /dev/null +++ b/spec/rubyspec/core/time/hour_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#hour" do + it "returns the hour of the day (0..23) for a local Time" do + with_timezone("CET", 1) do + Time.local(1970, 1, 1, 1).hour.should == 1 + end + end + + it "returns the hour of the day for a UTC Time" do + Time.utc(1970, 1, 1, 0).hour.should == 0 + end + + it "returns the hour of the day for a Time with a fixed offset" do + Time.new(2012, 1, 1, 0, 0, 0, -3600).hour.should == 0 + end +end diff --git a/spec/rubyspec/core/time/inspect_spec.rb b/spec/rubyspec/core/time/inspect_spec.rb new file mode 100644 index 0000000000..7f57a2c4cb --- /dev/null +++ b/spec/rubyspec/core/time/inspect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "Time#inspect" do + it_behaves_like :inspect, :inspect +end diff --git a/spec/rubyspec/core/time/isdst_spec.rb b/spec/rubyspec/core/time/isdst_spec.rb new file mode 100644 index 0000000000..de71bf68ff --- /dev/null +++ b/spec/rubyspec/core/time/isdst_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/isdst', __FILE__) + +describe "Time#isdst" do + it_behaves_like(:time_isdst, :isdst) +end diff --git a/spec/rubyspec/core/time/local_spec.rb b/spec/rubyspec/core/time/local_spec.rb new file mode 100644 index 0000000000..63c644e4ea --- /dev/null +++ b/spec/rubyspec/core/time/local_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/local', __FILE__) +require File.expand_path('../shared/time_params', __FILE__) + +describe "Time.local" do + it_behaves_like(:time_local, :local) + it_behaves_like(:time_local_10_arg, :local) + it_behaves_like(:time_params, :local) + it_behaves_like(:time_params_10_arg, :local) + it_behaves_like(:time_params_microseconds, :local) +end diff --git a/spec/rubyspec/core/time/localtime_spec.rb b/spec/rubyspec/core/time/localtime_spec.rb new file mode 100644 index 0000000000..8144fd67e6 --- /dev/null +++ b/spec/rubyspec/core/time/localtime_spec.rb @@ -0,0 +1,113 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#localtime" do + it "converts self to local time, modifying the receiver" do + # Testing with America/Regina here because it doesn't have DST. + with_timezone("CST", -6) do + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime + t.should == Time.local(2007, 1, 9, 6, 0, 0) + end + end + + it "returns self" do + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime.should equal(t) + end + + it "converts time to the UTC offset specified as an Integer number of seconds" do + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime(3630) + t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630) + t.utc_offset.should == 3630 + end + + describe "with an argument that responds to #to_int" do + it "coerces using #to_int" do + o = mock('integer') + o.should_receive(:to_int).and_return(3630) + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime(o) + t.should == Time.new(2007, 1, 9, 13, 0, 30, 3630) + t.utc_offset.should == 3630 + end + end + + it "returns a Time with a UTC offset of the specified number of Rational seconds" do + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime(Rational(7201, 2)) + t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2)) + t.utc_offset.should eql(Rational(7201, 2)) + end + + describe "with an argument that responds to #to_r" do + it "coerces using #to_r" do + o = mock_numeric('rational') + o.should_receive(:to_r).and_return(Rational(7201, 2)) + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime(o) + t.should == Time.new(2007, 1, 9, 13, 0, Rational(1, 2), Rational(7201, 2)) + t.utc_offset.should eql(Rational(7201, 2)) + end + end + + it "returns a Time with a UTC offset specified as +HH:MM" do + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime("+01:00") + t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600) + t.utc_offset.should == 3600 + end + + it "returns a Time with a UTC offset specified as -HH:MM" do + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime("-01:00") + t.should == Time.new(2007, 1, 9, 11, 0, 0, -3600) + t.utc_offset.should == -3600 + end + + platform_is_not :windows do + it "changes the timezone according to the set one" do + t = Time.new(2005, 2, 27, 22, 50, 0, -3600) + t.utc_offset.should == -3600 + + with_timezone("America/New_York") do + t.localtime + end + + t.utc_offset.should == -18000 + end + end + + describe "with an argument that responds to #to_str" do + it "coerces using #to_str" do + o = mock('string') + o.should_receive(:to_str).and_return("+01:00") + t = Time.gm(2007, 1, 9, 12, 0, 0) + t.localtime(o) + t.should == Time.new(2007, 1, 9, 13, 0, 0, 3600) + t.utc_offset.should == 3600 + end + end + + it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do + t = Time.now + lambda { t.localtime("3600") }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do + t = Time.now + lambda { t.localtime("-01:00".encode("UTF-16LE")) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the argument represents a value less than or equal to -86400 seconds" do + t = Time.new + t.localtime(-86400 + 1).utc_offset.should == (-86400 + 1) + lambda { t.localtime(-86400) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do + t = Time.new + t.localtime(86400 - 1).utc_offset.should == (86400 - 1) + lambda { t.localtime(86400) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/time/mday_spec.rb b/spec/rubyspec/core/time/mday_spec.rb new file mode 100644 index 0000000000..5fbff299cc --- /dev/null +++ b/spec/rubyspec/core/time/mday_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/day', __FILE__) + +describe "Time#mday" do + it_behaves_like(:time_day, :mday) +end diff --git a/spec/rubyspec/core/time/min_spec.rb b/spec/rubyspec/core/time/min_spec.rb new file mode 100644 index 0000000000..c1c3ebed3b --- /dev/null +++ b/spec/rubyspec/core/time/min_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#min" do + it "returns the minute of the hour (0..59) for a local Time" do + with_timezone("CET", 1) do + Time.local(1970, 1, 1, 0, 0).min.should == 0 + end + end + + it "returns the minute of the hour for a UTC Time" do + Time.utc(1970, 1, 1, 0, 0).min.should == 0 + end + + it "returns the minute of the hour for a Time with a fixed offset" do + Time.new(2012, 1, 1, 0, 0, 0, -3600).min.should == 0 + end +end diff --git a/spec/rubyspec/core/time/minus_spec.rb b/spec/rubyspec/core/time/minus_spec.rb new file mode 100644 index 0000000000..e4363e2467 --- /dev/null +++ b/spec/rubyspec/core/time/minus_spec.rb @@ -0,0 +1,103 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#-" do + it "decrements the time by the specified amount" do + (Time.at(100) - 100).should == Time.at(0) + (Time.at(100) - Time.at(99)).should == 1.0 + end + + it "understands negative subtractions" do + t = Time.at(100) - -1.3 + t.usec.should == 300000 + t.to_i.should == 101 + end + + #see [ruby-dev:38446] + it "accepts arguments that can be coerced into Rational" do + (obj = mock_numeric('10')).should_receive(:to_r).and_return(Rational(10)) + (Time.at(100) - obj).should == Time.at(90) + end + + it "raises a TypeError if given argument is a coercible String" do + lambda { Time.now - "1" }.should raise_error(TypeError) + lambda { Time.now - "0.1" }.should raise_error(TypeError) + lambda { Time.now - "1/3" }.should raise_error(TypeError) + end + + it "raises TypeError on argument that can't be coerced" do + lambda { Time.now - Object.new }.should raise_error(TypeError) + lambda { Time.now - "stuff" }.should raise_error(TypeError) + end + + it "raises TypeError on nil argument" do + lambda { Time.now - nil }.should raise_error(TypeError) + end + + it "tracks microseconds" do + time = Time.at(0.777777) + time -= 0.654321 + time.usec.should == 123456 + time -= 1 + time.usec.should == 123456 + end + + it "tracks microseconds" do + time = Time.at(Rational(777_777, 1_000_000)) + time -= Rational(654_321, 1_000_000) + time.usec.should == 123_456 + time -= Rational(123_456, 1_000_000) + time.usec.should == 0 + end + + it "tracks nanoseconds" do + time = Time.at(Rational(999_999_999, 1_000_000_000)) + time -= Rational(876_543_210, 1_000_000_000) + time.nsec.should == 123_456_789 + time -= Rational(123_456_789, 1_000_000_000) + time.nsec.should == 0 + end + + it "maintains precision" do + time = Time.at(10) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000) + time.should_not == Time.at(9) + end + + it "maintains microseconds precision" do + time = Time.at(10) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000) + time.usec.should == 999_999 + end + + it "maintains nanoseconds precision" do + time = Time.at(10) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000) + time.nsec.should == 999_999_999 + end + + it "maintains subseconds precision" do + time = Time.at(0) - Rational(1_000_000_000_000_001, 1_000_000_000_000_000) + time.subsec.should == Rational(999_999_999_999_999, 1_000_000_000_000_000) + end + + it "returns a UTC time if self is UTC" do + (Time.utc(2012) - 10).utc?.should == true + end + + it "returns a non-UTC time if self is non-UTC" do + (Time.local(2012) - 10).utc?.should == false + end + + it "returns a time with the same fixed offset as self" do + (Time.new(2012, 1, 1, 0, 0, 0, 3600) - 10).utc_offset.should == 3600 + end + + it "does not return a subclass instance" do + c = Class.new(Time) + x = c.now + 1 + x.should be_an_instance_of(Time) + end + + it "returns a time with nanoseconds precision between two time objects" do + time1 = Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)) + time2 = Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000)) + (time1 - time2).should == 86_399.999999998 + end +end diff --git a/spec/rubyspec/core/time/mktime_spec.rb b/spec/rubyspec/core/time/mktime_spec.rb new file mode 100644 index 0000000000..68ac1b90ac --- /dev/null +++ b/spec/rubyspec/core/time/mktime_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/local', __FILE__) +require File.expand_path('../shared/time_params', __FILE__) + +describe "Time.mktime" do + it_behaves_like(:time_local, :mktime) + it_behaves_like(:time_local_10_arg, :mktime) + it_behaves_like(:time_params, :mktime) + it_behaves_like(:time_params_10_arg, :mktime) + it_behaves_like(:time_params_microseconds, :mktime) +end diff --git a/spec/rubyspec/core/time/mon_spec.rb b/spec/rubyspec/core/time/mon_spec.rb new file mode 100644 index 0000000000..2408341143 --- /dev/null +++ b/spec/rubyspec/core/time/mon_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/month', __FILE__) + +describe "Time#mon" do + it_behaves_like(:time_month, :mon) +end diff --git a/spec/rubyspec/core/time/monday_spec.rb b/spec/rubyspec/core/time/monday_spec.rb new file mode 100644 index 0000000000..47b09c9a07 --- /dev/null +++ b/spec/rubyspec/core/time/monday_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#monday?" do + it "returns true if time represents Monday" do + Time.local(2000, 1, 3).monday?.should == true + end + + it "returns false if time doesn't represent Monday" do + Time.local(2000, 1, 1).monday?.should == false + end +end diff --git a/spec/rubyspec/core/time/month_spec.rb b/spec/rubyspec/core/time/month_spec.rb new file mode 100644 index 0000000000..6323c6205a --- /dev/null +++ b/spec/rubyspec/core/time/month_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/month', __FILE__) + +describe "Time#month" do + it_behaves_like(:time_month, :month) +end diff --git a/spec/rubyspec/core/time/new_spec.rb b/spec/rubyspec/core/time/new_spec.rb new file mode 100644 index 0000000000..a92715c81c --- /dev/null +++ b/spec/rubyspec/core/time/new_spec.rb @@ -0,0 +1,99 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/now', __FILE__) +require File.expand_path('../shared/local', __FILE__) +require File.expand_path('../shared/time_params', __FILE__) + +describe "Time.new" do + it_behaves_like(:time_now, :new) +end + +describe "Time.new" do + it_behaves_like(:time_local, :new) + it_behaves_like(:time_params, :new) +end + +describe "Time.new with a utc_offset argument" do + it "returns a non-UTC time" do + Time.new(2000, 1, 1, 0, 0, 0, 0).utc?.should == false + end + + it "returns a Time with a UTC offset of the specified number of Integer seconds" do + Time.new(2000, 1, 1, 0, 0, 0, 123).utc_offset.should == 123 + end + + describe "with an argument that responds to #to_int" do + it "coerces using #to_int" do + o = mock('integer') + o.should_receive(:to_int).and_return(123) + Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should == 123 + end + end + + it "returns a Time with a UTC offset of the specified number of Rational seconds" do + Time.new(2000, 1, 1, 0, 0, 0, Rational(5, 2)).utc_offset.should eql(Rational(5, 2)) + end + + describe "with an argument that responds to #to_r" do + it "coerces using #to_r" do + o = mock_numeric('rational') + o.should_receive(:to_r).and_return(Rational(5, 2)) + Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should eql(Rational(5, 2)) + end + end + + it "returns a Time with a UTC offset specified as +HH:MM" do + Time.new(2000, 1, 1, 0, 0, 0, "+05:30").utc_offset.should == 19800 + end + + it "returns a Time with a UTC offset specified as -HH:MM" do + Time.new(2000, 1, 1, 0, 0, 0, "-04:10").utc_offset.should == -15000 + end + + describe "with an argument that responds to #to_str" do + it "coerces using #to_str" do + o = mock('string') + o.should_receive(:to_str).and_return("+05:30") + Time.new(2000, 1, 1, 0, 0, 0, o).utc_offset.should == 19800 + end + end + + it "returns a local Time if the argument is nil" do + with_timezone("PST", -8) do + t = Time.new(2000, 1, 1, 0, 0, 0, nil) + t.utc_offset.should == -28800 + t.zone.should == "PST" + end + end + + # [Bug #8679], r47676 + it "disallows a value for minutes greater than 59" do + lambda { + Time.new(2000, 1, 1, 0, 0, 0, "+01:60") + }.should raise_error(ArgumentError) + lambda { + Time.new(2000, 1, 1, 0, 0, 0, "+01:99") + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the String argument is not of the form (+|-)HH:MM" do + lambda { Time.new(2000, 1, 1, 0, 0, 0, "3600") }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the hour value is greater than 23" do + lambda { Time.new(2000, 1, 1, 0, 0, 0, "+24:00") }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do + lambda { Time.new(2000, 1, 1, 0, 0, 0, "-04:10".encode("UTF-16LE")) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the argument represents a value less than or equal to -86400 seconds" do + Time.new(2000, 1, 1, 0, 0, 0, -86400 + 1).utc_offset.should == (-86400 + 1) + lambda { Time.new(2000, 1, 1, 0, 0, 0, -86400) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if the argument represents a value greater than or equal to 86400 seconds" do + Time.new(2000, 1, 1, 0, 0, 0, 86400 - 1).utc_offset.should == (86400 - 1) + lambda { Time.new(2000, 1, 1, 0, 0, 0, 86400) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/core/time/now_spec.rb b/spec/rubyspec/core/time/now_spec.rb new file mode 100644 index 0000000000..399a1a22e2 --- /dev/null +++ b/spec/rubyspec/core/time/now_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/now', __FILE__) + +describe "Time.now" do + it_behaves_like(:time_now, :now) +end diff --git a/spec/rubyspec/core/time/nsec_spec.rb b/spec/rubyspec/core/time/nsec_spec.rb new file mode 100644 index 0000000000..3a6be1d016 --- /dev/null +++ b/spec/rubyspec/core/time/nsec_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#nsec" do + it "returns 0 for a Time constructed with a whole number of seconds" do + Time.at(100).nsec.should == 0 + end + + it "returns the nanoseconds part of a Time constructed with a Float number of seconds" do + Time.at(10.75).nsec.should == 750_000_000 + end + + it "returns the nanoseconds part of a Time constructed with an Integer number of microseconds" do + Time.at(0, 999_999).nsec.should == 999_999_000 + end + + it "returns the nanoseconds part of a Time constructed with an Float number of microseconds" do + Time.at(0, 3.75).nsec.should == 3750 + end + + it "returns the nanoseconds part of a Time constructed with a Rational number of seconds" do + Time.at(Rational(3, 2)).nsec.should == 500_000_000 + end + + it "returns the nanoseconds part of a Time constructed with an Rational number of microseconds" do + Time.at(0, Rational(99, 10)).nsec.should == 9900 + end +end diff --git a/spec/rubyspec/core/time/plus_spec.rb b/spec/rubyspec/core/time/plus_spec.rb new file mode 100644 index 0000000000..29931f8a87 --- /dev/null +++ b/spec/rubyspec/core/time/plus_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#+" do + it "increments the time by the specified amount" do + (Time.at(0) + 100).should == Time.at(100) + end + + it "is a commutative operator" do + (Time.at(1.1) + 0.9).should == Time.at(0.9) + 1.1 + end + + it "adds a negative Float" do + t = Time.at(100) + -1.3 + t.usec.should == 699999 + t.to_i.should == 98 + end + + it "raises a TypeError if given argument is a coercible String" do + lambda { Time.now + "1" }.should raise_error(TypeError) + lambda { Time.now + "0.1" }.should raise_error(TypeError) + lambda { Time.now + "1/3" }.should raise_error(TypeError) + end + + it "increments the time by the specified amount as rational numbers" do + (Time.at(Rational(11, 10)) + Rational(9, 10)).should == Time.at(2) + end + + it "accepts arguments that can be coerced into Rational" do + (obj = mock_numeric('10')).should_receive(:to_r).and_return(Rational(10)) + (Time.at(100) + obj).should == Time.at(110) + end + + it "raises TypeError on argument that can't be coerced into Rational" do + lambda { Time.now + Object.new }.should raise_error(TypeError) + lambda { Time.now + "stuff" }.should raise_error(TypeError) + end + + it "returns a UTC time if self is UTC" do + (Time.utc(2012) + 10).utc?.should == true + end + + it "returns a non-UTC time if self is non-UTC" do + (Time.local(2012) + 10).utc?.should == false + end + + it "returns a time with the same fixed offset as self" do + (Time.new(2012, 1, 1, 0, 0, 0, 3600) + 10).utc_offset.should == 3600 + end + + it "does not return a subclass instance" do + c = Class.new(Time) + x = c.now + 1 + x.should be_an_instance_of(Time) + end + + it "raises TypeError on Time argument" do + lambda { Time.now + Time.now }.should raise_error(TypeError) + end + + it "raises TypeError on nil argument" do + lambda { Time.now + nil }.should raise_error(TypeError) + end + + #see [ruby-dev:38446] + it "tracks microseconds" do + time = Time.at(0) + time += Rational(123_456, 1_000_000) + time.usec.should == 123_456 + time += Rational(654_321, 1_000_000) + time.usec.should == 777_777 + end + + it "tracks nanoseconds" do + time = Time.at(0) + time += Rational(123_456_789, 1_000_000_000) + time.nsec.should == 123_456_789 + time += Rational(876_543_210, 1_000_000_000) + time.nsec.should == 999_999_999 + end + + it "maintains precision" do + t = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000) + t.should_not == Time.at(9) + end + + it "maintains microseconds precision" do + time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000) + time.usec.should == 999_999 + end + + it "maintains nanoseconds precision" do + time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000) + time.nsec.should == 999_999_999 + end + + it "maintains subseconds precision" do + time = Time.at(0) + Rational(8_999_999_999_999_999, 1_000_000_000_000_000) + time.subsec.should == Rational(999_999_999_999_999, 1_000_000_000_000_000) + end +end diff --git a/spec/rubyspec/core/time/round_spec.rb b/spec/rubyspec/core/time/round_spec.rb new file mode 100644 index 0000000000..a9a793c156 --- /dev/null +++ b/spec/rubyspec/core/time/round_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#round" do + before do + @time = Time.utc(2010, 3, 30, 5, 43, "25.123456789".to_r) + @subclass = Class.new(Time).now + end + + it "defaults to rounding to 0 places" do + @time.round.should == Time.utc(2010, 3, 30, 5, 43, 25.to_r) + end + + it "rounds to 0 decimal places with an explicit argument" do + @time.round(0).should == Time.utc(2010, 3, 30, 5, 43, 25.to_r) + end + + it "rounds to 7 decimal places with an explicit argument" do + @time.round(7).should == Time.utc(2010, 3, 30, 5, 43, "25.1234568".to_r) + end + + it "returns an instance of Time, even if #round is called on a subclass" do + @subclass.round.should be_an_instance_of(Time) + end + + it "copies own timezone to the returning value" do + @time.zone.should == @time.round.zone + + with_timezone "JST-9" do + time = Time.at 0, 1 + time.zone.should == time.round.zone + end + end +end diff --git a/spec/rubyspec/core/time/saturday_spec.rb b/spec/rubyspec/core/time/saturday_spec.rb new file mode 100644 index 0000000000..0d827a6184 --- /dev/null +++ b/spec/rubyspec/core/time/saturday_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#saturday?" do + it "returns true if time represents Saturday" do + Time.local(2000, 1, 1).saturday?.should == true + end + + it "returns false if time doesn't represent Saturday" do + Time.local(2000, 1, 2).saturday?.should == false + end +end diff --git a/spec/rubyspec/core/time/sec_spec.rb b/spec/rubyspec/core/time/sec_spec.rb new file mode 100644 index 0000000000..e753235b53 --- /dev/null +++ b/spec/rubyspec/core/time/sec_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#sec" do + it "returns the second of the minute(0..60) for time" do + Time.at(0).sec.should == 0 + end +end diff --git a/spec/rubyspec/core/time/shared/asctime.rb b/spec/rubyspec/core/time/shared/asctime.rb new file mode 100644 index 0000000000..d096666863 --- /dev/null +++ b/spec/rubyspec/core/time/shared/asctime.rb @@ -0,0 +1,6 @@ +describe :time_asctime, shared: true do + it "returns a canonical string representation of time" do + t = Time.now + t.send(@method).should == t.strftime("%a %b %e %H:%M:%S %Y") + end +end diff --git a/spec/rubyspec/core/time/shared/day.rb b/spec/rubyspec/core/time/shared/day.rb new file mode 100644 index 0000000000..472dc959c1 --- /dev/null +++ b/spec/rubyspec/core/time/shared/day.rb @@ -0,0 +1,15 @@ +describe :time_day, shared: true do + it "returns the day of the month (1..n) for a local Time" do + with_timezone("CET", 1) do + Time.local(1970, 1, 1).send(@method).should == 1 + end + end + + it "returns the day of the month for a UTC Time" do + Time.utc(1970, 1, 1).send(@method).should == 1 + end + + it "returns the day of the month for a Time with a fixed offset" do + Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1 + end +end diff --git a/spec/rubyspec/core/time/shared/getgm.rb b/spec/rubyspec/core/time/shared/getgm.rb new file mode 100644 index 0000000000..3576365772 --- /dev/null +++ b/spec/rubyspec/core/time/shared/getgm.rb @@ -0,0 +1,9 @@ +describe :time_getgm, shared: true do + it "returns a new time which is the utc representation of time" do + # Testing with America/Regina here because it doesn't have DST. + with_timezone("CST", -6) do + t = Time.local(2007, 1, 9, 6, 0, 0) + t.send(@method).should == Time.gm(2007, 1, 9, 12, 0, 0) + end + end +end diff --git a/spec/rubyspec/core/time/shared/gm.rb b/spec/rubyspec/core/time/shared/gm.rb new file mode 100644 index 0000000000..805e35766c --- /dev/null +++ b/spec/rubyspec/core/time/shared/gm.rb @@ -0,0 +1,22 @@ +describe :time_gm, shared: true do + it "creates a time based on given values, interpreted as UTC (GMT)" do + Time.send(@method, 2000,"jan",1,20,15,1).inspect.should == "2000-01-01 20:15:01 UTC" + end + + it "creates a time based on given C-style gmtime arguments, interpreted as UTC (GMT)" do + time = Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored) + time.inspect.should == "2000-01-01 20:15:01 UTC" + end + + it "interprets pre-Gregorian reform dates using Gregorian proleptic calendar" do + Time.send(@method, 1582, 10, 4, 12).to_i.should == -12220200000 # 2299150j + end + + it "interprets Julian-Gregorian gap dates using Gregorian proleptic calendar" do + Time.send(@method, 1582, 10, 14, 12).to_i.should == -12219336000 # 2299160j + end + + it "interprets post-Gregorian reform dates using Gregorian calendar" do + Time.send(@method, 1582, 10, 15, 12).to_i.should == -12219249600 # 2299161j + end +end diff --git a/spec/rubyspec/core/time/shared/gmt_offset.rb b/spec/rubyspec/core/time/shared/gmt_offset.rb new file mode 100644 index 0000000000..cb842be2f3 --- /dev/null +++ b/spec/rubyspec/core/time/shared/gmt_offset.rb @@ -0,0 +1,53 @@ +describe :time_gmt_offset, shared: true do + it "returns the offset in seconds between the timezone of time and UTC" do + with_timezone("AST", 3) do + Time.new.send(@method).should == 10800 + end + end + + platform_is_not :windows do + it "returns the correct offset for US Eastern time zone around daylight savings time change" do + # "2010-03-14 01:59:59 -0500" + 1 ==> "2010-03-14 03:00:00 -0400" + with_timezone("EST5EDT") do + t = Time.local(2010,3,14,1,59,59) + t.send(@method).should == -5*60*60 + (t + 1).send(@method).should == -4*60*60 + end + end + + it "returns the correct offset for Hawaii around daylight savings time change" do + # "2010-03-14 01:59:59 -1000" + 1 ==> "2010-03-14 02:00:00 -1000" + with_timezone("Pacific/Honolulu") do + t = Time.local(2010,3,14,1,59,59) + t.send(@method).should == -10*60*60 + (t + 1).send(@method).should == -10*60*60 + end + end + + it "returns the correct offset for New Zealand around daylight savings time change" do + # "2010-04-04 02:59:59 +1300" + 1 ==> "2010-04-04 02:00:00 +1200" + with_timezone("Pacific/Auckland") do + t = Time.local(2010,4,4,1,59,59) + (60 * 60) + t.send(@method).should == 13*60*60 + (t + 1).send(@method).should == 12*60*60 + end + end + end + + it "returns offset as Rational" do + Time.new(2010,4,4,1,59,59,7245).send(@method).should == 7245 + Time.new(2010,4,4,1,59,59,7245.5).send(@method).should == Rational(14491,2) + end + + context 'given positive offset' do + it 'returns a positive offset' do + Time.new(2013,3,17,nil,nil,nil,"+03:00").send(@method).should == 10800 + end + end + + context 'given negative offset' do + it 'returns a negative offset' do + Time.new(2013,3,17,nil,nil,nil,"-03:00").send(@method).should == -10800 + end + end +end diff --git a/spec/rubyspec/core/time/shared/gmtime.rb b/spec/rubyspec/core/time/shared/gmtime.rb new file mode 100644 index 0000000000..a1b4b88418 --- /dev/null +++ b/spec/rubyspec/core/time/shared/gmtime.rb @@ -0,0 +1,10 @@ +describe :time_gmtime, shared: true do + it "returns the utc representation of time" do + # Testing with America/Regina here because it doesn't have DST. + with_timezone("CST", -6) do + t = Time.local(2007, 1, 9, 6, 0, 0) + t.send(@method) + t.should == Time.gm(2007, 1, 9, 12, 0, 0) + end + end +end diff --git a/spec/rubyspec/core/time/shared/inspect.rb b/spec/rubyspec/core/time/shared/inspect.rb new file mode 100644 index 0000000000..c707382a6e --- /dev/null +++ b/spec/rubyspec/core/time/shared/inspect.rb @@ -0,0 +1,23 @@ +# -*- encoding: us-ascii -*- + +describe :inspect, shared: true do + it "formats the local time following the pattern 'yyyy-MM-dd HH:mm:ss Z'" do + with_timezone("PST", +1) do + Time.local(2000, 1, 1, 20, 15, 1).send(@method).should == "2000-01-01 20:15:01 +0100" + end + end + + it "formats the UTC time following the pattern 'yyyy-MM-dd HH:mm:ss UTC'" do + Time.utc(2000, 1, 1, 20, 15, 1).send(@method).should == "2000-01-01 20:15:01 UTC" + end + + it "formats the fixed offset time following the pattern 'yyyy-MM-dd HH:mm:ss +/-HHMM'" do + Time.new(2000, 1, 1, 20, 15, 01, 3600).send(@method).should == "2000-01-01 20:15:01 +0100" + end + + with_feature :encoding do + it "returns a US-ASCII encoded string" do + Time.now.send(@method).encoding.should equal(Encoding::US_ASCII) + end + end +end diff --git a/spec/rubyspec/core/time/shared/isdst.rb b/spec/rubyspec/core/time/shared/isdst.rb new file mode 100644 index 0000000000..bc6d139230 --- /dev/null +++ b/spec/rubyspec/core/time/shared/isdst.rb @@ -0,0 +1,8 @@ +describe :time_isdst, shared: true do + it "dst? returns whether time is during daylight saving time" do + with_timezone("America/Los_Angeles") do + Time.local(2007, 9, 9, 0, 0, 0).send(@method).should == true + Time.local(2007, 1, 9, 0, 0, 0).send(@method).should == false + end + end +end diff --git a/spec/rubyspec/core/time/shared/local.rb b/spec/rubyspec/core/time/shared/local.rb new file mode 100644 index 0000000000..43f331c4c1 --- /dev/null +++ b/spec/rubyspec/core/time/shared/local.rb @@ -0,0 +1,45 @@ +describe :time_local, shared: true do + it "creates a time based on given values, interpreted in the local time zone" do + with_timezone("PST", -8) do + Time.send(@method, 2000, "jan", 1, 20, 15, 1).to_a.should == + [1, 15, 20, 1, 1, 2000, 6, 1, false, "PST"] + end + end + + platform_is_not :windows do + describe "timezone changes" do + it "correctly adjusts the timezone change to 'CEST' on 'Europe/Amsterdam'" do + with_timezone("Europe/Amsterdam") do + Time.send(@method, 1940, 5, 16).to_a.should == + [0, 40, 1, 16, 5, 1940, 4, 137, true, "CEST"] + end + end + end + end +end + +describe :time_local_10_arg, shared: true do + it "creates a time based on given C-style gmtime arguments, interpreted in the local time zone" do + with_timezone("PST", -8) do + Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored).to_a.should == + [1, 15, 20, 1, 1, 2000, 6, 1, false, "PST"] + end + end + + platform_is_not :windows do + it "creates the correct time just before dst change" do + with_timezone("America/New_York") do + time = Time.send(@method, 0, 30, 1, 30, 10, 2005, 0, 0, true, ENV['TZ']) + time.utc_offset.should == -4 * 3600 + end + end + + it "creates the correct time just after dst change" do + with_timezone("America/New_York") do + time = Time.send(@method, 0, 30, 1, 30, 10, 2005, 0, 0, false, ENV['TZ']) + time.utc_offset.should == -5 * 3600 + end + end + end + +end diff --git a/spec/rubyspec/core/time/shared/month.rb b/spec/rubyspec/core/time/shared/month.rb new file mode 100644 index 0000000000..31ca679557 --- /dev/null +++ b/spec/rubyspec/core/time/shared/month.rb @@ -0,0 +1,15 @@ +describe :time_month, shared: true do + it "returns the month of the year for a local Time" do + with_timezone("CET", 1) do + Time.local(1970, 1).send(@method).should == 1 + end + end + + it "returns the month of the year for a UTC Time" do + Time.utc(1970, 1).send(@method).should == 1 + end + + it "returns the four digit year for a Time with a fixed offset" do + Time.new(2012, 1, 1, 0, 0, 0, -3600).send(@method).should == 1 + end +end diff --git a/spec/rubyspec/core/time/shared/now.rb b/spec/rubyspec/core/time/shared/now.rb new file mode 100644 index 0000000000..f570aeedd2 --- /dev/null +++ b/spec/rubyspec/core/time/shared/now.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :time_now, shared: true do + it "creates a subclass instance if called on a subclass" do + TimeSpecs::SubTime.send(@method).should be_an_instance_of(TimeSpecs::SubTime) + TimeSpecs::MethodHolder.send(@method).should be_an_instance_of(Time) + end +end diff --git a/spec/rubyspec/core/time/shared/time_params.rb b/spec/rubyspec/core/time/shared/time_params.rb new file mode 100644 index 0000000000..b60699dfb3 --- /dev/null +++ b/spec/rubyspec/core/time/shared/time_params.rb @@ -0,0 +1,255 @@ +describe :time_params, shared: true do + it "accepts 1 argument (year)" do + Time.send(@method, 2000).should == + Time.send(@method, 2000, 1, 1, 0, 0, 0) + end + + it "accepts 2 arguments (year, month)" do + Time.send(@method, 2000, 2).should == + Time.send(@method, 2000, 2, 1, 0, 0, 0) + end + + it "accepts 3 arguments (year, month, day)" do + Time.send(@method, 2000, 2, 3).should == + Time.send(@method, 2000, 2, 3, 0, 0, 0) + end + + it "accepts 4 arguments (year, month, day, hour)" do + Time.send(@method, 2000, 2, 3, 4).should == + Time.send(@method, 2000, 2, 3, 4, 0, 0) + end + + it "accepts 5 arguments (year, month, day, hour, minute)" do + Time.send(@method, 2000, 2, 3, 4, 5).should == + Time.send(@method, 2000, 2, 3, 4, 5, 0) + end + + it "raises a TypeError if the year is nil" do + lambda { Time.send(@method, nil) }.should raise_error(TypeError) + end + + it "accepts nil month, day, hour, minute, and second" do + Time.send(@method, 2000, nil, nil, nil, nil, nil).should == + Time.send(@method, 2000) + end + + it "handles a String year" do + Time.send(@method, "2000").should == + Time.send(@method, 2000) + end + + it "coerces the year with #to_int" do + m = mock(:int) + m.should_receive(:to_int).and_return(1) + Time.send(@method, m).should == Time.send(@method, 1) + end + + it "handles a String month given as a numeral" do + Time.send(@method, 2000, "12").should == + Time.send(@method, 2000, 12) + end + + it "handles a String month given as a short month name" do + Time.send(@method, 2000, "dec").should == + Time.send(@method, 2000, 12) + end + + it "coerces the month with #to_str" do + (obj = mock('12')).should_receive(:to_str).and_return("12") + Time.send(@method, 2008, obj).should == + Time.send(@method, 2008, 12) + end + + it "coerces the month with #to_int" do + m = mock(:int) + m.should_receive(:to_int).and_return(1) + Time.send(@method, 2008, m).should == Time.send(@method, 2008, 1) + end + + it "handles a String day" do + Time.send(@method, 2000, 12, "15").should == + Time.send(@method, 2000, 12, 15) + end + + it "coerces the day with #to_int" do + m = mock(:int) + m.should_receive(:to_int).and_return(1) + Time.send(@method, 2008, 1, m).should == Time.send(@method, 2008, 1, 1) + end + + it "handles a String hour" do + Time.send(@method, 2000, 12, 1, "5").should == + Time.send(@method, 2000, 12, 1, 5) + end + + it "coerces the hour with #to_int" do + m = mock(:int) + m.should_receive(:to_int).and_return(1) + Time.send(@method, 2008, 1, 1, m).should == Time.send(@method, 2008, 1, 1, 1) + end + + it "handles a String minute" do + Time.send(@method, 2000, 12, 1, 1, "8").should == + Time.send(@method, 2000, 12, 1, 1, 8) + end + + it "coerces the minute with #to_int" do + m = mock(:int) + m.should_receive(:to_int).and_return(1) + Time.send(@method, 2008, 1, 1, 0, m).should == Time.send(@method, 2008, 1, 1, 0, 1) + end + + it "handles a String second" do + Time.send(@method, 2000, 12, 1, 1, 1, "8").should == + Time.send(@method, 2000, 12, 1, 1, 1, 8) + end + + it "coerces the second with #to_int" do + m = mock(:int) + m.should_receive(:to_int).and_return(1) + Time.send(@method, 2008, 1, 1, 0, 0, m).should == Time.send(@method, 2008, 1, 1, 0, 0, 1) + end + + it "interprets all numerals as base 10" do + Time.send(@method, "2000", "08", "08", "08", "08", "08").should == + Time.send(@method, 2000, 8, 8, 8, 8, 8) + Time.send(@method, "2000", "09", "09", "09", "09", "09").should == + Time.send(@method, 2000, 9, 9, 9, 9, 9) + end + + it "handles fractional seconds as a Float" do + t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75) + t.sec.should == 1 + t.usec.should == 750000 + end + + it "handles fractional seconds as a Rational" do + t = Time.send(@method, 2000, 1, 1, 20, 15, Rational(99, 10)) + t.sec.should == 9 + t.usec.should == 900000 + end + + it "handles years from 0 as such" do + 0.upto(2100) do |year| + t = Time.send(@method, year) + t.year.should == year + end + end + + it "accepts various year ranges" do + Time.send(@method, 1801, 12, 31, 23, 59, 59).wday.should == 4 + Time.send(@method, 3000, 12, 31, 23, 59, 59).wday.should == 3 + end + + it "raises an ArgumentError for out of range month" do + lambda { + Time.send(@method, 2008, 13, 31, 23, 59, 59) + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for out of range day" do + lambda { + Time.send(@method, 2008, 12, 32, 23, 59, 59) + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for out of range hour" do + lambda { + Time.send(@method, 2008, 12, 31, 25, 59, 59) + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for out of range minute" do + lambda { + Time.send(@method, 2008, 12, 31, 23, 61, 59) + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for out of range second" do + lambda { + Time.send(@method, 2008, 12, 31, 23, 59, 61) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when given 9 arguments" do + lambda { Time.send(@method, *[0]*9) }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when given 11 arguments" do + lambda { Time.send(@method, *[0]*11) }.should raise_error(ArgumentError) + end + + it "returns subclass instances" do + c = Class.new(Time) + c.send(@method, 2008, "12").should be_an_instance_of(c) + end +end + +describe :time_params_10_arg, shared: true do + it "handles string arguments" do + Time.send(@method, "1", "15", "20", "1", "1", "2000", :ignored, :ignored, + :ignored, :ignored).should == + Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored) + end + + it "handles float arguments" do + Time.send(@method, 1.0, 15.0, 20.0, 1.0, 1.0, 2000.0, :ignored, :ignored, + :ignored, :ignored).should == + Time.send(@method, 1, 15, 20, 1, 1, 2000, :ignored, :ignored, :ignored, :ignored) + end + + it "raises an ArgumentError for out of range values" do + lambda { + Time.send(@method, 61, 59, 23, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored) + }.should raise_error(ArgumentError) # sec + + lambda { + Time.send(@method, 59, 61, 23, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored) + }.should raise_error(ArgumentError) # min + + lambda { + Time.send(@method, 59, 59, 25, 31, 12, 2008, :ignored, :ignored, :ignored, :ignored) + }.should raise_error(ArgumentError) # hour + + lambda { + Time.send(@method, 59, 59, 23, 32, 12, 2008, :ignored, :ignored, :ignored, :ignored) + }.should raise_error(ArgumentError) # day + + lambda { + Time.send(@method, 59, 59, 23, 31, 13, 2008, :ignored, :ignored, :ignored, :ignored) + }.should raise_error(ArgumentError) # month + end +end + +describe :time_params_microseconds, shared: true do + it "handles microseconds" do + t = Time.send(@method, 2000, 1, 1, 20, 15, 1, 123) + t.usec.should == 123 + end + + it "handles fractional microseconds as a Float" do + t = Time.send(@method, 2000, 1, 1, 20, 15, 1, 1.75) + t.usec.should == 1 + t.nsec.should == 1750 + end + + it "handles fractional microseconds as a Rational" do + t = Time.send(@method, 2000, 1, 1, 20, 15, 1, Rational(99, 10)) + t.usec.should == 9 + t.nsec.should == 9900 + end + + it "ignores fractional seconds if a passed whole number of microseconds" do + t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75, 2) + t.sec.should == 1 + t.usec.should == 2 + t.nsec.should == 2000 + end + + it "ignores fractional seconds if a passed fractional number of microseconds" do + t = Time.send(@method, 2000, 1, 1, 20, 15, 1.75, Rational(99, 10)) + t.sec.should == 1 + t.usec.should == 9 + t.nsec.should == 9900 + end +end diff --git a/spec/rubyspec/core/time/shared/to_i.rb b/spec/rubyspec/core/time/shared/to_i.rb new file mode 100644 index 0000000000..03497c700b --- /dev/null +++ b/spec/rubyspec/core/time/shared/to_i.rb @@ -0,0 +1,9 @@ +describe :time_to_i, shared: true do + it "returns the value of time as an integer number of seconds since epoch" do + Time.at(0).send(@method).should == 0 + end + + it "doesn't return an actual number of seconds in time" do + Time.at(65.5).send(@method).should == 65 + end +end diff --git a/spec/rubyspec/core/time/strftime_spec.rb b/spec/rubyspec/core/time/strftime_spec.rb new file mode 100644 index 0000000000..1cb3575eec --- /dev/null +++ b/spec/rubyspec/core/time/strftime_spec.rb @@ -0,0 +1,52 @@ +# encoding: utf-8 + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/time/strftime_for_date', __FILE__) +require File.expand_path('../../../shared/time/strftime_for_time', __FILE__) + +describe "Time#strftime" do + before :all do + @new_date = lambda { |y,m,d| Time.gm(y,m,d) } + @new_time = lambda { |*args| Time.gm(*args) } + @new_time_in_zone = lambda { |zone,offset,*args| + with_timezone(zone, offset) do + Time.new(*args) + end + } + @new_time_with_offset = lambda { |y,m,d,h,min,s,offset| + Time.new(y,m,d,h,min,s,offset) + } + + @time = @new_time[2001, 2, 3, 4, 5, 6] + end + + it_behaves_like :strftime_date, :strftime + it_behaves_like :strftime_time, :strftime + + # Differences with date + it "requires an argument" do + lambda { @time.strftime }.should raise_error(ArgumentError) + end + + # %Z is zone name or empty for Time + it "should be able to show the timezone if available" do + @time.strftime("%Z").should == @time.zone + with_timezone("UTC", 0) do + Time.gm(2000).strftime("%Z").should == "UTC" + end + + Time.new(2000, 1, 1, 0, 0, 0, 42).strftime("%Z").should == "" + end + + # %v is %e-%^b-%Y for Time + it "should be able to show the commercial week" do + @time.strftime("%v").should == " 3-FEB-2001" + @time.strftime("%v").should == @time.strftime('%e-%^b-%Y') + end + + # Date/DateTime round at creation time, but Time does it in strftime. + it "rounds an offset to the nearest second when formatting with %z" do + time = @new_time_with_offset[2012, 1, 1, 0, 0, 0, Rational(36645, 10)] + time.strftime("%::z").should == "+01:01:05" + end +end diff --git a/spec/rubyspec/core/time/subsec_spec.rb b/spec/rubyspec/core/time/subsec_spec.rb new file mode 100644 index 0000000000..d9d262a513 --- /dev/null +++ b/spec/rubyspec/core/time/subsec_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#subsec" do + it "returns 0 as a Fixnum for a Time with a whole number of seconds" do + Time.at(100).subsec.should eql(0) + end + + it "returns the fractional seconds as a Rational for a Time constructed with a Rational number of seconds" do + Time.at(Rational(3, 2)).subsec.should eql(Rational(1, 2)) + end + + it "returns the fractional seconds as a Rational for a Time constructed with a Float number of seconds" do + Time.at(10.75).subsec.should eql(Rational(3, 4)) + end + + it "returns the fractional seconds as a Rational for a Time constructed with an Integer number of microseconds" do + Time.at(0, 999999).subsec.should eql(Rational(999999, 1000000)) + end + + it "returns the fractional seconds as a Rational for a Time constructed with an Rational number of microseconds" do + Time.at(0, Rational(9, 10)).subsec.should eql(Rational(9, 10000000)) + end + + it "returns the fractional seconds as a Rational for a Time constructed with an Float number of microseconds" do + Time.at(0, 0.75).subsec.should eql(Rational(3, 4000000)) + end +end diff --git a/spec/rubyspec/core/time/succ_spec.rb b/spec/rubyspec/core/time/succ_spec.rb new file mode 100644 index 0000000000..6831200741 --- /dev/null +++ b/spec/rubyspec/core/time/succ_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#succ" do + it "returns a new time one second later than time" do + -> { + @result = Time.at(100).succ + }.should complain(/Time#succ is obsolete/) + @result.should == Time.at(101) + end + + it "returns a new instance" do + t1 = Time.at(100) + t2 = nil + -> { + t2 = t1.succ + }.should complain(/Time#succ is obsolete/) + t1.object_id.should_not == t2.object_id + end +end diff --git a/spec/rubyspec/core/time/sunday_spec.rb b/spec/rubyspec/core/time/sunday_spec.rb new file mode 100644 index 0000000000..823174cf71 --- /dev/null +++ b/spec/rubyspec/core/time/sunday_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#sunday?" do + it "returns true if time represents Sunday" do + Time.local(2000, 1, 2).sunday?.should == true + end + + it "returns false if time doesn't represent Sunday" do + Time.local(2000, 1, 1).sunday?.should == false + end +end diff --git a/spec/rubyspec/core/time/thursday_spec.rb b/spec/rubyspec/core/time/thursday_spec.rb new file mode 100644 index 0000000000..5788fd9bc7 --- /dev/null +++ b/spec/rubyspec/core/time/thursday_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#thursday?" do + it "returns true if time represents Thursday" do + Time.local(2000, 1, 6).thursday?.should == true + end + + it "returns false if time doesn't represent Thursday" do + Time.local(2000, 1, 1).thursday?.should == false + end +end diff --git a/spec/rubyspec/core/time/time_spec.rb b/spec/rubyspec/core/time/time_spec.rb new file mode 100644 index 0000000000..1c870c8e0f --- /dev/null +++ b/spec/rubyspec/core/time/time_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time" do + it "includes Comparable" do + Time.include?(Comparable).should == true + end +end diff --git a/spec/rubyspec/core/time/to_a_spec.rb b/spec/rubyspec/core/time/to_a_spec.rb new file mode 100644 index 0000000000..a4c4a8fbc9 --- /dev/null +++ b/spec/rubyspec/core/time/to_a_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#to_a" do + platform_is_not :windows do + it "returns a 10 element array representing the deconstructed time" do + # Testing with America/Regina here because it doesn't have DST. + with_timezone("America/Regina") do + Time.at(0).to_a.should == [0, 0, 18, 31, 12, 1969, 3, 365, false, "CST"] + end + end + end +end diff --git a/spec/rubyspec/core/time/to_f_spec.rb b/spec/rubyspec/core/time/to_f_spec.rb new file mode 100644 index 0000000000..d737848b4b --- /dev/null +++ b/spec/rubyspec/core/time/to_f_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#to_f" do + it "returns the float number of seconds + usecs since the epoch" do + Time.at(100, 100).to_f.should == 100.0001 + end +end diff --git a/spec/rubyspec/core/time/to_i_spec.rb b/spec/rubyspec/core/time/to_i_spec.rb new file mode 100644 index 0000000000..1a733f02cf --- /dev/null +++ b/spec/rubyspec/core/time/to_i_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Time#to_i" do + it_behaves_like(:time_to_i, :to_i) +end diff --git a/spec/rubyspec/core/time/to_r_spec.rb b/spec/rubyspec/core/time/to_r_spec.rb new file mode 100644 index 0000000000..53e469463a --- /dev/null +++ b/spec/rubyspec/core/time/to_r_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#to_r" do + it "returns the a Rational representing seconds and subseconds since the epoch" do + Time.at(Rational(11, 10)).to_r.should eql(Rational(11, 10)) + end + + it "returns a Rational even for a whole number of seconds" do + Time.at(2).to_r.should eql(Rational(2)) + end +end diff --git a/spec/rubyspec/core/time/to_s_spec.rb b/spec/rubyspec/core/time/to_s_spec.rb new file mode 100644 index 0000000000..8dc81f60a9 --- /dev/null +++ b/spec/rubyspec/core/time/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "Time#to_s" do + it_behaves_like :inspect, :to_s +end diff --git a/spec/rubyspec/core/time/tuesday_spec.rb b/spec/rubyspec/core/time/tuesday_spec.rb new file mode 100644 index 0000000000..87c3236eea --- /dev/null +++ b/spec/rubyspec/core/time/tuesday_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#tuesday?" do + it "returns true if time represents Tuesday" do + Time.local(2000, 1, 4).tuesday?.should == true + end + + it "returns false if time doesn't represent Tuesday" do + Time.local(2000, 1, 1).tuesday?.should == false + end +end diff --git a/spec/rubyspec/core/time/tv_nsec_spec.rb b/spec/rubyspec/core/time/tv_nsec_spec.rb new file mode 100644 index 0000000000..d477f6fbec --- /dev/null +++ b/spec/rubyspec/core/time/tv_nsec_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#tv_nsec" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/time/tv_sec_spec.rb b/spec/rubyspec/core/time/tv_sec_spec.rb new file mode 100644 index 0000000000..36f090be7b --- /dev/null +++ b/spec/rubyspec/core/time/tv_sec_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_i', __FILE__) + +describe "Time#tv_sec" do + it_behaves_like(:time_to_i, :tv_sec) +end diff --git a/spec/rubyspec/core/time/tv_usec_spec.rb b/spec/rubyspec/core/time/tv_usec_spec.rb new file mode 100644 index 0000000000..4a1b87be7a --- /dev/null +++ b/spec/rubyspec/core/time/tv_usec_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#tv_usec" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/time/usec_spec.rb b/spec/rubyspec/core/time/usec_spec.rb new file mode 100644 index 0000000000..018253ec77 --- /dev/null +++ b/spec/rubyspec/core/time/usec_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#usec" do + it "returns 0 for a Time constructed with a whole number of seconds" do + Time.at(100).usec.should == 0 + end + + it "returns the microseconds part of a Time constructed with a Float number of seconds" do + Time.at(10.75).usec.should == 750_000 + end + + it "returns the microseconds part of a Time constructed with an Integer number of microseconds" do + Time.at(0, 999_999).usec.should == 999_999 + end + + it "returns the microseconds part of a Time constructed with an Float number of microseconds > 1" do + Time.at(0, 3.75).usec.should == 3 + end + + it "returns 0 for a Time constructed with an Float number of microseconds < 1" do + Time.at(0, 0.75).usec.should == 0 + end + + it "returns the microseconds part of a Time constructed with a Rational number of seconds" do + Time.at(Rational(3, 2)).usec.should == 500_000 + end + + it "returns the microseconds part of a Time constructed with an Rational number of microseconds > 1" do + Time.at(0, Rational(99, 10)).usec.should == 9 + end + + it "returns 0 for a Time constructed with an Rational number of microseconds < 1" do + Time.at(0, Rational(9, 10)).usec.should == 0 + end + + it "returns the microseconds for time created by Time#local" do + Time.local(1,2,3,4,5,Rational(6.78)).usec.should == 780000 + end +end diff --git a/spec/rubyspec/core/time/utc_offset_spec.rb b/spec/rubyspec/core/time/utc_offset_spec.rb new file mode 100644 index 0000000000..4be885f2e3 --- /dev/null +++ b/spec/rubyspec/core/time/utc_offset_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gmt_offset', __FILE__) + +describe "Time#utc_offset" do + it_behaves_like(:time_gmt_offset, :utc_offset) +end diff --git a/spec/rubyspec/core/time/utc_spec.rb b/spec/rubyspec/core/time/utc_spec.rb new file mode 100644 index 0000000000..f88b9c7cbc --- /dev/null +++ b/spec/rubyspec/core/time/utc_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/gm', __FILE__) +require File.expand_path('../shared/gmtime', __FILE__) +require File.expand_path('../shared/time_params', __FILE__) + +describe "Time#utc?" do + it "returns true if time represents a time in UTC (GMT)" do + Time.now.utc?.should == false + end +end + +describe "Time.utc" do + it_behaves_like(:time_gm, :utc) + it_behaves_like(:time_params, :utc) + it_behaves_like(:time_params_10_arg, :utc) + it_behaves_like(:time_params_microseconds, :utc) +end + +describe "Time#utc" do + it_behaves_like(:time_gmtime, :utc) +end diff --git a/spec/rubyspec/core/time/wday_spec.rb b/spec/rubyspec/core/time/wday_spec.rb new file mode 100644 index 0000000000..72bc718356 --- /dev/null +++ b/spec/rubyspec/core/time/wday_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#wday" do + it "returns an integer representing the day of the week, 0..6, with Sunday being 0" do + with_timezone("GMT", 0) do + Time.at(0).wday.should == 4 + end + end +end diff --git a/spec/rubyspec/core/time/wednesday_spec.rb b/spec/rubyspec/core/time/wednesday_spec.rb new file mode 100644 index 0000000000..8e836e3e02 --- /dev/null +++ b/spec/rubyspec/core/time/wednesday_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#wednesday?" do + it "returns true if time represents Wednesday" do + Time.local(2000, 1, 5).wednesday?.should == true + end + + it "returns false if time doesn't represent Wednesday" do + Time.local(2000, 1, 1).wednesday?.should == false + end +end diff --git a/spec/rubyspec/core/time/yday_spec.rb b/spec/rubyspec/core/time/yday_spec.rb new file mode 100644 index 0000000000..2b7aca1565 --- /dev/null +++ b/spec/rubyspec/core/time/yday_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#yday" do + it "returns an integer representing the day of the year, 1..366" do + with_timezone("UTC") do + Time.at(9999999).yday.should == 116 + end + end + + it 'returns the correct value for each day of each month' do + mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + + yday = 1 + mdays.each_with_index do |days, month| + days.times do |day| + Time.new(2014, month+1, day+1).yday.should == yday + yday += 1 + end + end + end +end diff --git a/spec/rubyspec/core/time/year_spec.rb b/spec/rubyspec/core/time/year_spec.rb new file mode 100644 index 0000000000..4e18eb1353 --- /dev/null +++ b/spec/rubyspec/core/time/year_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#year" do + it "returns the four digit year for a local Time as an Integer" do + with_timezone("CET", 1) do + Time.local(1970).year.should == 1970 + end + end + + it "returns the four digit year for a UTC Time as an Integer" do + Time.utc(1970).year.should == 1970 + end + + it "returns the four digit year for a Time with a fixed offset" do + Time.new(2012, 1, 1, 0, 0, 0, -3600).year.should == 2012 + end +end diff --git a/spec/rubyspec/core/time/zone_spec.rb b/spec/rubyspec/core/time/zone_spec.rb new file mode 100644 index 0000000000..407841db3d --- /dev/null +++ b/spec/rubyspec/core/time/zone_spec.rb @@ -0,0 +1,78 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Time#zone" do + platform_is_not :windows do + it "returns the time zone used for time" do + with_timezone("America/New_York") do + Time.new(2001, 1, 1, 0, 0, 0).zone.should == "EST" + Time.new(2001, 7, 1, 0, 0, 0).zone.should == "EDT" + %w[EST EDT].should include Time.now.zone + end + end + end + + it "returns nil for a Time with a fixed offset" do + Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone.should == nil + end + + platform_is_not :windows do + it "returns the correct timezone for a local time" do + t = Time.new(2005, 2, 27, 22, 50, 0, -3600) + + with_timezone("America/New_York") do + t.getlocal.zone.should == "EST" + end + end + end + + it "returns nil when getting the local time with a fixed offset" do + t = Time.new(2005, 2, 27, 22, 50, 0, -3600) + + with_timezone("America/New_York") do + t.getlocal("+05:00").zone.should be_nil + end + end + + describe "Encoding.default_internal is set" do + before :each do + @encoding = Encoding.default_internal + Encoding.default_internal = Encoding::UTF_8 + end + + after :each do + Encoding.default_internal = @encoding + end + + it "returns an ASCII string" do + t = Time.new(2005, 2, 27, 22, 50, 0, -3600) + + with_timezone("America/New_York") do + t.getlocal.zone.encoding.should == Encoding::US_ASCII + end + end + + it "doesn't raise errors for a Time with a fixed offset" do + lambda { + Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone + }.should_not raise_error + end + end + + it "returns UTC when called on a UTC time" do + Time.now.utc.zone.should == "UTC" + end + + platform_is_not :aix do + it "defaults to UTC when bad zones given" do + with_timezone("hello-foo") do + Time.now.utc_offset.should == 0 + end + with_timezone("1,2") do + Time.now.utc_offset.should == 0 + end + with_timezone("Sun,Fri,2") do + Time.now.utc_offset.should == 0 + end + end + end +end diff --git a/spec/rubyspec/core/true/and_spec.rb b/spec/rubyspec/core/true/and_spec.rb new file mode 100644 index 0000000000..b81b6b36b6 --- /dev/null +++ b/spec/rubyspec/core/true/and_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "TrueClass#&" do + it "returns false if other is nil or false, otherwise true" do + (true & true).should == true + (true & false).should == false + (true & nil).should == false + (true & "").should == true + (true & mock('x')).should == true + end +end diff --git a/spec/rubyspec/core/true/inspect_spec.rb b/spec/rubyspec/core/true/inspect_spec.rb new file mode 100644 index 0000000000..baf26123c8 --- /dev/null +++ b/spec/rubyspec/core/true/inspect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "TrueClass#inspect" do + it "returns the string 'true'" do + true.inspect.should == "true" + end +end diff --git a/spec/rubyspec/core/true/or_spec.rb b/spec/rubyspec/core/true/or_spec.rb new file mode 100644 index 0000000000..a104551ae8 --- /dev/null +++ b/spec/rubyspec/core/true/or_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "TrueClass#|" do + it "returns true" do + (true | true).should == true + (true | false).should == true + (true | nil).should == true + (true | "").should == true + (true | mock('x')).should == true + end +end diff --git a/spec/rubyspec/core/true/to_s_spec.rb b/spec/rubyspec/core/true/to_s_spec.rb new file mode 100644 index 0000000000..0e2a807a95 --- /dev/null +++ b/spec/rubyspec/core/true/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "TrueClass#to_s" do + it "returns the string 'true'" do + true.to_s.should == "true" + end +end diff --git a/spec/rubyspec/core/true/xor_spec.rb b/spec/rubyspec/core/true/xor_spec.rb new file mode 100644 index 0000000000..1d0ad394da --- /dev/null +++ b/spec/rubyspec/core/true/xor_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "TrueClass#^" do + it "returns true if other is nil or false, otherwise false" do + (true ^ true).should == false + (true ^ false).should == true + (true ^ nil).should == true + (true ^ "").should == false + (true ^ mock('x')).should == false + end +end diff --git a/spec/rubyspec/core/unboundmethod/arity_spec.rb b/spec/rubyspec/core/unboundmethod/arity_spec.rb new file mode 100644 index 0000000000..a804235b70 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/arity_spec.rb @@ -0,0 +1,207 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "UnboundMethod#arity" do + SpecEvaluate.desc = "for method definition" + + context "returns zero" do + evaluate <<-ruby do + def m() end + ruby + + method(:m).unbind.arity.should == 0 + end + + evaluate <<-ruby do + def n(&b) end + ruby + + method(:n).unbind.arity.should == 0 + end + end + + context "returns positive values" do + evaluate <<-ruby do + def m(a) end + def n(a, b) end + def o(a, b, c) end + def p(a, b, c, d) end + ruby + + method(:m).unbind.arity.should == 1 + method(:n).unbind.arity.should == 2 + method(:o).unbind.arity.should == 3 + method(:p).unbind.arity.should == 4 + end + + evaluate <<-ruby do + def m(a:) end + def n(a:, b:) end + def o(a: 1, b:, c:, d: 2) end + ruby + + method(:m).unbind.arity.should == 1 + method(:n).unbind.arity.should == 1 + method(:o).unbind.arity.should == 1 + end + + evaluate <<-ruby do + def m(a, b:) end + def n(a, b:, &l) end + ruby + + method(:m).unbind.arity.should == 2 + method(:n).unbind.arity.should == 2 + end + + evaluate <<-ruby do + def m(a, b, c:, d: 1) end + def n(a, b, c:, d: 1, **k, &l) end + ruby + + method(:m).unbind.arity.should == 3 + method(:n).unbind.arity.should == 3 + end + end + + context "returns negative values" do + evaluate <<-ruby do + def m(a=1) end + def n(a=1, b=2) end + ruby + + method(:m).unbind.arity.should == -1 + method(:n).unbind.arity.should == -1 + end + + evaluate <<-ruby do + def m(a, b=1) end + def n(a, b, c=1, d=2) end + ruby + + method(:m).unbind.arity.should == -2 + method(:n).unbind.arity.should == -3 + end + + evaluate <<-ruby do + def m(a=1, *b) end + def n(a=1, b=2, *c) end + ruby + + method(:m).unbind.arity.should == -1 + method(:n).unbind.arity.should == -1 + end + + evaluate <<-ruby do + def m(*) end + def n(*a) end + ruby + + method(:m).unbind.arity.should == -1 + method(:n).unbind.arity.should == -1 + end + + evaluate <<-ruby do + def m(a, *) end + def n(a, *b) end + def o(a, b, *c) end + def p(a, b, c, *d) end + ruby + + method(:m).unbind.arity.should == -2 + method(:n).unbind.arity.should == -2 + method(:o).unbind.arity.should == -3 + method(:p).unbind.arity.should == -4 + end + + evaluate <<-ruby do + def m(*a, b) end + def n(*a, b, c) end + def o(*a, b, c, d) end + ruby + + method(:m).unbind.arity.should == -2 + method(:n).unbind.arity.should == -3 + method(:o).unbind.arity.should == -4 + end + + evaluate <<-ruby do + def m(a, *b, c) end + def n(a, b, *c, d, e) end + ruby + + method(:m).unbind.arity.should == -3 + method(:n).unbind.arity.should == -5 + end + + evaluate <<-ruby do + def m(a, b=1, c=2, *d, e, f) end + def n(a, b, c=1, *d, e, f, g) end + ruby + + method(:m).unbind.arity.should == -4 + method(:n).unbind.arity.should == -6 + end + + evaluate <<-ruby do + def m(a: 1) end + def n(a: 1, b: 2) end + ruby + + method(:m).unbind.arity.should == -1 + method(:n).unbind.arity.should == -1 + end + + evaluate <<-ruby do + def m(a=1, b: 2) end + def n(*a, b: 1) end + def o(a=1, b: 2) end + def p(a=1, *b, c: 2, &l) end + ruby + + method(:m).unbind.arity.should == -1 + method(:n).unbind.arity.should == -1 + method(:o).unbind.arity.should == -1 + method(:p).unbind.arity.should == -1 + end + + evaluate <<-ruby do + def m(**k, &l) end + def n(*a, **k) end + def o(a: 1, b: 2, **k) end + ruby + + method(:m).unbind.arity.should == -1 + method(:n).unbind.arity.should == -1 + method(:o).unbind.arity.should == -1 + end + + evaluate <<-ruby do + def m(a=1, *b, c:, d: 2, **k, &l) end + ruby + + method(:m).unbind.arity.should == -2 + end + + evaluate <<-ruby do + def m(a, b=1, *c, d, e:, f: 2, **k, &l) end + def n(a, b=1, *c, d:, e:, f: 2, **k, &l) end + def o(a=0, b=1, *c, d, e:, f: 2, **k, &l) end + def p(a=0, b=1, *c, d:, e:, f: 2, **k, &l) end + ruby + + method(:m).unbind.arity.should == -4 + method(:n).unbind.arity.should == -3 + method(:o).unbind.arity.should == -3 + method(:p).unbind.arity.should == -2 + end + end + + context "for a Method generated by respond_to_missing?" do + it "returns -1" do + obj = mock("method arity respond_to_missing") + obj.should_receive(:respond_to_missing?).and_return(true) + + obj.method(:m).unbind.arity.should == -1 + end + end +end diff --git a/spec/rubyspec/core/unboundmethod/bind_spec.rb b/spec/rubyspec/core/unboundmethod/bind_spec.rb new file mode 100644 index 0000000000..3f7a7bf3ac --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/bind_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "UnboundMethod#bind" do + before :each do + @normal_um = UnboundMethodSpecs::Methods.new.method(:foo).unbind + @parent_um = UnboundMethodSpecs::Parent.new.method(:foo).unbind + @child1_um = UnboundMethodSpecs::Child1.new.method(:foo).unbind + @child2_um = UnboundMethodSpecs::Child2.new.method(:foo).unbind + end + + it "raises TypeError if object is not kind_of? the Module the method defined in" do + lambda { @normal_um.bind(UnboundMethodSpecs::B.new) }.should raise_error(TypeError) + end + + it "returns Method for any object that is kind_of? the Module method was extracted from" do + @normal_um.bind(UnboundMethodSpecs::Methods.new).should be_kind_of(Method) + end + + it "returns Method on any object when UnboundMethod is unbound from a module" do + UnboundMethodSpecs::Mod.instance_method(:from_mod).bind(Object.new).should be_kind_of(Method) + end + + it "returns Method returned for obj is equal to one directly returned by obj.method" do + obj = UnboundMethodSpecs::Methods.new + @normal_um.bind(obj).should == obj.method(:foo) + end + + it "returns a callable method" do + obj = UnboundMethodSpecs::Methods.new + @normal_um.bind(obj).call.should == obj.foo + end + + it "binds a Parent's class method to any Child's class methods" do + m = UnboundMethodSpecs::Parent.method(:class_method).unbind.bind(UnboundMethodSpecs::Child1) + m.should be_an_instance_of(Method) + m.call.should == "I am UnboundMethodSpecs::Child1" + end + + it "will raise when binding a an object singleton's method to another object" do + other = UnboundMethodSpecs::Parent.new + p = UnboundMethodSpecs::Parent.new + class << p + def singleton_method + :single + end + end + um = p.method(:singleton_method).unbind + lambda{ um.bind(other) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/core/unboundmethod/clone_spec.rb b/spec/rubyspec/core/unboundmethod/clone_spec.rb new file mode 100644 index 0000000000..2f3fd834ad --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/clone_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "UnboundMethod#clone" do + it "returns a copy of the UnboundMethod" do + um1 = UnboundMethodSpecs::Methods.instance_method(:foo) + um2 = um1.clone + + (um1 == um2).should == true + um1.bind(UnboundMethodSpecs::Methods.new).call.should == um2.bind(UnboundMethodSpecs::Methods.new).call + end +end diff --git a/spec/rubyspec/core/unboundmethod/eql_spec.rb b/spec/rubyspec/core/unboundmethod/eql_spec.rb new file mode 100644 index 0000000000..8e91ef375e --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/eql_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "UnboundMethod#eql?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/unboundmethod/equal_value_spec.rb b/spec/rubyspec/core/unboundmethod/equal_value_spec.rb new file mode 100644 index 0000000000..de141254c1 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/equal_value_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +context "Creating UnboundMethods" do + specify "there is no difference between Method#unbind and Module#instance_method" do + UnboundMethodSpecs::Methods.instance_method(:foo).should be_kind_of(UnboundMethod) + UnboundMethodSpecs::Methods.new.method(:foo).unbind.should be_kind_of(UnboundMethod) + end +end + +describe "UnboundMethod#==" do + before :all do + @from_module = UnboundMethodSpecs::Methods.instance_method(:foo) + @from_unbind = UnboundMethodSpecs::Methods.new.method(:foo).unbind + + @with_block = UnboundMethodSpecs::Methods.instance_method(:with_block) + + @includee = UnboundMethodSpecs::Mod.instance_method(:from_mod) + @includer = UnboundMethodSpecs::Methods.instance_method(:from_mod) + + @alias_1 = UnboundMethodSpecs::Methods.instance_method(:alias_1) + @alias_2 = UnboundMethodSpecs::Methods.instance_method(:alias_2) + + @original_body = UnboundMethodSpecs::Methods.instance_method(:original_body) + @identical_body = UnboundMethodSpecs::Methods.instance_method(:identical_body) + + @parent = UnboundMethodSpecs::Parent.instance_method(:foo) + @child1 = UnboundMethodSpecs::Child1.instance_method(:foo) + @child2 = UnboundMethodSpecs::Child2.instance_method(:foo) + + @child1_alt = UnboundMethodSpecs::Child1.instance_method(:foo) + + @discard_1 = UnboundMethodSpecs::Methods.instance_method(:discard_1) + @discard_2 = UnboundMethodSpecs::Methods.instance_method(:discard_2) + + @method_one = UnboundMethodSpecs::Methods.instance_method(:one) + @method_two = UnboundMethodSpecs::Methods.instance_method(:two) + end + + it "returns true if objects refer to the same method" do + (@from_module == @from_module).should == true + (@from_unbind == @from_unbind).should == true + (@from_module == @from_unbind).should == true + (@from_unbind == @from_module).should == true + (@with_block == @with_block).should == true + end + + it "returns true if either is an alias for the other" do + (@from_module == @alias_1).should == true + (@alias_1 == @from_module).should == true + end + + it "returns true if both are aliases for a third method" do + (@from_module == @alias_1).should == true + (@alias_1 == @from_module).should == true + + (@from_module == @alias_2).should == true + (@alias_2 == @from_module).should == true + + (@alias_1 == @alias_2).should == true + (@alias_2 == @alias_1).should == true + end + + it "returns true if same method is extracted from the same subclass" do + (@child1 == @child1_alt).should == true + (@child1_alt == @child1).should == true + end + + it "returns false if UnboundMethods are different methods" do + (@method_one == @method_two).should == false + (@method_two == @method_one).should == false + end + + it "returns false if both have identical body but are not the same" do + (@original_body == @identical_body).should == false + (@identical_body == @original_body).should == false + end + + it "returns false if same method but one extracted from a subclass" do + (@parent == @child1).should == false + (@child1 == @parent).should == false + end + + it "returns false if same method but extracted from two different subclasses" do + (@child2 == @child1).should == false + (@child1 == @child2).should == false + end + + it "returns false if methods are the same but added from an included Module" do + (@includee == @includer).should == false + (@includer == @includee).should == false + end + + it "returns false if both have same Module, same name, identical body but not the same" do + class UnboundMethodSpecs::Methods + def discard_1; :discard; end + end + + (@discard_1 == UnboundMethodSpecs::Methods.instance_method(:discard_1)).should == false + end +end diff --git a/spec/rubyspec/core/unboundmethod/fixtures/classes.rb b/spec/rubyspec/core/unboundmethod/fixtures/classes.rb new file mode 100644 index 0000000000..43e21916bf --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/fixtures/classes.rb @@ -0,0 +1,86 @@ +module UnboundMethodSpecs + + + class SourceLocation + def self.location # This needs to be on this line + :location # for the spec to pass + end + + def self.redefined + :first + end + + def self.redefined + :last + end + + def original + end + + alias :aka :original + end + + module Mod + def from_mod; end + end + + class Methods + include Mod + + def foo + true + end + + def with_block(&block); end + + alias bar foo + alias alias_1 foo + alias alias_2 foo + + def original_body(); :this; end + def identical_body(); :this; end + + def one; end + def two(a); end + def three(a, b); end + def four(a, b, &c); end + + def neg_one(*a); end + def neg_two(a, *b); end + def neg_three(a, b, *c); end + def neg_four(a, b, *c, &d); end + + def discard_1(); :discard; end + def discard_2(); :discard; end + end + + class Parent + def foo; end + def self.class_method + "I am #{name}" + end + end + + class Child1 < Parent; end + class Child2 < Parent; end + class Child3 < Parent + class << self + alias_method :another_class_method, :class_method + end + end + + class A + def baz(a, b) + return [__FILE__, self.class] + end + def overridden; end + end + + class B < A + def overridden; end + end + + class C < B + def overridden; end + end +end diff --git a/spec/rubyspec/core/unboundmethod/hash_spec.rb b/spec/rubyspec/core/unboundmethod/hash_spec.rb new file mode 100644 index 0000000000..6ba6c04834 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/hash_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "UnboundMethod#hash" do + it "needs to be reviewed for spec completeness" + + it "returns the same value for user methods that are eql?" do + foo, bar = UnboundMethodSpecs::Methods.instance_method(:foo), UnboundMethodSpecs::Methods.instance_method(:bar) + foo.hash.should == bar.hash + end + + # See also redmine #6048 + it "returns the same value for builtin methods that are eql?" do + to_s, inspect = Array.instance_method(:to_s), Array.instance_method(:inspect) + to_s.hash.should == inspect.hash + end +end diff --git a/spec/rubyspec/core/unboundmethod/inspect_spec.rb b/spec/rubyspec/core/unboundmethod/inspect_spec.rb new file mode 100644 index 0000000000..ffef61a639 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/inspect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "UnboundMethod#inspect" do + it_behaves_like(:unboundmethod_to_s, :inspect) +end diff --git a/spec/rubyspec/core/unboundmethod/name_spec.rb b/spec/rubyspec/core/unboundmethod/name_spec.rb new file mode 100644 index 0000000000..c4602d529a --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/name_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "UnboundMethod#name" do + it "returns the name of the method" do + String.instance_method(:upcase).name.should == :upcase + end + + it "returns the name even when aliased" do + obj = UnboundMethodSpecs::Methods.new + obj.method(:foo).unbind.name.should == :foo + obj.method(:bar).unbind.name.should == :bar + UnboundMethodSpecs::Methods.instance_method(:bar).name.should == :bar + end +end diff --git a/spec/rubyspec/core/unboundmethod/owner_spec.rb b/spec/rubyspec/core/unboundmethod/owner_spec.rb new file mode 100644 index 0000000000..165b175147 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/owner_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "UnboundMethod#owner" do + it "returns the owner of the method" do + String.instance_method(:upcase).owner.should == String + end + + it "returns the same owner when aliased in the same classes" do + UnboundMethodSpecs::Methods.instance_method(:foo).owner.should == UnboundMethodSpecs::Methods + UnboundMethodSpecs::Methods.instance_method(:bar).owner.should == UnboundMethodSpecs::Methods + end + + it "returns the class/module it was defined in" do + UnboundMethodSpecs::C.instance_method(:baz).owner.should == UnboundMethodSpecs::A + UnboundMethodSpecs::Methods.instance_method(:from_mod).owner.should == UnboundMethodSpecs::Mod + end + + it "returns the new owner for aliased methods on singleton classes" do + parent_singleton_class = UnboundMethodSpecs::Parent.singleton_class + child_singleton_class = UnboundMethodSpecs::Child3.singleton_class + + child_singleton_class.instance_method(:class_method).owner.should == parent_singleton_class + child_singleton_class.instance_method(:another_class_method).owner.should == child_singleton_class + end +end diff --git a/spec/rubyspec/core/unboundmethod/parameters_spec.rb b/spec/rubyspec/core/unboundmethod/parameters_spec.rb new file mode 100644 index 0000000000..a48c967ea5 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/parameters_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "UnboundMethod#parameters" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/core/unboundmethod/shared/to_s.rb b/spec/rubyspec/core/unboundmethod/shared/to_s.rb new file mode 100644 index 0000000000..07ba0903e2 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/shared/to_s.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :unboundmethod_to_s, shared: true do + before :each do + @from_module = UnboundMethodSpecs::Methods.instance_method(:from_mod) + @from_method = UnboundMethodSpecs::Methods.new.method(:from_mod).unbind + end + + it "returns a String" do + @from_module.send(@method).should be_kind_of(String) + @from_method.send(@method).should be_kind_of(String) + end + + it "the String reflects that this is an UnboundMethod object" do + @from_module.send(@method).should =~ /\bUnboundMethod\b/ + @from_method.send(@method).should =~ /\bUnboundMethod\b/ + end + + it "the String shows the method name, Module defined in and Module extracted from" do + @from_module.send(@method).should =~ /\bfrom_mod\b/ + @from_module.send(@method).should =~ /\bUnboundMethodSpecs::Mod\b/ + @from_method.send(@method).should =~ /\bUnboundMethodSpecs::Methods\b/ + end +end diff --git a/spec/rubyspec/core/unboundmethod/source_location_spec.rb b/spec/rubyspec/core/unboundmethod/source_location_spec.rb new file mode 100644 index 0000000000..8b95f0776b --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/source_location_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "UnboundMethod#source_location" do + before :each do + @method = UnboundMethodSpecs::SourceLocation.method(:location).unbind + end + + it "sets the first value to the path of the file in which the method was defined" do + file = @method.source_location.first + file.should be_an_instance_of(String) + file.should == File.dirname(__FILE__) + '/fixtures/classes.rb' + end + + it "sets the last value to a Fixnum representing the line on which the method was defined" do + line = @method.source_location.last + line.should be_an_instance_of(Fixnum) + line.should == 5 + end + + it "returns the last place the method was defined" do + UnboundMethodSpecs::SourceLocation.method(:redefined).unbind.source_location.last.should == 13 + end + + it "returns the location of the original method even if it was aliased" do + UnboundMethodSpecs::SourceLocation.instance_method(:aka).source_location.last.should == 17 + end + + it "works for define_method methods" do + line = nil + cls = Class.new do + line = __LINE__ + 1 + define_method(:foo) { } + end + + method = cls.instance_method(:foo) + method.source_location[0].should =~ /#{__FILE__}/ + method.source_location[1].should == line + end + + it "works for define_singleton_method methods" do + line = nil + cls = Class.new do + line = __LINE__ + 1 + define_singleton_method(:foo) { } + end + + method = cls.method(:foo) + method.source_location[0].should =~ /#{__FILE__}/ + method.source_location[1].should == line + end +end diff --git a/spec/rubyspec/core/unboundmethod/super_method_spec.rb b/spec/rubyspec/core/unboundmethod/super_method_spec.rb new file mode 100644 index 0000000000..49d356fd2b --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/super_method_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "UnboundMethod#super_method" do + it "returns the method that would be called by super in the method" do + meth = UnboundMethodSpecs::C.instance_method(:overridden) + meth = meth.super_method + meth.should == UnboundMethodSpecs::B.instance_method(:overridden) + meth = meth.super_method + meth.should == UnboundMethodSpecs::A.instance_method(:overridden) + end + + it "returns nil when there's no super method in the parent" do + method = Kernel.instance_method(:method) + method.super_method.should == nil + end + + it "returns nil when the parent's method is removed" do + parent = Class.new { def foo; end } + child = Class.new(parent) { def foo; end } + + method = child.instance_method(:foo) + + parent.send(:undef_method, :foo) + + method.super_method.should == nil + end +end diff --git a/spec/rubyspec/core/unboundmethod/to_s_spec.rb b/spec/rubyspec/core/unboundmethod/to_s_spec.rb new file mode 100644 index 0000000000..77e1d713f0 --- /dev/null +++ b/spec/rubyspec/core/unboundmethod/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) + +describe "UnboundMethod#to_s" do + it_behaves_like(:unboundmethod_to_s, :to_s) +end diff --git a/spec/rubyspec/default.mspec b/spec/rubyspec/default.mspec new file mode 100644 index 0000000000..90f4c1ccaa --- /dev/null +++ b/spec/rubyspec/default.mspec @@ -0,0 +1,52 @@ +# Configuration file for Ruby >= 2.0 implementations. + +class MSpecScript + # Language features specs + set :language, [ 'language' ] + + # Core library specs + set :core, [ 'core' ] + + # Standard library specs + set :library, [ 'library' ] + + # Command line specs + set :command_line, [ 'command_line' ] + + # Security specs + set :security, [ 'security' ] + + # C extension API specs + set :capi, [ 'optional/capi' ] + + # A list of _all_ optional specs + set :optional, get(:capi) + + # An ordered list of the directories containing specs to run + set :files, get(:command_line) + get(:language) + get(:core) + get(:library) + get(:security) + get(:optional) + + # This set of files is run by mspec ci + set :ci_files, get(:files) + + # The default implementation to run the specs. + # TODO: this needs to be more sophisticated since the + # executable is not consistently named. + set :target, 'ruby' + + set :backtrace_filter, /mspec\// + + set :tags_patterns, [ + [%r(language/), 'tags/1.9/language/'], + [%r(core/), 'tags/1.9/core/'], + [%r(command_line/), 'tags/1.9/command_line/'], + [%r(library/), 'tags/1.9/library/'], + [%r(security/), 'tags/1.9/security/'], + [/_spec.rb$/, '_tags.txt'] + ] + + # Enable features + MSpec.enable_feature :fiber + MSpec.enable_feature :fiber_library + MSpec.enable_feature :fork if respond_to?(:fork, true) + MSpec.enable_feature :encoding +end diff --git a/spec/rubyspec/fixtures/basicobject/method_missing.rb b/spec/rubyspec/fixtures/basicobject/method_missing.rb new file mode 100644 index 0000000000..17a0fe904c --- /dev/null +++ b/spec/rubyspec/fixtures/basicobject/method_missing.rb @@ -0,0 +1,55 @@ +module KernelSpecs + module ModuleNoMM + class << self + def method_public() :module_public_method end + + def method_protected() :module_private_method end + protected :method_protected + + def method_private() :module_private_method end + private :method_private + end + end + + module ModuleMM + class << self + def method_missing(*args) :module_method_missing end + + def method_public() :module_public_method end + + def method_protected() :module_private_method end + protected :method_protected + + def method_private() :module_private_method end + private :method_private + end + end + + class ClassNoMM + class << self + def method_public() :class_public_method end + + def method_protected() :class_private_method end + protected :method_protected + + def method_private() :class_private_method end + private :method_private + end + + def method_public() :instance_public_method end + + def method_protected() :instance_private_method end + protected :method_protected + + def method_private() :instance_private_method end + private :method_private + end + + class ClassMM < ClassNoMM + class << self + def method_missing(*args) :class_method_missing end + end + + def method_missing(*args) :instance_method_missing end + end +end diff --git a/spec/rubyspec/fixtures/class.rb b/spec/rubyspec/fixtures/class.rb new file mode 100644 index 0000000000..9609eb6f3c --- /dev/null +++ b/spec/rubyspec/fixtures/class.rb @@ -0,0 +1,136 @@ +module ClassSpecs + + def self.sclass_with_block + class << self + yield + end + end + + def self.sclass_with_return + class << self + return :inner + end + return :outer + end + + class A; end + + def self.string_class_variables(obj) + obj.class_variables.map { |x| x.to_s } + end + + def self.string_instance_variables(obj) + obj.instance_variables.map { |x| x.to_s } + end + + class B + @@cvar = :cvar + @ivar = :ivar + + end + + class C + def self.make_class_variable + @@cvar = :cvar + end + + def self.make_class_instance_variable + @civ = :civ + end + end + + class D + def make_class_variable + @@cvar = :cvar + end + end + + class E + def self.cmeth() :cmeth end + def meth() :meth end + + class << self + def smeth() :smeth end + end + + CONSTANT = :constant! + end + + class F; end + class F + def meth() :meth end + end + class F + def another() :another end + end + + class G + def override() :nothing end + def override() :override end + end + + class Container + class A; end + class B; end + end + + O = Object.new + class << O + def smeth + :smeth + end + end + + class H + def self.inherited(sub) + track_inherited << sub + end + + def self.track_inherited + @inherited_modules ||= [] + end + end + + class K < H; end + + class I + class J < self + end + end + + class K + def example_instance_method + end + def self.example_class_method + end + end + + class L; end + + class M < L; end + + # Can't use a method here because of class definition in method body error + ANON_CLASS_FOR_NEW = lambda do + Class.new do + class NamedInModule + end + + def self.get_class_name + NamedInModule.name + end + end + end +end + +class Class + def example_instance_method_of_class; end + def self.example_class_method_of_class; end +end +class << Class + def example_instance_method_of_singleton_class; end + def self.example_class_method_of_singleton_class; end +end +class Object + def example_instance_method_of_object; end + def self.example_class_method_of_object; end +end diff --git a/spec/rubyspec/fixtures/class_variables.rb b/spec/rubyspec/fixtures/class_variables.rb new file mode 100644 index 0000000000..02ca042230 --- /dev/null +++ b/spec/rubyspec/fixtures/class_variables.rb @@ -0,0 +1,58 @@ +module ClassVariablesSpec + + class ClassA + @@cvar_a = :cvar_a + + def cvar_a + @@cvar_a + end + + def cvar_a=(val) + @@cvar_a = val + end + end + + class ClassB < ClassA; end + + # Extended in ClassC + module ModuleM + @@cvar_m = :value + + def cvar_m + @@cvar_m + end + + def cvar_m=(val) + @@cvar_m = val + end + end + + # Extended in ModuleO + module ModuleN + @@cvar_n = :value + + def cvar_n + @@cvar_n + end + + def cvar_n=(val) + @@cvar_n = val + end + end + + module ModuleO + extend ModuleN + end + + class ClassC + extend ModuleM + + def self.cvar_defined? + self.class_variable_defined?(:@@cvar) + end + + def self.cvar_c=(val) + @@cvar = val + end + end +end diff --git a/spec/rubyspec/fixtures/code/a/load_fixture.bundle b/spec/rubyspec/fixtures/code/a/load_fixture.bundle new file mode 100644 index 0000000000..30b53d7c0c --- /dev/null +++ b/spec/rubyspec/fixtures/code/a/load_fixture.bundle @@ -0,0 +1 @@ +ScratchPad << :ext_bundle diff --git a/spec/rubyspec/fixtures/code/a/load_fixture.dll b/spec/rubyspec/fixtures/code/a/load_fixture.dll new file mode 100644 index 0000000000..77c65b315b --- /dev/null +++ b/spec/rubyspec/fixtures/code/a/load_fixture.dll @@ -0,0 +1 @@ +ScratchPad << :ext_dll diff --git a/spec/rubyspec/fixtures/code/a/load_fixture.so b/spec/rubyspec/fixtures/code/a/load_fixture.so new file mode 100644 index 0000000000..4d02dea086 --- /dev/null +++ b/spec/rubyspec/fixtures/code/a/load_fixture.so @@ -0,0 +1 @@ +ScratchPad << :ext_so diff --git a/spec/rubyspec/fixtures/code/b/load_fixture.rb b/spec/rubyspec/fixtures/code/b/load_fixture.rb new file mode 100644 index 0000000000..4a6e9c9601 --- /dev/null +++ b/spec/rubyspec/fixtures/code/b/load_fixture.rb @@ -0,0 +1 @@ +ScratchPad << :loaded diff --git a/spec/rubyspec/fixtures/code/concurrent.rb b/spec/rubyspec/fixtures/code/concurrent.rb new file mode 100644 index 0000000000..054b8fc055 --- /dev/null +++ b/spec/rubyspec/fixtures/code/concurrent.rb @@ -0,0 +1,12 @@ +ScratchPad.recorded << :con_pre +Thread.current[:in_concurrent_rb] = true + +if t = Thread.current[:wait_for] + Thread.pass until t.backtrace && t.backtrace.any? { |call| call.include? 'require' } +end + +if Thread.current[:con_raise] + raise "con1" +end + +ScratchPad.recorded << :con_post diff --git a/spec/rubyspec/fixtures/code/concurrent2.rb b/spec/rubyspec/fixtures/code/concurrent2.rb new file mode 100644 index 0000000000..08a7264023 --- /dev/null +++ b/spec/rubyspec/fixtures/code/concurrent2.rb @@ -0,0 +1,8 @@ +ScratchPad.recorded << :con2_pre + +Thread.current[:in_concurrent_rb2] = true + +t = Thread.current[:concurrent_require_thread] +Thread.pass until t[:in_concurrent_rb3] + +ScratchPad.recorded << :con2_post diff --git a/spec/rubyspec/fixtures/code/concurrent3.rb b/spec/rubyspec/fixtures/code/concurrent3.rb new file mode 100644 index 0000000000..edb441d15d --- /dev/null +++ b/spec/rubyspec/fixtures/code/concurrent3.rb @@ -0,0 +1,2 @@ +ScratchPad.recorded << :con3 +Thread.current[:in_concurrent_rb3] = true diff --git a/spec/rubyspec/fixtures/code/file_fixture.rb b/spec/rubyspec/fixtures/code/file_fixture.rb new file mode 100644 index 0000000000..27388c7d8d --- /dev/null +++ b/spec/rubyspec/fixtures/code/file_fixture.rb @@ -0,0 +1 @@ +ScratchPad << __FILE__ diff --git a/spec/rubyspec/fixtures/code/gem/load_fixture.rb b/spec/rubyspec/fixtures/code/gem/load_fixture.rb new file mode 100644 index 0000000000..e1806de201 --- /dev/null +++ b/spec/rubyspec/fixtures/code/gem/load_fixture.rb @@ -0,0 +1 @@ +ScratchPad << :loaded_gem diff --git a/spec/rubyspec/fixtures/code/line_fixture.rb b/spec/rubyspec/fixtures/code/line_fixture.rb new file mode 100644 index 0000000000..2a2a0568cd --- /dev/null +++ b/spec/rubyspec/fixtures/code/line_fixture.rb @@ -0,0 +1,5 @@ +ScratchPad << __LINE__ + +# line 3 + +ScratchPad << __LINE__ diff --git a/spec/rubyspec/fixtures/code/load_ext_fixture.rb b/spec/rubyspec/fixtures/code/load_ext_fixture.rb new file mode 100644 index 0000000000..4a6e9c9601 --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_ext_fixture.rb @@ -0,0 +1 @@ +ScratchPad << :loaded diff --git a/spec/rubyspec/fixtures/code/load_fixture b/spec/rubyspec/fixtures/code/load_fixture new file mode 100644 index 0000000000..1b76dc8cad --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture @@ -0,0 +1 @@ +ScratchPad << :no_ext diff --git a/spec/rubyspec/fixtures/code/load_fixture.bundle b/spec/rubyspec/fixtures/code/load_fixture.bundle new file mode 100644 index 0000000000..30b53d7c0c --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.bundle @@ -0,0 +1 @@ +ScratchPad << :ext_bundle diff --git a/spec/rubyspec/fixtures/code/load_fixture.dll b/spec/rubyspec/fixtures/code/load_fixture.dll new file mode 100644 index 0000000000..77c65b315b --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.dll @@ -0,0 +1 @@ +ScratchPad << :ext_dll diff --git a/spec/rubyspec/fixtures/code/load_fixture.ext b/spec/rubyspec/fixtures/code/load_fixture.ext new file mode 100644 index 0000000000..f25b8e2951 --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.ext @@ -0,0 +1 @@ +ScratchPad << :no_rb_ext diff --git a/spec/rubyspec/fixtures/code/load_fixture.ext.bundle b/spec/rubyspec/fixtures/code/load_fixture.ext.bundle new file mode 100644 index 0000000000..30b53d7c0c --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.ext.bundle @@ -0,0 +1 @@ +ScratchPad << :ext_bundle diff --git a/spec/rubyspec/fixtures/code/load_fixture.ext.dll b/spec/rubyspec/fixtures/code/load_fixture.ext.dll new file mode 100644 index 0000000000..77c65b315b --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.ext.dll @@ -0,0 +1 @@ +ScratchPad << :ext_dll diff --git a/spec/rubyspec/fixtures/code/load_fixture.ext.rb b/spec/rubyspec/fixtures/code/load_fixture.ext.rb new file mode 100644 index 0000000000..4a6e9c9601 --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.ext.rb @@ -0,0 +1 @@ +ScratchPad << :loaded diff --git a/spec/rubyspec/fixtures/code/load_fixture.ext.so b/spec/rubyspec/fixtures/code/load_fixture.ext.so new file mode 100644 index 0000000000..4d02dea086 --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.ext.so @@ -0,0 +1 @@ +ScratchPad << :ext_so diff --git a/spec/rubyspec/fixtures/code/load_fixture.rb b/spec/rubyspec/fixtures/code/load_fixture.rb new file mode 100644 index 0000000000..4a6e9c9601 --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.rb @@ -0,0 +1 @@ +ScratchPad << :loaded diff --git a/spec/rubyspec/fixtures/code/load_fixture.so b/spec/rubyspec/fixtures/code/load_fixture.so new file mode 100644 index 0000000000..4d02dea086 --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_fixture.so @@ -0,0 +1 @@ +ScratchPad << :ext_so diff --git a/spec/rubyspec/fixtures/code/load_wrap_method_fixture.rb b/spec/rubyspec/fixtures/code/load_wrap_method_fixture.rb new file mode 100644 index 0000000000..b617473b4d --- /dev/null +++ b/spec/rubyspec/fixtures/code/load_wrap_method_fixture.rb @@ -0,0 +1,9 @@ +def top_level_method + ::ScratchPad << :load_wrap_loaded +end + +begin + top_level_method +rescue NameError + ::ScratchPad << :load_wrap_error +end diff --git a/spec/rubyspec/fixtures/code/methods_fixture.rb b/spec/rubyspec/fixtures/code/methods_fixture.rb new file mode 100644 index 0000000000..d42b5e4018 --- /dev/null +++ b/spec/rubyspec/fixtures/code/methods_fixture.rb @@ -0,0 +1,364 @@ +def foo1 +end + +def foo2 +end + +def foo3 +end + +def foo4 +end + +def foo5 +end + +def foo6 +end + +def foo7 +end + +def foo8 +end + +def foo9 +end + +def foo10 +end + +def foo11 +end + +def foo12 +end + +def foo13 +end + +def foo14 +end + +def foo15 +end + +def foo16 +end + +def foo17 +end + +def foo18 +end + +def foo19 +end + +def foo20 +end + +def foo21 +end + +def foo22 +end + +def foo23 +end + +def foo24 +end + +def foo25 +end + +def foo26 +end + +def foo27 +end + +def foo28 +end + +def foo29 +end + +def foo30 +end + +def foo31 +end + +def foo32 +end + +def foo33 +end + +def foo34 +end + +def foo35 +end + +def foo36 +end + +def foo37 +end + +def foo38 +end + +def foo39 +end + +def foo40 +end + +def foo41 +end + +def foo42 +end + +def foo43 +end + +def foo44 +end + +def foo45 +end + +def foo46 +end + +def foo47 +end + +def foo48 +end + +def foo49 +end + +def foo50 +end + +def foo51 +end + +def foo52 +end + +def foo53 +end + +def foo54 +end + +def foo55 +end + +def foo56 +end + +def foo57 +end + +def foo58 +end + +def foo59 +end + +def foo60 +end + +def foo61 +end + +def foo62 +end + +def foo63 +end + +def foo64 +end + +def foo65 +end + +def foo66 +end + +def foo67 +end + +def foo68 +end + +def foo69 +end + +def foo70 +end + +def foo71 +end + +def foo72 +end + +def foo73 +end + +def foo74 +end + +def foo75 +end + +def foo76 +end + +def foo77 +end + +def foo78 +end + +def foo79 +end + +def foo80 +end + +def foo81 +end + +def foo82 +end + +def foo83 +end + +def foo84 +end + +def foo85 +end + +def foo86 +end + +def foo87 +end + +def foo88 +end + +def foo89 +end + +def foo90 +end + +def foo91 +end + +def foo92 +end + +def foo93 +end + +def foo94 +end + +def foo95 +end + +def foo96 +end + +def foo97 +end + +def foo98 +end + +def foo99 +end + +def foo100 +end + +def foo101 +end + +def foo102 +end + +def foo103 +end + +def foo104 +end + +def foo105 +end + +def foo106 +end + +def foo107 +end + +def foo108 +end + +def foo109 +end + +def foo110 +end + +def foo111 +end + +def foo112 +end + +def foo113 +end + +def foo114 +end + +def foo115 +end + +def foo116 +end + +def foo117 +end + +def foo118 +end + +def foo119 +end + +def foo120 +end + +def foo121 +end + +ScratchPad << :loaded diff --git a/spec/rubyspec/fixtures/code/raise_fixture.rb b/spec/rubyspec/fixtures/code/raise_fixture.rb new file mode 100644 index 0000000000..9419089a06 --- /dev/null +++ b/spec/rubyspec/fixtures/code/raise_fixture.rb @@ -0,0 +1 @@ +raise "Exception loading a file" diff --git a/spec/rubyspec/fixtures/code/recursive_load_fixture.rb b/spec/rubyspec/fixtures/code/recursive_load_fixture.rb new file mode 100644 index 0000000000..18b144d44a --- /dev/null +++ b/spec/rubyspec/fixtures/code/recursive_load_fixture.rb @@ -0,0 +1,5 @@ +ScratchPad << :loaded + +if ScratchPad.recorded == [:loaded] + load File.expand_path("../recursive_load_fixture.rb", __FILE__) +end diff --git a/spec/rubyspec/fixtures/code/recursive_require_fixture.rb b/spec/rubyspec/fixtures/code/recursive_require_fixture.rb new file mode 100644 index 0000000000..8842a6ad74 --- /dev/null +++ b/spec/rubyspec/fixtures/code/recursive_require_fixture.rb @@ -0,0 +1,3 @@ +require File.expand_path("../recursive_require_fixture.rb", __FILE__) + +ScratchPad << :loaded diff --git a/spec/rubyspec/fixtures/code/symlink/symlink1.rb b/spec/rubyspec/fixtures/code/symlink/symlink1.rb new file mode 100644 index 0000000000..6a006eef14 --- /dev/null +++ b/spec/rubyspec/fixtures/code/symlink/symlink1.rb @@ -0,0 +1 @@ +require_relative 'symlink2/symlink2' diff --git a/spec/rubyspec/fixtures/code/symlink/symlink2/symlink2.rb b/spec/rubyspec/fixtures/code/symlink/symlink2/symlink2.rb new file mode 100644 index 0000000000..4a6e9c9601 --- /dev/null +++ b/spec/rubyspec/fixtures/code/symlink/symlink2/symlink2.rb @@ -0,0 +1 @@ +ScratchPad << :loaded diff --git a/spec/rubyspec/fixtures/code/wrap_fixture.rb b/spec/rubyspec/fixtures/code/wrap_fixture.rb new file mode 100644 index 0000000000..b83a8970d7 --- /dev/null +++ b/spec/rubyspec/fixtures/code/wrap_fixture.rb @@ -0,0 +1,3 @@ +class LoadSpecWrap + ScratchPad << self +end diff --git a/spec/rubyspec/fixtures/code_loading.rb b/spec/rubyspec/fixtures/code_loading.rb new file mode 100644 index 0000000000..d6cf0edb47 --- /dev/null +++ b/spec/rubyspec/fixtures/code_loading.rb @@ -0,0 +1,26 @@ +module CodeLoadingSpecs + # The #require instance method is private, so this class enables + # calling #require like obj.require(file). This is used to share + # specs between Kernel#require and Kernel.require. + class Method + def require(name) + super name + end + + def load(name, wrap=false) + super + end + end + + def self.spec_setup + @saved_loaded_features = $LOADED_FEATURES.clone + @saved_load_path = $LOAD_PATH.clone + ScratchPad.record [] + end + + def self.spec_cleanup + $LOADED_FEATURES.replace @saved_loaded_features + $LOAD_PATH.replace @saved_load_path + ScratchPad.clear + end +end diff --git a/spec/rubyspec/fixtures/constants.rb b/spec/rubyspec/fixtures/constants.rb new file mode 100644 index 0000000000..e5b20596ef --- /dev/null +++ b/spec/rubyspec/fixtures/constants.rb @@ -0,0 +1,288 @@ +# Contains all static code examples of all constants behavior in language and +# library specs. The specs include language/constants_spec.rb and the specs +# for Module#const_defined?, Module#const_get, Module#const_set, +# Module#remove_const, Module#const_missing and Module#constants. +# +# Rather than defining a class structure for each example, a canonical set of +# classes is used along with numerous constants, in most cases, a unique +# constant for each facet of behavior. This potentially leads to some +# redundancy but hopefully the minimal redundancy that includes reasonable +# variety in class and module configurations, including hierarchy, +# containment, inclusion, singletons and toplevel. +# +# Constants are numbered for for uniqueness. The CS_ prefix is uniformly used +# and is to minimize clashes with other toplevel constants (see e.g. ModuleA +# which is included in Object). Constant values are symbols. A numbered suffix +# is used to distinguish constants with the same name defined in different +# areas (e.g. CS_CONST10 has values :const10_1, :const10_2, etc.). +# +# Methods are named after the constants they reference (e.g. ClassA.const10 +# references CS_CONST10). Where it is reasonable to do so, both class and +# instance methods are defined. This is an instance of redundancy (class +# methods should behave no differently than instance methods) but is useful +# for ensuring compliance in implementations. + + +# This constant breaks the rule of defining all constants, classes, modules +# inside a module namespace for the particular specs, however, it is needed +# for completeness. No other constant of this name should be defined in the +# specs. +CS_CONST1 = :const1 # only defined here + +module ConstantSpecs + + # Included at toplevel + module ModuleA + CS_CONST10 = :const10_1 + CS_CONST12 = :const12_2 + CS_CONST13 = :const13 + CS_CONST21 = :const21_2 + end + + # Included in ParentA + module ModuleB + CS_CONST10 = :const10_9 + CS_CONST11 = :const11_2 + CS_CONST12 = :const12_1 + end + + # Included in ChildA + module ModuleC + CS_CONST10 = :const10_4 + CS_CONST15 = :const15_1 + end + + # Included in ChildA metaclass + module ModuleH + CS_CONST10 = :const10_7 + end + + # Included in ModuleD + module ModuleM + CS_CONST10 = :const10_11 + CS_CONST24 = :const24 + end + + # Included in ContainerA + module ModuleD + include ModuleM + + CS_CONST10 = :const10_8 + end + + # The following classes/modules have all the constants set "statically". + # Contrast with the classes below where the constants are set as the specs + # are run. + + class ClassA + CS_CONST10 = :const10_10 + CS_CONST16 = :const16 + CS_CONST17 = :const17_2 + CS_CONST22 = :const22_1 + + def self.const_missing(const) + const + end + + def self.constx; CS_CONSTX; end + def self.const10; CS_CONST10; end + def self.const16; ParentA.const16; end + def self.const22; ParentA.const22 { CS_CONST22 }; end + + def const10; CS_CONST10; end + def constx; CS_CONSTX; end + end + + class ParentA + include ModuleB + + CS_CONST4 = :const4 + CS_CONST10 = :const10_5 + CS_CONST11 = :const11_1 + CS_CONST15 = :const15_2 + CS_CONST20 = :const20_2 + CS_CONST21 = :const21_1 + CS_CONST22 = :const22_2 + + def self.constx; CS_CONSTX; end + def self.const10; CS_CONST10; end + def self.const16; CS_CONST16; end + def self.const22; yield; end + + def const10; CS_CONST10; end + def constx; CS_CONSTX; end + end + + class ContainerA + include ModuleD + + CS_CONST5 = :const5 + CS_CONST10 = :const10_2 + CS_CONST23 = :const23 + + class ChildA < ParentA + include ModuleC + + class << self + include ModuleH + + CS_CONST10 = :const10_6 + CS_CONST14 = :const14_1 + CS_CONST19 = :const19_1 + + def const19; CS_CONST19; end + end + + CS_CONST6 = :const6 + CS_CONST10 = :const10_3 + CS_CONST19 = :const19_2 + + def self.const10; CS_CONST10; end + def self.const11; CS_CONST11; end + def self.const12; CS_CONST12; end + def self.const13; CS_CONST13; end + def self.const15; CS_CONST15; end + def self.const21; CS_CONST21; end + + def const10; CS_CONST10; end + def const11; CS_CONST11; end + def const12; CS_CONST12; end + def const13; CS_CONST13; end + def const15; CS_CONST15; end + end + + def self.const10; CS_CONST10; end + + def const10; CS_CONST10; end + end + + class ContainerA::ChildA + def self.const23; CS_CONST23; end + end + + class ::Object + CS_CONST20 = :const20_1 + + module ConstantSpecs + class ContainerA + class ChildA + def self.const20; CS_CONST20; end + end + end + end + end + + # Included in ParentB + module ModuleE + end + + # Included in ChildB + module ModuleF + end + + # Included in ContainerB + module ModuleG + end + + # The following classes/modules have the same structure as the ones above + # but the constants are set as the specs are run. + + class ClassB + def self.const201; CS_CONST201; end + def self.const209; ParentB.const209; end + def self.const210; ParentB.const210 { CS_CONST210 }; end + + def const201; CS_CONST201; end + end + + class ParentB + include ModuleE + + def self.const201; CS_CONST201; end + def self.const209; CS_CONST209; end + def self.const210; yield; end + + def const201; CS_CONST201; end + end + + class ContainerB + include ModuleG + + class ChildB < ParentB + include ModuleF + + class << self + def const206; CS_CONST206; end + end + + def self.const201; CS_CONST201; end + def self.const202; CS_CONST202; end + def self.const203; CS_CONST203; end + def self.const204; CS_CONST204; end + def self.const205; CS_CONST205; end + def self.const212; CS_CONST212; end + def self.const213; CS_CONST213; end + + def const201; CS_CONST201; end + def const202; CS_CONST202; end + def const203; CS_CONST203; end + def const204; CS_CONST204; end + def const205; CS_CONST205; end + def const213; CS_CONST213; end + end + + def self.const201; CS_CONST201; end + end + + class ContainerB::ChildB + def self.const214; CS_CONST214; end + end + + class ::Object + module ConstantSpecs + class ContainerB + class ChildB + def self.const211; CS_CONST211; end + end + end + end + end + + # Constants + CS_CONST2 = :const2 # only defined here + CS_CONST17 = :const17_1 + + class << self + CS_CONST14 = :const14_2 + end + + # Singleton + a = ClassA.new + def a.const17; CS_CONST17; end + CS_CONST18 = a + + b = ClassB.new + def b.const207; CS_CONST207; end + CS_CONST208 = b + + # Methods + def self.get_const; self; end + + def const10; CS_CONST10; end + + class ClassC + CS_CONST1 = 1 + + class ClassE + CS_CONST2 = 2 + end + end + + class ClassD < ClassC + end + + CS_PRIVATE = :cs_private + private_constant :CS_PRIVATE +end + +include ConstantSpecs::ModuleA diff --git a/spec/rubyspec/fixtures/enumerator/classes.rb b/spec/rubyspec/fixtures/enumerator/classes.rb new file mode 100644 index 0000000000..6f285b8efa --- /dev/null +++ b/spec/rubyspec/fixtures/enumerator/classes.rb @@ -0,0 +1,15 @@ +module EnumSpecs + class Numerous + + include Enumerable + + def initialize(*list) + @list = list.empty? ? [2, 5, 3, 6, 1, 4] : list + end + + def each + @list.each { |i| yield i } + end + end + +end diff --git a/spec/rubyspec/fixtures/math/common.rb b/spec/rubyspec/fixtures/math/common.rb new file mode 100644 index 0000000000..024732fa7a --- /dev/null +++ b/spec/rubyspec/fixtures/math/common.rb @@ -0,0 +1,3 @@ +class IncludesMath + include Math +end diff --git a/spec/rubyspec/fixtures/rational.rb b/spec/rubyspec/fixtures/rational.rb new file mode 100644 index 0000000000..d0d05d0437 --- /dev/null +++ b/spec/rubyspec/fixtures/rational.rb @@ -0,0 +1,11 @@ +module RationalSpecs + class SubNumeric < Numeric + def initialize(value) + @value = Rational(value) + end + + def to_r + @value + end + end +end diff --git a/spec/rubyspec/fixtures/reflection.rb b/spec/rubyspec/fixtures/reflection.rb new file mode 100644 index 0000000000..fe004f6a82 --- /dev/null +++ b/spec/rubyspec/fixtures/reflection.rb @@ -0,0 +1,352 @@ +# These modules and classes are fixtures used by the Ruby reflection specs. +# These include specs for methods: +# +# Module: +# instance_methods +# public_instance_methods +# protected_instance_methods +# private_instance_methods +# +# Kernel: +# methods +# public_methods +# protected_methods +# private_methods +# singleton_methods +# +# The following naming scheme is used to keep the method names short and still +# communicate the relevant facts about the methods: +# +# X[s]_VIS +# +# where +# +# X is the name of the module or class in lower case +# s is the literal character 's' for singleton methods +# VIS is the first three letters of the corresponding visibility +# pub(lic), pro(tected), pri(vate) +# +# For example: +# +# l_pub is a public method on module L +# ls_pri is a private singleton method on module L + +module ReflectSpecs + # An object with no singleton methods. + def self.o + mock("Object with no singleton methods") + end + + # An object with singleton methods. + def self.os + obj = mock("Object with singleton methods") + class << obj + def os_pub; :os_pub; end + + def os_pro; :os_pro; end + protected :os_pro + + def os_pri; :os_pri; end + private :os_pri + end + obj + end + + # An object extended with a module. + def self.oe + obj = mock("Object extended") + obj.extend M + obj + end + + # An object with duplicate methods extended with a module. + def self.oed + obj = mock("Object extended") + obj.extend M + + class << obj + def pub; :pub; end + + def pro; :pro; end + protected :pro + + def pri; :pri; end + private :pri + end + + obj + end + + # An object extended with two modules. + def self.oee + obj = mock("Object extended twice") + obj.extend M + obj.extend N + obj + end + + # An object extended with a module including a module. + def self.oei + obj = mock("Object extended, included") + obj.extend N + obj + end + + # A simple module. + module L + class << self + def ls_pub; :ls_pub; end + + def ls_pro; :ls_pro; end + protected :ls_pro + + def ls_pri; :ls_pri; end + private :ls_pri + end + + def l_pub; :l_pub; end + + def l_pro; :l_pro; end + protected :l_pro + + def l_pri; :l_pri; end + private :l_pri + end + + # A module with no singleton methods. + module K + end + + # A simple module. + module M + class << self + def ms_pub; :ms_pub; end + + def ms_pro; :ms_pro; end + protected :ms_pro + + def ms_pri; :ms_pri; end + private :ms_pri + end + + def m_pub; :m_pub; end + + def m_pro; :m_pro; end + protected :m_pro + + def m_pri; :m_pri; end + private :m_pri + + def pub; :pub; end + + def pro; :pro; end + protected :pro + + def pri; :pri; end + private :pri + end + + # A module including a module + module N + include M + + class << self + def ns_pub; :ns_pub; end + + def ns_pro; :ns_pro; end + protected :ns_pro + + def ns_pri; :ns_pri; end + private :ns_pri + end + + def n_pub; :n_pub; end + + def n_pro; :n_pro; end + protected :n_pro + + def n_pri; :n_pri; end + private :n_pri + end + + # A simple class. + class A + class << self + def as_pub; :as_pub; end + + def as_pro; :as_pro; end + protected :as_pro + + def as_pri; :as_pri; end + private :as_pri + + def pub; :pub; end + + def pro; :pro; end + protected :pro + + def pri; :pri; end + private :pri + end + + def a_pub; :a_pub; end + + def a_pro; :a_pro; end + protected :a_pro + + def a_pri; :a_pri; end + private :a_pri + end + + # A simple subclass. + class B < A + class << self + def bs_pub; :bs_pub; end + + def bs_pro; :bs_pro; end + protected :bs_pro + + def bs_pri; :bs_pri; end + private :bs_pri + + def pub; :pub; end + + def pro; :pro; end + protected :pro + + def pri; :pri; end + private :pri + end + + def b_pub; :b_pub; end + + def b_pro; :b_pro; end + protected :b_pro + + def b_pri; :b_pri; end + private :b_pri + end + + # A subclass including a module. + class C < A + include M + + class << self + def cs_pub; :cs_pub; end + + def cs_pro; :cs_pro; end + protected :cs_pro + + def cs_pri; :cs_pri; end + private :cs_pri + + def pub; :pub; end + + def pro; :pro; end + protected :pro + + def pri; :pri; end + private :pri + end + + def c_pub; :c_pub; end + + def c_pro; :c_pro; end + protected :c_pro + + def c_pri; :c_pri; end + private :c_pri + end + + # A simple class including a module + class D + include M + + class << self + def ds_pub; :ds_pub; end + + def ds_pro; :ds_pro; end + protected :ds_pro + + def ds_pri; :ds_pri; end + private :ds_pri + end + + def d_pub; :d_pub; end + + def d_pro; :d_pro; end + protected :d_pro + + def d_pri; :d_pri; end + private :d_pri + + def pub; :pub; end + + def pro; :pro; end + protected :pro + + def pri; :pri; end + private :pri + end + + # A subclass of a class including a module. + class E < D + class << self + def es_pub; :es_pub; end + + def es_pro; :es_pro; end + protected :es_pro + + def es_pri; :es_pri; end + private :es_pri + end + + def e_pub; :e_pub; end + + def e_pro; :e_pro; end + protected :e_pro + + def e_pri; :e_pri; end + private :e_pri + + def pub; :pub; end + + def pro; :pro; end + protected :pro + + def pri; :pri; end + private :pri + end + + # A subclass that includes a module of a class including a module. + class F < D + include L + + class << self + def fs_pub; :fs_pub; end + + def fs_pro; :fs_pro; end + protected :fs_pro + + def fs_pri; :fs_pri; end + private :fs_pri + end + + def f_pub; :f_pub; end + + def f_pro; :f_pro; end + protected :f_pro + + def f_pri; :f_pri; end + private :f_pri + end + + # Class with no singleton methods. + class O + end + + # Class extended with a module. + class P + end + P.extend M +end diff --git a/spec/rubyspec/language/BEGIN_spec.rb b/spec/rubyspec/language/BEGIN_spec.rb new file mode 100644 index 0000000000..826d5f0c89 --- /dev/null +++ b/spec/rubyspec/language/BEGIN_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The BEGIN keyword" do + before :each do + ScratchPad.record [] + end + + it "runs in a shared scope" do + eval("BEGIN { var_in_begin = 'foo' }; var_in_begin").should == "foo" + end + + it "accesses variables outside the eval scope" do + outside_var = 'foo' + eval("BEGIN { var_in_begin = outside_var }; var_in_begin").should == "foo" + end + + it "must appear in a top-level context" do + lambda { eval "1.times { BEGIN { 1 } }" }.should raise_error(SyntaxError) + end + + it "runs first in a given code unit" do + eval "ScratchPad << 'foo'; BEGIN { ScratchPad << 'bar' }" + + ScratchPad.recorded.should == ['bar', 'foo'] + end + + it "runs multiple begins in FIFO order" do + eval "BEGIN { ScratchPad << 'foo' }; BEGIN { ScratchPad << 'bar' }" + + ScratchPad.recorded.should == ['foo', 'bar'] + end +end diff --git a/spec/rubyspec/language/README b/spec/rubyspec/language/README new file mode 100644 index 0000000000..b9d969ba1e --- /dev/null +++ b/spec/rubyspec/language/README @@ -0,0 +1,30 @@ +There are numerous possible way of categorizing the entities and concepts that +make up a programming language. Ruby has a fairly large number of reserved +words. These words significantly describe major elements of the language, +including flow control constructs like 'for' and 'while', conditional +execution like 'if' and 'unless', exceptional execution control like 'rescue', +etc. There are also literals for the basic "types" like String, Regexp, Array +and Fixnum. + +Behavorial specifications describe the behavior of concrete entities. Rather +than using concepts of computation to organize these spec files, we use +entities of the Ruby language. Consider looking at any syntactic element of a +Ruby program. With (almost) no ambiguity, one can identify it as a literal, +reserved word, variable, etc. There is a spec file that corresponds to each +literal construct and most reserved words, with the exceptions noted below. +There are also several files that are more difficult to classify: all +predefined variables, constants, and objects (predefined_spec.rb), the +precedence of all operators (precedence_spec.rb), the behavior of assignment +to variables (variables_spec.rb), the behavior of subprocess execution +(execution_spec.rb), the behavior of the raise method as it impacts the +execution of a Ruby program (raise_spec.rb), and the block entities like +'begin', 'do', ' { ... }' (block_spec.rb). + +Several reserved words and other entities are combined with the primary +reserved word or entity to which they are related: + +false, true, nil, self predefined_spec.rb +in for_spec.rb +then, elsif if_spec.rb +when case_spec.rb +catch throw_spec.rb diff --git a/spec/rubyspec/language/alias_spec.rb b/spec/rubyspec/language/alias_spec.rb new file mode 100644 index 0000000000..626e37a603 --- /dev/null +++ b/spec/rubyspec/language/alias_spec.rb @@ -0,0 +1,190 @@ +require File.expand_path('../../spec_helper', __FILE__) + +class AliasObject + attr :foo + attr_reader :bar + attr_accessor :baz + + def prep; @foo = 3; @bar = 4; end + def value; 5; end + def false_value; 6; end + def self.klass_method; 7; end +end + +describe "The alias keyword" do + before :each do + @obj = AliasObject.new + @meta = class << @obj;self;end + end + + it "creates a new name for an existing method" do + @meta.class_eval do + alias __value value + end + @obj.__value.should == 5 + end + + it "adds the new method to the list of methods" do + original_methods = @obj.methods + @meta.class_eval do + alias __value value + end + (@obj.methods - original_methods).map {|m| m.to_s }.should == ["__value"] + end + + it "adds the new method to the list of public methods" do + original_methods = @obj.public_methods + @meta.class_eval do + alias __value value + end + (@obj.public_methods - original_methods).map {|m| m.to_s }.should == ["__value"] + end + + it "overwrites an existing method with the target name" do + @meta.class_eval do + alias false_value value + end + @obj.false_value.should == 5 + end + + it "is reversible" do + @meta.class_eval do + alias __value value + alias value false_value + end + @obj.value.should == 6 + + @meta.class_eval do + alias value __value + end + @obj.value.should == 5 + end + + it "operates on the object's metaclass when used in instance_eval" do + @obj.instance_eval do + alias __value value + end + + @obj.__value.should == 5 + lambda { AliasObject.new.__value }.should raise_error(NoMethodError) + end + + it "operates on the class/module metaclass when used in instance_eval" do + AliasObject.instance_eval do + alias __klass_method klass_method + end + + AliasObject.__klass_method.should == 7 + lambda { Object.__klass_method }.should raise_error(NoMethodError) + end + + it "operates on the class/module metaclass when used in instance_exec" do + AliasObject.instance_exec do + alias __klass_method2 klass_method + end + + AliasObject.__klass_method2.should == 7 + lambda { Object.__klass_method2 }.should raise_error(NoMethodError) + end + + it "operates on methods defined via attr, attr_reader, and attr_accessor" do + @obj.prep + @obj.instance_eval do + alias afoo foo + alias abar bar + alias abaz baz + end + + @obj.afoo.should == 3 + @obj.abar.should == 4 + @obj.baz = 5 + @obj.abaz.should == 5 + end + + it "operates on methods with splat arguments" do + class AliasObject2;end + AliasObject2.class_eval do + def test(*args) + 4 + end + def test_with_check(*args) + test_without_check(*args) + end + alias test_without_check test + alias test test_with_check + end + AliasObject2.new.test(1,2,3,4,5).should == 4 + end + + it "operates on methods with splat arguments on eigenclasses" do + @meta.class_eval do + def test(*args) + 4 + end + def test_with_check(*args) + test_without_check(*args) + end + alias test_without_check test + alias test test_with_check + end + @obj.test(1,2,3,4,5).should == 4 + end + + it "operates on methods with splat arguments defined in a superclass" do + alias_class = Class.new + alias_class.class_eval do + def test(*args) + 4 + end + def test_with_check(*args) + test_without_check(*args) + end + end + sub = Class.new(alias_class) do + alias test_without_check test + alias test test_with_check + end + sub.new.test(1,2,3,4,5).should == 4 + end + + it "operates on methods with splat arguments defined in a superclass using text block for class eval" do + class Sub < AliasObject;end + AliasObject.class_eval <<-code + def test(*args) + 4 + end + def test_with_check(*args) + test_without_check(*args) + end + alias test_without_check test + alias test test_with_check + code + Sub.new.test("testing").should == 4 + end + + it "is not allowed against Fixnum or String instances" do + lambda do + 1.instance_eval do + alias :foo :to_s + end + end.should raise_error(TypeError) + + lambda do + :blah.instance_eval do + alias :foo :to_s + end + end.should raise_error(TypeError) + end + + it "on top level defines the alias on Object" do + # because it defines on the default definee / current module + ruby_exe("def foo; end; alias bla foo; print method(:bla).owner", escape: true).should == "Object" + end + + it "raises a NameError when passed a missing name" do + lambda { @meta.class_eval { alias undef_method not_exist } }.should raise_error(NameError) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end +end diff --git a/spec/rubyspec/language/and_spec.rb b/spec/rubyspec/language/and_spec.rb new file mode 100644 index 0000000000..e084fd3cef --- /dev/null +++ b/spec/rubyspec/language/and_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The '&&' statement" do + + it "short-circuits evaluation at the first condition to be false" do + x = nil + true && false && x = 1 + x.should be_nil + end + + it "evaluates to the first condition not to be true" do + value = nil + (value && nil).should == nil + (value && false).should == nil + value = false + (value && nil).should == false + (value && false).should == false + + ("yes" && 1 && nil && true).should == nil + ("yes" && 1 && false && true).should == false + end + + it "evaluates to the last condition if all are true" do + ("yes" && 1).should == 1 + (1 && "yes").should == "yes" + end + + it "evaluates the full set of chained conditions during assignment" do + x, y = nil + x = 1 && y = 2 + # "1 && y = 2" is evaluated and then assigned to x + x.should == 2 + end + + it "treats empty expressions as nil" do + (() && true).should be_nil + (true && ()).should be_nil + (() && ()).should be_nil + end + +end + +describe "The 'and' statement" do + it "short-circuits evaluation at the first condition to be false" do + x = nil + true and false and x = 1 + x.should be_nil + end + + it "evaluates to the first condition not to be true" do + value = nil + (value and nil).should == nil + (value and false).should == nil + value = false + (value and nil).should == false + (value and false).should == false + + ("yes" and 1 and nil and true).should == nil + ("yes" and 1 and false and true).should == false + end + + it "evaluates to the last condition if all are true" do + ("yes" and 1).should == 1 + (1 and "yes").should == "yes" + end + + it "when used in assignment, evaluates and assigns expressions individually" do + x, y = nil + x = 1 and y = 2 + # evaluates (x=1) and (y=2) + x.should == 1 + end + + it "treats empty expressions as nil" do + (() and true).should be_nil + (true and ()).should be_nil + (() and ()).should be_nil + end + +end diff --git a/spec/rubyspec/language/array_spec.rb b/spec/rubyspec/language/array_spec.rb new file mode 100644 index 0000000000..c3ed8c14c5 --- /dev/null +++ b/spec/rubyspec/language/array_spec.rb @@ -0,0 +1,155 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/array', __FILE__) + +describe "Array literals" do + it "[] should return a new array populated with the given elements" do + array = [1, 'a', nil] + array.should be_kind_of(Array) + array[0].should == 1 + array[1].should == 'a' + array[2].should == nil + end + + it "[] treats empty expressions as nil elements" do + array = [0, (), 2, (), 4] + array.should be_kind_of(Array) + array[0].should == 0 + array[1].should == nil + array[2].should == 2 + array[3].should == nil + array[4].should == 4 + end + + it "[] accepts a literal hash without curly braces as its only parameter" do + ["foo" => :bar, baz: 42].should == [{"foo" => :bar, baz: 42}] + end + + it "[] accepts a literal hash without curly braces as its last parameter" do + ["foo", "bar" => :baz].should == ["foo", {"bar" => :baz}] + [1, 2, 3 => 6, 4 => 24].should == [1, 2, {3 => 6, 4 => 24}] + end + + it "[] treats splatted nil as no element" do + [*nil].should == [] + [1, *nil].should == [1] + [1, 2, *nil].should == [1, 2] + [1, *nil, 3].should == [1, 3] + [*nil, *nil, *nil].should == [] + end +end + +describe "Bareword array literal" do + it "%w() transforms unquoted barewords into an array" do + a = 3 + %w(a #{3+a} 3).should == ["a", '#{3+a}', "3"] + end + + it "%W() transforms unquoted barewords into an array, supporting interpolation" do + a = 3 + %W(a #{3+a} 3).should == ["a", '6', "3"] + end + + it "%W() always treats interpolated expressions as a single word" do + a = "hello world" + %W(a b c #{a} d e).should == ["a", "b", "c", "hello world", "d", "e"] + end + + it "treats consecutive whitespace characters the same as one" do + %w(a b c d).should == ["a", "b", "c", "d"] + %W(hello + world).should == ["hello", "world"] + end + + it "treats whitespace as literals characters when escaped by a backslash" do + %w(a b\ c d e).should == ["a", "b c", "d", "e"] + %w(a b\ +c d).should == ["a", "b\nc", "d"] + %W(a\ b\tc).should == ["a ", "b\tc"] + %W(white\ \ \ \ \ space).should == ["white ", " ", " ", " space"] + end +end + +describe "The unpacking splat operator (*)" do + it "when applied to a literal nested array, unpacks its elements into the containing array" do + [1, 2, *[3, 4, 5]].should == [1, 2, 3, 4, 5] + end + + it "when applied to a nested referenced array, unpacks its elements into the containing array" do + splatted_array = [3, 4, 5] + [1, 2, *splatted_array].should == [1, 2, 3, 4, 5] + end + + it "returns a new array containing the same values when applied to an array inside an empty array" do + splatted_array = [3, 4, 5] + [*splatted_array].should == splatted_array + [*splatted_array].should_not equal(splatted_array) + end + + it "unpacks the start and count arguments in an array slice assignment" do + alphabet_1 = ['a'..'z'].to_a + alphabet_2 = alphabet_1.dup + start_and_count_args = [1, 10] + + alphabet_1[1, 10] = 'a' + alphabet_2[*start_and_count_args] = 'a' + + alphabet_1.should == alphabet_2 + end + + it "unpacks arguments as if they were listed statically" do + static = [1,2,3,4] + receiver = static.dup + args = [0,1] + static[0,1] = [] + static.should == [2,3,4] + receiver[*args] = [] + receiver.should == static + end + + it "unpacks a literal array into arguments in a method call" do + tester = ArraySpec::Splat.new + tester.unpack_3args(*[1, 2, 3]).should == [1, 2, 3] + tester.unpack_4args(1, 2, *[3, 4]).should == [1, 2, 3, 4] + tester.unpack_4args("a", %w(b c), *%w(d e)).should == ["a", ["b", "c"], "d", "e"] + end + + it "unpacks a referenced array into arguments in a method call" do + args = [1, 2, 3] + tester = ArraySpec::Splat.new + tester.unpack_3args(*args).should == [1, 2, 3] + tester.unpack_4args(0, *args).should == [0, 1, 2, 3] + end + + it "when applied to a non-Array value attempts to coerce it to Array if the object respond_to?(:to_a)" do + obj = mock("pseudo-array") + obj.should_receive(:to_a).and_return([2, 3, 4]) + [1, *obj].should == [1, 2, 3, 4] + end + + it "when applied to a non-Array value uses it unchanged if it does not respond_to?(:to_a)" do + obj = Object.new + obj.should_not respond_to(:to_a) + [1, *obj].should == [1, obj] + end + + it "when applied to a BasicObject coerces it to Array if it respond_to?(:to_a)" do + obj = BasicObject.new + def obj.to_a; [2, 3, 4]; end + [1, *obj].should == [1, 2, 3, 4] + end + + it "can be used before other non-splat elements" do + a = [1, 2] + [0, *a, 3].should == [0, 1, 2, 3] + end + + it "can be used multiple times in the same containing array" do + a = [1, 2] + b = [1, 0] + [*a, 3, *a, *b].should == [1, 2, 3, 1, 2, 1, 0] + end +end + +describe "The packing splat operator (*)" do + +end diff --git a/spec/rubyspec/language/block_spec.rb b/spec/rubyspec/language/block_spec.rb new file mode 100644 index 0000000000..0ae2125ade --- /dev/null +++ b/spec/rubyspec/language/block_spec.rb @@ -0,0 +1,865 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/block', __FILE__) + +describe "A block yielded a single" do + before :all do + def m(a) yield a end + end + + context "Array" do + it "assigns the Array to a single argument" do + m([1, 2]) { |a| a }.should == [1, 2] + end + + it "receives the identical Array object" do + ary = [1, 2] + m(ary) { |a| a }.should equal(ary) + end + + it "assigns the Array to a single rest argument" do + m([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]] + end + + it "assigns the first element to a single argument with trailing comma" do + m([1, 2]) { |a, | a }.should == 1 + end + + it "assigns elements to required arguments" do + m([1, 2, 3]) { |a, b| [a, b] }.should == [1, 2] + end + + it "assigns nil to unassigned required arguments" do + m([1, 2]) { |a, *b, c, d| [a, b, c, d] }.should == [1, [], 2, nil] + end + + it "assigns elements to optional arguments" do + m([1, 2]) { |a=5, b=4, c=3| [a, b, c] }.should == [1, 2, 3] + end + + it "assgins elements to post arguments" do + m([1, 2]) { |a=5, b, c, d| [a, b, c, d] }.should == [5, 1, 2, nil] + end + + it "assigns elements to required arguments when a keyword rest argument is present" do + m([1, 2]) { |a, **k| [a, k] }.should == [1, {}] + end + + it "assigns elements to mixed argument types" do + result = m([1, 2, 3, {x: 9}]) { |a, b=5, *c, d, e: 2, **k| [a, b, c, d, e, k] } + result.should == [1, 2, [], 3, 2, {x: 9}] + end + + it "assigns symbol keys from a Hash to keyword arguments" do + result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] } + result.should == [{"a" => 1}, a: 10] + end + + it "assigns symbol keys from a Hash returned by #to_hash to keyword arguments" do + obj = mock("coerce block keyword arguments") + obj.should_receive(:to_hash).and_return({"a" => 1, b: 2}) + + result = m([obj]) { |a=nil, **b| [a, b] } + result.should == [{"a" => 1}, b: 2] + end + + ruby_version_is "2.2.1" do # SEGV on MRI 2.2.0 + it "calls #to_hash on the argument but does not use the result when no keywords are present" do + obj = mock("coerce block keyword arguments") + obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2}) + + result = m([obj]) { |a=nil, **b| [a, b] } + result.should == [{"a" => 1, "b" => 2}, {}] + end + end + + it "assigns non-symbol keys to non-keyword arguments" do + result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] } + result.should == [{"a" => 10}, {b: 2}] + end + + it "does not treat hashes with string keys as keyword arguments" do + result = m(["a" => 10]) { |a = nil, **b| [a, b] } + result.should == [{"a" => 10}, {}] + end + + it "calls #to_hash on the last element if keyword arguments are present" do + obj = mock("destructure block keyword arguments") + obj.should_receive(:to_hash).and_return({x: 9}) + + result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] } + result.should == [1, [2], 3, {x: 9}] + end + + it "assigns the last element to a non-keyword argument if #to_hash returns nil" do + obj = mock("destructure block keyword arguments") + obj.should_receive(:to_hash).and_return(nil) + + result = m([1, 2, 3, obj]) { |a, *b, c, **k| [a, b, c, k] } + result.should == [1, [2, 3], obj, {}] + end + + it "calls #to_hash on the last element when there are more arguments than parameters" do + x = mock("destructure matching block keyword argument") + x.should_receive(:to_hash).and_return({x: 9}) + + result = m([1, 2, 3, {y: 9}, 4, 5, x]) { |a, b=5, c, **k| [a, b, c, k] } + result.should == [1, 2, 3, {x: 9}] + end + + it "raises a TypeError if #to_hash does not return a Hash" do + obj = mock("destructure block keyword arguments") + obj.should_receive(:to_hash).and_return(1) + + lambda { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(TypeError) + end + + it "raises the error raised inside #to_hash" do + obj = mock("destructure block keyword arguments") + error = RuntimeError.new("error while converting to a hash") + obj.should_receive(:to_hash).and_raise(error) + + lambda { m([1, 2, 3, obj]) { |a, *b, c, **k| } }.should raise_error(error) + end + + it "does not call #to_ary on the Array" do + ary = [1, 2] + ary.should_not_receive(:to_ary) + + m(ary) { |a, b, c| [a, b, c] }.should == [1, 2, nil] + end + end + + context "Object" do + it "calls #to_ary on the object when taking multiple arguments" do + obj = mock("destructure block arguments") + obj.should_receive(:to_ary).and_return([1, 2]) + + m(obj) { |a, b, c| [a, b, c] }.should == [1, 2, nil] + end + + it "does not call #to_ary when not taking any arguments" do + obj = mock("destructure block arguments") + obj.should_not_receive(:to_ary) + + m(obj) { 1 }.should == 1 + end + + it "does not call #to_ary on the object when taking a single argument" do + obj = mock("destructure block arguments") + obj.should_not_receive(:to_ary) + + m(obj) { |a| a }.should == obj + end + + it "does not call #to_ary on the object when taking a single rest argument" do + obj = mock("destructure block arguments") + obj.should_not_receive(:to_ary) + + m(obj) { |*a| a }.should == [obj] + end + + it "receives the object if #to_ary returns nil" do + obj = mock("destructure block arguments") + obj.should_receive(:to_ary).and_return(nil) + + m(obj) { |a, b, c| [a, b, c] }.should == [obj, nil, nil] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("destructure block arguments") + obj.should_receive(:to_ary).and_return(1) + + lambda { m(obj) { |a, b| } }.should raise_error(TypeError) + end + end +end + +# TODO: rewrite +describe "A block" do + before :each do + @y = BlockSpecs::Yielder.new + end + + it "captures locals from the surrounding scope" do + var = 1 + @y.z { var }.should == 1 + end + + it "allows for a leading space before the arguments" do + res = @y.s (:a){ 1 } + res.should == 1 + end + + it "allows to define a block variable with the same name as the enclosing block" do + o = BlockSpecs::OverwriteBlockVariable.new + o.z { 1 }.should == 1 + end + + it "does not capture a local when an argument has the same name" do + var = 1 + @y.s(2) { |var| var }.should == 2 + var.should == 1 + end + + it "does not capture a local when the block argument has the same name" do + var = 1 + proc { |&var| + var.call(2) + }.call { |x| x }.should == 2 + var.should == 1 + end + + describe "taking zero arguments" do + it "does not raise an exception when no values are yielded" do + @y.z { 1 }.should == 1 + end + + it "does not raise an exception when values are yielded" do + @y.s(0) { 1 }.should == 1 + end + end + + describe "taking || arguments" do + it "does not raise an exception when no values are yielded" do + @y.z { || 1 }.should == 1 + end + + it "does not raise an exception when values are yielded" do + @y.s(0) { || 1 }.should == 1 + end + end + + describe "taking |a| arguments" do + it "assigns nil to the argument when no values are yielded" do + @y.z { |a| a }.should be_nil + end + + it "assigns the value yielded to the argument" do + @y.s(1) { |a| a }.should == 1 + end + + it "does not call #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @y.s(obj) { |a| a }.should equal(obj) + end + + it "assigns the first value yielded to the argument" do + @y.m(1, 2) { |a| a }.should == 1 + end + + it "does not destructure a single Array value" do + @y.s([1, 2]) { |a| a }.should == [1, 2] + end + end + + describe "taking |a, b| arguments" do + it "assgins nil to the arguments when no values are yielded" do + @y.z { |a, b| [a, b] }.should == [nil, nil] + end + + it "assigns one value yielded to the first argument" do + @y.s(1) { |a, b| [a, b] }.should == [1, nil] + end + + it "assigns the first two values yielded to the arguments" do + @y.m(1, 2, 3) { |a, b| [a, b] }.should == [1, 2] + end + + it "does not destructure an Array value as one of several values yielded" do + @y.m([1, 2], 3, 4) { |a, b| [a, b] }.should == [[1, 2], 3] + end + + it "assigns 'nil' and 'nil' to the arguments when a single, empty Array is yielded" do + @y.s([]) { |a, b| [a, b] }.should == [nil, nil] + end + + it "assigns the element of a single element Array to the first argument" do + @y.s([1]) { |a, b| [a, b] }.should == [1, nil] + @y.s([nil]) { |a, b| [a, b] }.should == [nil, nil] + @y.s([[]]) { |a, b| [a, b] }.should == [[], nil] + end + + it "destructures a single Array value yielded" do + @y.s([1, 2, 3]) { |a, b| [a, b] }.should == [1, 2] + end + + it "destructures a splatted Array" do + @y.r([[]]) { |a, b| [a, b] }.should == [nil, nil] + @y.r([[1]]) { |a, b| [a, b] }.should == [1, nil] + end + + it "calls #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_receive(:to_ary).and_return([1, 2]) + + @y.s(obj) { |a, b| [a, b] }.should == [1, 2] + end + + it "does not call #to_ary if the single yielded object is an Array" do + obj = [1, 2] + obj.should_not_receive(:to_ary) + + @y.s(obj) { |a, b| [a, b] }.should == [1, 2] + end + + it "does not call #to_ary if the object does not respond to #to_ary" do + obj = mock("block yield no to_ary") + + @y.s(obj) { |a, b| [a, b] }.should == [obj, nil] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("block yield to_ary invalid") + obj.should_receive(:to_ary).and_return(1) + + lambda { @y.s(obj) { |a, b| } }.should raise_error(TypeError) + end + + it "raises the original exception if #to_ary raises an exception" do + obj = mock("block yield to_ary raising an exception") + obj.should_receive(:to_ary).and_raise(ZeroDivisionError) + + lambda { @y.s(obj) { |a, b| } }.should raise_error(ZeroDivisionError) + end + + end + + describe "taking |a, *b| arguments" do + it "assigns 'nil' and '[]' to the arguments when no values are yielded" do + @y.z { |a, *b| [a, b] }.should == [nil, []] + end + + it "assigns all yielded values after the first to the rest argument" do + @y.m(1, 2, 3) { |a, *b| [a, b] }.should == [1, [2, 3]] + end + + it "assigns 'nil' and '[]' to the arguments when a single, empty Array is yielded" do + @y.s([]) { |a, *b| [a, b] }.should == [nil, []] + end + + it "assigns the element of a single element Array to the first argument" do + @y.s([1]) { |a, *b| [a, b] }.should == [1, []] + @y.s([nil]) { |a, *b| [a, b] }.should == [nil, []] + @y.s([[]]) { |a, *b| [a, b] }.should == [[], []] + end + + it "destructures a splatted Array" do + @y.r([[]]) { |a, *b| [a, b] }.should == [nil, []] + @y.r([[1]]) { |a, *b| [a, b] }.should == [1, []] + end + + it "destructures a single Array value assigning the remaining values to the rest argument" do + @y.s([1, 2, 3]) { |a, *b| [a, b] }.should == [1, [2, 3]] + end + + it "calls #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_receive(:to_ary).and_return([1, 2]) + + @y.s(obj) { |a, *b| [a, b] }.should == [1, [2]] + end + + it "does not call #to_ary if the single yielded object is an Array" do + obj = [1, 2] + obj.should_not_receive(:to_ary) + + @y.s(obj) { |a, *b| [a, b] }.should == [1, [2]] + end + + it "does not call #to_ary if the object does not respond to #to_ary" do + obj = mock("block yield no to_ary") + + @y.s(obj) { |a, *b| [a, b] }.should == [obj, []] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("block yield to_ary invalid") + obj.should_receive(:to_ary).and_return(1) + + lambda { @y.s(obj) { |a, *b| } }.should raise_error(TypeError) + end + end + + describe "taking |*| arguments" do + it "does not raise an exception when no values are yielded" do + @y.z { |*| 1 }.should == 1 + end + + it "does not raise an exception when values are yielded" do + @y.s(0) { |*| 1 }.should == 1 + end + + it "does not call #to_ary if the single yielded object is an Array" do + obj = [1, 2] + obj.should_not_receive(:to_ary) + + @y.s(obj) { |*| 1 }.should == 1 + end + + it "does not call #to_ary if the object does not respond to #to_ary" do + obj = mock("block yield no to_ary") + + @y.s(obj) { |*| 1 }.should == 1 + end + + it "does not call #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @y.s(obj) { |*| 1 }.should == 1 + end + end + + describe "taking |*a| arguments" do + it "assigns '[]' to the argument when no values are yielded" do + @y.z { |*a| a }.should == [] + end + + it "assigns a single value yielded to the argument as an Array" do + @y.s(1) { |*a| a }.should == [1] + end + + it "assigns all the values passed to the argument as an Array" do + @y.m(1, 2, 3) { |*a| a }.should == [1, 2, 3] + end + + it "assigns '[[]]' to the argument when passed an empty Array" do + @y.s([]) { |*a| a }.should == [[]] + end + + it "assigns a single Array value passed to the argument by wrapping it in an Array" do + @y.s([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]] + end + + it "does not call #to_ary if the single yielded object is an Array" do + obj = [1, 2] + obj.should_not_receive(:to_ary) + + @y.s(obj) { |*a| a }.should == [[1, 2]] + end + + it "does not call #to_ary if the object does not respond to #to_ary" do + obj = mock("block yield no to_ary") + + @y.s(obj) { |*a| a }.should == [obj] + end + + it "does not call #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @y.s(obj) { |*a| a }.should == [obj] + end + end + + describe "taking |a, | arguments" do + it "assigns nil to the argument when no values are yielded" do + @y.z { |a, | a }.should be_nil + end + + it "assgins the argument a single value yielded" do + @y.s(1) { |a, | a }.should == 1 + end + + it "assigns the argument the first value yielded" do + @y.m(1, 2) { |a, | a }.should == 1 + end + + it "assigns the argument the first of several values yielded when it is an Array" do + @y.m([1, 2], 3) { |a, | a }.should == [1, 2] + end + + it "assigns nil to the argument when passed an empty Array" do + @y.s([]) { |a, | a }.should be_nil + end + + it "assigns the argument the first element of the Array when passed a single Array" do + @y.s([1, 2]) { |a, | a }.should == 1 + end + + it "calls #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_receive(:to_ary).and_return([1, 2]) + + @y.s(obj) { |a, | a }.should == 1 + end + + it "does not call #to_ary if the single yielded object is an Array" do + obj = [1, 2] + obj.should_not_receive(:to_ary) + + @y.s(obj) { |a, | a }.should == 1 + end + + it "does not call #to_ary if the object does not respond to #to_ary" do + obj = mock("block yield no to_ary") + + @y.s(obj) { |a, | a }.should == obj + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("block yield to_ary invalid") + obj.should_receive(:to_ary).and_return(1) + + lambda { @y.s(obj) { |a, | } }.should raise_error(TypeError) + end + end + + describe "taking |(a, b)| arguments" do + it "assigns nil to the arguments when yielded no values" do + @y.z { |(a, b)| [a, b] }.should == [nil, nil] + end + + it "destructures a single Array value yielded" do + @y.s([1, 2]) { |(a, b)| [a, b] }.should == [1, 2] + end + + it "destructures a single Array value yielded when shadowing an outer variable" do + a = 9 + @y.s([1, 2]) { |(a, b)| [a, b] }.should == [1, 2] + end + + it "calls #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_receive(:to_ary).and_return([1, 2]) + + @y.s(obj) { |(a, b)| [a, b] }.should == [1, 2] + end + + it "does not call #to_ary if the single yielded object is an Array" do + obj = [1, 2] + obj.should_not_receive(:to_ary) + + @y.s(obj) { |(a, b)| [a, b] }.should == [1, 2] + end + + it "does not call #to_ary if the object does not respond to #to_ary" do + obj = mock("block yield no to_ary") + + @y.s(obj) { |(a, b)| [a, b] }.should == [obj, nil] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("block yield to_ary invalid") + obj.should_receive(:to_ary).and_return(1) + + lambda { @y.s(obj) { |(a, b)| } }.should raise_error(TypeError) + end + end + + describe "taking |(a, b), c| arguments" do + it "assigns nil to the arguments when yielded no values" do + @y.z { |(a, b), c| [a, b, c] }.should == [nil, nil, nil] + end + + it "destructures a single one-level Array value yielded" do + @y.s([1, 2]) { |(a, b), c| [a, b, c] }.should == [1, nil, 2] + end + + it "destructures a single multi-level Array value yielded" do + @y.s([[1, 2, 3], 4]) { |(a, b), c| [a, b, c] }.should == [1, 2, 4] + end + + it "calls #to_ary to convert a single yielded object to an Array" do + obj = mock("block yield to_ary") + obj.should_receive(:to_ary).and_return([1, 2]) + + @y.s(obj) { |(a, b), c| [a, b, c] }.should == [1, nil, 2] + end + + it "does not call #to_ary if the single yielded object is an Array" do + obj = [1, 2] + obj.should_not_receive(:to_ary) + + @y.s(obj) { |(a, b), c| [a, b, c] }.should == [1, nil, 2] + end + + it "does not call #to_ary if the object does not respond to #to_ary" do + obj = mock("block yield no to_ary") + + @y.s(obj) { |(a, b), c| [a, b, c] }.should == [obj, nil, nil] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("block yield to_ary invalid") + obj.should_receive(:to_ary).and_return(1) + + lambda { @y.s(obj) { |(a, b), c| } }.should raise_error(TypeError) + end + end + + describe "taking nested |a, (b, (c, d))|" do + it "assigns nil to the arguments when yielded no values" do + @y.m { |a, (b, (c, d))| [a, b, c, d] }.should == [nil, nil, nil, nil] + end + + it "destructures separate yielded values" do + @y.m(1, 2) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, nil, nil] + end + + it "destructures a single multi-level Array value yielded" do + @y.m(1, [2, 3]) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, 3, nil] + end + + it "destructures a single multi-level Array value yielded" do + @y.m(1, [2, [3, 4]]) { |a, (b, (c, d))| [a, b, c, d] }.should == [1, 2, 3, 4] + end + end + + describe "taking nested |a, ((b, c), d)|" do + it "assigns nil to the arguments when yielded no values" do + @y.m { |a, ((b, c), d)| [a, b, c, d] }.should == [nil, nil, nil, nil] + end + + it "destructures separate yielded values" do + @y.m(1, 2) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, nil, nil] + end + + it "destructures a single multi-level Array value yielded" do + @y.m(1, [2, 3]) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, nil, 3] + end + + it "destructures a single multi-level Array value yielded" do + @y.m(1, [[2, 3], 4]) { |a, ((b, c), d)| [a, b, c, d] }.should == [1, 2, 3, 4] + end + end + + describe "arguments with _" do + it "extracts arguments with _" do + @y.m([[1, 2, 3], 4]) { |(_, a, _), _| a }.should == 2 + @y.m([1, [2, 3, 4]]) { |_, (_, a, _)| a }.should == 3 + end + + it "assigns the first variable named" do + @y.m(1, 2) { |_, _| _ }.should == 1 + end + end + + describe "taking identically-named arguments" do + it "raises a SyntaxError for standard arguments" do + lambda { eval "lambda { |x,x| }" }.should raise_error(SyntaxError) + lambda { eval "->(x,x) {}" }.should raise_error(SyntaxError) + lambda { eval "Proc.new { |x,x| }" }.should raise_error(SyntaxError) + end + + it "accepts unnamed arguments" do + eval("lambda { |_,_| }").should be_an_instance_of(Proc) + eval("->(_,_) {}").should be_an_instance_of(Proc) + eval("Proc.new { |_,_| }").should be_an_instance_of(Proc) + end + end +end + +describe "Block-local variables" do + it "are introduced with a semi-colon in the parameter list" do + [1].map {|one; bl| bl }.should == [nil] + end + + it "can be specified in a comma-separated list after the semi-colon" do + [1].map {|one; bl, bl2| [bl, bl2] }.should == [[nil, nil]] + end + + it "can not have the same name as one of the standard parameters" do + lambda { eval "[1].each {|foo; foo| }" }.should raise_error(SyntaxError) + lambda { eval "[1].each {|foo, bar; glark, bar| }" }.should raise_error(SyntaxError) + end + + it "can not be prefixed with an asterisk" do + lambda { eval "[1].each {|foo; *bar| }" }.should raise_error(SyntaxError) + lambda do + eval "[1].each {|foo, bar; glark, *fnord| }" + end.should raise_error(SyntaxError) + end + + it "can not be prefixed with an ampersand" do + lambda { eval "[1].each {|foo; &bar| }" }.should raise_error(SyntaxError) + lambda do + eval "[1].each {|foo, bar; glark, &fnord| }" + end.should raise_error(SyntaxError) + end + + it "can not be assigned default values" do + lambda { eval "[1].each {|foo; bar=1| }" }.should raise_error(SyntaxError) + lambda do + eval "[1].each {|foo, bar; glark, fnord=:fnord| }" + end.should raise_error(SyntaxError) + end + + it "need not be preceeded by standard parameters" do + [1].map {|; foo| foo }.should == [nil] + [1].map {|; glark, bar| [glark, bar] }.should == [[nil, nil]] + end + + it "only allow a single semi-colon in the parameter list" do + lambda { eval "[1].each {|foo; bar; glark| }" }.should raise_error(SyntaxError) + lambda { eval "[1].each {|; bar; glark| }" }.should raise_error(SyntaxError) + end + + it "override shadowed variables from the outer scope" do + out = :out + [1].each {|; out| out = :in } + out.should == :out + + a = :a + b = :b + c = :c + d = :d + {ant: :bee}.each_pair do |a, b; c, d| + a = :A + b = :B + c = :C + d = :D + end + a.should == :a + b.should == :b + c.should == :c + d.should == :d + end + + it "are not automatically instantiated in the outer scope" do + defined?(glark).should be_nil + [1].each {|;glark| 1} + defined?(glark).should be_nil + end + + it "are automatically instantiated in the block" do + [1].each do |;glark| + glark.should be_nil + end + end + + it "are visible in deeper scopes before initialization" do + [1].each {|;glark| + [1].each { + defined?(glark).should_not be_nil + glark = 1 + } + glark.should == 1 + } + end +end + +describe "Post-args" do + it "appear after a splat" do + proc do |*a, b| + [a, b] + end.call(1, 2, 3).should == [[1, 2], 3] + + proc do |*a, b, c| + [a, b, c] + end.call(1, 2, 3).should == [[1], 2, 3] + + proc do |*a, b, c, d| + [a, b, c, d] + end.call(1, 2, 3).should == [[], 1, 2, 3] + end + + it "are required" do + lambda { + lambda do |*a, b| + [a, b] + end.call + }.should raise_error(ArgumentError) + end + + describe "with required args" do + + it "gathers remaining args in the splat" do + proc do |a, *b, c| + [a, b, c] + end.call(1, 2, 3).should == [1, [2], 3] + end + + it "has an empty splat when there are no remaining args" do + proc do |a, b, *c, d| + [a, b, c, d] + end.call(1, 2, 3).should == [1, 2, [], 3] + + proc do |a, *b, c, d| + [a, b, c, d] + end.call(1, 2, 3).should == [1, [], 2, 3] + end + end + + describe "with optional args" do + + it "gathers remaining args in the splat" do + proc do |a=5, *b, c| + [a, b, c] + end.call(1, 2, 3).should == [1, [2], 3] + end + + it "overrides the optional arg before gathering in the splat" do + proc do |a=5, *b, c| + [a, b, c] + end.call(2, 3).should == [2, [], 3] + + proc do |a=5, b=6, *c, d| + [a, b, c, d] + end.call(1, 2, 3).should == [1, 2, [], 3] + + proc do |a=5, *b, c, d| + [a, b, c, d] + end.call(1, 2, 3).should == [1, [], 2, 3] + end + + it "uses the required arg before the optional and the splat" do + proc do |a=5, *b, c| + [a, b, c] + end.call(3).should == [5, [], 3] + + proc do |a=5, b=6, *c, d| + [a, b, c, d] + end.call(3).should == [5, 6, [], 3] + + proc do |a=5, *b, c, d| + [a, b, c, d] + end.call(2, 3).should == [5, [], 2, 3] + end + + it "overrides the optional args from left to right before gathering the splat" do + proc do |a=5, b=6, *c, d| + [a, b, c, d] + end.call(2, 3).should == [2, 6, [], 3] + end + + describe "with a circular argument reference" do + it "shadows an existing local with the same name as the argument" do + a = 1 + -> { + @proc = eval "proc { |a=a| a }" + }.should complain(/circular argument reference/) + @proc.call.should == nil + end + + it "shadows an existing method with the same name as the argument" do + def a; 1; end + -> { + @proc = eval "proc { |a=a| a }" + }.should complain(/circular argument reference/) + @proc.call.should == nil + end + + it "calls an existing method with the same name as the argument if explicitly using ()" do + def a; 1; end + proc { |a=a()| a }.call.should == 1 + end + end + end + + describe "with pattern matching" do + it "extracts matched blocks with post arguments" do + proc do |(a, *b, c), d, e| + [a, b, c, d, e] + end.call([1, 2, 3, 4], 5, 6).should == [1, [2, 3], 4, 5, 6] + end + + it "allows empty splats" do + proc do |a, (*), b| + [a, b] + end.call([1, 2, 3]).should == [1, 3] + end + end +end diff --git a/spec/rubyspec/language/break_spec.rb b/spec/rubyspec/language/break_spec.rb new file mode 100644 index 0000000000..4758e6ce50 --- /dev/null +++ b/spec/rubyspec/language/break_spec.rb @@ -0,0 +1,334 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/break', __FILE__) + +describe "The break statement in a block" do + before :each do + ScratchPad.record [] + @program = BreakSpecs::Block.new + end + + it "returns nil to method invoking the method yielding to the block when not passed an argument" do + @program.break_nil + ScratchPad.recorded.should == [:a, :aa, :b, nil, :d] + end + + it "returns a value to the method invoking the method yielding to the block" do + @program.break_value + ScratchPad.recorded.should == [:a, :aa, :b, :break, :d] + end + + describe "yielded inside a while" do + it "breaks out of the block" do + value = @program.break_in_block_in_while + ScratchPad.recorded.should == [:aa, :break] + value.should == :value + end + end +end + +describe "The break statement in a captured block" do + before :each do + ScratchPad.record [] + @program = BreakSpecs::Block.new + end + + describe "when the invocation of the scope creating the block is still active" do + it "raises a LocalJumpError when invoking the block from the scope creating the block" do + lambda { @program.break_in_method }.should raise_error(LocalJumpError) + ScratchPad.recorded.should == [:a, :xa, :d, :b] + end + + it "raises a LocalJumpError when invoking the block from a method" do + lambda { @program.break_in_nested_method }.should raise_error(LocalJumpError) + ScratchPad.recorded.should == [:a, :xa, :c, :aa, :b] + end + + it "raises a LocalJumpError when yielding to the block" do + lambda { @program.break_in_yielding_method }.should raise_error(LocalJumpError) + ScratchPad.recorded.should == [:a, :xa, :c, :aa, :b] + end + end + + describe "from a scope that has returned" do + it "raises a LocalJumpError when calling the block from a method" do + lambda { @program.break_in_method_captured }.should raise_error(LocalJumpError) + ScratchPad.recorded.should == [:a, :za, :xa, :zd, :zb] + end + + it "raises a LocalJumpError when yielding to the block" do + lambda { @program.break_in_yield_captured }.should raise_error(LocalJumpError) + ScratchPad.recorded.should == [:a, :za, :xa, :zd, :aa, :zb] + end + end +end + +describe "The break statement in a lambda" do + before :each do + ScratchPad.record [] + @program = BreakSpecs::Lambda.new + end + + it "returns from the lambda" do + l = lambda { + ScratchPad << :before + break :foo + ScratchPad << :after + } + l.call.should == :foo + ScratchPad.recorded.should == [:before] + end + + it "returns from the call site if the lambda is passed as a block" do + def mid(&b) + lambda { + ScratchPad << :before + b.call + ScratchPad << :unreachable1 + }.call + ScratchPad << :unreachable2 + end + + result = [1].each do |e| + mid { + break # This breaks from mid + ScratchPad << :unreachable3 + } + ScratchPad << :after + end + result.should == [1] + ScratchPad.recorded.should == [:before, :after] + end + + describe "when the invocation of the scope creating the lambda is still active" do + it "returns nil when not passed an argument" do + @program.break_in_defining_scope false + ScratchPad.recorded.should == [:a, :b, nil, :d] + end + + it "returns a value to the scope creating and calling the lambda" do + @program.break_in_defining_scope + ScratchPad.recorded.should == [:a, :b, :break, :d] + end + + it "returns a value to the method scope below invoking the lambda" do + @program.break_in_nested_scope + ScratchPad.recorded.should == [:a, :d, :aa, :b, :break, :bb, :e] + end + + it "returns a value to a block scope invoking the lambda in a method below" do + @program.break_in_nested_scope_block + ScratchPad.recorded.should == [:a, :d, :aa, :aaa, :bb, :b, :break, :cc, :bbb, :dd, :e] + end + + it "returns from the lambda" do + @program.break_in_nested_scope_yield + ScratchPad.recorded.should == [:a, :d, :aaa, :b, :bbb, :e] + end + end + + describe "created at the toplevel" do + it "returns a value when invoking from the toplevel" do + code = fixture __FILE__, "break_lambda_toplevel.rb" + ruby_exe(code).chomp.should == "a,b,break,d" + end + + it "returns a value when invoking from a method" do + code = fixture __FILE__, "break_lambda_toplevel_method.rb" + ruby_exe(code).chomp.should == "a,d,b,break,e,f" + end + + it "returns a value when invoking from a block" do + code = fixture __FILE__, "break_lambda_toplevel_block.rb" + ruby_exe(code).chomp.should == "a,d,f,b,break,g,e,h" + end + end + + describe "from a scope that has returned" do + it "returns a value to the method scope invoking the lambda" do + @program.break_in_method + ScratchPad.recorded.should == [:a, :la, :ld, :lb, :break, :b] + end + + it "returns a value to the block scope invoking the lambda in a method" do + @program.break_in_block_in_method + ScratchPad.recorded.should == [:a, :aaa, :b, :la, :ld, :lb, :break, :c, :bbb, :d] + end + + # By passing a lambda as a block argument, the user is requesting to treat + # the lambda as a block, which in this case means breaking to a scope that + # has returned. This is a subtle and confusing semantic where a block pass + # is removing the lambda-ness of a lambda. + it "raises a LocalJumpError when yielding to a lambda passed as a block argument" do + @program.break_in_method_yield + ScratchPad.recorded.should == [:a, :la, :ld, :aaa, :lb, :bbb, :b] + end + end +end + +describe "Break inside a while loop" do + describe "with a value" do + it "exits the loop and returns the value" do + a = while true; break; end; a.should == nil + a = while true; break nil; end; a.should == nil + a = while true; break 1; end; a.should == 1 + a = while true; break []; end; a.should == [] + a = while true; break [1]; end; a.should == [1] + end + + it "passes the value returned by a method with omitted parenthesis and passed block" do + obj = BreakSpecs::Block.new + lambda { break obj.method :value do |x| x end }.call.should == :value + end + end + + describe "with a splat" do + it "exits the loop and makes the splat an Array" do + a = while true; break *[1,2]; end; a.should == [1,2] + end + + it "treats nil as an empty array" do + a = while true; break *nil; end; a.should == [] + end + + it "preserves an array as is" do + a = while true; break *[]; end; a.should == [] + a = while true; break *[1,2]; end; a.should == [1,2] + a = while true; break *[nil]; end; a.should == [nil] + a = while true; break *[[]]; end; a.should == [[]] + end + + it "wraps a non-Array in an Array" do + a = while true; break *1; end; a.should == [1] + end + end + + it "stops a while loop when run" do + i = 0 + while true + break if i == 2 + i+=1 + end + i.should == 2 + end + + it "causes a call with a block to return when run" do + at = 0 + 0.upto(5) do |i| + at = i + break i if i == 2 + end.should == 2 + at.should == 2 + end +end + + +# TODO: Rewrite all the specs from here to the end of the file in the style +# above. +describe "Executing break from within a block" do + + before :each do + ScratchPad.clear + end + + # Discovered in JRuby (see JRUBY-2756) + it "returns from the original invoking method even in case of chained calls" do + class BreakTest + # case #1: yield + def self.meth_with_yield(&b) + yield + fail("break returned from yield to wrong place") + end + def self.invoking_method(&b) + meth_with_yield(&b) + fail("break returned from 'meth_with_yield' method to wrong place") + end + + # case #2: block.call + def self.meth_with_block_call(&b) + b.call + fail("break returned from b.call to wrong place") + end + def self.invoking_method2(&b) + meth_with_block_call(&b) + fail("break returned from 'meth_with_block_call' method to wrong place") + end + end + + # this calls a method that calls another method that yields to the block + BreakTest.invoking_method do + break + fail("break didn't, well, break") + end + + # this calls a method that calls another method that calls the block + BreakTest.invoking_method2 do + break + fail("break didn't, well, break") + end + + res = BreakTest.invoking_method do + break :return_value + fail("break didn't, well, break") + end + res.should == :return_value + + res = BreakTest.invoking_method2 do + break :return_value + fail("break didn't, well, break") + end + res.should == :return_value + + end + + class BreakTest2 + def one + two { yield } + end + + def two + yield + ensure + ScratchPad << :two_ensure + end + + def three + begin + one { break } + ScratchPad << :three_post + ensure + ScratchPad << :three_ensure + end + end + end + + it "runs ensures when continuing upward" do + ScratchPad.record [] + + bt2 = BreakTest2.new + bt2.one { break } + ScratchPad.recorded.should == [:two_ensure] + end + + it "runs ensures when breaking from a loop" do + ScratchPad.record [] + + while true + begin + ScratchPad << :begin + break if true + ensure + ScratchPad << :ensure + end + end + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "doesn't run ensures in the destination method" do + ScratchPad.record [] + + bt2 = BreakTest2.new + bt2.three + ScratchPad.recorded.should == [:two_ensure, :three_post, :three_ensure] + end +end diff --git a/spec/rubyspec/language/case_spec.rb b/spec/rubyspec/language/case_spec.rb new file mode 100644 index 0000000000..cb0e8150bf --- /dev/null +++ b/spec/rubyspec/language/case_spec.rb @@ -0,0 +1,382 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The 'case'-construct" do + it "evaluates the body of the when clause matching the case target expression" do + case 1 + when 2; false + when 1; true + end.should == true + end + + it "evaluates the body of the when clause whose array expression includes the case target expression" do + case 2 + when 3, 4; false + when 1, 2; true + end.should == true + end + + it "evaluates the body of the when clause in left-to-right order if it's an array expression" do + @calls = [] + def foo; @calls << :foo; end + def bar; @calls << :bar; end + + case true + when foo, bar; + end + + @calls.should == [:foo, :bar] + end + + it "evaluates the body of the when clause whose range expression includes the case target expression" do + case 5 + when 21..30; false + when 1..20; true + end.should == true + end + + it "returns nil when no 'then'-bodies are given" do + case "a" + when "a" + when "b" + end.should == nil + end + + it "evaluates the 'else'-body when no other expression matches" do + case "c" + when "a"; 'foo' + when "b"; 'bar' + else 'zzz' + end.should == 'zzz' + end + + it "returns nil when no expression matches and 'else'-body is empty" do + case "c" + when "a"; "a" + when "b"; "b" + else + end.should == nil + end + + it "returns 2 when a then body is empty" do + case Object.new + when Numeric then + 1 + when String then + # ok + else + 2 + end.should == 2 + end + + it "returns the statement following 'then'" do + case "a" + when "a" then 'foo' + when "b" then 'bar' + end.should == 'foo' + end + + it "tests classes with case equality" do + case "a" + when String + 'foo' + when Symbol + 'bar' + end.should == 'foo' + end + + it "tests with matching regexps" do + case "hello" + when /abc/; false + when /^hell/; true + end.should == true + end + + it "does not test with equality when given classes" do + case :symbol.class + when Symbol + "bar" + when String + "bar" + else + "foo" + end.should == "foo" + end + + it "takes lists of values" do + case 'z' + when 'a', 'b', 'c', 'd' + "foo" + when 'x', 'y', 'z' + "bar" + end.should == "bar" + + case 'b' + when 'a', 'b', 'c', 'd' + "foo" + when 'x', 'y', 'z' + "bar" + end.should == "foo" + end + + it "expands arrays to lists of values" do + case 'z' + when *['a', 'b', 'c', 'd'] + "foo" + when *['x', 'y', 'z'] + "bar" + end.should == "bar" + end + + it "takes an expanded array in addition to a list of values" do + case 'f' + when 'f', *['a', 'b', 'c', 'd'] + "foo" + when *['x', 'y', 'z'] + "bar" + end.should == "foo" + + case 'b' + when 'f', *['a', 'b', 'c', 'd'] + "foo" + when *['x', 'y', 'z'] + "bar" + end.should == "foo" + end + + it "takes an expanded array before additional listed values" do + case 'f' + when *['a', 'b', 'c', 'd'], 'f' + "foo" + when *['x', 'y', 'z'] + "bar" + end.should == 'foo' + end + + it "expands arrays from variables before additional listed values" do + a = ['a', 'b', 'c'] + case 'a' + when *a, 'd', 'e' + "foo" + when 'x' + "bar" + end.should == "foo" + end + + it "expands arrays from variables before a single additional listed value" do + a = ['a', 'b', 'c'] + case 'a' + when *a, 'd' + "foo" + when 'x' + "bar" + end.should == "foo" + end + + it "expands multiple arrays from variables before additional listed values" do + a = ['a', 'b', 'c'] + b = ['d', 'e', 'f'] + + case 'f' + when *a, *b, 'g', 'h' + "foo" + when 'x' + "bar" + end.should == "foo" + end + + # MR: critical + it "concats arrays before expanding them" do + a = ['a', 'b', 'c', 'd'] + b = ['f'] + + case 'f' + when 'f', *a|b + "foo" + when *['x', 'y', 'z'] + "bar" + end.should == "foo" + end + + it "never matches when clauses with no values" do + case nil + when *[] + "foo" + end.should == nil + end + + it "lets you define a method after the case statement" do + case (def foo; 'foo'; end; 'f') + when 'a' + 'foo' + when 'f' + 'bar' + end.should == 'bar' + end + + it "raises a SyntaxError when 'else' is used when no 'when' is given" do + lambda { + eval <<-CODE + case 4 + else + true + end + CODE + }.should raise_error(SyntaxError) + end + + it "raises a SyntaxError when 'else' is used before a 'when' was given" do + lambda { + eval <<-CODE + case 4 + else + true + when 4; false + end + CODE + }.should raise_error(SyntaxError) + end + + it "supports nested case statements" do + result = false + case :x + when Symbol + case :y + when Symbol + result = true + end + end + result.should == true + end + + it "supports nested case statements followed by a when with a splatted array" do + result = false + case :x + when Symbol + case :y + when Symbol + result = true + end + when *[Symbol] + result = false + end + result.should == true + end + + it "supports nested case statements followed by a when with a splatted non-array" do + result = false + case :x + when Symbol + case :y + when Symbol + result = true + end + when *Symbol + result = false + end + result.should == true + end + + it "works even if there's only one when statement" do + case 1 + when 1 + 100 + end.should == 100 + end +end + +describe "The 'case'-construct with no target expression" do + it "evaluates the body of the first clause when at least one of its condition expressions is true" do + case + when true, false; 'foo' + end.should == 'foo' + end + + it "evaluates the body of the first when clause that is not false/nil" do + case + when false; 'foo' + when 2; 'bar' + when 1 == 1; 'baz' + end.should == 'bar' + + case + when false; 'foo' + when nil; 'foo' + when 1 == 1; 'bar' + end.should == 'bar' + end + + it "evaluates the body of the else clause if all when clauses are false/nil" do + case + when false; 'foo' + when nil; 'foo' + when 1 == 2; 'bar' + else 'baz' + end.should == 'baz' + end + + it "evaluates multiple conditional expressions as a boolean disjunction" do + case + when true, false; 'foo' + else 'bar' + end.should == 'foo' + + case + when false, true; 'foo' + else 'bar' + end.should == 'foo' + end + + it "evaluates true as only 'true' when true is the first clause" do + case 1 + when true; "bad" + when Integer; "good" + end.should == "good" + end + + it "evaluates false as only 'false' when false is the first clause" do + case nil + when false; "bad" + when nil; "good" + end.should == "good" + end + + it "treats a literal array as its own when argument, rather than a list of arguments" do + case 'foo' + when ['foo', 'foo']; 'bad' + when 'foo'; 'good' + end.should == 'good' + end + + it "takes multiple expanded arrays" do + a1 = ['f', 'o', 'o'] + a2 = ['b', 'a', 'r'] + + case 'f' + when *a1, *['x', 'y', 'z'] + "foo" + when *a2, *['x', 'y', 'z'] + "bar" + end.should == "foo" + + case 'b' + when *a1, *['x', 'y', 'z'] + "foo" + when *a2, *['x', 'y', 'z'] + "bar" + end.should == "bar" + end + + it "calls === even when private" do + klass = Class.new do + def ===(o) + true + end + private :=== + end + + case 1 + when klass.new + :called + end.should == :called + end +end diff --git a/spec/rubyspec/language/class_spec.rb b/spec/rubyspec/language/class_spec.rb new file mode 100644 index 0000000000..354570e5e9 --- /dev/null +++ b/spec/rubyspec/language/class_spec.rb @@ -0,0 +1,332 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/class', __FILE__) + +ClassSpecsNumber = 12 + +module ClassSpecs + Number = 12 +end + +describe "The class keyword" do + it "creates a new class with semicolon" do + class ClassSpecsKeywordWithSemicolon; end + ClassSpecsKeywordWithSemicolon.should be_an_instance_of(Class) + end + + ruby_version_is "2.3" do + it "does not raise a SyntaxError when opening a class without a semicolon" do + eval "class ClassSpecsKeywordWithoutSemicolon end" + ClassSpecsKeywordWithoutSemicolon.should be_an_instance_of(Class) + end + end +end + +describe "A class definition" do + it "creates a new class" do + ClassSpecs::A.should be_kind_of(Class) + ClassSpecs::A.new.should be_kind_of(ClassSpecs::A) + end + + it "has no class variables" do + ClassSpecs::A.class_variables.should == [] + end + + it "raises TypeError if constant given as class name exists and is not a Module" do + lambda { + class ClassSpecsNumber + end + }.should raise_error(TypeError) + end + + # test case known to be detecting bugs (JRuby, MRI) + it "raises TypeError if the constant qualifying the class is nil" do + lambda { + class nil::Foo + end + }.should raise_error(TypeError) + end + + it "raises TypeError if any constant qualifying the class is not a Module" do + lambda { + class ClassSpecs::Number::MyClass + end + }.should raise_error(TypeError) + + lambda { + class ClassSpecsNumber::MyClass + end + }.should raise_error(TypeError) + end + + it "inherits from Object by default" do + ClassSpecs::A.superclass.should == Object + end + + it "raises an error when trying to change the superclass" do + module ClassSpecs + class SuperclassResetToSubclass < L + end + lambda { + class SuperclassResetToSubclass < M + end + }.should raise_error(TypeError, /superclass mismatch/) + end + end + + it "raises an error when reopening a class with BasicObject as superclass" do + module ClassSpecs + class SuperclassReopenedBasicObject < A + end + SuperclassReopenedBasicObject.superclass.should == A + + lambda { + class SuperclassReopenedBasicObject < BasicObject + end + }.should raise_error(TypeError, /superclass mismatch/) + SuperclassReopenedBasicObject.superclass.should == A + end + end + + # [Bug #12367] [ruby-core:75446] + ruby_version_is "2.4" do # Until backported + it "raises an error when reopening a class with Object as superclass" do + module ClassSpecs + class SuperclassReopenedObject < A + end + SuperclassReopenedObject.superclass.should == A + + lambda { + class SuperclassReopenedObject < Object + end + }.should raise_error(TypeError, /superclass mismatch/) + SuperclassReopenedObject.superclass.should == A + end + end + end + + it "allows reopening a class without specifying the superclass" do + module ClassSpecs + class SuperclassNotGiven < A + end + SuperclassNotGiven.superclass.should == A + + class SuperclassNotGiven + end + SuperclassNotGiven.superclass.should == A + end + end + + it "does not allow to set the superclass even if it was not specified by the first declaration" do + module ClassSpecs + class NoSuperclassSet + end + + lambda { + class NoSuperclassSet < String + end + }.should raise_error(TypeError, /superclass mismatch/) + end + end + + it "allows using self as the superclass if self is a class" do + ClassSpecs::I::J.superclass.should == ClassSpecs::I + + lambda { + class ShouldNotWork < self; end + }.should raise_error(TypeError) + end + + it "first evaluates the superclass before checking if the class already exists" do + module ClassSpecs + class SuperclassEvaluatedFirst + end + a = SuperclassEvaluatedFirst + + class SuperclassEvaluatedFirst < remove_const(:SuperclassEvaluatedFirst) + end + b = SuperclassEvaluatedFirst + b.superclass.should == a + end + end + + it "raises a TypeError if inheriting from a metaclass" do + obj = mock("metaclass super") + meta = obj.singleton_class + lambda { class ClassSpecs::MetaclassSuper < meta; end }.should raise_error(TypeError) + end + + it "allows the declaration of class variables in the body" do + ClassSpecs.string_class_variables(ClassSpecs::B).should == ["@@cvar"] + ClassSpecs::B.send(:class_variable_get, :@@cvar).should == :cvar + end + + it "stores instance variables defined in the class body in the class object" do + ClassSpecs.string_instance_variables(ClassSpecs::B).should include("@ivar") + ClassSpecs::B.instance_variable_get(:@ivar).should == :ivar + end + + it "allows the declaration of class variables in a class method" do + ClassSpecs::C.class_variables.should == [] + ClassSpecs::C.make_class_variable + ClassSpecs.string_class_variables(ClassSpecs::C).should == ["@@cvar"] + ClassSpecs::C.remove_class_variable :@@cvar + end + + it "allows the definition of class-level instance variables in a class method" do + ClassSpecs.string_instance_variables(ClassSpecs::C).should_not include("@civ") + ClassSpecs::C.make_class_instance_variable + ClassSpecs.string_instance_variables(ClassSpecs::C).should include("@civ") + ClassSpecs::C.remove_instance_variable :@civ + end + + it "allows the declaration of class variables in an instance method" do + ClassSpecs::D.class_variables.should == [] + ClassSpecs::D.new.make_class_variable + ClassSpecs.string_class_variables(ClassSpecs::D).should == ["@@cvar"] + ClassSpecs::D.remove_class_variable :@@cvar + end + + it "allows the definition of instance methods" do + ClassSpecs::E.new.meth.should == :meth + end + + it "allows the definition of class methods" do + ClassSpecs::E.cmeth.should == :cmeth + end + + it "allows the definition of class methods using class << self" do + ClassSpecs::E.smeth.should == :smeth + end + + it "allows the definition of Constants" do + Object.const_defined?('CONSTANT').should == false + ClassSpecs::E.const_defined?('CONSTANT').should == true + ClassSpecs::E::CONSTANT.should == :constant! + end + + it "returns the value of the last statement in the body" do + class ClassSpecs::Empty; end.should == nil + class ClassSpecs::Twenty; 20; end.should == 20 + class ClassSpecs::Plus; 10 + 20; end.should == 30 + class ClassSpecs::Singleton; class << self; :singleton; end; end.should == :singleton + end + + describe "within a block creates a new class in the lexical scope" do + it "for named classes at the toplevel" do + klass = Class.new do + class Howdy + end + + def self.get_class_name + Howdy.name + end + end + + Howdy.name.should == 'Howdy' + klass.get_class_name.should == 'Howdy' + end + + it "for named classes in a module" do + klass = ClassSpecs::ANON_CLASS_FOR_NEW.call + + ClassSpecs::NamedInModule.name.should == 'ClassSpecs::NamedInModule' + klass.get_class_name.should == 'ClassSpecs::NamedInModule' + end + + it "for anonymous classes" do + klass = Class.new do + def self.get_class + Class.new do + def self.foo + 'bar' + end + end + end + + def self.get_result + get_class.foo + end + end + + klass.get_result.should == 'bar' + end + + it "for anonymous classes assigned to a constant" do + klass = Class.new do + AnonWithConstant = Class.new + + def self.get_class_name + AnonWithConstant.name + end + end + + AnonWithConstant.name.should == 'AnonWithConstant' + klass.get_class_name.should == 'AnonWithConstant' + end + end +end + +describe "An outer class definition" do + it "contains the inner classes" do + ClassSpecs::Container.constants.should include(:A, :B) + end +end + +describe "A class definition extending an object (sclass)" do + it "allows adding methods" do + ClassSpecs::O.smeth.should == :smeth + end + + it "raises a TypeError when trying to extend numbers" do + lambda { + eval <<-CODE + class << 1 + def xyz + self + end + end + CODE + }.should raise_error(TypeError) + end + + it "allows accessing the block of the original scope" do + ClassSpecs.sclass_with_block { 123 }.should == 123 + end + + it "can use return to cause the enclosing method to return" do + ClassSpecs.sclass_with_return.should == :inner + end +end + +describe "Reopening a class" do + it "extends the previous definitions" do + c = ClassSpecs::F.new + c.meth.should == :meth + c.another.should == :another + end + + it "overwrites existing methods" do + ClassSpecs::G.new.override.should == :override + end + + it "raises a TypeError when superclasses mismatch" do + lambda { class ClassSpecs::A < Array; end }.should raise_error(TypeError) + end + + it "adds new methods to subclasses" do + lambda { ClassSpecs::M.m }.should raise_error(NoMethodError) + class ClassSpecs::L + def self.m + 1 + end + end + ClassSpecs::M.m.should == 1 + ClassSpecs::L.singleton_class.send(:remove_method, :m) + end +end + +describe "class provides hooks" do + it "calls inherited when a class is created" do + ClassSpecs::H.track_inherited.should == [ClassSpecs::K] + end +end diff --git a/spec/rubyspec/language/class_variable_spec.rb b/spec/rubyspec/language/class_variable_spec.rb new file mode 100644 index 0000000000..463f731a93 --- /dev/null +++ b/spec/rubyspec/language/class_variable_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/class_variables', __FILE__) + +describe "A class variable" do + after :each do + ClassVariablesSpec::ClassA.new.cvar_a = :cvar_a + end + + it "can be accessed from a subclass" do + ClassVariablesSpec::ClassB.new.cvar_a.should == :cvar_a + end + + it "is set in the superclass" do + a = ClassVariablesSpec::ClassA.new + b = ClassVariablesSpec::ClassB.new + b.cvar_a = :new_val + + a.cvar_a.should == :new_val + end +end + +describe "A class variable defined in a module" do + after :each do + ClassVariablesSpec::ClassC.cvar_m = :value + ClassVariablesSpec::ClassC.remove_class_variable(:@@cvar) if ClassVariablesSpec::ClassC.cvar_defined? + end + + it "can be accessed from classes that extend the module" do + ClassVariablesSpec::ClassC.cvar_m.should == :value + end + + it "is not defined in these classes" do + ClassVariablesSpec::ClassC.cvar_defined?.should be_false + end + + it "is only updated in the module a method defined in the module is used" do + ClassVariablesSpec::ClassC.cvar_m = "new value" + ClassVariablesSpec::ClassC.cvar_m.should == "new value" + + ClassVariablesSpec::ClassC.cvar_defined?.should be_false + end + + it "is updated in the class when a Method defined in the class is used" do + ClassVariablesSpec::ClassC.cvar_c = "new value" + ClassVariablesSpec::ClassC.cvar_defined?.should be_true + end + + it "can be accessed inside the class using the module methods" do + ClassVariablesSpec::ClassC.cvar_c = "new value" + ClassVariablesSpec::ClassC.cvar_m.should == :value + end + + it "can be accessed from modules that extend the module" do + ClassVariablesSpec::ModuleO.cvar_n.should == :value + end + + it "is defined in the extended module" do + ClassVariablesSpec::ModuleN.class_variable_defined?(:@@cvar_n).should be_true + end + + it "is not defined in the extending module" do + ClassVariablesSpec::ModuleO.class_variable_defined?(:@@cvar_n).should be_false + end +end + +describe 'A class variable definition' do + it "is created in a module if any of the parents do not define it" do + a = Class.new + b = Class.new(a) + c = Class.new(b) + b.class_variable_set(:@@cv, :value) + + lambda { a.class_variable_get(:@@cv) }.should raise_error(NameError) + b.class_variable_get(:@@cv).should == :value + c.class_variable_get(:@@cv).should == :value + + # updates the same variable + c.class_variable_set(:@@cv, :next) + + lambda { a.class_variable_get(:@@cv) }.should raise_error(NameError) + b.class_variable_get(:@@cv).should == :next + c.class_variable_get(:@@cv).should == :next + end +end diff --git a/spec/rubyspec/language/constants_spec.rb b/spec/rubyspec/language/constants_spec.rb new file mode 100644 index 0000000000..e68ba495be --- /dev/null +++ b/spec/rubyspec/language/constants_spec.rb @@ -0,0 +1,613 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/constants', __FILE__) +require File.expand_path('../fixtures/constants_sclass', __FILE__) +require File.expand_path('../fixtures/constant_visibility', __FILE__) + +# Read the documentation in fixtures/constants.rb for the guidelines and +# rationale for the structure and organization of these specs. + +describe "Literal (A::X) constant resolution" do + describe "with statically assigned constants" do + it "searches the immediate class or module scope first" do + ConstantSpecs::ClassA::CS_CONST10.should == :const10_10 + ConstantSpecs::ModuleA::CS_CONST10.should == :const10_1 + ConstantSpecs::ParentA::CS_CONST10.should == :const10_5 + ConstantSpecs::ContainerA::CS_CONST10.should == :const10_2 + ConstantSpecs::ContainerA::ChildA::CS_CONST10.should == :const10_3 + end + + it "searches a module included in the immediate class before the superclass" do + ConstantSpecs::ContainerA::ChildA::CS_CONST15.should == :const15_1 + end + + it "searches the superclass before a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA::CS_CONST11.should == :const11_1 + end + + it "searches a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA::CS_CONST12.should == :const12_1 + end + + it "searches the superclass chain" do + ConstantSpecs::ContainerA::ChildA::CS_CONST13.should == :const13 + end + + it "searches Object if no class or module qualifier is given" do + CS_CONST1.should == :const1 + CS_CONST10.should == :const10_1 + end + + it "searches Object after searching other scopes" do + module ConstantSpecs::SpecAdded1 + CS_CONST10.should == :const10_1 + end + end + + it "searches Object if a toplevel qualifier (::X) is given" do + ::CS_CONST1.should == :const1 + ::CS_CONST10.should == :const10_1 + end + + it "does not search the singleton class of the class or module" do + lambda do + ConstantSpecs::ContainerA::ChildA::CS_CONST14 + end.should raise_error(NameError) + lambda { ConstantSpecs::CS_CONST14 }.should raise_error(NameError) + end + end + + describe "with dynamically assigned constants" do + it "searches the immediate class or module scope first" do + ConstantSpecs::ClassB::CS_CONST101 = :const101_1 + ConstantSpecs::ClassB::CS_CONST101.should == :const101_1 + + ConstantSpecs::ParentB::CS_CONST101 = :const101_2 + ConstantSpecs::ParentB::CS_CONST101.should == :const101_2 + + ConstantSpecs::ContainerB::CS_CONST101 = :const101_3 + ConstantSpecs::ContainerB::CS_CONST101.should == :const101_3 + + ConstantSpecs::ContainerB::ChildB::CS_CONST101 = :const101_4 + ConstantSpecs::ContainerB::ChildB::CS_CONST101.should == :const101_4 + + ConstantSpecs::ModuleA::CS_CONST101 = :const101_5 + ConstantSpecs::ModuleA::CS_CONST101.should == :const101_5 + end + + it "searches a module included in the immediate class before the superclass" do + ConstantSpecs::ParentB::CS_CONST102 = :const102_1 + ConstantSpecs::ModuleF::CS_CONST102 = :const102_2 + ConstantSpecs::ContainerB::ChildB::CS_CONST102.should == :const102_2 + end + + it "searches the superclass before a module included in the superclass" do + ConstantSpecs::ModuleE::CS_CONST103 = :const103_1 + ConstantSpecs::ParentB::CS_CONST103 = :const103_2 + ConstantSpecs::ContainerB::ChildB::CS_CONST103.should == :const103_2 + end + + it "searches a module included in the superclass" do + ConstantSpecs::ModuleA::CS_CONST104 = :const104_1 + ConstantSpecs::ModuleE::CS_CONST104 = :const104_2 + ConstantSpecs::ContainerB::ChildB::CS_CONST104.should == :const104_2 + end + + it "searches the superclass chain" do + ConstantSpecs::ModuleA::CS_CONST105 = :const105 + ConstantSpecs::ContainerB::ChildB::CS_CONST105.should == :const105 + end + + it "searches Object if no class or module qualifier is given" do + CS_CONST106 = :const106 + CS_CONST106.should == :const106 + end + + it "searches Object if a toplevel qualifier (::X) is given" do + ::CS_CONST107 = :const107 + ::CS_CONST107.should == :const107 + end + + it "does not search the singleton class of the class or module" do + class << ConstantSpecs::ContainerB::ChildB + CS_CONST108 = :const108_1 + end + + lambda do + ConstantSpecs::ContainerB::ChildB::CS_CONST108 + end.should raise_error(NameError) + + module ConstantSpecs + class << self + CS_CONST108 = :const108_2 + end + end + + lambda { ConstantSpecs::CS_CONST108 }.should raise_error(NameError) + end + + it "returns the updated value when a constant is reassigned" do + ConstantSpecs::ClassB::CS_CONST109 = :const109_1 + ConstantSpecs::ClassB::CS_CONST109.should == :const109_1 + + -> { + ConstantSpecs::ClassB::CS_CONST109 = :const109_2 + }.should complain(/already initialized constant/) + ConstantSpecs::ClassB::CS_CONST109.should == :const109_2 + end + + it "evaluates the right hand side before evaluating a constant path" do + mod = Module.new + + mod.module_eval <<-EOC + ConstantSpecsRHS::B = begin + module ConstantSpecsRHS; end + + "hello" + end + EOC + + mod::ConstantSpecsRHS::B.should == 'hello' + end + end + + it "raises a NameError if no constant is defined in the search path" do + lambda { ConstantSpecs::ParentA::CS_CONSTX }.should raise_error(NameError) + end + + it "sends #const_missing to the original class or module scope" do + ConstantSpecs::ClassA::CS_CONSTX.should == :CS_CONSTX + end + + it "evaluates the qualifier" do + ConstantSpecs.get_const::CS_CONST2.should == :const2 + end + + it "raises a TypeError if a non-class or non-module qualifier is given" do + lambda { CS_CONST1::CS_CONST }.should raise_error(TypeError) + lambda { 1::CS_CONST }.should raise_error(TypeError) + lambda { "mod"::CS_CONST }.should raise_error(TypeError) + lambda { false::CS_CONST }.should raise_error(TypeError) + end +end + +describe "Constant resolution within methods" do + describe "with statically assigned constants" do + it "searches the immediate class or module scope first" do + ConstantSpecs::ClassA.const10.should == :const10_10 + ConstantSpecs::ParentA.const10.should == :const10_5 + ConstantSpecs::ContainerA.const10.should == :const10_2 + ConstantSpecs::ContainerA::ChildA.const10.should == :const10_3 + + ConstantSpecs::ClassA.new.const10.should == :const10_10 + ConstantSpecs::ParentA.new.const10.should == :const10_5 + ConstantSpecs::ContainerA::ChildA.new.const10.should == :const10_3 + end + + it "searches a module included in the immediate class before the superclass" do + ConstantSpecs::ContainerA::ChildA.const15.should == :const15_1 + ConstantSpecs::ContainerA::ChildA.new.const15.should == :const15_1 + end + + it "searches the superclass before a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA.const11.should == :const11_1 + ConstantSpecs::ContainerA::ChildA.new.const11.should == :const11_1 + end + + it "searches a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA.const12.should == :const12_1 + ConstantSpecs::ContainerA::ChildA.new.const12.should == :const12_1 + end + + it "searches the superclass chain" do + ConstantSpecs::ContainerA::ChildA.const13.should == :const13 + ConstantSpecs::ContainerA::ChildA.new.const13.should == :const13 + end + + it "searches the lexical scope of the method not the receiver's immediate class" do + ConstantSpecs::ContainerA::ChildA.const19.should == :const19_1 + end + + it "searches the lexical scope of a singleton method" do + ConstantSpecs::CS_CONST18.const17.should == :const17_1 + end + + it "does not search the lexical scope of the caller" do + lambda { ConstantSpecs::ClassA.const16 }.should raise_error(NameError) + end + + it "searches the lexical scope of a block" do + ConstantSpecs::ClassA.const22.should == :const22_1 + end + + it "searches Object as a lexical scope only if Object is explicitly opened" do + ConstantSpecs::ContainerA::ChildA.const20.should == :const20_1 + ConstantSpecs::ContainerA::ChildA.const21.should == :const21_1 + end + + it "does not search the lexical scope of qualifying modules" do + lambda do + ConstantSpecs::ContainerA::ChildA.const23 + end.should raise_error(NameError) + end + end + + describe "with dynamically assigned constants" do + it "searches the immediate class or module scope first" do + ConstantSpecs::ModuleA::CS_CONST201 = :const201_1 + + class ConstantSpecs::ClassB; CS_CONST201 = :const201_2; end + ConstantSpecs::ParentB::CS_CONST201 = :const201_3 + ConstantSpecs::ContainerB::CS_CONST201 = :const201_4 + ConstantSpecs::ContainerB::ChildB::CS_CONST201 = :const201_5 + + ConstantSpecs::ClassB.const201.should == :const201_2 + ConstantSpecs::ParentB.const201.should == :const201_3 + ConstantSpecs::ContainerB.const201.should == :const201_4 + ConstantSpecs::ContainerB::ChildB.const201.should == :const201_5 + + ConstantSpecs::ClassB.new.const201.should == :const201_2 + ConstantSpecs::ParentB.new.const201.should == :const201_3 + ConstantSpecs::ContainerB::ChildB.new.const201.should == :const201_5 + end + + it "searches a module included in the immediate class before the superclass" do + ConstantSpecs::ParentB::CS_CONST202 = :const202_2 + ConstantSpecs::ContainerB::ChildB::CS_CONST202 = :const202_1 + + ConstantSpecs::ContainerB::ChildB.const202.should == :const202_1 + ConstantSpecs::ContainerB::ChildB.new.const202.should == :const202_1 + end + + it "searches the superclass before a module included in the superclass" do + ConstantSpecs::ParentB::CS_CONST203 = :const203_1 + ConstantSpecs::ModuleE::CS_CONST203 = :const203_2 + + ConstantSpecs::ContainerB::ChildB.const203.should == :const203_1 + ConstantSpecs::ContainerB::ChildB.new.const203.should == :const203_1 + end + + it "searches a module included in the superclass" do + ConstantSpecs::ModuleA::CS_CONST204 = :const204_2 + ConstantSpecs::ModuleE::CS_CONST204 = :const204_1 + + ConstantSpecs::ContainerB::ChildB.const204.should == :const204_1 + ConstantSpecs::ContainerB::ChildB.new.const204.should == :const204_1 + end + + it "searches the superclass chain" do + ConstantSpecs::ModuleA::CS_CONST205 = :const205 + + ConstantSpecs::ContainerB::ChildB.const205.should == :const205 + ConstantSpecs::ContainerB::ChildB.new.const205.should == :const205 + end + + it "searches the lexical scope of the method not the receiver's immediate class" do + ConstantSpecs::ContainerB::ChildB::CS_CONST206 = :const206_2 + class ConstantSpecs::ContainerB::ChildB + class << self + CS_CONST206 = :const206_1 + end + end + + ConstantSpecs::ContainerB::ChildB.const206.should == :const206_1 + end + + it "searches the lexical scope of a singleton method" do + ConstantSpecs::CS_CONST207 = :const207_1 + ConstantSpecs::ClassB::CS_CONST207 = :const207_2 + + ConstantSpecs::CS_CONST208.const207.should == :const207_1 + end + + it "does not search the lexical scope of the caller" do + ConstantSpecs::ClassB::CS_CONST209 = :const209 + + lambda { ConstantSpecs::ClassB.const209 }.should raise_error(NameError) + end + + it "searches the lexical scope of a block" do + ConstantSpecs::ClassB::CS_CONST210 = :const210_1 + ConstantSpecs::ParentB::CS_CONST210 = :const210_2 + + ConstantSpecs::ClassB.const210.should == :const210_1 + end + + it "searches Object as a lexical scope only if Object is explicitly opened" do + Object::CS_CONST211 = :const211_1 + ConstantSpecs::ParentB::CS_CONST211 = :const211_2 + ConstantSpecs::ContainerB::ChildB.const211.should == :const211_1 + + Object::CS_CONST212 = :const212_2 + ConstantSpecs::ParentB::CS_CONST212 = :const212_1 + ConstantSpecs::ContainerB::ChildB.const212.should == :const212_1 + end + + it "returns the updated value when a constant is reassigned" do + ConstantSpecs::ParentB::CS_CONST213 = :const213_1 + ConstantSpecs::ContainerB::ChildB.const213.should == :const213_1 + ConstantSpecs::ContainerB::ChildB.new.const213.should == :const213_1 + + -> { + ConstantSpecs::ParentB::CS_CONST213 = :const213_2 + }.should complain(/already initialized constant/) + ConstantSpecs::ContainerB::ChildB.const213.should == :const213_2 + ConstantSpecs::ContainerB::ChildB.new.const213.should == :const213_2 + end + + it "does not search the lexical scope of qualifying modules" do + ConstantSpecs::ContainerB::CS_CONST214 = :const214 + + lambda do + ConstantSpecs::ContainerB::ChildB.const214 + end.should raise_error(NameError) + end + end + + it "raises a NameError if no constant is defined in the search path" do + lambda { ConstantSpecs::ParentA.constx }.should raise_error(NameError) + end + + it "sends #const_missing to the original class or module scope" do + ConstantSpecs::ClassA.constx.should == :CS_CONSTX + ConstantSpecs::ClassA.new.constx.should == :CS_CONSTX + end + + describe "with ||=" do + it "assignes constant if previously undefined" do + ConstantSpecs.should_not have_constant(:OpAssignUndefined) + # Literally opening the module is required to avoid content + # re-assignment error + module ConstantSpecs + OpAssignUndefined ||= 42 + end + ConstantSpecs::OpAssignUndefined.should == 42 + ConstantSpecs.send(:remove_const, :OpAssignUndefined) + end + end +end + +describe "Constant resolution within a singleton class (class << obj)" do + it "works like normal classes or modules" do + ConstantSpecs::CS_SINGLETON1.foo.should == 1 + end + + ruby_version_is "2.3" do + it "uses its own namespace for each object" do + a = ConstantSpecs::CS_SINGLETON2[0].foo + b = ConstantSpecs::CS_SINGLETON2[1].foo + [a, b].should == [1, 2] + end + + it "uses its own namespace for nested modules" do + a = ConstantSpecs::CS_SINGLETON3[0].x + b = ConstantSpecs::CS_SINGLETON3[1].x + a.should_not equal(b) + end + + it "allows nested modules to have proper resolution" do + a = ConstantSpecs::CS_SINGLETON4_CLASSES[0].new + b = ConstantSpecs::CS_SINGLETON4_CLASSES[1].new + [a.foo, b.foo].should == [1, 2] + end + end +end + +describe "Module#private_constant marked constants" do + + it "remain private even when updated" do + mod = Module.new + mod.const_set :Foo, true + mod.send :private_constant, :Foo + -> { + mod.const_set :Foo, false + }.should complain(/already initialized constant/) + + lambda {mod::Foo}.should raise_error(NameError) + end + + describe "in a module" do + it "cannot be accessed from outside the module" do + lambda do + ConstantVisibility::PrivConstModule::PRIVATE_CONSTANT_MODULE + end.should raise_error(NameError) + end + + it "cannot be reopened as a module from scope where constant would be private" do + lambda do + module ConstantVisibility::ModuleContainer::PrivateModule; end + end.should raise_error(NameError) + end + + it "cannot be reopened as a class from scope where constant would be private" do + lambda do + class ConstantVisibility::ModuleContainer::PrivateClass; end + end.should raise_error(NameError) + end + + it "can be reopened as a module where constant is not private" do + module ::ConstantVisibility::ModuleContainer + module PrivateModule + X = 1 + end + + PrivateModule::X.should == 1 + end + end + + it "can be reopened as a class where constant is not private" do + module ::ConstantVisibility::ModuleContainer + class PrivateClass + X = 1 + end + + PrivateClass::X.should == 1 + end + end + + it "is not defined? with A::B form" do + defined?(ConstantVisibility::PrivConstModule::PRIVATE_CONSTANT_MODULE).should == nil + end + + it "can be accessed from the module itself" do + ConstantVisibility::PrivConstModule.private_constant_from_self.should be_true + end + + it "is defined? from the module itself" do + ConstantVisibility::PrivConstModule.defined_from_self.should == "constant" + end + + it "can be accessed from lexical scope" do + ConstantVisibility::PrivConstModule::Nested.private_constant_from_scope.should be_true + end + + it "is defined? from lexical scope" do + ConstantVisibility::PrivConstModule::Nested.defined_from_scope.should == "constant" + end + + it "can be accessed from classes that include the module" do + ConstantVisibility::PrivConstModuleChild.new.private_constant_from_include.should be_true + end + + it "is defined? from classes that include the module" do + ConstantVisibility::PrivConstModuleChild.new.defined_from_include.should == "constant" + end + end + + describe "in a class" do + it "cannot be accessed from outside the class" do + lambda do + ConstantVisibility::PrivConstClass::PRIVATE_CONSTANT_CLASS + end.should raise_error(NameError) + end + + it "cannot be reopened as a module" do + lambda do + module ConstantVisibility::ClassContainer::PrivateModule; end + end.should raise_error(NameError) + end + + it "cannot be reopened as a class" do + lambda do + class ConstantVisibility::ClassContainer::PrivateClass; end + end.should raise_error(NameError) + end + + it "can be reopened as a module where constant is not private" do + class ::ConstantVisibility::ClassContainer + module PrivateModule + X = 1 + end + + PrivateModule::X.should == 1 + end + end + + it "can be reopened as a class where constant is not private" do + class ::ConstantVisibility::ClassContainer + class PrivateClass + X = 1 + end + + PrivateClass::X.should == 1 + end + end + + it "is not defined? with A::B form" do + defined?(ConstantVisibility::PrivConstClass::PRIVATE_CONSTANT_CLASS).should == nil + end + + it "can be accessed from the class itself" do + ConstantVisibility::PrivConstClass.private_constant_from_self.should be_true + end + + it "is defined? from the class itself" do + ConstantVisibility::PrivConstClass.defined_from_self.should == "constant" + end + + it "can be accessed from lexical scope" do + ConstantVisibility::PrivConstClass::Nested.private_constant_from_scope.should be_true + end + + it "is defined? from lexical scope" do + ConstantVisibility::PrivConstClass::Nested.defined_from_scope.should == "constant" + end + + it "can be accessed from subclasses" do + ConstantVisibility::PrivConstClassChild.new.private_constant_from_subclass.should be_true + end + + it "is defined? from subclasses" do + ConstantVisibility::PrivConstClassChild.new.defined_from_subclass.should == "constant" + end + end + + describe "in Object" do + it "cannot be accessed using ::Const form" do + lambda do + ::PRIVATE_CONSTANT_IN_OBJECT + end.should raise_error(NameError) + end + + it "is not defined? using ::Const form" do + defined?(::PRIVATE_CONSTANT_IN_OBJECT).should == nil + end + + it "can be accessed through the normal search" do + PRIVATE_CONSTANT_IN_OBJECT.should == true + end + + it "is defined? through the normal search" do + defined?(PRIVATE_CONSTANT_IN_OBJECT).should == "constant" + end + end +end + +describe "Module#public_constant marked constants" do + before :each do + @module = ConstantVisibility::PrivConstModule.dup + end + + describe "in a module" do + it "can be accessed from outside the module" do + @module.send :public_constant, :PRIVATE_CONSTANT_MODULE + @module::PRIVATE_CONSTANT_MODULE.should == true + end + + it "is defined? with A::B form" do + @module.send :public_constant, :PRIVATE_CONSTANT_MODULE + defined?(@module::PRIVATE_CONSTANT_MODULE).should == "constant" + end + end + + describe "in a class" do + before :each do + @class = ConstantVisibility::PrivConstClass.dup + end + + it "can be accessed from outside the class" do + @class.send :public_constant, :PRIVATE_CONSTANT_CLASS + @class::PRIVATE_CONSTANT_CLASS.should == true + end + + it "is defined? with A::B form" do + @class.send :public_constant, :PRIVATE_CONSTANT_CLASS + defined?(@class::PRIVATE_CONSTANT_CLASS).should == "constant" + end + end + + describe "in Object" do + after :each do + ConstantVisibility.reset_private_constants + end + + it "can be accessed using ::Const form" do + Object.send :public_constant, :PRIVATE_CONSTANT_IN_OBJECT + ::PRIVATE_CONSTANT_IN_OBJECT.should == true + end + + it "is defined? using ::Const form" do + Object.send :public_constant, :PRIVATE_CONSTANT_IN_OBJECT + defined?(::PRIVATE_CONSTANT_IN_OBJECT).should == "constant" + end + end +end diff --git a/spec/rubyspec/language/def_spec.rb b/spec/rubyspec/language/def_spec.rb new file mode 100644 index 0000000000..55ee283b90 --- /dev/null +++ b/spec/rubyspec/language/def_spec.rb @@ -0,0 +1,714 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/def', __FILE__) + +# Language-level method behaviour +describe "Redefining a method" do + it "replaces the original method" do + def barfoo; 100; end + barfoo.should == 100 + + def barfoo; 200; end + barfoo.should == 200 + end +end + +describe "Defining a method at the top-level" do + it "defines it on Object with private visibility by default" do + Object.should have_private_instance_method(:some_toplevel_method, false) + end + + it "defines it on Object with public visibility after calling public" do + Object.should have_public_instance_method(:public_toplevel_method, false) + end +end + +describe "Defining an 'initialize' method" do + it "sets the method's visibility to private" do + class DefInitializeSpec + def initialize + end + end + DefInitializeSpec.should have_private_instance_method(:initialize, false) + end +end + +describe "Defining an 'initialize_copy' method" do + it "sets the method's visibility to private" do + class DefInitializeCopySpec + def initialize_copy + end + end + DefInitializeCopySpec.should have_private_instance_method(:initialize_copy, false) + end +end + +describe "Defining an 'initialize_dup' method" do + it "sets the method's visibility to private" do + class DefInitializeDupSpec + def initialize_dup + end + end + DefInitializeDupSpec.should have_private_instance_method(:initialize_dup, false) + end +end + +describe "Defining an 'initialize_clone' method" do + it "sets the method's visibility to private" do + class DefInitializeCloneSpec + def initialize_clone + end + end + DefInitializeCloneSpec.should have_private_instance_method(:initialize_clone, false) + end +end + +describe "Defining a 'respond_to_missing?' method" do + it "sets the method's visibility to private" do + class DefRespondToMissingPSpec + def respond_to_missing? + end + end + DefRespondToMissingPSpec.should have_private_instance_method(:respond_to_missing?, false) + end +end + +describe "Defining a method" do + it "returns a symbol of the method name" do + method_name = def some_method; end + method_name.should == :some_method + end +end + +describe "An instance method definition with a splat" do + it "accepts an unnamed '*' argument" do + def foo(*); end; + + foo.should == nil + foo(1, 2).should == nil + foo(1, 2, 3, 4, :a, :b, 'c', 'd').should == nil + end + + it "accepts a named * argument" do + def foo(*a); a; end; + foo.should == [] + foo(1, 2).should == [1, 2] + foo([:a]).should == [[:a]] + end + + it "accepts non-* arguments before the * argument" do + def foo(a, b, c, d, e, *f); [a, b, c, d, e, f]; end + foo(1, 2, 3, 4, 5, 6, 7, 8).should == [1, 2, 3, 4, 5, [6, 7, 8]] + end + + it "allows only a single * argument" do + lambda { eval 'def foo(a, *b, *c); end' }.should raise_error(SyntaxError) + end + + it "requires the presence of any arguments that precede the *" do + def foo(a, b, *c); end + lambda { foo 1 }.should raise_error(ArgumentError) + end +end + +describe "An instance method with a default argument" do + it "evaluates the default when no arguments are passed" do + def foo(a = 1) + a + end + foo.should == 1 + foo(2).should == 2 + end + + it "evaluates the default empty expression when no arguments are passed" do + def foo(a = ()) + a + end + foo.should == nil + foo(2).should == 2 + end + + it "assigns an empty Array to an unused splat argument" do + def foo(a = 1, *b) + [a,b] + end + foo.should == [1, []] + foo(2).should == [2, []] + end + + it "evaluates the default when required arguments precede it" do + def foo(a, b = 2) + [a,b] + end + lambda { foo }.should raise_error(ArgumentError) + foo(1).should == [1, 2] + end + + it "prefers to assign to a default argument before a splat argument" do + def foo(a, b = 2, *c) + [a,b,c] + end + lambda { foo }.should raise_error(ArgumentError) + foo(1).should == [1,2,[]] + end + + it "prefers to assign to a default argument when there are no required arguments" do + def foo(a = 1, *args) + [a,args] + end + foo(2,2).should == [2,[2]] + end + + it "does not evaluate the default when passed a value and a * argument" do + def foo(a, b = 2, *args) + [a,b,args] + end + foo(2,3,3).should == [2,3,[3]] + end + + it "shadows an existing method with the same name as the local" do + def bar + 1 + end + -> { + eval "def foo(bar = bar) + bar + end" + }.should complain(/circular argument reference/) + foo.should == nil + foo(2).should == 2 + end + + it "calls a method with the same name as the local when explicitly using ()" do + def bar + 1 + end + def foo(bar = bar()) + bar + end + foo.should == 1 + foo(2).should == 2 + end +end + +describe "A singleton method definition" do + it "can be declared for a local variable" do + a = Object.new + def a.foo + 5 + end + a.foo.should == 5 + end + + it "can be declared for an instance variable" do + @a = Object.new + def @a.foo + 6 + end + @a.foo.should == 6 + end + + it "can be declared for a global variable" do + $__a__ = "hi" + def $__a__.foo + 7 + end + $__a__.foo.should == 7 + end + + it "can be declared with an empty method body" do + class DefSpec + def self.foo;end + end + DefSpec.foo.should == nil + end + + it "can be redefined" do + obj = Object.new + def obj.==(other) + 1 + end + (obj==1).should == 1 + def obj.==(other) + 2 + end + (obj==2).should == 2 + end + + it "raises RuntimeError if frozen" do + obj = Object.new + obj.freeze + lambda { def obj.foo; end }.should raise_error(RuntimeError) + end +end + +describe "Redefining a singleton method" do + it "does not inherit a previously set visibility" do + o = Object.new + + class << o; private; def foo; end; end; + + class << o; should have_private_instance_method(:foo); end + + class << o; def foo; end; end; + + class << o; should_not have_private_instance_method(:foo); end + class << o; should have_instance_method(:foo); end + + end +end + +describe "Redefining a singleton method" do + it "does not inherit a previously set visibility" do + o = Object.new + + class << o; private; def foo; end; end; + + class << o; should have_private_instance_method(:foo); end + + class << o; def foo; end; end; + + class << o; should_not have_private_instance_method(:foo); end + class << o; should have_instance_method(:foo); end + + end +end + +describe "A method defined with extreme default arguments" do + it "can redefine itself when the default is evaluated" do + class DefSpecs + def foo(x = (def foo; "hello"; end;1));x;end + end + + d = DefSpecs.new + d.foo(42).should == 42 + d.foo.should == 1 + d.foo.should == 'hello' + end + + it "may use an fcall as a default" do + def bar + 1 + end + def foo(x = bar()) + x + end + foo.should == 1 + foo(2).should == 2 + end + + it "evaluates the defaults in the method's scope" do + def foo(x = ($foo_self = self; nil)); end + foo + $foo_self.should == self + end + + it "may use preceding arguments as defaults" do + def foo(obj, width=obj.length) + width + end + foo('abcde').should == 5 + end + + it "may use a lambda as a default" do + def foo(output = 'a', prc = lambda {|n| output * n}) + prc.call(5) + end + foo.should == 'aaaaa' + end +end + +describe "A singleton method defined with extreme default arguments" do + it "may use a method definition as a default" do + $__a = Object.new + def $__a.foo(x = (def $__a.foo; "hello"; end;1));x;end + + $__a.foo(42).should == 42 + $__a.foo.should == 1 + $__a.foo.should == 'hello' + end + + it "may use an fcall as a default" do + a = Object.new + def a.bar + 1 + end + def a.foo(x = bar()) + x + end + a.foo.should == 1 + a.foo(2).should == 2 + end + + it "evaluates the defaults in the singleton scope" do + a = Object.new + def a.foo(x = ($foo_self = self; nil)); 5 ;end + a.foo + $foo_self.should == a + end + + it "may use preceding arguments as defaults" do + a = Object.new + def a.foo(obj, width=obj.length) + width + end + a.foo('abcde').should == 5 + end + + it "may use a lambda as a default" do + a = Object.new + def a.foo(output = 'a', prc = lambda {|n| output * n}) + prc.call(5) + end + a.foo.should == 'aaaaa' + end +end + +describe "A method definition inside a metaclass scope" do + it "can create a class method" do + class DefSpecSingleton + class << self + def a_class_method;self;end + end + end + + DefSpecSingleton.a_class_method.should == DefSpecSingleton + lambda { Object.a_class_method }.should raise_error(NoMethodError) + end + + it "can create a singleton method" do + obj = Object.new + class << obj + def a_singleton_method;self;end + end + + obj.a_singleton_method.should == obj + lambda { Object.new.a_singleton_method }.should raise_error(NoMethodError) + end + + it "raises RuntimeError if frozen" do + obj = Object.new + obj.freeze + + class << obj + lambda { def foo; end }.should raise_error(RuntimeError) + end + end +end + +describe "A nested method definition" do + it "creates an instance method when evaluated in an instance method" do + class DefSpecNested + def create_instance_method + def an_instance_method;self;end + an_instance_method + end + end + + obj = DefSpecNested.new + obj.create_instance_method.should == obj + obj.an_instance_method.should == obj + + other = DefSpecNested.new + other.an_instance_method.should == other + + DefSpecNested.should have_instance_method(:an_instance_method) + end + + it "creates a class method when evaluated in a class method" do + class DefSpecNested + class << self + # cleanup + remove_method :a_class_method if method_defined? :a_class_method + def create_class_method + def a_class_method;self;end + a_class_method + end + end + end + + lambda { DefSpecNested.a_class_method }.should raise_error(NoMethodError) + DefSpecNested.create_class_method.should == DefSpecNested + DefSpecNested.a_class_method.should == DefSpecNested + lambda { Object.a_class_method }.should raise_error(NoMethodError) + lambda { DefSpecNested.new.a_class_method }.should raise_error(NoMethodError) + end + + it "creates a singleton method when evaluated in the metaclass of an instance" do + class DefSpecNested + def create_singleton_method + class << self + def a_singleton_method;self;end + end + a_singleton_method + end + end + + obj = DefSpecNested.new + obj.create_singleton_method.should == obj + obj.a_singleton_method.should == obj + + other = DefSpecNested.new + lambda { other.a_singleton_method }.should raise_error(NoMethodError) + end + + it "creates a method in the surrounding context when evaluated in a def expr.method" do + class DefSpecNested + TARGET = Object.new + def TARGET.defs_method + def inherited_method;self;end + end + end + + DefSpecNested::TARGET.defs_method + DefSpecNested.should have_instance_method :inherited_method + DefSpecNested::TARGET.should_not have_method :inherited_method + + obj = DefSpecNested.new + obj.inherited_method.should == obj + end + + # See http://yugui.jp/articles/846#label-3 + it "inside an instance_eval creates a singleton method" do + class DefSpecNested + OBJ = Object.new + OBJ.instance_eval do + def create_method_in_instance_eval(a = (def arg_method; end)) + def body_method; end + end + end + end + + obj = DefSpecNested::OBJ + obj.create_method_in_instance_eval + + obj.should have_method :arg_method + obj.should have_method :body_method + + DefSpecNested.should_not have_instance_method :arg_method + DefSpecNested.should_not have_instance_method :body_method + end + + it "defines methods as public by default" do + cls = Class.new do + def do_def + def new_def + 1 + end + end + end + + obj = cls.new + obj.do_def + obj.new_def.should == 1 + end +end + +describe "A method definition inside an instance_eval" do + it "creates a singleton method" do + obj = Object.new + obj.instance_eval do + def an_instance_eval_method;self;end + end + obj.an_instance_eval_method.should == obj + + other = Object.new + lambda { other.an_instance_eval_method }.should raise_error(NoMethodError) + end + + it "creates a singleton method when evaluated inside a metaclass" do + obj = Object.new + obj.instance_eval do + class << self + def a_metaclass_eval_method;self;end + end + end + obj.a_metaclass_eval_method.should == obj + + other = Object.new + lambda { other.a_metaclass_eval_method }.should raise_error(NoMethodError) + end + + it "creates a class method when the receiver is a class" do + DefSpecNested.instance_eval do + def an_instance_eval_class_method;self;end + end + + DefSpecNested.an_instance_eval_class_method.should == DefSpecNested + lambda { Object.an_instance_eval_class_method }.should raise_error(NoMethodError) + end + + it "creates a class method when the receiver is an anonymous class" do + m = Class.new + m.instance_eval do + def klass_method + :test + end + end + + m.klass_method.should == :test + lambda { Object.klass_method }.should raise_error(NoMethodError) + end + + it "creates a class method when instance_eval is within class" do + m = Class.new do + instance_eval do + def klass_method + :test + end + end + end + + m.klass_method.should == :test + lambda { Object.klass_method }.should raise_error(NoMethodError) + end +end + +describe "A method definition inside an instance_exec" do + it "creates a class method when the receiver is a class" do + DefSpecNested.instance_exec(1) do |param| + @stuff = param + + def an_instance_exec_class_method; @stuff; end + end + + DefSpecNested.an_instance_exec_class_method.should == 1 + lambda { Object.an_instance_exec_class_method }.should raise_error(NoMethodError) + end + + it "creates a class method when the receiver is an anonymous class" do + m = Class.new + m.instance_exec(1) do |param| + @stuff = param + + def klass_method + @stuff + end + end + + m.klass_method.should == 1 + lambda { Object.klass_method }.should raise_error(NoMethodError) + end + + it "creates a class method when instance_exec is within class" do + m = Class.new do + instance_exec(2) do |param| + @stuff = param + + def klass_method + @stuff + end + end + end + + m.klass_method.should == 2 + lambda { Object.klass_method }.should raise_error(NoMethodError) + end +end + +describe "A method definition in an eval" do + it "creates an instance method" do + class DefSpecNested + def eval_instance_method + eval "def an_eval_instance_method;self;end", binding + an_eval_instance_method + end + end + + obj = DefSpecNested.new + obj.eval_instance_method.should == obj + obj.an_eval_instance_method.should == obj + + other = DefSpecNested.new + other.an_eval_instance_method.should == other + + lambda { Object.new.an_eval_instance_method }.should raise_error(NoMethodError) + end + + it "creates a class method" do + class DefSpecNestedB + class << self + def eval_class_method + eval "def an_eval_class_method;self;end" #, binding + an_eval_class_method + end + end + end + + DefSpecNestedB.eval_class_method.should == DefSpecNestedB + DefSpecNestedB.an_eval_class_method.should == DefSpecNestedB + + lambda { Object.an_eval_class_method }.should raise_error(NoMethodError) + lambda { DefSpecNestedB.new.an_eval_class_method}.should raise_error(NoMethodError) + end + + it "creates a singleton method" do + class DefSpecNested + def eval_singleton_method + class << self + eval "def an_eval_singleton_method;self;end", binding + end + an_eval_singleton_method + end + end + + obj = DefSpecNested.new + obj.eval_singleton_method.should == obj + obj.an_eval_singleton_method.should == obj + + other = DefSpecNested.new + lambda { other.an_eval_singleton_method }.should raise_error(NoMethodError) + end +end + +describe "a method definition that sets more than one default parameter all to the same value" do + def foo(a=b=c={}) + [a,b,c] + end + it "assigns them all the same object by default" do + foo.should == [{},{},{}] + a, b, c = foo + a.should eql(b) + a.should eql(c) + end + + it "allows the first argument to be given, and sets the rest to null" do + foo(1).should == [1,nil,nil] + end + + it "assigns the parameters different objects across different default calls" do + a, _b, _c = foo + d, _e, _f = foo + a.should_not equal(d) + end + + it "only allows overriding the default value of the first such parameter in each set" do + lambda { foo(1,2) }.should raise_error(ArgumentError) + end + + def bar(a=b=c=1,d=2) + [a,b,c,d] + end + + it "treats the argument after the multi-parameter normally" do + bar.should == [1,1,1,2] + bar(3).should == [3,nil,nil,2] + bar(3,4).should == [3,nil,nil,4] + lambda { bar(3,4,5) }.should raise_error(ArgumentError) + end +end + +describe "The def keyword" do + describe "within a closure" do + it "looks outside the closure for the visibility" do + module DefSpecsLambdaVisibility + private + + lambda { + def some_method; end + }.call + end + + DefSpecsLambdaVisibility.should have_private_instance_method("some_method") + end + end +end diff --git a/spec/rubyspec/language/defined_spec.rb b/spec/rubyspec/language/defined_spec.rb new file mode 100644 index 0000000000..790db64e86 --- /dev/null +++ b/spec/rubyspec/language/defined_spec.rb @@ -0,0 +1,1130 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/defined', __FILE__) + +describe "The defined? keyword for literals" do + it "returns 'self' for self" do + ret = defined?(self) + ret.should == "self" + end + + it "returns 'nil' for nil" do + ret = defined?(nil) + ret.should == "nil" + end + + it "returns 'true' for true" do + ret = defined?(true) + ret.should == "true" + end + + it "returns 'false' for false" do + ret = defined?(false) + ret.should == "false" + end + + describe "for a literal Array" do + + it "returns 'expression' if each element is defined" do + ret = defined?([Object, Array]) + ret.should == "expression" + end + + it "returns nil if one element is not defined" do + ret = defined?([NonExistantConstant, Array]) + ret.should == nil + end + + it "returns nil if all elements are not defined" do + ret = defined?([NonExistantConstant, AnotherNonExistantConstant]) + ret.should == nil + end + + end +end + +describe "The defined? keyword when called with a method name" do + describe "without a receiver" do + it "returns 'method' if the method is defined" do + defined?(puts).should == "method" + end + + it "returns nil if the method is not defined" do + defined?(defined_specs_undefined_method).should be_nil + end + + it "returns 'method' if the method is defined and private" do + obj = DefinedSpecs::Basic.new + obj.private_method_defined.should == "method" + end + + it "returns 'method' if the predicate method is defined and private" do + obj = DefinedSpecs::Basic.new + obj.private_predicate_defined.should == "method" + end + end + + describe "having a module as receiver" do + it "returns 'method' if the method is defined" do + defined?(Kernel.puts).should == "method" + end + + it "returns nil if the method is private" do + defined?(Object.print).should be_nil + end + + it "returns nil if the method is protected" do + defined?(DefinedSpecs::Basic.new.protected_method).should be_nil + end + + it "returns nil if the method is not defined" do + defined?(Kernel.defined_specs_undefined_method).should be_nil + end + + it "returns nil if the class is not defined" do + defined?(DefinedSpecsUndefined.puts).should be_nil + end + + it "returns nil if the subclass is not defined" do + defined?(DefinedSpecs::Undefined.puts).should be_nil + end + end + + describe "having a local variable as receiver" do + it "returns 'method' if the method is defined" do + obj = DefinedSpecs::Basic.new + defined?(obj.a_defined_method).should == "method" + end + + it "returns nil if the method is not defined" do + obj = DefinedSpecs::Basic.new + defined?(obj.an_undefined_method).should be_nil + end + + it "returns nil if the variable does not exist" do + defined?(nonexistent_local_variable.some_method).should be_nil + end + + it "calls #respond_to_missing?" do + obj = mock("respond_to_missing object") + obj.should_receive(:respond_to_missing?).and_return(true) + defined?(obj.something_undefined).should == "method" + end + end + + describe "having an instance variable as receiver" do + it "returns 'method' if the method is defined" do + @defined_specs_obj = DefinedSpecs::Basic.new + defined?(@defined_specs_obj.a_defined_method).should == "method" + end + + it "returns nil if the method is not defined" do + @defined_specs_obj = DefinedSpecs::Basic.new + defined?(@defined_specs_obj.an_undefined_method).should be_nil + end + + it "returns nil if the variable does not exist" do + defined?(@nonexistent_instance_variable.some_method).should be_nil + end + end + + describe "having a global variable as receiver" do + it "returns 'method' if the method is defined" do + $defined_specs_obj = DefinedSpecs::Basic.new + defined?($defined_specs_obj.a_defined_method).should == "method" + end + + it "returns nil if the method is not defined" do + $defined_specs_obj = DefinedSpecs::Basic.new + defined?($defined_specs_obj.an_undefined_method).should be_nil + end + + it "returns nil if the variable does not exist" do + defined?($nonexistent_global_variable.some_method).should be_nil + end + end + + describe "having a method call as a receiver" do + it "returns nil if evaluating the receiver raises an exception" do + defined?(DefinedSpecs.exception_method / 2).should be_nil + ScratchPad.recorded.should == :defined_specs_exception + end + + it "returns nil if the method is not defined on the object the receiver returns" do + defined?(DefinedSpecs.side_effects / 2).should be_nil + ScratchPad.recorded.should == :defined_specs_side_effects + end + + it "returns 'method' if the method is defined on the object the receiver returns" do + defined?(DefinedSpecs.fixnum_method / 2).should == "method" + ScratchPad.recorded.should == :defined_specs_fixnum_method + end + end +end + +describe "The defined? keyword for an expression" do + before :each do + ScratchPad.clear + end + + it "returns 'assignment' for assigning a local variable" do + defined?(x = 2).should == "assignment" + end + + it "returns 'assignment' for assigning an instance variable" do + defined?(@defined_specs_x = 2).should == "assignment" + end + + it "returns 'assignment' for assigning a global variable" do + defined?($defined_specs_x = 2).should == "assignment" + end + + it "returns 'assignment' for assigning a class variable" do + defined?(@@defined_specs_x = 2).should == "assignment" + end + + it "returns 'assignment' for assigning multiple variables" do + defined?((a, b = 1, 2)).should == "assignment" + end + + it "returns 'assignment' for an expression with '%='" do + defined?(x %= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '/='" do + defined?(x /= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '-='" do + defined?(x -= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '+='" do + defined?(x += 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '*='" do + defined?(x *= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '|='" do + defined?(x |= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '&='" do + defined?(x &= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '^='" do + defined?(x ^= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '~='" do + defined?(x = 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '<<='" do + defined?(x <<= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '>>='" do + defined?(x >>= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '||='" do + defined?(x ||= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '&&='" do + defined?(x &&= 2).should == "assignment" + end + + it "returns 'assignment' for an expression with '**='" do + defined?(x **= 2).should == "assignment" + end + + it "returns nil for an expression with == and an undefined method" do + defined?(defined_specs_undefined_method == 2).should be_nil + end + + it "returns nil for an expression with != and an undefined method" do + defined?(defined_specs_undefined_method != 2).should be_nil + end + + it "returns nil for an expression with !~ and an undefined method" do + defined?(defined_specs_undefined_method !~ 2).should be_nil + end + + it "returns 'method' for an expression with '=='" do + x = 42 + defined?(x == 2).should == "method" + end + + it "returns 'method' for an expression with '!='" do + x = 42 + defined?(x != 2).should == "method" + end + + it "returns 'method' for an expression with '!~'" do + x = 42 + defined?(x !~ 2).should == "method" + end + + describe "with logical connectives" do + it "returns nil for an expression with '!' and an undefined method" do + defined?(!defined_specs_undefined_method).should be_nil + end + + it "returns nil for an expression with '!' and an unset class variable" do + -> { + @result = defined?(!@@defined_specs_undefined_class_variable) + }.should complain(/class variable access from toplevel/) + @result.should be_nil + end + + it "returns nil for an expression with 'not' and an undefined method" do + defined?(not defined_specs_undefined_method).should be_nil + end + + it "returns nil for an expression with 'not' and an unset class variable" do + -> { + @result = defined?(not @@defined_specs_undefined_class_variable) + }.should complain(/class variable access from toplevel/) + @result.should be_nil + end + + it "does not propagate an exception raised by a method in a 'not' expression" do + defined?(not DefinedSpecs.exception_method).should be_nil + ScratchPad.recorded.should == :defined_specs_exception + end + + it "returns 'expression' for an expression with '&&/and' and an unset global variable" do + defined?($defined_specs_undefined_global_variable && true).should == "expression" + defined?(true && $defined_specs_undefined_global_variable).should == "expression" + defined?($defined_specs_undefined_global_variable and true).should == "expression" + end + + it "returns 'expression' for an expression with '&&/and' and an unset instance variable" do + defined?(@defined_specs_undefined_instance_variable && true).should == "expression" + defined?(true && @defined_specs_undefined_instance_variable).should == "expression" + defined?(@defined_specs_undefined_instance_variable and true).should == "expression" + end + + it "returns 'expression' for an expression '&&/and' regardless of its truth value" do + defined?(true && false).should == "expression" + defined?(true and false).should == "expression" + end + + it "returns 'expression' for an expression with '||/or' and an unset global variable" do + defined?($defined_specs_undefined_global_variable || true).should == "expression" + defined?(true || $defined_specs_undefined_global_variable).should == "expression" + defined?($defined_specs_undefined_global_variable or true).should == "expression" + end + + it "returns 'expression' for an expression with '||/or' and an unset instance variable" do + defined?(@defined_specs_undefined_instance_variable || true).should == "expression" + defined?(true || @defined_specs_undefined_instance_variable).should == "expression" + defined?(@defined_specs_undefined_instance_variable or true).should == "expression" + end + + it "returns 'expression' for an expression '||/or' regardless of its truth value" do + defined?(true || false).should == "expression" + defined?(true or false).should == "expression" + end + + it "returns nil for an expression with '!' and an unset global variable" do + defined?(!$defined_specs_undefined_global_variable).should be_nil + end + + it "returns nil for an expression with '!' and an unset instance variable" do + defined?(!@defined_specs_undefined_instance_variable).should be_nil + end + + it "returns 'method' for a 'not' expression with a method" do + defined?(not DefinedSpecs.side_effects).should == "method" + end + + it "calls a method in a 'not' expression and returns 'method'" do + defined?(not DefinedSpecs.side_effects).should == "method" + ScratchPad.recorded.should == :defined_specs_side_effects + end + + it "returns nil for an expression with 'not' and an unset global variable" do + defined?(not $defined_specs_undefined_global_variable).should be_nil + end + + it "returns nil for an expression with 'not' and an unset instance variable" do + defined?(not @defined_specs_undefined_instance_variable).should be_nil + end + + it "returns 'expression' for an expression with '&&/and' and an undefined method" do + defined?(defined_specs_undefined_method && true).should == "expression" + defined?(defined_specs_undefined_method and true).should == "expression" + end + + it "returns 'expression' for an expression with '&&/and' and an unset class variable" do + defined?(@@defined_specs_undefined_class_variable && true).should == "expression" + defined?(@@defined_specs_undefined_class_variable and true).should == "expression" + end + + it "does not call a method in an '&&' expression and returns 'expression'" do + defined?(DefinedSpecs.side_effects && true).should == "expression" + ScratchPad.recorded.should be_nil + end + + it "does not call a method in an 'and' expression and returns 'expression'" do + defined?(DefinedSpecs.side_effects and true).should == "expression" + ScratchPad.recorded.should be_nil + end + + it "returns 'expression' for an expression with '||/or' and an undefined method" do + defined?(defined_specs_undefined_method || true).should == "expression" + defined?(defined_specs_undefined_method or true).should == "expression" + end + + it "returns 'expression' for an expression with '||/or' and an unset class variable" do + defined?(@@defined_specs_undefined_class_variable || true).should == "expression" + defined?(@@defined_specs_undefined_class_variable or true).should == "expression" + end + + it "does not call a method in an '||' expression and returns 'expression'" do + defined?(DefinedSpecs.side_effects || true).should == "expression" + ScratchPad.recorded.should be_nil + end + + it "does not call a method in an 'or' expression and returns 'expression'" do + defined?(DefinedSpecs.side_effects or true).should == "expression" + ScratchPad.recorded.should be_nil + end + end + + it "returns 'expression' when passed a String" do + defined?("garble gooble gable").should == "expression" + end + + describe "with a dynamic String" do + it "returns 'expression' when the String contains a literal" do + defined?("garble #{42}").should == "expression" + end + + it "returns 'expression' when the String contains a call to a defined method" do + defined?("garble #{DefinedSpecs.side_effects}").should == "expression" + end + + it "returns 'expression' when the String contains a call to an undefined method" do + defined?("garble #{DefinedSpecs.undefined_method}").should == "expression" + end + + it "does not call the method in the String" do + defined?("garble #{DefinedSpecs.dynamic_string}").should == "expression" + ScratchPad.recorded.should be_nil + end + end + + describe "with a dynamic Regexp" do + it "returns 'expression' when the Regexp contains a literal" do + defined?(/garble #{42}/).should == "expression" + end + + it "returns 'expression' when the Regexp contains a call to a defined method" do + defined?(/garble #{DefinedSpecs.side_effects}/).should == "expression" + end + + it "returns 'expression' when the Regexp contains a call to an undefined method" do + defined?(/garble #{DefinedSpecs.undefined_method}/).should == "expression" + end + + it "does not call the method in the Regexp" do + defined?(/garble #{DefinedSpecs.dynamic_string}/).should == "expression" + ScratchPad.recorded.should be_nil + end + end + + it "returns 'expression' when passed a Fixnum literal" do + defined?(42).should == "expression" + end + + it "returns 'expression' when passed a Bignum literal" do + defined?(0xdead_beef_deed_feed).should == "expression" + end + + it "returns 'expression' when passed a Float literal" do + defined?(1.5).should == "expression" + end + + it "returns 'expression' when passed a Range literal" do + defined?(0..2).should == "expression" + end + + it "returns 'expression' when passed a Regexp literal" do + defined?(/undefined/).should == "expression" + end + + it "returns 'expression' when passed an Array literal" do + defined?([1, 2]).should == "expression" + end + + it "returns 'expression' when passed a Hash literal" do + defined?({a: :b}).should == "expression" + end + + it "returns 'expression' when passed a Symbol literal" do + defined?(:defined_specs).should == "expression" + end +end + +describe "The defined? keyword for variables" do + it "returns 'local-variable' when called with the name of a local variable" do + DefinedSpecs::Basic.new.local_variable_defined.should == "local-variable" + end + + it "returns 'local-variable' when called with the name of a local variable assigned to nil" do + DefinedSpecs::Basic.new.local_variable_defined_nil.should == "local-variable" + end + + it "returns nil for an instance variable that has not been read" do + DefinedSpecs::Basic.new.instance_variable_undefined.should be_nil + end + + it "returns nil for an instance variable that has been read but not assigned to" do + DefinedSpecs::Basic.new.instance_variable_read.should be_nil + end + + it "returns 'instance-variable' for an instance variable that has been assigned" do + DefinedSpecs::Basic.new.instance_variable_defined.should == "instance-variable" + end + + it "returns 'instance-variable' for an instance variable that has been assigned to nil" do + DefinedSpecs::Basic.new.instance_variable_defined_nil.should == "instance-variable" + end + + it "returns nil for a global variable that has not been read" do + DefinedSpecs::Basic.new.global_variable_undefined.should be_nil + end + + it "returns nil for a global variable that has been read but not assigned to" do + DefinedSpecs::Basic.new.global_variable_read.should be_nil + end + + # MRI appears to special case defined? for $! and $~ in that it returns + # 'global-variable' even when they are not set (or they are always "set" + # but the value may be nil). In other words, 'defined?($~)' will return + # 'global-variable' even if no match has been done. + + it "returns 'global-variable' for $!" do + defined?($!).should == "global-variable" + end + + it "returns 'global-variable for $~" do + defined?($~).should == "global-variable" + end + + describe "when a String does not match a Regexp" do + before :each do + "mis-matched" =~ /z(z)z/ + end + + it "returns 'global-variable' for $~" do + defined?($~).should == "global-variable" + end + + it "returns nil for $&" do + defined?($&).should be_nil + end + + it "returns nil for $`" do + defined?($`).should be_nil + end + + it "returns nil for $'" do + defined?($').should be_nil + end + + it "returns nil for $+" do + defined?($+).should be_nil + end + + it "returns nil for $1-$9" do + defined?($1).should be_nil + defined?($2).should be_nil + defined?($3).should be_nil + defined?($4).should be_nil + defined?($5).should be_nil + defined?($6).should be_nil + defined?($7).should be_nil + defined?($8).should be_nil + defined?($9).should be_nil + end + end + + describe "when a String matches a Regexp" do + before :each do + "mis-matched" =~ /s(-)m(.)/ + end + + it "returns 'global-variable' for $~" do + defined?($~).should == "global-variable" + end + + it "returns 'global-variable' for $&" do + defined?($&).should == "global-variable" + end + + it "returns 'global-variable' for $`" do + defined?($`).should == "global-variable" + end + + it "returns 'global-variable' for $'" do + defined?($').should == "global-variable" + end + + it "returns 'global-variable' for $+" do + defined?($+).should == "global-variable" + end + + it "returns 'global-variable' for the capture references" do + defined?($1).should == "global-variable" + defined?($2).should == "global-variable" + end + + it "returns nil for non-captures" do + defined?($3).should be_nil + defined?($4).should be_nil + defined?($5).should be_nil + defined?($6).should be_nil + defined?($7).should be_nil + defined?($8).should be_nil + defined?($9).should be_nil + end + end + + describe "when a Regexp does not match a String" do + before :each do + /z(z)z/ =~ "mis-matched" + end + + it "returns 'global-variable' for $~" do + defined?($~).should == "global-variable" + end + + it "returns nil for $&" do + defined?($&).should be_nil + end + + it "returns nil for $`" do + defined?($`).should be_nil + end + + it "returns nil for $'" do + defined?($').should be_nil + end + + it "returns nil for $+" do + defined?($+).should be_nil + end + + it "returns nil for $1-$9" do + defined?($1).should be_nil + defined?($2).should be_nil + defined?($3).should be_nil + defined?($4).should be_nil + defined?($5).should be_nil + defined?($6).should be_nil + defined?($7).should be_nil + defined?($8).should be_nil + defined?($9).should be_nil + end + end + + describe "when a Regexp matches a String" do + before :each do + /s(-)m(.)/ =~ "mis-matched" + end + + it "returns 'global-variable' for $~" do + defined?($~).should == "global-variable" + end + + it "returns 'global-variable' for $&" do + defined?($&).should == "global-variable" + end + + it "returns 'global-variable' for $`" do + defined?($`).should == "global-variable" + end + + it "returns 'global-variable' for $'" do + defined?($').should == "global-variable" + end + + it "returns 'global-variable' for $+" do + defined?($+).should == "global-variable" + end + + it "returns 'global-variable' for the capture references" do + defined?($1).should == "global-variable" + defined?($2).should == "global-variable" + end + + it "returns nil for non-captures" do + defined?($3).should be_nil + defined?($4).should be_nil + defined?($5).should be_nil + defined?($6).should be_nil + defined?($7).should be_nil + defined?($8).should be_nil + defined?($9).should be_nil + end + end + it "returns 'global-variable' for a global variable that has been assigned" do + DefinedSpecs::Basic.new.global_variable_defined.should == "global-variable" + end + + it "returns nil for a class variable that has not been read" do + DefinedSpecs::Basic.new.class_variable_undefined.should be_nil + end + + # There is no spec for a class variable that is read before being assigned + # to because setting up the code for this raises a NameError before you + # get to the defined? call so it really has nothing to do with 'defined?'. + + it "returns 'class variable' when called with the name of a class variable" do + DefinedSpecs::Basic.new.class_variable_defined.should == "class variable" + end + + it "returns 'local-variable' when called with the name of a block local" do + block = Proc.new { |xxx| defined?(xxx) } + block.call(1).should == "local-variable" + end +end + +describe "The defined? keyword for a simple constant" do + it "returns 'constant' when the constant is defined" do + defined?(DefinedSpecs).should == "constant" + end + + it "returns nil when the constant is not defined" do + defined?(DefinedSpecsUndefined).should be_nil + end + + it "does not call Object.const_missing if the constant is not defined" do + Object.should_not_receive(:const_missing) + defined?(DefinedSpecsUndefined).should be_nil + end + + it "returns 'constant' for an included module" do + DefinedSpecs::Child.module_defined.should == "constant" + end + + it "returns 'constant' for a constant defined in an included module" do + DefinedSpecs::Child.module_constant_defined.should == "constant" + end +end + +describe "The defined? keyword for a top-level constant" do + it "returns 'constant' when passed the name of a top-level constant" do + defined?(::DefinedSpecs).should == "constant" + end + + it "retuns nil if the constant is not defined" do + defined?(::DefinedSpecsUndefined).should be_nil + end + + it "does not call Object.const_missing if the constant is not defined" do + Object.should_not_receive(:const_missing) + defined?(::DefinedSpecsUndefined).should be_nil + end +end + +describe "The defined? keyword for a scoped constant" do + it "returns 'constant' when the scoped constant is defined" do + defined?(DefinedSpecs::Basic).should == "constant" + end + + it "returns nil when the scoped constant is not defined" do + defined?(DefinedSpecs::Undefined).should be_nil + end + + it "does not call .const_missing if the constant is not defined" do + DefinedSpecs.should_not_receive(:const_missing) + defined?(DefinedSpecs::UnknownChild).should be_nil + end + + it "returns nil when an undefined constant is scoped to a defined constant" do + defined?(DefinedSpecs::Child::B).should be_nil + end + + it "returns nil when a constant is scoped to an undefined constant" do + Object.should_not_receive(:const_missing) + defined?(Undefined::Object).should be_nil + end + + it "returns nil when the undefined constant is scoped to an undefined constant" do + defined?(DefinedSpecs::Undefined::Undefined).should be_nil + end + + it "returns nil when a constant is defined on top-level but not on the module" do + defined?(DefinedSpecs::String).should be_nil + end + + it "returns 'constant' when a constant is defined on top-level but not on the class" do + defined?(DefinedSpecs::Basic::String).should == "constant" + end + + it "returns 'constant' if the scoped-scoped constant is defined" do + defined?(DefinedSpecs::Child::A).should == "constant" + end +end + +describe "The defined? keyword for a top-level scoped constant" do + it "returns 'constant' when the scoped constant is defined" do + defined?(::DefinedSpecs::Basic).should == "constant" + end + + it "returns nil when the scoped constant is not defined" do + defined?(::DefinedSpecs::Undefined).should be_nil + end + + it "returns nil when an undefined constant is scoped to a defined constant" do + defined?(::DefinedSpecs::Child::B).should be_nil + end + + it "returns nil when the undefined constant is scoped to an undefined constant" do + defined?(::DefinedSpecs::Undefined::Undefined).should be_nil + end + + it "returns 'constant' if the scoped-scoped constant is defined" do + defined?(::DefinedSpecs::Child::A).should == "constant" + end +end + +describe "The defined? keyword for a self-send method call scoped constant" do + it "returns nil if the constant is not defined in the scope of the method's value" do + defined?(defined_specs_method::Undefined).should be_nil + end + + it "returns 'constant' if the constant is defined in the scope of the method's value" do + defined?(defined_specs_method::Basic).should == "constant" + end + + it "returns nil if the last constant is not defined in the scope chain" do + defined?(defined_specs_method::Basic::Undefined).should be_nil + end + + it "returns nil if the middle constant is not defined in the scope chain" do + defined?(defined_specs_method::Undefined::Undefined).should be_nil + end + + it "returns 'constant' if all the constants in the scope chain are defined" do + defined?(defined_specs_method::Basic::A).should == "constant" + end +end + +describe "The defined? keyword for a receiver method call scoped constant" do + it "returns nil if the constant is not defined in the scope of the method's value" do + defined?(defined_specs_receiver.defined_method::Undefined).should be_nil + end + + it "returns 'constant' if the constant is defined in the scope of the method's value" do + defined?(defined_specs_receiver.defined_method::Basic).should == "constant" + end + + it "returns nil if the last constant is not defined in the scope chain" do + defined?(defined_specs_receiver.defined_method::Basic::Undefined).should be_nil + end + + it "returns nil if the middle constant is not defined in the scope chain" do + defined?(defined_specs_receiver.defined_method::Undefined::Undefined).should be_nil + end + + it "returns 'constant' if all the constants in the scope chain are defined" do + defined?(defined_specs_receiver.defined_method::Basic::A).should == "constant" + end +end + +describe "The defined? keyword for a module method call scoped constant" do + it "returns nil if the constant is not defined in the scope of the method's value" do + defined?(DefinedSpecs.defined_method::Undefined).should be_nil + end + + it "returns 'constant' if the constant scoped by the method's value is defined" do + defined?(DefinedSpecs.defined_method::Basic).should == "constant" + end + + it "returns nil if the last constant in the scope chain is not defined" do + defined?(DefinedSpecs.defined_method::Basic::Undefined).should be_nil + end + + it "returns nil if the middle constant in the scope chain is not defined" do + defined?(DefinedSpecs.defined_method::Undefined::Undefined).should be_nil + end + + it "returns 'constant' if all the constants in the scope chain are defined" do + defined?(DefinedSpecs.defined_method::Basic::A).should == "constant" + end + + it "returns nil if the outer scope constant in the receiver is not defined" do + defined?(Undefined::DefinedSpecs.defined_method::Basic).should be_nil + end + + it "returns nil if the scoped constant in the receiver is not defined" do + defined?(DefinedSpecs::Undefined.defined_method::Basic).should be_nil + end + + it "returns 'constant' if all the constants in the receiver are defined" do + defined?(Object::DefinedSpecs.defined_method::Basic).should == "constant" + end + + it "returns 'constant' if all the constants in the receiver and scope chain are defined" do + defined?(Object::DefinedSpecs.defined_method::Basic::A).should == "constant" + end +end + +describe "The defined? keyword for a variable scoped constant" do + after :all do + Object.__send__(:remove_class_variable, :@@defined_specs_obj) + end + + it "returns nil if the scoped constant is not defined" do + @defined_specs_obj = DefinedSpecs::Basic + defined?(@defined_specs_obj::Undefined).should be_nil + end + + it "returns 'constant' if the constant is defined in the scope of the variable reference" do + @defined_specs_obj = DefinedSpecs::Basic + defined?(@defined_specs_obj::A).should == "constant" + end + + it "returns nil if the scoped constant is not defined" do + $defined_specs_obj = DefinedSpecs::Basic + defined?($defined_specs_obj::Undefined).should be_nil + end + + it "returns 'constant' if the constant is defined in the scope of the variable reference" do + $defined_specs_obj = DefinedSpecs::Basic + defined?($defined_specs_obj::A).should == "constant" + end + + it "returns nil if the scoped constant is not defined" do + -> { + @@defined_specs_obj = DefinedSpecs::Basic + defined?(@@defined_specs_obj::Undefined).should be_nil + }.should complain(/class variable access from toplevel/) + end + + it "returns 'constant' if the constant is defined in the scope of the variable reference" do + -> { + @@defined_specs_obj = DefinedSpecs::Basic + defined?(@@defined_specs_obj::A).should == "constant" + }.should complain(/class variable access from toplevel/) + end + + it "returns nil if the scoped constant is not defined" do + defined_specs_obj = DefinedSpecs::Basic + defined?(defined_specs_obj::Undefined).should be_nil + end + + it "returns 'constant' if the constant is defined in the scope of the variable reference" do + defined_specs_obj = DefinedSpecs::Basic + defined?(defined_specs_obj::A).should == "constant" + end +end + +describe "The defined? keyword for a self:: scoped constant" do + it "returns 'constant' for a constant explicitly scoped to self:: when set" do + defined?(DefinedSpecs::SelfScoped).should == "constant" + end + + it "returns 'constant' for a constant explicitly scoped to self:: in subclass's metaclass" do + DefinedSpecs::Child.parent_constant_defined.should == "constant" + end +end + +describe "The defined? keyword for yield" do + it "returns nil if no block is passed to a method not taking a block parameter" do + DefinedSpecs::Basic.new.no_yield_block.should be_nil + end + + it "returns nil if no block is passed to a method taking a block parameter" do + DefinedSpecs::Basic.new.no_yield_block_parameter.should be_nil + end + + it "returns 'yield' if a block is passed to a method not taking a block parameter" do + DefinedSpecs::Basic.new.yield_block.should == "yield" + end + + it "returns 'yield' if a block is passed to a method taking a block parameter" do + DefinedSpecs::Basic.new.yield_block_parameter.should == "yield" + end +end + +describe "The defined? keyword for super" do + it "returns nil when a superclass undef's the method" do + DefinedSpecs::ClassWithoutMethod.new.test.should be_nil + end + + describe "for a method taking no arguments" do + it "returns nil when no superclass method exists" do + DefinedSpecs::Super.new.no_super_method_no_args.should be_nil + end + + it "returns nil from a block when no superclass method exists" do + DefinedSpecs::Super.new.no_super_method_block_no_args.should be_nil + end + + it "returns nil from a #define_method when no superclass method exists" do + DefinedSpecs::Super.new.no_super_define_method_no_args.should be_nil + end + + it "returns nil from a block in a #define_method when no superclass method exists" do + DefinedSpecs::Super.new.no_super_define_method_block_no_args.should be_nil + end + + it "returns 'super' when a superclass method exists" do + DefinedSpecs::Super.new.method_no_args.should == "super" + end + + it "returns 'super' from a block when a superclass method exists" do + DefinedSpecs::Super.new.method_block_no_args.should == "super" + end + + it "returns 'super' from a #define_method when a superclass method exists" do + DefinedSpecs::Super.new.define_method_no_args.should == "super" + end + + it "returns 'super' from a block in a #define_method when a superclass method exists" do + DefinedSpecs::Super.new.define_method_block_no_args.should == "super" + end + + it "returns 'super' when the method exists in a supermodule" do + DefinedSpecs::SuperWithIntermediateModules.new.method_no_args.should == "super" + end + end + + describe "for a method taking arguments" do + it "returns nil when no superclass method exists" do + DefinedSpecs::Super.new.no_super_method_args.should be_nil + end + + it "returns nil from a block when no superclass method exists" do + DefinedSpecs::Super.new.no_super_method_block_args.should be_nil + end + + it "returns nil from a #define_method when no superclass method exists" do + DefinedSpecs::Super.new.no_super_define_method_args.should be_nil + end + + it "returns nil from a block in a #define_method when no superclass method exists" do + DefinedSpecs::Super.new.no_super_define_method_block_args.should be_nil + end + + it "returns 'super' when a superclass method exists" do + DefinedSpecs::Super.new.method_args.should == "super" + end + + it "returns 'super' from a block when a superclass method exists" do + DefinedSpecs::Super.new.method_block_args.should == "super" + end + + it "returns 'super' from a #define_method when a superclass method exists" do + DefinedSpecs::Super.new.define_method_args.should == "super" + end + + it "returns 'super' from a block in a #define_method when a superclass method exists" do + DefinedSpecs::Super.new.define_method_block_args.should == "super" + end + end + + describe "within an included module's method" do + it "returns 'super' when a superclass method exists in the including hierarchy" do + DefinedSpecs::Child.new.defined_super.should == "super" + end + end +end + + +describe "The defined? keyword for instance variables" do + it "returns 'instance-variable' if assigned" do + @assigned_ivar = "some value" + defined?(@assigned_ivar).should == "instance-variable" + end + + it "returns nil if not assigned" do + defined?(@unassigned_ivar).should be_nil + end +end + +describe "The defined? keyword for pseudo-variables" do + it "returns 'expression' for __FILE__" do + defined?(__FILE__).should == "expression" + end + + it "returns 'expression' for __LINE__" do + defined?(__LINE__).should == "expression" + end + + it "returns 'expression' for __ENCODING__" do + defined?(__ENCODING__).should == "expression" + end +end + +describe "The defined? keyword for conditional expressions" do + it "returns 'expression' for an 'if' conditional" do + defined?(if x then 'x' else '' end).should == "expression" + end + + it "returns 'expression' for an 'unless' conditional" do + defined?(unless x then '' else 'x' end).should == "expression" + end + + it "returns 'expression' for ternary expressions" do + defined?(x ? 'x' : '').should == "expression" + end +end + +describe "The defined? keyword for case expressions" do + it "returns 'expression'" do + defined?(case x; when 'x'; 'y' end).should == "expression" + end +end + +describe "The defined? keyword for loop expressions" do + it "returns 'expression' for a 'for' expression" do + defined?(for n in 1..3 do true end).should == "expression" + end + + it "returns 'expression' for a 'while' expression" do + defined?(while x do y end).should == "expression" + end + + it "returns 'expression' for an 'until' expression" do + defined?(until x do y end).should == "expression" + end + + it "returns 'expression' for a 'break' expression" do + defined?(break).should == "expression" + end + + it "returns 'expression' for a 'next' expression" do + defined?(next).should == "expression" + end + + it "returns 'expression' for a 'redo' expression" do + defined?(redo).should == "expression" + end + + it "returns 'expression' for a 'retry' expression" do + defined?(retry).should == "expression" + end +end + +describe "The defined? keyword for return expressions" do + it "returns 'expression'" do + defined?(return).should == "expression" + end +end + +describe "The defined? keyword for exception expressions" do + it "returns 'expression'" do + defined?(begin 1 end).should == "expression" + end +end diff --git a/spec/rubyspec/language/encoding_spec.rb b/spec/rubyspec/language/encoding_spec.rb new file mode 100644 index 0000000000..070fa52bba --- /dev/null +++ b/spec/rubyspec/language/encoding_spec.rb @@ -0,0 +1,36 @@ +# -*- encoding: us-ascii -*- +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/coding_us_ascii', __FILE__) +require File.expand_path('../fixtures/coding_utf_8', __FILE__) + +describe "The __ENCODING__ pseudo-variable" do + it "is an instance of Encoding" do + __ENCODING__.should be_kind_of(Encoding) + end + + it "is US-ASCII by default" do + __ENCODING__.should == Encoding::US_ASCII + end + + it "is the evaluated strings's one inside an eval" do + eval("__ENCODING__".force_encoding("US-ASCII")).should == Encoding::US_ASCII + eval("__ENCODING__".force_encoding("ASCII-8BIT")).should == Encoding::ASCII_8BIT + end + + it "is the encoding specified by a magic comment inside an eval" do + code = "# encoding: ASCII-8BIT\n__ENCODING__".force_encoding("US-ASCII") + eval(code).should == Encoding::ASCII_8BIT + + code = "# encoding: us-ascii\n__ENCODING__".force_encoding("ASCII-8BIT") + eval(code).should == Encoding::US_ASCII + end + + it "is the encoding specified by a magic comment in the file" do + CodingUS_ASCII.encoding.should == Encoding::US_ASCII + CodingUTF_8.encoding.should == Encoding::UTF_8 + end + + it "raises a SyntaxError if assigned to" do + lambda { eval("__ENCODING__ = 1") }.should raise_error(SyntaxError) + end +end diff --git a/spec/rubyspec/language/ensure_spec.rb b/spec/rubyspec/language/ensure_spec.rb new file mode 100644 index 0000000000..13575fccc5 --- /dev/null +++ b/spec/rubyspec/language/ensure_spec.rb @@ -0,0 +1,126 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/ensure', __FILE__) + +describe "An ensure block inside a begin block" do + before :each do + ScratchPad.record [] + end + + it "is executed when an exception is raised in it's corresponding begin block" do + begin + lambda { + begin + ScratchPad << :begin + raise "An exception occured!" + ensure + ScratchPad << :ensure + end + }.should raise_error(RuntimeError) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + it "is executed when an exception is raised and rescued in it's corresponding begin block" do + begin + begin + ScratchPad << :begin + raise "An exception occured!" + rescue + ScratchPad << :rescue + ensure + ScratchPad << :ensure + end + + ScratchPad.recorded.should == [:begin, :rescue, :ensure] + end + end + + it "is executed even when a symbol is thrown in it's corresponding begin block" do + begin + catch(:symbol) do + begin + ScratchPad << :begin + throw(:symbol) + rescue + ScratchPad << :rescue + ensure + ScratchPad << :ensure + end + end + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + it "is executed when nothing is raised or thrown in it's corresponding begin block" do + begin + ScratchPad << :begin + rescue + ScratchPad << :rescue + ensure + ScratchPad << :ensure + end + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "has no return value" do + begin + :begin + ensure + :ensure + end.should == :begin + end +end + +describe "The value of an ensure expression," do + it "in no-exception scenarios, is the value of the last statement of the protected body" do + begin + v = 1 + eval('x=1') # to prevent opts from triggering + v + ensure + v = 2 + end.should == 1 + end + + it "when an exception is rescued, is the value of the rescuing block" do + begin + raise 'foo' + rescue + v = 3 + ensure + v = 2 + end.should == 3 + end +end + +describe "An ensure block inside a method" do + before :each do + @obj = EnsureSpec::Container.new + end + + it "is executed when an exception is raised in the method" do + lambda { @obj.raise_in_method_with_ensure }.should raise_error(RuntimeError) + @obj.executed.should == [:method, :ensure] + end + + it "is executed when an exception is raised and rescued in the method" do + @obj.raise_and_rescue_in_method_with_ensure + @obj.executed.should == [:method, :rescue, :ensure] + end + + it "is executed even when a symbol is thrown in the method" do + catch(:symbol) { @obj.throw_in_method_with_ensure } + @obj.executed.should == [:method, :ensure] + end + + it "has no impact on the method's implicit return value" do + @obj.implicit_return_in_method_with_ensure.should == :method + end + + it "has an impact on the method's explicit return value" do + @obj.explicit_return_in_method_with_ensure.should == :ensure + end +end diff --git a/spec/rubyspec/language/execution_spec.rb b/spec/rubyspec/language/execution_spec.rb new file mode 100644 index 0000000000..3e6e7ff48c --- /dev/null +++ b/spec/rubyspec/language/execution_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "``" do + it "returns the output of the executed sub-process" do + ip = 'world' + `echo disc #{ip}`.should == "disc world\n" + end +end + +describe "%x" do + it "is the same as ``" do + ip = 'world' + %x(echo disc #{ip}).should == "disc world\n" + end +end diff --git a/spec/rubyspec/language/file_spec.rb b/spec/rubyspec/language/file_spec.rb new file mode 100644 index 0000000000..409400ca83 --- /dev/null +++ b/spec/rubyspec/language/file_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/code_loading', __FILE__) +require File.expand_path('../shared/__FILE__', __FILE__) + +describe "The __FILE__ pseudo-variable" do + it "raises a SyntaxError if assigned to" do + lambda { eval("__FILE__ = 1") }.should raise_error(SyntaxError) + end + + it "equals (eval) inside an eval" do + eval("__FILE__").should == "(eval)" + end +end + +describe "The __FILE__ pseudo-variable" do + it_behaves_like :language___FILE__, :require, CodeLoadingSpecs::Method.new +end + +describe "The __FILE__ pseudo-variable" do + it_behaves_like :language___FILE__, :require, Kernel +end + +describe "The __FILE__ pseudo-variable" do + it_behaves_like :language___FILE__, :load, CodeLoadingSpecs::Method.new +end + +describe "The __FILE__ pseudo-variable" do + it_behaves_like :language___FILE__, :load, Kernel +end diff --git a/spec/rubyspec/language/fixtures/argv_encoding.rb b/spec/rubyspec/language/fixtures/argv_encoding.rb new file mode 100644 index 0000000000..8192b2d9a0 --- /dev/null +++ b/spec/rubyspec/language/fixtures/argv_encoding.rb @@ -0,0 +1 @@ +p ARGV.map { |a| a.encoding.name } diff --git a/spec/rubyspec/language/fixtures/array.rb b/spec/rubyspec/language/fixtures/array.rb new file mode 100644 index 0000000000..4d8ce74ed6 --- /dev/null +++ b/spec/rubyspec/language/fixtures/array.rb @@ -0,0 +1,11 @@ +module ArraySpec + class Splat + def unpack_3args(a, b, c) + [a, b, c] + end + + def unpack_4args(a, b, c, d) + [a, b, c, d] + end + end +end diff --git a/spec/rubyspec/language/fixtures/block.rb b/spec/rubyspec/language/fixtures/block.rb new file mode 100644 index 0000000000..9848d18776 --- /dev/null +++ b/spec/rubyspec/language/fixtures/block.rb @@ -0,0 +1,57 @@ +module BlockSpecs + class Yielder + def z + yield + end + + def m(*a) + yield(*a) + end + + def s(a) + yield(a) + end + + def r(a) + yield(*a) + end + end + + # TODO: rewrite all specs that use Yield to use Yielder + + class Yield + def splat(*args) + yield(*args) + end + + def two_args + yield 1, 2 + end + + def two_arg_array + yield [1, 2] + end + + def yield_splat_inside_block + [1, 2].send(:each_with_index) {|*args| yield(*args)} + end + + def yield_this(obj) + yield obj + end + end + + class OverwriteBlockVariable + def initialize + @y = Yielder.new + end + + def method_missing(method, *args, &block) + self.class.send :define_method, method do |*a, &b| + @y.send method, *a, &b + end + + send method, *args, &block + end + end +end diff --git a/spec/rubyspec/language/fixtures/break.rb b/spec/rubyspec/language/fixtures/break.rb new file mode 100644 index 0000000000..165d3395c3 --- /dev/null +++ b/spec/rubyspec/language/fixtures/break.rb @@ -0,0 +1,263 @@ +module BreakSpecs + class Driver + def initialize(ensures=false) + @ensures = ensures + end + + def note(value) + ScratchPad << value + end + end + + class Block < Driver + def break_nil + note :a + note yielding { + note :b + break + note :c + } + note :d + end + + def break_value + note :a + note yielding { + note :b + break :break + note :c + } + note :d + end + + def yielding + note :aa + note yield + note :bb + end + + def create_block + note :za + b = capture_block do + note :zb + break :break + note :zc + end + note :zd + b + end + + def capture_block(&b) + note :xa + b + end + + def break_in_method_captured + note :a + create_block.call + note :b + end + + def break_in_yield_captured + note :a + yielding(&create_block) + note :b + end + + def break_in_method + note :a + b = capture_block { + note :b + break :break + note :c + } + note :d + note b.call + note :e + end + + def call_method(b) + note :aa + note b.call + note :bb + end + + def break_in_nested_method + note :a + b = capture_block { + note :b + break :break + note :c + } + note :c + note call_method(b) + note :d + end + + def break_in_yielding_method + note :a + b = capture_block { + note :b + break :break + note :c + } + note :c + note yielding(&b) + note :d + end + + def method(v) + yield v + end + + def invoke_yield_in_while + looping = true + while looping + note :aa + yield + note :bb + looping = false + end + note :should_not_reach_here + end + + def break_in_block_in_while + invoke_yield_in_while do + note :break + break :value + note :c + end + end + end + + class Lambda < Driver + # Cases for the invocation of the scope defining the lambda still active + # on the call stack when the lambda is invoked. + def break_in_defining_scope(value=true) + note :a + note lambda { + note :b + if value + break :break + else + break + end + note :c + }.call + note :d + end + + def break_in_nested_scope + note :a + l = lambda do + note :b + break :break + note :c + end + note :d + + invoke_lambda l + + note :e + end + + def invoke_lambda(l) + note :aa + note l.call + note :bb + end + + def break_in_nested_scope_yield + note :a + l = lambda do + note :b + break :break + note :c + end + note :d + + invoke_yield(&l) + + note :e + end + + def note_invoke_yield + note :aa + note yield + note :bb + end + + def break_in_nested_scope_block + note :a + l = lambda do + note :b + break :break + note :c + end + note :d + + invoke_lambda_block l + + note :e + end + + def invoke_yield + note :aaa + yield + note :bbb + end + + def invoke_lambda_block(b) + note :aa + invoke_yield do + note :bb + + note b.call + + note :cc + end + note :dd + end + + # Cases for the invocation of the scope defining the lambda NOT still + # active on the call stack when the lambda is invoked. + def create_lambda + note :la + l = lambda do + note :lb + break :break + note :lc + end + note :ld + l + end + + def break_in_method + note :a + + note create_lambda.call + + note :b + end + + def break_in_block_in_method + note :a + invoke_yield do + note :b + + note create_lambda.call + + note :c + end + note :d + end + + def break_in_method_yield + note :a + + invoke_yield(&create_lambda) + + note :b + end + end +end diff --git a/spec/rubyspec/language/fixtures/break_lambda_toplevel.rb b/spec/rubyspec/language/fixtures/break_lambda_toplevel.rb new file mode 100644 index 0000000000..05af1d3fdc --- /dev/null +++ b/spec/rubyspec/language/fixtures/break_lambda_toplevel.rb @@ -0,0 +1,9 @@ +print "a," + +print lambda { + print "b," + break "break," + print "c," +}.call + +puts "d" diff --git a/spec/rubyspec/language/fixtures/break_lambda_toplevel_block.rb b/spec/rubyspec/language/fixtures/break_lambda_toplevel_block.rb new file mode 100644 index 0000000000..a35cb8a8a1 --- /dev/null +++ b/spec/rubyspec/language/fixtures/break_lambda_toplevel_block.rb @@ -0,0 +1,23 @@ +print "a," + +l = lambda { + print "b," + break "break," + print "c," +} + +def a(l) + print "d," + b { l.call } + print "e," +end + +def b + print "f," + print yield + print "g," +end + +a(l) + +puts "h" diff --git a/spec/rubyspec/language/fixtures/break_lambda_toplevel_method.rb b/spec/rubyspec/language/fixtures/break_lambda_toplevel_method.rb new file mode 100644 index 0000000000..200040d614 --- /dev/null +++ b/spec/rubyspec/language/fixtures/break_lambda_toplevel_method.rb @@ -0,0 +1,17 @@ +print "a," + +l = lambda { + print "b," + break "break," + print "c," +} + +def a(l) + print "d," + print l.call + print "e," +end + +a(l) + +puts "f" diff --git a/spec/rubyspec/language/fixtures/classes.rb b/spec/rubyspec/language/fixtures/classes.rb new file mode 100644 index 0000000000..eb239e1e29 --- /dev/null +++ b/spec/rubyspec/language/fixtures/classes.rb @@ -0,0 +1,31 @@ +module LanguageSpecs + # Regexp support + + def self.paired_delimiters + [%w[( )], %w[{ }], %w[< >], ["[", "]"]] + end + + def self.non_paired_delimiters + %w[~ ! # $ % ^ & * _ + ` - = " ' , . ? / | \\] + end + + def self.blanks + " \t" + end + + def self.white_spaces + return blanks + "\f\n\r\v" + end + + def self.non_alphanum_non_space + '~!@#$%^&*()+-\|{}[]:";\'<>?,./' + end + + def self.punctuations + ",.?" # TODO - Need to fill in the full list + end + + def self.get_regexp_with_substitution o + /#{o}/o + end +end diff --git a/spec/rubyspec/language/fixtures/coding_us_ascii.rb b/spec/rubyspec/language/fixtures/coding_us_ascii.rb new file mode 100644 index 0000000000..7df66109d7 --- /dev/null +++ b/spec/rubyspec/language/fixtures/coding_us_ascii.rb @@ -0,0 +1,11 @@ +# encoding: us-ascii + +module CodingUS_ASCII + def self.encoding + __ENCODING__ + end + + def self.string_literal + "string literal" + end +end diff --git a/spec/rubyspec/language/fixtures/coding_utf_8.rb b/spec/rubyspec/language/fixtures/coding_utf_8.rb new file mode 100644 index 0000000000..3d8e1d9a34 --- /dev/null +++ b/spec/rubyspec/language/fixtures/coding_utf_8.rb @@ -0,0 +1,11 @@ +# encoding: utf-8 + +module CodingUTF_8 + def self.encoding + __ENCODING__ + end + + def self.string_literal + "string literal" + end +end diff --git a/spec/rubyspec/language/fixtures/constant_visibility.rb b/spec/rubyspec/language/fixtures/constant_visibility.rb new file mode 100644 index 0000000000..022554430e --- /dev/null +++ b/spec/rubyspec/language/fixtures/constant_visibility.rb @@ -0,0 +1,98 @@ +module ConstantVisibility + module ModuleContainer + module PrivateModule + end + private_constant :PrivateModule + + class PrivateClass + end + private_constant :PrivateClass + end + + class ClassContainer + module PrivateModule + end + private_constant :PrivateModule + + class PrivateClass + end + private_constant :PrivateClass + end + + module PrivConstModule + PRIVATE_CONSTANT_MODULE = true + private_constant :PRIVATE_CONSTANT_MODULE + + def self.private_constant_from_self + PRIVATE_CONSTANT_MODULE + end + + def self.defined_from_self + defined? PRIVATE_CONSTANT_MODULE + end + + module Nested + def self.private_constant_from_scope + PRIVATE_CONSTANT_MODULE + end + + def self.defined_from_scope + defined? PRIVATE_CONSTANT_MODULE + end + end + end + + class PrivConstClass + PRIVATE_CONSTANT_CLASS = true + private_constant :PRIVATE_CONSTANT_CLASS + + def self.private_constant_from_self + PRIVATE_CONSTANT_CLASS + end + + def self.defined_from_self + defined? PRIVATE_CONSTANT_CLASS + end + + module Nested + def self.private_constant_from_scope + PRIVATE_CONSTANT_CLASS + end + + def self.defined_from_scope + defined? PRIVATE_CONSTANT_CLASS + end + end + end + + class PrivConstModuleChild + include PrivConstModule + + def private_constant_from_include + PRIVATE_CONSTANT_MODULE + end + + def defined_from_include + defined? PRIVATE_CONSTANT_MODULE + end + end + + class PrivConstClassChild < PrivConstClass + def private_constant_from_subclass + PRIVATE_CONSTANT_CLASS + end + + def defined_from_subclass + defined? PRIVATE_CONSTANT_CLASS + end + end + + def self.reset_private_constants + Object.send :private_constant, :PRIVATE_CONSTANT_IN_OBJECT + end +end + +class Object + PRIVATE_CONSTANT_IN_OBJECT = true + private_constant :PRIVATE_CONSTANT_IN_OBJECT +end diff --git a/spec/rubyspec/language/fixtures/constants_sclass.rb b/spec/rubyspec/language/fixtures/constants_sclass.rb new file mode 100644 index 0000000000..21dc4081e2 --- /dev/null +++ b/spec/rubyspec/language/fixtures/constants_sclass.rb @@ -0,0 +1,54 @@ +module ConstantSpecs + + CS_SINGLETON1 = Object.new + class << CS_SINGLETON1 + CONST = 1 + def foo + CONST + end + end + + CS_SINGLETON2 = [Object.new, Object.new] + 2.times do |i| + obj = CS_SINGLETON2[i] + $spec_i = i + class << obj + CONST = ($spec_i + 1) + def foo + CONST + end + end + end + + CS_SINGLETON3 = [Object.new, Object.new] + 2.times do |i| + obj = CS_SINGLETON3[i] + class << obj + class X + # creates + end + + def x + X + end + end + end + + CS_SINGLETON4 = [Object.new, Object.new] + CS_SINGLETON4_CLASSES = [] + 2.times do |i| + obj = CS_SINGLETON4[i] + $spec_i = i + class << obj + class X + CS_SINGLETON4_CLASSES << self + CONST = ($spec_i + 1) + + def foo + CONST + end + end + end + end + +end diff --git a/spec/rubyspec/language/fixtures/def.rb b/spec/rubyspec/language/fixtures/def.rb new file mode 100644 index 0000000000..81bfce73d0 --- /dev/null +++ b/spec/rubyspec/language/fixtures/def.rb @@ -0,0 +1,8 @@ +def some_toplevel_method +end + +public +def public_toplevel_method +end + +private diff --git a/spec/rubyspec/language/fixtures/defined.rb b/spec/rubyspec/language/fixtures/defined.rb new file mode 100644 index 0000000000..ce330b486e --- /dev/null +++ b/spec/rubyspec/language/fixtures/defined.rb @@ -0,0 +1,296 @@ +module DefinedSpecs + self::SelfScoped = 42 + + def self.side_effects + ScratchPad.record :defined_specs_side_effects + end + + def self.fixnum_method + ScratchPad.record :defined_specs_fixnum_method + 42 + end + + def self.exception_method + ScratchPad.record :defined_specs_exception + raise "defined? specs exception method" + end + + def self.defined_method + DefinedSpecs + end + + class Basic + A = 42 + + def defined_method + DefinedSpecs + end + + def a_defined_method + end + + def protected_method + end + protected :protected_method + + def private_method + end + private :private_method + + def private_method_defined + defined? private_method + end + + def private_predicate? + end + private :private_predicate? + + def private_predicate_defined + defined? private_predicate? + end + + def local_variable_defined + x = 2 + defined? x + end + + def local_variable_defined_nil + x = nil + defined? x + end + + def instance_variable_undefined + defined? @instance_variable_undefined + end + + def instance_variable_read + value = @instance_variable_read + defined? @instance_variable_read + end + + def instance_variable_defined + @instance_variable_defined = 1 + defined? @instance_variable_defined + end + + def instance_variable_defined_nil + @instance_variable_defined_nil = nil + defined? @instance_variable_defined_nil + end + + def global_variable_undefined + defined? $defined_specs_global_variable_undefined + end + + def global_variable_read + value = $defined_specs_global_variable_read + defined? $defined_specs_global_variable_read + end + + def global_variable_defined + $defined_specs_global_variable_defined = 1 + defined? $defined_specs_global_variable_defined + end + + def class_variable_undefined + defined? @@class_variable_undefined + end + + def class_variable_defined + @@class_variable_defined = 1 + defined? @@class_variable_defined + end + + def yield_defined_method + defined? yield + end + + def yield_defined_parameter_method(&block) + defined? yield + end + + def no_yield_block + yield_defined_method + end + + def no_yield_block_parameter + yield_defined_parameter_method + end + + def yield_block + yield_defined_method { 42 } + end + + def yield_block_parameter + yield_defined_parameter_method { 42 } + end + end + + module Mixin + MixinConstant = 42 + + def defined_super + defined? super() + end + end + + class Parent + ParentConstant = 42 + + def defined_super; end + end + + class Child < Parent + include Mixin + + A = 42 + + def self.parent_constant_defined + defined? self::ParentConstant + end + + def self.module_defined + defined? Mixin + end + + def self.module_constant_defined + defined? MixinConstant + end + + def defined_super + super + end + end + + class Superclass + def yield_method + yield + end + + def method_no_args + end + + def method_args + end + + def method_block_no_args + end + + def method_block_args + end + + def define_method_no_args + end + + def define_method_args + end + + def define_method_block_no_args + end + + def define_method_block_args + end + end + + class Super < Superclass + def no_super_method_no_args + defined? super + end + + def no_super_method_args + defined? super() + end + + def method_no_args + defined? super + end + + def method_args + defined? super() + end + + def no_super_method_block_no_args + yield_method { defined? super } + end + + def no_super_method_block_args + yield_method { defined? super() } + end + + def method_block_no_args + yield_method { defined? super } + end + + def method_block_args + yield_method { defined? super() } + end + + define_method(:no_super_define_method_no_args) { defined? super } + define_method(:no_super_define_method_args) { defined? super() } + define_method(:define_method_no_args) { defined? super } + define_method(:define_method_args) { defined? super() } + + define_method(:no_super_define_method_block_no_args) do + yield_method { defined? super } + end + + define_method(:no_super_define_method_block_args) do + yield_method { defined? super() } + end + + define_method(:define_method_block_no_args) do + yield_method { defined? super } + end + + define_method(:define_method_block_args) do + yield_method { defined? super() } + end + end + + class ClassWithMethod + def test + end + end + + class ClassUndefiningMethod < ClassWithMethod + undef :test + end + + class ClassWithoutMethod < ClassUndefiningMethod + # If an undefined method overridden in descendants + # define?(super) should return nil + def test + defined?(super) + end + end + + module IntermediateModule1 + def method_no_args + end + end + + module IntermediateModule2 + def method_no_args + defined?(super) + end + end + + class SuperWithIntermediateModules + include IntermediateModule1 + include IntermediateModule2 + + def method_no_args + super + end + end +end + +class Object + def defined_specs_method + DefinedSpecs + end + + def defined_specs_receiver + DefinedSpecs::Basic.new + end +end diff --git a/spec/rubyspec/language/fixtures/dollar_zero.rb b/spec/rubyspec/language/fixtures/dollar_zero.rb new file mode 100644 index 0000000000..683bce8d4e --- /dev/null +++ b/spec/rubyspec/language/fixtures/dollar_zero.rb @@ -0,0 +1,6 @@ +puts $0 +puts __FILE__ + +if $0 == __FILE__ + print "OK" +end diff --git a/spec/rubyspec/language/fixtures/ensure.rb b/spec/rubyspec/language/fixtures/ensure.rb new file mode 100644 index 0000000000..0dad7d8401 --- /dev/null +++ b/spec/rubyspec/language/fixtures/ensure.rb @@ -0,0 +1,72 @@ +module EnsureSpec + class Container + attr_reader :executed + + def initialize + @executed = [] + end + + def raise_in_method_with_ensure + @executed << :method + raise "An Exception" + ensure + @executed << :ensure + end + + def raise_and_rescue_in_method_with_ensure + @executed << :method + raise "An Exception" + rescue + @executed << :rescue + ensure + @executed << :ensure + end + + def throw_in_method_with_ensure + @executed << :method + throw(:symbol) + ensure + @executed << :ensure + end + + def implicit_return_in_method_with_ensure + :method + ensure + :ensure + end + + def explicit_return_in_method_with_ensure + return :method + ensure + return :ensure + end + end +end + +module EnsureSpec + + class Test + + def initialize + @values = [] + end + + attr_reader :values + + def call_block + begin + @values << :start + yield + ensure + @values << :end + end + end + + def do_test + call_block do + @values << :in_block + return :did_test + end + end + end +end diff --git a/spec/rubyspec/language/fixtures/file.rb b/spec/rubyspec/language/fixtures/file.rb new file mode 100644 index 0000000000..7b862cfe1a --- /dev/null +++ b/spec/rubyspec/language/fixtures/file.rb @@ -0,0 +1 @@ +ScratchPad.record __FILE__ diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files.rb new file mode 100644 index 0000000000..3aed2f29b6 --- /dev/null +++ b/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative 'freeze_magic_comment_required' + +p "abc".object_id == $second_literal_id diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb new file mode 100644 index 0000000000..53ef959970 --- /dev/null +++ b/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_diff_enc.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative 'freeze_magic_comment_required_diff_enc' + +p "abc".object_id != $second_literal_id diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_no_comment.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_no_comment.rb new file mode 100644 index 0000000000..fc6cd5bf82 --- /dev/null +++ b/spec/rubyspec/language/fixtures/freeze_magic_comment_across_files_no_comment.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative 'freeze_magic_comment_required_no_comment' + +p "abc".object_id != $second_literal_id diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_one_literal.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_one_literal.rb new file mode 100644 index 0000000000..d35905b332 --- /dev/null +++ b/spec/rubyspec/language/fixtures/freeze_magic_comment_one_literal.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +ids = Array.new(2) { "abc".object_id } +p ids.first == ids.last diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_required.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_required.rb new file mode 100644 index 0000000000..a4ff4459b1 --- /dev/null +++ b/spec/rubyspec/language/fixtures/freeze_magic_comment_required.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +$second_literal_id = "abc".object_id diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_required_diff_enc.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_required_diff_enc.rb new file mode 100644 index 0000000000..d0558a2251 Binary files /dev/null and b/spec/rubyspec/language/fixtures/freeze_magic_comment_required_diff_enc.rb differ diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_required_no_comment.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_required_no_comment.rb new file mode 100644 index 0000000000..e09232a5f4 --- /dev/null +++ b/spec/rubyspec/language/fixtures/freeze_magic_comment_required_no_comment.rb @@ -0,0 +1 @@ +$second_literal_id = "abc".object_id diff --git a/spec/rubyspec/language/fixtures/freeze_magic_comment_two_literals.rb b/spec/rubyspec/language/fixtures/freeze_magic_comment_two_literals.rb new file mode 100644 index 0000000000..a4d655ad02 --- /dev/null +++ b/spec/rubyspec/language/fixtures/freeze_magic_comment_two_literals.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +p "abc".object_id == "abc".object_id diff --git a/spec/rubyspec/language/fixtures/hash_strings_ascii8bit.rb b/spec/rubyspec/language/fixtures/hash_strings_ascii8bit.rb new file mode 100644 index 0000000000..4ac11b9930 --- /dev/null +++ b/spec/rubyspec/language/fixtures/hash_strings_ascii8bit.rb @@ -0,0 +1,7 @@ +# encoding: ascii-8bit + +module HashStringsASCII8BIT + def self.literal_hash + {"foo" => "bar"} + end +end diff --git a/spec/rubyspec/language/fixtures/hash_strings_usascii.rb b/spec/rubyspec/language/fixtures/hash_strings_usascii.rb new file mode 100644 index 0000000000..18cfef7c8c --- /dev/null +++ b/spec/rubyspec/language/fixtures/hash_strings_usascii.rb @@ -0,0 +1,7 @@ +# encoding: us-ascii + +module HashStringsUSASCII + def self.literal_hash + {"foo" => "bar"} + end +end diff --git a/spec/rubyspec/language/fixtures/hash_strings_utf8.rb b/spec/rubyspec/language/fixtures/hash_strings_utf8.rb new file mode 100644 index 0000000000..7928090282 --- /dev/null +++ b/spec/rubyspec/language/fixtures/hash_strings_utf8.rb @@ -0,0 +1,7 @@ +# encoding: utf-8 + +module HashStringsUTF8 + def self.literal_hash + {"foo" => "bar"} + end +end diff --git a/spec/rubyspec/language/fixtures/match_operators.rb b/spec/rubyspec/language/fixtures/match_operators.rb new file mode 100644 index 0000000000..f04c54d723 --- /dev/null +++ b/spec/rubyspec/language/fixtures/match_operators.rb @@ -0,0 +1,9 @@ +class OperatorImplementor + def =~(val) + return val + end + + def !~(val) + return val + end +end diff --git a/spec/rubyspec/language/fixtures/metaclass.rb b/spec/rubyspec/language/fixtures/metaclass.rb new file mode 100644 index 0000000000..a1990b9225 --- /dev/null +++ b/spec/rubyspec/language/fixtures/metaclass.rb @@ -0,0 +1,34 @@ +module MetaClassSpecs + + def self.metaclass_of obj + class << obj + self + end + end + + class A + def self.cheese + 'edam' + end + end + + class B < A + def self.cheese + 'stilton' + end + end + + class C + class << self + class << self + def ham + 'iberico' + end + end + end + end + + class D < C; end + +end + diff --git a/spec/rubyspec/language/fixtures/module.rb b/spec/rubyspec/language/fixtures/module.rb new file mode 100644 index 0000000000..33d323846e --- /dev/null +++ b/spec/rubyspec/language/fixtures/module.rb @@ -0,0 +1,24 @@ +module ModuleSpecs + module Modules + class Klass + end + + A = "Module" + B = 1 + C = nil + D = true + E = false + end + + module Anonymous + end + + module IncludedInObject + module IncludedModuleSpecs + end + end +end + +class Object + include ModuleSpecs::IncludedInObject +end diff --git a/spec/rubyspec/language/fixtures/next.rb b/spec/rubyspec/language/fixtures/next.rb new file mode 100644 index 0000000000..fbca842334 --- /dev/null +++ b/spec/rubyspec/language/fixtures/next.rb @@ -0,0 +1,134 @@ +class NextSpecs + def self.yielding_method(expected) + yield.should == expected + :method_return_value + end + + def self.yielding + yield + end + + # The methods below are defined to spec the behavior of the next statement + # while specifically isolating whether the statement is in an Iter block or + # not. In a normal spec example, the code is always nested inside a block. + # Rather than rely on that implicit context in this case, the context is + # made explicit because of the interaction of next in a loop nested inside + # an Iter block. + def self.while_next(arg) + x = true + while x + begin + ScratchPad << :begin + x = false + if arg + next 42 + else + next + end + ensure + ScratchPad << :ensure + end + end + end + + def self.while_within_iter(arg) + yielding do + x = true + while x + begin + ScratchPad << :begin + x = false + if arg + next 42 + else + next + end + ensure + ScratchPad << :ensure + end + end + end + end + + def self.until_next(arg) + x = false + until x + begin + ScratchPad << :begin + x = true + if arg + next 42 + else + next + end + ensure + ScratchPad << :ensure + end + end + end + + def self.until_within_iter(arg) + yielding do + x = false + until x + begin + ScratchPad << :begin + x = true + if arg + next 42 + else + next + end + ensure + ScratchPad << :ensure + end + end + end + end + + def self.loop_next(arg) + x = 1 + loop do + break if x == 2 + + begin + ScratchPad << :begin + x += 1 + if arg + next 42 + else + next + end + ensure + ScratchPad << :ensure + end + end + end + + def self.loop_within_iter(arg) + yielding do + x = 1 + loop do + break if x == 2 + + begin + ScratchPad << :begin + x += 1 + if arg + next 42 + else + next + end + ensure + ScratchPad << :ensure + end + end + end + end + + class Block + def method(v) + yield v + end + end +end diff --git a/spec/rubyspec/language/fixtures/precedence.rb b/spec/rubyspec/language/fixtures/precedence.rb new file mode 100644 index 0000000000..d2295c755b --- /dev/null +++ b/spec/rubyspec/language/fixtures/precedence.rb @@ -0,0 +1,16 @@ +module PrecedenceSpecs + class NonUnaryOpTest + def add_num(arg) + [1].collect { |i| arg + i +1 } + end + def sub_num(arg) + [1].collect { |i| arg + i -1 } + end + def add_str + %w[1].collect { |i| i +'1' } + end + def add_var + [1].collect { |i| i +i } + end + end +end diff --git a/spec/rubyspec/language/fixtures/private.rb b/spec/rubyspec/language/fixtures/private.rb new file mode 100644 index 0000000000..96f73cea3f --- /dev/null +++ b/spec/rubyspec/language/fixtures/private.rb @@ -0,0 +1,59 @@ +module Private + class A + def foo + "foo" + end + + private + def bar + "bar" + end + end + + class B + def foo + "foo" + end + + private + + def self.public_defs_method; 0; end + + class C + def baz + "baz" + end + end + + class << self + def public_class_method1; 1; end + private + def private_class_method1; 1; end + end + + def bar + "bar" + end + end + + module D + private + def foo + "foo" + end + end + + class E + include D + end + + class G + def foo + "foo" + end + end + + class H < A + private :foo + end +end diff --git a/spec/rubyspec/language/fixtures/rescue.rb b/spec/rubyspec/language/fixtures/rescue.rb new file mode 100644 index 0000000000..3fa5df1eb5 --- /dev/null +++ b/spec/rubyspec/language/fixtures/rescue.rb @@ -0,0 +1,63 @@ +module RescueSpecs + def self.begin_else(raise_exception) + begin + ScratchPad << :one + raise "an error occurred" if raise_exception + rescue + ScratchPad << :rescue_ran + :rescue_val + else + ScratchPad << :else_ran + :val + end + end + + def self.begin_else_ensure(raise_exception) + begin + ScratchPad << :one + raise "an error occurred" if raise_exception + rescue + ScratchPad << :rescue_ran + :rescue_val + else + ScratchPad << :else_ran + :val + ensure + ScratchPad << :ensure_ran + :ensure_val + end + end + + def self.begin_else_return(raise_exception) + begin + ScratchPad << :one + raise "an error occurred" if raise_exception + rescue + ScratchPad << :rescue_ran + :rescue_val + else + ScratchPad << :else_ran + :val + end + ScratchPad << :outside_begin + :return_val + end + + def self.begin_else_return_ensure(raise_exception) + begin + ScratchPad << :one + raise "an error occurred" if raise_exception + rescue + ScratchPad << :rescue_ran + :rescue_val + else + ScratchPad << :else_ran + :val + ensure + ScratchPad << :ensure_ran + :ensure_val + end + ScratchPad << :outside_begin + :return_val + end +end diff --git a/spec/rubyspec/language/fixtures/return.rb b/spec/rubyspec/language/fixtures/return.rb new file mode 100644 index 0000000000..0414c356e8 --- /dev/null +++ b/spec/rubyspec/language/fixtures/return.rb @@ -0,0 +1,139 @@ +module ReturnSpecs + class Blocks + def yielding_method + yield + ScratchPad.record :after_yield + end + + def enclosing_method + yielding_method do + ScratchPad.record :before_return + return :return_value + ScratchPad.record :after_return + end + + ScratchPad.record :after_call + end + end + + class NestedCalls < Blocks + def invoking_method(&b) + yielding_method(&b) + ScratchPad.record :after_invoke + end + + def enclosing_method + invoking_method do + ScratchPad.record :before_return + return :return_value + ScratchPad.record :after_return + end + ScratchPad.record :after_invoke + end + end + + class NestedBlocks < Blocks + def enclosing_method + yielding_method do + yielding_method do + ScratchPad.record :before_return + return :return_value + ScratchPad.record :after_return + end + ScratchPad.record :after_invoke1 + end + ScratchPad.record :after_invoke2 + end + end + + class SavedInnerBlock + def add(&b) + @block = b + end + + def outer + yield + @block.call + end + + def inner + yield + end + + def start + outer do + inner do + add do + ScratchPad.record :before_return + return :return_value + end + end + end + + ScratchPad.record :bottom_of_start + + return false + end + end + + class ThroughDefineMethod + lamb = proc { |x| x.call } + define_method :foo, lamb + + def mp(&b); b; end + + def outer + pr = mp { return :good } + + foo(pr) + return :bad + end + end + + class DefineMethod + lamb = proc { return :good } + define_method :foo, lamb + + def outer + val = :bad + + # This is tricky, but works. If lamb properly returns, then the + # return value will go into val before we run the ensure. + # + # If lamb's return keeps unwinding incorrectly, val will still + # have it's old value. + # + # We can therefore use val to figure out what happened. + begin + val = foo() + ensure + if val != :good + return :bad + end + end + + return val + end + end + + class MethodWithBlock + def method1 + return [2, 3].inject 0 do |a, b| + a + b + end + nil + end + + def get_ary(count, &blk) + count.times.to_a do |i| + blk.call(i) if blk + end + end + + def method2 + return get_ary 3 do |i| + end + nil + end + end +end diff --git a/spec/rubyspec/language/fixtures/send.rb b/spec/rubyspec/language/fixtures/send.rb new file mode 100644 index 0000000000..c3013616b2 --- /dev/null +++ b/spec/rubyspec/language/fixtures/send.rb @@ -0,0 +1,140 @@ +module LangSendSpecs + module_function + + def fooM0; 100 end + def fooM1(a); [a]; end + def fooM2(a,b); [a,b]; end + def fooM3(a,b,c); [a,b,c]; end + def fooM4(a,b,c,d); [a,b,c,d]; end + def fooM5(a,b,c,d,e); [a,b,c,d,e]; end + def fooM0O1(a=1); [a]; end + def fooM1O1(a,b=1); [a,b]; end + def fooM2O1(a,b,c=1); [a,b,c]; end + def fooM3O1(a,b,c,d=1); [a,b,c,d]; end + def fooM4O1(a,b,c,d,e=1); [a,b,c,d,e]; end + def fooM0O2(a=1,b=2); [a,b]; end + def fooM0R(*r); r; end + def fooM1R(a, *r); [a, r]; end + def fooM0O1R(a=1, *r); [a, r]; end + def fooM1O1R(a, b=1, *r); [a, b, r]; end + + def one(a); a; end + def oneb(a,&b); [a,yield(b)]; end + def twob(a,b,&c); [a,b,yield(c)]; end + def makeproc(&b) b end + + def yield_now; yield; end + + def double(x); x * 2 end + def weird_parens + # means double((5).to_s) + # NOT (double(5)).to_s + double (5).to_s + end + + def rest_len(*a); a.size; end + + def self.twos(a,b,*c) + [c.size, c.last] + end + + class PrivateSetter + attr_reader :foo + attr_writer :foo + private :foo= + + def call_self_foo_equals(value) + self.foo = value + end + + def call_self_foo_equals_masgn(value) + a, self.foo = 1, value + end + end + + class PrivateGetter + attr_reader :foo + private :foo + + def call_self_foo + self.foo + end + + def call_self_foo_or_equals(value) + self.foo ||= 6 + end + end + + class AttrSet + attr_reader :result + def []=(a, b, c, d); @result = [a,b,c,d]; end + end + + class ToProc + def initialize(val) + @val = val + end + + def to_proc + Proc.new { @val } + end + end + + class ToAry + def initialize(obj) + @obj = obj + end + + def to_ary + @obj + end + end + + class MethodMissing + def initialize + @message = nil + @args = nil + end + + attr_reader :message, :args + + def method_missing(m, *a) + @message = m + @args = a + end + end + + class Attr19Set + attr_reader :result + def []=(*args); @result = args; end + end + + module_function + + def fooR(*r); r; end + def fooM0RQ1(*r, q); [r, q]; end + def fooM0RQ2(*r, s, q); [r, s, q]; end + def fooM1RQ1(a, *r, q); [a, r, q]; end + def fooM1O1RQ1(a, b=9, *r, q); [a, b, r, q]; end + def fooM1O1RQ2(a, b=9, *r, q, t); [a, b, r, q, t]; end + + def fooO1Q1(a=1, b); [a,b]; end + def fooM1O1Q1(a,b=2,c); [a,b,c]; end + def fooM2O1Q1(a,b,c=3,d); [a,b,c,d]; end + def fooM2O2Q1(a,b,c=3,d=4,e); [a,b,c,d,e]; end + def fooO4Q1(a=1,b=2,c=3,d=4,e); [a,b,c,d,e]; end + def fooO4Q2(a=1,b=2,c=3,d=4,e,f); [a,b,c,d,e,f]; end + + def destructure2((a,b)); a+b; end + def destructure2b((a,b)); [a,b]; end + def destructure4r((a,b,*c,d,e)); [a,b,c,d,e]; end + def destructure4o(a=1,(b,c),d,&e); [a,b,c,d]; end + def destructure5o(a=1, f=2, (b,c),d,&e); [a,f,b,c,d]; end + def destructure7o(a=1, f=2, (b,c),(d,e), &g); [a,f,b,c,d,e]; end + def destructure7b(a=1, f=2, (b,c),(d,e), &g); g.call([a,f,b,c,d,e]); end + def destructure4os(a=1,(b,*c)); [a,b,c]; end +end + +def lang_send_rest_len(*a) + a.size +end diff --git a/spec/rubyspec/language/fixtures/squiggly_heredoc.rb b/spec/rubyspec/language/fixtures/squiggly_heredoc.rb new file mode 100644 index 0000000000..afc87514c7 --- /dev/null +++ b/spec/rubyspec/language/fixtures/squiggly_heredoc.rb @@ -0,0 +1,39 @@ +module SquigglyHeredocSpecs + def self.message + <<~HEREDOC + character density, n.: + The number of very weird people in the office. + HEREDOC + end + + def self.blank + <<~HERE + HERE + end + + def self.unquoted + <<~HERE + unquoted #{"interpolated"} + HERE + end + + def self.doublequoted + <<~"HERE" + doublequoted #{"interpolated"} + HERE + end + + def self.singlequoted + <<~'HERE' + singlequoted #{"interpolated"} + HERE + end + + def self.least_indented_on_the_last_line + <<~HERE + a + b + c + HERE + end +end diff --git a/spec/rubyspec/language/fixtures/super.rb b/spec/rubyspec/language/fixtures/super.rb new file mode 100644 index 0000000000..c7d47cc465 --- /dev/null +++ b/spec/rubyspec/language/fixtures/super.rb @@ -0,0 +1,569 @@ +module Super + module S1 + class A + def foo(a) + a << "A#foo" + bar(a) + end + def bar(a) + a << "A#bar" + end + end + class B < A + def foo(a) + a << "B#foo" + super(a) + end + def bar(a) + a << "B#bar" + super(a) + end + end + end + + module S2 + class A + def baz(a) + a << "A#baz" + end + end + class B < A + def foo(a) + a << "B#foo" + baz(a) + end + end + class C < B + def baz(a) + a << "C#baz" + super(a) + end + end + end + + module S3 + class A + def foo(a) + a << "A#foo" + end + def self.foo(a) + a << "A.foo" + end + def self.bar(a) + a << "A.bar" + foo(a) + end + end + class B < A + def self.foo(a) + a << "B.foo" + super(a) + end + def self.bar(a) + a << "B.bar" + super(a) + end + end + end + + module S4 + class A + def foo(a) + a << "A#foo" + end + end + class B < A + def foo(a, b) + a << "B#foo(a,#{b})" + super(a) + end + end + end + + class S5 + def here + :good + end + end + + class S6 < S5 + def under + yield + end + + def here + under { + super + } + end + end + + class S7 < S5 + define_method(:here) { super() } + end + + module MS1 + module ModA + def foo(a) + a << "ModA#foo" + bar(a) + end + def bar(a) + a << "ModA#bar" + end + end + class A + include ModA + end + module ModB + def bar(a) + a << "ModB#bar" + super(a) + end + end + class B < A + def foo(a) + a << "B#foo" + super(a) + end + include ModB + end + end + + module MS2 + class A + def baz(a) + a << "A#baz" + end + end + module ModB + def foo(a) + a << "ModB#foo" + baz(a) + end + end + class B < A + include ModB + end + class C < B + def baz(a) + a << "C#baz" + super(a) + end + end + end + + module MultiSuperTargets + module M + def foo + super + end + end + + class BaseA + def foo + :BaseA + end + end + + class BaseB + def foo + :BaseB + end + end + + class A < BaseA + include M + end + + class B < BaseB + include M + end + end + + module MS3 + module ModA + def foo(a) + a << "ModA#foo" + end + def bar(a) + a << "ModA#bar" + foo(a) + end + end + class A + def foo(a) + a << "A#foo" + end + class << self + include ModA + end + end + class B < A + def self.foo(a) + a << "B.foo" + super(a) + end + def self.bar(a) + a << "B.bar" + super(a) + end + end + end + + module MS4 + module Layer1 + def example + 5 + end + end + + module Layer2 + include Layer1 + def example + super + end + end + + class A + include Layer2 + public :example + end + end + + class MM_A + undef_method :is_a? + end + + class MM_B < MM_A + def is_a?(blah) + # should fire the method_missing below + super + end + + def method_missing(*) + false + end + end + + class Alias1 + def name + [:alias1] + end + end + + class Alias2 < Alias1 + def initialize + @times = 0 + end + + def name + if @times >= 10 + raise "runaway super" + end + + @times += 1 + + # Use this so that we can see collect all supers that we see. + # One bug that arises is that we call Alias2#name from Alias2#name + # as it's superclass. In that case, either we get a runaway recursion + # super OR we get the return value being [:alias2, :alias2, :alias1] + # rather than [:alias2, :alias1]. + # + # Which one depends on caches and how super is implemented. + [:alias2] + super + end + end + + class Alias3 < Alias2 + alias_method :name3, :name + # In the method table for Alias3 now should be a special alias entry + # that references Alias2 and Alias2#name (probably as an object). + # + # When name3 is called then, Alias2 (NOT Alias3) is presented as the + # current module to Alias2#name, so that when super is called, + # Alias2->superclass is next. + # + # Otherwise, Alias2 is next, which is where name was to begin with, + # causing the wrong #name method to be called. + end + + module AliasWithSuper + module AS1 + def foo + :a + end + end + + module BS1 + def foo + [:b, super] + end + end + + class Base + extend AS1 + extend BS1 + end + + class Trigger < Base + class << self + def foo_quux + foo_baz + end + + alias_method :foo_baz, :foo + alias_method :foo, :foo_quux + end + end + end + + module RestArgsWithSuper + class A + def a(*args) + args + end + end + + class B < A + def a(*args) + args << "foo" + + super + end + end + end + + class AnonymousModuleIncludedTwiceBase + def self.whatever + mod = Module.new do + def a(array) + array << "anon" + super + end + end + + include mod + end + + def a(array) + array << "non-anon" + end + end + + class AnonymousModuleIncludedTwice < AnonymousModuleIncludedTwiceBase + whatever + whatever + end + + module ZSuperWithBlock + class A + def a + yield + end + + def b(&block) + block.call + end + + def c + yield + end + end + + class B < A + def a + super { 14 } + end + + def b + block_ref = lambda { 15 } + [super { 14 }, super(&block_ref)] + end + + def c + block_ref = lambda { 16 } + super(&block_ref) + end + end + end + + module ZSuperWithOptional + class A + def m(x, y, z) + z + end + end + + class B < A + def m(x, y, z = 14) + super + end + end + + class C < A + def m(x, y, z = 14) + z = 100 + super + end + end + end + + module ZSuperWithRest + class A + def m(*args) + args + end + + def m_modified(*args) + args + end + end + + class B < A + def m(*args) + super + end + + def m_modified(*args) + args[1] = 14 + super + end + end + end + + module ZSuperWithRestAndOthers + class A + def m(a, b, *args) + args + end + + def m_modified(a, b, *args) + args + end + end + + class B < A + def m(a, b, *args) + super + end + + def m_modified(a, b, *args) + args[1] = 14 + super + end + end + end + + module ZSuperWithUnderscores + class A + def m(*args) + args + end + + def m_modified(*args) + args + end + end + + class B < A + def m(_, _) + super + end + + def m_modified(_, _) + _ = 14 + super + end + end + end + + module KeywordArguments + class A + def foo(**args) + args + end + end + + class B < A + def foo(**) + super + end + end + + class C < A + end + end + + module FromBasicObject + def __send__(name, *args, &block) + super + end + end + + module IntermediateBasic + include FromBasicObject + end + + class IncludesFromBasic + include FromBasicObject + + def foobar; 43; end + end + + class IncludesIntermediate + include IntermediateBasic + + def foobar; 42; end + end + + module SingletonCase + class Base + def foobar(array) + array << :base + end + end + + class Foo < Base + def foobar(array) + array << :foo + super + end + end + end + + module SingletonAliasCase + class Base + def foobar(array) + array << :base + end + + def alias_on_singleton + object = self + singleton = (class << object; self; end) + singleton.__send__(:alias_method, :new_foobar, :foobar) + end + end + + class Foo < Base + def foobar(array) + array << :foo + super + end + end + end + + module SplatAndKeyword + class A + def foo(*args, **options) + [args, options] + end + end + + class B < A + def foo(*args, **options) + super + end + end + end +end diff --git a/spec/rubyspec/language/fixtures/variables.rb b/spec/rubyspec/language/fixtures/variables.rb new file mode 100644 index 0000000000..07265dbb2b --- /dev/null +++ b/spec/rubyspec/language/fixtures/variables.rb @@ -0,0 +1,85 @@ +module VariablesSpecs + class ParAsgn + attr_accessor :x + + def initialize + @x = 0 + end + + def inc + @x += 1 + end + + def to_ary + [1,2,3,4] + end + end + + class OpAsgn + attr_accessor :a, :b, :side_effect + + def do_side_effect + self.side_effect = true + return @a + end + + def do_more_side_effects + @a += 5 + self + end + + def do_bool_side_effects + @b += 1 + self + end + end + + class Hashalike + def [](k) k end + def []=(k, v) [k, v] end + end + + def self.reverse_foo(a, b) + return b, a + end + + class ArrayLike + def initialize(array) + @array = array + end + + def to_a + @array + end + end + + class ArraySubclass < Array + end + + class PrivateMethods + private + + def to_ary + [1, 2] + end + + def to_a + [3, 4] + end + end + + class ToAryNil + def to_ary + end + end + + class Chain + def self.without_parenthesis a + a + end + end + + def self.false + false + end +end diff --git a/spec/rubyspec/language/fixtures/yield.rb b/spec/rubyspec/language/fixtures/yield.rb new file mode 100644 index 0000000000..a195616640 --- /dev/null +++ b/spec/rubyspec/language/fixtures/yield.rb @@ -0,0 +1,37 @@ +module YieldSpecs + class Yielder + def z + yield + end + + def ze(&block) + block = proc { block } + yield + end + + def s(a) + yield(a) + end + + def m(a, b, c) + yield(a, b, c) + end + + def r(a) + yield(*a) + end + + def rs(a, b, c) + yield(a, b, *c) + end + + def self.define_deep(&inned_block) + define_method 'deep' do |v| + # should yield to inner_block + yield v + end + end + + define_deep { |v| v * 2} + end +end diff --git a/spec/rubyspec/language/for_spec.rb b/spec/rubyspec/language/for_spec.rb new file mode 100644 index 0000000000..a043da5476 --- /dev/null +++ b/spec/rubyspec/language/for_spec.rb @@ -0,0 +1,171 @@ +require File.expand_path('../../spec_helper', __FILE__) + +# for name[, name]... in expr [do] +# body +# end +describe "The for expression" do + it "iterates over an Enumerable passing each element to the block" do + j = 0 + for i in 1..3 + j += i + end + j.should == 6 + end + + it "iterates over an Hash passing each key-value pair to the block" do + k = 0 + l = 0 + + for i, j in { 1 => 10, 2 => 20 } + k += i + l += j + end + + k.should == 3 + l.should == 30 + end + + it "iterates over any object responding to 'each'" do + class XYZ + def each + (0..10).each { |i| yield i } + end + end + + j = 0 + for i in XYZ.new + j += i + end + j.should == 55 + end + + it "allows an instance variable as an iterator name" do + m = [1,2,3] + n = 0 + for @var in m + n += 1 + end + @var.should == 3 + n.should == 3 + end + + it "allows a class variable as an iterator name" do + class OFor + m = [1,2,3] + n = 0 + for @@var in m + n += 1 + end + @@var.should == 3 + n.should == 3 + end + end + + it "allows a constant as an iterator name" do + class OFor + m = [1,2,3] + n = 0 + -> { + for CONST in m + n += 1 + end + }.should complain(/already initialized constant/) + CONST.should == 3 + n.should == 3 + end + end + + # 1.9 behaviour verified by nobu in + # http://redmine.ruby-lang.org/issues/show/2053 + it "yields only as many values as there are arguments" do + class OFor + def each + [[1,2,3], [4,5,6]].each do |a| + yield(a[0],a[1],a[2]) + end + end + end + o = OFor.new + qs = [] + for q in o + qs << q + end + qs.should == [1, 4] + q.should == 4 + end + + it "optionally takes a 'do' after the expression" do + j = 0 + for i in 1..3 do + j += i + end + j.should == 6 + end + + it "allows body begin on the same line if do is used" do + j = 0 + for i in 1..3 do j += i + end + j.should == 6 + end + + it "executes code in containing variable scope" do + for i in 1..2 + a = 123 + end + + a.should == 123 + end + + it "executes code in containing variable scope with 'do'" do + for i in 1..2 do + a = 123 + end + + a.should == 123 + end + + it "returns expr" do + for i in 1..3; end.should == (1..3) + for i,j in { 1 => 10, 2 => 20 }; end.should == { 1 => 10, 2 => 20 } + end + + it "breaks out of a loop upon 'break', returning nil" do + j = 0 + for i in 1..3 + j += i + + break if i == 2 + end.should == nil + + j.should == 3 + end + + it "allows 'break' to have an argument which becomes the value of the for expression" do + for i in 1..3 + break 10 if i == 2 + end.should == 10 + end + + it "starts the next iteration with 'next'" do + j = 0 + for i in 1..5 + next if i == 2 + + j += i + end + + j.should == 13 + end + + it "repeats current iteration with 'redo'" do + j = 0 + for i in 1..3 + j += i + + redo if i == 2 && j < 4 + end + + j.should == 8 + end +end diff --git a/spec/rubyspec/language/hash_spec.rb b/spec/rubyspec/language/hash_spec.rb new file mode 100644 index 0000000000..edd9d4fbb2 --- /dev/null +++ b/spec/rubyspec/language/hash_spec.rb @@ -0,0 +1,154 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/hash_strings_ascii8bit', __FILE__) +require File.expand_path('../fixtures/hash_strings_utf8', __FILE__) +require File.expand_path('../fixtures/hash_strings_usascii', __FILE__) + +describe "Hash literal" do + it "{} should return an empty hash" do + {}.size.should == 0 + {}.should == {} + end + + it "{} should return a new hash populated with the given elements" do + h = {a: 'a', 'b' => 3, 44 => 2.3} + h.size.should == 3 + h.should == {a: "a", "b" => 3, 44 => 2.3} + end + + it "treats empty expressions as nils" do + h = {() => ()} + h.keys.should == [nil] + h.values.should == [nil] + h[nil].should == nil + + h = {() => :value} + h.keys.should == [nil] + h.values.should == [:value] + h[nil].should == :value + + h = {key: ()} + h.keys.should == [:key] + h.values.should == [nil] + h[:key].should == nil + end + + it "freezes string keys on initialization" do + key = "foo" + h = {key => "bar"} + key.reverse! + h["foo"].should == "bar" + h.keys.first.should == "foo" + h.keys.first.frozen?.should == true + key.should == "oof" + end + + it "checks duplicated keys on initialization" do + -> { + @h = eval "{foo: :bar, foo: :foo}" + }.should complain(/key :foo is duplicated|duplicated key/) + @h.keys.size.should == 1 + @h.should == {foo: :foo} + end + + it "accepts a hanging comma" do + h = {a: 1, b: 2,} + h.size.should == 2 + h.should == {a: 1, b: 2} + end + + it "recognizes '=' at the end of the key" do + eval("{:a==>1}").should == {:"a=" => 1} + eval("{:a= =>1}").should == {:"a=" => 1} + eval("{:a= => 1}").should == {:"a=" => 1} + end + + it "with '==>' in the middle raises SyntaxError" do + lambda { eval("{:a ==> 1}") }.should raise_error(SyntaxError) + end + + it "constructs a new hash with the given elements" do + {foo: 123}.should == {foo: 123} + h = {rbx: :cool, specs: 'fail_sometimes'} + {rbx: :cool, specs: 'fail_sometimes'}.should == h + end + + it "ignores a hanging comma" do + {foo: 123,}.should == {foo: 123} + h = {rbx: :cool, specs: 'fail_sometimes'} + {rbx: :cool, specs: 'fail_sometimes',}.should == h + end + + it "accepts mixed 'key: value' and 'key => value' syntax" do + h = {:a => 1, :b => 2, "c" => 3} + {a: 1, b: 2, "c" => 3}.should == h + end + + it "accepts mixed 'key: value', 'key => value' and '\"key\"': value' syntax" do + h = {:a => 1, :b => 2, "c" => 3, :d => 4} + eval('{a: 1, :b => 2, "c" => 3, "d": 4}').should == h + end + + it "expands an '**{}' element into the containing Hash literal initialization" do + {a: 1, **{b: 2}, c: 3}.should == {a: 1, b: 2, c: 3} + end + + it "expands an '**obj' element into the containing Hash literal initialization" do + h = {b: 2, c: 3} + {**h, a: 1}.should == {b: 2, c: 3, a: 1} + {a: 1, **h}.should == {a: 1, b: 2, c: 3} + {a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4} + end + + it "expands a BasicObject using ** into the containing Hash literal initialization" do + h = BasicObject.new + def h.to_hash; {:b => 2, :c => 3}; end + {**h, a: 1}.should == {b: 2, c: 3, a: 1} + {a: 1, **h}.should == {a: 1, b: 2, c: 3} + {a: 1, **h, c: 4}.should == {a: 1, b: 2, c: 4} + end + + it "expands an '**{}' element with the last key/value pair taking precedence" do + -> { + @h = eval "{a: 1, **{a: 2, b: 3, c: 1}, c: 3}" + }.should complain(/key :a is duplicated|duplicated key/) + @h.should == {a: 2, b: 3, c: 3} + end + + it "merges multiple nested '**obj' in Hash literals" do + -> { + @h = eval "{a: 1, **{a: 2, **{b: 3, **{c: 4}}, **{d: 5}, }, **{d: 6}}" + }.should complain(/key :a is duplicated|duplicated key/) + @h.should == {a: 2, b: 3, c: 4, d: 6} + end + + it "calls #to_hash to expand an '**obj' element" do + obj = mock("hash splat") + obj.should_receive(:to_hash).and_return({b: 2, d: 4}) + + {a: 1, **obj, c: 3}.should == {a:1, b: 2, c: 3, d: 4} + end + + it "raises a TypeError if any splatted elements keys are not symbols" do + h = {1 => 2, b: 3} + lambda { {a: 1, **h} }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_hash does not return a Hash" do + obj = mock("hash splat") + obj.should_receive(:to_hash).and_return(obj) + + lambda { {**obj} }.should raise_error(TypeError) + end + + it "does not change encoding of literal string keys during creation" do + ascii8bit_hash = HashStringsASCII8BIT.literal_hash + utf8_hash = HashStringsUTF8.literal_hash + usascii_hash = HashStringsUSASCII.literal_hash + + ascii8bit_hash.keys.first.encoding.should == Encoding::ASCII_8BIT + ascii8bit_hash.keys.first.should == utf8_hash.keys.first + utf8_hash.keys.first.encoding.should == Encoding::UTF_8 + utf8_hash.keys.first.should == usascii_hash.keys.first + usascii_hash.keys.first.encoding.should == Encoding::US_ASCII + end +end diff --git a/spec/rubyspec/language/heredoc_spec.rb b/spec/rubyspec/language/heredoc_spec.rb new file mode 100644 index 0000000000..436fcb9c9e --- /dev/null +++ b/spec/rubyspec/language/heredoc_spec.rb @@ -0,0 +1,87 @@ +# -*- encoding: us-ascii -*- + +require File.expand_path('../../spec_helper', __FILE__) + +describe "Heredoc string" do + + before :each do + @ip = 'xxx' # used for interpolation + end + + it "allows HEREDOC with < () { }" do + SpecEvaluate.desc = "for definition" + + it "returns a Proc object when used in a BasicObject method" do + klass = Class.new(BasicObject) do + def create_lambda + -> () { } + end + end + + klass.new.create_lambda.should be_an_instance_of(Proc) + end + + it "does not execute the block" do + ->() { fail }.should be_an_instance_of(Proc) + end + + it "returns a lambda" do + -> () { }.lambda?.should be_true + end + + it "has its own scope for local variables" do + l = -> arg { + var = arg + # this would override var if it was declared outside the lambda + l.call(arg-1) if arg > 0 + var + } + l.call(1).should == 1 + end + + context "assigns no local variables" do + evaluate <<-ruby do + @a = -> { } + @b = ->() { } + @c = -> () { } + @d = -> do end + ruby + + @a.().should be_nil + @b.().should be_nil + @c.().should be_nil + @d.().should be_nil + end + end + + context "assigns variables from parameters" do + evaluate <<-ruby do + @a = -> (a) { a } + ruby + + @a.(1).should == 1 + end + + evaluate <<-ruby do + @a = -> ((a)) { a } + ruby + + @a.(1).should == 1 + @a.([1, 2, 3]).should == 1 + end + + evaluate <<-ruby do + @a = -> ((*a, b)) { [a, b] } + ruby + + @a.(1).should == [[], 1] + @a.([1, 2, 3]).should == [[1, 2], 3] + end + + evaluate <<-ruby do + @a = -> (a={}) { a } + ruby + + @a.().should == {} + @a.(2).should == 2 + end + + evaluate <<-ruby do + @a = -> (*) { } + ruby + + @a.().should be_nil + @a.(1).should be_nil + @a.(1, 2, 3).should be_nil + end + + evaluate <<-ruby do + @a = -> (*a) { a } + ruby + + @a.().should == [] + @a.(1).should == [1] + @a.(1, 2, 3).should == [1, 2, 3] + end + + evaluate <<-ruby do + @a = -> (a:) { a } + ruby + + lambda { @a.() }.should raise_error(ArgumentError) + @a.(a: 1).should == 1 + end + + evaluate <<-ruby do + @a = -> (a: 1) { a } + ruby + + @a.().should == 1 + @a.(a: 2).should == 2 + end + + evaluate <<-ruby do + @a = -> (**) { } + ruby + + @a.().should be_nil + @a.(a: 1, b: 2).should be_nil + lambda { @a.(1) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + @a = -> (**k) { k } + ruby + + @a.().should == {} + @a.(a: 1, b: 2).should == {a: 1, b: 2} + end + + evaluate <<-ruby do + @a = -> (&b) { b } + ruby + + @a.().should be_nil + @a.() { }.should be_an_instance_of(Proc) + end + + evaluate <<-ruby do + @a = -> (a, b) { [a, b] } + ruby + + @a.(1, 2).should == [1, 2] + lambda { @a.() }.should raise_error(ArgumentError) + lambda { @a.(1) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + @a = -> ((a, b, *c, d), (*e, f, g), (*h)) do + [a, b, c, d, e, f, g, h] + end + ruby + + @a.(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]] + result = @a.([1, 2, 3], [4, 5, 6, 7, 8], [9, 10]) + result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]] + end + + evaluate <<-ruby do + @a = -> (a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))) do + [a, b, c, d, e, f, g, h, i, j] + end + ruby + + @a.(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil] + result = @a.(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]]) + result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12] + end + + evaluate <<-ruby do + @a = -> (*, **k) { k } + ruby + + @a.().should == {} + @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5} + + h = mock("keyword splat") + h.should_receive(:to_hash).and_return({a: 1}) + @a.(h).should == {a: 1} + end + + evaluate <<-ruby do + @a = -> (*, &b) { b } + ruby + + @a.().should be_nil + @a.(1, 2, 3, 4).should be_nil + @a.(&(l = ->{})).should equal(l) + end + + evaluate <<-ruby do + @a = -> (a:, b:) { [a, b] } + ruby + + @a.(a: 1, b: 2).should == [1, 2] + end + + evaluate <<-ruby do + @a = -> (a:, b: 1) { [a, b] } + ruby + + @a.(a: 1).should == [1, 1] + @a.(a: 1, b: 2).should == [1, 2] + end + + evaluate <<-ruby do + @a = -> (a: 1, b:) { [a, b] } + ruby + + @a.(b: 0).should == [1, 0] + @a.(b: 2, a: 3).should == [3, 2] + end + + evaluate <<-ruby do + @a = -> (a: @a = -> (a: 1) { a }, b:) do + [a, b] + end + ruby + + @a.(a: 2, b: 3).should == [2, 3] + @a.(b: 1).should == [@a, 1] + + # Note the default value of a: in the original method. + @a.().should == 1 + end + + evaluate <<-ruby do + @a = -> (a: 1, b: 2) { [a, b] } + ruby + + @a.().should == [1, 2] + @a.(b: 3, a: 4).should == [4, 3] + end + + evaluate <<-ruby do + @a = -> (a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) do + [a, b, c, d, e, f, g, h, k, l] + end + ruby + + result = @a.(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{})) + result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l] + end + + evaluate <<-ruby do + @a = -> a, b=1, *c, d, e:, f: 2, g:, **k, &l do + [a, b, c, d, e, f, g, k, l] + end + ruby + + result = @a.(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{})) + result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l] + end + + describe "with circular optional argument reference" do + it "shadows an existing local with the same name as the argument" do + a = 1 + -> { + @proc = eval "-> (a=a) { a }" + }.should complain(/circular argument reference/) + @proc.call.should == nil + end + + it "shadows an existing method with the same name as the argument" do + def a; 1; end + -> { + @proc = eval "-> (a=a) { a }" + }.should complain(/circular argument reference/) + @proc.call.should == nil + end + + it "calls an existing method with the same name as the argument if explicitly using ()" do + def a; 1; end + -> (a=a()) { a }.call.should == 1 + end + end + end +end + +describe "A lambda expression 'lambda { ... }'" do + SpecEvaluate.desc = "for definition" + + it "calls the #lambda method" do + obj = mock("lambda definition") + obj.should_receive(:lambda).and_return(obj) + + def obj.define + lambda { } + end + + obj.define.should equal(obj) + end + + it "does not execute the block" do + lambda { fail }.should be_an_instance_of(Proc) + end + + it "returns a lambda" do + lambda { }.lambda?.should be_true + end + + it "requires a block" do + lambda { lambda }.should raise_error(ArgumentError) + end + + context "with an implicit block" do + before do + def meth; lambda; end + end + + it "can be created" do + implicit_lambda = nil + -> { + implicit_lambda = meth { 1 } + }.should complain(/tried to create Proc object without a block/) + + implicit_lambda.lambda?.should be_true + implicit_lambda.call.should == 1 + end + end + + context "assigns no local variables" do + evaluate <<-ruby do + @a = lambda { } + @b = lambda { || } + ruby + + @a.().should be_nil + @b.().should be_nil + end + end + + context "assigns variables from parameters" do + evaluate <<-ruby do + @a = lambda { |a| a } + ruby + + @a.(1).should == 1 + end + + evaluate <<-ruby do + def m(*a) yield(*a) end + @a = lambda { |a| a } + ruby + + lambda { m(&@a) }.should raise_error(ArgumentError) + lambda { m(1, 2, &@a) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + @a = lambda { |a, | a } + ruby + + @a.(1).should == 1 + @a.([1, 2]).should == [1, 2] + + lambda { @a.() }.should raise_error(ArgumentError) + lambda { @a.(1, 2) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(a) yield a end + def m2() yield end + + @a = lambda { |a, | a } + ruby + + m(1, &@a).should == 1 + m([1, 2], &@a).should == [1, 2] + + lambda { m2(&@a) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + @a = lambda { |(a)| a } + ruby + + @a.(1).should == 1 + @a.([1, 2, 3]).should == 1 + end + + evaluate <<-ruby do + @a = lambda { |(*a, b)| [a, b] } + ruby + + @a.(1).should == [[], 1] + @a.([1, 2, 3]).should == [[1, 2], 3] + end + + evaluate <<-ruby do + @a = lambda { |a={}| a } + ruby + + @a.().should == {} + @a.(2).should == 2 + end + + evaluate <<-ruby do + @a = lambda { |*| } + ruby + + @a.().should be_nil + @a.(1).should be_nil + @a.(1, 2, 3).should be_nil + end + + evaluate <<-ruby do + @a = lambda { |*a| a } + ruby + + @a.().should == [] + @a.(1).should == [1] + @a.(1, 2, 3).should == [1, 2, 3] + end + + evaluate <<-ruby do + @a = lambda { |a:| a } + ruby + + lambda { @a.() }.should raise_error(ArgumentError) + @a.(a: 1).should == 1 + end + + evaluate <<-ruby do + @a = lambda { |a: 1| a } + ruby + + @a.().should == 1 + @a.(a: 2).should == 2 + end + + evaluate <<-ruby do + @a = lambda { |**| } + ruby + + @a.().should be_nil + @a.(a: 1, b: 2).should be_nil + lambda { @a.(1) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + @a = lambda { |**k| k } + ruby + + @a.().should == {} + @a.(a: 1, b: 2).should == {a: 1, b: 2} + end + + evaluate <<-ruby do + @a = lambda { |&b| b } + ruby + + @a.().should be_nil + @a.() { }.should be_an_instance_of(Proc) + end + + evaluate <<-ruby do + @a = lambda { |a, b| [a, b] } + ruby + + @a.(1, 2).should == [1, 2] + end + + evaluate <<-ruby do + @a = lambda do |(a, b, *c, d), (*e, f, g), (*h)| + [a, b, c, d, e, f, g, h] + end + ruby + + @a.(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]] + result = @a.([1, 2, 3], [4, 5, 6, 7, 8], [9, 10]) + result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]] + end + + evaluate <<-ruby do + @a = lambda do |a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))| + [a, b, c, d, e, f, g, h, i, j] + end + ruby + + @a.(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil] + result = @a.(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]]) + result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12] + end + + evaluate <<-ruby do + @a = lambda { |*, **k| k } + ruby + + @a.().should == {} + @a.(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5} + + h = mock("keyword splat") + h.should_receive(:to_hash).and_return({a: 1}) + @a.(h).should == {a: 1} + end + + evaluate <<-ruby do + @a = lambda { |*, &b| b } + ruby + + @a.().should be_nil + @a.(1, 2, 3, 4).should be_nil + @a.(&(l = ->{})).should equal(l) + end + + evaluate <<-ruby do + @a = lambda { |a:, b:| [a, b] } + ruby + + @a.(a: 1, b: 2).should == [1, 2] + end + + evaluate <<-ruby do + @a = lambda { |a:, b: 1| [a, b] } + ruby + + @a.(a: 1).should == [1, 1] + @a.(a: 1, b: 2).should == [1, 2] + end + + evaluate <<-ruby do + @a = lambda { |a: 1, b:| [a, b] } + ruby + + @a.(b: 0).should == [1, 0] + @a.(b: 2, a: 3).should == [3, 2] + end + + evaluate <<-ruby do + @a = lambda do |a: (@a = -> (a: 1) { a }), b:| + [a, b] + end + ruby + + @a.(a: 2, b: 3).should == [2, 3] + @a.(b: 1).should == [@a, 1] + + # Note the default value of a: in the original method. + @a.().should == 1 + end + + evaluate <<-ruby do + @a = lambda { |a: 1, b: 2| [a, b] } + ruby + + @a.().should == [1, 2] + @a.(b: 3, a: 4).should == [4, 3] + end + + evaluate <<-ruby do + @a = lambda do |a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l| + [a, b, c, d, e, f, g, h, k, l] + end + ruby + + result = @a.(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{})) + result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l] + end + + evaluate <<-ruby do + @a = lambda do |a, b=1, *c, d, e:, f: 2, g:, **k, &l| + [a, b, c, d, e, f, g, k, l] + end + ruby + + result = @a.(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{})) + result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l] + end + end +end diff --git a/spec/rubyspec/language/line_spec.rb b/spec/rubyspec/language/line_spec.rb new file mode 100644 index 0000000000..d9fd307dab --- /dev/null +++ b/spec/rubyspec/language/line_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/code_loading', __FILE__) +require File.expand_path('../shared/__LINE__', __FILE__) + +describe "The __LINE__ pseudo-variable" do + it "raises a SyntaxError if assigned to" do + lambda { eval("__LINE__ = 1") }.should raise_error(SyntaxError) + end + + before :each do + ScratchPad.record [] + end + + after :each do + ScratchPad.clear + end + + it "equals the line number of the text inside an eval" do + eval <<-EOC +ScratchPad << __LINE__ + +# line 3 + +ScratchPad << __LINE__ + EOC + + ScratchPad.recorded.should == [1, 5] + end +end + +describe "The __LINE__ pseudo-variable" do + it_behaves_like :language___LINE__, :require, CodeLoadingSpecs::Method.new +end + +describe "The __LINE__ pseudo-variable" do + it_behaves_like :language___LINE__, :require, Kernel +end + +describe "The __LINE__ pseudo-variable" do + it_behaves_like :language___LINE__, :load, CodeLoadingSpecs::Method.new +end + +describe "The __LINE__ pseudo-variable" do + it_behaves_like :language___LINE__, :load, Kernel +end diff --git a/spec/rubyspec/language/loop_spec.rb b/spec/rubyspec/language/loop_spec.rb new file mode 100644 index 0000000000..4e60e0d8e6 --- /dev/null +++ b/spec/rubyspec/language/loop_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The loop expression" do + it "repeats the given block until a break is called" do + outer_loop = 0 + loop do + outer_loop += 1 + break if outer_loop == 10 + end + outer_loop.should == 10 + end + + it "executes code in its own scope" do + loop do + inner_loop = 123 + break + end + lambda { inner_loop }.should raise_error(NameError) + end + + it "returns the value passed to break if interrupted by break" do + loop do + break 123 + end.should == 123 + end + + it "returns nil if interrupted by break with no arguments" do + loop do + break + end.should == nil + end + + it "skips to end of body with next" do + a = [] + i = 0 + loop do + break if (i+=1) >= 5 + next if i == 3 + a << i + end + a.should == [1, 2, 4] + end + + it "restarts the current iteration with redo" do + a = [] + loop do + a << 1 + redo if a.size < 2 + a << 2 + break if a.size == 3 + end + a.should == [1, 1, 2] + end + + it "uses a spaghetti nightmare of redo, next and break" do + a = [] + loop do + a << 1 + redo if a.size == 1 + a << 2 + next if a.size == 3 + a << 3 + break if a.size > 6 + end + a.should == [1, 1, 2, 1, 2, 3, 1, 2, 3] + end +end diff --git a/spec/rubyspec/language/magic_comment_spec.rb b/spec/rubyspec/language/magic_comment_spec.rb new file mode 100644 index 0000000000..2f6e3b5c3a --- /dev/null +++ b/spec/rubyspec/language/magic_comment_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "Magic comment" do + it "is optional" do + eval("__ENCODING__").should be_an_instance_of(Encoding) + end + + it "determines __ENCODING__" do + eval(<foo)(?bar)?/ + @string = "foofoo" + end + + describe "on syntax of /regexp/ =~ string_variable" do + it "sets local variables by the captured pairs" do + /(?foo)(?bar)?/ =~ @string + local_variables.should == [:matched, :unmatched] + matched.should == "foo" + unmatched.should == nil + end + end + + describe "on syntax of string_variable =~ /regexp/" do + it "does not set local variables" do + @string =~ /(?foo)(?bar)?/ + local_variables.should == [] + end + end + + describe "on syntax of regexp_variable =~ string_variable" do + it "does not set local variables" do + @regexp =~ @string + local_variables.should == [] + end + end + + describe "on the method calling" do + it "does not set local variables" do + @regexp.=~(@string) + local_variables.should == [] + + @regexp.send :=~, @string + local_variables.should == [] + end + end +end diff --git a/spec/rubyspec/language/metaclass_spec.rb b/spec/rubyspec/language/metaclass_spec.rb new file mode 100644 index 0000000000..b3bcd9ef18 --- /dev/null +++ b/spec/rubyspec/language/metaclass_spec.rb @@ -0,0 +1,143 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/class', __FILE__) +require File.expand_path('../fixtures/metaclass', __FILE__) + +describe "self in a metaclass body (class << obj)" do + it "is TrueClass for true" do + class << true; self; end.should == TrueClass + end + + it "is FalseClass for false" do + class << false; self; end.should == FalseClass + end + + it "is NilClass for nil" do + class << nil; self; end.should == NilClass + end + + it "raises a TypeError for numbers" do + lambda { class << 1; self; end }.should raise_error(TypeError) + end + + it "raises a TypeError for symbols" do + lambda { class << :symbol; self; end }.should raise_error(TypeError) + end + + it "is a singleton Class instance" do + cls = class << mock('x'); self; end + cls.is_a?(Class).should == true + cls.should_not equal(Object) + end +end + +describe "A constant on a metaclass" do + before :each do + @object = Object.new + class << @object + CONST = self + end + end + + it "can be accessed after the metaclass body is reopened" do + class << @object + CONST.should == self + end + end + + it "can be accessed via self::CONST" do + class << @object + self::CONST.should == self + end + end + + it "can be accessed via const_get" do + class << @object + const_get(:CONST).should == self + end + end + + it "is not defined on the object's class" do + @object.class.const_defined?(:CONST).should be_false + end + + it "is not defined in the metaclass opener's scope" do + class << @object + CONST + end + lambda { CONST }.should raise_error(NameError) + end + + it "cannot be accessed via object::CONST" do + lambda do + @object::CONST + end.should raise_error(TypeError) + end + + it "raises a NameError for anonymous_module::CONST" do + @object = Class.new + class << @object + CONST = 100 + end + + lambda do + @object::CONST + end.should raise_error(NameError) + end + + it "appears in the metaclass constant list" do + constants = class << @object; constants; end + constants.should include(:CONST) + end + + it "does not appear in the object's class constant list" do + @object.class.constants.should_not include(:CONST) + end + + it "is not preserved when the object is duped" do + @object = @object.dup + + lambda do + class << @object; CONST; end + end.should raise_error(NameError) + end + + it "is preserved when the object is cloned" do + @object = @object.clone + + class << @object + CONST.should_not be_nil + end + end +end + +describe "calling methods on the metaclass" do + + it "calls a method on the metaclass" do + MetaClassSpecs::A.cheese.should == 'edam' + MetaClassSpecs::B.cheese.should == 'stilton' + end + + it "calls a method on the instance's metaclass" do + b = MetaClassSpecs::B.new + b_meta = MetaClassSpecs.metaclass_of b + b_meta.send(:define_method, :cheese) {'cheshire'} + b.cheese.should == 'cheshire' + end + + it "calls a method in deeper chains of metaclasses" do + b = MetaClassSpecs::B.new + b_meta = MetaClassSpecs.metaclass_of b + b_meta_meta = MetaClassSpecs.metaclass_of b_meta + b_meta_meta.send(:define_method, :cheese) {'gouda'} + b_meta.cheese.should == 'gouda' + + b_meta_meta_meta = MetaClassSpecs.metaclass_of b_meta_meta + b_meta_meta_meta.send(:define_method, :cheese) {'wensleydale'} + b_meta_meta.cheese.should == 'wensleydale' + end + + it "calls a method defined on the metaclass of the metaclass" do + d_meta = MetaClassSpecs::D.singleton_class + d_meta.ham.should == 'iberico' + end +end diff --git a/spec/rubyspec/language/method_spec.rb b/spec/rubyspec/language/method_spec.rb new file mode 100644 index 0000000000..c92a8ee51b --- /dev/null +++ b/spec/rubyspec/language/method_spec.rb @@ -0,0 +1,1290 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "A method send" do + evaluate <<-ruby do + def m(a) a end + ruby + + a = b = m 1 + a.should == 1 + b.should == 1 + end + + context "with a single splatted Object argument" do + before :all do + def m(a) a end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + m(*x).should equal(x) + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([1]) + + m(*x).should == 1 + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + m(*x).should == x + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { m(*x) }.should raise_error(TypeError) + end + end + + context "with a leading splatted Object argument" do + before :all do + def m(a, b, *c, d, e) [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + m(*x, 1, 2, 3).should == [x, 1, [], 2, 3] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([1]) + + m(*x, 2, 3, 4).should == [1, 2, [], 3, 4] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + m(*x, 2, 3, 4).should == [x, 2, [], 3, 4] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { m(*x, 2, 3) }.should raise_error(TypeError) + end + end + + context "with a middle splatted Object argument" do + before :all do + def m(a, b, *c, d, e) [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + m(1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([5, 6, 7]) + + m(1, 2, *x, 3).should == [1, 2, [5, 6], 7, 3] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + m(1, 2, *x, 4).should == [1, 2, [], x, 4] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { m(1, *x, 2, 3) }.should raise_error(TypeError) + end + + it "copies the splatted array" do + args = [3, 4] + m(1, 2, *args, 4, 5).should == [1, 2, [3, 4], 4, 5] + m(1, 2, *args, 4, 5)[2].should_not equal(args) + end + + it "allows an array being splatted to be modified by another argument" do + args = [3, 4] + m(1, args.shift, *args, 4, 5).should == [1, 3, [4], 4, 5] + end + end + + context "with a trailing splatted Object argument" do + before :all do + def m(a, *b, c) [a, b, c] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + m(1, 2, *x).should == [1, [2], x] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([5, 6, 7]) + + m(1, 2, *x).should == [1, [2, 5, 6], 7] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + m(1, 2, *x, 4).should == [1, [2, x], 4] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { m(1, 2, *x) }.should raise_error(TypeError) + end + end +end + +describe "An element assignment method send" do + before :each do + ScratchPad.clear + end + + context "with a single splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.[]=(a, b) ScratchPad.record [a, b] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o[*x] = 1).should == 1 + ScratchPad.recorded.should == [x, 1] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([1]) + + (@o[*x] = 2).should == 2 + ScratchPad.recorded.should == [1, 2] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o[*x] = 1).should == 1 + ScratchPad.recorded.should == [x, 1] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o[*x] = 1 }.should raise_error(TypeError) + end + end + + context "with a leading splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o[*x, 2, 3, 4] = 1).should == 1 + ScratchPad.recorded.should == [x, 2, [3], 4, 1] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([1, 2, 3]) + + (@o[*x, 4, 5] = 6).should == 6 + ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o[*x, 2, 3, 4] = 5).should == 5 + ScratchPad.recorded.should == [x, 2, [3], 4, 5] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o[*x, 2, 3] = 4 }.should raise_error(TypeError) + end + end + + context "with a middle splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o[1, *x, 2, 3] = 4).should == 4 + ScratchPad.recorded.should == [1, x, [2], 3, 4] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([2, 3]) + + (@o[1, *x, 4] = 5).should == 5 + ScratchPad.recorded.should == [1, 2, [3], 4, 5] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o[1, 2, *x, 3] = 4).should == 4 + ScratchPad.recorded.should == [1, 2, [x], 3, 4] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o[1, 2, *x, 3] = 4 }.should raise_error(TypeError) + end + end + + context "with a trailing splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o[1, 2, 3, 4, *x] = 5).should == 5 + ScratchPad.recorded.should == [1, 2, [3, 4], x, 5] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([4, 5]) + + (@o[1, 2, 3, *x] = 6).should == 6 + ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o[1, 2, 3, *x] = 4).should == 4 + ScratchPad.recorded.should == [1, 2, [3], x, 4] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o[1, 2, 3, *x] = 4 }.should raise_error(TypeError) + end + end +end + +describe "An attribute assignment method send" do + context "with a single splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.m=(a, b) [a, b] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o.send :m=, *x, 1).should == [x, 1] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([1]) + + (@o.send :m=, *x, 2).should == [1, 2] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o.send :m=, *x, 1).should == [x, 1] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o.send :m=, *x, 1 }.should raise_error(TypeError) + end + end + + context "with a leading splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o.send :m=, *x, 2, 3, 4, 1).should == [x, 2, [3], 4, 1] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([1, 2, 3]) + + (@o.send :m=, *x, 4, 5, 6).should == [1, 2, [3, 4], 5, 6] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o.send :m=, *x, 2, 3, 4, 5).should == [x, 2, [3], 4, 5] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o.send :m=, *x, 2, 3, 4 }.should raise_error(TypeError) + end + end + + context "with a middle splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o.send :m=, 1, *x, 2, 3, 4).should == [1, x, [2], 3, 4] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([2, 3]) + + (@o.send :m=, 1, *x, 4, 5).should == [1, 2, [3], 4, 5] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o.send :m=, 1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o.send :m=, 1, 2, *x, 3, 4 }.should raise_error(TypeError) + end + end + + context "with a trailing splatted Object argument" do + before :all do + @o = mock("element set receiver") + def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end + end + + it "does not call #to_ary" do + x = mock("splat argument") + x.should_not_receive(:to_ary) + + (@o.send :m=, 1, 2, 3, 4, *x, 5).should == [1, 2, [3, 4], x, 5] + end + + it "calls #to_a" do + x = mock("splat argument") + x.should_receive(:to_a).and_return([4, 5]) + + (@o.send :m=, 1, 2, 3, *x, 6).should == [1, 2, [3, 4], 5, 6] + end + + it "wraps the argument in an Array if #to_a returns nil" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(nil) + + (@o.send :m=, 1, 2, 3, *x, 4).should == [1, 2, [3], x, 4] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("splat argument") + x.should_receive(:to_a).and_return(1) + + lambda { @o.send :m=, 1, 2, 3, *x, 4 }.should raise_error(TypeError) + end + end +end + +describe "A method" do + SpecEvaluate.desc = "for definition" + + context "assigns no local variables" do + evaluate <<-ruby do + def m + end + ruby + + m.should be_nil + end + + evaluate <<-ruby do + def m() + end + ruby + + m.should be_nil + end + end + + context "assigns local variables from method parameters" do + evaluate <<-ruby do + def m(a) a end + ruby + + m((args = 1, 2, 3)).should equal(args) + end + + evaluate <<-ruby do + def m((a)) a end + ruby + + m(1).should == 1 + m([1, 2, 3]).should == 1 + end + + evaluate <<-ruby do + def m((*a, b)) [a, b] end + ruby + + m(1).should == [[], 1] + m([1, 2, 3]).should == [[1, 2], 3] + end + + evaluate <<-ruby do + def m(a=1) a end + ruby + + m().should == 1 + m(2).should == 2 + end + + evaluate <<-ruby do + def m(*) end + ruby + + m().should be_nil + m(1).should be_nil + m(1, 2, 3).should be_nil + end + + evaluate <<-ruby do + def m(*a) a end + ruby + + m().should == [] + m(1).should == [1] + m(1, 2, 3).should == [1, 2, 3] + end + + evaluate <<-ruby do + def m(a:) a end + ruby + + lambda { m() }.should raise_error(ArgumentError) + m(a: 1).should == 1 + lambda { m("a" => 1, a: 1) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(a: 1) a end + ruby + + m().should == 1 + m(a: 2).should == 2 + end + + evaluate <<-ruby do + def m(**) end + ruby + + m().should be_nil + m(a: 1, b: 2).should be_nil + lambda { m(1) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(**k) k end + ruby + + m().should == {} + m(a: 1, b: 2).should == { a: 1, b: 2 } + lambda { m(2) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(&b) b end + ruby + + m { }.should be_an_instance_of(Proc) + end + + evaluate <<-ruby do + def m(a, b) [a, b] end + ruby + + m(1, 2).should == [1, 2] + end + + evaluate <<-ruby do + def m(a, (b, c)) [a, b, c] end + ruby + + m(1, 2).should == [1, 2, nil] + m(1, [2, 3, 4]).should == [1, 2, 3] + end + + evaluate <<-ruby do + def m((a), (b)) [a, b] end + ruby + + m(1, 2).should == [1, 2] + m([1, 2], [3, 4]).should == [1, 3] + end + + evaluate <<-ruby do + def m((*), (*)) end + ruby + + m(2, 3).should be_nil + m([2, 3, 4], [5, 6]).should be_nil + lambda { m a: 1 }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m((*a), (*b)) [a, b] end + ruby + + m(1, 2).should == [[1], [2]] + m([1, 2], [3, 4]).should == [[1, 2], [3, 4]] + end + + evaluate <<-ruby do + def m((a, b), (c, d)) + [a, b, c, d] + end + ruby + + m(1, 2).should == [1, nil, 2, nil] + m([1, 2, 3], [4, 5, 6]).should == [1, 2, 4, 5] + end + + evaluate <<-ruby do + def m((a, *b), (*c, d)) + [a, b, c, d] + end + ruby + + m(1, 2).should == [1, [], [], 2] + m([1, 2, 3], [4, 5, 6]).should == [1, [2, 3], [4, 5], 6] + end + + evaluate <<-ruby do + def m((a, b, *c, d), (*e, f, g), (*h)) + [a, b, c, d, e, f, g, h] + end + ruby + + m(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]] + result = m([1, 2, 3], [4, 5, 6, 7, 8], [9, 10]) + result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]] + end + + evaluate <<-ruby do + def m(a, (b, (c, *d), *e)) + [a, b, c, d, e] + end + ruby + + m(1, 2).should == [1, 2, nil, [], []] + m(1, [2, [3, 4, 5], 6, 7, 8]).should == [1, 2, 3, [4, 5], [6, 7, 8]] + end + + evaluate <<-ruby do + def m(a, (b, (c, *d, (e, (*f)), g), (h, (i, j)))) + [a, b, c, d, e, f, g, h, i, j] + end + ruby + + m(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil] + result = m(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]]) + result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12] + end + + evaluate <<-ruby do + def m(a, b=1) [a, b] end + ruby + + m(2).should == [2, 1] + m(1, 2).should == [1, 2] + end + + evaluate <<-ruby do + def m(a, *) a end + ruby + + m(1).should == 1 + m(1, 2, 3).should == 1 + end + + evaluate <<-ruby do + def m(a, *b) [a, b] end + ruby + + m(1).should == [1, []] + m(1, 2, 3).should == [1, [2, 3]] + end + + evaluate <<-ruby do + def m(a, b:) [a, b] end + ruby + + m(1, b: 2).should == [1, 2] + lambda { m("a" => 1, b: 2) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(a, b: 1) [a, b] end + ruby + + m(2).should == [2, 1] + m(1, b: 2).should == [1, 2] + m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, 1] + end + + evaluate <<-ruby do + def m(a, **) a end + ruby + + m(1).should == 1 + m(1, a: 2, b: 3).should == 1 + m("a" => 1, b: 2).should == {"a" => 1, b: 2} + end + + evaluate <<-ruby do + def m(a, **k) [a, k] end + ruby + + m(1).should == [1, {}] + m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}] + m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, {}] + end + + evaluate <<-ruby do + def m(a, &b) [a, b] end + ruby + + m(1).should == [1, nil] + m(1, &(l = -> {})).should == [1, l] + end + + evaluate <<-ruby do + def m(a=1, b) [a, b] end + ruby + + m(2).should == [1, 2] + m(2, 3).should == [2, 3] + end + + evaluate <<-ruby do + def m(a=1, *) a end + ruby + + m().should == 1 + m(2, 3, 4).should == 2 + end + + evaluate <<-ruby do + def m(a=1, *b) [a, b] end + ruby + + m().should == [1, []] + m(2, 3, 4).should == [2, [3, 4]] + end + + evaluate <<-ruby do + def m(a=1, (b, c)) [a, b, c] end + ruby + + m(2).should == [1, 2, nil] + m(2, 3).should == [2, 3, nil] + m(2, [3, 4, 5]).should == [2, 3, 4] + end + + evaluate <<-ruby do + def m(a=1, (b, (c, *d))) [a, b, c, d] end + ruby + + m(2).should == [1, 2, nil, []] + m(2, 3).should == [2, 3, nil, []] + m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6]] + end + + evaluate <<-ruby do + def m(a=1, (b, (c, *d), *e)) [a, b, c, d, e] end + ruby + + m(2).should == [1, 2, nil, [], []] + m(2, [3, 4, 5, 6]).should == [2, 3, 4, [], [5, 6]] + m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6], [7]] + end + + evaluate <<-ruby do + def m(a=1, (b), (c)) [a, b, c] end + ruby + + m(2, 3).should == [1, 2, 3] + m(2, 3, 4).should == [2, 3, 4] + m(2, [3, 4], [5, 6, 7]).should == [2, 3, 5] + end + + evaluate <<-ruby do + def m(a=1, (*b), (*c)) [a, b, c] end + ruby + + lambda { m() }.should raise_error(ArgumentError) + lambda { m(2) }.should raise_error(ArgumentError) + m(2, 3).should == [1, [2], [3]] + m(2, [3, 4], [5, 6]).should == [2, [3, 4], [5, 6]] + end + + evaluate <<-ruby do + def m(a=1, (b, c), (d, e)) [a, b, c, d, e] end + ruby + + m(2, 3).should == [1, 2, nil, 3, nil] + m(2, [3, 4, 5], [6, 7, 8]).should == [2, 3, 4, 6, 7] + end + + evaluate <<-ruby do + def m(a=1, (b, *c), (*d, e)) + [a, b, c, d, e] + end + ruby + + m(1, 2).should == [1, 1, [], [], 2] + m(1, [2, 3], [4, 5, 6]).should == [1, 2, [3], [4, 5], 6] + end + + evaluate <<-ruby do + def m(a=1, (b, *c), (d, (*e, f))) + [a, b, c, d, e, f] + end + ruby + + m(1, 2).should == [1, 1, [], 2, [], nil] + m(nil, nil).should == [1, nil, [], nil, [], nil] + result = m([1, 2, 3], [4, 5, 6], [7, 8, 9]) + result.should == [[1, 2, 3], 4, [5, 6], 7, [], 8] + end + + evaluate <<-ruby do + def m(a=1, b:) [a, b] end + ruby + + m(b: 2).should == [1, 2] + m(2, b: 1).should == [2, 1] + m("a" => 1, b: 2).should == [{"a" => 1}, 2] + end + + evaluate <<-ruby do + def m(a=1, b: 2) [a, b] end + ruby + + m().should == [1, 2] + m(2).should == [2, 2] + m(b: 3).should == [1, 3] + m("a" => 1, b: 2).should == [{"a" => 1}, 2] + end + + evaluate <<-ruby do + def m(a=1, **) a end + ruby + + m().should == 1 + m(2, a: 1, b: 0).should == 2 + m("a" => 1, a: 2).should == {"a" => 1} + end + + evaluate <<-ruby do + def m(a=1, **k) [a, k] end + ruby + + m().should == [1, {}] + m(2, a: 1, b: 2).should == [2, {a: 1, b: 2}] + end + + evaluate <<-ruby do + def m(a=1, &b) [a, b] end + ruby + + m().should == [1, nil] + m(&(l = -> {})).should == [1, l] + + p = -> {} + l = mock("to_proc") + l.should_receive(:to_proc).and_return(p) + m(&l).should == [1, p] + end + + evaluate <<-ruby do + def m(*, a) a end + ruby + + m(1).should == 1 + m(1, 2, 3).should == 3 + end + + evaluate <<-ruby do + def m(*a, b) [a, b] end + ruby + + m(1).should == [[], 1] + m(1, 2, 3).should == [[1, 2], 3] + end + + evaluate <<-ruby do + def m(*, a:) a end + ruby + + m(a: 1).should == 1 + m(1, 2, a: 3).should == 3 + m("a" => 1, a: 2).should == 2 + end + + evaluate <<-ruby do + def m(*a, b:) [a, b] end + ruby + + m(b: 1).should == [[], 1] + m(1, 2, b: 3).should == [[1, 2], 3] + m("a" => 1, b: 2).should == [[{"a" => 1}], 2] + end + + evaluate <<-ruby do + def m(*, a: 1) a end + ruby + + m().should == 1 + m(1, 2).should == 1 + m(a: 2).should == 2 + m(1, a: 2).should == 2 + m("a" => 1, a: 2).should == 2 + end + + evaluate <<-ruby do + def m(*a, b: 1) [a, b] end + ruby + + m().should == [[], 1] + m(1, 2, 3, b: 4).should == [[1, 2, 3], 4] + m("a" => 1, b: 2).should == [[{"a" => 1}], 2] + + a = mock("splat") + a.should_not_receive(:to_ary) + m(*a).should == [[a], 1] + end + + evaluate <<-ruby do + def m(*, **) end + ruby + + m().should be_nil + m(a: 1, b: 2).should be_nil + m(1, 2, 3, a: 4, b: 5).should be_nil + + h = mock("keyword splat") + h.should_receive(:to_hash).and_return({a: 1}) + m(h).should be_nil + + h = mock("keyword splat") + error = RuntimeError.new("error while converting to a hash") + h.should_receive(:to_hash).and_raise(error) + lambda { m(h) }.should raise_error(error) + end + + evaluate <<-ruby do + def m(*a, **) a end + ruby + + m().should == [] + m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3] + m("a" => 1, a: 1).should == [{"a" => 1}] + m(1, **{a: 2}).should == [1] + + h = mock("keyword splat") + h.should_receive(:to_hash) + lambda { m(**h) }.should raise_error(TypeError) + end + + evaluate <<-ruby do + def m(*, **k) k end + ruby + + m().should == {} + m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5} + m("a" => 1, a: 1).should == {a: 1} + + h = mock("keyword splat") + h.should_receive(:to_hash).and_return({a: 1}) + m(h).should == {a: 1} + end + + evaluate <<-ruby do + def m(a = nil, **k) [a, k] end + ruby + + m().should == [nil, {}] + m("a" => 1).should == [{"a" => 1}, {}] + m(a: 1).should == [nil, {a: 1}] + m("a" => 1, a: 1).should == [{"a" => 1}, {a: 1}] + m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}] + m({a: 1}, {}).should == [{a: 1}, {}] + + h = {"a" => 1, b: 2} + m(h).should == [{"a" => 1}, {b: 2}] + h.should == {"a" => 1, b: 2} + + h = {"a" => 1} + m(h).first.should == h + + h = {} + r = m(h) + r.first.should be_nil + r.last.should == {} + + hh = {} + h = mock("keyword splat empty hash") + h.should_receive(:to_hash).and_return(hh) + r = m(h) + r.first.should be_nil + r.last.should == {} + + h = mock("keyword splat") + h.should_receive(:to_hash).and_return({"a" => 1, a: 2}) + m(h).should == [{"a" => 1}, {a: 2}] + end + + evaluate <<-ruby do + def m(*a, **k) [a, k] end + ruby + + m().should == [[], {}] + m(1).should == [[1], {}] + m(a: 1, b: 2).should == [[], {a: 1, b: 2}] + m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}] + + m("a" => 1).should == [[{"a" => 1}], {}] + m(a: 1).should == [[], {a: 1}] + m("a" => 1, a: 1).should == [[{"a" => 1}], {a: 1}] + m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}] + m({a: 1}, {}).should == [[{a: 1}], {}] + m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}] + + bo = BasicObject.new + def bo.to_a; [1, 2, 3]; end + def bo.to_hash; {:b => 2, :c => 3}; end + + m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}] + end + + evaluate <<-ruby do + def m(*, &b) b end + ruby + + m().should be_nil + m(1, 2, 3, 4).should be_nil + m(&(l = ->{})).should equal(l) + end + + evaluate <<-ruby do + def m(*a, &b) [a, b] end + ruby + + m().should == [[], nil] + m(1).should == [[1], nil] + m(1, 2, 3, &(l = -> {})).should == [[1, 2, 3], l] + end + + evaluate <<-ruby do + def m(a:, b:) [a, b] end + ruby + + m(a: 1, b: 2).should == [1, 2] + lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(a:, b: 1) [a, b] end + ruby + + m(a: 1).should == [1, 1] + m(a: 1, b: 2).should == [1, 2] + lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(a:, **) a end + ruby + + m(a: 1).should == 1 + m(a: 1, b: 2).should == 1 + lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(a:, **k) [a, k] end + ruby + + m(a: 1).should == [1, {}] + m(a: 1, b: 2, c: 3).should == [1, {b: 2, c: 3}] + lambda { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError) + end + + evaluate <<-ruby do + def m(a:, &b) [a, b] end + ruby + + m(a: 1).should == [1, nil] + m(a: 1, &(l = ->{})).should == [1, l] + end + + evaluate <<-ruby do + def m(a: 1, b:) [a, b] end + ruby + + m(b: 0).should == [1, 0] + m(b: 2, a: 3).should == [3, 2] + end + + evaluate <<-ruby do + def m(a: def m(a: 1) a end, b:) + [a, b] + end + ruby + + m(a: 2, b: 3).should == [2, 3] + m(b: 1).should == [:m, 1] + + # Note the default value of a: in the original method. + m().should == 1 + end + + evaluate <<-ruby do + def m(a: 1, b: 2) [a, b] end + ruby + + m().should == [1, 2] + m(b: 3, a: 4).should == [4, 3] + end + + evaluate <<-ruby do + def m(a: 1, **) a end + ruby + + m().should == 1 + m(a: 2, b: 1).should == 2 + end + + evaluate <<-ruby do + def m(a: 1, **k) [a, k] end + ruby + + m(b: 2, c: 3).should == [1, {b: 2, c: 3}] + end + + evaluate <<-ruby do + def m(a: 1, &b) [a, b] end + ruby + + m(&(l = ->{})).should == [1, l] + m().should == [1, nil] + end + + evaluate <<-ruby do + def m(**, &b) b end + ruby + + m(a: 1, b: 2, &(l = ->{})).should == l + end + + evaluate <<-ruby do + def m(**k, &b) [k, b] end + ruby + + m(a: 1, b: 2).should == [{ a: 1, b: 2}, nil] + end + + evaluate <<-ruby do + def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l) + [a, b, c, d, e, f, g, h, k, l] + end + ruby + + result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{})) + result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l] + end + + evaluate <<-ruby do + def m a, b=1, *c, d, e:, f: 2, g:, **k, &l + [a, b, c, d, e, f, g, k, l] + end + ruby + + result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{})) + result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l] + end + + evaluate <<-ruby do + def m (a, b = nil, c = nil, d, e: nil, **f) + [a, b, c, d, e, f] + end + ruby + + result = m(1, 2) + result.should == [1, nil, nil, 2, nil, {}] + end + end +end + +describe "A method call with a space between method name and parentheses" do + before(:each) do + def m(*args) + args + end + + def n(value, &block) + [value, block.call] + end + end + + context "when no arguments provided" do + it "assigns nil" do + args = m () + args.should == [nil] + end + end + + context "when a single argument provided" do + it "assigns it" do + args = m (1 == 1 ? true : false) + args.should == [true] + end + end + + context "when 2+ arguments provided" do + it "raises a syntax error" do + lambda { + eval("m (1, 2)") + }.should raise_error(SyntaxError) + + lambda { + eval("m (1, 2, 3)") + }.should raise_error(SyntaxError) + end + end + + it "allows to pass a block with curly braces" do + args = n () { :block_value } + args.should == [nil, :block_value] + + args = n (1) { :block_value } + args.should == [1, :block_value] + end + + it "allows to pass a block with do/end" do + args = n () do + :block_value + end + args.should == [nil, :block_value] + + args = n (1) do + :block_value + end + args.should == [1, :block_value] + end +end + +describe "An array-dereference method ([])" do + SpecEvaluate.desc = "for definition" + + context "received the passed-in block" do + evaluate <<-ruby do + def [](*, &b) + b.call + end + ruby + pr = proc {:ok} + + self[&pr].should == :ok + self['foo', &pr].should == :ok + self.[](&pr).should == :ok + self.[]('foo', &pr).should == :ok + end + + evaluate <<-ruby do + def [](*) + yield + end + ruby + pr = proc {:ok} + + self[&pr].should == :ok + self['foo', &pr].should == :ok + self.[](&pr).should == :ok + self.[]('foo', &pr).should == :ok + end + end +end diff --git a/spec/rubyspec/language/module_spec.rb b/spec/rubyspec/language/module_spec.rb new file mode 100644 index 0000000000..d5ca71b3b4 --- /dev/null +++ b/spec/rubyspec/language/module_spec.rb @@ -0,0 +1,91 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/module', __FILE__) + +describe "The module keyword" do + it "creates a new module without semicolon" do + module ModuleSpecsKeywordWithoutSemicolon end + ModuleSpecsKeywordWithoutSemicolon.should be_an_instance_of(Module) + end + + it "creates a new module with a non-qualified constant name" do + module ModuleSpecsToplevel; end + ModuleSpecsToplevel.should be_an_instance_of(Module) + end + + it "creates a new module with a qualified constant name" do + module ModuleSpecs::Nested; end + ModuleSpecs::Nested.should be_an_instance_of(Module) + end + + it "creates a new module with a variable qualified constant name" do + m = Module.new + module m::N; end + m::N.should be_an_instance_of(Module) + end + + it "reopens an existing module" do + module ModuleSpecs; Reopened = true; end + ModuleSpecs::Reopened.should be_true + end + + it "reopens a module included in Object" do + module IncludedModuleSpecs; Reopened = true; end + ModuleSpecs::IncludedInObject::IncludedModuleSpecs::Reopened.should be_true + end + + it "raises a TypeError if the constant is a Class" do + lambda do + module ModuleSpecs::Modules::Klass; end + end.should raise_error(TypeError) + end + + it "raises a TypeError if the constant is a String" do + lambda { module ModuleSpecs::Modules::A; end }.should raise_error(TypeError) + end + + it "raises a TypeError if the constant is a Fixnum" do + lambda { module ModuleSpecs::Modules::B; end }.should raise_error(TypeError) + end + + it "raises a TypeError if the constant is nil" do + lambda { module ModuleSpecs::Modules::C; end }.should raise_error(TypeError) + end + + it "raises a TypeError if the constant is true" do + lambda { module ModuleSpecs::Modules::D; end }.should raise_error(TypeError) + end + + it "raises a TypeError if the constant is false" do + lambda { module ModuleSpecs::Modules::D; end }.should raise_error(TypeError) + end +end + +describe "Assigning an anonymous module to a constant" do + it "sets the name of the module" do + mod = Module.new + mod.name.should be_nil + + ::ModuleSpecs_CS1 = mod + mod.name.should == "ModuleSpecs_CS1" + end + + it "does not set the name of a module scoped by an anonymous module" do + a, b = Module.new, Module.new + a::B = b + b.name.should be_nil + end + + it "sets the name of contained modules when assigning a toplevel anonymous module" do + a, b, c, d = Module.new, Module.new, Module.new, Module.new + a::B = b + a::B::C = c + a::B::C::E = c + a::D = d + + ::ModuleSpecs_CS2 = a + a.name.should == "ModuleSpecs_CS2" + b.name.should == "ModuleSpecs_CS2::B" + c.name.should == "ModuleSpecs_CS2::B::C" + d.name.should == "ModuleSpecs_CS2::D" + end +end diff --git a/spec/rubyspec/language/next_spec.rb b/spec/rubyspec/language/next_spec.rb new file mode 100644 index 0000000000..67da5224dc --- /dev/null +++ b/spec/rubyspec/language/next_spec.rb @@ -0,0 +1,410 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/next', __FILE__) + +describe "The next statement from within the block" do + before :each do + ScratchPad.record [] + end + + it "ends block execution" do + a = [] + lambda { + a << 1 + next + a << 2 + }.call + a.should == [1] + end + + it "causes block to return nil if invoked without arguments" do + lambda { 123; next; 456 }.call.should == nil + end + + it "causes block to return nil if invoked with an empty expression" do + lambda { next (); 456 }.call.should be_nil + end + + it "returns the argument passed" do + lambda { 123; next 234; 345 }.call.should == 234 + end + + it "returns to the invoking method" do + NextSpecs.yielding_method(nil) { next }.should == :method_return_value + end + + it "returns to the invoking method, with the specified value" do + NextSpecs.yielding_method(nil) { + next nil; + fail("next didn't end the block execution") + }.should == :method_return_value + + NextSpecs.yielding_method(1) { + next 1 + fail("next didn't end the block execution") + }.should == :method_return_value + + NextSpecs.yielding_method([1, 2, 3]) { + next 1, 2, 3 + fail("next didn't end the block execution") + }.should == :method_return_value + end + + it "returns to the currently yielding method in case of chained calls" do + class ChainedNextTest + def self.meth_with_yield(&b) + yield.should == :next_return_value + :method_return_value + end + def self.invoking_method(&b) + meth_with_yield(&b) + end + def self.enclosing_method + invoking_method do + next :next_return_value + :wrong_return_value + end + end + end + + ChainedNextTest.enclosing_method.should == :method_return_value + end + + it "causes ensure blocks to run" do + [1].each do |i| + begin + ScratchPad << :begin + next + ensure + ScratchPad << :ensure + end + end + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "skips following code outside an exception block" do + 3.times do |i| + begin + ScratchPad << :begin + next if i == 0 + break if i == 2 + ScratchPad << :begin_end + ensure + ScratchPad << :ensure + end + + ScratchPad << :after + end + + ScratchPad.recorded.should == [ + :begin, :ensure, :begin, :begin_end, :ensure, :after, :begin, :ensure] + end + + it "passes the value returned by a method with omitted parenthesis and passed block" do + obj = NextSpecs::Block.new + lambda { next obj.method :value do |x| x end }.call.should == :value + end +end + +describe "The next statement" do + describe "in a method" do + it "is invalid and raises a SyntaxError" do + lambda { + eval("def m; next; end") + }.should raise_error(SyntaxError) + end + end +end + +describe "The next statement" do + before :each do + ScratchPad.record [] + end + + describe "in a while loop" do + describe "when not passed an argument" do + it "causes ensure blocks to run" do + NextSpecs.while_next(false) + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "causes ensure blocks to run when nested in an block" do + NextSpecs.while_within_iter(false) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + describe "when passed an argument" do + it "causes ensure blocks to run" do + NextSpecs.while_next(true) + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "causes ensure blocks to run when nested in an block" do + NextSpecs.while_within_iter(true) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + it "causes nested ensure blocks to run" do + x = true + while x + begin + ScratchPad << :outer_begin + x = false + begin + ScratchPad << :inner_begin + next + ensure + ScratchPad << :inner_ensure + end + ensure + ScratchPad << :outer_ensure + end + end + + ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure] + end + + it "causes ensure blocks to run when mixed with break" do + x = 1 + while true + begin + ScratchPad << :begin + break if x > 1 + x += 1 + next + ensure + ScratchPad << :ensure + end + end + + ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure] + end + end + + describe "in an until loop" do + describe "when not passed an argument" do + it "causes ensure blocks to run" do + NextSpecs.until_next(false) + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "causes ensure blocks to run when nested in an block" do + NextSpecs.until_within_iter(false) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + describe "when passed an argument" do + it "causes ensure blocks to run" do + NextSpecs.until_next(true) + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "causes ensure blocks to run when nested in an block" do + NextSpecs.until_within_iter(true) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + it "causes nested ensure blocks to run" do + x = false + until x + begin + ScratchPad << :outer_begin + x = true + begin + ScratchPad << :inner_begin + next + ensure + ScratchPad << :inner_ensure + end + ensure + ScratchPad << :outer_ensure + end + end + + ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure] + end + + it "causes ensure blocks to run when mixed with break" do + x = 1 + until false + begin + ScratchPad << :begin + break if x > 1 + x += 1 + next + ensure + ScratchPad << :ensure + end + end + + ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure] + end + end + + describe "in a loop" do + describe "when not passed an argument" do + it "causes ensure blocks to run" do + NextSpecs.loop_next(false) + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "causes ensure blocks to run when nested in an block" do + NextSpecs.loop_within_iter(false) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + describe "when passed an argument" do + it "causes ensure blocks to run" do + NextSpecs.loop_next(true) + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "causes ensure blocks to run when nested in an block" do + NextSpecs.loop_within_iter(true) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + it "causes nested ensure blocks to run" do + x = 1 + loop do + break if x == 2 + + begin + ScratchPad << :outer_begin + begin + ScratchPad << :inner_begin + x += 1 + next + ensure + ScratchPad << :inner_ensure + end + ensure + ScratchPad << :outer_ensure + end + end + + ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure] + end + + it "causes ensure blocks to run when mixed with break" do + x = 1 + loop do + begin + ScratchPad << :begin + break if x > 1 + x += 1 + next + ensure + ScratchPad << :ensure + end + end + + ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure] + end + end +end + +describe "Assignment via next" do + it "assigns objects" do + def r(val); a = yield(); val.should == a; end + r(nil){next} + r(nil){next nil} + r(1){next 1} + r([]){next []} + r([1]){next [1]} + r([nil]){next [nil]} + r([[]]){next [[]]} + r([]){next [*[]]} + r([1]){next [*[1]]} + r([1,2]){next [*[1,2]]} + end + + it "assigns splatted objects" do + def r(val); a = yield(); val.should == a; end + r([]){next *nil} + r([1]){next *1} + r([]){next *[]} + r([1]){next *[1]} + r([nil]){next *[nil]} + r([[]]){next *[[]]} + r([]){next *[*[]]} + r([1]){next *[*[1]]} + r([1,2]){next *[*[1,2]]} + end + + it "assigns objects to a splatted reference" do + def r(val); *a = yield(); val.should == a; end + r([nil]){next} + r([nil]){next nil} + r([1]){next 1} + r([]){next []} + r([1]){next [1]} + r([nil]){next [nil]} + r([[]]){next [[]]} + r([1,2]){next [1,2]} + r([]){next [*[]]} + r([1]){next [*[1]]} + r([1,2]){next [*[1,2]]} + end + + it "assigns splatted objects to a splatted reference via a splatted yield" do + def r(val); *a = *yield(); val.should == a; end + r([]){next *nil} + r([1]){next *1} + r([]){next *[]} + r([1]){next *[1]} + r([nil]){next *[nil]} + r([[]]){next *[[]]} + r([1,2]){next *[1,2]} + r([]){next *[*[]]} + r([1]){next *[*[1]]} + r([1,2]){next *[*[1,2]]} + end + + it "assigns objects to multiple variables" do + def r(val); a,b,*c = yield(); val.should == [a,b,c]; end + r([nil,nil,[]]){next} + r([nil,nil,[]]){next nil} + r([1,nil,[]]){next 1} + r([nil,nil,[]]){next []} + r([1,nil,[]]){next [1]} + r([nil,nil,[]]){next [nil]} + r([[],nil,[]]){next [[]]} + r([1,2,[]]){next [1,2]} + r([nil,nil,[]]){next [*[]]} + r([1,nil,[]]){next [*[1]]} + r([1,2,[]]){next [*[1,2]]} + end + + it "assigns splatted objects to multiple variables" do + def r(val); a,b,*c = *yield(); val.should == [a,b,c]; end + r([nil,nil,[]]){next *nil} + r([1,nil,[]]){next *1} + r([nil,nil,[]]){next *[]} + r([1,nil,[]]){next *[1]} + r([nil,nil,[]]){next *[nil]} + r([[],nil,[]]){next *[[]]} + r([1,2,[]]){next *[1,2]} + r([nil,nil,[]]){next *[*[]]} + r([1,nil,[]]){next *[*[1]]} + r([1,2,[]]){next *[*[1,2]]} + end +end diff --git a/spec/rubyspec/language/not_spec.rb b/spec/rubyspec/language/not_spec.rb new file mode 100644 index 0000000000..a0cf6b15a6 --- /dev/null +++ b/spec/rubyspec/language/not_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The not keyword" do + it "negates a `true' value" do + (not true).should be_false + (not 'true').should be_false + end + + it "negates a `false' value" do + (not false).should be_true + (not nil).should be_true + end + + it "accepts an argument" do + not(true).should be_false + end + + it "returns false if the argument is true" do + (not(true)).should be_false + end + + it "returns true if the argument is false" do + (not(false)).should be_true + end + + it "returns true if the argument is nil" do + (not(nil)).should be_true + end +end + +describe "The `!' keyword" do + it "negates a `true' value" do + (!true).should be_false + (!'true').should be_false + end + + it "negates a `false' value" do + (!false).should be_true + (!nil).should be_true + end + + it "doubled turns a truthful object into `true'" do + (!!true).should be_true + (!!'true').should be_true + end + + it "doubled turns a not truthful object into `false'" do + (!!false).should be_false + (!!nil).should be_false + end +end diff --git a/spec/rubyspec/language/numbers_spec.rb b/spec/rubyspec/language/numbers_spec.rb new file mode 100644 index 0000000000..e8c82f09a7 --- /dev/null +++ b/spec/rubyspec/language/numbers_spec.rb @@ -0,0 +1,97 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "A number literal" do + + it "can be a sequence of decimal digits" do + 435.should == 435 + end + + it "can have '_' characters between digits" do + 4_3_5_7.should == 4357 + end + + it "cannot have a leading underscore" do + lambda { eval("_4_2") }.should raise_error(NameError) + end + + it "can have a decimal point" do + 4.35.should == 4.35 + end + + it "must have a digit before the decimal point" do + 0.75.should == 0.75 + lambda { eval(".75") }.should raise_error(SyntaxError) + lambda { eval("-.75") }.should raise_error(SyntaxError) + end + + it "can have an exponent" do + 1.2e-3.should == 0.0012 + end + + it "can be a sequence of hexadecimal digits with a leading '0x'" do + 0xffff.should == 65535 + end + + it "can be a sequence of binary digits with a leading '0x'" do + 0b01011.should == 11 + end + + it "can be a sequence of octal digits with a leading '0'" do + 0377.should == 255 + end + + it "can be an integer literal with trailing 'r' to represent a Rational" do + eval('3r').should == Rational(3, 1) + eval('-3r').should == Rational(-3, 1) + end + + it "can be an bignum literal with trailing 'r' to represent a Rational" do + eval('1111111111111111111111111111111111111111111111r').should == Rational(1111111111111111111111111111111111111111111111, 1) + eval('-1111111111111111111111111111111111111111111111r').should == Rational(-1111111111111111111111111111111111111111111111, 1) + end + + it "can be a decimal literal with trailing 'r' to represent a Rational" do + eval('0.3r').should == Rational(3, 10) + eval('-0.3r').should == Rational(-3, 10) + end + + it "can be a hexadecimal literal with trailing 'r' to represent a Rational" do + eval('0xffr').should == Rational(255, 1) + eval('-0xffr').should == Rational(-255, 1) + end + + it "can be an octal literal with trailing 'r' to represent a Rational" do + eval('042r').should == Rational(34, 1) + eval('-042r').should == Rational(-34, 1) + end + + it "can be a binary literal with trailing 'r' to represent a Rational" do + eval('0b1111r').should == Rational(15, 1) + eval('-0b1111r').should == Rational(-15, 1) + end + + it "can be an integer literal with trailing 'i' to represent a Complex" do + eval('5i').should == Complex(0, 5) + eval('-5i').should == Complex(0, -5) + end + + it "can be a decimal literal with trailing 'i' to represent a Complex" do + eval('0.6i').should == Complex(0, 0.6) + eval('-0.6i').should == Complex(0, -0.6) + end + + it "can be a hexadecimal literal with trailing 'i' to represent a Complex" do + eval('0xffi').should == Complex(0, 255) + eval('-0xffi').should == Complex(0, -255) + end + + it "can be a octal literal with trailing 'i' to represent a Complex" do + eval("042i").should == Complex(0, 34) + eval("-042i").should == Complex(0, -34) + end + + it "can be a binary literal with trailing 'i' to represent a Complex" do + eval('0b1110i').should == Complex(0, 14) + eval('-0b1110i').should == Complex(0, -14) + end +end diff --git a/spec/rubyspec/language/optional_assignments_spec.rb b/spec/rubyspec/language/optional_assignments_spec.rb new file mode 100644 index 0000000000..c104d08253 --- /dev/null +++ b/spec/rubyspec/language/optional_assignments_spec.rb @@ -0,0 +1,226 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe 'Optional variable assignments' do + describe 'using ||=' do + describe 'using a single variable' do + it 'assigns a new variable' do + a ||= 10 + + a.should == 10 + end + + it 're-assigns an existing variable set to false' do + a = false + a ||= 10 + + a.should == 10 + end + + it 're-assigns an existing variable set to nil' do + a = nil + a ||= 10 + + a.should == 10 + end + + it 'does not re-assign a variable with a truthy value' do + a = 10 + a ||= 20 + + a.should == 10 + end + + it 'does not evaluate the right side when not needed' do + a = 10 + a ||= raise('should not be executed') + a.should == 10 + end + + it 'does not re-assign a variable with a truthy value when using an inline rescue' do + a = 10 + a ||= 20 rescue 30 + + a.should == 10 + end + end + + describe 'using a accessor' do + before do + klass = Class.new { attr_accessor :b } + @a = klass.new + end + + it 'assigns a new variable' do + @a.b ||= 10 + + @a.b.should == 10 + end + + it 're-assigns an existing variable set to false' do + @a.b = false + @a.b ||= 10 + + @a.b.should == 10 + end + + it 're-assigns an existing variable set to nil' do + @a.b = nil + @a.b ||= 10 + + @a.b.should == 10 + end + + it 'does not re-assign a variable with a truthy value' do + @a.b = 10 + @a.b ||= 20 + + @a.b.should == 10 + end + + it 'does not evaluate the right side when not needed' do + @a.b = 10 + @a.b ||= raise('should not be executed') + @a.b.should == 10 + end + + it 'does not re-assign a variable with a truthy value when using an inline rescue' do + @a.b = 10 + @a.b ||= 20 rescue 30 + + @a.b.should == 10 + end + end + end + + describe 'using &&=' do + describe 'using a single variable' do + it 'leaves new variable unassigned' do + a &&= 10 + + a.should == nil + end + + it 'leaves false' do + a = false + a &&= 10 + + a.should == false + end + + it 'leaves nil' do + a = nil + a &&= 10 + + a.should == nil + end + + it 'does not evaluate the right side when not needed' do + a = nil + a &&= raise('should not be executed') + a.should == nil + end + + it 'does re-assign a variable with a truthy value' do + a = 10 + a &&= 20 + + a.should == 20 + end + + it 'does re-assign a variable with a truthy value when using an inline rescue' do + a = 10 + a &&= 20 rescue 30 + + a.should == 20 + end + end + + describe 'using a single variable' do + before do + klass = Class.new { attr_accessor :b } + @a = klass.new + end + + it 'leaves new variable unassigned' do + @a.b &&= 10 + + @a.b.should == nil + end + + it 'leaves false' do + @a.b = false + @a.b &&= 10 + + @a.b.should == false + end + + it 'leaves nil' do + @a.b = nil + @a.b &&= 10 + + @a.b.should == nil + end + + it 'does not evaluate the right side when not needed' do + @a.b = nil + @a.b &&= raise('should not be executed') + @a.b.should == nil + end + + it 'does re-assign a variable with a truthy value' do + @a.b = 10 + @a.b &&= 20 + + @a.b.should == 20 + end + + it 'does re-assign a variable with a truthy value when using an inline rescue' do + @a.b = 10 + @a.b &&= 20 rescue 30 + + @a.b.should == 20 + end + end + end + + describe 'using compunded constants' do + before do + Object.send(:remove_const, :A) if defined? Object::A + end + + it 'with ||= assignments' do + Object::A ||= 10 + Object::A.should == 10 + end + + it 'with ||= do not reassign' do + Object::A = 20 + Object::A ||= 10 + Object::A.should == 20 + end + + it 'with &&= assignments' do + Object::A = 20 + -> { + Object::A &&= 10 + }.should complain(/already initialized constant/) + Object::A.should == 10 + end + + it 'with &&= assignments will fail with non-existant constants' do + lambda { Object::A &&= 10 }.should raise_error(NameError) + end + + it 'with operator assignments' do + Object::A = 20 + -> { + Object::A += 10 + }.should complain(/already initialized constant/) + Object::A.should == 30 + end + + it 'with operator assignments will fail with non-existant constants' do + lambda { Object::A += 10 }.should raise_error(NameError) + end + end +end diff --git a/spec/rubyspec/language/or_spec.rb b/spec/rubyspec/language/or_spec.rb new file mode 100644 index 0000000000..150d693996 --- /dev/null +++ b/spec/rubyspec/language/or_spec.rb @@ -0,0 +1,90 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The || operator" do + it "evaluates to true if any of its operands are true" do + if false || true || nil + x = true + end + x.should == true + end + + it "evaluated to false if all of its operands are false" do + if false || nil + x = true + end + x.should == nil + end + + it "is evaluated before assignment operators" do + x = nil || true + x.should == true + end + + it "has a lower precedence than the && operator" do + x = 1 || false && x = 2 + x.should == 1 + end + + it "treats empty expressions as nil" do + (() || true).should be_true + (() || false).should be_false + (true || ()).should be_true + (false || ()).should be_nil + (() || ()).should be_nil + end + + it "has a higher precedence than 'break' in 'break true || false'" do + # see also 'break true or false' below + lambda { break false || true }.call.should be_true + end + + it "has a higher precedence than 'next' in 'next true || false'" do + lambda { next false || true }.call.should be_true + end + + it "has a higher precedence than 'return' in 'return true || false'" do + lambda { return false || true }.call.should be_true + end +end + +describe "The or operator" do + it "evaluates to true if any of its operands are true" do + x = nil + if false or true + x = true + end + x.should == true + end + + it "is evaluated after variables are assigned" do + x = nil or true + x.should == nil + end + + it "has a lower precedence than the || operator" do + x,y = nil + x = true || false or y = 1 + y.should == nil + end + + it "treats empty expressions as nil" do + (() or true).should be_true + (() or false).should be_false + (true or ()).should be_true + (false or ()).should be_nil + (() or ()).should be_nil + end + + it "has a lower precedence than 'break' in 'break true or false'" do + # see also 'break true || false' above + lambda { eval "break true or false" }.should raise_error(SyntaxError, /void value expression/) + end + + it "has a lower precedence than 'next' in 'next true or false'" do + lambda { eval "next true or false" }.should raise_error(SyntaxError, /void value expression/) + end + + it "has a lower precedence than 'return' in 'return true or false'" do + lambda { eval "return true or false" }.should raise_error(SyntaxError, /void value expression/) + end +end diff --git a/spec/rubyspec/language/order_spec.rb b/spec/rubyspec/language/order_spec.rb new file mode 100644 index 0000000000..d02bf04077 --- /dev/null +++ b/spec/rubyspec/language/order_spec.rb @@ -0,0 +1,75 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "A method call" do + before :each do + @obj = Object.new + def @obj.foo0(&a) + [a ? a.call : nil] + end + def @obj.foo1(a, &b) + [a, b ? b.call : nil] + end + def @obj.foo2(a, b, &c) + [a, b, c ? c.call : nil] + end + def @obj.foo3(a, b, c, &d) + [a, b, c, d ? d.call : nil] + end + def @obj.foo4(a, b, c, d, &e) + [a, b, c, d, e ? e.call : nil] + end + end + + it "evaluates the receiver first" do + (obj = @obj).foo1(obj = nil).should == [nil, nil] + (obj = @obj).foo2(obj = nil, obj = nil).should == [nil, nil, nil] + (obj = @obj).foo3(obj = nil, obj = nil, obj = nil).should == [nil, nil, nil, nil] + (obj = @obj).foo4(obj = nil, obj = nil, obj = nil, obj = nil).should == [nil, nil, nil, nil, nil] + end + + it "evaluates arguments after receiver" do + a = 0 + (a += 1; @obj).foo1(a).should == [1, nil] + (a += 1; @obj).foo2(a, a).should == [2, 2, nil] + (a += 1; @obj).foo3(a, a, a).should == [3, 3, 3, nil] + (a += 1; @obj).foo4(a, a, a, a).should == [4, 4, 4, 4, nil] + a.should == 4 + end + + it "evaluates arguments left-to-right" do + a = 0 + @obj.foo1(a += 1).should == [1, nil] + @obj.foo2(a += 1, a += 1).should == [2, 3, nil] + @obj.foo3(a += 1, a += 1, a += 1).should == [4, 5, 6, nil] + @obj.foo4(a += 1, a += 1, a += 1, a += 1).should == [7, 8, 9, 10, nil] + a.should == 10 + end + + it "evaluates block pass after arguments" do + a = 0 + p = proc {true} + @obj.foo1(a += 1, &(a += 1; p)).should == [1, true] + @obj.foo2(a += 1, a += 1, &(a += 1; p)).should == [3, 4, true] + @obj.foo3(a += 1, a += 1, a += 1, &(a += 1; p)).should == [6, 7, 8, true] + @obj.foo4(a += 1, a += 1, a += 1, a += 1, &(a += 1; p)).should == [10, 11, 12, 13, true] + a.should == 14 + end + + it "evaluates block pass after receiver" do + p1 = proc {true} + p2 = proc {false} + p1.should_not == p2 + + p = p1 + (p = p2; @obj).foo0(&p).should == [false] + p = p1 + (p = p2; @obj).foo1(1, &p).should == [1, false] + p = p1 + (p = p2; @obj).foo2(1, 1, &p).should == [1, 1, false] + p = p1 + (p = p2; @obj).foo3(1, 1, 1, &p).should == [1, 1, 1, false] + p = p1 + (p = p2; @obj).foo4(1, 1, 1, 1, &p).should == [1, 1, 1, 1, false] + p = p1 + end +end diff --git a/spec/rubyspec/language/precedence_spec.rb b/spec/rubyspec/language/precedence_spec.rb new file mode 100644 index 0000000000..90734022ff --- /dev/null +++ b/spec/rubyspec/language/precedence_spec.rb @@ -0,0 +1,448 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/precedence', __FILE__) + +# Specifying the behavior of operators in combination could +# lead to combinatorial explosion. A better way seems to be +# to use a technique from formal proofs that involve a set of +# equivalent statements. Suppose you have statements A, B, C. +# If they are claimed to be equivalent, this can be shown by +# proving that A implies B, B implies C, and C implies A. +# (Actually any closed circuit of implications.) +# +# Here, we can use a similar technique where we show starting +# at the top that each level of operator has precedence over +# the level below (as well as showing associativity within +# the precedence level). + +=begin +Excerpted from 'Programming Ruby: The Pragmatic Programmer's Guide' +Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 324 + +Table 22.4. Ruby operators (high to low precedence) +Method Operator Description +----------------------------------------------------------------------- + :: . + x* [ ] [ ]= Element reference, element set + x ** Exponentiation + x ! ~ + - Not, complement, unary plus and minus + (method names for the last two are +@ and -@) + x * / % Multiply, divide, and modulo + x + - Plus and minus + x >> << Right and left shift + x & “And” (bitwise for integers) + x ^ | Exclusive “or” and regular “or” (bitwise for integers) + x <= < > >= Comparison operators + x <=> == === != =~ !~ Equality and pattern match operators (!= + and !~ may not be defined as methods) + && Logical “and” + || Logical “or” + .. ... Range (inclusive and exclusive) + ? : Ternary if-then-else + = %= /= -= += |= &= Assignment + >>= <<= *= &&= ||= **= + defined? Check if symbol defined + not Logical negation + or and Logical composition + if unless while until Expression modifiers + begin/end Block expression +----------------------------------------------------------------------- + +* Operators marked with 'x' in the Method column are implemented as methods +and can be overridden (except != and !~ as noted). (But see the specs +below for implementations that define != and !~ as methods.) + +** These are not included in the excerpted table but are shown here for +completeness. +=end + +# ----------------------------------------------------------------------- +# It seems that this table is not correct anymore +# The correct table derived from MRI's parse.y is as follows: +# +# Operator Assoc Description +#--------------------------------------------------------------- +# ! ~ + > Not, complement, unary plus +# ** > Exponentiation +# - > Unary minus +# * / % < Multiply, divide, and modulo +# + - < Plus and minus +# >> << < Right and left shift +# & < “And” (bitwise for integers) +# ^ | < Exclusive “or” and regular “or” (bitwise for integers) +# <= < > >= < Comparison operators +# <=> == === != =~ !~ no Equality and pattern match operators (!= +# and !~ may not be defined as methods) +# && < Logical “and” +# || < Logical “or” +# .. ... no Range (inclusive and exclusive) +# ? : > Ternary if-then-else +# rescue < Rescue modifier +# = %= /= -= += |= &= > Assignment +# >>= <<= *= &&= ||= **= +# defined? no Check if symbol defined +# not > Logical negation +# or and < Logical composition +# if unless while until no Expression modifiers +# ----------------------------------------------------------------------- +# +# [] and []= seem to fall out of here, as well as begin/end +# + +# TODO: Resolve these two tables with actual specs. As the comment at the +# top suggests, these specs need to be reorganized into a single describe +# block for each operator. The describe block should include an example +# for associativity (if relevant), an example for any short circuit behavior +# (e.g. &&, ||, etc.) and an example block for each operator over which the +# instant operator has immediately higher precedence. + +describe "Operators" do + it "! ~ + is right-associative" do + (!!true).should == true + (~~0).should == 0 + (++2).should == 2 + end + + it "** is right-associative" do + (2**2**3).should == 256 + end + + it "** has higher precedence than unary minus" do + (-2**2).should == -4 + end + + it "unary minus is right-associative" do + (--2).should == 2 + end + + it "unary minus has higher precedence than * / %" do + class UnaryMinusTest; def -@; 50; end; end + b = UnaryMinusTest.new + + (-b * 5).should == 250 + (-b / 5).should == 10 + (-b % 7).should == 1 + end + + it "treats +/- as a regular send if the arguments are known locals or block locals" do + a = PrecedenceSpecs::NonUnaryOpTest.new + a.add_num(1).should == [3] + a.sub_num(1).should == [1] + a.add_str.should == ['11'] + a.add_var.should == [2] + end + + it "* / % are left-associative" do + (2*1/2).should == (2*1)/2 + # Guard against the Mathn library + # TODO: Make these specs not rely on specific behaviour / result values + # by using mocks. + conflicts_with :Prime do + (2*1/2).should_not == 2*(1/2) + end + + (10/7/5).should == (10/7)/5 + (10/7/5).should_not == 10/(7/5) + + (101 % 55 % 7).should == (101 % 55) % 7 + (101 % 55 % 7).should_not == 101 % (55 % 7) + + (50*20/7%42).should == ((50*20)/7)%42 + (50*20/7%42).should_not == 50*(20/(7%42)) + end + + it "* / % have higher precedence than + -" do + (2+2*2).should == 6 + (1+10/5).should == 3 + (2+10%5).should == 2 + + (2-2*2).should == -2 + (1-10/5).should == -1 + (10-10%4).should == 8 + end + + it "+ - are left-associative" do + (2-3-4).should == -5 + (4-3+2).should == 3 + + binary_plus = Class.new(String) do + alias_method :plus, :+ + def +(a) + plus(a) + "!" + end + end + s = binary_plus.new("a") + + (s+s+s).should == (s+s)+s + (s+s+s).should_not == s+(s+s) + end + + it "+ - have higher precedence than >> <<" do + (2<<1+2).should == 16 + (8>>1+2).should == 1 + (4<<1-3).should == 1 + (2>>1-3).should == 8 + end + + it ">> << are left-associative" do + (1 << 2 << 3).should == 32 + (10 >> 1 >> 1).should == 2 + (10 << 4 >> 1).should == 80 + end + + it ">> << have higher precedence than &" do + (4 & 2 << 1).should == 4 + (2 & 4 >> 1).should == 2 + end + + it "& is left-associative" do + class BitwiseAndTest; def &(a); a+1; end; end + c = BitwiseAndTest.new + + (c & 5 & 2).should == (c & 5) & 2 + (c & 5 & 2).should_not == c & (5 & 2) + end + + it "& has higher precedence than ^ |" do + (8 ^ 16 & 16).should == 24 + (8 | 16 & 16).should == 24 + end + + it "^ | are left-associative" do + class OrAndXorTest; def ^(a); a+10; end; def |(a); a-10; end; end + d = OrAndXorTest.new + + (d ^ 13 ^ 16).should == (d ^ 13) ^ 16 + (d ^ 13 ^ 16).should_not == d ^ (13 ^ 16) + + (d | 13 | 4).should == (d | 13) | 4 + (d | 13 | 4).should_not == d | (13 | 4) + end + + it "^ | have higher precedence than <= < > >=" do + (10 <= 7 ^ 7).should == false + (10 < 7 ^ 7).should == false + (10 > 7 ^ 7).should == true + (10 >= 7 ^ 7).should == true + (10 <= 7 | 7).should == false + (10 < 7 | 7).should == false + (10 > 7 | 7).should == true + (10 >= 7 | 7).should == true + end + + it "<= < > >= are left-associative" do + class ComparisonTest + def <=(a); 0; end; + def <(a); 0; end; + def >(a); 0; end; + def >=(a); 0; end; + end + + e = ComparisonTest.new + + (e <= 0 <= 1).should == (e <= 0) <= 1 + (e <= 0 <= 1).should_not == e <= (0 <= 1) + + (e < 0 < 1).should == (e < 0) < 1 + (e < 0 < 1).should_not == e < (0 < 1) + + (e >= 0 >= 1).should == (e >= 0) >= 1 + (e >= 0 >= 1).should_not == e >= (0 >= 1) + + (e > 0 > 1).should == (e > 0) > 1 + (e > 0 > 1).should_not == e > (0 > 1) + end + + it "<=> == === != =~ !~ are non-associative" do + lambda { eval("1 <=> 2 <=> 3") }.should raise_error(SyntaxError) + lambda { eval("1 == 2 == 3") }.should raise_error(SyntaxError) + lambda { eval("1 === 2 === 3") }.should raise_error(SyntaxError) + lambda { eval("1 != 2 != 3") }.should raise_error(SyntaxError) + lambda { eval("1 =~ 2 =~ 3") }.should raise_error(SyntaxError) + lambda { eval("1 !~ 2 !~ 3") }.should raise_error(SyntaxError) + end + + it "<=> == === != =~ !~ have higher precedence than &&" do + (false && 2 <=> 3).should == false + (false && 3 == false).should == false + (false && 3 === false).should == false + (false && 3 != true).should == false + + class FalseClass; def =~(o); o == false; end; end + (false && true =~ false).should == (false && (true =~ false)) + (false && true =~ false).should_not == ((false && true) =~ false) + class FalseClass; undef_method :=~; end + + (false && true !~ true).should == false + end + + # XXX: figure out how to test it + # (a && b) && c equals to a && (b && c) for all a,b,c values I can imagine so far + it "&& is left-associative" + + it "&& has higher precedence than ||" do + (true || false && false).should == true + end + + # XXX: figure out how to test it + it "|| is left-associative" + + it "|| has higher precedence than .. ..." do + (1..false||10).should == (1..10) + (1...false||10).should == (1...10) + end + + it ".. ... are non-associative" do + lambda { eval("1..2..3") }.should raise_error(SyntaxError) + lambda { eval("1...2...3") }.should raise_error(SyntaxError) + end + +# XXX: this is commented now due to a bug in compiler, which cannot +# distinguish between range and flip-flop operator so far. zenspider is +# currently working on a new lexer, which will be able to do that. +# As soon as it's done, these piece should be reenabled. +# +# it ".. ... have higher precedence than ? :" do +# (1..2 ? 3 : 4).should == 3 +# (1...2 ? 3 : 4).should == 3 +# end + + it "? : is right-associative" do + (true ? 2 : 3 ? 4 : 5).should == 2 + end + + def oops; raise end + + it "? : has higher precedence than rescue" do + (true ? oops : 0 rescue 10).should == 10 + end + + # XXX: figure how to test it (problem similar to || associativity) + it "rescue is left-associative" + + it "rescue has higher precedence than =" do + a = oops rescue 10 + a.should == 10 + + # rescue doesn't have the same sense for %= /= and friends + end + + it "= %= /= -= += |= &= >>= <<= *= &&= ||= **= are right-associative" do + a = b = 10 + a.should == 10 + b.should == 10 + + a = b = 10 + a %= b %= 3 + a.should == 0 + b.should == 1 + + a = b = 10 + a /= b /= 2 + a.should == 2 + b.should == 5 + + a = b = 10 + a -= b -= 2 + a.should == 2 + b.should == 8 + + a = b = 10 + a += b += 2 + a.should == 22 + b.should == 12 + + a,b = 32,64 + a |= b |= 2 + a.should == 98 + b.should == 66 + + a,b = 25,13 + a &= b &= 7 + a.should == 1 + b.should == 5 + + a,b=8,2 + a >>= b >>= 1 + a.should == 4 + b.should == 1 + + a,b=8,2 + a <<= b <<= 1 + a.should == 128 + b.should == 4 + + a,b=8,2 + a *= b *= 2 + a.should == 32 + b.should == 4 + + a,b=10,20 + a &&= b &&= false + a.should == false + b.should == false + + a,b=nil,nil + a ||= b ||= 10 + a.should == 10 + b.should == 10 + + a,b=2,3 + a **= b **= 2 + a.should == 512 + b.should == 9 + end + + it "= %= /= -= += |= &= >>= <<= *= &&= ||= **= have higher precedence than defined? operator" do + (defined? a = 10).should == "assignment" + (defined? a %= 10).should == "assignment" + (defined? a /= 10).should == "assignment" + (defined? a -= 10).should == "assignment" + (defined? a += 10).should == "assignment" + (defined? a |= 10).should == "assignment" + (defined? a &= 10).should == "assignment" + (defined? a >>= 10).should == "assignment" + (defined? a <<= 10).should == "assignment" + (defined? a *= 10).should == "assignment" + (defined? a &&= 10).should == "assignment" + (defined? a ||= 10).should == "assignment" + (defined? a **= 10).should == "assignment" + end + + # XXX: figure out how to test it + it "defined? is non-associative" + + it "defined? has higher precedence than not" do + # does it have sense? + (not defined? qqq).should == true + end + + it "not is right-associative" do + (not not false).should == false + (not not 10).should == true + end + + it "not has higher precedence than or/and" do + (not false and false).should == false + (not false or true).should == true + end + + # XXX: figure out how to test it + it "or/and are left-associative" + + it "or/and have higher precedence than if unless while until modifiers" do + (1 if 2 and 3).should == 1 + (1 if 2 or 3).should == 1 + + (1 unless false and true).should == 1 + (1 unless false or false).should == 1 + + (1 while true and false).should == nil # would hang upon error + (1 while false or false).should == nil + + ((raise until true and false) rescue 10).should == 10 + (1 until false or true).should == nil # would hang upon error + end + + # XXX: it seems to me they are right-associative + it "if unless while until are non-associative" +end diff --git a/spec/rubyspec/language/predefined/data_spec.rb b/spec/rubyspec/language/predefined/data_spec.rb new file mode 100644 index 0000000000..f616879527 --- /dev/null +++ b/spec/rubyspec/language/predefined/data_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "The DATA constant" do + it "exists when the main script contains __END__" do + ruby_exe(fixture(__FILE__, "data1.rb")).chomp.should == "true" + end + + it "does not exist when the main script contains no __END__" do + ruby_exe("puts Object.const_defined?(:DATA)").chomp.should == 'false' + end + + it "does not exist when an included file has a __END__" do + ruby_exe(fixture(__FILE__, "data2.rb")).chomp.should == "false" + end + + it "does not change when an included files also has a __END__" do + ruby_exe(fixture(__FILE__, "data3.rb")).chomp.should == "data 3" + end + + it "is included in an otherwise empty file" do + ap = fixture(__FILE__, "print_data.rb") + str = ruby_exe(fixture(__FILE__, "data_only.rb"), options: "-r#{ap}") + str.chomp.should == "data only" + end + + it "rewinds to the head of the main script" do + ruby_exe(fixture(__FILE__, "data5.rb")).chomp.should == "DATA.rewind" + end +end diff --git a/spec/rubyspec/language/predefined/fixtures/data1.rb b/spec/rubyspec/language/predefined/fixtures/data1.rb new file mode 100644 index 0000000000..cb9572255b --- /dev/null +++ b/spec/rubyspec/language/predefined/fixtures/data1.rb @@ -0,0 +1,4 @@ +puts Object.const_defined?(:DATA) + +__END__ +data1 diff --git a/spec/rubyspec/language/predefined/fixtures/data2.rb b/spec/rubyspec/language/predefined/fixtures/data2.rb new file mode 100644 index 0000000000..0f714b06d4 --- /dev/null +++ b/spec/rubyspec/language/predefined/fixtures/data2.rb @@ -0,0 +1,4 @@ + +require File.expand_path("../data4.rb", __FILE__) + +p Object.const_defined?(:DATA) diff --git a/spec/rubyspec/language/predefined/fixtures/data3.rb b/spec/rubyspec/language/predefined/fixtures/data3.rb new file mode 100644 index 0000000000..6cbf63dae6 --- /dev/null +++ b/spec/rubyspec/language/predefined/fixtures/data3.rb @@ -0,0 +1,7 @@ + +require File.expand_path("../data4.rb", __FILE__) + +puts DATA.read + +__END__ +data 3 diff --git a/spec/rubyspec/language/predefined/fixtures/data4.rb b/spec/rubyspec/language/predefined/fixtures/data4.rb new file mode 100644 index 0000000000..139ef80d7b --- /dev/null +++ b/spec/rubyspec/language/predefined/fixtures/data4.rb @@ -0,0 +1,4 @@ +# nothing + +__END__ +data 4 diff --git a/spec/rubyspec/language/predefined/fixtures/data5.rb b/spec/rubyspec/language/predefined/fixtures/data5.rb new file mode 100644 index 0000000000..48f060e1a9 --- /dev/null +++ b/spec/rubyspec/language/predefined/fixtures/data5.rb @@ -0,0 +1,5 @@ +DATA.rewind +puts DATA.gets + +__END__ +data 5 diff --git a/spec/rubyspec/language/predefined/fixtures/data_only.rb b/spec/rubyspec/language/predefined/fixtures/data_only.rb new file mode 100644 index 0000000000..004ac62737 --- /dev/null +++ b/spec/rubyspec/language/predefined/fixtures/data_only.rb @@ -0,0 +1,2 @@ +__END__ +data only diff --git a/spec/rubyspec/language/predefined/fixtures/print_data.rb b/spec/rubyspec/language/predefined/fixtures/print_data.rb new file mode 100644 index 0000000000..4a5692e6a7 --- /dev/null +++ b/spec/rubyspec/language/predefined/fixtures/print_data.rb @@ -0,0 +1,3 @@ +at_exit { + puts DATA.read +} diff --git a/spec/rubyspec/language/predefined_spec.rb b/spec/rubyspec/language/predefined_spec.rb new file mode 100644 index 0000000000..6bbd2e216f --- /dev/null +++ b/spec/rubyspec/language/predefined_spec.rb @@ -0,0 +1,1221 @@ +require File.expand_path('../../spec_helper', __FILE__) +require 'stringio' + +# The following tables are excerpted from Programming Ruby: The Pragmatic Programmer's Guide' +# Second Edition by Dave Thomas, Chad Fowler, and Andy Hunt, page 319-22. +# +# Entries marked [r/o] are read-only and an error will be raised of the program attempts to +# modify them. Entries marked [thread] are thread local. + +=begin +Exception Information +--------------------------------------------------------------------------------------------------- + +$! Exception The exception object passed to raise. [thread] +$@ Array The stack backtrace generated by the last exception. [thread] +=end + +=begin +Pattern Matching Variables +--------------------------------------------------------------------------------------------------- + +These variables are set to nil after an unsuccessful pattern match. + +$& String The string matched (following a successful pattern match). This variable is + local to the current scope. [r/o, thread] +$+ String The contents of the highest-numbered group matched following a successful + pattern match. Thus, in "cat" =~/(c|a)(t|z)/, $+ will be set to “t”. This + variable is local to the current scope. [r/o, thread] +$` String The string preceding the match in a successful pattern match. This variable + is local to the current scope. [r/o, thread] +$' String The string following the match in a successful pattern match. This variable + is local to the current scope. [r/o, thread] +$1 to $9 String The contents of successive groups matched in a successful pattern match. In + "cat" =~/(c|a)(t|z)/, $1 will be set to “a” and $2 to “t”. This variable + is local to the current scope. [r/o, thread] +$~ MatchData An object that encapsulates the results of a successful pattern match. The + variables $&, $`, $', and $1 to $9 are all derived from $~. Assigning to $~ + changes the values of these derived variables. This variable is local to the + current scope. [thread] +=end + + +describe "Predefined global $~" do + it "is set to contain the MatchData object of the last match if successful" do + md = /foo/.match 'foo' + $~.should be_kind_of(MatchData) + $~.object_id.should == md.object_id + + /bar/ =~ 'bar' + $~.should be_kind_of(MatchData) + $~.object_id.should_not == md.object_id + end + + it "is set to nil if the last match was unsuccessful" do + /foo/ =~ 'foo' + $~.nil?.should == false + + /foo/ =~ 'bar' + $~.nil?.should == true + end + + it "is set at the method-scoped level rather than block-scoped" do + obj = Object.new + def obj.foo; yield; end + def obj.foo2(&proc); proc.call; end + + match2 = nil + match3 = nil + match4 = nil + + match1 = /foo/.match "foo" + + obj.foo { match2 = /bar/.match("bar") } + + match2.should_not == nil + $~.should == match2 + + eval 'match3 = /baz/.match("baz")' + + match3.should_not == nil + $~.should == match3 + + obj.foo2 { match4 = /qux/.match("qux") } + + match4.should_not == nil + $~.should == match4 + end + + it "raises an error if assigned an object not nil or instanceof MatchData" do + $~ = nil + $~.should == nil + $~ = /foo/.match("foo") + $~.should be_an_instance_of(MatchData) + + lambda { $~ = Object.new }.should raise_error(TypeError) + lambda { $~ = 1 }.should raise_error(TypeError) + end + + it "changes the value of derived capture globals when assigned" do + "foo" =~ /(f)oo/ + foo_match = $~ + "bar" =~ /(b)ar/ + $~ = foo_match + $1.should == "f" + end + + it "changes the value of the derived preceding match global" do + "foo hello" =~ /hello/ + foo_match = $~ + "bar" =~ /(bar)/ + $~ = foo_match + $`.should == "foo " + end + + it "changes the value of the derived following match global" do + "foo hello" =~ /foo/ + foo_match = $~ + "bar" =~ /(bar)/ + $~ = foo_match + $'.should == " hello" + end + + it "changes the value of the derived full match global" do + "foo hello" =~ /foo/ + foo_match = $~ + "bar" =~ /(bar)/ + $~ = foo_match + $&.should == "foo" + end +end + +describe "Predefined global $&" do + it "is equivalent to MatchData#[0] on the last match $~" do + /foo/ =~ 'barfoobaz' + $&.should == $~[0] + $&.should == 'foo' + end + + with_feature :encoding do + it "sets the encoding to the encoding of the source String" do + "abc".force_encoding(Encoding::EUC_JP) =~ /b/ + $&.encoding.should equal(Encoding::EUC_JP) + end + end +end + +describe "Predefined global $`" do + it "is equivalent to MatchData#pre_match on the last match $~" do + /foo/ =~ 'barfoobaz' + $`.should == $~.pre_match + $`.should == 'bar' + end + + with_feature :encoding do + it "sets the encoding to the encoding of the source String" do + "abc".force_encoding(Encoding::EUC_JP) =~ /b/ + $`.encoding.should equal(Encoding::EUC_JP) + end + + it "sets an empty result to the encoding of the source String" do + "abc".force_encoding(Encoding::ISO_8859_1) =~ /a/ + $`.encoding.should equal(Encoding::ISO_8859_1) + end + end +end + +describe "Predefined global $'" do + it "is equivalent to MatchData#post_match on the last match $~" do + /foo/ =~ 'barfoobaz' + $'.should == $~.post_match + $'.should == 'baz' + end + + with_feature :encoding do + it "sets the encoding to the encoding of the source String" do + "abc".force_encoding(Encoding::EUC_JP) =~ /b/ + $'.encoding.should equal(Encoding::EUC_JP) + end + + it "sets an empty result to the encoding of the source String" do + "abc".force_encoding(Encoding::ISO_8859_1) =~ /c/ + $'.encoding.should equal(Encoding::ISO_8859_1) + end + end +end + +describe "Predefined global $+" do + it "is equivalent to $~.captures.last" do + /(f(o)o)/ =~ 'barfoobaz' + $+.should == $~.captures.last + $+.should == 'o' + end + + it "captures the last non nil capture" do + /(a)|(b)/ =~ 'a' + $+.should == 'a' + end + + with_feature :encoding do + it "sets the encoding to the encoding of the source String" do + "abc".force_encoding(Encoding::EUC_JP) =~ /(b)/ + $+.encoding.should equal(Encoding::EUC_JP) + end + end +end + +describe "Predefined globals $1..N" do + it "are equivalent to $~[N]" do + /(f)(o)(o)/ =~ 'foo' + $1.should == $~[1] + $2.should == $~[2] + $3.should == $~[3] + $4.should == $~[4] + + [$1, $2, $3, $4].should == ['f', 'o', 'o', nil] + end + + it "are nil unless a match group occurs" do + def test(arg) + case arg + when /-(.)?/ + $1 + end + end + test("-").should == nil + end + + with_feature :encoding do + it "sets the encoding to the encoding of the source String" do + "abc".force_encoding(Encoding::EUC_JP) =~ /(b)/ + $1.encoding.should equal(Encoding::EUC_JP) + end + end +end + +describe "Predefined global $stdout" do + before :each do + @old_stdout = $stdout + end + + after :each do + $stdout = @old_stdout + end + + it "raises TypeError error if assigned to nil" do + lambda { $stdout = nil }.should raise_error(TypeError) + end + + it "raises TypeError error if assigned to object that doesn't respond to #write" do + obj = mock('object') + lambda { $stdout = obj }.should raise_error(TypeError) + + obj.stub!(:write) + $stdout = obj + $stdout.should equal(obj) + end +end + +describe "Predefined global $!" do + # See http://jira.codehaus.org/browse/JRUBY-5550 + it "remains nil after a failed core class \"checked\" coercion against a class that defines method_missing" do + $!.should == nil + + obj = Class.new do + def method_missing(*args) + super + end + end.new + + [obj, 'foo'].join + + $!.should == nil + end + + it "should be set to the value of $! before the begin after a successful rescue" do + outer = StandardError.new 'outer' + inner = StandardError.new 'inner' + + begin + raise outer + rescue + $!.should == outer + + # nested rescue + begin + $!.should == outer + raise inner + rescue + $!.should == inner + ensure + $!.should == outer + end + $!.should == outer + end + $!.should == nil + end + + it "should be set to the value of $! before the begin after a rescue which returns" do + def foo + outer = StandardError.new 'outer' + inner = StandardError.new 'inner' + + begin + raise outer + rescue + $!.should == outer + + # nested rescue + begin + $!.should == outer + raise inner + rescue + $!.should == inner + return + ensure + $!.should == outer + end + $!.should == outer + end + $!.should == nil + end + foo + end + + it "should be set to the value of $! before the begin after a successful rescue within an ensure" do + outer = StandardError.new 'outer' + inner = StandardError.new 'inner' + + begin + begin + raise outer + ensure + $!.should == outer + + # nested rescue + begin + $!.should == outer + raise inner + rescue + $!.should == inner + ensure + $!.should == outer + end + $!.should == outer + end + flunk "outer should be raised after the ensure" + rescue + $!.should == outer + end + $!.should == nil + end + + it "should be set to the new exception after a throwing rescue" do + outer = StandardError.new 'outer' + inner = StandardError.new 'inner' + + begin + raise outer + rescue + $!.should == outer + + begin + # nested rescue + begin + $!.should == outer + raise inner + rescue # the throwing rescue + $!.should == inner + raise inner + ensure + $!.should == inner + end + rescue # do not make the exception fail the example + $!.should == inner + end + $!.should == outer + end + $!.should == nil + end + + describe "in bodies without ensure" do + it "should be cleared when an exception is rescued" do + e = StandardError.new 'foo' + begin + raise e + rescue + $!.should == e + end + $!.should == nil + end + + it "should be cleared when an exception is rescued even when a non-local return is present" do + def foo(e) + $!.should == e + yield + end + def bar + e = StandardError.new 'foo' + begin + raise e + rescue + $!.should == e + foo(e) { return } + end + end + + bar + $!.should == nil + end + + it "should not be cleared when an exception is not rescued" do + e = StandardError.new + begin + begin + begin + raise e + rescue TypeError + flunk + end + ensure + $!.should == e + end + rescue + $!.should == e + end + $!.should == nil + end + + it "should not be cleared when an exception is rescued and rethrown" do + e = StandardError.new 'foo' + begin + begin + begin + raise e + rescue => e + $!.should == e + raise e + end + ensure + $!.should == e + end + rescue + $!.should == e + end + $!.should == nil + end + end + + describe "in ensure-protected bodies" do + it "should be cleared when an exception is rescued" do + e = StandardError.new 'foo' + begin + raise e + rescue + $!.should == e + ensure + $!.should == nil + end + $!.should == nil + end + + it "should not be cleared when an exception is not rescued" do + e = StandardError.new + begin + begin + begin + raise e + rescue TypeError + flunk + ensure + $!.should == e + end + ensure + $!.should == e + end + rescue + $!.should == e + end + end + + it "should not be cleared when an exception is rescued and rethrown" do + e = StandardError.new + begin + begin + begin + raise e + rescue => e + $!.should == e + raise e + ensure + $!.should == e + end + ensure + $!.should == e + end + rescue + $!.should == e + end + end + end +end + +=begin +Input/Output Variables +--------------------------------------------------------------------------------------------------- + +$/ String The input record separator (newline by default). This is the value that rou- + tines such as Kernel#gets use to determine record boundaries. If set to + nil, gets will read the entire file. +$-0 String Synonym for $/. +$\ String The string appended to the output of every call to methods such as + Kernel#print and IO#write. The default value is nil. +$, String The separator string output between the parameters to methods such as + Kernel#print and Array#join. Defaults to nil, which adds no text. +$. Fixnum The number of the last line read from the current input file. +$; String The default separator pattern used by String#split. May be set from the + command line using the -F flag. +$< Object An object that provides access to the concatenation of the contents of all + the files given as command-line arguments or $stdin (in the case where + there are no arguments). $< supports methods similar to a File object: + binmode, close, closed?, each, each_byte, each_line, eof, eof?, + file, filename, fileno, getc, gets, lineno, lineno=, path, pos, pos=, + read, readchar, readline, readlines, rewind, seek, skip, tell, to_a, + to_i, to_io, to_s, along with the methods in Enumerable. The method + file returns a File object for the file currently being read. This may change + as $< reads through the files on the command line. [r/o] +$> IO The destination of output for Kernel#print and Kernel#printf. The + default value is $stdout. +$_ String The last line read by Kernel#gets or Kernel#readline. Many string- + related functions in the Kernel module operate on $_ by default. The vari- + able is local to the current scope. [thread] +$-F String Synonym for $;. +$stderr IO The current standard error output. +$stdin IO The current standard input. +$stdout IO The current standard output. Assignment to $stdout is deprecated: use + $stdout.reopen instead. +=end + +describe "Predefined global $/" do + before :each do + @dollar_slash = $/ + @dollar_dash_zero = $-0 + end + + after :each do + $/ = @dollar_slash + $-0 = @dollar_dash_zero + end + + it "can be assigned a String" do + str = "abc" + $/ = str + $/.should equal(str) + end + + it "can be assigned nil" do + $/ = nil + $/.should be_nil + end + + it "returns the value assigned" do + ($/ = "xyz").should == "xyz" + end + + + it "changes $-0" do + $/ = "xyz" + $-0.should equal($/) + end + + it "does not call #to_str to convert the object to a String" do + obj = mock("$/ value") + obj.should_not_receive(:to_str) + + lambda { $/ = obj }.should raise_error(TypeError) + end + + it "raises a TypeError if assigned a Fixnum" do + lambda { $/ = 1 }.should raise_error(TypeError) + end + + it "raises a TypeError if assigned a boolean" do + lambda { $/ = true }.should raise_error(TypeError) + end +end + +describe "Predefined global $-0" do + before :each do + @dollar_slash = $/ + @dollar_dash_zero = $-0 + end + + after :each do + $/ = @dollar_slash + $-0 = @dollar_dash_zero + end + + it "can be assigned a String" do + str = "abc" + $-0 = str + $-0.should equal(str) + end + + it "can be assigned nil" do + $-0 = nil + $-0.should be_nil + end + + it "returns the value assigned" do + ($-0 = "xyz").should == "xyz" + end + + it "changes $/" do + $-0 = "xyz" + $/.should equal($-0) + end + + it "does not call #to_str to convert the object to a String" do + obj = mock("$-0 value") + obj.should_not_receive(:to_str) + + lambda { $-0 = obj }.should raise_error(TypeError) + end + + it "raises a TypeError if assigned a Fixnum" do + lambda { $-0 = 1 }.should raise_error(TypeError) + end + + it "raises a TypeError if assigned a boolean" do + lambda { $-0 = true }.should raise_error(TypeError) + end +end + +describe "Predefined global $," do + after :each do + $, = nil + end + + it "defaults to nil" do + $,.should be_nil + end + + it "raises TypeError if assigned a non-String" do + lambda { $, = Object.new }.should raise_error(TypeError) + end +end + +describe "Predefined global $_" do + it "is set to the last line read by e.g. StringIO#gets" do + stdin = StringIO.new("foo\nbar\n", "r") + + read = stdin.gets + read.should == "foo\n" + $_.should == read + + read = stdin.gets + read.should == "bar\n" + $_.should == read + + read = stdin.gets + read.should == nil + $_.should == read + end + + it "is set at the method-scoped level rather than block-scoped" do + obj = Object.new + def obj.foo; yield; end + def obj.foo2; yield; end + + stdin = StringIO.new("foo\nbar\nbaz\nqux\n", "r") + match = stdin.gets + + obj.foo { match = stdin.gets } + + match.should == "bar\n" + $_.should == match + + eval 'match = stdin.gets' + + match.should == "baz\n" + $_.should == match + + obj.foo2 { match = stdin.gets } + + match.should == "qux\n" + $_.should == match + end + + it "is Thread-local" do + $_ = nil + running = false + + thr = Thread.new do + $_ = "last line" + running = true + end + + Thread.pass until running + $_.should be_nil + + thr.join + end + + it "can be assigned any value" do + $_ = nil + $_.should == nil + $_ = "foo" + $_.should == "foo" + o = Object.new + $_ = o + $_.should == o + $_ = 1 + $_.should == 1 + end +end + +=begin +Execution Environment Variables +--------------------------------------------------------------------------------------------------- + +$0 String The name of the top-level Ruby program being executed. Typically this will + be the program’s filename. On some operating systems, assigning to this + variable will change the name of the process reported (for example) by the + ps(1) command. +$* Array An array of strings containing the command-line options from the invoca- + tion of the program. Options used by the Ruby interpreter will have been + removed. [r/o] +$" Array An array containing the filenames of modules loaded by require. [r/o] +$$ Fixnum The process number of the program being executed. [r/o] +$? Process::Status The exit status of the last child process to terminate. [r/o, thread] +$: Array An array of strings, where each string specifies a directory to be searched for + Ruby scripts and binary extensions used by the load and require methods. + The initial value is the value of the arguments passed via the -I command- + line option, followed by an installation-defined standard library location, fol- + lowed by the current directory (“.”). This variable may be set from within a + program to alter the default search path; typically, programs use $: << dir + to append dir to the path. [r/o] +$-a Object True if the -a option is specified on the command line. [r/o] +$-d Object Synonym for $DEBUG. +$DEBUG Object Set to true if the -d command-line option is specified. +__FILE__ String The name of the current source file. [r/o] +$F Array The array that receives the split input line if the -a command-line option is + used. +$FILENAME String The name of the current input file. Equivalent to $<.filename. [r/o] +$-i String If in-place edit mode is enabled (perhaps using the -i command-line + option), $-i holds the extension used when creating the backup file. If you + set a value into $-i, enables in-place edit mode. +$-I Array Synonym for $:. [r/o] +$-K String Sets the multibyte coding system for strings and regular expressions. Equiv- + alent to the -K command-line option. +$-l Object Set to true if the -l option (which enables line-end processing) is present + on the command line. [r/o] +__LINE__ String The current line number in the source file. [r/o] +$LOAD_PATH Array A synonym for $:. [r/o] +$-p Object Set to true if the -p option (which puts an implicit while gets . . . end + loop around your program) is present on the command line. [r/o] +$SAFE Fixnum The current safe level. This variable’s value may never be + reduced by assignment. [thread] (Not implemented in Rubinius) +$VERBOSE Object Set to true if the -v, --version, -W, or -w option is specified on the com- + mand line. Set to false if no option, or -W1 is given. Set to nil if -W0 + was specified. Setting this option to true causes the interpreter and some + library routines to report additional information. Setting to nil suppresses + all warnings (including the output of Kernel.warn). +$-v Object Synonym for $VERBOSE. +$-w Object Synonym for $VERBOSE. +=end +describe "Execution variable $:" do + it "is initialized to an array of strings" do + $:.is_a?(Array).should == true + ($:.length > 0).should == true + end + + it "does not include the current directory" do + $:.should_not include(".") + end + + it "is the same object as $LOAD_PATH and $-I" do + $:.__id__.should == $LOAD_PATH.__id__ + $:.__id__.should == $-I.__id__ + end + + it "can be changed via <<" do + $: << "foo" + $:.should include("foo") + end + + it "is read-only" do + lambda { + $: = [] + }.should raise_error(NameError) + + lambda { + $LOAD_PATH = [] + }.should raise_error(NameError) + + lambda { + $-I = [] + }.should raise_error(NameError) + end +end + +describe "Global variable $\"" do + it "is an alias for $LOADED_FEATURES" do + $".object_id.should == $LOADED_FEATURES.object_id + end + + it "is read-only" do + lambda { + $" = [] + }.should raise_error(NameError) + + lambda { + $LOADED_FEATURES = [] + }.should raise_error(NameError) + end +end + +describe "Global variable $<" do + it "is read-only" do + lambda { + $< = nil + }.should raise_error(NameError) + end +end + +describe "Global variable $FILENAME" do + it "is read-only" do + lambda { + $FILENAME = "-" + }.should raise_error(NameError) + end +end + +describe "Global variable $?" do + it "is read-only" do + lambda { + $? = nil + }.should raise_error(NameError) + end + + it "is thread-local" do + system(ruby_cmd('exit 0')) + Thread.new { $?.should be_nil }.join + end +end + +describe "Global variable $-a" do + it "is read-only" do + lambda { $-a = true }.should raise_error(NameError) + end +end + +describe "Global variable $-l" do + it "is read-only" do + lambda { $-l = true }.should raise_error(NameError) + end +end + +describe "Global variable $-p" do + it "is read-only" do + lambda { $-p = true }.should raise_error(NameError) + end +end + +describe "Global variable $-d" do + before :each do + @debug = $DEBUG + end + + after :each do + $DEBUG = @debug + end + + it "is an alias of $DEBUG" do + $DEBUG = true + $-d.should be_true + $-d = false + $DEBUG.should be_false + end +end + +describe :verbose_global_alias, shared: true do + before :each do + @verbose = $VERBOSE + end + + after :each do + $VERBOSE = @verbose + end + + it "is an alias of $VERBOSE" do + $VERBOSE = true + eval(@method).should be_true + eval("#{@method} = false") + $VERBOSE.should be_false + end +end + +describe "Global variable $-v" do + it_behaves_like :verbose_global_alias, '$-v' +end + +describe "Global variable $-w" do + it_behaves_like :verbose_global_alias, '$-w' +end + +describe "Global variable $0" do + before :each do + @orig_program_name = $0 + end + + after :each do + $0 = @orig_program_name + end + + it "is the path given as the main script and the same as __FILE__" do + script = "fixtures/dollar_zero.rb" + Dir.chdir(File.dirname(__FILE__)) do + ruby_exe(script).should == "#{script}\n#{script}\nOK" + end + end + + it "returns the program name" do + $0 = "rbx" + $0.should == "rbx" + end + + platform_is :linux, :darwin do + it "actually sets the program name" do + title = "rubyspec-dollar0-test" + $0 = title + `ps -ocommand= -p#{$$}`.should include(title) + end + end + + it "returns the given value when set" do + ($0 = "rbx").should == "rbx" + end + + it "raises a TypeError when not given an object that can be coerced to a String" do + lambda { $0 = nil }.should raise_error(TypeError) + end +end + +=begin +Standard Objects +--------------------------------------------------------------------------------------------------- + +ARGF Object A synonym for $<. +ARGV Array A synonym for $*. +ENV Object A hash-like object containing the program’s environment variables. An + instance of class Object, ENV implements the full set of Hash methods. Used + to query and set the value of an environment variable, as in ENV["PATH"] + and ENV["term"]="ansi". +false FalseClass Singleton instance of class FalseClass. [r/o] +nil NilClass The singleton instance of class NilClass. The value of uninitialized + instance and global variables. [r/o] +self Object The receiver (object) of the current method. [r/o] +true TrueClass Singleton instance of class TrueClass. [r/o] +=end + +describe "The predefined standard objects" do + it "includes ARGF" do + Object.const_defined?(:ARGF).should == true + end + + it "includes ARGV" do + Object.const_defined?(:ARGV).should == true + end + + it "includes a hash-like object ENV" do + Object.const_defined?(:ENV).should == true + ENV.respond_to?(:[]).should == true + end +end + +describe "The predefined standard object nil" do + it "is an instance of NilClass" do + nil.should be_kind_of(NilClass) + end + + it "raises a SyntaxError if assigned to" do + lambda { eval("nil = true") }.should raise_error(SyntaxError) + end +end + +describe "The predefined standard object true" do + it "is an instance of TrueClass" do + true.should be_kind_of(TrueClass) + end + + it "raises a SyntaxError if assigned to" do + lambda { eval("true = false") }.should raise_error(SyntaxError) + end +end + +describe "The predefined standard object false" do + it "is an instance of FalseClass" do + false.should be_kind_of(FalseClass) + end + + it "raises a SyntaxError if assigned to" do + lambda { eval("false = nil") }.should raise_error(SyntaxError) + end +end + +describe "The self pseudo-variable" do + it "raises a SyntaxError if assigned to" do + lambda { eval("self = 1") }.should raise_error(SyntaxError) + end +end + +=begin +Global Constants +--------------------------------------------------------------------------------------------------- + +The following constants are defined by the Ruby interpreter. + +DATA IO If the main program file contains the directive __END__, then + the constant DATA will be initialized so that reading from it will + return lines following __END__ from the source file. +FALSE FalseClass Synonym for false. +NIL NilClass Synonym for nil. +RUBY_PLATFORM String The identifier of the platform running this program. This string + is in the same form as the platform identifier used by the GNU + configure utility (which is not a coincidence). +RUBY_RELEASE_DATE String The date of this release. +RUBY_VERSION String The version number of the interpreter. +STDERR IO The actual standard error stream for the program. The initial + value of $stderr. +STDIN IO The actual standard input stream for the program. The initial + value of $stdin. +STDOUT IO The actual standard output stream for the program. The initial + value of $stdout. +SCRIPT_LINES__ Hash If a constant SCRIPT_LINES__ is defined and references a Hash, + Ruby will store an entry containing the contents of each file it + parses, with the file’s name as the key and an array of strings as + the value. +TOPLEVEL_BINDING Binding A Binding object representing the binding at Ruby’s top level— + the level where programs are initially executed. +TRUE TrueClass Synonym for true. +=end + +describe "The predefined global constants" do + ruby_version_is ""..."2.4" do + it "includes TRUE" do + Object.const_defined?(:TRUE).should == true + TRUE.should equal(true) + end + + it "includes FALSE" do + Object.const_defined?(:FALSE).should == true + FALSE.should equal(false) + end + + it "includes NIL" do + Object.const_defined?(:NIL).should == true + NIL.should equal(nil) + end + end + + ruby_version_is "2.4" do + it "includes TRUE" do + Object.const_defined?(:TRUE).should == true + -> { + TRUE.should equal(true) + }.should complain(/constant ::TRUE is deprecated/) + end + + it "includes FALSE" do + Object.const_defined?(:FALSE).should == true + -> { + FALSE.should equal(false) + }.should complain(/constant ::FALSE is deprecated/) + end + + it "includes NIL" do + Object.const_defined?(:NIL).should == true + -> { + NIL.should equal(nil) + }.should complain(/constant ::NIL is deprecated/) + end + end + + it "includes STDIN" do + Object.const_defined?(:STDIN).should == true + end + + it "includes STDOUT" do + Object.const_defined?(:STDOUT).should == true + end + + it "includes STDERR" do + Object.const_defined?(:STDERR).should == true + end + + it "includes RUBY_VERSION" do + Object.const_defined?(:RUBY_VERSION).should == true + end + + it "includes RUBY_RELEASE_DATE" do + Object.const_defined?(:RUBY_RELEASE_DATE).should == true + end + + it "includes RUBY_PLATFORM" do + Object.const_defined?(:RUBY_PLATFORM).should == true + end + + it "includes TOPLEVEL_BINDING" do + Object.const_defined?(:TOPLEVEL_BINDING).should == true + end + +end + +with_feature :encoding do + describe "The predefined global constant" do + before :each do + @external = Encoding.default_external + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + describe "STDIN" do + it "has the same external encoding as Encoding.default_external" do + STDIN.external_encoding.should equal(Encoding.default_external) + end + + it "has the same external encoding as Encoding.default_external when that encoding is changed" do + Encoding.default_external = Encoding::ISO_8859_16 + STDIN.external_encoding.should equal(Encoding::ISO_8859_16) + end + + it "has the encodings set by #set_encoding"do + code = "STDIN.set_encoding Encoding::IBM775, Encoding::IBM866; " \ + "p [STDIN.external_encoding.name, STDIN.internal_encoding.name]" + ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]} + end + + it "retains the encoding set by #set_encoding when Encoding.default_external is changed" do + code = "STDIN.set_encoding Encoding::IBM775, Encoding::IBM866; " \ + "Encoding.default_external = Encoding::ISO_8859_16;" \ + "p [STDIN.external_encoding.name, STDIN.internal_encoding.name]" + ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]} + end + + it "has nil for the internal encoding" do + STDIN.internal_encoding.should be_nil + end + + it "has nil for the internal encoding despite Encoding.default_internal being changed" do + Encoding.default_internal = Encoding::IBM437 + STDIN.internal_encoding.should be_nil + end + end + + describe "STDOUT" do + it "has nil for the external encoding" do + STDOUT.external_encoding.should be_nil + end + + it "has nil for the external encoding despite Encoding.default_external being changed" do + Encoding.default_external = Encoding::ISO_8859_1 + STDOUT.external_encoding.should be_nil + end + + it "has the encodings set by #set_encoding"do + code = "STDOUT.set_encoding Encoding::IBM775, Encoding::IBM866; " \ + "p [STDOUT.external_encoding.name, STDOUT.internal_encoding.name]" + ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]} + end + + it "has nil for the internal encoding" do + STDOUT.internal_encoding.should be_nil + end + + it "has nil for the internal encoding despite Encoding.default_internal being changed" do + Encoding.default_internal = Encoding::IBM437 + STDOUT.internal_encoding.should be_nil + end + end + + describe "STDERR" do + it "has nil for the external encoding" do + STDERR.external_encoding.should be_nil + end + + it "has nil for the external encoding despite Encoding.default_external being changed" do + Encoding.default_external = Encoding::ISO_8859_1 + STDERR.external_encoding.should be_nil + end + + it "has the encodings set by #set_encoding"do + code = "STDERR.set_encoding Encoding::IBM775, Encoding::IBM866; " \ + "p [STDERR.external_encoding.name, STDERR.internal_encoding.name]" + ruby_exe(code).chomp.should == %{["IBM775", "IBM866"]} + end + + it "has nil for the internal encoding" do + STDERR.internal_encoding.should be_nil + end + + it "has nil for the internal encoding despite Encoding.default_internal being changed" do + Encoding.default_internal = Encoding::IBM437 + STDERR.internal_encoding.should be_nil + end + end + + describe "ARGV" do + it "contains Strings encoded in locale Encoding" do + code = fixture __FILE__, "argv_encoding.rb" + result = ruby_exe(code, args: "a b") + encoding = Encoding.default_external + result.chomp.should == %{["#{encoding}", "#{encoding}"]} + end + end + end +end diff --git a/spec/rubyspec/language/private_spec.rb b/spec/rubyspec/language/private_spec.rb new file mode 100644 index 0000000000..796c0c1711 --- /dev/null +++ b/spec/rubyspec/language/private_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/private', __FILE__) + +describe "The private keyword" do + it "marks following methods as being private" do + a = Private::A.new + a.methods.should_not include(:bar) + lambda { a.bar }.should raise_error(NoMethodError) + + b = Private::B.new + b.methods.should_not include(:bar) + lambda { b.bar }.should raise_error(NoMethodError) + end + + # def expr.meth() methods are always public + it "has no effect on def expr.meth() methods" do + Private::B.public_defs_method.should == 0 + end + + it "is overridden when a new class is opened" do + c = Private::B::C.new + c.methods.should include(:baz) + c.baz + Private::B.public_class_method1.should == 1 + lambda { Private::B.private_class_method1 }.should raise_error(NoMethodError) + end + + it "is no longer in effect when the class is closed" do + b = Private::B.new + b.methods.should include(:foo) + b.foo + end + + it "changes visibility of previously called method" do + klass = Class.new do + def foo + "foo" + end + end + f = klass.new + f.foo + klass.class_eval do + private :foo + end + lambda { f.foo }.should raise_error(NoMethodError) + end + + it "changes visiblity of previously called methods with same send/call site" do + g = ::Private::G.new + lambda { + 2.times do + g.foo + module ::Private + class G + private :foo + end + end + end + }.should raise_error(NoMethodError) + end + + it "changes the visibility of the existing method in the subclass" do + ::Private::A.new.foo.should == 'foo' + lambda {::Private::H.new.foo}.should raise_error(NoMethodError) + ::Private::H.new.send(:foo).should == 'foo' + end +end diff --git a/spec/rubyspec/language/proc_spec.rb b/spec/rubyspec/language/proc_spec.rb new file mode 100644 index 0000000000..bbef318826 --- /dev/null +++ b/spec/rubyspec/language/proc_spec.rb @@ -0,0 +1,220 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "A Proc" do + it "captures locals from the surrounding scope" do + var = 1 + lambda { var }.call.should == 1 + end + + it "does not capture a local when an argument has the same name" do + var = 1 + lambda { |var| var }.call(2).should == 2 + var.should == 1 + end + + describe "taking zero arguments" do + before :each do + @l = lambda { 1 } + end + + it "does not raise an exception if no values are passed" do + @l.call.should == 1 + end + + it "raises an ArgumentErro if a value is passed" do + lambda { @l.call(0) }.should raise_error(ArgumentError) + end + end + + describe "taking || arguments" do + before :each do + @l = lambda { || 1 } + end + + it "does not raise an exception when passed no values" do + @l.call.should == 1 + end + + it "raises an ArgumentError if a value is passed" do + lambda { @l.call(0) }.should raise_error(ArgumentError) + end + end + + describe "taking |a| arguments" do + before :each do + @l = lambda { |a| a } + end + + it "assigns the value passed to the argument" do + @l.call(2).should == 2 + end + + it "does not destructure a single Array value" do + @l.call([1, 2]).should == [1, 2] + end + + it "does not call #to_ary to convert a single passed object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @l.call(obj).should equal(obj) + end + + it "raises an ArgumentError if no value is passed" do + lambda { @l.call }.should raise_error(ArgumentError) + end + end + + describe "taking |a, b| arguments" do + before :each do + @l = lambda { |a, b| [a, b] } + end + + it "raises an ArgumentError if passed no values" do + lambda { @l.call }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if passed one value" do + lambda { @l.call(0) }.should raise_error(ArgumentError) + end + + it "assigns the values passed to the arguments" do + @l.call(1, 2).should == [1, 2] + end + + it "does not call #to_ary to convert a single passed object to an Array" do + obj = mock("proc call to_ary") + obj.should_not_receive(:to_ary) + + lambda { @l.call(obj) }.should raise_error(ArgumentError) + end + end + + describe "taking |a, *b| arguments" do + before :each do + @l = lambda { |a, *b| [a, b] } + end + + it "raises an ArgumentError if passed no values" do + lambda { @l.call }.should raise_error(ArgumentError) + end + + it "does not destructure a single Array value yielded" do + @l.call([1, 2, 3]).should == [[1, 2, 3], []] + end + + it "assigns all passed values after the first to the rest argument" do + @l.call(1, 2, 3).should == [1, [2, 3]] + end + + it "does not call #to_ary to convert a single passed object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @l.call(obj).should == [obj, []] + end + end + + describe "taking |*| arguments" do + before :each do + @l = lambda { |*| 1 } + end + + it "does not raise an exception when passed no values" do + @l.call.should == 1 + end + + it "does not raise an exception when passed multiple values" do + @l.call(2, 3, 4).should == 1 + end + + it "does not call #to_ary to convert a single passed object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @l.call(obj).should == 1 + end + end + + describe "taking |*a| arguments" do + before :each do + @l = lambda { |*a| a } + end + + it "assigns [] to the argument when passed no values" do + @l.call.should == [] + end + + it "assigns the argument an Array wrapping one passed value" do + @l.call(1).should == [1] + end + + it "assigns the argument an Array wrapping all values passed" do + @l.call(1, 2, 3).should == [1, 2, 3] + end + + it "does not call #to_ary to convert a single passed object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @l.call(obj).should == [obj] + end + end + + describe "taking |a, | arguments" do + before :each do + @l = lambda { |a, | a } + end + + it "raises an ArgumentError when passed no values" do + lambda { @l.call }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed more than one value" do + lambda { @l.call(1, 2) }.should raise_error(ArgumentError) + end + + it "assigns the argument the value passed" do + @l.call(1).should == 1 + end + + it "does not destructure when passed a single Array" do + @l.call([1,2]).should == [1, 2] + end + + it "does not call #to_ary to convert a single passed object to an Array" do + obj = mock("block yield to_ary") + obj.should_not_receive(:to_ary) + + @l.call(obj).should == obj + end + end + + describe "taking |(a, b)| arguments" do + before :each do + @l = lambda { |(a, b)| [a, b] } + end + + it "raises an ArgumentError when passed no values" do + lambda { @l.call }.should raise_error(ArgumentError) + end + + it "destructures a single Array value yielded" do + @l.call([1, 2]).should == [1, 2] + end + + it "calls #to_ary to convert a single passed object to an Array" do + obj = mock("block yield to_ary") + obj.should_receive(:to_ary).and_return([1, 2]) + + @l.call(obj).should == [1, 2] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("block yield to_ary invalid") + obj.should_receive(:to_ary).and_return(1) + + lambda { @l.call(obj) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/language/redo_spec.rb b/spec/rubyspec/language/redo_spec.rb new file mode 100644 index 0000000000..53fd30b4f2 --- /dev/null +++ b/spec/rubyspec/language/redo_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The redo statement" do + it "restarts block execution if used within block" do + a = [] + lambda { + a << 1 + redo if a.size < 2 + a << 2 + }.call + a.should == [1, 1, 2] + end + + it "re-executes the closest loop" do + exist = [2,3] + processed = [] + order = [] + [1,2,3,4].each do |x| + order << x + begin + processed << x + if exist.include?(x) + raise StandardError, "included" + end + rescue StandardError + exist.delete(x) + redo + end + end + processed.should == [1,2,2,3,3,4] + exist.should == [] + order.should == [1,2,2,3,3,4] + end + + it "re-executes the last step in enumeration" do + list = [] + [1,2,3].each do |x| + list << x + break if list.size == 6 + redo if x == 3 + end + list.should == [1,2,3,3,3,3] + end + + it "triggers ensure block when re-executing a block" do + list = [] + [1,2,3].each do |x| + list << x + begin + list << 10*x + redo if list.count(1) == 1 + ensure + list << 100*x + end + end + list.should == [1,10,100,1,10,100,2,20,200,3,30,300] + end + + describe "in a method" do + it "is invalid and raises a SyntaxError" do + lambda { + eval("def m; redo; end") + }.should raise_error(SyntaxError) + end + end +end diff --git a/spec/rubyspec/language/regexp/anchors_spec.rb b/spec/rubyspec/language/regexp/anchors_spec.rb new file mode 100644 index 0000000000..c6a620a221 --- /dev/null +++ b/spec/rubyspec/language/regexp/anchors_spec.rb @@ -0,0 +1,179 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexps with anchors" do + it "supports ^ (line start anchor)" do + # Basic matching + /^foo/.match("foo").to_a.should == ["foo"] + /^bar/.match("foo\nbar").to_a.should == ["bar"] + # Basic non-matching + /^foo/.match(" foo").should be_nil + /foo^/.match("foo\n\n\n").should be_nil + + # A bit advanced + /^^^foo/.match("foo").to_a.should == ["foo"] + (/^[^f]/ =~ "foo\n\n").should == "foo\n".size and $~.to_a.should == ["\n"] + (/($^)($^)/ =~ "foo\n\n").should == "foo\n".size and $~.to_a.should == ["", "", ""] + + # Different start of line chars + /^bar/.match("foo\rbar").should be_nil + /^bar/.match("foo\0bar").should be_nil + + # Trivial + /^/.match("foo").to_a.should == [""] + + # Grouping + /(^foo)/.match("foo").to_a.should == ["foo", "foo"] + /(^)/.match("foo").to_a.should == ["", ""] + /(foo\n^)(^bar)/.match("foo\nbar").to_a.should == ["foo\nbar", "foo\n", "bar"] + end + + it "does not match ^ after trailing \\n" do + /^(?!\A)/.match("foo\n").should be_nil # There is no (empty) line after a trailing \n + end + + it "supports $ (line end anchor)" do + # Basic matching + /foo$/.match("foo").to_a.should == ["foo"] + /foo$/.match("foo\nbar").to_a.should == ["foo"] + # Basic non-matching + /foo$/.match("foo ").should be_nil + /$foo/.match("\n\n\nfoo").should be_nil + + # A bit advanced + /foo$$$/.match("foo").to_a.should == ["foo"] + (/[^o]$/ =~ "foo\n\n").should == ("foo\n".size - 1) and $~.to_a.should == ["\n"] + + # Different end of line chars + /foo$/.match("foo\r\nbar").should be_nil + /foo$/.match("foo\0bar").should be_nil + + # Trivial + (/$/ =~ "foo").should == "foo".size and $~.to_a.should == [""] + + # Grouping + /(foo$)/.match("foo").to_a.should == ["foo", "foo"] + (/($)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""] + /(foo$)($\nbar)/.match("foo\nbar").to_a.should == ["foo\nbar", "foo", "\nbar"] + end + + it "supports \\A (string start anchor)" do + # Basic matching + /\Afoo/.match("foo").to_a.should == ["foo"] + # Basic non-matching + /\Abar/.match("foo\nbar").should be_nil + /\Afoo/.match(" foo").should be_nil + + # A bit advanced + /\A\A\Afoo/.match("foo").to_a.should == ["foo"] + /(\A\Z)(\A\Z)/.match("").to_a.should == ["", "", ""] + + # Different start of line chars + /\Abar/.match("foo\0bar").should be_nil + + # Grouping + /(\Afoo)/.match("foo").to_a.should == ["foo", "foo"] + /(\A)/.match("foo").to_a.should == ["", ""] + end + + it "supports \\Z (string end anchor, including before trailing \\n)" do + # Basic matching + /foo\Z/.match("foo").to_a.should == ["foo"] + /foo\Z/.match("foo\n").to_a.should == ["foo"] + # Basic non-matching + /foo\Z/.match("foo\nbar").should be_nil + /foo\Z/.match("foo ").should be_nil + + # A bit advanced + /foo\Z\Z\Z/.match("foo\n").to_a.should == ["foo"] + (/($\Z)($\Z)/ =~ "foo\n").should == "foo".size and $~.to_a.should == ["", "", ""] + (/(\z\Z)(\z\Z)/ =~ "foo\n").should == "foo\n".size and $~.to_a.should == ["", "", ""] + + # Different end of line chars + /foo\Z/.match("foo\0bar").should be_nil + /foo\Z/.match("foo\r\n").should be_nil + + # Grouping + /(foo\Z)/.match("foo").to_a.should == ["foo", "foo"] + (/(\Z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""] + end + + it "supports \\z (string end anchor)" do + # Basic matching + /foo\z/.match("foo").to_a.should == ["foo"] + # Basic non-matching + /foo\z/.match("foo\nbar").should be_nil + /foo\z/.match("foo\n").should be_nil + /foo\z/.match("foo ").should be_nil + + # A bit advanced + /foo\z\z\z/.match("foo").to_a.should == ["foo"] + (/($\z)($\z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", "", ""] + + # Different end of line chars + /foo\z/.match("foo\0bar").should be_nil + /foo\z/.match("foo\r\nbar").should be_nil + + # Grouping + /(foo\z)/.match("foo").to_a.should == ["foo", "foo"] + (/(\z)/ =~ "foo").should == "foo".size and $~.to_a.should == ["", ""] + end + + it "supports \\b (word boundary)" do + # Basic matching + /foo\b/.match("foo").to_a.should == ["foo"] + /foo\b/.match("foo\n").to_a.should == ["foo"] + LanguageSpecs.white_spaces.scan(/./).each do |c| + /foo\b/.match("foo" + c).to_a.should == ["foo"] + end + LanguageSpecs.non_alphanum_non_space.scan(/./).each do |c| + /foo\b/.match("foo" + c).to_a.should == ["foo"] + end + /foo\b/.match("foo\0").to_a.should == ["foo"] + # Basic non-matching + /foo\b/.match("foobar").should be_nil + /foo\b/.match("foo123").should be_nil + /foo\b/.match("foo_").should be_nil + end + + it "supports \\B (non-word-boundary)" do + # Basic matching + /foo\B/.match("foobar").to_a.should == ["foo"] + /foo\B/.match("foo123").to_a.should == ["foo"] + /foo\B/.match("foo_").to_a.should == ["foo"] + # Basic non-matching + /foo\B/.match("foo").should be_nil + /foo\B/.match("foo\n").should be_nil + LanguageSpecs.white_spaces.scan(/./).each do |c| + /foo\B/.match("foo" + c).should be_nil + end + LanguageSpecs.non_alphanum_non_space.scan(/./).each do |c| + /foo\B/.match("foo" + c).should be_nil + end + /foo\B/.match("foo\0").should be_nil + end + + it "supports (?= ) (positive lookahead)" do + /foo.(?=bar)/.match("foo1 foo2bar").to_a.should == ["foo2"] + end + + it "supports (?! ) (negative lookahead)" do + /foo.(?!bar)/.match("foo1bar foo2").to_a.should == ["foo2"] + end + + it "supports (?!<) (negative lookbehind)" do + /(? (backreference to previous group match)" do + /(foo.)\1/.match("foo1foo1").to_a.should == ["foo1foo1", "foo1"] + /(foo.)\1/.match("foo1foo2").should be_nil + end + + it "resets nested \ backreference before match of outer subexpression" do + /(a\1?){2}/.match("aaaa").to_a.should == ["aa", "a"] + end +end diff --git a/spec/rubyspec/language/regexp/character_classes_spec.rb b/spec/rubyspec/language/regexp/character_classes_spec.rb new file mode 100644 index 0000000000..c922ba8a5a --- /dev/null +++ b/spec/rubyspec/language/regexp/character_classes_spec.rb @@ -0,0 +1,617 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexp with character classes" do + it "supports \\w (word character)" do + /\w/.match("a").to_a.should == ["a"] + /\w/.match("1").to_a.should == ["1"] + /\w/.match("_").to_a.should == ["_"] + + # Non-matches + /\w/.match(LanguageSpecs.white_spaces).should be_nil + /\w/.match(LanguageSpecs.non_alphanum_non_space).should be_nil + /\w/.match("\0").should be_nil + end + + it "supports \\W (non-word character)" do + /\W+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces] + /\W+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space] + /\W/.match("\0").to_a.should == ["\0"] + + # Non-matches + /\W/.match("a").should be_nil + /\W/.match("1").should be_nil + /\W/.match("_").should be_nil + end + + it "supports \\s (space character)" do + /\s+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces] + + # Non-matches + /\s/.match("a").should be_nil + /\s/.match("1").should be_nil + /\s/.match(LanguageSpecs.non_alphanum_non_space).should be_nil + /\s/.match("\0").should be_nil + end + + it "supports \\S (non-space character)" do + /\S/.match("a").to_a.should == ["a"] + /\S/.match("1").to_a.should == ["1"] + /\S+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space] + /\S/.match("\0").to_a.should == ["\0"] + + # Non-matches + /\S/.match(LanguageSpecs.white_spaces).should be_nil + end + + it "supports \\d (numeric digit)" do + /\d/.match("1").to_a.should == ["1"] + + # Non-matches + /\d/.match("a").should be_nil + /\d/.match(LanguageSpecs.white_spaces).should be_nil + /\d/.match(LanguageSpecs.non_alphanum_non_space).should be_nil + /\d/.match("\0").should be_nil + end + + it "supports \\D (non-digit)" do + /\D/.match("a").to_a.should == ["a"] + /\D+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces] + /\D+/.match(LanguageSpecs.non_alphanum_non_space).to_a.should == [LanguageSpecs.non_alphanum_non_space] + /\D/.match("\0").to_a.should == ["\0"] + + # Non-matches + /\D/.match("1").should be_nil + end + + it "supports [] (character class)" do + /[a-z]+/.match("fooBAR").to_a.should == ["foo"] + /[\b]/.match("\b").to_a.should == ["\b"] # \b inside character class is backspace + end + + it "supports [[:alpha:][:digit:][:etc:]] (predefined character classes)" do + /[[:alnum:]]+/.match("a1").to_a.should == ["a1"] + /[[:alpha:]]+/.match("Aa1").to_a.should == ["Aa"] + /[[:blank:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.blanks] + # /[[:cntrl:]]/.match("").to_a.should == [""] # TODO: what should this match? + /[[:digit:]]/.match("1").to_a.should == ["1"] + # /[[:graph:]]/.match("").to_a.should == [""] # TODO: what should this match? + /[[:lower:]]+/.match("Aa1").to_a.should == ["a"] + /[[:print:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [" "] # include all of multibyte encoded characters + /[[:punct:]]+/.match(LanguageSpecs.punctuations).to_a.should == [LanguageSpecs.punctuations] + /[[:space:]]+/.match(LanguageSpecs.white_spaces).to_a.should == [LanguageSpecs.white_spaces] + /[[:upper:]]+/.match("123ABCabc").to_a.should == ["ABC"] + /[[:xdigit:]]+/.match("xyz0123456789ABCDEFabcdefXYZ").to_a.should == ["0123456789ABCDEFabcdef"] + + # Parsing + /[[:lower:][:digit:]A-C]+/.match("a1ABCDEF").to_a.should == ["a1ABC"] # can be composed with other constructs in the character class + /[^[:lower:]A-C]+/.match("abcABCDEF123def").to_a.should == ["DEF123"] # negated character class + /[:alnum:]+/.match("a:l:n:u:m").to_a.should == ["a:l:n:u:m"] # should behave like regular character class composed of the individual letters + /[\[:alnum:]+/.match("[:a:l:n:u:m").to_a.should == ["[:a:l:n:u:m"] # should behave like regular character class composed of the individual letters + lambda { eval('/[[:alpha:]-[:digit:]]/') }.should raise_error(SyntaxError) # can't use character class as a start value of range + end + + it "matches ASCII characters with [[:ascii:]]" do + "\x00".match(/[[:ascii:]]/).to_a.should == ["\x00"] + "\x7F".match(/[[:ascii:]]/).to_a.should == ["\x7F"] + end + + not_supported_on :opal do + it "doesn't match non-ASCII characters with [[:ascii:]]" do + /[[:ascii:]]/.match("\u{80}").should be_nil + /[[:ascii:]]/.match("\u{9898}").should be_nil + end + end + + it "matches Unicode letter characters with [[:alnum:]]" do + "à".match(/[[:alnum:]]/).to_a.should == ["à"] + end + + it "matches Unicode digits with [[:alnum:]]" do + "\u{0660}".match(/[[:alnum:]]/).to_a.should == ["\u{0660}"] + end + + it "doesn't matches Unicode marks with [[:alnum:]]" do + "\u{36F}".match(/[[:alnum:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:alnum:]]" do + "\u{16}".match(/[[:alnum:]]/).to_a.should == [] + end + + it "doesn't match Unicode punctuation characters with [[:alnum:]]" do + "\u{3F}".match(/[[:alnum:]]/).to_a.should == [] + end + + it "matches Unicode letter characters with [[:alpha:]]" do + "à".match(/[[:alpha:]]/).to_a.should == ["à"] + end + + it "doesn't match Unicode digits with [[:alpha:]]" do + "\u{0660}".match(/[[:alpha:]]/).to_a.should == [] + end + + it "doesn't matches Unicode marks with [[:alpha:]]" do + "\u{36F}".match(/[[:alpha:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:alpha:]]" do + "\u{16}".match(/[[:alpha:]]/).to_a.should == [] + end + + it "doesn't match Unicode punctuation characters with [[:alpha:]]" do + "\u{3F}".match(/[[:alpha:]]/).to_a.should == [] + end + + it "matches Unicode space characters with [[:blank:]]" do + "\u{1680}".match(/[[:blank:]]/).to_a.should == ["\u{1680}"] + end + + it "doesn't match Unicode control characters with [[:blank:]]" do + "\u{16}".match(/[[:blank:]]/).should be_nil + end + + it "doesn't match Unicode punctuation characters with [[:blank:]]" do + "\u{3F}".match(/[[:blank:]]/).should be_nil + end + + it "doesn't match Unicode letter characters with [[:blank:]]" do + "à".match(/[[:blank:]]/).should be_nil + end + + it "doesn't match Unicode digits with [[:blank:]]" do + "\u{0660}".match(/[[:blank:]]/).should be_nil + end + + it "doesn't match Unicode marks with [[:blank:]]" do + "\u{36F}".match(/[[:blank:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:blank:]]" do + "\u{16}".match(/[[:blank:]]/).should be_nil + end + + it "doesn't Unicode letter characters with [[:cntrl:]]" do + "à".match(/[[:cntrl:]]/).should be_nil + end + + it "doesn't match Unicode digits with [[:cntrl:]]" do + "\u{0660}".match(/[[:cntrl:]]/).should be_nil + end + + it "doesn't match Unicode marks with [[:cntrl:]]" do + "\u{36F}".match(/[[:cntrl:]]/).should be_nil + end + + it "doesn't match Unicode punctuation characters with [[:cntrl:]]" do + "\u{3F}".match(/[[:cntrl:]]/).should be_nil + end + + it "matches Unicode control characters with [[:cntrl:]]" do + "\u{16}".match(/[[:cntrl:]]/).to_a.should == ["\u{16}"] + end + + it "doesn't match Unicode format characters with [[:cntrl:]]" do + "\u{2060}".match(/[[:cntrl:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:cntrl:]]" do + "\u{E001}".match(/[[:cntrl:]]/).should be_nil + end + + it "doesn't match Unicode letter characters with [[:digit:]]" do + "à".match(/[[:digit:]]/).should be_nil + end + + it "matches Unicode digits with [[:digit:]]" do + "\u{0660}".match(/[[:digit:]]/).to_a.should == ["\u{0660}"] + "\u{FF12}".match(/[[:digit:]]/).to_a.should == ["\u{FF12}"] + end + + it "doesn't match Unicode marks with [[:digit:]]" do + "\u{36F}".match(/[[:digit:]]/).should be_nil + end + + it "doesn't match Unicode punctuation characters with [[:digit:]]" do + "\u{3F}".match(/[[:digit:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:digit:]]" do + "\u{16}".match(/[[:digit:]]/).should be_nil + end + + it "doesn't match Unicode format characters with [[:digit:]]" do + "\u{2060}".match(/[[:digit:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:digit:]]" do + "\u{E001}".match(/[[:digit:]]/).should be_nil + end + + it "matches Unicode letter characters with [[:graph:]]" do + "à".match(/[[:graph:]]/).to_a.should == ["à"] + end + + it "matches Unicode digits with [[:graph:]]" do + "\u{0660}".match(/[[:graph:]]/).to_a.should == ["\u{0660}"] + "\u{FF12}".match(/[[:graph:]]/).to_a.should == ["\u{FF12}"] + end + + it "matches Unicode marks with [[:graph:]]" do + "\u{36F}".match(/[[:graph:]]/).to_a.should ==["\u{36F}"] + end + + it "matches Unicode punctuation characters with [[:graph:]]" do + "\u{3F}".match(/[[:graph:]]/).to_a.should == ["\u{3F}"] + end + + it "doesn't match Unicode control characters with [[:graph:]]" do + "\u{16}".match(/[[:graph:]]/).should be_nil + end + + it "match Unicode format characters with [[:graph:]]" do + "\u{2060}".match(/[[:graph:]]/).to_a.should == ["\u2060"] + end + + it "match Unicode private-use characters with [[:graph:]]" do + "\u{E001}".match(/[[:graph:]]/).to_a.should == ["\u{E001}"] + end + + it "matches Unicode lowercase letter characters with [[:lower:]]" do + "\u{FF41}".match(/[[:lower:]]/).to_a.should == ["\u{FF41}"] + "\u{1D484}".match(/[[:lower:]]/).to_a.should == ["\u{1D484}"] + "\u{E8}".match(/[[:lower:]]/).to_a.should == ["\u{E8}"] + end + + it "doesn't match Unicode uppercase letter characters with [[:lower:]]" do + "\u{100}".match(/[[:lower:]]/).should be_nil + "\u{130}".match(/[[:lower:]]/).should be_nil + "\u{405}".match(/[[:lower:]]/).should be_nil + end + + it "doesn't match Unicode title-case characters with [[:lower:]]" do + "\u{1F88}".match(/[[:lower:]]/).should be_nil + "\u{1FAD}".match(/[[:lower:]]/).should be_nil + "\u{01C5}".match(/[[:lower:]]/).should be_nil + end + + it "doesn't match Unicode digits with [[:lower:]]" do + "\u{0660}".match(/[[:lower:]]/).should be_nil + "\u{FF12}".match(/[[:lower:]]/).should be_nil + end + + it "doesn't match Unicode marks with [[:lower:]]" do + "\u{36F}".match(/[[:lower:]]/).should be_nil + end + + it "doesn't match Unicode punctuation characters with [[:lower:]]" do + "\u{3F}".match(/[[:lower:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:lower:]]" do + "\u{16}".match(/[[:lower:]]/).should be_nil + end + + it "doesn't match Unicode format characters with [[:lower:]]" do + "\u{2060}".match(/[[:lower:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:lower:]]" do + "\u{E001}".match(/[[:lower:]]/).should be_nil + end + + it "matches Unicode lowercase letter characters with [[:print:]]" do + "\u{FF41}".match(/[[:print:]]/).to_a.should == ["\u{FF41}"] + "\u{1D484}".match(/[[:print:]]/).to_a.should == ["\u{1D484}"] + "\u{E8}".match(/[[:print:]]/).to_a.should == ["\u{E8}"] + end + + it "matches Unicode uppercase letter characters with [[:print:]]" do + "\u{100}".match(/[[:print:]]/).to_a.should == ["\u{100}"] + "\u{130}".match(/[[:print:]]/).to_a.should == ["\u{130}"] + "\u{405}".match(/[[:print:]]/).to_a.should == ["\u{405}"] + end + + it "matches Unicode title-case characters with [[:print:]]" do + "\u{1F88}".match(/[[:print:]]/).to_a.should == ["\u{1F88}"] + "\u{1FAD}".match(/[[:print:]]/).to_a.should == ["\u{1FAD}"] + "\u{01C5}".match(/[[:print:]]/).to_a.should == ["\u{01C5}"] + end + + it "matches Unicode digits with [[:print:]]" do + "\u{0660}".match(/[[:print:]]/).to_a.should == ["\u{0660}"] + "\u{FF12}".match(/[[:print:]]/).to_a.should == ["\u{FF12}"] + end + + it "matches Unicode marks with [[:print:]]" do + "\u{36F}".match(/[[:print:]]/).to_a.should == ["\u{36F}"] + end + + it "matches Unicode punctuation characters with [[:print:]]" do + "\u{3F}".match(/[[:print:]]/).to_a.should == ["\u{3F}"] + end + + it "doesn't match Unicode control characters with [[:print:]]" do + "\u{16}".match(/[[:print:]]/).should be_nil + end + + it "match Unicode format characters with [[:print:]]" do + "\u{2060}".match(/[[:print:]]/).to_a.should == ["\u{2060}"] + end + + it "match Unicode private-use characters with [[:print:]]" do + "\u{E001}".match(/[[:print:]]/).to_a.should == ["\u{E001}"] + end + + + it "doesn't match Unicode lowercase letter characters with [[:punct:]]" do + "\u{FF41}".match(/[[:punct:]]/).should be_nil + "\u{1D484}".match(/[[:punct:]]/).should be_nil + "\u{E8}".match(/[[:punct:]]/).should be_nil + end + + it "doesn't match Unicode uppercase letter characters with [[:punct:]]" do + "\u{100}".match(/[[:punct:]]/).should be_nil + "\u{130}".match(/[[:punct:]]/).should be_nil + "\u{405}".match(/[[:punct:]]/).should be_nil + end + + it "doesn't match Unicode title-case characters with [[:punct:]]" do + "\u{1F88}".match(/[[:punct:]]/).should be_nil + "\u{1FAD}".match(/[[:punct:]]/).should be_nil + "\u{01C5}".match(/[[:punct:]]/).should be_nil + end + + it "doesn't match Unicode digits with [[:punct:]]" do + "\u{0660}".match(/[[:punct:]]/).should be_nil + "\u{FF12}".match(/[[:punct:]]/).should be_nil + end + + it "doesn't match Unicode marks with [[:punct:]]" do + "\u{36F}".match(/[[:punct:]]/).should be_nil + end + + it "matches Unicode Pc characters with [[:punct:]]" do + "\u{203F}".match(/[[:punct:]]/).to_a.should == ["\u{203F}"] + end + + it "matches Unicode Pd characters with [[:punct:]]" do + "\u{2E17}".match(/[[:punct:]]/).to_a.should == ["\u{2E17}"] + end + + it "matches Unicode Ps characters with [[:punct:]]" do + "\u{0F3A}".match(/[[:punct:]]/).to_a.should == ["\u{0F3A}"] + end + + it "matches Unicode Pe characters with [[:punct:]]" do + "\u{2046}".match(/[[:punct:]]/).to_a.should == ["\u{2046}"] + end + + it "matches Unicode Pi characters with [[:punct:]]" do + "\u{00AB}".match(/[[:punct:]]/).to_a.should == ["\u{00AB}"] + end + + it "matches Unicode Pf characters with [[:punct:]]" do + "\u{201D}".match(/[[:punct:]]/).to_a.should == ["\u{201D}"] + end + + it "matches Unicode Pf characters with [[:punct:]]" do + "\u{00BB}".match(/[[:punct:]]/).to_a.should == ["\u{00BB}"] + end + + it "matches Unicode Po characters with [[:punct:]]" do + "\u{00BF}".match(/[[:punct:]]/).to_a.should == ["\u{00BF}"] + end + + it "doesn't match Unicode format characters with [[:punct:]]" do + "\u{2060}".match(/[[:punct:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:punct:]]" do + "\u{E001}".match(/[[:punct:]]/).should be_nil + end + + it "doesn't match Unicode lowercase letter characters with [[:space:]]" do + "\u{FF41}".match(/[[:space:]]/).should be_nil + "\u{1D484}".match(/[[:space:]]/).should be_nil + "\u{E8}".match(/[[:space:]]/).should be_nil + end + + it "doesn't match Unicode uppercase letter characters with [[:space:]]" do + "\u{100}".match(/[[:space:]]/).should be_nil + "\u{130}".match(/[[:space:]]/).should be_nil + "\u{405}".match(/[[:space:]]/).should be_nil + end + + it "doesn't match Unicode title-case characters with [[:space:]]" do + "\u{1F88}".match(/[[:space:]]/).should be_nil + "\u{1FAD}".match(/[[:space:]]/).should be_nil + "\u{01C5}".match(/[[:space:]]/).should be_nil + end + + it "doesn't match Unicode digits with [[:space:]]" do + "\u{0660}".match(/[[:space:]]/).should be_nil + "\u{FF12}".match(/[[:space:]]/).should be_nil + end + + it "doesn't match Unicode marks with [[:space:]]" do + "\u{36F}".match(/[[:space:]]/).should be_nil + end + + it "matches Unicode Zs characters with [[:space:]]" do + "\u{205F}".match(/[[:space:]]/).to_a.should == ["\u{205F}"] + end + + it "matches Unicode Zl characters with [[:space:]]" do + "\u{2028}".match(/[[:space:]]/).to_a.should == ["\u{2028}"] + end + + it "matches Unicode Zp characters with [[:space:]]" do + "\u{2029}".match(/[[:space:]]/).to_a.should == ["\u{2029}"] + end + + it "doesn't match Unicode format characters with [[:space:]]" do + "\u{2060}".match(/[[:space:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:space:]]" do + "\u{E001}".match(/[[:space:]]/).should be_nil + end + + it "doesn't match Unicode lowercase characters with [[:upper:]]" do + "\u{FF41}".match(/[[:upper:]]/).should be_nil + "\u{1D484}".match(/[[:upper:]]/).should be_nil + "\u{E8}".match(/[[:upper:]]/).should be_nil + end + + it "matches Unicode uppercase characters with [[:upper:]]" do + "\u{100}".match(/[[:upper:]]/).to_a.should == ["\u{100}"] + "\u{130}".match(/[[:upper:]]/).to_a.should == ["\u{130}"] + "\u{405}".match(/[[:upper:]]/).to_a.should == ["\u{405}"] + end + + it "doesn't match Unicode title-case characters with [[:upper:]]" do + "\u{1F88}".match(/[[:upper:]]/).should be_nil + "\u{1FAD}".match(/[[:upper:]]/).should be_nil + "\u{01C5}".match(/[[:upper:]]/).should be_nil + end + + it "doesn't match Unicode digits with [[:upper:]]" do + "\u{0660}".match(/[[:upper:]]/).should be_nil + "\u{FF12}".match(/[[:upper:]]/).should be_nil + end + + it "doesn't match Unicode marks with [[:upper:]]" do + "\u{36F}".match(/[[:upper:]]/).should be_nil + end + + it "doesn't match Unicode punctuation characters with [[:upper:]]" do + "\u{3F}".match(/[[:upper:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:upper:]]" do + "\u{16}".match(/[[:upper:]]/).should be_nil + end + + it "doesn't match Unicode format characters with [[:upper:]]" do + "\u{2060}".match(/[[:upper:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:upper:]]" do + "\u{E001}".match(/[[:upper:]]/).should be_nil + end + + it "doesn't match Unicode letter characters [^a-fA-F] with [[:xdigit:]]" do + "à".match(/[[:xdigit:]]/).should be_nil + "g".match(/[[:xdigit:]]/).should be_nil + "X".match(/[[:xdigit:]]/).should be_nil + end + + it "matches Unicode letter characters [a-fA-F] with [[:xdigit:]]" do + "a".match(/[[:xdigit:]]/).to_a.should == ["a"] + "F".match(/[[:xdigit:]]/).to_a.should == ["F"] + end + + it "doesn't match Unicode digits [^0-9] with [[:xdigit:]]" do + "\u{0660}".match(/[[:xdigit:]]/).should be_nil + "\u{FF12}".match(/[[:xdigit:]]/).should be_nil + end + + it "doesn't match Unicode marks with [[:xdigit:]]" do + "\u{36F}".match(/[[:xdigit:]]/).should be_nil + end + + it "doesn't match Unicode punctuation characters with [[:xdigit:]]" do + "\u{3F}".match(/[[:xdigit:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:xdigit:]]" do + "\u{16}".match(/[[:xdigit:]]/).should be_nil + end + + it "doesn't match Unicode format characters with [[:xdigit:]]" do + "\u{2060}".match(/[[:xdigit:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:xdigit:]]" do + "\u{E001}".match(/[[:xdigit:]]/).should be_nil + end + + it "matches Unicode lowercase characters with [[:word:]]" do + "\u{FF41}".match(/[[:word:]]/).to_a.should == ["\u{FF41}"] + "\u{1D484}".match(/[[:word:]]/).to_a.should == ["\u{1D484}"] + "\u{E8}".match(/[[:word:]]/).to_a.should == ["\u{E8}"] + end + + it "matches Unicode uppercase characters with [[:word:]]" do + "\u{100}".match(/[[:word:]]/).to_a.should == ["\u{100}"] + "\u{130}".match(/[[:word:]]/).to_a.should == ["\u{130}"] + "\u{405}".match(/[[:word:]]/).to_a.should == ["\u{405}"] + end + + it "matches Unicode title-case characters with [[:word:]]" do + "\u{1F88}".match(/[[:word:]]/).to_a.should == ["\u{1F88}"] + "\u{1FAD}".match(/[[:word:]]/).to_a.should == ["\u{1FAD}"] + "\u{01C5}".match(/[[:word:]]/).to_a.should == ["\u{01C5}"] + end + + it "matches Unicode decimal digits with [[:word:]]" do + "\u{FF10}".match(/[[:word:]]/).to_a.should == ["\u{FF10}"] + "\u{096C}".match(/[[:word:]]/).to_a.should == ["\u{096C}"] + end + + it "matches Unicode marks with [[:word:]]" do + "\u{36F}".match(/[[:word:]]/).to_a.should == ["\u{36F}"] + end + + it "match Unicode Nl characters with [[:word:]]" do + "\u{16EE}".match(/[[:word:]]/).to_a.should == ["\u{16EE}"] + end + + it "doesn't match Unicode No characters with [[:word:]]" do + "\u{17F0}".match(/[[:word:]]/).should be_nil + end + it "doesn't match Unicode punctuation characters with [[:word:]]" do + "\u{3F}".match(/[[:word:]]/).should be_nil + end + + it "doesn't match Unicode control characters with [[:word:]]" do + "\u{16}".match(/[[:word:]]/).should be_nil + end + + it "doesn't match Unicode format characters with [[:word:]]" do + "\u{2060}".match(/[[:word:]]/).should be_nil + end + + it "doesn't match Unicode private-use characters with [[:word:]]" do + "\u{E001}".match(/[[:word:]]/).should be_nil + end + + it "matches unicode named character properties" do + "a1".match(/\p{Alpha}/).to_a.should == ["a"] + end + + it "matches unicode abbreviated character properties" do + "a1".match(/\p{L}/).to_a.should == ["a"] + end + + it "matches unicode script properties" do + "a\u06E9b".match(/\p{Arabic}/).to_a.should == ["\u06E9"] + end + + it "matches unicode Han properties" do + "松本行弘 Ruby".match(/\p{Han}+/u).to_a.should == ["松本行弘"] + end + + it "matches unicode Hiragana properties" do + "Ruby(ルビー)、まつもとゆきひろ".match(/\p{Hiragana}+/u).to_a.should == ["まつもとゆきひろ"] + end + + it "matches unicode Katakana properties" do + "Ruby(ルビー)、まつもとゆきひろ".match(/\p{Katakana}+/u).to_a.should == ["ルビ"] + end + + it "matches unicode Hangul properties" do + "루비(Ruby)".match(/\p{Hangul}+/u).to_a.should == ["루비"] + end +end diff --git a/spec/rubyspec/language/regexp/encoding_spec.rb b/spec/rubyspec/language/regexp/encoding_spec.rb new file mode 100644 index 0000000000..1f62244a28 --- /dev/null +++ b/spec/rubyspec/language/regexp/encoding_spec.rb @@ -0,0 +1,103 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexps with encoding modifiers" do + it "supports /e (EUC encoding)" do + match = /./e.match("\303\251".force_encoding(Encoding::EUC_JP)) + match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)] + end + + it "supports /e (EUC encoding) with interpolation" do + match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP)) + match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)] + end + + it "supports /e (EUC encoding) with interpolation /o" do + match = /#{/./}/e.match("\303\251".force_encoding(Encoding::EUC_JP)) + match.to_a.should == ["\303\251".force_encoding(Encoding::EUC_JP)] + end + + it 'uses EUC-JP as /e encoding' do + /./e.encoding.should == Encoding::EUC_JP + end + + it 'preserves EUC-JP as /e encoding through interpolation' do + /#{/./}/e.encoding.should == Encoding::EUC_JP + end + + it "supports /n (No encoding)" do + /./n.match("\303\251").to_a.should == ["\303"] + end + + it "supports /n (No encoding) with interpolation" do + /#{/./}/n.match("\303\251").to_a.should == ["\303"] + end + + it "supports /n (No encoding) with interpolation /o" do + /#{/./}/n.match("\303\251").to_a.should == ["\303"] + end + + it 'uses US-ASCII as /n encoding if all chars are 7-bit' do + /./n.encoding.should == Encoding::US_ASCII + end + + it 'uses ASCII-8BIT as /n encoding if not all chars are 7-bit' do + /\xFF/n.encoding.should == Encoding::ASCII_8BIT + end + + it 'preserves US-ASCII as /n encoding through interpolation if all chars are 7-bit' do + /.#{/./}/n.encoding.should == Encoding::US_ASCII + end + + it 'preserves ASCII-8BIT as /n encoding through interpolation if all chars are 7-bit' do + /\xFF#{/./}/n.encoding.should == Encoding::ASCII_8BIT + end + + it "supports /s (Windows_31J encoding)" do + match = /./s.match("\303\251".force_encoding(Encoding::Windows_31J)) + match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)] + end + + it "supports /s (Windows_31J encoding) with interpolation" do + match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J)) + match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)] + end + + it "supports /s (Windows_31J encoding) with interpolation and /o" do + match = /#{/./}/s.match("\303\251".force_encoding(Encoding::Windows_31J)) + match.to_a.should == ["\303".force_encoding(Encoding::Windows_31J)] + end + + it 'uses Windows-31J as /s encoding' do + /./s.encoding.should == Encoding::Windows_31J + end + + it 'preserves Windows-31J as /s encoding through interpolation' do + /#{/./}/s.encoding.should == Encoding::Windows_31J + end + + it "supports /u (UTF8 encoding)" do + /./u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"] + end + + it "supports /u (UTF8 encoding) with interpolation" do + /#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"] + end + + it "supports /u (UTF8 encoding) with interpolation and /o" do + /#{/./}/u.match("\303\251".force_encoding('utf-8')).to_a.should == ["\u{e9}"] + end + + it 'uses UTF-8 as /u encoding' do + /./u.encoding.should == Encoding::UTF_8 + end + + it 'preserves UTF-8 as /u encoding through interpolation' do + /#{/./}/u.encoding.should == Encoding::UTF_8 + end + + it "selects last of multiple encoding specifiers" do + /foo/ensuensuens.should == /foo/s + end +end diff --git a/spec/rubyspec/language/regexp/escapes_spec.rb b/spec/rubyspec/language/regexp/escapes_spec.rb new file mode 100644 index 0000000000..50ac22e51e --- /dev/null +++ b/spec/rubyspec/language/regexp/escapes_spec.rb @@ -0,0 +1,81 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexps with escape characters" do + it "they're supported" do + /\t/.match("\t").to_a.should == ["\t"] # horizontal tab + /\v/.match("\v").to_a.should == ["\v"] # vertical tab + /\n/.match("\n").to_a.should == ["\n"] # newline + /\r/.match("\r").to_a.should == ["\r"] # return + /\f/.match("\f").to_a.should == ["\f"] # form feed + /\a/.match("\a").to_a.should == ["\a"] # bell + /\e/.match("\e").to_a.should == ["\e"] # escape + + # \nnn octal char (encoded byte value) + end + + it "support quoting meta-characters via escape sequence" do + /\\/.match("\\").to_a.should == ["\\"] + /\//.match("/").to_a.should == ["/"] + # parenthesis, etc + /\(/.match("(").to_a.should == ["("] + /\)/.match(")").to_a.should == [")"] + /\[/.match("[").to_a.should == ["["] + /\]/.match("]").to_a.should == ["]"] + /\{/.match("{").to_a.should == ["{"] + /\}/.match("}").to_a.should == ["}"] + # alternation separator + /\|/.match("|").to_a.should == ["|"] + # quantifiers + /\?/.match("?").to_a.should == ["?"] + /\./.match(".").to_a.should == ["."] + /\*/.match("*").to_a.should == ["*"] + /\+/.match("+").to_a.should == ["+"] + # line anchors + /\^/.match("^").to_a.should == ["^"] + /\$/.match("$").to_a.should == ["$"] + end + + it "allows any character to be escaped" do + /\y/.match("y").to_a.should == ["y"] + end + + it "support \\x (hex characters)" do + /\xA/.match("\nxyz").to_a.should == ["\n"] + /\x0A/.match("\n").to_a.should == ["\n"] + /\xAA/.match("\nA").should be_nil + /\x0AA/.match("\nA").to_a.should == ["\nA"] + /\xAG/.match("\nG").to_a.should == ["\nG"] + # Non-matches + lambda { eval('/\xG/') }.should raise_error(SyntaxError) + + # \x{7HHHHHHH} wide hexadecimal char (character code point value) + end + + it "support \\c (control characters)" do + #/\c \c@\c`/.match("\00\00\00").to_a.should == ["\00\00\00"] + /\c#\cc\cC/.match("\03\03\03").to_a.should == ["\03\03\03"] + /\c'\cG\cg/.match("\a\a\a").to_a.should == ["\a\a\a"] + /\c(\cH\ch/.match("\b\b\b").to_a.should == ["\b\b\b"] + /\c)\cI\ci/.match("\t\t\t").to_a.should == ["\t\t\t"] + /\c*\cJ\cj/.match("\n\n\n").to_a.should == ["\n\n\n"] + /\c+\cK\ck/.match("\v\v\v").to_a.should == ["\v\v\v"] + /\c,\cL\cl/.match("\f\f\f").to_a.should == ["\f\f\f"] + /\c-\cM\cm/.match("\r\r\r").to_a.should == ["\r\r\r"] + + /\cJ/.match("\r").should be_nil + + # Parsing precedence + /\cJ+/.match("\n\n").to_a.should == ["\n\n"] # Quantifers apply to entire escape sequence + /\\cJ/.match("\\cJ").to_a.should == ["\\cJ"] + lambda { eval('/[abc\x]/') }.should raise_error(SyntaxError) # \x is treated as a escape sequence even inside a character class + # Syntax error + lambda { eval('/\c/') }.should raise_error(SyntaxError) + + # \cx control char (character code point value) + # \C-x control char (character code point value) + # \M-x meta (x|0x80) (character code point value) + # \M-\C-x meta control char (character code point value) + end +end diff --git a/spec/rubyspec/language/regexp/grouping_spec.rb b/spec/rubyspec/language/regexp/grouping_spec.rb new file mode 100644 index 0000000000..443cab7ee0 --- /dev/null +++ b/spec/rubyspec/language/regexp/grouping_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexps with grouping" do + it "support ()" do + /(a)/.match("a").to_a.should == ["a", "a"] + end + + it "allows groups to be nested" do + md = /(hay(st)a)ck/.match('haystack') + md.to_a.should == ['haystack','haysta', 'st'] + end + + it "raises a SyntaxError when parentheses aren't balanced" do + lambda { eval "/(hay(st)ack/" }.should raise_error(SyntaxError) + end + + it "supports (?: ) (non-capturing group)" do + /(?:foo)(bar)/.match("foobar").to_a.should == ["foobar", "bar"] + # Parsing precedence + /(?:xdigit:)/.match("xdigit:").to_a.should == ["xdigit:"] + end +end diff --git a/spec/rubyspec/language/regexp/interpolation_spec.rb b/spec/rubyspec/language/regexp/interpolation_spec.rb new file mode 100644 index 0000000000..5536c718f1 --- /dev/null +++ b/spec/rubyspec/language/regexp/interpolation_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexps with interpolation" do + + it "allows interpolation of strings" do + str = "foo|bar" + /#{str}/.should == /foo|bar/ + end + + it "allows interpolation of literal regexps" do + re = /foo|bar/ + /#{re}/.should == /(?-mix:foo|bar)/ + end + + it "allows interpolation of any object that responds to to_s" do + o = Object.new + def o.to_s + "object_with_to_s" + end + /#{o}/.should == /object_with_to_s/ + end + + it "allows interpolation which mixes modifiers" do + re = /foo/i + /#{re} bar/m.should == /(?i-mx:foo) bar/m + end + + it "allows interpolation to interact with other Regexp constructs" do + str = "foo)|(bar" + /(#{str})/.should == /(foo)|(bar)/ + + str = "a" + /[#{str}-z]/.should == /[a-z]/ + end + + it "gives precedence to escape sequences over substitution" do + str = "J" + /\c#{str}/.to_s.should == '(?-mix:\c#' + '{str})' + end + + it "throws RegexpError for malformed interpolation" do + s = "" + lambda { /(#{s}/ }.should raise_error(RegexpError) + s = "(" + lambda { /#{s}/ }.should raise_error(RegexpError) + end + + it "allows interpolation in extended mode" do + var = "#comment\n foo #comment\n | bar" + (/#{var}/x =~ "foo").should == (/foo|bar/ =~ "foo") + end + + it "allows escape sequences in interpolated regexps" do + escape_seq = %r{"\x80"}n + %r{#{escape_seq}}n.should == /(?-mix:"\x80")/n + end +end diff --git a/spec/rubyspec/language/regexp/modifiers_spec.rb b/spec/rubyspec/language/regexp/modifiers_spec.rb new file mode 100644 index 0000000000..03dec26f3f --- /dev/null +++ b/spec/rubyspec/language/regexp/modifiers_spec.rb @@ -0,0 +1,110 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexps with modifers" do + it "supports /i (case-insensitive)" do + /foo/i.match("FOO").to_a.should == ["FOO"] + end + + it "supports /m (multiline)" do + /foo.bar/m.match("foo\nbar").to_a.should == ["foo\nbar"] + /foo.bar/.match("foo\nbar").should be_nil + end + + it "supports /x (extended syntax)" do + /\d +/x.match("abc123").to_a.should == ["123"] # Quantifiers can be separated from the expression they apply to + end + + it "supports /o (once)" do + 2.times do |i| + /#{i}/o.should == /0/ + end + end + + it "invokes substitutions for /o only once" do + ScratchPad.record [] + o = Object.new + def o.to_s + ScratchPad << :to_s + "class_with_to_s" + end + eval "2.times { /#{o}/o }" + ScratchPad.recorded.should == [:to_s] + end + + it "supports modifier combinations" do + /foo/imox.match("foo").to_a.should == ["foo"] + /foo/imoximox.match("foo").to_a.should == ["foo"] + + lambda { eval('/foo/a') }.should raise_error(SyntaxError) + end + + it "supports (?imx-imx) (inline modifiers)" do + /(?i)foo/.match("FOO").to_a.should == ["FOO"] + /foo(?i)/.match("FOO").should be_nil + # Interaction with /i + /(?-i)foo/i.match("FOO").should be_nil + /foo(?-i)/i.match("FOO").to_a.should == ["FOO"] + # Multiple uses + /foo (?i)bar (?-i)baz/.match("foo BAR baz").to_a.should == ["foo BAR baz"] + /foo (?i)bar (?-i)baz/.match("foo BAR BAZ").should be_nil + + /(?m)./.match("\n").to_a.should == ["\n"] + /.(?m)/.match("\n").should be_nil + # Interaction with /m + /(?-m)./m.match("\n").should be_nil + /.(?-m)/m.match("\n").to_a.should == ["\n"] + # Multiple uses + /. (?m). (?-m)./.match(". \n .").to_a.should == [". \n ."] + /. (?m). (?-m)./.match(". \n \n").should be_nil + + /(?x) foo /.match("foo").to_a.should == ["foo"] + / foo (?x)/.match("foo").should be_nil + # Interaction with /x + /(?-x) foo /x.match("foo").should be_nil + / foo (?-x)/x.match("foo").to_a.should == ["foo"] + # Multiple uses + /( foo )(?x)( bar )(?-x)( baz )/.match(" foo bar baz ").to_a.should == [" foo bar baz ", " foo ", "bar", " baz "] + /( foo )(?x)( bar )(?-x)( baz )/.match(" foo barbaz").should be_nil + + # Parsing + /(?i-i)foo/.match("FOO").should be_nil + /(?ii)foo/.match("FOO").to_a.should == ["FOO"] + /(?-)foo/.match("foo").to_a.should == ["foo"] + lambda { eval('/(?o)/') }.should raise_error(SyntaxError) + end + + it "supports (?imx-imx:expr) (scoped inline modifiers)" do + /foo (?i:bar) baz/.match("foo BAR baz").to_a.should == ["foo BAR baz"] + /foo (?i:bar) baz/.match("foo BAR BAZ").should be_nil + /foo (?-i:bar) baz/i.match("foo BAR BAZ").should be_nil + + /. (?m:.) ./.match(". \n .").to_a.should == [". \n ."] + /. (?m:.) ./.match(". \n \n").should be_nil + /. (?-m:.) ./m.match("\n \n \n").should be_nil + + /( foo )(?x: bar )( baz )/.match(" foo bar baz ").to_a.should == [" foo bar baz ", " foo ", " baz "] + /( foo )(?x: bar )( baz )/.match(" foo barbaz").should be_nil + /( foo )(?-x: bar )( baz )/x.match("foo bar baz").to_a.should == ["foo bar baz", "foo", "baz"] + + # Parsing + /(?i-i:foo)/.match("FOO").should be_nil + /(?ii:foo)/.match("FOO").to_a.should == ["FOO"] + /(?-:)foo/.match("foo").to_a.should == ["foo"] + lambda { eval('/(?o:)/') }.should raise_error(SyntaxError) + end + + it "supports . with /m" do + # Basic matching + /./m.match("\n").to_a.should == ["\n"] + end + + it "supports ASII/Unicode modifiers" do + eval('/(?a)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a"] + eval('/(?d)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a\u3042"] + eval('/(?u)[[:alpha:]]+/').match("a\u3042").to_a.should == ["a\u3042"] + eval('/(?a)\w+/').match("a\u3042").to_a.should == ["a"] + eval('/(?d)\w+/').match("a\u3042").to_a.should == ["a"] + eval('/(?u)\w+/').match("a\u3042").to_a.should == ["a\u3042"] + end +end diff --git a/spec/rubyspec/language/regexp/repetition_spec.rb b/spec/rubyspec/language/regexp/repetition_spec.rb new file mode 100644 index 0000000000..2fc8a74a47 --- /dev/null +++ b/spec/rubyspec/language/regexp/repetition_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Regexps with repetition" do + it "supports * (0 or more of previous subexpression)" do + /a*/.match("aaa").to_a.should == ["aaa"] + /a*/.match("bbb").to_a.should == [""] + /<.*>/.match("foo").to_a.should == ["foo"] # it is greedy + end + + it "supports *? (0 or more of previous subexpression - lazy)" do + /a*?/.match("aaa").to_a.should == [""] + /<.*?>/.match("foo").to_a.should == [""] + end + + it "supports + (1 or more of previous subexpression)" do + /a+/.match("aaa").to_a.should == ["aaa"] + /a+/.match("bbb").should be_nil + /<.+>/.match("foo").to_a.should == ["foo"] # it is greedy + end + + it "supports +? (0 or more of previous subexpression - lazy)" do + /a+?/.match("aaa").to_a.should == ["a"] + /<.+?>/.match("foo").to_a.should == [""] + end + + it "supports {m,n} (m to n of previous subexpression)" do + /a{2,4}/.match("aaaaaa").to_a.should == ["aaaa"] + /<.{1,}>/.match("foo").to_a.should == ["foo"] # it is greedy + end + + it "supports {m,n}? (m to n of previous subexpression) - lazy)" do + /<.{1,}?>/.match("foo").to_a.should == [""] + /.([0-9]){3,5}?foo/.match("9876543210foo").to_a.should == ["543210foo", "0"] + end + + ruby_version_is ""..."2.4" do + it "does not treat {m,n}+ as possessive" do + @regexp = eval "/foo(A{0,1}+)Abar/" + @regexp.match("fooAAAbar").to_a.should == ["fooAAAbar", "AA"] + end + end + + ruby_version_is "2.4" do + it "does not treat {m,n}+ as possessive" do + -> { + @regexp = eval "/foo(A{0,1}+)Abar/" + }.should complain(/nested repeat operato/) + @regexp.match("fooAAAbar").to_a.should == ["fooAAAbar", "AA"] + end + end + + it "supports ? (0 or 1 of previous subexpression)" do + /a?/.match("aaa").to_a.should == ["a"] + /a?/.match("bbb").to_a.should == [""] + end +end diff --git a/spec/rubyspec/language/regexp_spec.rb b/spec/rubyspec/language/regexp_spec.rb new file mode 100644 index 0000000000..4a5828f2d1 --- /dev/null +++ b/spec/rubyspec/language/regexp_spec.rb @@ -0,0 +1,150 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Literal Regexps" do + it "matches against $_ (last input) in a conditional if no explicit matchee provided" do + -> { + eval <<-EOR + $_ = nil + (true if /foo/).should_not == true + + $_ = "foo" + (true if /foo/).should == true + EOR + }.should complain(/regex literal in condition/) + end + + it "yields a Regexp" do + /Hello/.should be_kind_of(Regexp) + end + + it "caches the Regexp object" do + rs = [] + 2.times do |i| + rs << /foo/ + end + rs[0].should equal(rs[1]) + end + + it "throws SyntaxError for malformed literals" do + lambda { eval('/(/') }.should raise_error(SyntaxError) + end + + ############################################################################# + # %r + ############################################################################# + + it "supports paired delimiters with %r" do + LanguageSpecs.paired_delimiters.each do |p0, p1| + eval("%r#{p0} foo #{p1}").should == / foo / + end + end + + it "supports grouping constructs that are also paired delimiters" do + LanguageSpecs.paired_delimiters.each do |p0, p1| + eval("%r#{p0} () [c]{1} #{p1}").should == / () [c]{1} / + end + end + + it "allows second part of paired delimiters to be used as non-paired delimiters" do + LanguageSpecs.paired_delimiters.each do |p0, p1| + eval("%r#{p1} foo #{p1}").should == / foo / + end + end + + it "disallows first part of paired delimiters to be used as non-paired delimiters" do + LanguageSpecs.paired_delimiters.each do |p0, p1| + lambda { eval("%r#{p0} foo #{p0}") }.should raise_error(SyntaxError) + end + end + + it "supports non-paired delimiters delimiters with %r" do + LanguageSpecs.non_paired_delimiters.each do |c| + eval("%r#{c} foo #{c}").should == / foo / + end + end + + it "disallows alphabets as non-paired delimiter with %r" do + lambda { eval('%ra foo a') }.should raise_error(SyntaxError) + end + + it "disallows spaces after %r and delimiter" do + lambda { eval('%r !foo!') }.should raise_error(SyntaxError) + end + + it "allows unescaped / to be used with %r" do + %r[/].to_s.should == /\//.to_s + end + + + ############################################################################# + # Specs for the matching semantics + ############################################################################# + + it "supports . (any character except line terminator)" do + # Basic matching + /./.match("foo").to_a.should == ["f"] + # Basic non-matching + /./.match("").should be_nil + /./.match("\n").should be_nil + /./.match("\0").to_a.should == ["\0"] + end + + + it "supports | (alternations)" do + /a|b/.match("a").to_a.should == ["a"] + end + + it "supports (?> ) (embedded subexpression)" do + /(?>foo)(?>bar)/.match("foobar").to_a.should == ["foobar"] + /(?>foo*)obar/.match("foooooooobar").should be_nil # it is possesive + end + + it "supports (?# )" do + /foo(?#comment)bar/.match("foobar").to_a.should == ["foobar"] + /foo(?#)bar/.match("foobar").to_a.should == ["foobar"] + end + + it "supports (?<= ) (positive lookbehind)" do + /foo.(?<=\d)/.match("fooA foo1").to_a.should == ["foo1"] + end + + it "supports (?foo.)bar\g/.match("foo1barfoo2").to_a.should == ["foo1barfoo2", "foo2"] + end + + it "supports character class composition" do + /[a-z&&[^a-c]]+/.match("abcdef").to_a.should == ["def"] + /[a-z&&[^d-i&&[^d-f]]]+/.match("abcdefghi").to_a.should == ["abcdef"] + end + + it "supports possessive quantifiers" do + /fooA++bar/.match("fooAAAbar").to_a.should == ["fooAAAbar"] + + /fooA++Abar/.match("fooAAAbar").should be_nil + /fooA?+Abar/.match("fooAAAbar").should be_nil + /fooA*+Abar/.match("fooAAAbar").should be_nil + end + + it "supports conditional regular expressions with positional capture groups" do + pattern = /\A(foo)?(?(1)(T)|(F))\z/ + + pattern.should =~ 'fooT' + pattern.should =~ 'F' + pattern.should_not =~ 'fooF' + pattern.should_not =~ 'T' + end + + it "supports conditional regular expressions with positional capture groups" do + pattern = /\A(?foo)?(?()(T)|(F))\z/ + + pattern.should =~ 'fooT' + pattern.should =~ 'F' + pattern.should_not =~ 'fooF' + pattern.should_not =~ 'T' + end +end diff --git a/spec/rubyspec/language/rescue_spec.rb b/spec/rubyspec/language/rescue_spec.rb new file mode 100644 index 0000000000..3d45eb1917 --- /dev/null +++ b/spec/rubyspec/language/rescue_spec.rb @@ -0,0 +1,293 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/rescue', __FILE__) + +class SpecificExampleException < StandardError +end +class OtherCustomException < StandardError +end +class ArbitraryException < StandardError +end + +exception_list = [SpecificExampleException, ArbitraryException] + +describe "The rescue keyword" do + before :each do + ScratchPad.record [] + end + + it "can be used to handle a specific exception" do + begin + raise SpecificExampleException, "Raising this to be handled below" + rescue SpecificExampleException + :caught + end.should == :caught + end + + it "can capture the raised exception in a local variable" do + begin + raise SpecificExampleException, "some text" + rescue SpecificExampleException => e + e.message.should == "some text" + end + end + + it "can rescue multiple raised exceptions with a single rescue block" do + [lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].map do |block| + begin + block.call + rescue SpecificExampleException, ArbitraryException + :caught + end + end.should == [:caught, :caught] + end + + it "can rescue a splatted list of exceptions" do + caught_it = false + begin + raise SpecificExampleException, "not important" + rescue *exception_list + caught_it = true + end + caught_it.should be_true + caught = [] + [lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].each do |block| + begin + block.call + rescue *exception_list + caught << $! + end + end + caught.size.should == 2 + exception_list.each do |exception_class| + caught.map{|e| e.class}.should include(exception_class) + end + end + + it "can combine a splatted list of exceptions with a literal list of exceptions" do + caught_it = false + begin + raise SpecificExampleException, "not important" + rescue ArbitraryException, *exception_list + caught_it = true + end + caught_it.should be_true + caught = [] + [lambda{raise ArbitraryException}, lambda{raise SpecificExampleException}].each do |block| + begin + block.call + rescue ArbitraryException, *exception_list + caught << $! + end + end + caught.size.should == 2 + exception_list.each do |exception_class| + caught.map{|e| e.class}.should include(exception_class) + end + end + + it "will only rescue the specified exceptions when doing a splat rescue" do + lambda do + begin + raise OtherCustomException, "not rescued!" + rescue *exception_list + end + end.should raise_error(OtherCustomException) + end + + it "will execute an else block only if no exceptions were raised" do + result = begin + ScratchPad << :one + rescue + ScratchPad << :does_not_run + else + ScratchPad << :two + :val + end + result.should == :val + ScratchPad.recorded.should == [:one, :two] + end + + it "will execute an else block with ensure only if no exceptions were raised" do + result = begin + ScratchPad << :one + rescue + ScratchPad << :does_not_run + else + ScratchPad << :two + :val + ensure + ScratchPad << :ensure + :ensure_val + end + result.should == :val + ScratchPad.recorded.should == [:one, :two, :ensure] + end + + it "will execute an else block only if no exceptions were raised in a method" do + result = RescueSpecs.begin_else(false) + result.should == :val + ScratchPad.recorded.should == [:one, :else_ran] + end + + it "will execute an else block with ensure only if no exceptions were raised in a method" do + result = RescueSpecs.begin_else_ensure(false) + result.should == :val + ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran] + end + + it "will execute an else block but use the outer scope return value in a method" do + result = RescueSpecs.begin_else_return(false) + result.should == :return_val + ScratchPad.recorded.should == [:one, :else_ran, :outside_begin] + end + + it "will execute an else block with ensure but use the outer scope return value in a method" do + result = RescueSpecs.begin_else_return_ensure(false) + result.should == :return_val + ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran, :outside_begin] + end + + it "will not execute an else block if an exception was raised" do + result = begin + ScratchPad << :one + raise "an error occurred" + rescue + ScratchPad << :two + :val + else + ScratchPad << :does_not_run + end + result.should == :val + ScratchPad.recorded.should == [:one, :two] + end + + it "will not execute an else block with ensure if an exception was raised" do + result = begin + ScratchPad << :one + raise "an error occurred" + rescue + ScratchPad << :two + :val + else + ScratchPad << :does_not_run + ensure + ScratchPad << :ensure + :ensure_val + end + result.should == :val + ScratchPad.recorded.should == [:one, :two, :ensure] + end + + it "will not execute an else block if an exception was raised in a method" do + result = RescueSpecs.begin_else(true) + result.should == :rescue_val + ScratchPad.recorded.should == [:one, :rescue_ran] + end + + it "will not execute an else block with ensure if an exception was raised in a method" do + result = RescueSpecs.begin_else_ensure(true) + result.should == :rescue_val + ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran] + end + + it "will not execute an else block but use the outer scope return value in a method" do + result = RescueSpecs.begin_else_return(true) + result.should == :return_val + ScratchPad.recorded.should == [:one, :rescue_ran, :outside_begin] + end + + it "will not execute an else block with ensure but use the outer scope return value in a method" do + result = RescueSpecs.begin_else_return_ensure(true) + result.should == :return_val + ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran, :outside_begin] + end + + it "will not rescue errors raised in an else block in the rescue block above it" do + lambda do + begin + ScratchPad << :one + rescue Exception + ScratchPad << :does_not_run + else + ScratchPad << :two + raise SpecificExampleException, "an error from else" + end + end.should raise_error(SpecificExampleException) + ScratchPad.recorded.should == [:one, :two] + end + + it "parses 'a += b rescue c' as 'a += (b rescue c)'" do + a = 'a' + c = 'c' + a += b rescue c + a.should == 'ac' + end + + it "without classes will not rescue Exception" do + lambda do + begin + raise Exception + rescue + 'Exception wrongly rescued' + end + end.should raise_error(Exception) + end + + it "uses === to compare against rescued classes" do + rescuer = Class.new + + def rescuer.===(exception) + true + end + + begin + raise Exception + rescue rescuer + rescued = :success + rescue Exception + rescued = :failure + end + + rescued.should == :success + end + + it "only accepts Module or Class in rescue clauses" do + rescuer = 42 + lambda { + begin + raise "error" + rescue rescuer + end + }.should raise_error(TypeError) { |e| + e.message.should =~ /class or module required for rescue clause/ + } + end + + it "only accepts Module or Class in splatted rescue clauses" do + rescuer = [42] + lambda { + begin + raise "error" + rescue *rescuer + end + }.should raise_error(TypeError) { |e| + e.message.should =~ /class or module required for rescue clause/ + } + end + + it "evaluates rescue expressions only when needed" do + invalid_rescuer = Object.new + begin + :foo + rescue rescuer + end.should == :foo + end + + it "should splat the handling Error classes" do + begin + raise "raise" + rescue *(RuntimeError) => e + :expected + end.should == :expected + end +end diff --git a/spec/rubyspec/language/retry_spec.rb b/spec/rubyspec/language/retry_spec.rb new file mode 100644 index 0000000000..96e69b763a --- /dev/null +++ b/spec/rubyspec/language/retry_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The retry statement" do + it "re-executes the closest block" do + retry_first = true + retry_second = true + results = [] + begin + results << 1 + raise + rescue + results << 2 + if retry_first + results << 3 + retry_first = false + retry + end + begin + results << 4 + raise + rescue + results << 5 + if retry_second + results << 6 + retry_second = false + retry + end + end + end + + results.should == [1, 2, 3, 1, 2, 4, 5, 6, 4, 5] + end + + it "raises a SyntaxError when used outside of a begin statement" do + lambda { eval 'retry' }.should raise_error(SyntaxError) + end +end + +describe "The retry keyword inside a begin block's rescue block" do + it "causes the begin block to be executed again" do + counter = 0 + + begin + counter += 1 + raise "An exception" + rescue + retry unless counter == 7 + end + + counter.should == 7 + end +end diff --git a/spec/rubyspec/language/return_spec.rb b/spec/rubyspec/language/return_spec.rb new file mode 100644 index 0000000000..323f74aad3 --- /dev/null +++ b/spec/rubyspec/language/return_spec.rb @@ -0,0 +1,245 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/return', __FILE__) + +describe "The return keyword" do + it "returns any object directly" do + def r; return 1; end + r().should == 1 + end + + it "returns an single element array directly" do + def r; return [1]; end + r().should == [1] + end + + it "returns an multi element array directly" do + def r; return [1,2]; end + r().should == [1,2] + end + + it "returns nil by default" do + def r; return; end + r().should be_nil + end + + describe "in a Thread" do + it "raises a LocalJumpError if used to exit a thread" do + lambda { Thread.new { return }.join }.should raise_error(LocalJumpError) + end + end + + describe "when passed a splat" do + it "returns [] when the ary is empty" do + def r; ary = []; return *ary; end + r.should == [] + end + + it "returns the array when the array is size of 1" do + def r; ary = [1]; return *ary; end + r.should == [1] + end + + it "returns the whole array when size is greater than 1" do + def r; ary = [1,2]; return *ary; end + r.should == [1,2] + + def r; ary = [1,2,3]; return *ary; end + r.should == [1,2,3] + end + + it "returns an array when used as a splat" do + def r; value = 1; return *value; end + r.should == [1] + end + + it "calls 'to_a' on the splatted value first" do + def r + obj = Object.new + def obj.to_a + [1,2] + end + + return *obj + end + + r().should == [1,2] + end + end + + describe "within a begin" do + before :each do + ScratchPad.record [] + end + + it "executes ensure before returning" do + def f() + begin + ScratchPad << :begin + return :begin + ScratchPad << :after_begin + ensure + ScratchPad << :ensure + end + ScratchPad << :function + end + f().should == :begin + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "returns last value returned in ensure" do + def f() + begin + ScratchPad << :begin + return :begin + ScratchPad << :after_begin + ensure + ScratchPad << :ensure + return :ensure + ScratchPad << :after_ensure + end + ScratchPad << :function + end + f().should == :ensure + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "executes nested ensures before returning" do + def f() + begin + begin + ScratchPad << :inner_begin + return :inner_begin + ScratchPad << :after_inner_begin + ensure + ScratchPad << :inner_ensure + end + ScratchPad << :outer_begin + return :outer_begin + ScratchPad << :after_outer_begin + ensure + ScratchPad << :outer_ensure + end + ScratchPad << :function + end + f().should == :inner_begin + ScratchPad.recorded.should == [:inner_begin, :inner_ensure, :outer_ensure] + end + + it "returns last value returned in nested ensures" do + def f() + begin + begin + ScratchPad << :inner_begin + return :inner_begin + ScratchPad << :after_inner_begin + ensure + ScratchPad << :inner_ensure + return :inner_ensure + ScratchPad << :after_inner_ensure + end + ScratchPad << :outer_begin + return :outer_begin + ScratchPad << :after_outer_begin + ensure + ScratchPad << :outer_ensure + return :outer_ensure + ScratchPad << :after_outer_ensure + end + ScratchPad << :function + end + f().should == :outer_ensure + ScratchPad.recorded.should == [:inner_begin, :inner_ensure, :outer_ensure] + end + + it "executes the ensure clause when begin/ensure are inside a lambda" do + lambda do + begin + return + ensure + ScratchPad.recorded << :ensure + end + end.call + ScratchPad.recorded.should == [:ensure] + end + end + + describe "within a block" do + before :each do + ScratchPad.clear + end + + it "causes lambda to return nil if invoked without any arguments" do + lambda { return; 456 }.call.should be_nil + end + + it "causes lambda to return nil if invoked with an empty expression" do + lambda { return (); 456 }.call.should be_nil + end + + it "causes lambda to return the value passed to return" do + lambda { return 123; 456 }.call.should == 123 + end + + it "causes the method that lexically encloses the block to return" do + ReturnSpecs::Blocks.new.enclosing_method.should == :return_value + ScratchPad.recorded.should == :before_return + end + + it "returns from the lexically enclosing method even in case of chained calls" do + ReturnSpecs::NestedCalls.new.enclosing_method.should == :return_value + ScratchPad.recorded.should == :before_return + end + + it "returns from the lexically enclosing method even in case of chained calls(in yield)" do + ReturnSpecs::NestedBlocks.new.enclosing_method.should == :return_value + ScratchPad.recorded.should == :before_return + end + + it "causes the method to return even when the immediate parent has already returned" do + ReturnSpecs::SavedInnerBlock.new.start.should == :return_value + ScratchPad.recorded.should == :before_return + end + + # jruby/jruby#3143 + describe "downstream from a lambda" do + it "returns to its own return-capturing lexical enclosure" do + def a + ->{ yield }.call + return 2 + end + def b + a { return 1 } + end + + b.should == 1 + end + end + + end + + describe "within two blocks" do + it "causes the method that lexically encloses the block to return" do + def f + 1.times { 1.times {return true}; false}; false + end + f.should be_true + end + end + + describe "within define_method" do + it "goes through the method via a closure" do + ReturnSpecs::ThroughDefineMethod.new.outer.should == :good + end + + it "stops at the method when the return is used directly" do + ReturnSpecs::DefineMethod.new.outer.should == :good + end + end + + describe "invoked with a method call without parentheses with a block" do + it "returns the value returned from the method call" do + ReturnSpecs::MethodWithBlock.new.method1.should == 5 + ReturnSpecs::MethodWithBlock.new.method2.should == [0, 1, 2] + end + end +end diff --git a/spec/rubyspec/language/safe_navigator_spec.rb b/spec/rubyspec/language/safe_navigator_spec.rb new file mode 100644 index 0000000000..a8b29dc5a3 --- /dev/null +++ b/spec/rubyspec/language/safe_navigator_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path("../../spec_helper", __FILE__) + +ruby_version_is "2.3" do + describe "Safe navigator" do + it "requires a method name to be provided" do + lambda { eval("obj&. {}") }.should raise_error(SyntaxError) + end + + context "when context is nil" do + it "always returns nil" do + eval("nil&.unknown").should == nil + eval("[][10]&.unknown").should == nil + end + + it "can be chained" do + eval("nil&.one&.two&.three").should == nil + end + + it "doesn't evaluate arguments" do + obj = Object.new + obj.should_not_receive(:m) + eval("nil&.unknown(obj.m) { obj.m }") + end + end + + context "when context is false" do + it "calls the method" do + eval("false&.to_s").should == "false" + + lambda { eval("false&.unknown") }.should raise_error(NoMethodError) + end + end + + context "when context is truthy" do + it "calls the method" do + eval("1&.to_s").should == "1" + + lambda { eval("1&.unknown") }.should raise_error(NoMethodError) + end + end + + it "takes a list of arguments" do + eval("[1,2,3]&.first(2)").should == [1,2] + end + + it "takes a block" do + eval("[1,2]&.map { |i| i * 2 }").should == [2, 4] + end + + it "allows assignment methods" do + klass = Class.new do + attr_reader :foo + def foo=(val) + @foo = val + 42 + end + end + obj = klass.new + + eval("obj&.foo = 3").should == 3 + obj.foo.should == 3 + + obj = nil + eval("obj&.foo = 3").should == nil + end + + it "allows assignment operators" do + klass = Class.new do + attr_accessor :m + + def initialize + @m = 0 + end + end + + obj = klass.new + + eval("obj&.m += 3") + obj.m.should == 3 + + obj = nil + eval("obj&.m += 3").should == nil + end + + it "does not call the operator method lazily with an assignment operator" do + klass = Class.new do + attr_writer :foo + def foo + nil + end + end + obj = klass.new + + lambda { + eval("obj&.foo += 3") + }.should raise_error(NoMethodError) { |e| + e.name.should == :+ + } + end + end +end diff --git a/spec/rubyspec/language/send_spec.rb b/spec/rubyspec/language/send_spec.rb new file mode 100644 index 0000000000..646a700785 --- /dev/null +++ b/spec/rubyspec/language/send_spec.rb @@ -0,0 +1,521 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/send', __FILE__) + +# Why so many fixed arg tests? JRuby and I assume other Ruby impls have +# separate call paths for simple fixed arity methods. Testing up to five +# will verify special and generic arity code paths for all impls. +# +# Method naming conventions: +# M - Manditory Args +# O - Optional Arg +# R - Rest Arg +# Q - Post Manditory Args + +specs = LangSendSpecs + +describe "Invoking a method" do + describe "with zero arguments" do + it "requires no arguments passed" do + specs.fooM0.should == 100 + end + + it "raises ArgumentError if the method has a positive arity" do + lambda { + specs.fooM1 + }.should raise_error(ArgumentError) + end + end + + describe "with only mandatory arguments" do + it "requires exactly the same number of passed values" do + specs.fooM1(1).should == [1] + specs.fooM2(1,2).should == [1,2] + specs.fooM3(1,2,3).should == [1,2,3] + specs.fooM4(1,2,3,4).should == [1,2,3,4] + specs.fooM5(1,2,3,4,5).should == [1,2,3,4,5] + end + + it "raises ArgumentError if the methods arity doesn't match" do + lambda { + specs.fooM1(1,2) + }.should raise_error(ArgumentError) + end + end + + describe "with optional arguments" do + it "uses the optional argument if none is is passed" do + specs.fooM0O1.should == [1] + end + + it "uses the passed argument if available" do + specs.fooM0O1(2).should == [2] + end + + it "raises ArgumentError if extra arguments are passed" do + lambda { + specs.fooM0O1(2,3) + }.should raise_error(ArgumentError) + end + end + + describe "with mandatory and optional arguments" do + it "uses the passed values in left to right order" do + specs.fooM1O1(2).should == [2,1] + end + + it "raises an ArgumentError if there are no values for the mandatory args" do + lambda { + specs.fooM1O1 + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if too many values are passed" do + lambda { + specs.fooM1O1(1,2,3) + }.should raise_error(ArgumentError) + end + end + + describe "with a rest argument" do + it "is an empty array if there are no additional arguments" do + specs.fooM0R().should == [] + specs.fooM1R(1).should == [1, []] + end + + it "gathers unused arguments" do + specs.fooM0R(1).should == [1] + specs.fooM1R(1,2).should == [1, [2]] + end + end + + it "with a block makes it available to yield" do + specs.oneb(10) { 200 }.should == [10,200] + end + + it "with a block converts the block to a Proc" do + prc = specs.makeproc { "hello" } + prc.should be_kind_of(Proc) + prc.call.should == "hello" + end + + it "with an object as a block uses 'to_proc' for coercion" do + o = LangSendSpecs::ToProc.new(:from_to_proc) + + specs.makeproc(&o).call.should == :from_to_proc + + specs.yield_now(&o).should == :from_to_proc + end + + it "raises a SyntaxError with both a literal block and an object as block" do + lambda { + eval "specs.oneb(10, &l){ 42 }" + }.should raise_error(SyntaxError) + end + + it "with same names as existing variables is ok" do + foobar = 100 + + def foobar; 200; end + + foobar.should == 100 + foobar().should == 200 + end + + it "with splat operator makes the object the direct arguments" do + a = [1,2,3] + specs.fooM3(*a).should == [1,2,3] + end + + it "without parentheses works" do + (specs.fooM3 1,2,3).should == [1,2,3] + end + + it "with a space separating method name and parenthesis treats expression in parenthesis as first argument" do + specs.weird_parens().should == "55" + end + + describe "allows []=" do + before :each do + @obj = LangSendSpecs::AttrSet.new + end + + it "with *args in the [] expanded to individual arguments" do + ary = [2,3] + (@obj[1, *ary] = 4).should == 4 + @obj.result.should == [1,2,3,4] + end + + it "with multiple *args" do + ary = [2,3] + post = [4,5] + (@obj[1, *ary] = *post).should == [4,5] + @obj.result.should == [1,2,3,[4,5]] + end + + it "with multiple *args and does not unwrap the last splat" do + ary = [2,3] + post = [4] + (@obj[1, *ary] = *post).should == [4] + @obj.result.should == [1,2,3,[4]] + end + + it "with a *args and multiple rhs args" do + ary = [2,3] + (@obj[1, *ary] = 4, 5).should == [4,5] + @obj.result.should == [1,2,3,[4,5]] + end + end + + it "passes literal hashes without curly braces as the last parameter" do + specs.fooM3('abc', 456, 'rbx' => 'cool', + 'specs' => 'fail sometimes', 'oh' => 'weh').should == \ + ['abc', 456, {'rbx' => 'cool', 'specs' => 'fail sometimes', 'oh' => 'weh'}] + end + + it "passes a literal hash without curly braces or parens" do + (specs.fooM3 'abc', 456, 'rbx' => 'cool', + 'specs' => 'fail sometimes', 'oh' => 'weh').should == \ + ['abc', 456, { 'rbx' => 'cool', 'specs' => 'fail sometimes', 'oh' => 'weh'}] + end + + it "allows to literal hashes without curly braces as the only parameter" do + specs.fooM1(rbx: :cool, specs: :fail_sometimes).should == + [{ rbx: :cool, specs: :fail_sometimes }] + + (specs.fooM1 rbx: :cool, specs: :fail_sometimes).should == + [{ rbx: :cool, specs: :fail_sometimes }] + end + + describe "when the method is not available" do + it "invokes method_missing if it is defined" do + o = LangSendSpecs::MethodMissing.new + o.not_there(1,2) + o.message.should == :not_there + o.args.should == [1,2] + end + + it "raises NameError if invoked as a vcall" do + lambda { no_such_method }.should raise_error NameError + end + + it "raises NoMethodError if invoked as an unambiguous method call" do + lambda { no_such_method() }.should raise_error NoMethodError + lambda { no_such_method(1,2,3) }.should raise_error NoMethodError + end + end + +end + +describe "Invoking a public setter method" do + it 'returns the set value' do + klass = Class.new do + def foobar=(*) + 1 + end + end + + (klass.new.foobar = 'bar').should == 'bar' + (klass.new.foobar = 'bar', 'baz').should == ["bar", "baz"] + end +end + +describe "Invoking []= methods" do + it 'returns the set value' do + klass = Class.new do + def []=(*) + 1 + end + end + + (klass.new[33] = 'bar').should == 'bar' + (klass.new[33] = 'bar', 'baz').should == ['bar', 'baz'] + (klass.new[33, 34] = 'bar', 'baz').should == ['bar', 'baz'] + end +end + +describe "Invoking a private setter method" do + describe "permits self as a receiver" do + it "for normal assignment" do + receiver = LangSendSpecs::PrivateSetter.new + receiver.call_self_foo_equals(42) + receiver.foo.should == 42 + end + + it "for multiple assignment" do + receiver = LangSendSpecs::PrivateSetter.new + receiver.call_self_foo_equals_masgn(42) + receiver.foo.should == 42 + end + end +end + +describe "Invoking a private getter method" do + it "does not permit self as a receiver" do + receiver = LangSendSpecs::PrivateGetter.new + lambda { receiver.call_self_foo }.should raise_error(NoMethodError) + lambda { receiver.call_self_foo_or_equals(6) }.should raise_error(NoMethodError) + end +end + +describe "Invoking a method" do + describe "with required args after the rest arguments" do + it "binds the required arguments first" do + specs.fooM0RQ1(1).should == [[], 1] + specs.fooM0RQ1(1,2).should == [[1], 2] + specs.fooM0RQ1(1,2,3).should == [[1,2], 3] + + specs.fooM1RQ1(1,2).should == [1, [], 2] + specs.fooM1RQ1(1,2,3).should == [1, [2], 3] + specs.fooM1RQ1(1,2,3,4).should == [1, [2, 3], 4] + + specs.fooM1O1RQ1(1,2).should == [1, 9, [], 2] + specs.fooM1O1RQ1(1,2,3).should == [1, 2, [], 3] + specs.fooM1O1RQ1(1,2,3,4).should == [1, 2, [3], 4] + + specs.fooM1O1RQ2(1,2,3).should == [1, 9, [], 2, 3] + specs.fooM1O1RQ2(1,2,3,4).should == [1, 2, [], 3, 4] + specs.fooM1O1RQ2(1,2,3,4,5).should == [1, 2, [3], 4, 5] + end + end + + describe "with mandatory arguments after optional arguments" do + it "binds the required arguments first" do + specs.fooO1Q1(0,1).should == [0,1] + specs.fooO1Q1(2).should == [1,2] + + specs.fooM1O1Q1(2,3,4).should == [2,3,4] + specs.fooM1O1Q1(1,3).should == [1,2,3] + + specs.fooM2O1Q1(1,2,4).should == [1,2,3,4] + + specs.fooM2O2Q1(1,2,3,4,5).should == [1,2,3,4,5] + specs.fooM2O2Q1(1,2,3,5).should == [1,2,3,4,5] + specs.fooM2O2Q1(1,2,5).should == [1,2,3,4,5] + + specs.fooO4Q1(1,2,3,4,5).should == [1,2,3,4,5] + specs.fooO4Q1(1,2,3,5).should == [1,2,3,4,5] + specs.fooO4Q1(1,2,5).should == [1,2,3,4,5] + specs.fooO4Q1(1,5).should == [1,2,3,4,5] + specs.fooO4Q1(5).should == [1,2,3,4,5] + + specs.fooO4Q2(1,2,3,4,5,6).should == [1,2,3,4,5,6] + specs.fooO4Q2(1,2,3,5,6).should == [1,2,3,4,5,6] + specs.fooO4Q2(1,2,5,6).should == [1,2,3,4,5,6] + specs.fooO4Q2(1,5,6).should == [1,2,3,4,5,6] + specs.fooO4Q2(5,6).should == [1,2,3,4,5,6] + end + end + + it "with .() invokes #call" do + q = proc { |z| z } + q.(1).should == 1 + + obj = mock("paren call") + obj.should_receive(:call).and_return(:called) + obj.().should == :called + end + + it "allows a vestigial trailing ',' in the arguments" do + specs.fooM1(1,).should == [1] + end + + it "with splat operator attempts to coerce it to an Array if the object respond_to?(:to_a)" do + ary = [2,3,4] + obj = mock("to_a") + obj.should_receive(:to_a).and_return(ary).twice + specs.fooM0R(*obj).should == ary + specs.fooM1R(1,*obj).should == [1, ary] + end + + it "with splat operator * and non-Array value uses value unchanged if it does not respond_to?(:to_ary)" do + obj = Object.new + obj.should_not respond_to(:to_a) + + specs.fooM0R(*obj).should == [obj] + specs.fooM1R(1,*obj).should == [1, [obj]] + end + + it "accepts additional arguments after splat expansion" do + a = [1,2] + specs.fooM4(*a,3,4).should == [1,2,3,4] + specs.fooM4(0,*a,3).should == [0,1,2,3] + end + + it "does not expand final array arguments after a splat expansion" do + a = [1, 2] + specs.fooM3(*a, [3, 4]).should == [1, 2, [3, 4]] + end + + it "accepts final explicit literal Hash arguments after the splat" do + a = [1, 2] + specs.fooM0RQ1(*a, { a: 1 }).should == [[1, 2], { a: 1 }] + end + + it "accepts final implicit literal Hash arguments after the splat" do + a = [1, 2] + specs.fooM0RQ1(*a, a: 1).should == [[1, 2], { a: 1 }] + end + + it "accepts final Hash arguments after the splat" do + a = [1, 2] + b = { a: 1 } + specs.fooM0RQ1(*a, b).should == [[1, 2], { a: 1 }] + end + + it "accepts mandatory and explicit literal Hash arguments after the splat" do + a = [1, 2] + specs.fooM0RQ2(*a, 3, { a: 1 }).should == [[1, 2], 3, { a: 1 }] + end + + it "accepts mandatory and implicit literal Hash arguments after the splat" do + a = [1, 2] + specs.fooM0RQ2(*a, 3, a: 1).should == [[1, 2], 3, { a: 1 }] + end + + it "accepts mandatory and Hash arguments after the splat" do + a = [1, 2] + b = { a: 1 } + specs.fooM0RQ2(*a, 3, b).should == [[1, 2], 3, { a: 1 }] + end + + it "converts a final splatted explicit Hash to an Array" do + a = [1, 2] + specs.fooR(*a, 3, *{ a: 1 }).should == [1, 2, 3, [:a, 1]] + end + + it "calls #to_a to convert a final splatted Hash object to an Array" do + a = [1, 2] + b = { a: 1 } + b.should_receive(:to_a).and_return([:a, 1]) + + specs.fooR(*a, 3, *b).should == [1, 2, 3, :a, 1] + end + + it "accepts multiple splat expansions in the same argument list" do + a = [1,2,3] + b = 7 + c = mock("pseudo-array") + c.should_receive(:to_a).and_return([0,0]) + + d = [4,5] + specs.rest_len(*a,*d,6,*b).should == 7 + specs.rest_len(*a,*a,*a).should == 9 + specs.rest_len(0,*a,4,*5,6,7,*c,-1).should == 11 + end + + it "expands an array to arguments grouped in parentheses" do + specs.destructure2([40,2]).should == 42 + end + + it "expands an array to arguments grouped in parentheses and ignores any rest arguments in the array" do + specs.destructure2([40,2,84]).should == 42 + end + + it "expands an array to arguments grouped in parentheses and sets not specified arguments to nil" do + specs.destructure2b([42]).should == [42, nil] + end + + it "expands an array to arguments grouped in parentheses which in turn takes rest arguments" do + specs.destructure4r([1, 2, 3]).should == [1, 2, [], 3, nil] + specs.destructure4r([1, 2, 3, 4]).should == [1, 2, [], 3, 4] + specs.destructure4r([1, 2, 3, 4, 5]).should == [1, 2, [3], 4, 5] + end + + it "with optional argument(s), expands an array to arguments grouped in parentheses" do + specs.destructure4o(1, [2, 3]).should == [1, 1, nil, [2, 3]] + specs.destructure4o(1, [], 2).should == [1, nil, nil, 2] + specs.destructure4os(1, [2, 3]).should == [1, 2, [3]] + specs.destructure5o(1, [2, 3]).should == [1, 2, 1, nil, [2, 3]] + specs.destructure7o(1, [2, 3]).should == [1, 2, 1, nil, 2, 3] + specs.destructure7b(1, [2, 3]) do |(a,*b,c)| + [a, c] + end.should == [1, 3] + end + + describe "new-style hash arguments" do + describe "as the only parameter" do + it "passes without curly braces" do + specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "passes without curly braces or parens" do + (specs.fooM1 rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "handles a hanging comma without curly braces" do + specs.fooM1(abc: 123,).should == [{abc: 123}] + specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should == + [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + end + + describe "as the last parameter" do + it "passes without curly braces" do + specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "passes without curly braces or parens" do + (specs.fooM3 'abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "handles a hanging comma without curly braces" do + specs.fooM3('abc', 123, abc: 123,).should == ['abc', 123, {abc: 123}] + specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should == + ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + end + end + + describe "mixed new- and old-style hash arguments" do + describe "as the only parameter" do + it "passes without curly braces" do + specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "passes without curly braces or parens" do + (specs.fooM1 rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "handles a hanging comma without curly braces" do + specs.fooM1(rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should == + [{ rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + end + + describe "as the last parameter" do + it "passes without curly braces" do + specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "passes without curly braces or parens" do + (specs.fooM3 'abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234).should == + ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + + it "handles a hanging comma without curly braces" do + specs.fooM3('abc', 123, rbx: 'cool', specs: :fail_sometimes, non_sym: 1234,).should == + ['abc', 123, { rbx: 'cool', specs: :fail_sometimes, non_sym: 1234 }] + end + end + end + +end + +describe "allows []= with arguments after splat" do + before :each do + @obj = LangSendSpecs::Attr19Set.new + @ary = ["a"] + end + + it "with *args in the [] and post args" do + @obj[1,*@ary,123] = 2 + @obj.result.should == [1, "a", 123, 2] + end +end diff --git a/spec/rubyspec/language/shared/__FILE__.rb b/spec/rubyspec/language/shared/__FILE__.rb new file mode 100644 index 0000000000..3e4f5c958d --- /dev/null +++ b/spec/rubyspec/language/shared/__FILE__.rb @@ -0,0 +1,23 @@ +describe :language___FILE__, shared: true do + before :each do + CodeLoadingSpecs.spec_setup + @path = File.join(CODE_LOADING_DIR, "file_fixture.rb") + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "equals the absolute path of a file loaded by an absolute path" do + @object.send(@method, @path).should be_true + ScratchPad.recorded.should == [@path] + end + + it "equals the absolute path of a file loaded by a relative path" do + $LOAD_PATH << "." + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "file_fixture.rb").should be_true + end + ScratchPad.recorded.should == [@path] + end +end diff --git a/spec/rubyspec/language/shared/__LINE__.rb b/spec/rubyspec/language/shared/__LINE__.rb new file mode 100644 index 0000000000..076b74b3ba --- /dev/null +++ b/spec/rubyspec/language/shared/__LINE__.rb @@ -0,0 +1,15 @@ +describe :language___LINE__, shared: true do + before :each do + CodeLoadingSpecs.spec_setup + @path = File.expand_path("line_fixture.rb", CODE_LOADING_DIR) + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "equals the line number of the text in a loaded file" do + @object.send(@method, @path).should be_true + ScratchPad.recorded.should == [1, 5] + end +end diff --git a/spec/rubyspec/language/singleton_class_spec.rb b/spec/rubyspec/language/singleton_class_spec.rb new file mode 100644 index 0000000000..837f479440 --- /dev/null +++ b/spec/rubyspec/language/singleton_class_spec.rb @@ -0,0 +1,293 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/class', __FILE__) + +describe "A singleton class" do + it "is TrueClass for true" do + true.singleton_class.should == TrueClass + end + + it "is FalseClass for false" do + false.singleton_class.should == FalseClass + end + + it "is NilClass for nil" do + nil.singleton_class.should == NilClass + end + + it "raises a TypeError for Fixnum's" do + lambda { 1.singleton_class }.should raise_error(TypeError) + end + + it "raises a TypeError for symbols" do + lambda { :symbol.singleton_class }.should raise_error(TypeError) + end + + it "is a singleton Class instance" do + o = mock('x') + o.singleton_class.should be_kind_of(Class) + o.singleton_class.should_not equal(Object) + o.should be_kind_of(o.singleton_class) + end + + it "is a Class for classes" do + ClassSpecs::A.singleton_class.should be_kind_of(Class) + end + + it "inherits from Class for classes" do + Class.should be_ancestor_of(Object.singleton_class) + end + + it "is a subclass of Class's singleton class" do + ec = ClassSpecs::A.singleton_class + ec.should be_kind_of(Class.singleton_class) + end + + it "is a subclass of the same level of Class's singleton class" do + ecec = ClassSpecs::A.singleton_class.singleton_class + class_ec = Class.singleton_class + + ecec.should be_kind_of(class_ec.singleton_class) + ecec.should be_kind_of(class_ec) + end + + it "is a subclass of a superclass's singleton class" do + ClassSpecs::K.singleton_class.superclass.should == + ClassSpecs::H.singleton_class + end + + it "is a subclass of the same level of superclass's singleton class" do + ClassSpecs::K.singleton_class.singleton_class.superclass.should == + ClassSpecs::H.singleton_class.singleton_class + end + + it "for BasicObject has Class as it's superclass" do + BasicObject.singleton_class.superclass.should == Class + end + + it "for BasicObject has the proper level of superclass for Class" do + BasicObject.singleton_class.singleton_class.superclass.should == + Class.singleton_class + end + + it "has class String as the superclass of a String instance" do + "blah".singleton_class.superclass.should == String + end + + it "doesn't have singleton class" do + lambda { bignum_value.singleton_class.superclass.should == Bignum }.should raise_error(TypeError) + end +end + +describe "A constant on a singleton class" do + before :each do + @object = Object.new + class << @object + CONST = self + end + end + + it "can be accessed after the singleton class body is reopened" do + class << @object + CONST.should == self + end + end + + it "can be accessed via self::CONST" do + class << @object + self::CONST.should == self + end + end + + it "can be accessed via const_get" do + class << @object + const_get(:CONST).should == self + end + end + + it "is not defined on the object's class" do + @object.class.const_defined?(:CONST).should be_false + end + + it "is not defined in the singleton class opener's scope" do + class << @object + CONST + end + lambda { CONST }.should raise_error(NameError) + end + + it "cannot be accessed via object::CONST" do + lambda do + @object::CONST + end.should raise_error(TypeError) + end + + it "raises a NameError for anonymous_module::CONST" do + @object = Class.new + class << @object + CONST = 100 + end + + lambda do + @object::CONST + end.should raise_error(NameError) + end + + it "appears in the singleton class constant list" do + @object.singleton_class.should have_constant(:CONST) + end + + it "does not appear in the object's class constant list" do + @object.class.should_not have_constant(:CONST) + end + + it "is not preserved when the object is duped" do + @object = @object.dup + + lambda do + class << @object; CONST; end + end.should raise_error(NameError) + end + + it "is preserved when the object is cloned" do + @object = @object.clone + + class << @object + CONST.should_not be_nil + end + end +end + +describe "Defining instance methods on a singleton class" do + before :each do + @k = ClassSpecs::K.new + class << @k + def singleton_method; 1 end + end + + @k_sc = @k.singleton_class + end + + it "defines public methods" do + @k_sc.should have_public_instance_method(:singleton_method) + end +end + +describe "Instance methods of a singleton class" do + before :each do + k = ClassSpecs::K.new + @k_sc = k.singleton_class + @a_sc = ClassSpecs::A.new.singleton_class + @a_c_sc = ClassSpecs::A.singleton_class + end + + it "include ones of the object's class" do + @k_sc.should have_instance_method(:example_instance_method) + end + + it "does not include class methods of the object's class" do + @k_sc.should_not have_instance_method(:example_class_method) + end + + it "include instance methods of Object" do + @a_sc.should have_instance_method(:example_instance_method_of_object) + end + + it "does not include class methods of Object" do + @a_sc.should_not have_instance_method(:example_class_method_of_object) + end + + describe "for a class" do + it "include instance methods of Class" do + @a_c_sc.should have_instance_method(:example_instance_method_of_class) + end + + it "does not include class methods of Class" do + @a_c_sc.should_not have_instance_method(:example_class_method_of_class) + end + + it "does not include instance methods of the singleton class of Class" do + @a_c_sc.should_not have_instance_method(:example_instance_method_of_singleton_class) + end + + it "does not include class methods of the singleton class of Class" do + @a_c_sc.should_not have_instance_method(:example_class_method_of_singleton_class) + end + end + + describe "for a singleton class" do + it "includes instance methods of the singleton class of Class" do + @a_c_sc.singleton_class.should have_instance_method(:example_instance_method_of_singleton_class) + end + + it "does not include class methods of the singleton class of Class" do + @a_c_sc.singleton_class.should_not have_instance_method(:example_class_method_of_singleton_class) + end + end +end + +describe "Class methods of a singleton class" do + before :each do + k = ClassSpecs::K.new + @k_sc = k.singleton_class + @a_sc = ClassSpecs::A.new.singleton_class + @a_c_sc = ClassSpecs::A.singleton_class + end + + it "include ones of the object's class" do + @k_sc.should have_method(:example_class_method) + end + + it "does not include instance methods of the object's class" do + @k_sc.should_not have_method(:example_instance_method) + end + + it "include instance methods of Class" do + @a_sc.should have_method(:example_instance_method_of_class) + end + + it "does not include class methods of Class" do + @a_sc.should_not have_method(:example_class_method_of_class) + end + + describe "for a class" do + it "include instance methods of Class" do + @a_c_sc.should have_method(:example_instance_method_of_class) + end + + it "include class methods of Class" do + @a_c_sc.should have_method(:example_class_method_of_class) + end + + it "include instance methods of the singleton class of Class" do + @a_c_sc.should have_method(:example_instance_method_of_singleton_class) + end + + it "does not include class methods of the singleton class of Class" do + @a_c_sc.should_not have_method(:example_class_method_of_singleton_class) + end + end + + describe "for a singleton class" do + it "include instance methods of the singleton class of Class" do + @a_c_sc.singleton_class.should have_method(:example_instance_method_of_singleton_class) + end + + it "include class methods of the singleton class of Class" do + @a_c_sc.singleton_class.should have_method(:example_class_method_of_singleton_class) + end + end +end + +describe "Instantiating a singleton class" do + it "raises a TypeError when new is called" do + lambda { + Object.new.singleton_class.new + }.should raise_error(TypeError) + end + + it "raises a TypeError when allocate is called" do + lambda { + Object.new.singleton_class.allocate + }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/language/string_spec.rb b/spec/rubyspec/language/string_spec.rb new file mode 100644 index 0000000000..dbec2652ed --- /dev/null +++ b/spec/rubyspec/language/string_spec.rb @@ -0,0 +1,282 @@ +# -*- encoding: binary -*- + +require File.expand_path('../../spec_helper', __FILE__) + +# TODO: rewrite these horrid specs. it "are..." seriously?! + +describe "Ruby character strings" do + + before :each do + @ip = 'xxx' # used for interpolation + $ip = 'xxx' + end + + it "don't get interpolated when put in single quotes" do + '#{@ip}'.should == '#{@ip}' + end + + it 'get interpolated with #{} when put in double quotes' do + "#{@ip}".should == 'xxx' + end + + it "interpolate instance variables just with the # character" do + "#@ip".should == 'xxx' + end + + it "interpolate global variables just with the # character" do + "#$ip".should == 'xxx' + end + + it "allows underscore as part of a variable name in a simple interpolation" do + @my_ip = 'xxx' + "#@my_ip".should == 'xxx' + end + + it "has characters [.(=?!# end simple # interpolation" do + "#@ip[".should == 'xxx[' + "#@ip.".should == 'xxx.' + "#@ip(".should == 'xxx(' + "#@ip=".should == 'xxx=' + "#@ip?".should == 'xxx?' + "#@ip!".should == 'xxx!' + "#@ip#@ip".should == 'xxxxxx' + end + + it "taints the result of interpolation when an interpolated value is tainted" do + "#{"".taint}".tainted?.should be_true + + @ip.taint + "#@ip".tainted?.should be_true + + $ip.taint + "#$ip".tainted?.should be_true + end + + it "untrusts the result of interpolation when an interpolated value is untrusted" do + "#{"".untrust}".untrusted?.should be_true + + @ip.untrust + "#@ip".untrusted?.should be_true + + $ip.untrust + "#$ip".untrusted?.should be_true + end + + it "allows using non-alnum characters as string delimiters" do + %(hey #{@ip}).should == "hey xxx" + %[hey #{@ip}].should == "hey xxx" + %{hey #{@ip}}.should == "hey xxx" + %.should == "hey xxx" + %!hey #{@ip}!.should == "hey xxx" + %@hey #{@ip}@.should == "hey xxx" + %#hey hey#.should == "hey hey" + %%hey #{@ip}%.should == "hey xxx" + %^hey #{@ip}^.should == "hey xxx" + %&hey #{@ip}&.should == "hey xxx" + %*hey #{@ip}*.should == "hey xxx" + %-hey #{@ip}-.should == "hey xxx" + %_hey #{@ip}_.should == "hey xxx" + %=hey #{@ip}=.should == "hey xxx" + %+hey #{@ip}+.should == "hey xxx" + %~hey #{@ip}~.should == "hey xxx" + %:hey #{@ip}:.should == "hey xxx" + %;hey #{@ip};.should == "hey xxx" + %"hey #{@ip}".should == "hey xxx" + %|hey #{@ip}|.should == "hey xxx" + %?hey #{@ip}?.should == "hey xxx" + %/hey #{@ip}/.should == "hey xxx" + %,hey #{@ip},.should == "hey xxx" + %.hey #{@ip}..should == "hey xxx" + + # surprised? huh + %'hey #{@ip}'.should == "hey xxx" + %\hey #{@ip}\.should == "hey xxx" + %`hey #{@ip}`.should == "hey xxx" + %$hey #{@ip}$.should == "hey xxx" + end + + it "using percent with 'q', stopping interpolation" do + %q(#{@ip}).should == '#{@ip}' + end + + it "using percent with 'Q' to interpolate" do + %Q(#{@ip}).should == 'xxx' + end + + # The backslashes : + # + # \t (tab), \n (newline), \r (carriage return), \f (form feed), \b + # (backspace), \a (bell), \e (escape), \s (whitespace), \nnn (octal), + # \xnn (hexadecimal), \cx (control x), \C-x (control x), \M-x (meta x), + # \M-\C-x (meta control x) + + it "backslashes follow the same rules as interpolation" do + "\t\n\r\f\b\a\e\s\075\x62\cx".should == "\t\n\r\f\b\a\e =b\030" + '\t\n\r\f\b\a\e =b\030'.should == "\\t\\n\\r\\f\\b\\a\\e =b\\030" + end + + it "calls #to_s when the object is not a String" do + obj = mock('to_s') + obj.stub!(:to_s).and_return('42') + + "#{obj}".should == '42' + end + + it "calls #to_s as a private method" do + obj = mock('to_s') + obj.stub!(:to_s).and_return('42') + + class << obj + private :to_s + end + + "#{obj}".should == '42' + end + + it "uses an internal representation when #to_s doesn't return a String" do + obj = mock('to_s') + obj.stub!(:to_s).and_return(42) + + # See rubyspec commit 787c132d by yugui. There is value in + # ensuring that this behavior works. So rather than removing + # this spec completely, the only thing that can be asserted + # is that if you interpolate an object that fails to return + # a String, you will still get a String and not raise an + # exception. + "#{obj}".should be_an_instance_of(String) + end + + it "allows a dynamic string to parse a nested do...end block as an argument to a call without parens, interpolated" do + s = eval 'eval "#{proc do; 1; end.call}"' + s.should == 1 + end + + it "are produced from character shortcuts" do + ?z.should == 'z' + end + + it "are produced from control character shortcuts" do + # Control-Z + ?\C-z.should == "\x1A" + + # Meta-Z + ?\M-z.should == "\xFA" + + # Meta-Control-Z + ?\M-\C-z.should == "\x9A" + end + + describe "Unicode escaping" do + it "can be done with \\u and four hex digits" do + [ ["\u0000", 0x0000], + ["\u2020", 0x2020] + ].should be_computed_by(:ord) + end + + it "can be done with \\u{} and one to six hex digits" do + [ ["\u{a}", 0xa], + ["\u{ab}", 0xab], + ["\u{abc}", 0xabc], + ["\u{1abc}", 0x1abc], + ["\u{12abc}", 0x12abc], + ["\u{100000}", 0x100000] + ].should be_computed_by(:ord) + end + + # TODO: spec other source encodings + describe "with ASCII_8BIT source encoding" do + it "produces an ASCII string when escaping ASCII characters via \\u" do + "\u0000".encoding.should == Encoding::ASCII_8BIT + end + + it "produces an ASCII string when escaping ASCII characters via \\u{}" do + "\u{0000}".encoding.should == Encoding::ASCII_8BIT + end + + it "produces a UTF-8-encoded string when escaping non-ASCII characters via \\u" do + "\u1234".encoding.should == Encoding::UTF_8 + end + + it "produces a UTF-8-encoded string when escaping non-ASCII characters via \\u{}" do + "\u{1234}".encoding.should == Encoding::UTF_8 + end + end + end +end + +# TODO: rewrite all specs above this + +describe "Ruby String literals" do + def str_concat + "foo" "bar" "baz" + end + + def long_string_literals + "Beautiful is better than ugly." \ + "Explicit is better than implicit." + end + + it "on a single line with spaces in between are concatenated together" do + str_concat.should == "foobarbaz" + end + + it "on multiple lines with newlines and backslash in between are concatenated together" do + long_string_literals.should == "Beautiful is better than ugly.Explicit is better than implicit." + end + + ruby_version_is "2.3" do + describe "with a magic frozen comment" do + it "produce the same object each time" do + ruby_exe(fixture(__FILE__, "freeze_magic_comment_one_literal.rb")).chomp.should == "true" + end + + it "produce the same object for literals with the same content" do + ruby_exe(fixture(__FILE__, "freeze_magic_comment_two_literals.rb")).chomp.should == "true" + end + + it "produce the same object for literals with the same content in different files" do + ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files.rb")).chomp.should == "true" + end + + it "produce different objects for literals with the same content in different files if the other file doesn't have the comment" do + ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_no_comment.rb")).chomp.should == "true" + end + + it "produce different objects for literals with the same content in different files if they have different encodings" do + ruby_exe(fixture(__FILE__, "freeze_magic_comment_across_files_diff_enc.rb")).chomp.should == "true" + end + end + end + +end + +with_feature :encoding do + describe "Ruby String interpolation" do + it "creates a String having an Encoding compatible with all components" do + a = "\u3042" + b = "abc".encode("ascii-8bit") + + str = "#{a} x #{b}" + + str.should == "\xe3\x81\x82\x20\x78\x20\x61\x62\x63".force_encoding("utf-8") + str.encoding.should == Encoding::UTF_8 + end + + it "creates a String having the Encoding of the components when all are the same Encoding" do + a = "abc".force_encoding("euc-jp") + b = "def".force_encoding("euc-jp") + str = '"#{a} x #{b}"'.force_encoding("euc-jp") + + result = eval(str) + result.should == "\x61\x62\x63\x20\x78\x20\x64\x65\x66".force_encoding("euc-jp") + result.encoding.should == Encoding::EUC_JP + end + + it "raises an Encoding::CompatibilityError if the Encodings are not compatible" do + a = "\u3042" + b = "\xff".force_encoding "ascii-8bit" + + lambda { "#{a} #{b}" }.should raise_error(Encoding::CompatibilityError) + end + end +end diff --git a/spec/rubyspec/language/super_spec.rb b/spec/rubyspec/language/super_spec.rb new file mode 100644 index 0000000000..f7dc0d132c --- /dev/null +++ b/spec/rubyspec/language/super_spec.rb @@ -0,0 +1,282 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/super', __FILE__) + +describe "The super keyword" do + it "calls the method on the calling class" do + Super::S1::A.new.foo([]).should == ["A#foo","A#bar"] + Super::S1::A.new.bar([]).should == ["A#bar"] + Super::S1::B.new.foo([]).should == ["B#foo","A#foo","B#bar","A#bar"] + Super::S1::B.new.bar([]).should == ["B#bar","A#bar"] + end + + it "searches the full inheritence chain" do + Super::S2::B.new.foo([]).should == ["B#foo","A#baz"] + Super::S2::B.new.baz([]).should == ["A#baz"] + Super::S2::C.new.foo([]).should == ["B#foo","C#baz","A#baz"] + Super::S2::C.new.baz([]).should == ["C#baz","A#baz"] + end + + it "searches class methods" do + Super::S3::A.new.foo([]).should == ["A#foo"] + Super::S3::A.foo([]).should == ["A.foo"] + Super::S3::A.bar([]).should == ["A.bar","A.foo"] + Super::S3::B.new.foo([]).should == ["A#foo"] + Super::S3::B.foo([]).should == ["B.foo","A.foo"] + Super::S3::B.bar([]).should == ["B.bar","A.bar","B.foo","A.foo"] + end + + it "calls the method on the calling class including modules" do + Super::MS1::A.new.foo([]).should == ["ModA#foo","ModA#bar"] + Super::MS1::A.new.bar([]).should == ["ModA#bar"] + Super::MS1::B.new.foo([]).should == ["B#foo","ModA#foo","ModB#bar","ModA#bar"] + Super::MS1::B.new.bar([]).should == ["ModB#bar","ModA#bar"] + end + + it "searches the full inheritence chain including modules" do + Super::MS2::B.new.foo([]).should == ["ModB#foo","A#baz"] + Super::MS2::B.new.baz([]).should == ["A#baz"] + Super::MS2::C.new.baz([]).should == ["C#baz","A#baz"] + Super::MS2::C.new.foo([]).should == ["ModB#foo","C#baz","A#baz"] + end + + it "can resolve to different methods in an included module method" do + Super::MultiSuperTargets::A.new.foo.should == :BaseA + Super::MultiSuperTargets::B.new.foo.should == :BaseB + end + + it "searches class methods including modules" do + Super::MS3::A.new.foo([]).should == ["A#foo"] + Super::MS3::A.foo([]).should == ["ModA#foo"] + Super::MS3::A.bar([]).should == ["ModA#bar","ModA#foo"] + Super::MS3::B.new.foo([]).should == ["A#foo"] + Super::MS3::B.foo([]).should == ["B.foo","ModA#foo"] + Super::MS3::B.bar([]).should == ["B.bar","ModA#bar","B.foo","ModA#foo"] + end + + it "searches BasicObject from a module for methods defined there" do + Super::IncludesFromBasic.new.__send__(:foobar).should == 43 + end + + it "searches BasicObject through another module for methods defined there" do + Super::IncludesIntermediate.new.__send__(:foobar).should == 42 + end + + it "calls the correct method when the method visibility is modified" do + Super::MS4::A.new.example.should == 5 + end + + it "calls the correct method when the superclass argument list is different from the subclass" do + Super::S4::A.new.foo([]).should == ["A#foo"] + Super::S4::B.new.foo([],"test").should == ["B#foo(a,test)", "A#foo"] + end + + it "raises an error error when super method does not exist" do + sup = Class.new + sub_normal = Class.new(sup) do + def foo + super() + end + end + sub_zsuper = Class.new(sup) do + def foo + super + end + end + + lambda {sub_normal.new.foo}.should raise_error(NoMethodError, /super/) + lambda {sub_zsuper.new.foo}.should raise_error(NoMethodError, /super/) + end + + it "uses given block even if arguments are passed explicitly" do + c1 = Class.new do + def m + yield + end + end + c2 = Class.new(c1) do + def m(v) + super() + end + end + + c2.new.m(:dump) { :value }.should == :value + end + + it "calls the superclass method when in a block" do + Super::S6.new.here.should == :good + end + + it "calls the superclass method when initial method is defined_method'd" do + Super::S7.new.here.should == :good + end + + it "can call through a define_method multiple times (caching check)" do + obj = Super::S7.new + + 2.times do + obj.here.should == :good + end + end + + it "supers up appropriate name even if used for multiple method names" do + sup = Class.new do + def a; "a"; end + def b; "b"; end + end + + sub = Class.new(sup) do + [:a, :b].each do |name| + define_method name do + super() + end + end + end + + sub.new.a.should == "a" + sub.new.b.should == "b" + sub.new.a.should == "a" + end + + it "raises a RuntimeError when called with implicit arguments from a method defined with define_method" do + super_class = Class.new do + def a(arg) + arg + end + end + + klass = Class.new super_class do + define_method :a do |arg| + super + end + end + + lambda { klass.new.a(:a_called) }.should raise_error(RuntimeError) + end + + # Rubinius ticket github#157 + it "calls method_missing when a superclass method is not found" do + Super::MM_B.new.is_a?(Hash).should == false + end + + # Rubinius ticket github#180 + it "respects the original module a method is aliased from" do + Super::Alias3.new.name3.should == [:alias2, :alias1] + end + + it "sees the included version of a module a method is alias from" do + Super::AliasWithSuper::Trigger.foo.should == [:b, :a] + end + + it "find super from a singleton class" do + obj = Super::SingletonCase::Foo.new + def obj.foobar(array) + array << :singleton + super + end + obj.foobar([]).should == [:singleton, :foo, :base] + end + + it "finds super on other objects if a singleton class aliased the method" do + orig_obj = Super::SingletonAliasCase::Foo.new + orig_obj.alias_on_singleton + orig_obj.new_foobar([]).should == [:foo, :base] + Super::SingletonAliasCase::Foo.new.foobar([]).should == [:foo, :base] + end + + it "passes along modified rest args when they weren't originally empty" do + Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"] + end + + it "passes along modified rest args when they were originally empty" do + Super::RestArgsWithSuper::B.new.a.should == ["foo"] + end + + it "invokes methods from a chain of anonymous modules" do + Super::AnonymousModuleIncludedTwice.new.a([]).should == ["anon", "anon", "non-anon"] + end + + it "without explicit arguments can accept a block but still pass the original arguments" do + Super::ZSuperWithBlock::B.new.a.should == 14 + end + + it "passes along block via reference to method expecting a reference" do + Super::ZSuperWithBlock::B.new.b.should == [14, 15] + end + + it "passes along a block via reference to a method that yields" do + Super::ZSuperWithBlock::B.new.c.should == 16 + end + + it "without explicit arguments passes optional arguments that have a default value" do + Super::ZSuperWithOptional::B.new.m(1, 2).should == 14 + end + + it "without explicit arguments passes optional arguments that have a non-default value" do + Super::ZSuperWithOptional::B.new.m(1, 2, 3).should == 3 + end + + it "without explicit arguments passes optional arguments that have a default value but were modified" do + Super::ZSuperWithOptional::C.new.m(1, 2).should == 100 + end + + it "without explicit arguments passes optional arguments that have a non-default value but were modified" do + Super::ZSuperWithOptional::C.new.m(1, 2, 3).should == 100 + end + + it "without explicit arguments passes rest arguments" do + Super::ZSuperWithRest::B.new.m(1, 2, 3).should == [1, 2, 3] + end + + it "without explicit arguments passes rest arguments including any modifications" do + Super::ZSuperWithRest::B.new.m_modified(1, 2, 3).should == [1, 14, 3] + end + + it "without explicit arguments passes arguments and rest arguments" do + Super::ZSuperWithRestAndOthers::B.new.m(1, 2, 3, 4, 5).should == [3, 4, 5] + end + + it "without explicit arguments passes arguments and rest arguments including any modifications" do + Super::ZSuperWithRestAndOthers::B.new.m_modified(1, 2, 3, 4, 5).should == [3, 14, 5] + end + + it "without explicit arguments that are '_'" do + Super::ZSuperWithUnderscores::B.new.m(1, 2).should == [1, 2] + end + + it "without explicit arguments that are '_' including any modifications" do + Super::ZSuperWithUnderscores::B.new.m_modified(1, 2).should == [14, 2] + end + + describe 'when using keyword arguments' do + it 'passes any given keyword arguments to the parent' do + b = Super::KeywordArguments::B.new + b.foo(:number => 10).should == {:number => 10} + end + + it "passes any given keyword arguments including optional and required ones to the parent" do + class Super::KeywordArguments::C + eval <<-RUBY + def foo(a:, b: 'b', **) + super + end + RUBY + end + c = Super::KeywordArguments::C.new + + c.foo(a: 'a', c: 'c').should == {a: 'a', b: 'b', c: 'c'} + end + + it 'does not pass any keyword arguments to the parent when none are given' do + b = Super::KeywordArguments::B.new + b.foo.should == {} + end + + describe 'when using splat arguments' do + it 'passes splat arguments and keyword arguments to the parent' do + b = Super::SplatAndKeyword::B.new + + b.foo('bar', baz: true).should == [['bar'], {baz: true}] + end + end + end +end diff --git a/spec/rubyspec/language/symbol_spec.rb b/spec/rubyspec/language/symbol_spec.rb new file mode 100644 index 0000000000..41be5b2236 --- /dev/null +++ b/spec/rubyspec/language/symbol_spec.rb @@ -0,0 +1,93 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "A Symbol literal" do + it "is a ':' followed by any number of valid characters" do + a = :foo + a.should be_kind_of(Symbol) + a.inspect.should == ':foo' + end + + it "is a ':' followed by any valid variable, method, or constant name" do + # Add more of these? + [ :Foo, + :foo, + :@foo, + :@@foo, + :$foo, + :_, + :~, + :- , + :FOO, + :_Foo, + :&, + :_9 + ].each { |s| s.should be_kind_of(Symbol) } + end + + it "is a ':' followed by a single- or double-quoted string that may contain otherwise invalid characters" do + [ [:'foo bar', ':"foo bar"'], + [:'++', ':"++"'], + [:'9', ':"9"'], + [:"foo #{1 + 1}", ':"foo 2"'], + [:"foo\nbar", ':"foo\nbar"'], + ].each { |sym, str| + sym.should be_kind_of(Symbol) + sym.inspect.should == str + } + end + + it "may contain '::' in the string" do + :'Some::Class'.should be_kind_of(Symbol) + end + + it "is converted to a literal, unquoted representation if the symbol contains only valid characters" do + a, b, c = :'foo', :'+', :'Foo__9' + a.should be_kind_of(Symbol) + a.inspect.should == ':foo' + b.should be_kind_of(Symbol) + b.inspect.should == ':+' + c.should be_kind_of(Symbol) + c.inspect.should == ':Foo__9' + end + + it "can be created by the %s-delimited expression" do + a, b = :'foo bar', %s{foo bar} + b.should be_kind_of(Symbol) + b.inspect.should == ':"foo bar"' + b.should == a + end + + it "is the same object when created from identical strings" do + var = "@@var" + [ [:symbol, :symbol], + [:'a string', :'a string'], + [:"#{var}", :"#{var}"] + ].each { |a, b| + a.should equal(b) + } + end + + it "can contain null in the string" do + eval(':"\0" ').inspect.should == ':"\\x00"' + end + + it "can be an empty string" do + c = :'' + c.should be_kind_of(Symbol) + c.inspect.should == ':""' + end + + it "can be :!, :!=, or :!~" do + %w{'!', '!=', '!~'}.each do |sym| + sym.to_sym.to_s.should == sym + end + end + + it "can be created from list syntax %i{a b c} without interpolation" do + %i{a b #{c}}.should == [:a, :b, :"\#{c}"] + end + + it "can be created from list syntax %I{a b c} with interpolation" do + %I{a b #{"c"}}.should == [:a, :b, :c] + end +end diff --git a/spec/rubyspec/language/throw_spec.rb b/spec/rubyspec/language/throw_spec.rb new file mode 100644 index 0000000000..d78c137708 --- /dev/null +++ b/spec/rubyspec/language/throw_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The throw keyword" do + it "abandons processing" do + i = 0 + catch(:done) do + loop do + i += 1 + throw :done if i > 4 + end + i += 1 + end + i.should == 5 + end + + it "supports a second parameter" do + msg = catch(:exit) do + throw :exit,:msg + end + msg.should == :msg + end + + it "uses nil as a default second parameter" do + msg = catch(:exit) do + throw :exit + end + msg.should == nil + end + + it "clears the current exception" do + catch :exit do + begin + raise "exception" + rescue + throw :exit + end + end + $!.should be_nil + end + + it "allows any object as its argument" do + catch(1) { throw 1, 2 }.should == 2 + o = Object.new + catch(o) { throw o, o }.should == o + end + + it "does not convert strings to a symbol" do + lambda { catch(:exit) { throw "exit" } }.should raise_error(ArgumentError) + end + + it "unwinds stack from within a method" do + def throw_method(handler, val) + throw handler, val + end + + catch(:exit) do + throw_method(:exit, 5) + end.should == 5 + end + + it "unwinds stack from within a lambda" do + c = lambda { throw :foo, :msg } + catch(:foo) { c.call }.should == :msg + end + + it "raises an ArgumentError if outside of scope of a matching catch" do + lambda { throw :test, 5 }.should raise_error(ArgumentError) + lambda { catch(:different) { throw :test, 5 } }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if used to exit a thread" do + lambda { + catch(:what) do + Thread.new { + throw :what + }.join + end + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/language/undef_spec.rb b/spec/rubyspec/language/undef_spec.rb new file mode 100644 index 0000000000..45fe4e9043 --- /dev/null +++ b/spec/rubyspec/language/undef_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The undef keyword" do + it "undefines a method" do + undef_class = Class.new do + def meth(o); o; end + end + obj = undef_class.new + obj.meth(5).should == 5 + undef_class.class_eval do + undef meth + end + lambda { obj.meth(5) }.should raise_error(NoMethodError) + end + + it "allows undefining multiple methods at a time" do + undef_multiple = Class.new do + def method1; end + def method2; :nope; end + + undef :method1, :method2 + end + + obj = undef_multiple.new + obj.respond_to?(:method1).should == false + obj.respond_to?(:method2).should == false + end + + it "raises a NameError when passed a missing name" do + Class.new do + lambda { + undef not_exist + }.should raise_error(NameError) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end + end +end diff --git a/spec/rubyspec/language/unless_spec.rb b/spec/rubyspec/language/unless_spec.rb new file mode 100644 index 0000000000..681f0adfdd --- /dev/null +++ b/spec/rubyspec/language/unless_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe "The unless expression" do + it "evaluates the unless body when the expression is false" do + unless false + a = true + else + a = false + end + + a.should == true + end + + it "returns the last statement in the body" do + unless false + 'foo' + 'bar' + 'baz' + end.should == 'baz' + end + + it "evaluates the else body when the expression is true" do + unless true + 'foo' + else + 'bar' + end.should == 'bar' + end + + it "takes an optional then after the expression" do + unless false then + 'baz' + end.should == 'baz' + end + + it "does not return a value when the expression is true" do + unless true; end.should == nil + end + + it "allows expression and body to be on one line (using 'then')" do + unless false then 'foo'; else 'bar'; end.should == 'foo' + end +end diff --git a/spec/rubyspec/language/until_spec.rb b/spec/rubyspec/language/until_spec.rb new file mode 100644 index 0000000000..08898644ce --- /dev/null +++ b/spec/rubyspec/language/until_spec.rb @@ -0,0 +1,234 @@ +require File.expand_path('../../spec_helper', __FILE__) + +# until bool-expr [do] +# body +# end +# +# begin +# body +# end until bool-expr +# +# expr until bool-expr +describe "The until expression" do + it "runs while the expression is false" do + i = 0 + until i > 9 + i += 1 + end + + i.should == 10 + end + + it "optionally takes a 'do' after the expression" do + i = 0 + until i > 9 do + i += 1 + end + + i.should == 10 + end + + it "allows body begin on the same line if do is used" do + i = 0 + until i > 9 do i += 1 + end + + i.should == 10 + end + + it "executes code in containing variable scope" do + i = 0 + until i == 1 + a = 123 + i = 1 + end + + a.should == 123 + end + + it "executes code in containing variable scope with 'do'" do + i = 0 + until i == 1 do + a = 123 + i = 1 + end + + a.should == 123 + end + + it "returns nil if ended when condition became true" do + i = 0 + until i > 9 + i += 1 + end.should == nil + end + + it "evaluates the body if expression is empty" do + a = [] + until () + a << :body_evaluated + break + end + a.should == [:body_evaluated] + end + + it "stops running body if interrupted by break" do + i = 0 + until i > 9 + i += 1 + break if i > 5 + end + i.should == 6 + end + + it "returns value passed to break if interrupted by break" do + until false + break 123 + end.should == 123 + end + + it "returns nil if interrupted by break with no arguments" do + until false + break + end.should == nil + end + + it "skips to end of body with next" do + a = [] + i = 0 + until (i+=1)>=5 + next if i==3 + a << i + end + a.should == [1, 2, 4] + end + + it "restarts the current iteration without reevaluating condition with redo" do + a = [] + i = 0 + j = 0 + until (i+=1)>=3 + a << i + j+=1 + redo if j<3 + end + a.should == [1, 1, 1, 2] + end +end + +describe "The until modifier" do + it "runs preceding statement while the condition is false" do + i = 0 + i += 1 until i > 9 + i.should == 10 + end + + it "evaluates condition before statement execution" do + a = [] + i = 0 + a << i until (i+=1) >= 3 + a.should == [1, 2] + end + + it "does not run preceding statement if the condition is true" do + i = 0 + i += 1 until true + i.should == 0 + end + + it "returns nil if ended when condition became true" do + i = 0 + (i += 1 until i>9).should == nil + end + + it "returns value passed to break if interrupted by break" do + (break 123 until false).should == 123 + end + + it "returns nil if interrupted by break with no arguments" do + (break until false).should == nil + end + + it "skips to end of body with next" do + i = 0 + j = 0 + ((i+=1) == 3 ? next : j+=i) until i > 10 + j.should == 63 + end + + it "restarts the current iteration without reevaluating condition with redo" do + i = 0 + j = 0 + (i+=1) == 4 ? redo : j+=i until (i+=1) > 10 + j.should == 34 + end +end + +describe "The until modifier with begin .. end block" do + it "runs block while the expression is false" do + i = 0 + begin + i += 1 + end until i > 9 + + i.should == 10 + end + + it "stops running block if interrupted by break" do + i = 0 + begin + i += 1 + break if i > 5 + end until i > 9 + + i.should == 6 + end + + it "returns value passed to break if interrupted by break" do + (begin; break 123; end until false).should == 123 + end + + it "returns nil if interrupted by break with no arguments" do + (begin; break; end until false).should == nil + end + + it "runs block at least once (even if the expression is true)" do + i = 0 + begin + i += 1 + end until true + + i.should == 1 + end + + it "evaluates condition after block execution" do + a = [] + i = 0 + begin + a << i + end until (i+=1)>=5 + a.should == [0, 1, 2, 3, 4] + end + + it "skips to end of body with next" do + a = [] + i = 0 + begin + next if i==3 + a << i + end until (i+=1)>=5 + a.should == [0, 1, 2, 4] + end + + it "restart the current iteration without reevaluting condition with redo" do + a = [] + i = 0 + j = 0 + begin + a << i + j+=1 + redo if j<3 + end until (i+=1)>=3 + a.should == [0, 0, 0, 1, 2] + end +end diff --git a/spec/rubyspec/language/variables_spec.rb b/spec/rubyspec/language/variables_spec.rb new file mode 100644 index 0000000000..e0ef6b21c4 --- /dev/null +++ b/spec/rubyspec/language/variables_spec.rb @@ -0,0 +1,776 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/variables', __FILE__) + +describe "Multiple assignment" do + context "with a single RHS value" do + it "assigns a simple MLHS" do + (a, b, c = 1).should == 1 + [a, b, c].should == [1, nil, nil] + end + + it "calls #to_ary to convert an Object RHS when assigning a simple MLHS" do + x = mock("multi-assign single RHS") + x.should_receive(:to_ary).and_return([1, 2]) + + (a, b, c = x).should == x + [a, b, c].should == [1, 2, nil] + end + + it "calls #to_ary if it is private" do + x = mock("multi-assign single RHS") + x.should_receive(:to_ary).and_return([1, 2]) + class << x; private :to_ary; end + + (a, b, c = x).should == x + [a, b, c].should == [1, 2, nil] + end + + it "does not call #to_ary if #respond_to? returns false" do + x = mock("multi-assign single RHS") + x.should_receive(:respond_to?).with(:to_ary, true).and_return(false) + x.should_not_receive(:to_ary) + + (a, b, c = x).should == x + [a, b, c].should == [x, nil, nil] + end + + it "wraps the Object in an Array if #to_ary returns nil" do + x = mock("multi-assign single RHS") + x.should_receive(:to_ary).and_return(nil) + + (a, b, c = x).should == x + [a, b, c].should == [x, nil, nil] + end + + it "raises a TypeError of #to_ary does not return an Array" do + x = mock("multi-assign single RHS") + x.should_receive(:to_ary).and_return(1) + + lambda { a, b, c = x }.should raise_error(TypeError) + end + + it "does not call #to_a to convert an Object RHS when assigning a simple MLHS" do + x = mock("multi-assign single RHS") + x.should_not_receive(:to_a) + + (a, b, c = x).should == x + [a, b, c].should == [x, nil, nil] + end + + it "does not call #to_ary on an Array instance" do + x = [1, 2] + x.should_not_receive(:to_ary) + + (a, b = x).should == x + [a, b].should == [1, 2] + end + + it "does not call #to_a on an Array instance" do + x = [1, 2] + x.should_not_receive(:to_a) + + (a, b = x).should == x + [a, b].should == [1, 2] + end + + it "returns the RHS when it is an Array" do + ary = [1, 2] + + x = (a, b = ary) + x.should equal(ary) + end + + it "returns the RHS when it is an Array subclass" do + cls = Class.new(Array) + ary = cls.new [1, 2] + + x = (a, b = ary) + x.should equal(ary) + end + + it "does not call #to_ary on an Array subclass instance" do + x = Class.new(Array).new [1, 2] + x.should_not_receive(:to_ary) + + (a, b = x).should == x + [a, b].should == [1, 2] + end + + it "does not call #to_a on an Array subclass instance" do + x = Class.new(Array).new [1, 2] + x.should_not_receive(:to_a) + + (a, b = x).should == x + [a, b].should == [1, 2] + end + + it "assigns a MLHS with a trailing comma" do + a, = 1 + b, c, = [] + [a, b, c].should == [1, nil, nil] + end + + it "assigns a single LHS splat" do + (*a = 1).should == 1 + a.should == [1] + end + + it "calls #to_ary to convert an Object RHS" do + x = mock("multi-assign splat") + x.should_receive(:to_ary).and_return([1, 2]) + + (*a = x).should == x + a.should == [1, 2] + end + + it "raises a TypeError if #to_ary does not return an Array" do + x = mock("multi-assign splat") + x.should_receive(:to_ary).and_return(1) + + lambda { *a = x }.should raise_error(TypeError) + end + + it "does not call #to_ary on an Array subclass" do + cls = Class.new(Array) + ary = cls.new [1, 2] + ary.should_not_receive(:to_ary) + + (*a = ary).should == [1, 2] + a.should == [1, 2] + end + + it "assigns an Array when the RHS is an Array subclass" do + cls = Class.new(Array) + ary = cls.new [1, 2] + + x = (*a = ary) + x.should equal(ary) + a.should be_an_instance_of(Array) + end + + it "calls #to_ary to convert an Object RHS with MLHS" do + x = mock("multi-assign splat") + x.should_receive(:to_ary).and_return([1, 2]) + + (a, *b, c = x).should == x + [a, b, c].should == [1, [], 2] + end + + it "raises a TypeError if #to_ary does not return an Array" do + x = mock("multi-assign splat") + x.should_receive(:to_ary).and_return(1) + + lambda { a, *b, c = x }.should raise_error(TypeError) + end + + it "does not call #to_a to convert an Object RHS with a MLHS" do + x = mock("multi-assign splat") + x.should_not_receive(:to_a) + + (a, *b = x).should == x + [a, b].should == [x, []] + end + + it "assigns a MLHS with leading splat" do + (*a, b, c = 1).should == 1 + [a, b, c].should == [[], 1, nil] + end + + it "assigns a MLHS with a middle splat" do + a, b, *c, d, e = 1 + [a, b, c, d, e].should == [1, nil, [], nil, nil] + end + + it "assigns a MLHS with a trailing splat" do + a, b, *c = 1 + [a, b, c].should == [1, nil, []] + end + + it "assigns a grouped LHS without splat" do + ((a, b), c), (d, (e,), (f, (g, h))) = 1 + [a, b, c, d, e, f, g, h].should == [1, nil, nil, nil, nil, nil, nil, nil] + end + + it "assigns a single grouped LHS splat" do + (*a) = nil + a.should == [nil] + end + + it "assigns a grouped LHS with splats" do + (a, *b), c, (*d, (e, *f, g)) = 1 + [a, b, c, d, e, f, g].should == [1, [], nil, [], nil, [], nil] + end + + it "consumes values for an anonymous splat" do + (* = 1).should == 1 + end + + it "consumes values for a grouped anonymous splat" do + ((*) = 1).should == 1 + end + + it "does not mutate a RHS Array" do + x = [1, 2, 3, 4] + a, *b, c, d = x + [a, b, c, d].should == [1, [2], 3, 4] + x.should == [1, 2, 3, 4] + end + + it "assigns values from a RHS method call" do + def x() 1 end + + (a, b = x).should == 1 + [a, b].should == [1, nil] + end + + it "assigns values from a RHS method call with arguments" do + def x(a) a end + + (a, b = x []).should == [] + [a, b].should == [nil, nil] + end + + it "assigns values from a RHS method call with receiver" do + x = mock("multi-assign attributes") + x.should_receive(:m).and_return([1, 2, 3]) + + a, b = x.m + [a, b].should == [1, 2] + end + + it "calls #to_ary on the value returned by the method call" do + y = mock("multi-assign method return value") + y.should_receive(:to_ary).and_return([1, 2]) + + x = mock("multi-assign attributes") + x.should_receive(:m).and_return(y) + + (a, b = x.m).should == y + [a, b].should == [1, 2] + end + + it "raises a TypeError if #to_ary does not return an Array" do + y = mock("multi-assign method return value") + y.should_receive(:to_ary).and_return(1) + + x = mock("multi-assign attributes") + x.should_receive(:m).and_return(y) + + lambda { a, b = x.m }.should raise_error(TypeError) + end + + it "assigns values from a RHS method call with receiver and arguments" do + x = mock("multi-assign attributes") + x.should_receive(:m).with(1, 2).and_return([1, 2, 3]) + + a, b = x.m 1, 2 + [a, b].should == [1, 2] + end + + it "assigns global variables" do + $spec_a, $spec_b = 1 + [$spec_a, $spec_b].should == [1, nil] + end + + it "assigns instance variables" do + @a, @b = 1 + [@a, @b].should == [1, nil] + end + + it "assigns attributes" do + a = mock("multi-assign attributes") + a.should_receive(:x=).with(1) + a.should_receive(:y=).with(nil) + + a.x, a.y = 1 + end + + it "assigns indexed elements" do + a = [] + a[1], a[2] = 1 + a.should == [nil, 1, nil] + end + + it "assigns constants" do + module VariableSpecs + SINGLE_RHS_1, SINGLE_RHS_2 = 1 + [SINGLE_RHS_1, SINGLE_RHS_2].should == [1, nil] + end + end + end + + context "with a single splatted RHS value" do + it "assigns a single grouped LHS splat" do + (*a) = *1 + a.should == [1] + end + + it "assigns an empty Array to a single LHS value when passed nil" do + (a = *nil).should == [] + a.should == [] + end + + it "calls #to_a to convert nil to an empty Array" do + nil.should_receive(:to_a).and_return([]) + + (*a = *nil).should == [] + a.should == [] + end + + it "does not call #to_a on an Array" do + ary = [1, 2] + ary.should_not_receive(:to_a) + + (a = *ary).should == [1, 2] + a.should == [1, 2] + end + + it "returns a copy of a splatted Array" do + ary = [1, 2] + + (a = *ary).should == [1, 2] + a.should_not equal(ary) + end + + it "does not call #to_a on an Array subclass" do + cls = Class.new(Array) + ary = cls.new [1, 2] + ary.should_not_receive(:to_a) + + (a = *ary).should == [1, 2] + a.should == [1, 2] + end + + it "returns an Array when the splatted object is an Array subclass" do + cls = Class.new(Array) + ary = cls.new [1, 2] + + x = (a = *ary) + + x.should == [1, 2] + x.should be_an_instance_of(Array) + + a.should == [1, 2] + a.should be_an_instance_of(Array) + end + + it "consumes values for an anonymous splat" do + a = 1 + (* = *a).should == [1] + end + + it "assigns a single LHS splat" do + x = 1 + (*a = *x).should == [1] + a.should == [1] + end + + it "calls #to_a to convert an Object RHS with a single splat LHS" do + x = mock("multi-assign RHS splat") + x.should_receive(:to_a).and_return([1, 2]) + + (*a = *x).should == [1, 2] + a.should == [1, 2] + end + + it "calls #to_a if it is private" do + x = mock("multi-assign RHS splat") + x.should_receive(:to_a).and_return([1, 2]) + class << x; private :to_a; end + + (*a = *x).should == [1, 2] + a.should == [1, 2] + end + + it "does not call #to_a if #respond_to? returns false" do + x = mock("multi-assign RHS splat") + x.should_receive(:respond_to?).with(:to_a, true).and_return(false) + x.should_not_receive(:to_a) + + (*a = *x).should == [x] + a.should == [x] + end + + it "wraps the Object in an Array if #to_a returns nil" do + x = mock("multi-assign RHS splat") + x.should_receive(:to_a).and_return(nil) + + (*a = *x).should == [x] + a.should == [x] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("multi-assign RHS splat") + x.should_receive(:to_a).and_return(1) + + lambda { *a = *x }.should raise_error(TypeError) + end + + it "does not call #to_ary to convert an Object RHS with a single splat LHS" do + x = mock("multi-assign RHS splat") + x.should_not_receive(:to_ary) + + (*a = *x).should == [x] + a.should == [x] + end + + it "assigns a MLHS with leading splat" do + (*a, b, c = *1).should == [1] + [a, b, c].should == [[], 1, nil] + end + + it "assigns a MLHS with a middle splat" do + a, b, *c, d, e = *1 + [a, b, c, d, e].should == [1, nil, [], nil, nil] + end + + it "assigns a MLHS with a trailing splat" do + a, b, *c = *nil + [a, b, c].should == [nil, nil, []] + end + + it "calls #to_a to convert an Object RHS with a single LHS" do + x = mock("multi-assign RHS splat") + x.should_receive(:to_a).and_return([1, 2]) + + (a = *x).should == [1, 2] + a.should == [1, 2] + end + + it "does not call #to_ary to convert an Object RHS with a single LHS" do + x = mock("multi-assign RHS splat") + x.should_not_receive(:to_ary) + + (a = *x).should == [x] + a.should == [x] + end + + it "calls #to_a to convert an Object RHS with a single LHS" do + x = mock("multi-assign splat") + x.should_receive(:to_a).and_return([1, 2]) + + a = *x + a.should == [1, 2] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("multi-assign splat") + x.should_receive(:to_a).and_return(1) + + lambda { a = *x }.should raise_error(TypeError) + end + + it "calls #to_a to convert an Object splat RHS when assigned to a simple MLHS" do + x = mock("multi-assign splat") + x.should_receive(:to_a).and_return([1, 2]) + + (a, b, c = *x).should == [1, 2] + [a, b, c].should == [1, 2, nil] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("multi-assign splat") + x.should_receive(:to_a).and_return(1) + + lambda { a, b, c = *x }.should raise_error(TypeError) + end + + it "does not call #to_ary to convert an Object splat RHS when assigned to a simple MLHS" do + x = mock("multi-assign splat") + x.should_not_receive(:to_ary) + + (a, b, c = *x).should == [x] + [a, b, c].should == [x, nil, nil] + end + + it "calls #to_a to convert an Object RHS with MLHS" do + x = mock("multi-assign splat") + x.should_receive(:to_a).and_return([1, 2]) + + a, *b, c = *x + [a, b, c].should == [1, [], 2] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("multi-assign splat") + x.should_receive(:to_a).and_return(1) + + lambda { a, *b, c = *x }.should raise_error(TypeError) + end + + it "does not call #to_ary to convert an Object RHS with a MLHS" do + x = mock("multi-assign splat") + x.should_not_receive(:to_ary) + + a, *b = *x + [a, b].should == [x, []] + end + + it "assigns a grouped LHS without splats" do + ((a, b), c), (d, (e,), (f, (g, h))) = *1 + [a, b, c, d, e, f, g, h].should == [1, nil, nil, nil, nil, nil, nil, nil] + end + + it "assigns a grouped LHS with splats" do + (a, *b), c, (*d, (e, *f, g)) = *1 + [a, b, c, d, e, f, g].should == [1, [], nil, [], nil, [], nil] + end + + it "consumes values for an anonymous splat" do + (* = *1).should == [1] + end + + it "consumes values for a grouped anonymous splat" do + ((*) = *1).should == [1] + end + + it "does not mutate a RHS Array" do + x = [1, 2, 3, 4] + a, *b, c, d = *x + [a, b, c, d].should == [1, [2], 3, 4] + x.should == [1, 2, 3, 4] + end + + it "assigns constants" do + module VariableSpecs + (*SINGLE_SPLATTED_RHS) = *1 + SINGLE_SPLATTED_RHS.should == [1] + end + end + end + + context "with a MRHS value" do + it "consumes values for an anonymous splat" do + (* = 1, 2, 3).should == [1, 2, 3] + end + + it "consumes values for multiple '_' variables" do + a, _, b, _, c = 1, 2, 3, 4, 5 + [a, b, c].should == [1, 3, 5] + end + + it "does not call #to_a to convert an Object in a MRHS" do + x = mock("multi-assign MRHS") + x.should_not_receive(:to_a) + + (a, b = 1, x).should == [1, x] + [a, b].should == [1, x] + end + + it "does not call #to_ary to convert an Object in a MRHS" do + x = mock("multi-assign MRHS") + x.should_not_receive(:to_ary) + + (a, b = 1, x).should == [1, x] + [a, b].should == [1, x] + end + + it "calls #to_a to convert a splatted Object as part of a MRHS with a splat MLHS" do + x = mock("multi-assign splat MRHS") + x.should_receive(:to_a).and_return([3, 4]) + + (a, *b = 1, *x).should == [1, 3, 4] + [a, b].should == [1, [3, 4]] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("multi-assign splat MRHS") + x.should_receive(:to_a).and_return(1) + + lambda { a, *b = 1, *x }.should raise_error(TypeError) + end + + it "does not call #to_ary to convert a splatted Object as part of a MRHS with a splat MRHS" do + x = mock("multi-assign splat MRHS") + x.should_not_receive(:to_ary) + + (a, *b = 1, *x).should == [1, x] + [a, b].should == [1, [x]] + end + + it "calls #to_a to convert a splatted Object as part of a MRHS with a splat MLHS" do + x = mock("multi-assign splat MRHS") + x.should_receive(:to_a).and_return([3, 4]) + + (a, *b = *x, 1).should == [3, 4, 1] + [a, b].should == [3, [4, 1]] + end + + it "raises a TypeError if #to_a does not return an Array" do + x = mock("multi-assign splat MRHS") + x.should_receive(:to_a).and_return(1) + + lambda { a, *b = *x, 1 }.should raise_error(TypeError) + end + + it "does not call #to_ary to convert a splatted Object as part of a MRHS with a splat MRHS" do + x = mock("multi-assign splat MRHS") + x.should_not_receive(:to_ary) + + (a, *b = *x, 1).should == [x, 1] + [a, b].should == [x, [1]] + end + + it "assigns a grouped LHS without splat from a simple Array" do + ((a, b), c), (d, (e,), (f, (g, h))) = 1, 2, 3, 4, 5 + [a, b, c, d, e, f, g, h].should == [1, nil, nil, 2, nil, nil, nil, nil] + end + + it "assigns a grouped LHS without splat from nested Arrays" do + ary = [[1, 2, 3], 4], [[5], [6, 7], [8, [9, 10]]] + ((a, b), c), (d, (e,), (f, (g, h))) = ary + [a, b, c, d, e, f, g, h].should == [1, 2, 4, [5], 6, 8, 9, 10] + end + + it "assigns a single grouped LHS splat" do + (*a) = 1, 2, 3 + a.should == [1, 2, 3] + end + + it "assigns a grouped LHS with splats from nested Arrays" do + (a, *b), c, (*d, (e, *f, g)) = 1, 2, 3, 4 + [a, b, c, d, e, f, g].should == [1, [], 2, [], 3, [], nil] + end + + it "assigns a grouped LHS with splats from nested Arrays" do + (a, *b), c, (*d, (e, *f, g)) = [1, [2, 3]], [4, 5], [6, 7, 8] + [a, b, c, d, e, f, g].should == [1, [[2, 3]], [4, 5], [6, 7], 8, [], nil] + end + + it "consumes values for an anonymous splat" do + (* = 1, 2, 3).should == [1, 2, 3] + end + + it "consumes values for a grouped anonymous splat" do + ((*) = 1, 2, 3).should == [1, 2, 3] + end + + it "calls #to_ary to convert an Object when the position receiving the value is a multiple assignment" do + x = mock("multi-assign mixed RHS") + x.should_receive(:to_ary).and_return([1, 2]) + + (a, (b, c), d, e = 1, x, 3, 4).should == [1, x, 3, 4] + [a, b, c, d, e].should == [1, 1, 2, 3, 4] + end + + it "raises a TypeError if #to_ary does not return an Array" do + x = mock("multi-assign mixed RHS") + x.should_receive(:to_ary).and_return(x) + + lambda { a, (b, c), d = 1, x, 3, 4 }.should raise_error(TypeError) + end + + it "calls #to_a to convert a splatted Object value in a MRHS" do + x = mock("multi-assign mixed splatted RHS") + x.should_receive(:to_a).and_return([4, 5]) + + (a, *b, (c, d) = 1, 2, 3, *x).should == [1, 2, 3, 4, 5] + [a, b, c, d].should == [1, [2, 3, 4], 5, nil] + + end + + it "calls #to_ary to convert a splatted Object when the position receiving the value is a multiple assignment" do + x = mock("multi-assign mixed splatted RHS") + x.should_receive(:to_ary).and_return([4, 5]) + + (a, *b, (c, d) = 1, 2, 3, *x).should == [1, 2, 3, x] + [a, b, c, d].should == [1, [2, 3], 4, 5] + end + + it "raises a TypeError if #to_ary does not return an Array" do + x = mock("multi-assign mixed splatted RHS") + x.should_receive(:to_ary).and_return(x) + + lambda { a, *b, (c, d) = 1, 2, 3, *x }.should raise_error(TypeError) + end + + it "does not call #to_ary to convert an Object when the position receiving the value is a simple variable" do + x = mock("multi-assign mixed RHS") + x.should_not_receive(:to_ary) + + a, b, c, d = 1, x, 3, 4 + [a, b, c, d].should == [1, x, 3, 4] + end + + it "does not call #to_ary to convert an Object when the position receiving the value is a rest variable" do + x = mock("multi-assign mixed RHS") + x.should_not_receive(:to_ary) + + a, *b, c, d = 1, x, 3, 4 + [a, b, c, d].should == [1, [x], 3, 4] + end + + it "does not call #to_ary to convert a splatted Object when the position receiving the value is a simple variable" do + x = mock("multi-assign mixed splatted RHS") + x.should_not_receive(:to_ary) + + a, *b, c = 1, 2, *x + [a, b, c].should == [1, [2], x] + end + + it "does not call #to_ary to convert a splatted Object when the position receiving the value is a rest variable" do + x = mock("multi-assign mixed splatted RHS") + x.should_not_receive(:to_ary) + + a, b, *c = 1, 2, *x + [a, b, c].should == [1, 2, [x]] + end + + it "does not mutate the assigned Array" do + x = ((a, *b, c, d) = 1, 2, 3, 4, 5) + x.should == [1, 2, 3, 4, 5] + end + + it "assigns RHS values to LHS constants" do + module VariableSpecs + MRHS_VALUES_1, MRHS_VALUES_2 = 1, 2 + MRHS_VALUES_1.should == 1 + MRHS_VALUES_2.should == 2 + end + end + + it "assigns all RHS values as an array to a single LHS constant" do + module VariableSpecs + MRHS_VALUES = 1, 2, 3 + MRHS_VALUES.should == [1, 2, 3] + end + end + end + + context "with a RHS assignment value" do + it "consumes values for an anonymous splat" do + (* = (a = 1)).should == 1 + a.should == 1 + end + + it "does not mutate a RHS Array" do + a, *b, c, d = (e = [1, 2, 3, 4]) + [a, b, c, d].should == [1, [2], 3, 4] + e.should == [1, 2, 3, 4] + end + end +end + +describe "A local variable assigned only within a conditional block" do + context "accessed from a later closure" do + it "is defined?" do + if VariablesSpecs.false + a = 1 + end + + 1.times do + defined?(a).should == "local-variable" + end + end + + it "is nil" do + if VariablesSpecs.false + a = 1 + end + + 1.times do + a.inspect.should == "nil" + end + end + end +end diff --git a/spec/rubyspec/language/while_spec.rb b/spec/rubyspec/language/while_spec.rb new file mode 100644 index 0000000000..a498112919 --- /dev/null +++ b/spec/rubyspec/language/while_spec.rb @@ -0,0 +1,344 @@ +require File.expand_path('../../spec_helper', __FILE__) + +# while bool-expr [do] +# body +# end +# +# begin +# body +# end while bool-expr +# +# expr while bool-expr +describe "The while expression" do + it "runs while the expression is true" do + i = 0 + while i < 3 + i += 1 + end + i.should == 3 + end + + it "optionally takes a 'do' after the expression" do + i = 0 + while i < 3 do + i += 1 + end + + i.should == 3 + end + + it "allows body begin on the same line if do is used" do + i = 0 + while i < 3 do i += 1 + end + + i.should == 3 + end + + it "executes code in containing variable scope" do + i = 0 + while i != 1 + a = 123 + i = 1 + end + + a.should == 123 + end + + it "executes code in containing variable scope with 'do'" do + i = 0 + while i != 1 do + a = 123 + i = 1 + end + + a.should == 123 + end + + it "returns nil if ended when condition became false" do + i = 0 + while i < 3 + i += 1 + end.should == nil + end + + it "does not evaluate the body if expression is empty" do + a = [] + while () + a << :body_evaluated + end + a.should == [] + end + + it "stops running body if interrupted by break" do + i = 0 + while i < 10 + i += 1 + break if i > 5 + end + i.should == 6 + end + + it "stops running body if interrupted by break in a parenthesized element op-assign-or value" do + c = true + a = [] + while c + a[1] ||= + ( + break if c + c = false + ) + end.should be_nil + end + + it "stops running body if interrupted by break in a begin ... end element op-assign-or value" do + c = true + a = [] + while c + a[1] ||= begin + break if c + c = false + end + end.should be_nil + end + + it "stops running body if interrupted by break in a parenthesized element op-assign value" do + c = true + a = [1, 2] + while c + a[1] += + ( + break if c + c = false + ) + end.should be_nil + a.should == [1, 2] + end + + it "stops running body if interrupted by break in a begin ... end element op-assign value" do + c = true + a = [1, 2] + while c + a[1] += begin + break if c + c = false + end + end.should be_nil + a.should == [1, 2] + end + + it "stops running body if interrupted by break in a parenthesized attribute op-assign-or value" do + a = mock("attribute assignment break") + a.should_receive(:m).twice.and_return(nil) + a.should_receive(:m=) + + c = d = true + while c + a.m ||= + ( + break unless d + d = false + ) + end.should be_nil + end + + it "stops running body if interrupted by break in a begin ... end attribute op-assign-or value" do + a = mock("attribute assignment break") + a.should_receive(:m).twice.and_return(nil) + a.should_receive(:m=) + + c = d = true + while c + a.m ||= begin + break unless d + d = false + end + end.should be_nil + end + + it "stops running body if interrupted by break in a parenthesized attribute op-assign-or value" do + a = mock("attribute assignment break") + a.should_receive(:m).and_return(nil) + a.should_not_receive(:m=) + + c = true + while c + a.m += + ( + break if c + c = false + ) + end.should be_nil + end + + it "stops running body if interrupted by break in a begin ... end attribute op-assign-or value" do + a = mock("attribute assignment break") + a.should_receive(:m).and_return(nil) + a.should_not_receive(:m=) + + c = true + while c + a.m += begin + break if c + c = false + end + end.should be_nil + end + + it "returns value passed to break if interrupted by break" do + while true + break 123 + end.should == 123 + end + + it "returns nil if interrupted by break with no arguments" do + while true + break + end.should == nil + end + + it "skips to end of body with next" do + a = [] + i = 0 + while (i+=1)<5 + next if i==3 + a << i + end + a.should == [1, 2, 4] + end + + it "restarts the current iteration without reevaluating condition with redo" do + a = [] + i = 0 + j = 0 + while (i+=1)<3 + a << i + j+=1 + redo if j<3 + end + a.should == [1, 1, 1, 2] + end +end + +describe "The while modifier" do + it "runs preceding statement while the condition is true" do + i = 0 + i += 1 while i < 3 + i.should == 3 + end + + it "evaluates condition before statement execution" do + a = [] + i = 0 + a << i while (i+=1) < 3 + a.should == [1, 2] + end + + it "does not run preceding statement if the condition is false" do + i = 0 + i += 1 while false + i.should == 0 + end + + it "does not run preceding statement if the condition is empty" do + i = 0 + i += 1 while () + i.should == 0 + end + + it "returns nil if ended when condition became false" do + i = 0 + (i += 1 while i<10).should == nil + end + + it "returns value passed to break if interrupted by break" do + (break 123 while true).should == 123 + end + + it "returns nil if interrupted by break with no arguments" do + (break while true).should == nil + end + + it "skips to end of body with next" do + i = 0 + j = 0 + ((i+=1) == 3 ? next : j+=i) while i <= 10 + j.should == 63 + end + + it "restarts the current iteration without reevaluating condition with redo" do + i = 0 + j = 0 + (i+=1) == 4 ? redo : j+=i while (i+=1) <= 10 + j.should == 34 + end +end + +describe "The while modifier with begin .. end block" do + it "runs block while the expression is true" do + i = 0 + begin + i += 1 + end while i < 3 + + i.should == 3 + end + + it "stops running block if interrupted by break" do + i = 0 + begin + i += 1 + break if i > 5 + end while i < 10 + + i.should == 6 + end + + it "returns value passed to break if interrupted by break" do + (begin; break 123; end while true).should == 123 + end + + it "returns nil if interrupted by break with no arguments" do + (begin; break; end while true).should == nil + end + + it "runs block at least once (even if the expression is false)" do + i = 0 + begin + i += 1 + end while false + + i.should == 1 + end + + it "evaluates condition after block execution" do + a = [] + i = 0 + begin + a << i + end while (i+=1)<5 + a.should == [0, 1, 2, 3, 4] + end + + it "skips to end of body with next" do + a = [] + i = 0 + begin + next if i==3 + a << i + end while (i+=1)<5 + a.should == [0, 1, 2, 4] + end + + it "restarts the current iteration without reevaluting condition with redo" do + a = [] + i = 0 + j = 0 + begin + a << i + j+=1 + redo if j<3 + end while (i+=1)<3 + a.should == [0, 0, 0, 1, 2] + end +end diff --git a/spec/rubyspec/language/yield_spec.rb b/spec/rubyspec/language/yield_spec.rb new file mode 100644 index 0000000000..663110cbe6 --- /dev/null +++ b/spec/rubyspec/language/yield_spec.rb @@ -0,0 +1,179 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../fixtures/yield', __FILE__) + +# Note that these specs use blocks defined as { |*a| ... } to capture the +# arguments with which the block is invoked. This is slightly confusing +# because the outer Array is a consequence of |*a| but it is necessary to +# clearly distinguish some behaviors. + +describe "The yield call" do + before :each do + @y = YieldSpecs::Yielder.new + end + + describe "taking no arguments" do + it "raises a LocalJumpError when the method is not passed a block" do + lambda { @y.z }.should raise_error(LocalJumpError) + end + + it "ignores assignment to the explicit block argument and calls the passed block" do + @y.ze { 42 }.should == 42 + end + end + + describe "taking a single argument" do + describe "when no block is given" do + it "raises a LocalJumpError" do + lambda { @y.s(1) }.should raise_error(LocalJumpError) + end + end + + describe "yielding to a literal block" do + it "passes an empty Array when the argument is an empty Array" do + @y.s([]) { |*a| a }.should == [[]] + end + + it "passes nil as a value" do + @y.s(nil) { |*a| a }.should == [nil] + end + + it "passes a single value" do + @y.s(1) { |*a| a }.should == [1] + end + + it "passes a single, multi-value Array" do + @y.s([1, 2, 3]) { |*a| a }.should == [[1, 2, 3]] + end + end + + describe "yielding to a lambda" do + it "passes an empty Array when the argument is an empty Array" do + @y.s([], &lambda { |*a| a }).should == [[]] + end + + it "passes nil as a value" do + @y.s(nil, &lambda { |*a| a }).should == [nil] + end + + it "passes a single value" do + @y.s(1, &lambda { |*a| a }).should == [1] + end + + it "passes a single, multi-value Array" do + @y.s([1, 2, 3], &lambda { |*a| a }).should == [[1, 2, 3]] + end + + it "raises an ArgumentError if too few arguments are passed" do + lambda { + @y.s(1, &lambda { |a,b| [a,b] }) + }.should raise_error(ArgumentError) + end + + ruby_bug "#12705", "2.2"..."2.5" do + it "should not destructure an Array into multiple arguments" do + lambda { + @y.s([1, 2], &lambda { |a,b| [a,b] }) + }.should raise_error(ArgumentError) + end + end + end + end + + describe "taking multiple arguments" do + it "raises a LocalJumpError when the method is not passed a block" do + lambda { @y.m(1, 2, 3) }.should raise_error(LocalJumpError) + end + + it "passes the arguments to the block" do + @y.m(1, 2, 3) { |*a| a }.should == [1, 2, 3] + end + + it "passes only the first argument if the block takes one parameter" do + @y.m(1, 2, 3) { |a| a }.should == 1 + end + + it "raises an ArgumentError if too many arguments are passed to a lambda" do + lambda { + @y.m(1, 2, 3, &lambda { |a| }) + }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if too few arguments are passed to a lambda" do + lambda { + @y.m(1, 2, 3, &lambda { |a,b,c,d| }) + }.should raise_error(ArgumentError) + end + end + + describe "taking a single splatted argument" do + it "raises a LocalJumpError when the method is not passed a block" do + lambda { @y.r(0) }.should raise_error(LocalJumpError) + end + + it "passes a single value" do + @y.r(1) { |*a| a }.should == [1] + end + + it "passes no arguments when the argument is an empty Array" do + @y.r([]) { |*a| a }.should == [] + end + + it "passes the value when the argument is an Array containing a single value" do + @y.r([1]) { |*a| a }.should == [1] + end + + it "passes the values of the Array as individual arguments" do + @y.r([1, 2, 3]) { |*a| a }.should == [1, 2, 3] + end + + it "passes the element of a single element Array" do + @y.r([[1, 2]]) { |*a| a }.should == [[1, 2]] + @y.r([nil]) { |*a| a }.should == [nil] + @y.r([[]]) { |*a| a }.should == [[]] + end + + it "passes no values when give nil as an argument" do + @y.r(nil) { |*a| a }.should == [] + end + end + + describe "taking multiple arguments with a splat" do + it "raises a LocalJumpError when the method is not passed a block" do + lambda { @y.rs(1, 2, [3, 4]) }.should raise_error(LocalJumpError) + end + + it "passes the arguments to the block" do + @y.rs(1, 2, 3) { |*a| a }.should == [1, 2, 3] + end + + it "does not pass an argument value if the splatted argument is an empty Array" do + @y.rs(1, 2, []) { |*a| a }.should == [1, 2] + end + + it "passes the Array elements as arguments if the splatted argument is a non-empty Array" do + @y.rs(1, 2, [3]) { |*a| a }.should == [1, 2, 3] + @y.rs(1, 2, [nil]) { |*a| a }.should == [1, 2, nil] + @y.rs(1, 2, [[]]) { |*a| a }.should == [1, 2, []] + @y.rs(1, 2, [3, 4, 5]) { |*a| a }.should == [1, 2, 3, 4, 5] + end + + it "does not pass an argument value if the splatted argument is nil" do + @y.rs(1, 2, nil) { |*a| a }.should == [1, 2] + end + end + + describe "taking matching arguments with splats and post args" do + it "raises a LocalJumpError when the method is not passed a block" do + lambda { @y.rs(1, 2, [3, 4]) }.should raise_error(LocalJumpError) + end + + it "passes the arguments to the block" do + @y.rs([1, 2], 3, 4) { |(*a, b), c, d| [a, b, c, d] }.should == [[1], 2, 3, 4] + end + end + + it "uses captured block of a block used in define_method" do + @y.deep(2).should == 4 + end + +end diff --git a/spec/rubyspec/library/English/English_spec.rb b/spec/rubyspec/library/English/English_spec.rb new file mode 100644 index 0000000000..75a336eba2 --- /dev/null +++ b/spec/rubyspec/library/English/English_spec.rb @@ -0,0 +1,171 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'English' + +describe "English" do + it "aliases $ERROR_INFO to $!" do + begin + raise "error" + rescue + $ERROR_INFO.should_not be_nil + $ERROR_INFO.should == $! + end + $ERROR_INFO.should be_nil + end + + it "aliases $ERROR_POSITION to $@" do + begin + raise "error" + rescue + $ERROR_POSITION.should_not be_nil + $ERROR_POSITION.should == $@ + end + $ERROR_POSITION.should be_nil + end + + it "aliases $FS to $;" do + original = $; + $; = "," + $FS.should_not be_nil + $FS.should == $; + $; = original + end + + it "aliases $FIELD_SEPARATOR to $;" do + original = $; + $; = "," + $FIELD_SEPARATOR.should_not be_nil + $FIELD_SEPARATOR.should == $; + $; = original + end + + it "aliases $OFS to $," do + original = $, + $, = "|" + $OFS.should_not be_nil + $OFS.should == $, + $, = original + end + + it "aliases $OUTPUT_FIELD_SEPARATOR to $," do + original = $, + $, = "|" + $OUTPUT_FIELD_SEPARATOR.should_not be_nil + $OUTPUT_FIELD_SEPARATOR.should == $, + $, = original + end + + it "aliases $RS to $/" do + $RS.should_not be_nil + $RS.should == $/ + end + + it "aliases $INPUT_RECORD_SEPARATOR to $/" do + $INPUT_RECORD_SEPARATOR.should_not be_nil + $INPUT_RECORD_SEPARATOR.should == $/ + end + + it "aliases $ORS to $\\" do + original = $\ + $\ = "\t" + $ORS.should_not be_nil + $ORS.should == $\ + $\ = original + end + + it "aliases $OUTPUT_RECORD_SEPARATOR to $\\" do + original = $\ + $\ = "\t" + $OUTPUT_RECORD_SEPARATOR.should_not be_nil + $OUTPUT_RECORD_SEPARATOR.should == $\ + $\ = original + end + + it "aliases $INPUT_LINE_NUMBER to $." do + $INPUT_LINE_NUMBER.should_not be_nil + $INPUT_LINE_NUMBER.should == $. + end + + it "aliases $NR to $." do + $NR.should_not be_nil + $NR.should == $. + end + + it "aliases $LAST_READ_LINE to $_ needs to be reviewed for spec completeness" + + it "aliases $DEFAULT_OUTPUT to $>" do + $DEFAULT_OUTPUT.should_not be_nil + $DEFAULT_OUTPUT.should == $> + end + + it "aliases $DEFAULT_INPUT to $<" do + $DEFAULT_INPUT.should_not be_nil + $DEFAULT_INPUT.should == $< + end + + it "aliases $PID to $$" do + $PID.should_not be_nil + $PID.should == $$ + end + + it "aliases $PID to $$" do + $PID.should_not be_nil + $PID.should == $$ + end + + it "aliases $PROCESS_ID to $$" do + $PROCESS_ID.should_not be_nil + $PROCESS_ID.should == $$ + end + + it "aliases $CHILD_STATUS to $?" do + ruby_exe('exit 0') + $CHILD_STATUS.should_not be_nil + $CHILD_STATUS.should == $? + end + + it "aliases $LAST_MATCH_INFO to $~" do + /c(a)t/ =~ "cat" + $LAST_MATCH_INFO.should_not be_nil + $LAST_MATCH_INFO.should == $~ + end + + it "aliases $IGNORECASE to $=" do + $VERBOSE, verbose = nil, $VERBOSE + begin + $IGNORECASE.should_not be_nil + $IGNORECASE.should == $= + ensure + $VERBOSE = verbose + end + end + + it "aliases $ARGV to $*" do + $ARGV.should_not be_nil + $ARGV.should == $* + end + + it "aliases $MATCH to $&" do + /c(a)t/ =~ "cat" + $MATCH.should_not be_nil + $MATCH.should == $& + end + + it "aliases $PREMATCH to $`" do + /c(a)t/ =~ "cat" + $PREMATCH.should_not be_nil + $PREMATCH.should == $` + end + + it "aliases $POSTMATCH to $'" do + /c(a)t/ =~ "cat" + $POSTMATCH.should_not be_nil + $POSTMATCH.should == $' + end + + it "aliases $LAST_PAREN_MATCH to $+" do + /c(a)t/ =~ "cat" + $LAST_PAREN_MATCH.should_not be_nil + $LAST_PAREN_MATCH.should == $+ + end +end diff --git a/spec/rubyspec/library/abbrev/abbrev_spec.rb b/spec/rubyspec/library/abbrev/abbrev_spec.rb new file mode 100644 index 0000000000..b2321cc679 --- /dev/null +++ b/spec/rubyspec/library/abbrev/abbrev_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'abbrev' + +#test both Abbrev.abbrev and Array#abbrev in +#the same manner, as they're more or less aliases +#of one another + +[["Abbrev.abbrev", lambda {|a| Abbrev.abbrev(a)}], + ["Array#abbrev", lambda {|a| a.abbrev}] +].each do |(name, func)| + + describe name do + it "returns a hash of all unambiguous abbreviations of the array of strings passed in" do + func.call(['ruby', 'rules']).should == {"rub" => "ruby", + "ruby" => "ruby", + "rul" => "rules", + "rule" => "rules", + "rules" => "rules"} + + func.call(["car", "cone"]).should == {"ca" => "car", + "car" => "car", + "co" => "cone", + "con" => "cone", + "cone" => "cone"} + end + + it "returns an empty hash when called on an empty array" do + func.call([]).should == {} + end + end +end diff --git a/spec/rubyspec/library/base64/decode64_spec.rb b/spec/rubyspec/library/base64/decode64_spec.rb new file mode 100644 index 0000000000..34d5ed6989 --- /dev/null +++ b/spec/rubyspec/library/base64/decode64_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'base64' + +describe "Base64#decode64" do + it "returns the Base64-decoded version of the given string" do + Base64.decode64("U2VuZCByZWluZm9yY2VtZW50cw==\n").should == "Send reinforcements" + end +end diff --git a/spec/rubyspec/library/base64/encode64_spec.rb b/spec/rubyspec/library/base64/encode64_spec.rb new file mode 100644 index 0000000000..08df694e54 --- /dev/null +++ b/spec/rubyspec/library/base64/encode64_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'base64' + +describe "Base64#encode64" do + it "returns the Base64-encoded version of the given string" do + Base64.encode64("Now is the time for all good coders\nto learn Ruby").should == + "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g\nUnVieQ==\n" + end + + it "returns the Base64-encoded version of the given string" do + Base64.encode64('Send reinforcements').should == "U2VuZCByZWluZm9yY2VtZW50cw==\n" + end +end diff --git a/spec/rubyspec/library/base64/urlsafe_decode64_spec.rb b/spec/rubyspec/library/base64/urlsafe_decode64_spec.rb new file mode 100644 index 0000000000..a3347db7d7 --- /dev/null +++ b/spec/rubyspec/library/base64/urlsafe_decode64_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'base64' + +describe "Base64#urlsafe_decode64" do + it "uses '_' instead of '/'" do + decoded = Base64.urlsafe_decode64("V2hlcmUgYW0gST8gV2hvIGFtIEk_IEFtIEk_IEk_") + decoded.should == 'Where am I? Who am I? Am I? I?' + end + + it "uses '-' instead of '+'" do + decoded = Base64.urlsafe_decode64('IkJlaW5nIGRpc2ludGVncmF0ZWQgbWFrZXMgbWUgdmUtcnkgYW4tZ3J5ISIgPGh1ZmYsIGh1ZmY-') + decoded.should == '"Being disintegrated makes me ve-ry an-gry!" ' + end + + ruby_version_is ""..."2.3" do + it "requires padding" do + lambda { Base64.urlsafe_decode64("MQ") }.should raise_error(ArgumentError) + end + end + + ruby_version_is "2.3" do + it "does not require padding" do + Base64.urlsafe_decode64("MQ").should == "1" + end + end +end diff --git a/spec/rubyspec/library/base64/urlsafe_encode64_spec.rb b/spec/rubyspec/library/base64/urlsafe_encode64_spec.rb new file mode 100644 index 0000000000..32fce72603 --- /dev/null +++ b/spec/rubyspec/library/base64/urlsafe_encode64_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'base64' + +describe "Base64#urlsafe_encode64" do + it "uses '_' instead of '/'" do + encoded = Base64.urlsafe_encode64('Where am I? Who am I? Am I? I?') + encoded.should == "V2hlcmUgYW0gST8gV2hvIGFtIEk_IEFtIEk_IEk_" + end + + it "uses '-' instead of '+'" do + encoded = Base64.urlsafe_encode64('"Being disintegrated makes me ve-ry an-gry!" ') + encoded.should == 'IkJlaW5nIGRpc2ludGVncmF0ZWQgbWFrZXMgbWUgdmUtcnkgYW4tZ3J5ISIgPGh1ZmYsIGh1ZmY-' + end + + ruby_version_is "2.3" do + it "makes padding optional" do + Base64.urlsafe_encode64("1", padding: false).should == "MQ" + Base64.urlsafe_encode64("1").should == "MQ==" + end + end +end diff --git a/spec/rubyspec/library/bigdecimal/abs_spec.rb b/spec/rubyspec/library/bigdecimal/abs_spec.rb new file mode 100644 index 0000000000..9027abfb47 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/abs_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#abs" do + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @mixed = BigDecimal("1.23456789") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "returns the absolute value" do + pos_int = BigDecimal("2E5555") + neg_int = BigDecimal("-2E5555") + pos_frac = BigDecimal("2E-9999") + neg_frac = BigDecimal("-2E-9999") + + pos_int.abs.should == pos_int + neg_int.abs.should == pos_int + pos_frac.abs.should == pos_frac + neg_frac.abs.should == pos_frac + @one.abs.should == 1 + @two.abs.should == 2 + @three.abs.should == 3 + @mixed.abs.should == @mixed + @one_minus.abs.should == @one + end + + it "properly handles special values" do + @infinity.abs.should == @infinity + @infinity_minus.abs.should == @infinity + @nan.abs.nan?.should == true # have to do it this way, since == doesn't work on NaN + @zero.abs.should == 0 + @zero.abs.sign.should == BigDecimal::SIGN_POSITIVE_ZERO + @zero_pos.abs.should == 0 + @zero_pos.abs.sign.should == BigDecimal::SIGN_POSITIVE_ZERO + @zero_neg.abs.should == 0 + @zero_neg.abs.sign.should == BigDecimal::SIGN_POSITIVE_ZERO + end + +end diff --git a/spec/rubyspec/library/bigdecimal/add_spec.rb b/spec/rubyspec/library/bigdecimal/add_spec.rb new file mode 100644 index 0000000000..6136c9dccb --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/add_spec.rb @@ -0,0 +1,179 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +require 'bigdecimal' + +describe "BigDecimal#add" do + + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @ten = BigDecimal("10") + @eleven = BigDecimal("11") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + @frac_3 = BigDecimal("12345E10") + @frac_4 = BigDecimal("98765E10") + @dot_ones = BigDecimal("0.1111111111") + end + + it "returns a + b with given precision" do + # documentation states, that precision ist optional, but it ain't, + @two.add(@one, 1).should == @three + @one .add(@two, 1).should == @three + @one.add(@one_minus, 1).should == @zero + @ten.add(@one, 2).should == @eleven + @zero.add(@one, 1).should == @one + @frac_2.add(@frac_1, 10000).should == BigDecimal("1.9E-99999") + @frac_1.add(@frac_1, 10000).should == BigDecimal("2E-99999") + @frac_3.add(@frac_4, 0).should == BigDecimal("0.11111E16") + @frac_3.add(@frac_4, 1).should == BigDecimal("0.1E16") + @frac_3.add(@frac_4, 2).should == BigDecimal("0.11E16") + @frac_3.add(@frac_4, 3).should == BigDecimal("0.111E16") + @frac_3.add(@frac_4, 4).should == BigDecimal("0.1111E16") + @frac_3.add(@frac_4, 5).should == BigDecimal("0.11111E16") + @frac_3.add(@frac_4, 6).should == BigDecimal("0.11111E16") + end + + it "returns a + [Fixnum value] with given precision" do + (1..10).each {|precision| + @dot_ones.add(0, precision).should == BigDecimal("0." + "1" * precision) + } + BigDecimal("0.88").add(0, 1).should == BigDecimal("0.9") + end + + it "returns a + [Bignum value] with given precision" do + bignum = 10000000000000000000 + (1..20).each {|precision| + @dot_ones.add(bignum, precision).should == BigDecimal("0.1E20") + } + (21..30).each {|precision| + @dot_ones.add(bignum, precision).should == BigDecimal( + "0.10000000000000000000" + "1" * (precision - 20) + "E20") + } + end + +# TODO: +# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17374 +# +# This doesn't work on MRI and looks like a bug to me: +# one can use BigDecimal + Float, but not Bigdecimal.add(Float) +# +# it "returns a + [Float value] with given precision" do +# (1..10).each {|precision| +# @dot_ones.add(0.0, precision).should == BigDecimal("0." + "1" * precision) +# } +# +# BigDecimal("0.88").add(0.0, 1).should == BigDecimal("0.9") +# end + + it "favors the precision specified in the second argument over the global limit" do + BigDecimalSpecs.with_limit(1) do + BigDecimal('0.888').add(@zero, 3).should == BigDecimal('0.888') + end + + BigDecimalSpecs.with_limit(2) do + BigDecimal('0.888').add(@zero, 1).should == BigDecimal('0.9') + end + end + + it "uses the current rounding mode if rounding is needed" do + BigDecimalSpecs.with_rounding(BigDecimal::ROUND_UP) do + BigDecimal('0.111').add(@zero, 1).should == BigDecimal('0.2') + BigDecimal('-0.111').add(@zero, 1).should == BigDecimal('-0.2') + end + BigDecimalSpecs.with_rounding(BigDecimal::ROUND_DOWN) do + BigDecimal('0.999').add(@zero, 1).should == BigDecimal('0.9') + BigDecimal('-0.999').add(@zero, 1).should == BigDecimal('-0.9') + end + BigDecimalSpecs.with_rounding(BigDecimal::ROUND_HALF_UP) do + BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.9') + BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.9') + end + BigDecimalSpecs.with_rounding(BigDecimal::ROUND_HALF_DOWN) do + BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.8') + BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.8') + end + BigDecimalSpecs.with_rounding(BigDecimal::ROUND_HALF_EVEN) do + BigDecimal('0.75').add(@zero, 1).should == BigDecimal('0.8') + BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.8') + BigDecimal('-0.75').add(@zero, 1).should == BigDecimal('-0.8') + BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.8') + end + BigDecimalSpecs.with_rounding(BigDecimal::ROUND_CEILING) do + BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.9') + BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.8') + end + BigDecimalSpecs.with_rounding(BigDecimal::ROUND_FLOOR) do + BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.8') + BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.9') + end + end + + it "uses the default ROUND_HALF_UP rounding if it wasn't explicitly changed" do + BigDecimal('0.85').add(@zero, 1).should == BigDecimal('0.9') + BigDecimal('-0.85').add(@zero, 1).should == BigDecimal('-0.9') + end + + it "returns NaN if NaN is involved" do + @one.add(@nan, 10000).nan?.should == true + @nan.add(@one, 1).nan?.should == true + end + + it "returns Infinity or -Infinity if these are involved" do + @zero.add(@infinity, 1).should == @infinity + @frac_2.add(@infinity, 1).should == @infinity + @one_minus.add(@infinity, 1).should == @infinity + @two.add(@infinity, 1).should == @infinity + + @zero.add(@infinity_minus, 1).should == @infinity_minus + @frac_2.add(@infinity_minus, 1).should == @infinity_minus + @one_minus.add(@infinity_minus, 1).should == @infinity_minus + @two.add(@infinity_minus, 1).should == @infinity_minus + + @infinity.add(@zero, 1).should == @infinity + @infinity.add(@frac_2, 1).should == @infinity + @infinity.add(@one_minus, 1).should == @infinity + @infinity.add(@two, 1).should == @infinity + + @infinity_minus.add(@zero, 1).should == @infinity_minus + @infinity_minus.add(@frac_2, 1).should == @infinity_minus + @infinity_minus.add(@one_minus, 1).should == @infinity_minus + @infinity_minus.add(@two, 1).should == @infinity_minus + + @infinity.add(@infinity, 10000).should == @infinity + @infinity_minus.add(@infinity_minus, 10000).should == @infinity_minus + end + + it "returns NaN if Infinity + (- Infinity)" do + @infinity.add(@infinity_minus, 10000).nan?.should == true + @infinity_minus.add(@infinity, 10000).nan?.should == true + end + + it "raises TypeError when adds nil" do + lambda { + @one.add(nil, 10) + }.should raise_error(TypeError) + lambda { + @one.add(nil, 0) + }.should raise_error(TypeError) + end + + it "raises TypeError when precision parameter is nil" do + lambda { + @one.add(@one, nil) + }.should raise_error(TypeError) + end + + it "raises ArgumentError when precision parameter is negative" do + lambda { + @one.add(@one, -10) + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/case_compare_spec.rb b/spec/rubyspec/library/bigdecimal/case_compare_spec.rb new file mode 100644 index 0000000000..dcbde80e85 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/case_compare_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql.rb', __FILE__) + + +describe "BigDecimal#===" do + it_behaves_like(:bigdecimal_eql, :===) +end diff --git a/spec/rubyspec/library/bigdecimal/ceil_spec.rb b/spec/rubyspec/library/bigdecimal/ceil_spec.rb new file mode 100644 index 0000000000..d8829411b9 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/ceil_spec.rb @@ -0,0 +1,104 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#ceil" do + before :each do + @zero = BigDecimal("0") + @one = BigDecimal("1") + @three = BigDecimal("3") + @four = BigDecimal("4") + @mixed = BigDecimal("1.23456789") + @mixed_big = BigDecimal("1.23456789E100") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + end + + it "returns an Integer, if n is unspecified" do + @mixed.ceil.kind_of?(Integer).should == true + end + + it "returns a BigDecimal, if n is specified" do + @pos_int.ceil(2).kind_of?(BigDecimal).should == true + end + + it "returns the smallest integer greater or equal to self, if n is unspecified" do + @pos_int.ceil.should == @pos_int + @neg_int.ceil.should == @neg_int + @pos_frac.ceil.should == BigDecimal("1") + @neg_frac.ceil.should == @zero + @zero.ceil.should == 0 + @zero_pos.ceil.should == @zero_pos + @zero_neg.ceil.should == @zero_neg + + + BigDecimal('2.3').ceil.should == 3 + BigDecimal('2.5').ceil.should == 3 + BigDecimal('2.9999').ceil.should == 3 + BigDecimal('-2.3').ceil.should == -2 + BigDecimal('-2.5').ceil.should == -2 + BigDecimal('-2.9999').ceil.should == -2 + end + + it "raise exception, if self is special value" do + lambda { @infinity.ceil }.should raise_error(FloatDomainError) + lambda { @infinity_neg.ceil }.should raise_error(FloatDomainError) + lambda { @nan.ceil }.should raise_error(FloatDomainError) + end + + it "returns n digits right of the decimal point if given n > 0" do + @mixed.ceil(1).should == BigDecimal("1.3") + @mixed.ceil(5).should == BigDecimal("1.23457") + + BigDecimal("-0.03").ceil(1).should == BigDecimal("0") + BigDecimal("0.03").ceil(1).should == BigDecimal("0.1") + + BigDecimal("23.45").ceil(0).should == BigDecimal('24') + BigDecimal("23.45").ceil(1).should == BigDecimal('23.5') + BigDecimal("23.45").ceil(2).should == BigDecimal('23.45') + + BigDecimal("-23.45").ceil(0).should == BigDecimal('-23') + BigDecimal("-23.45").ceil(1).should == BigDecimal('-23.4') + BigDecimal("-23.45").ceil(2).should == BigDecimal('-23.45') + + BigDecimal("2E-10").ceil(0).should == @one + BigDecimal("2E-10").ceil(9).should == BigDecimal('1E-9') + BigDecimal("2E-10").ceil(10).should == BigDecimal('2E-10') + BigDecimal("2E-10").ceil(11).should == BigDecimal('2E-10') + + (1..10).each do |n| + # 0.4, 0.34, 0.334, etc. + (@one.div(@three,20)).ceil(n).should == BigDecimal("0.#{'3'*(n-1)}4") + # 1.4, 1.34, 1.334, etc. + (@four.div(@three,20)).ceil(n).should == BigDecimal("1.#{'3'*(n-1)}4") + (BigDecimal('31').div(@three,20)).ceil(n).should == BigDecimal("10.#{'3'*(n-1)}4") + end + (1..10).each do |n| + # -0.4, -0.34, -0.334, etc. + (-@one.div(@three,20)).ceil(n).should == BigDecimal("-0.#{'3'* n}") + end + (1..10).each do |n| + (@three.div(@one,20)).ceil(n).should == @three + end + (1..10).each do |n| + (-@three.div(@one,20)).ceil(n).should == -@three + end + end + + it "sets n digits left of the decimal point to 0, if given n < 0" do + BigDecimal("13345.234").ceil(-2).should == BigDecimal("13400.0") + @mixed_big.ceil(-99).should == BigDecimal("0.13E101") + @mixed_big.ceil(-100).should == BigDecimal("0.2E101") + @mixed_big.ceil(-95).should == BigDecimal("0.123457E101") + BigDecimal("1E10").ceil(-30).should == BigDecimal('1E30') + BigDecimal("-1E10").ceil(-30).should == @zero + end + +end diff --git a/spec/rubyspec/library/bigdecimal/coerce_spec.rb b/spec/rubyspec/library/bigdecimal/coerce_spec.rb new file mode 100644 index 0000000000..1c63ddf2bc --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/coerce_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#coerce" do + + it "returns [other, self] both as BigDecimal" do + one = BigDecimal("1.0") + five_point_28 = BigDecimal("5.28") + zero_minus = BigDecimal("-0.0") + some_value = 32434234234234234234 + + BigDecimal("1.2").coerce(1).should == [one, BigDecimal("1.2")] + five_point_28.coerce(1.0).should == [one, BigDecimal("5.28")] + one.coerce(one).should == [one, one] + one.coerce(2.5).should == [2.5, one] + BigDecimal("1").coerce(3.14).should == [3.14, one] + a, b = zero_minus.coerce(some_value) + a.should == BigDecimal(some_value.to_s) + b.should == zero_minus + a, b = one.coerce(some_value) + a.should == BigDecimal(some_value.to_s) + b.to_f.should be_close(1.0, TOLERANCE) # can we take out the to_f once BigDecimal#- is implemented? + b.should == one + end + +end diff --git a/spec/rubyspec/library/bigdecimal/comparison_spec.rb b/spec/rubyspec/library/bigdecimal/comparison_spec.rb new file mode 100644 index 0000000000..c4de8348b2 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/comparison_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#<=>" do + before :each do + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @mixed = BigDecimal("1.23456789") + @mixed_big = BigDecimal("1.23456789E100") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @int_mock = mock('123') + class << @int_mock + def coerce(other) + return [other, BigDecimal('123')] + end + def >= (other) + BigDecimal('123') >= other + end + end + + @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac, + -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1, + @zero , 1, 2, 10, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg] + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + end + + + it "returns 0 if a == b" do + (@pos_int <=> @pos_int).should == 0 + (@neg_int <=> @neg_int).should == 0 + (@pos_frac <=> @pos_frac).should == 0 + (@neg_frac <=> @neg_frac).should == 0 + (@zero <=> @zero).should == 0 + (@infinity <=> @infinity).should == 0 + (@infinity_neg <=> @infinity_neg).should == 0 + end + + it "returns 1 if a > b" do + (@pos_int <=> @neg_int).should == 1 + (@pos_frac <=> @neg_frac).should == 1 + (@pos_frac <=> @zero).should == 1 + @values.each { |val| + (@infinity <=> val).should == 1 + } + end + + it "returns -1 if a < b" do + (@zero <=> @pos_frac).should == -1 + (@neg_int <=> @pos_frac).should == -1 + (@pos_frac <=> @pos_int).should == -1 + @values.each { |val| + (@infinity_neg <=> val).should == -1 + } + end + + it "returns nil if NaN is involved" do + @values += [@infinity, @infinity_neg, @nan] + @values << nil + @values << Object.new + @values.each { |val| + (@nan <=> val).should == nil + } + end + + it "returns nil if the argument is nil" do + (@zero <=> nil).should == nil + (@infinity <=> nil).should == nil + (@infinity_neg <=> nil).should == nil + (@mixed <=> nil).should == nil + (@pos_int <=> nil).should == nil + (@neg_frac <=> nil).should == nil + end +end diff --git a/spec/rubyspec/library/bigdecimal/div_spec.rb b/spec/rubyspec/library/bigdecimal/div_spec.rb new file mode 100644 index 0000000000..f8f8ac4e5e --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/div_spec.rb @@ -0,0 +1,102 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quo', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#div with precision set to 0" do + # TODO: figure out if there is a better way to do these + # shared specs rather than sending [0]. See other specs + # that share :bigdecimal_quo. + it_behaves_like :bigdecimal_quo, :div, [0] +end + +describe "BigDecimal#div" do + + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_plus = BigDecimal("+0") + @zero_minus = BigDecimal("-0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "returns a / b with optional precision" do + @two.div(@one).should == @two + @one.div(@two).should == @zero + # ^^ is this really intended for a class with arbitrary precision? + @one.div(@two, 1).should == BigDecimal("0.5") + @one.div(@one_minus).should == @one_minus + @one_minus.div(@one_minus).should == @one + @frac_2.div(@frac_1, 1).should == BigDecimal("0.9") + @frac_1.div(@frac_1).should == @one + + res = "0." + "3" * 1000 + (1..100).each { |idx| + @one.div(@three, idx).to_s("F").should == "0." + res[2, idx] + } + end + + it "raises FloatDomainError if NaN is involved" do + lambda { @one.div(@nan) }.should raise_error(FloatDomainError) + lambda { @nan.div(@one) }.should raise_error(FloatDomainError) + lambda { @nan.div(@nan) }.should raise_error(FloatDomainError) + end + + it "returns 0 if divided by Infinity and no precision given" do + @zero.div(@infinity).should == 0 + @frac_2.div(@infinity).should == 0 + end + + it "returns 0 if divided by Infinity with given precision" do + @zero.div(@infinity, 0).should == 0 + @frac_2.div(@infinity, 1).should == 0 + @zero.div(@infinity, 100000).should == 0 + @frac_2.div(@infinity, 100000).should == 0 + end + + it "raises ZeroDivisionError if divided by zero and no precision given" do + lambda { @one.div(@zero) }.should raise_error(ZeroDivisionError) + lambda { @one.div(@zero_plus) }.should raise_error(ZeroDivisionError) + lambda { @one.div(@zero_minus) }.should raise_error(ZeroDivisionError) + + lambda { @zero.div(@zero) }.should raise_error(ZeroDivisionError) + lambda { @zero_minus.div(@zero_plus) }.should raise_error(ZeroDivisionError) + lambda { @zero_minus.div(@zero_minus) }.should raise_error(ZeroDivisionError) + lambda { @zero_plus.div(@zero_minus) }.should raise_error(ZeroDivisionError) + end + + it "returns NaN if zero is divided by zero" do + @zero.div(@zero, 0).nan?.should == true + @zero_minus.div(@zero_plus, 0).nan?.should == true + @zero_plus.div(@zero_minus, 0).nan?.should == true + + @zero.div(@zero, 10).nan?.should == true + @zero_minus.div(@zero_plus, 10).nan?.should == true + @zero_plus.div(@zero_minus, 10).nan?.should == true + end + + it "raises FloatDomainError if (+|-) Infinity divided by 1 and no precision given" do + lambda { @infinity_minus.div(@one) }.should raise_error(FloatDomainError) + lambda { @infinity.div(@one) }.should raise_error(FloatDomainError) + lambda { @infinity_minus.div(@one_minus) }.should raise_error(FloatDomainError) + end + + it "returns (+|-)Infinity if (+|-)Infinity by 1 and precision given" do + @infinity_minus.div(@one, 0).should == @infinity_minus + @infinity.div(@one, 0).should == @infinity + @infinity_minus.div(@one_minus, 0).should == @infinity + end + + it "returns NaN if Infinity / ((+|-) Infinity)" do + @infinity.div(@infinity_minus, 100000).nan?.should == true + @infinity_minus.div(@infinity, 1).nan?.should == true + end + + +end diff --git a/spec/rubyspec/library/bigdecimal/divide_spec.rb b/spec/rubyspec/library/bigdecimal/divide_spec.rb new file mode 100644 index 0000000000..4dfe2702bb --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/divide_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quo', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#/" do + it_behaves_like :bigdecimal_quo, :/, [] +end diff --git a/spec/rubyspec/library/bigdecimal/divmod_spec.rb b/spec/rubyspec/library/bigdecimal/divmod_spec.rb new file mode 100644 index 0000000000..839a289ab6 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/divmod_spec.rb @@ -0,0 +1,180 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/modulo', __FILE__) +require 'bigdecimal' + +module DivmodSpecs + def self.check_both_nan(array) + array.length.should == 2 + array[0].nan?.should == true + array[1].nan?.should == true + end + def self.check_both_bigdecimal(array) + array.length.should == 2 + array[0].kind_of?(BigDecimal).should == true + array[1].kind_of?(BigDecimal).should == true + end +end + +# TODO: figure out a way to do the shared specs with helpers instead +# of spec'ing a method that does not really exist +describe "BigDecimal#mod_part_of_divmod" do + # BigDecimal#divmod[1] behaves exactly like #modulo + before :all do + class BigDecimal + def mod_part_of_divmod(arg) + divmod(arg)[1] + end + end + end + + after :all do + class BigDecimal + undef mod_part_of_divmod + end + end + + it_behaves_like :bigdecimal_modulo, :mod_part_of_divmod + + it "raises ZeroDivisionError if other is zero" do + bd5667 = BigDecimal.new("5667.19") + + lambda { bd5667.send(@method, 0) }.should raise_error(ZeroDivisionError) + lambda { bd5667.send(@method, BigDecimal("0")) }.should raise_error(ZeroDivisionError) + lambda { @zero.send(@method, @zero) }.should raise_error(ZeroDivisionError) + end +end + +describe "BigDecimal#divmod" do + + before :each do + @a = BigDecimal("42.00000000000000000001") + + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + + @one = BigDecimal("1") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + + @special_vals = [@infinity, @infinity_minus, @nan] + @regular_vals = [ + @one, @mixed, @pos_int, @neg_int, @pos_frac, + @neg_frac, @one_minus, @frac_1, @frac_2] + @zeroes = [@zero, @zero_pos, @zero_neg] + end + + it "divides value, returns an array" do + res = @a.divmod(5) + res.kind_of?(Array).should == true + end + + it "array contains quotient and modulus as BigDecimal" do + res = @a.divmod(5) + DivmodSpecs.check_both_bigdecimal(res) + res[0].should == BigDecimal('0.8E1') + res[1].should == BigDecimal('2.00000000000000000001') + + BigDecimal('1').divmod(BigDecimal('2')).should == [0, 1] + BigDecimal('2').divmod(BigDecimal('1')).should == [2, 0] + + BigDecimal('1').divmod(BigDecimal('-2')).should == [-1, -1] + BigDecimal('2').divmod(BigDecimal('-1')).should == [-2, 0] + + BigDecimal('-1').divmod(BigDecimal('2')).should == [-1, 1] + BigDecimal('-2').divmod(BigDecimal('1')).should == [-2, 0] + end + + it "can be reversed with * and +" do + # Example taken from BigDecimal documentation + a = BigDecimal.new("42") + b = BigDecimal.new("9") + q, m = a.divmod(b) + c = q * b + m + a.should == c + + values = [@one, @one_minus, BigDecimal('2'), BigDecimal('-2'), + BigDecimal('5'), BigDecimal('-5'), BigDecimal('10'), BigDecimal('-10'), + BigDecimal('20'), BigDecimal('-20'), BigDecimal('100'), BigDecimal('-100'), + BigDecimal('1.23456789E10'), BigDecimal('-1.23456789E10') + ] + + # TODO: file MRI bug: + # BigDecimal('1').divmod(BigDecimal('3E-9'))[0] #=> 0.3E9, + # but really should be 0.333333333E9 + values << BigDecimal('1E-10') + values << BigDecimal('-1E-10') + values << BigDecimal('2E55') + values << BigDecimal('-2E55') + values << BigDecimal('2E-5555') + values << BigDecimal('-2E-5555') + + + values_and_zeroes = values + @zeroes + values_and_zeroes.each do |val1| + values.each do |val2| + res = val1.divmod(val2) + DivmodSpecs.check_both_bigdecimal(res) + res[0].should == ((val1/val2).floor) + res[1].should == (val1 - res[0] * val2) + end + end + end + + it "returns an array of two NaNs if NaN is involved" do + (@special_vals + @regular_vals + @zeroes).each do |val| + DivmodSpecs.check_both_nan(val.divmod(@nan)) + DivmodSpecs.check_both_nan(@nan.divmod(val)) + end + end + + it "raises ZeroDivisionError if the divisor is zero" do + (@special_vals + @regular_vals + @zeroes - [@nan]).each do |val| + @zeroes.each do |zero| + lambda { val.divmod(zero) }.should raise_error(ZeroDivisionError) + end + end + end + + it "returns an array of Infinity and NaN if the dividend is Infinity" do + @regular_vals.each do |val| + array = @infinity.divmod(val) + array.length.should == 2 + array[0].infinite?.should == (val > 0 ? 1 : -1) + array[1].nan?.should == true + end + end + + it "returns an array of zero and the dividend if the divisor is Infinity" do + @regular_vals.each do |val| + array = val.divmod(@infinity) + array.length.should == 2 + array[0].should == @zero + array[1].should == val + end + end + + it "returns an array of two zero if the diviend is zero" do + @zeroes.each do |zero| + @regular_vals.each do |val| + zero.divmod(val).should == [@zero, @zero] + end + end + end + + it "raises TypeError if the argument cannot be coerced to BigDecimal" do + lambda { + @one.divmod('1') + }.should raise_error(TypeError) + end + +end diff --git a/spec/rubyspec/library/bigdecimal/double_fig_spec.rb b/spec/rubyspec/library/bigdecimal/double_fig_spec.rb new file mode 100644 index 0000000000..3ab5c2f207 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/double_fig_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal.double_fig" do + # The result depends on the CPU and OS + it "returns the number of digits a Float number is allowed to have" do + BigDecimal.double_fig.should_not == nil + end +end diff --git a/spec/rubyspec/library/bigdecimal/eql_spec.rb b/spec/rubyspec/library/bigdecimal/eql_spec.rb new file mode 100644 index 0000000000..f3f525a7ba --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/eql_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql.rb', __FILE__) + +describe "BigDecimal#eql?" do + it_behaves_like(:bigdecimal_eql, :eql?) +end diff --git a/spec/rubyspec/library/bigdecimal/equal_value_spec.rb b/spec/rubyspec/library/bigdecimal/equal_value_spec.rb new file mode 100644 index 0000000000..bd07217b98 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/equal_value_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eql.rb', __FILE__) + + +describe "BigDecimal#==" do + it_behaves_like(:bigdecimal_eql, :==) +end diff --git a/spec/rubyspec/library/bigdecimal/exponent_spec.rb b/spec/rubyspec/library/bigdecimal/exponent_spec.rb new file mode 100644 index 0000000000..6bb678d4ed --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/exponent_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/power', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#**" do + it_behaves_like(:bigdecimal_power, :**) +end + +describe "BigDecimal#exponent" do + + it "returns an Integer" do + BigDecimal("2E100000000").exponent.kind_of?(Integer).should == true + BigDecimal("2E-999").exponent.kind_of?(Integer).should == true + end + + it "is n if number can be represented as 0.xxx*10**n" do + BigDecimal("2E1000").exponent.should == 1001 + BigDecimal("1234567E10").exponent.should == 17 + end + +# commenting this spec out after discussion with Defiler, since it seems to be an MRI bug, not a real feature +=begin + platform_is wordsize: 32 do + # TODO: write specs for both 32 and 64 bit + it "returns 0 if exponent can't be represented as Fixnum" do + BigDecimal("2E1000000000000000").exponent.should == 0 + BigDecimal("-5E-999999999999999").exponent.should == 0 + end + end +=end + + it "returns 0 if self is 0" do + BigDecimal("0").exponent.should == 0 + BigDecimal("+0").exponent.should == 0 + BigDecimal("-0").exponent.should == 0 + end + +end diff --git a/spec/rubyspec/library/bigdecimal/finite_spec.rb b/spec/rubyspec/library/bigdecimal/finite_spec.rb new file mode 100644 index 0000000000..6d6d8398fa --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/finite_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#finite?" do + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + @big = BigDecimal("2E40001") + @finite_vals = [@one, @zero, @zero_pos, @zero_neg, @two, + @three, @frac_1, @frac_2, @big, @one_minus] + end + + it "is false if Infinity or NaN" do + @infinity.finite?.should == false + @infinity_minus.finite?.should == false + @nan.finite?.should == false + end + + it "returns true for finite values" do + @finite_vals.each do |val| + val.finite?.should == true + end + end +end + diff --git a/spec/rubyspec/library/bigdecimal/fix_spec.rb b/spec/rubyspec/library/bigdecimal/fix_spec.rb new file mode 100644 index 0000000000..0a4a98c241 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/fix_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#fix" do + before :each do + @zero = BigDecimal("0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + end + + it "returns a BigDecimal" do + BigDecimal("2E100000000").fix.kind_of?(BigDecimal).should == true + BigDecimal("2E-999").kind_of?(BigDecimal).should == true + end + + it "returns the integer part of the absolute value" do + a = BigDecimal("2E1000") + a.fix.should == a + b = BigDecimal("-2E1000") + b.fix.should == b + BigDecimal("0.123456789E5").fix.should == BigDecimal("0.12345E5") + BigDecimal("-0.123456789E5").fix.should == BigDecimal("-0.12345E5") + end + + it "correctly handles special values" do + @infinity.fix.should == @infinity + @infinity_neg.fix.should == @infinity_neg + @nan.fix.nan?.should == true + end + + it "returns 0 if the absolute value is < 1" do + BigDecimal("0.99999").fix.should == 0 + BigDecimal("-0.99999").fix.should == 0 + BigDecimal("0.000000001").fix.should == 0 + BigDecimal("-0.00000001").fix.should == 0 + BigDecimal("-1000000").fix.should_not == 0 + @zero.fix.should == 0 + @zero_pos.fix.should == @zero_pos + @zero_neg.fix.should == @zero_neg + end + + it "does not allow any arguments" do + lambda { + @mixed.fix(10) + }.should raise_error(ArgumentError) + end + +end diff --git a/spec/rubyspec/library/bigdecimal/fixtures/classes.rb b/spec/rubyspec/library/bigdecimal/fixtures/classes.rb new file mode 100644 index 0000000000..06e4474cf0 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/fixtures/classes.rb @@ -0,0 +1,17 @@ +module BigDecimalSpecs + # helper method to sure that the global limit is reset back + def self.with_limit(l) + old = BigDecimal.limit(l) + yield + ensure + BigDecimal.limit(old) + end + + def self.with_rounding(r) + old = BigDecimal.mode(BigDecimal::ROUND_MODE) + BigDecimal.mode(BigDecimal::ROUND_MODE, r) + yield + ensure + BigDecimal.mode(BigDecimal::ROUND_MODE, old) + end +end diff --git a/spec/rubyspec/library/bigdecimal/floor_spec.rb b/spec/rubyspec/library/bigdecimal/floor_spec.rb new file mode 100644 index 0000000000..0bae6e4ab0 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/floor_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#floor" do + before :each do + @one = BigDecimal("1") + @three = BigDecimal("3") + @four = BigDecimal("4") + @zero = BigDecimal("0") + @mixed = BigDecimal("1.23456789") + @mixed_big = BigDecimal("1.23456789E100") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + end + + it "returns the greatest integer smaller or equal to self" do + @pos_int.floor.should == @pos_int + @neg_int.floor.should == @neg_int + @pos_frac.floor.should == @zero + @neg_frac.floor.should == BigDecimal("-1") + @zero.floor.should == 0 + @zero_pos.floor.should == @zero_pos + @zero_neg.floor.should == @zero_neg + + BigDecimal('2.3').floor.should == 2 + BigDecimal('2.5').floor.should == 2 + BigDecimal('2.9999').floor.should == 2 + BigDecimal('-2.3').floor.should == -3 + BigDecimal('-2.5').floor.should == -3 + BigDecimal('-2.9999').floor.should == -3 + BigDecimal('0.8').floor.should == 0 + BigDecimal('-0.8').floor.should == -1 + end + + it "raise exception, if self is special value" do + lambda { @infinity.floor }.should raise_error(FloatDomainError) + lambda { @infinity_neg.floor }.should raise_error(FloatDomainError) + lambda { @nan.floor }.should raise_error(FloatDomainError) + end + + it "returns n digits right of the decimal point if given n > 0" do + @mixed.floor(1).should == BigDecimal("1.2") + @mixed.floor(5).should == BigDecimal("1.23456") + + BigDecimal("-0.03").floor(1).should == BigDecimal("-0.1") + BigDecimal("0.03").floor(1).should == BigDecimal("0") + + BigDecimal("23.45").floor(0).should == BigDecimal('23') + BigDecimal("23.45").floor(1).should == BigDecimal('23.4') + BigDecimal("23.45").floor(2).should == BigDecimal('23.45') + + BigDecimal("-23.45").floor(0).should == BigDecimal('-24') + BigDecimal("-23.45").floor(1).should == BigDecimal('-23.5') + BigDecimal("-23.45").floor(2).should == BigDecimal('-23.45') + + BigDecimal("2E-10").floor(0).should == @zero + BigDecimal("2E-10").floor(9).should == @zero + BigDecimal("2E-10").floor(10).should == BigDecimal('2E-10') + BigDecimal("2E-10").floor(11).should == BigDecimal('2E-10') + + (1..10).each do |n| + # 0.3, 0.33, 0.333, etc. + (@one.div(@three,20)).floor(n).should == BigDecimal("0.#{'3'*n}") + # 1.3, 1.33, 1.333, etc. + (@four.div(@three,20)).floor(n).should == BigDecimal("1.#{'3'*n}") + (BigDecimal('31').div(@three,20)).floor(n).should == BigDecimal("10.#{'3'*n}") + end + (1..10).each do |n| + # -0.4, -0.34, -0.334, etc. + (-@one.div(@three,20)).floor(n).should == BigDecimal("-0.#{'3'*(n-1)}4") + end + (1..10).each do |n| + (@three.div(@one,20)).floor(n).should == @three + end + (1..10).each do |n| + (-@three.div(@one,20)).floor(n).should == -@three + end + end + + it "sets n digits left of the decimal point to 0, if given n < 0" do + BigDecimal("13345.234").floor(-2).should == BigDecimal("13300.0") + @mixed_big.floor(-99).should == BigDecimal("0.12E101") + @mixed_big.floor(-100).should == BigDecimal("0.1E101") + @mixed_big.floor(-95).should == BigDecimal("0.123456E101") + (1..10).each do |n| + BigDecimal('1.8').floor(-n).should == @zero + end + BigDecimal("1E10").floor(-30).should == @zero + BigDecimal("-1E10").floor(-30).should == BigDecimal('-1E30') + end + +end diff --git a/spec/rubyspec/library/bigdecimal/frac_spec.rb b/spec/rubyspec/library/bigdecimal/frac_spec.rb new file mode 100644 index 0000000000..9a727a70dd --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/frac_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#frac" do + before :each do + @zero = BigDecimal("0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + end + + it "returns a BigDecimal" do + @pos_int.frac.kind_of?(BigDecimal).should == true + @neg_int.frac.kind_of?(BigDecimal).should == true + @pos_frac.kind_of?(BigDecimal).should == true + @neg_frac.kind_of?(BigDecimal).should == true + end + + it "returns the fractional part of the absolute value" do + @mixed.frac.should == BigDecimal("0.23456789") + @pos_frac.frac.should == @pos_frac + @neg_frac.frac.should == @neg_frac + end + + it "returns 0 if the value is 0" do + @zero.frac.should == @zero + end + + it "returns 0 if the value is an integer" do + @pos_int.frac.should == @zero + @neg_int.frac.should == @zero + end + + it "correctly handles special values" do + @infinity.frac.should == @infinity + @infinity_neg.frac.should == @infinity_neg + @nan.frac.nan?.should == true + end + +end diff --git a/spec/rubyspec/library/bigdecimal/gt_spec.rb b/spec/rubyspec/library/bigdecimal/gt_spec.rb new file mode 100644 index 0000000000..effd3ea35f --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/gt_spec.rb @@ -0,0 +1,83 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#>" do + before :each do + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @int_mock = mock('123') + class << @int_mock + def coerce(other) + return [other, BigDecimal('123')] + end + def > (other) + BigDecimal('123') > other + end + end + + @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac, + -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1, + @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg] + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + end + + it "returns true if a > b" do + one = BigDecimal("1") + two = BigDecimal("2") + + frac_1 = BigDecimal("1E-99999") + frac_2 = BigDecimal("0.9E-99999") + (@zero > one).should == false + (two > @zero).should == true + (frac_2 > frac_1).should == false + + (@neg_int > @pos_int).should == false + (@pos_int > @neg_int).should == true + (@neg_int > @pos_frac).should == false + (@pos_frac > @neg_int).should == true + (@zero > @zero_pos).should == false + (@zero > @zero_neg).should == false + (@zero_neg > @zero_pos).should == false + (@zero_pos > @zero_neg).should == false + end + + it "properly handles infinity values" do + @values.each { |val| + (val > @infinity).should == false + (@infinity > val).should == true + (val > @infinity_neg).should == true + (@infinity_neg > val).should == false + } + (@infinity > @infinity).should == false + (@infinity_neg > @infinity_neg).should == false + (@infinity > @infinity_neg).should == true + (@infinity_neg > @infinity).should == false + end + + it "properly handles NaN values" do + @values += [@infinity, @infinity_neg, @nan] + @values.each { |val| + (@nan > val).should == false + (val > @nan).should == false + } + end + + it "raises an ArgumentError if the argument can't be coerced into a BigDecimal" do + lambda {@zero > nil }.should raise_error(ArgumentError) + lambda {@infinity > nil }.should raise_error(ArgumentError) + lambda {@infinity_neg > nil }.should raise_error(ArgumentError) + lambda {@mixed > nil }.should raise_error(ArgumentError) + lambda {@pos_int > nil }.should raise_error(ArgumentError) + lambda {@neg_frac > nil }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/gte_spec.rb b/spec/rubyspec/library/bigdecimal/gte_spec.rb new file mode 100644 index 0000000000..28e84690ad --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/gte_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#>=" do + before :each do + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @int_mock = mock('123') + class << @int_mock + def coerce(other) + return [other, BigDecimal('123')] + end + def >= (other) + BigDecimal('123') >= other + end + end + + @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac, + -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1, + @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg] + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + end + + it "returns true if a >= b" do + one = BigDecimal("1") + two = BigDecimal("2") + + frac_1 = BigDecimal("1E-99999") + frac_2 = BigDecimal("0.9E-99999") + + (@zero >= one).should == false + (two >= @zero).should == true + + (frac_2 >= frac_1).should == false + (two >= two).should == true + (frac_1 >= frac_1).should == true + + (@neg_int >= @pos_int).should == false + (@pos_int >= @neg_int).should == true + (@neg_int >= @pos_frac).should == false + (@pos_frac >= @neg_int).should == true + (@zero >= @zero_pos).should == true + (@zero >= @zero_neg).should == true + (@zero_neg >= @zero_pos).should == true + (@zero_pos >= @zero_neg).should == true + end + + it "properly handles infinity values" do + @values.each { |val| + (val >= @infinity).should == false + (@infinity >= val).should == true + (val >= @infinity_neg).should == true + (@infinity_neg >= val).should == false + } + (@infinity >= @infinity).should == true + (@infinity_neg >= @infinity_neg).should == true + (@infinity >= @infinity_neg).should == true + (@infinity_neg >= @infinity).should == false + end + + it "properly handles NaN values" do + @values += [@infinity, @infinity_neg, @nan] + @values.each { |val| + (@nan >= val).should == false + (val >= @nan).should == false + } + end + + it "returns nil if the argument is nil" do + lambda {@zero >= nil }.should raise_error(ArgumentError) + lambda {@infinity >= nil }.should raise_error(ArgumentError) + lambda {@infinity_neg >= nil }.should raise_error(ArgumentError) + lambda {@mixed >= nil }.should raise_error(ArgumentError) + lambda {@pos_int >= nil }.should raise_error(ArgumentError) + lambda {@neg_frac >= nil }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/infinite_spec.rb b/spec/rubyspec/library/bigdecimal/infinite_spec.rb new file mode 100644 index 0000000000..b218ee371c --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/infinite_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#infinite?" do + + it "returns 1 if self is Infinity" do + BigDecimal("Infinity").infinite?.should == 1 + end + + it "returns -1 if self is -Infinity" do + BigDecimal("-Infinity").infinite?.should == -1 + end + + it "returns not true otherwise" do + e2_plus = BigDecimal("2E40001") + e3_minus = BigDecimal("3E-20001") + really_small_zero = BigDecimal("0E-200000000") + really_big_zero = BigDecimal("0E200000000000") + e3_minus.infinite?.should == nil + e2_plus.infinite?.should == nil + really_small_zero.infinite?.should == nil + really_big_zero.infinite?.should == nil + BigDecimal("0.000000000000000000000000").infinite?.should == nil + end + + it "returns not true if self is NaN" do + # NaN is a special value which is neither finite nor infinite. + nan = BigDecimal("NaN") + nan.infinite?.should == nil + end + +end diff --git a/spec/rubyspec/library/bigdecimal/inspect_spec.rb b/spec/rubyspec/library/bigdecimal/inspect_spec.rb new file mode 100644 index 0000000000..9831b2be8e --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/inspect_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#inspect" do + + before :each do + @bigdec = BigDecimal.new("1234.5678") + end + + it "returns String" do + @bigdec.inspect.kind_of?(String).should == true + end + + ruby_version_is ""..."2.4" do + it "returns String starting with #" do + @bigdec.inspect[0].should == ?# + end + + it "encloses information in angle brackets" do + @bigdec.inspect.should =~ /^.<.*>$/ + end + + it "is comma separated list of three items" do + @bigdec.inspect.should =~ /...*,.*,.*/ + end + + it "value after first comma is value as string" do + @bigdec.inspect.split(",")[1].should == "\'0.12345678E4\'" + end + + it "last part is number of significant digits" do + signific_string = "#{@bigdec.precs[0]}(#{@bigdec.precs[1]})" + @bigdec.inspect.split(",")[2].should == signific_string + ">" + end + + it "looks like this" do + regex = /^\#\$/ + @bigdec.inspect.should =~ regex + end + end + + ruby_version_is "2.4" do + it "looks like this" do + @bigdec.inspect.should == "0.12345678e4" + end + end +end diff --git a/spec/rubyspec/library/bigdecimal/limit_spec.rb b/spec/rubyspec/library/bigdecimal/limit_spec.rb new file mode 100644 index 0000000000..41308abcd8 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/limit_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'bigdecimal' + +describe "BigDecimal.limit" do + it "returns the value before set if the passed argument is nil or is not specified" do + old = BigDecimal.limit + BigDecimal.limit.should == 0 + BigDecimal.limit(10).should == 0 + BigDecimal.limit.should == 10 + BigDecimal.limit(old) + end + + it "use the global limit if no precision is specified" do + BigDecimalSpecs.with_limit(0) do + (BigDecimal('0.888') + BigDecimal('0')).should == BigDecimal('0.888') + (BigDecimal('0.888') * BigDecimal('3')).should == BigDecimal('2.664') + end + + BigDecimalSpecs.with_limit(1) do + (BigDecimal('0.888') + BigDecimal('0')).should == BigDecimal('0.9') + (BigDecimal('0.888') * BigDecimal('3')).should == BigDecimal('3') + end + + BigDecimalSpecs.with_limit(2) do + (BigDecimal('0.888') + BigDecimal('0')).should == BigDecimal('0.89') + (BigDecimal('0.888') * BigDecimal('3')).should == BigDecimal('2.7') + end + end +end diff --git a/spec/rubyspec/library/bigdecimal/lt_spec.rb b/spec/rubyspec/library/bigdecimal/lt_spec.rb new file mode 100644 index 0000000000..67811c5db4 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/lt_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#<" do + before :each do + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @int_mock = mock('123') + class << @int_mock + def coerce(other) + return [other, BigDecimal('123')] + end + def < (other) + BigDecimal('123') < other + end + end + + @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac, + -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1, + @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg] + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + end + + it "returns true if a < b" do + one = BigDecimal("1") + two = BigDecimal("2") + frac_1 = BigDecimal("1E-99999") + frac_2 = BigDecimal("0.9E-99999") + (@zero < one).should == true + (two < @zero).should == false + (frac_2 < frac_1).should == true + (@neg_int < @pos_int).should == true + (@pos_int < @neg_int).should == false + (@neg_int < @pos_frac).should == true + (@pos_frac < @neg_int).should == false + (@zero < @zero_pos).should == false + (@zero < @zero_neg).should == false + (@zero_neg < @zero_pos).should == false + (@zero_pos < @zero_neg).should == false + end + + it "properly handles infinity values" do + @values.each { |val| + (val < @infinity).should == true + (@infinity < val).should == false + (val < @infinity_neg).should == false + (@infinity_neg < val).should == true + } + (@infinity < @infinity).should == false + (@infinity_neg < @infinity_neg).should == false + (@infinity < @infinity_neg).should == false + (@infinity_neg < @infinity).should == true + end + + it "properly handles NaN values" do + @values += [@infinity, @infinity_neg, @nan] + @values.each { |val| + (@nan < val).should == false + (val < @nan).should == false + } + end + + it "raises an ArgumentError if the argument can't be coerced into a BigDecimal" do + lambda {@zero < nil }.should raise_error(ArgumentError) + lambda {@infinity < nil }.should raise_error(ArgumentError) + lambda {@infinity_neg < nil }.should raise_error(ArgumentError) + lambda {@mixed < nil }.should raise_error(ArgumentError) + lambda {@pos_int < nil }.should raise_error(ArgumentError) + lambda {@neg_frac < nil }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/lte_spec.rb b/spec/rubyspec/library/bigdecimal/lte_spec.rb new file mode 100644 index 0000000000..61fb676245 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/lte_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#<=" do + before :each do + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + + @int_mock = mock('123') + class << @int_mock + def coerce(other) + return [other, BigDecimal('123')] + end + def <= (other) + BigDecimal('123') <= other + end + end + + @values = [@mixed, @pos_int, @neg_int, @pos_frac, @neg_frac, + -2**32, -2**31, -2**30, -2**16, -2**8, -100, -10, -1, + @zero , 1, 2, 10, 10.5, 2**8, 2**16, 2**32, @int_mock, @zero_pos, @zero_neg] + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + end + + it "returns true if a <= b" do + one = BigDecimal("1") + two = BigDecimal("2") + + frac_1 = BigDecimal("1E-99999") + frac_2 = BigDecimal("0.9E-99999") + + (@zero <= one).should == true + (two <= @zero).should == false + + (frac_2 <= frac_1).should == true + (two <= two).should == true + (frac_1 <= frac_1).should == true + + (@neg_int <= @pos_int).should == true + (@pos_int <= @neg_int).should == false + (@neg_int <= @pos_frac).should == true + (@pos_frac <= @neg_int).should == false + (@zero <= @zero_pos).should == true + (@zero <= @zero_neg).should == true + (@zero_neg <= @zero_pos).should == true + (@zero_pos <= @zero_neg).should == true + end + + it "properly handles infinity values" do + @values.each { |val| + (val <= @infinity).should == true + (@infinity <= val).should == false + (val <= @infinity_neg).should == false + (@infinity_neg <= val).should == true + } + (@infinity <= @infinity).should == true + (@infinity_neg <= @infinity_neg).should == true + (@infinity <= @infinity_neg).should == false + (@infinity_neg <= @infinity).should == true + end + + it "properly handles NaN values" do + @values += [@infinity, @infinity_neg, @nan] + @values.each { |val| + (@nan <= val).should == false + (val <= @nan).should == false + } + end + + it "raises an ArgumentError if the argument can't be coerced into a BigDecimal" do + lambda {@zero <= nil }.should raise_error(ArgumentError) + lambda {@infinity <= nil }.should raise_error(ArgumentError) + lambda {@infinity_neg <= nil }.should raise_error(ArgumentError) + lambda {@mixed <= nil }.should raise_error(ArgumentError) + lambda {@pos_int <= nil }.should raise_error(ArgumentError) + lambda {@neg_frac <= nil }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/minus_spec.rb b/spec/rubyspec/library/bigdecimal/minus_spec.rb new file mode 100644 index 0000000000..bdd8478465 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/minus_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#-" do + + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @two = BigDecimal("2") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "returns a - b" do + (@two - @one).should == @one + (@one - @two).should == @one_minus + (@one - @one_minus).should == @two + (@frac_2 - @frac_1).should == BigDecimal("-0.1E-99999") + (@two - @two).should == @zero + (@frac_1 - @frac_1).should == @zero + (BigDecimal('1.23456789') - BigDecimal('1.2')).should == BigDecimal("0.03456789") + end + + it "returns NaN if NaN is involved" do + (@one - @nan).nan?.should == true + (@nan - @one).nan?.should == true + (@nan - @nan).nan?.should == true + (@nan - @infinity).nan?.should == true + (@nan - @infinity_minus).nan?.should == true + (@infinity - @nan).nan?.should == true + (@infinity_minus - @nan).nan?.should == true + end + + it "returns NaN both operands are infinite with the same sign" do + (@infinity - @infinity).nan?.should == true + (@infinity_minus - @infinity_minus).nan?.should == true + end + + it "returns Infinity or -Infinity if these are involved" do + (@infinity - @infinity_minus).should == @infinity + (@infinity_minus - @infinity).should == @infinity_minus + + (@infinity - @zero).should == @infinity + (@infinity - @frac_2).should == @infinity + (@infinity - @two).should == @infinity + (@infinity - @one_minus).should == @infinity + + (@zero - @infinity).should == @infinity_minus + (@frac_2 - @infinity).should == @infinity_minus + (@two - @infinity).should == @infinity_minus + (@one_minus - @infinity).should == @infinity_minus + end + +end diff --git a/spec/rubyspec/library/bigdecimal/mode_spec.rb b/spec/rubyspec/library/bigdecimal/mode_spec.rb new file mode 100644 index 0000000000..d88ac61aaf --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/mode_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal.mode" do + #the default value of BigDecimal exception constants is false + after :each do + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false) + BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, false) + BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) + BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, false) + end + + it "returns the appropriate value and continue the computation if the flag is false" do + BigDecimal("NaN").add(BigDecimal("1"),0).nan?.should == true + BigDecimal("0").add(BigDecimal("Infinity"),0).should == BigDecimal("Infinity") + BigDecimal("1").quo(BigDecimal("0")).should == BigDecimal("Infinity") + end + + it "returns Infinity when too big" do + BigDecimal("1E11111111111111111111").should == BigDecimal("Infinity") + (BigDecimal("1E1000000000000000000")**10).should == BigDecimal("Infinity") + end + + it "raise an exception if the flag is true" do + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, true) + lambda { BigDecimal("NaN").add(BigDecimal("1"),0) }.should raise_error(FloatDomainError) + BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) + lambda { BigDecimal("0").add(BigDecimal("Infinity"),0) }.should raise_error(FloatDomainError) + BigDecimal.mode(BigDecimal::EXCEPTION_ZERODIVIDE, true) + lambda { BigDecimal("1").quo(BigDecimal("0")) }.should raise_error(FloatDomainError) + BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, true) + lambda { BigDecimal("1E11111111111111111111") }.should raise_error(FloatDomainError) + lambda { (BigDecimal("1E1000000000000000000")**10) }.should raise_error(FloatDomainError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/modulo_spec.rb b/spec/rubyspec/library/bigdecimal/modulo_spec.rb new file mode 100644 index 0000000000..6feeb685eb --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/modulo_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/modulo', __FILE__) + +describe "BigDecimal#%" do + it_behaves_like(:bigdecimal_modulo, :%) + it_behaves_like(:bigdecimal_modulo_zerodivisionerror, :%) +end + +describe "BigDecimal#modulo" do + it_behaves_like(:bigdecimal_modulo, :modulo) + it_behaves_like(:bigdecimal_modulo_zerodivisionerror, :modulo) +end diff --git a/spec/rubyspec/library/bigdecimal/mult_spec.rb b/spec/rubyspec/library/bigdecimal/mult_spec.rb new file mode 100644 index 0000000000..a140cf899a --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/mult_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/mult', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#mult" do + it_behaves_like :bigdecimal_mult, :mult, [10] +end + +describe "BigDecimal#mult" do + before :each do + @one = BigDecimal "1" + @e3_minus = BigDecimal "3E-20001" + @e = BigDecimal "1.00000000000000000000123456789" + @tolerance = @e.sub @one, 1000 + @tolerance2 = BigDecimal "30001E-20005" + + end + + it "multiply self with other with (optional) precision" do + @e.mult(@one, 1).should be_close(@one, @tolerance) + @e3_minus.mult(@one, 1).should be_close(0, @tolerance2) + end + +end diff --git a/spec/rubyspec/library/bigdecimal/multiply_spec.rb b/spec/rubyspec/library/bigdecimal/multiply_spec.rb new file mode 100644 index 0000000000..842e22eaa2 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/multiply_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/mult', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#*" do + it_behaves_like :bigdecimal_mult, :*, [] +end + +describe "BigDecimal#*" do + before :each do + @e3_minus = BigDecimal("3E-20001") + @e3_plus = BigDecimal("3E20001") + @e = BigDecimal("1.00000000000000000000123456789") + @one = BigDecimal("1") + end + + it "multiply self with other" do + (@one * @one).should == @one + (@e3_minus * @e3_plus).should == BigDecimal("9") + # Can't do this till we implement ** + # (@e3_minus * @e3_minus).should == @e3_minus ** 2 + # So let's rewrite it as: + (@e3_minus * @e3_minus).should == BigDecimal("9E-40002") + (@e * @one).should == @e + end +end diff --git a/spec/rubyspec/library/bigdecimal/nan_spec.rb b/spec/rubyspec/library/bigdecimal/nan_spec.rb new file mode 100644 index 0000000000..5c291629b3 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/nan_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#nan?" do + + it "returns true if self is not a number" do + BigDecimal("NaN").nan?.should == true + end + + it "returns false if self is not a NaN" do + BigDecimal("Infinity").nan?.should == false + BigDecimal("-Infinity").nan?.should == false + BigDecimal("0").nan?.should == false + BigDecimal("+0").nan?.should == false + BigDecimal("-0").nan?.should == false + BigDecimal("2E40001").nan?.should == false + BigDecimal("3E-20001").nan?.should == false + BigDecimal("0E-200000000").nan?.should == false + BigDecimal("0E200000000000").nan?.should == false + BigDecimal("0.000000000000000000000000").nan?.should == false + end + +end diff --git a/spec/rubyspec/library/bigdecimal/new_spec.rb b/spec/rubyspec/library/bigdecimal/new_spec.rb new file mode 100644 index 0000000000..de3aa195ca --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/new_spec.rb @@ -0,0 +1,109 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal.new" do + + it "creates a new object of class BigDecimal" do + BigDecimal.new("3.14159").should be_kind_of(BigDecimal) + (0..9).each {|i| + BigDecimal.new("1#{i}").should == 10 + i + BigDecimal.new("-1#{i}").should == -10 - i + BigDecimal.new("1E#{i}").should == 10**i + BigDecimal.new("1000000E-#{i}").should == 10**(6-i).to_f + # ^ to_f to avoid Rational type + } + (1..9).each {|i| + BigDecimal.new("100.#{i}").to_s.should =~ /\A0\.100#{i}E3\z/i + BigDecimal.new("-100.#{i}").to_s.should =~ /\A-0\.100#{i}E3\z/i + } + end + + it "accepts significant digits >= given precision" do + BigDecimal.new("3.1415923", 10).precs[1].should >= 10 + end + + it "determines precision from initial value" do + pi_string = "3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043" + BigDecimal.new(pi_string).precs[1].should >= pi_string.size-1 + end + + it "ignores leading whitespace" do + BigDecimal.new(" \t\n \r1234").should == BigDecimal.new("1234") + BigDecimal.new(" \t\n \rNaN \n").nan?.should == true + BigDecimal.new(" \t\n \rInfinity \n").infinite?.should == 1 + BigDecimal.new(" \t\n \r-Infinity \n").infinite?.should == -1 + end + + it "ignores trailing garbage" do + BigDecimal.new("123E45ruby").should == BigDecimal.new("123E45") + BigDecimal.new("123x45").should == BigDecimal.new("123") + BigDecimal.new("123.4%E5").should == BigDecimal.new("123.4") + BigDecimal.new("1E2E3E4E5E").should == BigDecimal.new("100") + end + + ruby_version_is ""..."2.4" do + it "treats invalid strings as 0.0" do + BigDecimal.new("ruby").should == BigDecimal.new("0.0") + BigDecimal.new(" \t\n \r-\t\t\tInfinity \n").should == BigDecimal.new("0.0") + end + end + + ruby_version_is "2.4" do + it "raises ArgumentError for invalid strings" do + lambda { BigDecimal.new("ruby") }.should raise_error(ArgumentError) + lambda { BigDecimal.new(" \t\n \r-\t\t\tInfinity \n") }.should raise_error(ArgumentError) + end + end + + it "allows omitting the integer part" do + BigDecimal.new(".123").should == BigDecimal.new("0.123") + end + + it "allows for underscores in all parts" do + reference = BigDecimal.new("12345.67E89") + + BigDecimal.new("12_345.67E89").should == reference + BigDecimal.new("1_2_3_4_5_._6____7_E89").should == reference + BigDecimal.new("12345_.67E_8__9_").should == reference + end + + it "accepts NaN and [+-]Infinity" do + BigDecimal.new("NaN").nan?.should == true + + pos_inf = BigDecimal.new("Infinity") + pos_inf.finite?.should == false + pos_inf.should > 0 + pos_inf.should == BigDecimal.new("+Infinity") + + neg_inf = BigDecimal.new("-Infinity") + neg_inf.finite?.should == false + neg_inf.should < 0 + end + + it "allows for [eEdD] as exponent separator" do + reference = BigDecimal.new("12345.67E89") + + BigDecimal.new("12345.67e89").should == reference + BigDecimal.new("12345.67E89").should == reference + BigDecimal.new("12345.67d89").should == reference + BigDecimal.new("12345.67D89").should == reference + end + + it "allows for varying signs" do + reference = BigDecimal.new("123.456E1") + + BigDecimal.new("+123.456E1").should == reference + BigDecimal.new("-123.456E1").should == -reference + BigDecimal.new("123.456E+1").should == reference + BigDecimal.new("12345.6E-1").should == reference + BigDecimal.new("+123.456E+1").should == reference + BigDecimal.new("+12345.6E-1").should == reference + BigDecimal.new("-123.456E+1").should == -reference + BigDecimal.new("-12345.6E-1").should == -reference + end + + it 'raises ArgumentError when Float is used without precision' do + lambda { BigDecimal(1.0) }.should raise_error(ArgumentError) + end + +end diff --git a/spec/rubyspec/library/bigdecimal/nonzero_spec.rb b/spec/rubyspec/library/bigdecimal/nonzero_spec.rb new file mode 100644 index 0000000000..336c72a6b2 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/nonzero_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#nonzero?" do + + it "returns self if self doesn't equal zero" do + # documentation says, it returns true. (04/10/08) + e2_plus = BigDecimal("2E40001") + e3_minus = BigDecimal("3E-20001") + infinity = BigDecimal("Infinity") + infinity_minus = BigDecimal("-Infinity") + nan = BigDecimal("NaN") + infinity.nonzero?.should equal(infinity) + infinity_minus.nonzero?.should equal(infinity_minus) + nan.nonzero?.should equal(nan) + e3_minus.nonzero?.should equal(e3_minus) + e2_plus.nonzero?.should equal(e2_plus) + end + + it "returns nil otherwise" do + # documentation states, it should return false. (04/10/08) + really_small_zero = BigDecimal("0E-200000000") + really_big_zero = BigDecimal("0E200000000000") + really_small_zero.nonzero?.should == nil + really_big_zero.nonzero?.should == nil + BigDecimal("0.000000000000000000000000").nonzero?.should == nil + end + +end diff --git a/spec/rubyspec/library/bigdecimal/plus_spec.rb b/spec/rubyspec/library/bigdecimal/plus_spec.rb new file mode 100644 index 0000000000..72ec282075 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/plus_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#+" do + + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @ten = BigDecimal("10") + @eleven = BigDecimal("11") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "returns a + b" do + (@two + @one).should == @three + (@one + @two).should == @three + (@one + @one_minus).should == @zero + (@zero + @one).should == @one + (@ten + @one).should == @eleven + (@frac_1 + @frac_2).should == BigDecimal("1.9E-99999") + (@frac_2 + @frac_1).should == BigDecimal("1.9E-99999") + (@frac_1 + @frac_1).should == BigDecimal("2E-99999") + end + + it "returns NaN if NaN is involved" do + (@one + @nan).nan?.should == true + (@nan + @one).nan?.should == true + end + + it "returns Infinity or -Infinity if these are involved" do + (@zero + @infinity).should == @infinity + (@frac_2 + @infinity).should == @infinity + (@two + @infinity_minus).should == @infinity_minus + end + + it "returns NaN if Infinity + (- Infinity)" do + (@infinity + @infinity_minus).nan?.should == true + end + +end diff --git a/spec/rubyspec/library/bigdecimal/power_spec.rb b/spec/rubyspec/library/bigdecimal/power_spec.rb new file mode 100644 index 0000000000..f2b1a9b5c8 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/power_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/power', __FILE__) + +describe "BigDecimal#power" do + it_behaves_like(:bigdecimal_power, :power) +end diff --git a/spec/rubyspec/library/bigdecimal/precs_spec.rb b/spec/rubyspec/library/bigdecimal/precs_spec.rb new file mode 100644 index 0000000000..c7700a3819 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/precs_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#precs" do + + before :each do + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + @zero = BigDecimal("0") + @zero_neg = BigDecimal("-0") + + @arr = [BigDecimal("2E40001"), BigDecimal("3E-20001"),\ + @infinity, @infinity_neg, @nan, @zero, @zero_neg] + @precision = BigDecimal::BASE.to_s.length - 1 + end + + it "returns array of two values" do + @arr.each do |x| + x.precs.kind_of?(Array).should == true + x.precs.size.should == 2 + end + end + + it "returns Integers as array values" do + @arr.each do |x| + x.precs[0].kind_of?(Integer).should == true + x.precs[1].kind_of?(Integer).should == true + end + end + + it "returns the current value of significant digits as the first value" do + BigDecimal("3.14159").precs[0].should >= 6 + BigDecimal('1').precs[0].should == BigDecimal('1' + '0' * 100).precs[0] + [@infinity, @infinity_neg, @nan, @zero, @zero_neg].each do |value| + value.precs[0].should <= @precision + end + end + + it "returns the maximum number of significant digits as the second value" do + BigDecimal("3.14159").precs[1].should >= 6 + BigDecimal('1').precs[1].should >= 1 + BigDecimal('1' + '0' * 100).precs[1] >= 101 + [@infinity, @infinity_neg, @nan, @zero, @zero_neg].each do |value| + value.precs[1].should >= 1 + end + end +end + diff --git a/spec/rubyspec/library/bigdecimal/quo_spec.rb b/spec/rubyspec/library/bigdecimal/quo_spec.rb new file mode 100644 index 0000000000..bc27fd0f5e --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/quo_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/quo', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#quo" do + it_behaves_like :bigdecimal_quo, :quo, [] + + it "returns NaN if NaN is involved" do + BigDecimal("1").quo(BigDecimal("NaN")).nan?.should == true + BigDecimal("NaN").quo(BigDecimal("1")).nan?.should == true + end +end + diff --git a/spec/rubyspec/library/bigdecimal/remainder_spec.rb b/spec/rubyspec/library/bigdecimal/remainder_spec.rb new file mode 100644 index 0000000000..796522c5c8 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/remainder_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#remainder" do + + before :each do + @zero = BigDecimal("0") + @one = BigDecimal("0") + @mixed = BigDecimal("1.23456789") + @pos_int = BigDecimal("2E5555") + @neg_int = BigDecimal("-2E5555") + @pos_frac = BigDecimal("2E-9999") + @neg_frac = BigDecimal("-2E-9999") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "it equals modulo, if both values are of same sign" do + BigDecimal('1234567890123456789012345679').remainder(BigDecimal('1')).should == @zero + BigDecimal('123456789').remainder(BigDecimal('333333333333333333333333333E-50')).should == BigDecimal('0.12233333333333333333345679E-24') + + @mixed.remainder(@pos_frac).should == @mixed % @pos_frac + @pos_int.remainder(@pos_frac).should == @pos_int % @pos_frac + @neg_frac.remainder(@neg_int).should == @neg_frac % @neg_int + @neg_int.remainder(@neg_frac).should == @neg_int % @neg_frac + end + + it "means self-arg*(self/arg).truncate" do + @mixed.remainder(@neg_frac).should == @mixed - @neg_frac * (@mixed / @neg_frac).truncate + @pos_int.remainder(@neg_frac).should == @pos_int - @neg_frac * (@pos_int / @neg_frac).truncate + @neg_frac.remainder(@pos_int).should == @neg_frac - @pos_int * (@neg_frac / @pos_int).truncate + @neg_int.remainder(@pos_frac).should == @neg_int - @pos_frac * (@neg_int / @pos_frac).truncate + end + + it "returns NaN used with zero" do + @mixed.remainder(@zero).nan?.should == true + @zero.remainder(@zero).nan?.should == true + end + + it "returns zero if used on zero" do + @zero.remainder(@mixed).should == @zero + end + + it "returns NaN if NaN is involved" do + @nan.remainder(@nan).nan?.should == true + @nan.remainder(@one).nan?.should == true + @one.remainder(@nan).nan?.should == true + @infinity.remainder(@nan).nan?.should == true + @nan.remainder(@infinity).nan?.should == true + end + + it "returns NaN if Infinity is involved" do + @infinity.remainder(@infinity).nan?.should == true + @infinity.remainder(@one).nan?.should == true + @infinity.remainder(@mixed).nan?.should == true + @infinity.remainder(@one_minus).nan?.should == true + @infinity.remainder(@frac_1).nan?.should == true + @one.remainder(@infinity).nan?.should == true + + @infinity_minus.remainder(@infinity_minus).nan?.should == true + @infinity_minus.remainder(@one).nan?.should == true + @one.remainder(@infinity_minus).nan?.should == true + @frac_2.remainder(@infinity_minus).nan?.should == true + + @infinity.remainder(@infinity_minus).nan?.should == true + @infinity_minus.remainder(@infinity).nan?.should == true + end + + it "coerces arguments to BigDecimal if possible" do + @one.remainder(2).should == @one + end + + + it "raises TypeError if the argument cannot be coerced to BigDecimal" do + lambda { + @one.remainder('2') + }.should raise_error(TypeError) + end + +end diff --git a/spec/rubyspec/library/bigdecimal/round_spec.rb b/spec/rubyspec/library/bigdecimal/round_spec.rb new file mode 100644 index 0000000000..6c1987c5d8 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/round_spec.rb @@ -0,0 +1,202 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#round" do + before :each do + @one = BigDecimal("1") + @two = BigDecimal("2") + @three = BigDecimal("3") + + @neg_one = BigDecimal("-1") + @neg_two = BigDecimal("-2") + @neg_three = BigDecimal("-3") + + @p1_50 = BigDecimal("1.50") + @p1_51 = BigDecimal("1.51") + @p1_49 = BigDecimal("1.49") + @n1_50 = BigDecimal("-1.50") + @n1_51 = BigDecimal("-1.51") + @n1_49 = BigDecimal("-1.49") + + @p2_50 = BigDecimal("2.50") + @p2_51 = BigDecimal("2.51") + @p2_49 = BigDecimal("2.49") + @n2_50 = BigDecimal("-2.50") + @n2_51 = BigDecimal("-2.51") + @n2_49 = BigDecimal("-2.49") + end + + after :each do + BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP) + end + + it "uses default rounding method unless given" do + @p1_50.round(0).should == @two + @p1_51.round(0).should == @two + @p1_49.round(0).should == @one + @n1_50.round(0).should == @neg_two + @n1_51.round(0).should == @neg_two + @n1_49.round(0).should == @neg_one + + @p2_50.round(0).should == @three + @p2_51.round(0).should == @three + @p2_49.round(0).should == @two + @n2_50.round(0).should == @neg_three + @n2_51.round(0).should == @neg_three + @n2_49.round(0).should == @neg_two + + BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_DOWN) + + @p1_50.round(0).should == @one + @p1_51.round(0).should == @one + @p1_49.round(0).should == @one + @n1_50.round(0).should == @neg_one + @n1_51.round(0).should == @neg_one + @n1_49.round(0).should == @neg_one + + @p2_50.round(0).should == @two + @p2_51.round(0).should == @two + @p2_49.round(0).should == @two + @n2_50.round(0).should == @neg_two + @n2_51.round(0).should == @neg_two + @n2_49.round(0).should == @neg_two + end + + describe "BigDecimal::ROUND_UP" do + it "rounds values away from zero" do + @p1_50.round(0, BigDecimal::ROUND_UP).should == @two + @p1_51.round(0, BigDecimal::ROUND_UP).should == @two + @p1_49.round(0, BigDecimal::ROUND_UP).should == @two + @n1_50.round(0, BigDecimal::ROUND_UP).should == @neg_two + @n1_51.round(0, BigDecimal::ROUND_UP).should == @neg_two + @n1_49.round(0, BigDecimal::ROUND_UP).should == @neg_two + + @p2_50.round(0, BigDecimal::ROUND_UP).should == @three + @p2_51.round(0, BigDecimal::ROUND_UP).should == @three + @p2_49.round(0, BigDecimal::ROUND_UP).should == @three + @n2_50.round(0, BigDecimal::ROUND_UP).should == @neg_three + @n2_51.round(0, BigDecimal::ROUND_UP).should == @neg_three + @n2_49.round(0, BigDecimal::ROUND_UP).should == @neg_three + end + end + + describe "BigDecimal::ROUND_DOWN" do + it "rounds values towards zero" do + @p1_50.round(0, BigDecimal::ROUND_DOWN).should == @one + @p1_51.round(0, BigDecimal::ROUND_DOWN).should == @one + @p1_49.round(0, BigDecimal::ROUND_DOWN).should == @one + @n1_50.round(0, BigDecimal::ROUND_DOWN).should == @neg_one + @n1_51.round(0, BigDecimal::ROUND_DOWN).should == @neg_one + @n1_49.round(0, BigDecimal::ROUND_DOWN).should == @neg_one + + @p2_50.round(0, BigDecimal::ROUND_DOWN).should == @two + @p2_51.round(0, BigDecimal::ROUND_DOWN).should == @two + @p2_49.round(0, BigDecimal::ROUND_DOWN).should == @two + @n2_50.round(0, BigDecimal::ROUND_DOWN).should == @neg_two + @n2_51.round(0, BigDecimal::ROUND_DOWN).should == @neg_two + @n2_49.round(0, BigDecimal::ROUND_DOWN).should == @neg_two + end + end + + describe "BigDecimal::ROUND_HALF_UP" do + it "rounds values >= 5 up, otherwise down" do + @p1_50.round(0, BigDecimal::ROUND_HALF_UP).should == @two + @p1_51.round(0, BigDecimal::ROUND_HALF_UP).should == @two + @p1_49.round(0, BigDecimal::ROUND_HALF_UP).should == @one + @n1_50.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two + @n1_51.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two + @n1_49.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_one + + @p2_50.round(0, BigDecimal::ROUND_HALF_UP).should == @three + @p2_51.round(0, BigDecimal::ROUND_HALF_UP).should == @three + @p2_49.round(0, BigDecimal::ROUND_HALF_UP).should == @two + @n2_50.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_three + @n2_51.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_three + @n2_49.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two + end + end + + describe "BigDecimal::ROUND_HALF_DOWN" do + it "rounds values > 5 up, otherwise down" do + @p1_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @one + @p1_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two + @p1_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @one + @n1_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_one + @n1_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two + @n1_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_one + + @p2_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two + @p2_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @three + @p2_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two + @n2_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two + @n2_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_three + @n2_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two + end + end + + describe "BigDecimal::ROUND_CEILING" do + it "rounds values towards +infinity" do + @p1_50.round(0, BigDecimal::ROUND_CEILING).should == @two + @p1_51.round(0, BigDecimal::ROUND_CEILING).should == @two + @p1_49.round(0, BigDecimal::ROUND_CEILING).should == @two + @n1_50.round(0, BigDecimal::ROUND_CEILING).should == @neg_one + @n1_51.round(0, BigDecimal::ROUND_CEILING).should == @neg_one + @n1_49.round(0, BigDecimal::ROUND_CEILING).should == @neg_one + + @p2_50.round(0, BigDecimal::ROUND_CEILING).should == @three + @p2_51.round(0, BigDecimal::ROUND_CEILING).should == @three + @p2_49.round(0, BigDecimal::ROUND_CEILING).should == @three + @n2_50.round(0, BigDecimal::ROUND_CEILING).should == @neg_two + @n2_51.round(0, BigDecimal::ROUND_CEILING).should == @neg_two + @n2_49.round(0, BigDecimal::ROUND_CEILING).should == @neg_two + end + end + + describe "BigDecimal::ROUND_FLOOR" do + it "rounds values towards -infinity" do + @p1_50.round(0, BigDecimal::ROUND_FLOOR).should == @one + @p1_51.round(0, BigDecimal::ROUND_FLOOR).should == @one + @p1_49.round(0, BigDecimal::ROUND_FLOOR).should == @one + @n1_50.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two + @n1_51.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two + @n1_49.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two + + @p2_50.round(0, BigDecimal::ROUND_FLOOR).should == @two + @p2_51.round(0, BigDecimal::ROUND_FLOOR).should == @two + @p2_49.round(0, BigDecimal::ROUND_FLOOR).should == @two + @n2_50.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three + @n2_51.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three + @n2_49.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three + end + end + + describe "BigDecimal::ROUND_HALF_EVEN" do + it "rounds values > 5 up, < 5 down and == 5 towards even neighbor" do + @p1_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two + @p1_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two + @p1_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @one + @n1_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two + @n1_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two + @n1_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_one + + @p2_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two + @p2_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @three + @p2_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two + @n2_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two + @n2_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_three + @n2_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two + end + end + + it 'raise exception, if self is special value' do + lambda { BigDecimal('NaN').round }.should raise_error(FloatDomainError) + lambda { BigDecimal('Infinity').round }.should raise_error(FloatDomainError) + lambda { BigDecimal('-Infinity').round }.should raise_error(FloatDomainError) + end + + it 'do not raise exception, if self is special value and precision is given' do + lambda { BigDecimal('NaN').round(2) }.should_not raise_error(FloatDomainError) + lambda { BigDecimal('Infinity').round(2) }.should_not raise_error(FloatDomainError) + lambda { BigDecimal('-Infinity').round(2) }.should_not raise_error(FloatDomainError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/shared/eql.rb b/spec/rubyspec/library/bigdecimal/shared/eql.rb new file mode 100644 index 0000000000..eaad272e9a --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/shared/eql.rb @@ -0,0 +1,61 @@ +require 'bigdecimal' + +describe :bigdecimal_eql, shared: true do + before :each do + @bg6543_21 = BigDecimal.new("6543.21") + @bg5667_19 = BigDecimal.new("5667.19") + @a = BigDecimal("1.0000000000000000000000000000000000000000005") + @b = BigDecimal("1.00000000000000000000000000000000000000000005") + @bigint = BigDecimal("1000.0") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + end + + it "tests for equality" do + @bg6543_21.send(@method, @bg6543_21).should == true + @a.send(@method, @a).should == true + @a.send(@method, @b).should == false + @bg6543_21.send(@method, @a).should == false + @bigint.send(@method, 1000).should == true + end + + it "returns false for NaN as it is never equal to any number" do + @nan.send(@method, @nan).should == false + @a.send(@method, @nan).should == false + @nan.send(@method, @a).should == false + @nan.send(@method, @infinity).should == false + @nan.send(@method, @infinity_minus).should == false + @infinity.send(@method, @nan).should == false + @infinity_minus.send(@method, @nan).should == false + end + + it "returns true for infinity values with the same sign" do + @infinity.send(@method, @infinity).should == true + @infinity.send(@method, BigDecimal("Infinity")).should == true + BigDecimal("Infinity").send(@method, @infinity).should == true + + @infinity_minus.send(@method, @infinity_minus).should == true + @infinity_minus.send(@method, BigDecimal("-Infinity")).should == true + BigDecimal("-Infinity").send(@method, @infinity_minus).should == true + end + + it "returns false for infinity values with different signs" do + @infinity.send(@method, @infinity_minus).should == false + @infinity_minus.send(@method, @infinity).should == false + end + + it "returns false when infinite value compared to finite one" do + @infinity.send(@method, @a).should == false + @infinity_minus.send(@method, @a).should == false + + @a.send(@method, @infinity).should == false + @a.send(@method, @infinity_minus).should == false + end + + it "returns false when compared objects that can not be coerced into BigDecimal" do + @infinity.send(@method, nil).should == false + @bigint.send(@method, nil).should == false + @nan.send(@method, nil).should == false + end +end diff --git a/spec/rubyspec/library/bigdecimal/shared/modulo.rb b/spec/rubyspec/library/bigdecimal/shared/modulo.rb new file mode 100644 index 0000000000..78ebe8360d --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/shared/modulo.rb @@ -0,0 +1,116 @@ +require 'bigdecimal' + +describe :bigdecimal_modulo, shared: true do + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @mixed = BigDecimal("1.23456789") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-9999") + @frac_2 = BigDecimal("0.9E-9999") + end + + it "returns self modulo other" do + bd6543 = BigDecimal.new("6543.21") + bd5667 = BigDecimal.new("5667.19") + a = BigDecimal("1.0000000000000000000000000000000000000000005") + b = BigDecimal("1.00000000000000000000000000000000000000000005") + + bd6543.send(@method, 137).should == BigDecimal("104.21") + bd5667.send(@method, bignum_value).should == 5667.19 + bd6543.send(@method, BigDecimal("137.24")).should == BigDecimal("92.93") + bd6543.send(@method, 137).should be_close(6543.21.%(137), TOLERANCE) + bd6543.send(@method, 137).should == bd6543 % 137 + bd5667.send(@method, bignum_value).should be_close(5667.19.%(0xffffffff), TOLERANCE) + bd5667.send(@method, bignum_value).should == bd5667.%(0xffffffff) + bd6543.send(@method, 137.24).should be_close(6543.21.%(137.24), TOLERANCE) + a.send(@method, b).should == BigDecimal("0.45E-42") + @zero.send(@method, @one).should == @zero + @zero.send(@method, @one_minus).should == @zero + @two.send(@method, @one).should == @zero + @one.send(@method, @two).should == @one + @frac_1.send(@method, @one).should == @frac_1 + @frac_2.send(@method, @one).should == @frac_2 + @one_minus.send(@method, @one_minus).should == @zero + @one_minus.send(@method, @one).should == @zero + @one_minus.send(@method, @two).should == @one + @one.send(@method, -@two).should == -@one + + @one_minus.modulo(BigDecimal('0.9')).should == BigDecimal('0.8') + @one.modulo(BigDecimal('-0.9')).should == BigDecimal('-0.8') + + @one_minus.modulo(BigDecimal('0.8')).should == BigDecimal('0.6') + @one.modulo(BigDecimal('-0.8')).should == BigDecimal('-0.6') + + @one_minus.modulo(BigDecimal('0.6')).should == BigDecimal('0.2') + @one.modulo(BigDecimal('-0.6')).should == BigDecimal('-0.2') + + @one_minus.modulo(BigDecimal('0.5')).should == @zero + @one.modulo(BigDecimal('-0.5')).should == @zero + @one_minus.modulo(BigDecimal('-0.5')).should == @zero + + @one_minus.modulo(BigDecimal('0.4')).should == BigDecimal('0.2') + @one.modulo(BigDecimal('-0.4')).should == BigDecimal('-0.2') + + @one_minus.modulo(BigDecimal('0.3')).should == BigDecimal('0.2') + @one_minus.modulo(BigDecimal('0.2')).should == @zero + end + + it "returns a [Float value] when the argument is Float" do + @two.send(@method, 2.0).should == 0.0 + @one.send(@method, 2.0).should == 1.0 + res = @two.send(@method, 5.0) + res.kind_of?(BigDecimal).should == true + end + + it "returns NaN if NaN is involved" do + @nan.send(@method, @nan).nan?.should == true + @nan.send(@method, @one).nan?.should == true + @one.send(@method, @nan).nan?.should == true + @infinity.send(@method, @nan).nan?.should == true + @nan.send(@method, @infinity).nan?.should == true + end + + it "returns NaN if the dividend is Infinity" do + @infinity.send(@method, @infinity).nan?.should == true + @infinity.send(@method, @one).nan?.should == true + @infinity.send(@method, @mixed).nan?.should == true + @infinity.send(@method, @one_minus).nan?.should == true + @infinity.send(@method, @frac_1).nan?.should == true + + @infinity_minus.send(@method, @infinity_minus).nan?.should == true + @infinity_minus.send(@method, @one).nan?.should == true + + @infinity.send(@method, @infinity_minus).nan?.should == true + @infinity_minus.send(@method, @infinity).nan?.should == true + end + + it "returns the dividend if the divisor is Infinity" do + @one.send(@method, @infinity).should == @one + @one.send(@method, @infinity_minus).should == @one + @frac_2.send(@method, @infinity_minus).should == @frac_2 + end + + it "raises TypeError if the argument cannot be coerced to BigDecimal" do + lambda { + @one.send(@method, '2') + }.should raise_error(TypeError) + end +end + +describe :bigdecimal_modulo_zerodivisionerror, shared: true do + it "raises ZeroDivisionError if other is zero" do + bd5667 = BigDecimal.new("5667.19") + + lambda { bd5667.send(@method, 0) }.should raise_error(ZeroDivisionError) + lambda { bd5667.send(@method, BigDecimal("0")) }.should raise_error(ZeroDivisionError) + lambda { @zero.send(@method, @zero) }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/shared/mult.rb b/spec/rubyspec/library/bigdecimal/shared/mult.rb new file mode 100644 index 0000000000..b94c9385de --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/shared/mult.rb @@ -0,0 +1,97 @@ +require 'bigdecimal' + +describe :bigdecimal_mult, shared: true do + before :each do + @zero = BigDecimal "0" + @zero_pos = BigDecimal "+0" + @zero_neg = BigDecimal "-0" + + @one = BigDecimal "1" + @mixed = BigDecimal "1.23456789" + @pos_int = BigDecimal "2E5555" + @neg_int = BigDecimal "-2E5555" + @pos_frac = BigDecimal "2E-9999" + @neg_frac = BigDecimal "-2E-9999" + @nan = BigDecimal "NaN" + @infinity = BigDecimal "Infinity" + @infinity_minus = BigDecimal "-Infinity" + @one_minus = BigDecimal "-1" + @frac_1 = BigDecimal "1E-99999" + @frac_2 = BigDecimal "0.9E-99999" + + @e3_minus = BigDecimal "3E-20001" + @e = BigDecimal "1.00000000000000000000123456789" + @tolerance = @e.sub @one, 1000 + @tolerance2 = BigDecimal "30001E-20005" + + @special_vals = [@infinity, @infinity_minus, @nan] + @regular_vals = [ @one, @mixed, @pos_int, @neg_int, + @pos_frac, @neg_frac, @one_minus, + @frac_1, @frac_2 + ] + @zeroes = [@zero, @zero_pos, @zero_neg] + end + + it "returns zero of appropriate sign if self or argument is zero" do + @zero.send(@method, @zero, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO + @zero_neg.send(@method, @zero_neg, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO + @zero.send(@method, @zero_neg, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + @zero_neg.send(@method, @zero, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + + @one.send(@method, @zero, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO + @one.send(@method, @zero_neg, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + + @zero.send(@method, @one, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO + @zero.send(@method, @one_minus, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + @zero_neg.send(@method, @one_minus, *@object).sign.should == BigDecimal::SIGN_POSITIVE_ZERO + @zero_neg.send(@method, @one, *@object).sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + end + + it "returns NaN if NaN is involved" do + values = @regular_vals + @zeroes + + values.each do |val| + @nan.send(@method, val, *@object).nan?.should == true + val.send(@method, @nan, *@object).nan?.should == true + end + end + + it "returns zero if self or argument is zero" do + values = @regular_vals + @zeroes + + values.each do |val| + @zeroes.each do |zero| + zero.send(@method, val, *@object).should == 0 + zero.send(@method, val, *@object).zero?.should == true + val.send(@method, zero, *@object).should == 0 + val.send(@method, zero, *@object).zero?.should == true + end + end + end + + it "returns infinite value if self or argument is infinite" do + values = @regular_vals + infs = [@infinity, @infinity_minus] + + values.each do |val| + infs.each do |inf| + inf.send(@method, val, *@object).finite?.should == false + val.send(@method, inf, *@object).finite?.should == false + end + end + + @infinity.send(@method, @infinity, *@object).infinite?.should == 1 + @infinity_minus.send(@method, @infinity_minus, *@object).infinite?.should == 1 + @infinity.send(@method, @infinity_minus, *@object).infinite?.should == -1 + @infinity_minus.send(@method, @infinity, *@object).infinite?.should == -1 + @infinity.send(@method, @one, *@object).infinite?.should == 1 + @infinity_minus.send(@method, @one, *@object).infinite?.should == -1 + end + + it "returns NaN if the result is undefined" do + @zero.send(@method, @infinity, *@object).nan?.should == true + @zero.send(@method, @infinity_minus, *@object).nan?.should == true + @infinity.send(@method, @zero, *@object).nan?.should == true + @infinity_minus.send(@method, @zero, *@object).nan?.should == true + end +end diff --git a/spec/rubyspec/library/bigdecimal/shared/power.rb b/spec/rubyspec/library/bigdecimal/shared/power.rb new file mode 100644 index 0000000000..a4848fb2e2 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/shared/power.rb @@ -0,0 +1,72 @@ +require 'bigdecimal' + +describe :bigdecimal_power, shared: true do + it "powers of self" do + e3_minus = BigDecimal("3E-20001") + e3_minus_power_2 = BigDecimal("9E-40002") + e3_plus = BigDecimal("3E20001") + e2_plus = BigDecimal("2E40001") + e5_minus = BigDecimal("5E-40002") + e = BigDecimal("1.00000000000000000000123456789") + one = BigDecimal("1") + ten = BigDecimal("10") + # The tolerance is dependent upon the size of BASE_FIG + tolerance = BigDecimal("1E-70") + ten_powers = BigDecimal("1E10000") + pi = BigDecimal("3.14159265358979") + e3_minus.send(@method, 2).should == e3_minus_power_2 + e3_plus.send(@method, 0).should == 1 + e3_minus.send(@method, 1).should == e3_minus + e2_plus.send(@method, -1).should == e5_minus + e2_plus.send(@method, -1).should == e5_minus.power(1) + (e2_plus.send(@method, -1) * e5_minus.send(@method, -1)).should == 1 + e.send(@method, 2).should == e * e + e.send(@method, -1).should be_close(one.div(e, 120), tolerance) + ten.send(@method, 10000).should == ten_powers + pi.send(@method, 10).should be_close(Math::PI ** 10, TOLERANCE) + end + + it "powers of 1 equal 1" do + one = BigDecimal("1") + one.send(@method, 0).should == 1 + one.send(@method, 1).should == 1 + one.send(@method, 10).should == 1 + one.send(@method, -10).should == 1 + end + + it "0 to power of 0 is 1" do + zero = BigDecimal("0") + zero.send(@method, 0).should == 1 + end + + it "0 to powers < 0 is Infinity" do + zero = BigDecimal("0") + infinity = BigDecimal("Infinity") + zero.send(@method, -10).should == infinity + zero.send(@method, -1).should == infinity + end + + it "other powers of 0 are 0" do + zero = BigDecimal("0") + zero.send(@method, 1).should == 0 + zero.send(@method, 10).should == 0 + end + + it "returns NaN if self is NaN" do + BigDecimal("NaN").send(@method, -5).nan?.should == true + BigDecimal("NaN").send(@method, 5).nan?.should == true + end + + it "returns 0.0 if self is infinite and argument is negative" do + BigDecimal("Infinity").send(@method, -5).should == 0 + BigDecimal("-Infinity").send(@method, -5).should == 0 + end + + it "returns infinite if self is infinite and argument is positive" do + infinity = BigDecimal("Infinity") + BigDecimal("Infinity").send(@method, 4).should == infinity + BigDecimal("-Infinity").send(@method, 4).should == infinity + BigDecimal("Infinity").send(@method, 5).should == infinity + BigDecimal("-Infinity").send(@method, 5).should == -infinity + end +end diff --git a/spec/rubyspec/library/bigdecimal/shared/quo.rb b/spec/rubyspec/library/bigdecimal/shared/quo.rb new file mode 100644 index 0000000000..cb51c10d71 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/shared/quo.rb @@ -0,0 +1,59 @@ +require 'bigdecimal' + +describe :bigdecimal_quo, shared: true do + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_plus = BigDecimal("+0") + @zero_minus = BigDecimal("-0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @eleven = BigDecimal("11") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "returns a / b" do + @two.send(@method, @one, *@object).should == @two + @one.send(@method, @two, *@object).should == BigDecimal("0.5") + @eleven.send(@method, @three, *@object).should be_close(@three + (@two / @three), TOLERANCE) + @one.send(@method, @one_minus, *@object).should == @one_minus + @one_minus.send(@method, @one_minus, *@object).should == @one + @frac_2.send(@method, @frac_1, *@object).should == BigDecimal("0.9") + @frac_1.send(@method, @frac_1, *@object).should == @one + @one.send(@method, BigDecimal('-2E5555'), *@object).should == BigDecimal('-0.5E-5555') + @one.send(@method, BigDecimal('2E-5555'), *@object).should == BigDecimal('0.5E5555') + end + + it "returns 0 if divided by Infinity" do + @zero.send(@method, @infinity, *@object).should == 0 + @frac_2.send(@method, @infinity, *@object).should == 0 + end + + it "returns (+|-) Infinity if (+|-) Infinity divided by one" do + @infinity_minus.send(@method, @one, *@object).should == @infinity_minus + @infinity.send(@method, @one, *@object).should == @infinity + @infinity_minus.send(@method, @one_minus, *@object).should == @infinity + end + + it "returns NaN if Infinity / ((+|-) Infinity)" do + @infinity.send(@method, @infinity_minus, *@object).nan?.should == true + @infinity_minus.send(@method, @infinity, *@object).nan?.should == true + end + + it "returns (+|-) Infinity if divided by zero" do + @one.send(@method, @zero, *@object).should == @infinity + @one.send(@method, @zero_plus, *@object).should == @infinity + @one.send(@method, @zero_minus, *@object).should == @infinity_minus + end + + it "returns NaN if zero is divided by zero" do + @zero.send(@method, @zero, *@object).nan?.should == true + @zero_minus.send(@method, @zero_plus, *@object).nan?.should == true + @zero_plus.send(@method, @zero_minus, *@object).nan?.should == true + end +end diff --git a/spec/rubyspec/library/bigdecimal/shared/to_int.rb b/spec/rubyspec/library/bigdecimal/shared/to_int.rb new file mode 100644 index 0000000000..729a25f511 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/shared/to_int.rb @@ -0,0 +1,16 @@ +require 'bigdecimal' + +describe :bigdecimal_to_int , shared: true do + it "raises FloatDomainError if BigDecimal is infinity or NaN" do + lambda { BigDecimal("Infinity").send(@method) }.should raise_error(FloatDomainError) + lambda { BigDecimal("NaN").send(@method) }.should raise_error(FloatDomainError) + end + + it "returns Integer or Bignum otherwise" do + BigDecimal("3E-20001").send(@method).should == 0 + BigDecimal("2E4000").send(@method).should == 2 * 10 ** 4000 + BigDecimal("2").send(@method).should == 2 + BigDecimal("2E10").send(@method).should == 20000000000 + BigDecimal("3.14159").send(@method).should == 3 + end +end diff --git a/spec/rubyspec/library/bigdecimal/sign_spec.rb b/spec/rubyspec/library/bigdecimal/sign_spec.rb new file mode 100644 index 0000000000..0d722987b5 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/sign_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#sign" do + + it "defines several constants for signs" do + # are these really correct? + BigDecimal::SIGN_POSITIVE_INFINITE.should == 3 + BigDecimal::SIGN_NEGATIVE_INFINITE.should == -3 + BigDecimal::SIGN_POSITIVE_ZERO.should == 1 + BigDecimal::SIGN_NEGATIVE_ZERO.should == -1 + BigDecimal::SIGN_POSITIVE_FINITE.should == 2 + BigDecimal::SIGN_NEGATIVE_FINITE.should == -2 + end + + it "returns positive value if BigDecimal greater than 0" do + BigDecimal("1").sign.should == BigDecimal::SIGN_POSITIVE_FINITE + BigDecimal("1E-20000000").sign.should == BigDecimal::SIGN_POSITIVE_FINITE + BigDecimal("1E200000000").sign.should == BigDecimal::SIGN_POSITIVE_FINITE + BigDecimal("Infinity").sign.should == BigDecimal::SIGN_POSITIVE_INFINITE + end + + it "returns negative value if BigDecimal less than 0" do + BigDecimal("-1").sign.should == BigDecimal::SIGN_NEGATIVE_FINITE + BigDecimal("-1E-9990000").sign.should == BigDecimal::SIGN_NEGATIVE_FINITE + BigDecimal("-1E20000000").sign.should == BigDecimal::SIGN_NEGATIVE_FINITE + BigDecimal("-Infinity").sign.should == BigDecimal::SIGN_NEGATIVE_INFINITE + end + + it "returns positive zero if BigDecimal equals positve zero" do + BigDecimal("0").sign.should == BigDecimal::SIGN_POSITIVE_ZERO + BigDecimal("0E-200000000").sign.should == BigDecimal::SIGN_POSITIVE_ZERO + BigDecimal("0E200000000").sign.should == BigDecimal::SIGN_POSITIVE_ZERO + end + + it "returns negative zero if BigDecimal equals negative zero" do + BigDecimal("-0").sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + BigDecimal("-0E-200000000").sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + BigDecimal("-0E200000000").sign.should == BigDecimal::SIGN_NEGATIVE_ZERO + end + + it "returns BigDecimal::SIGN_NaN if BigDecimal is NaN" do + BigDecimal("NaN").sign.should == BigDecimal::SIGN_NaN + end + +end + diff --git a/spec/rubyspec/library/bigdecimal/split_spec.rb b/spec/rubyspec/library/bigdecimal/split_spec.rb new file mode 100644 index 0000000000..e2ba955a3b --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/split_spec.rb @@ -0,0 +1,88 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#split" do + + before :each do + @arr = BigDecimal("0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043E1").split + @arr_neg = BigDecimal("-0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043E1").split + @digits = "922337203685477580810101333333333333333333333333333" + @arr_big = BigDecimal("00#{@digits}000").split + @arr_big_neg = BigDecimal("-00#{@digits}000").split + @huge = BigDecimal('100000000000000000000000000000000000000000001E90000000').split + + @infinity = BigDecimal("Infinity") + @infinity_neg = BigDecimal("-Infinity") + @nan = BigDecimal("NaN") + @zero = BigDecimal("0") + @zero_neg = BigDecimal("-0") + end + + it "splits BigDecimal in an array with four values" do + @arr.size.should == 4 + end + + it "first value: 1 for numbers > 0" do + @arr[0].should == 1 + @arr_big[0].should == 1 + @zero.split[0].should == 1 + @huge[0].should == 1 + BigDecimal("+0").split[0].should == 1 + BigDecimal("1E400").split[0].should == 1 + @infinity.split[0].should == 1 + end + + it "first value: -1 for numbers < 0" do + @arr_neg[0].should == -1 + @arr_big_neg[0].should == -1 + @zero_neg.split[0].should == -1 + BigDecimal("-1E400").split[0].should == -1 + @infinity_neg.split[0].should == -1 + end + + it "first value: 0 if BigDecimal is NaN" do + BigDecimal("NaN").split[0].should == 0 + end + + it "second value: a string with the significant digits" do + string = "314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043" + @arr[1].should == string + @arr_big[1].should == @digits + @arr_big_neg[1].should == @digits + @huge[1].should == "100000000000000000000000000000000000000000001" + @infinity.split[1].should == @infinity.to_s + @nan.split[1].should == @nan.to_s + @infinity_neg.split[1].should == @infinity.to_s + @zero.split[1].should == "0" + BigDecimal("-0").split[1].should == "0" + end + + it "third value: the base (currently always ten)" do + @arr[2].should == 10 + @arr_neg[2].should == 10 + @arr_big[2].should == 10 + @arr_big_neg[2].should == 10 + @huge[2].should == 10 + @infinity.split[2].should == 10 + @nan.split[2].should == 10 + @infinity_neg.split[2].should == 10 + @zero.split[2].should == 10 + @zero_neg.split[2].should == 10 + end + + it "fourth value: the exponent" do + @arr[3].should == 1 + @arr_neg[3].should == 1 + @arr_big[3].should == 54 + @arr_big_neg[3].should == 54 + @huge[3].should == 90000045 + @infinity.split[3].should == 0 + @nan.split[3].should == 0 + @infinity_neg.split[3].should == 0 + @zero.split[3].should == 0 + @zero_neg.split[3].should == 0 + end + +end + + diff --git a/spec/rubyspec/library/bigdecimal/sqrt_spec.rb b/spec/rubyspec/library/bigdecimal/sqrt_spec.rb new file mode 100644 index 0000000000..f677192b33 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/sqrt_spec.rb @@ -0,0 +1,112 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#sqrt" do + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @two = BigDecimal("2.0") + @three = BigDecimal("3.0") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "returns square root of 2 with desired precision" do + string = "1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157" + (1..99).each { |idx| + @two.sqrt(idx).should be_close(BigDecimal(string), BigDecimal("1E-#{idx-1}")) + } + end + + it "returns square root of 3 with desired precision" do + sqrt_3 = "1.732050807568877293527446341505872366942805253810380628055806979451933016908800037081146186757248575" + (1..99).each { |idx| + @three.sqrt(idx).should be_close(BigDecimal(sqrt_3), BigDecimal("1E-#{idx-1}")) + } + end + + it "returns square root of 121 with desired precision" do + BigDecimal('121').sqrt(5).should be_close(11, 0.00001) + end + + it "returns square root of 0.9E-99999 with desired precision" do + @frac_2.sqrt(1).to_s.should =~ /\A0\.3E-49999\z/i + end + + it "raises ArgumentError when no argument is given" do + lambda { + @one.sqrt + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if a negative number is given" do + lambda { + @one.sqrt(-1) + }.should raise_error(ArgumentError) + end + + it "raises ArgumentError if 2 arguments are given" do + lambda { + @one.sqrt(1, 1) + }.should raise_error(ArgumentError) + end + + it "raises TypeError if nil is given" do + lambda { + @one.sqrt(nil) + }.should raise_error(TypeError) + end + + it "raises TypeError if a string is given" do + lambda { + @one.sqrt("stuff") + }.should raise_error(TypeError) + end + + it "raises TypeError if a plain Object is given" do + lambda { + @one.sqrt(Object.new) + }.should raise_error(TypeError) + end + + it "returns 1 if precision is 0 or 1" do + @one.sqrt(1).should == 1 + @one.sqrt(0).should == 1 + end + + it "raises FloatDomainError on negative values" do + lambda { + BigDecimal('-1').sqrt(10) + }.should raise_error(FloatDomainError) + end + + it "returns positive infitinity for infinity" do + @infinity.sqrt(1).should == @infinity + end + + it "raises FloatDomainError for negative infinity" do + lambda { + @infinity_minus.sqrt(1) + }.should raise_error(FloatDomainError) + end + + it "raises FloatDomainError for NaN" do + lambda { + @nan.sqrt(1) + }.should raise_error(FloatDomainError) + end + + it "returns 0 for 0, +0.0 and -0.0" do + @zero.sqrt(1).should == 0 + @zero_pos.sqrt(1).should == 0 + @zero_neg.sqrt(1).should == 0 + end + +end diff --git a/spec/rubyspec/library/bigdecimal/sub_spec.rb b/spec/rubyspec/library/bigdecimal/sub_spec.rb new file mode 100644 index 0000000000..06ad3b79d2 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/sub_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#sub" do + + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @two = BigDecimal("2") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + end + + it "returns a - b with given precision" do + # documentation states, that precision is optional + # but implementation raises ArgumentError if not given. + + @two.sub(@one, 1).should == @one + @one.sub(@two, 1).should == @one_minus + @one.sub(@one_minus, 1).should == @two + @frac_2.sub(@frac_1, 1000000).should == BigDecimal("-0.1E-99999") + @frac_2.sub(@frac_1, 1).should == BigDecimal("-0.1E-99999") + # the above two examples puzzle me. + in_arow_one = BigDecimal("1.23456789") + in_arow_two = BigDecimal("1.2345678") + in_arow_one.sub(in_arow_two, 10).should == BigDecimal("0.9E-7") + @two.sub(@two,1).should == @zero + @frac_1.sub(@frac_1, 1000000).should == @zero + end + + it "returns NaN if NaN is involved" do + @one.sub(@nan, 1).nan?.should == true + @nan.sub(@one, 1).nan?.should == true + end + + it "returns NaN if both values are infinite with the same signs" do + @infinity.sub(@infinity, 1).nan?.should == true + @infinity_minus.sub(@infinity_minus, 1).nan?.should == true + end + + it "returns Infinity or -Infinity if these are involved" do + @infinity.sub(@infinity_minus, 1).should == @infinity + @infinity_minus.sub(@infinity, 1).should == @infinity_minus + @zero.sub(@infinity, 1).should == @infinity_minus + @frac_2.sub( @infinity, 1).should == @infinity_minus + @two.sub(@infinity, 1).should == @infinity_minus + end + +end diff --git a/spec/rubyspec/library/bigdecimal/to_f_spec.rb b/spec/rubyspec/library/bigdecimal/to_f_spec.rb new file mode 100644 index 0000000000..b490fce4d9 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/to_f_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#to_f" do + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @two = BigDecimal("2") + @three = BigDecimal("3") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + @vals = [@one, @zero, @two, @three, @frac_1, @frac_2] + @spec_vals = [@zero_pos, @zero_neg, @nan, @infinity, @infinity_minus] + end + + it "returns number of type float" do + BigDecimal("3.14159").to_f.should be_kind_of(Float) + @vals.each { |val| val.to_f.should be_kind_of(Float) } + @spec_vals.each { |val| val.to_f.should be_kind_of(Float) } + end + + it "rounds correctly to Float precision" do + bigdec = BigDecimal("3.141592653589793238462643383279502884197169399375") + bigdec.to_f.should be_close(3.14159265358979, TOLERANCE) + @one.to_f.should == 1.0 + @two.to_f.should == 2.0 + @three.to_f.should be_close(3.0, TOLERANCE) + @one_minus.to_f.should == -1.0 + + # regression test for [ruby-talk:338957] + BigDecimal("10.03").to_f.should == 10.03 + end + + it "properly handles special values" do + @zero.to_f.should == 0 + @zero.to_f.to_s.should == "0.0" + + @nan.to_f.nan?.should == true + + @infinity.to_f.infinite?.should == 1 + @infinity_minus.to_f.infinite?.should == -1 + end + + it "remembers negative zero when converted to float" do + @zero_neg.to_f.should == 0 + @zero_neg.to_f.to_s.should == "-0.0" + end +end + diff --git a/spec/rubyspec/library/bigdecimal/to_i_spec.rb b/spec/rubyspec/library/bigdecimal/to_i_spec.rb new file mode 100644 index 0000000000..8db69003c5 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/to_i_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_int', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#to_i" do + it_behaves_like(:bigdecimal_to_int, :to_i) +end diff --git a/spec/rubyspec/library/bigdecimal/to_int_spec.rb b/spec/rubyspec/library/bigdecimal/to_int_spec.rb new file mode 100644 index 0000000000..56a60d4a03 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/to_int_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_int', __FILE__) +require 'bigdecimal' + + +describe "BigDecimal#to_int" do + it_behaves_like(:bigdecimal_to_int, :to_int) +end diff --git a/spec/rubyspec/library/bigdecimal/to_s_spec.rb b/spec/rubyspec/library/bigdecimal/to_s_spec.rb new file mode 100644 index 0000000000..b97311c800 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/to_s_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#to_s" do + + before :each do + @bigdec_str = "3.14159265358979323846264338327950288419716939937" + @bigneg_str = "-3.1415926535897932384626433832795028841971693993" + @bigdec = BigDecimal(@bigdec_str) + @bigneg = BigDecimal(@bigneg_str) + end + + it "return type is of class String" do + @bigdec.to_s.kind_of?(String).should == true + @bigneg.to_s.kind_of?(String).should == true + end + + it "the default format looks like 0.xxxxEnn" do + @bigdec.to_s.should =~ /^0\.[0-9]*E[0-9]*$/i + end + + it "takes an optional argument" do + lambda {@bigdec.to_s("F")}.should_not raise_error() + end + + it "starts with + if + is supplied and value is positive" do + @bigdec.to_s("+").should =~ /^\+.*/ + @bigneg.to_s("+").should_not =~ /^\+.*/ + end + + it "inserts a space every n chars, if integer n is supplied" do + re =\ + /\A0\.314 159 265 358 979 323 846 264 338 327 950 288 419 716 939 937E1\z/i + @bigdec.to_s(3).should =~ re + + str1 = '-123.45678 90123 45678 9' + BigDecimal.new("-123.45678901234567890").to_s('5F').should == str1 + # trailing zeroes removed + BigDecimal.new("1.00000000000").to_s('1F').should == "1.0" + # 0 is treated as no spaces + BigDecimal.new("1.2345").to_s('0F').should == "1.2345" + end + + it "can return a leading space for values > 0" do + @bigdec.to_s(" F").should =~ /\ .*/ + @bigneg.to_s(" F").should_not =~ /\ .*/ + end + + it "removes trailing spaces in floating point notation" do + BigDecimal.new('-123.45678901234567890').to_s('F').should == "-123.4567890123456789" + BigDecimal.new('1.2500').to_s('F').should == "1.25" + BigDecimal.new('0000.00000').to_s('F').should == "0.0" + BigDecimal.new('-00.000010000').to_s('F').should == "-0.00001" + BigDecimal.new("5.00000E-2").to_s("F").should == "0.05" + + BigDecimal.new("500000").to_s("F").should == "500000.0" + BigDecimal.new("5E2").to_s("F").should == "500.0" + BigDecimal.new("-5E100").to_s("F").should == "-5" + "0" * 100 + ".0" + end + + it "can use engineering notation" do + @bigdec.to_s("E").should =~ /^0\.[0-9]*E[0-9]*$/i + end + + it "can use conventional floating point notation" do + @bigdec.to_s("F").should == @bigdec_str + @bigneg.to_s("F").should == @bigneg_str + str2 = "+123.45678901 23456789" + BigDecimal.new('123.45678901234567890').to_s('+8F').should == str2 + end + +end + diff --git a/spec/rubyspec/library/bigdecimal/truncate_spec.rb b/spec/rubyspec/library/bigdecimal/truncate_spec.rb new file mode 100644 index 0000000000..41f0afc8fa --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/truncate_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#truncate" do + + before :each do + @arr = ['3.14159', '8.7', "0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593014782083152134043E1"] + @big = BigDecimal("123456.789") + @nan = BigDecimal('NaN') + @infinity = BigDecimal('Infinity') + @infinity_negative = BigDecimal('-Infinity') + end + + it "returns value of type Integer." do + @arr.each do |x| + BigDecimal(x).truncate.kind_of?(Integer).should == true + end + end + + it "returns the integer part as a BigDecimal if no precision given" do + BigDecimal(@arr[0]).truncate.should == 3 + BigDecimal(@arr[1]).truncate.should == 8 + BigDecimal(@arr[2]).truncate.should == 3 + BigDecimal('0').truncate.should == 0 + BigDecimal('0.1').truncate.should == 0 + BigDecimal('-0.1').truncate.should == 0 + BigDecimal('1.5').truncate.should == 1 + BigDecimal('-1.5').truncate.should == -1 + BigDecimal('1E10').truncate.should == BigDecimal('1E10') + BigDecimal('-1E10').truncate.should == BigDecimal('-1E10') + BigDecimal('1.8888E10').truncate.should == BigDecimal('1.8888E10') + BigDecimal('-1E-1').truncate.should == 0 + end + + it "returns value of given precision otherwise" do + BigDecimal('-1.55').truncate(1).should == BigDecimal('-1.5') + BigDecimal('1.55').truncate(1).should == BigDecimal('1.5') + BigDecimal(@arr[0]).truncate(2).should == BigDecimal("3.14") + BigDecimal('123.456').truncate(2).should == BigDecimal("123.45") + BigDecimal('123.456789').truncate(4).should == BigDecimal("123.4567") + BigDecimal('0.456789').truncate(10).should == BigDecimal("0.456789") + BigDecimal('-1E-1').truncate(1).should == BigDecimal('-0.1') + BigDecimal('-1E-1').truncate(2).should == BigDecimal('-0.1E0') + BigDecimal('-1E-1').truncate.should == BigDecimal('0') + BigDecimal('-1E-1').truncate(0).should == BigDecimal('0') + BigDecimal('-1E-1').truncate(-1).should == BigDecimal('0') + BigDecimal('-1E-1').truncate(-2).should == BigDecimal('0') + + BigDecimal(@arr[1]).truncate(1).should == BigDecimal("8.7") + BigDecimal(@arr[2]).truncate(100).should == BigDecimal(\ + "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679") + end + + it "sets n digits left of the decimal point to 0, if given n < 0" do + @big.truncate(-1).should == BigDecimal("123450.0") + @big.truncate(-2).should == BigDecimal("123400.0") + BigDecimal(@arr[2]).truncate(-1).should == 0 + end + + it "returns NaN if self is NaN" do + @nan.truncate(-1).nan?.should == true + @nan.truncate(+1).nan?.should == true + @nan.truncate(0).nan?.should == true + end + + it "returns Infinity if self is infinite" do + @infinity.truncate(-1).should == @infinity + @infinity.truncate(+1).should == @infinity + @infinity.truncate(0).should == @infinity + + @infinity_negative.truncate(-1).should == @infinity_negative + @infinity_negative.truncate(+1).should == @infinity_negative + @infinity_negative.truncate(0).should == @infinity_negative + end + + it "returns the same value if self is special value" do + lambda { @nan.truncate }.should raise_error(FloatDomainError) + lambda { @infinity.truncate }.should raise_error(FloatDomainError) + lambda { @infinity_negative.truncate }.should raise_error(FloatDomainError) + end +end diff --git a/spec/rubyspec/library/bigdecimal/uminus_spec.rb b/spec/rubyspec/library/bigdecimal/uminus_spec.rb new file mode 100644 index 0000000000..901f9f59ce --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/uminus_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#-@" do + before :each do + @one = BigDecimal("1") + @zero = BigDecimal("0") + @zero_pos = BigDecimal("+0") + @zero_neg = BigDecimal("-0") + @nan = BigDecimal("NaN") + @infinity = BigDecimal("Infinity") + @infinity_minus = BigDecimal("-Infinity") + @one_minus = BigDecimal("-1") + @frac_1 = BigDecimal("1E-99999") + @frac_2 = BigDecimal("0.9E-99999") + @big = BigDecimal("333E99999") + @big_neg = BigDecimal("-333E99999") + @values = [@one, @zero, @zero_pos, @zero_neg, @infinity, + @infinity_minus, @one_minus, @frac_1, @frac_2, @big, @big_neg] + end + + it "negates self" do + @one.send(:-@).should == @one_minus + @one_minus.send(:-@).should == @one + @frac_1.send(:-@).should == BigDecimal("-1E-99999") + @frac_2.send(:-@).should == BigDecimal("-0.9E-99999") + @big.send(:-@).should == @big_neg + @big_neg.send(:-@).should == @big + BigDecimal("2.221").send(:-@).should == BigDecimal("-2.221") + BigDecimal("2E10000").send(:-@).should == BigDecimal("-2E10000") + some_number = BigDecimal("2455999221.5512") + some_number_neg = BigDecimal("-2455999221.5512") + some_number.send(:-@).should == some_number_neg + (-BigDecimal("-5.5")).should == BigDecimal("5.5") + another_number = BigDecimal("-8.551551551551551551") + another_number_pos = BigDecimal("8.551551551551551551") + another_number.send(:-@).should == another_number_pos + @values.each do |val| + (val.send(:-@).send(:-@)).should == val + end + end + + it "properly handles special values" do + @infinity.send(:-@).should == @infinity_minus + @infinity_minus.send(:-@).should == @infinity + @infinity.send(:-@).infinite?.should == -1 + @infinity_minus.send(:-@).infinite?.should == 1 + + @zero.send(:-@).should == @zero + @zero.send(:-@).sign.should == -1 + @zero_pos.send(:-@).should == @zero + @zero_pos.send(:-@).sign.should == -1 + @zero_neg.send(:-@).should == @zero + @zero_neg.send(:-@).sign.should == 1 + + @nan.send(:-@).nan?.should == true + end +end diff --git a/spec/rubyspec/library/bigdecimal/uplus_spec.rb b/spec/rubyspec/library/bigdecimal/uplus_spec.rb new file mode 100644 index 0000000000..00aadc723c --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/uplus_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#+@" do + it "returns the same value with same sign (twos complement)" do + first = BigDecimal("34.56") + first.send(:+@).should == first + second = BigDecimal("-34.56") + second.send(:+@).should == second + third = BigDecimal("0.0") + third.send(:+@).should == third + fourth = BigDecimal("2E1000000") + fourth.send(:+@).should == fourth + fifth = BigDecimal("123456789E-1000000") + fifth.send(:+@).should == fifth + end +end + + + diff --git a/spec/rubyspec/library/bigdecimal/ver_spec.rb b/spec/rubyspec/library/bigdecimal/ver_spec.rb new file mode 100644 index 0000000000..61b073d8f1 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/ver_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal.ver" do + + it "returns the Version number" do + lambda {BigDecimal.ver }.should_not raise_error() + BigDecimal.ver.should_not == nil + end + +end diff --git a/spec/rubyspec/library/bigdecimal/zero_spec.rb b/spec/rubyspec/library/bigdecimal/zero_spec.rb new file mode 100644 index 0000000000..7dbb0fa7f0 --- /dev/null +++ b/spec/rubyspec/library/bigdecimal/zero_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#zero?" do + + it "returns true if self does equal zero" do + really_small_zero = BigDecimal("0E-200000000") + really_big_zero = BigDecimal("0E200000000000") + really_small_zero.zero?.should == true + really_big_zero.zero?.should == true + BigDecimal("0.000000000000000000000000").zero?.should == true + BigDecimal("0").zero?.should == true + BigDecimal("0E0").zero?.should == true + BigDecimal("+0").zero?.should == true + BigDecimal("-0").zero?.should == true + end + + it "returns false otherwise" do + BigDecimal("0000000001").zero?.should == false + BigDecimal("2E40001").zero?.should == false + BigDecimal("3E-20001").zero?.should == false + BigDecimal("Infinity").zero?.should == false + BigDecimal("-Infinity").zero?.should == false + BigDecimal("NaN").zero?.should == false + end + +end + diff --git a/spec/rubyspec/library/bigmath/log_spec.rb b/spec/rubyspec/library/bigmath/log_spec.rb new file mode 100644 index 0000000000..126461f0e8 --- /dev/null +++ b/spec/rubyspec/library/bigmath/log_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'bigdecimal' + +describe "BigDecimal#log" do + it "handles high-precision Rational arguments" do + result = BigDecimal('0.22314354220170971436137296411949880462556361100856391620766259404746040597133837784E0') + r = Rational(1_234_567_890, 987_654_321) + BigMath.log(r, 50).should == result + end +end diff --git a/spec/rubyspec/library/cgi/cookie/domain_spec.rb b/spec/rubyspec/library/cgi/cookie/domain_spec.rb new file mode 100644 index 0000000000..05137dd4d1 --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/domain_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#domain" do + it "returns self's domain" do + cookie = CGI::Cookie.new("test-cookie") + cookie.domain.should be_nil + + cookie = CGI::Cookie.new("name" => "test-cookie", "domain" => "example.com") + cookie.domain.should == "example.com" + end +end + +describe "CGI::Cookie#domain=" do + it "sets self's domain" do + cookie = CGI::Cookie.new("test-cookie") + cookie.domain = "test.com" + cookie.domain.should == "test.com" + + cookie.domain = "example.com" + cookie.domain.should == "example.com" + end +end diff --git a/spec/rubyspec/library/cgi/cookie/expires_spec.rb b/spec/rubyspec/library/cgi/cookie/expires_spec.rb new file mode 100644 index 0000000000..89f18fe071 --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/expires_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#expires" do + it "returns self's expiration date" do + cookie = CGI::Cookie.new("test-cookie") + cookie.expires.should be_nil + + cookie = CGI::Cookie.new("name" => "test-cookie", "expires" => Time.at(1196524602)) + cookie.expires.should == Time.at(1196524602) + end +end + +describe "CGI::Cookie#expires=" do + it "sets self's expiration date" do + cookie = CGI::Cookie.new("test-cookie") + cookie.expires = Time.at(1196524602) + cookie.expires.should == Time.at(1196524602) + + cookie.expires = Time.at(1196525000) + cookie.expires.should == Time.at(1196525000) + end +end diff --git a/spec/rubyspec/library/cgi/cookie/initialize_spec.rb b/spec/rubyspec/library/cgi/cookie/initialize_spec.rb new file mode 100644 index 0000000000..02f2a8ed2b --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/initialize_spec.rb @@ -0,0 +1,147 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#initialize when passed String" do + before :each do + @cookie = CGI::Cookie.allocate + end + + it "sets the self's name to the passed String" do + @cookie.send(:initialize, "test-cookie") + @cookie.name.should == "test-cookie" + end + + it "sets the self's value to an empty Array" do + @cookie.send(:initialize, "test-cookie") + @cookie.value.should == [] + end + + it "sets self to a non-secure cookie" do + @cookie.send(:initialize, "test") + @cookie.secure.should be_false + end + + it "does set self's path to an empty String when ENV[\"SCRIPT_NAME\"] is not set" do + @cookie.send(:initialize, "test-cookie") + @cookie.path.should == "" + end + + it "does set self's path based on ENV[\"SCRIPT_NAME\"] when ENV[\"SCRIPT_NAME\"] is set" do + old_script_name = ENV["SCRIPT_NAME"] + + begin + ENV["SCRIPT_NAME"] = "some/path/script.rb" + @cookie.send(:initialize, "test-cookie") + @cookie.path.should == "some/path/" + + ENV["SCRIPT_NAME"] = "script.rb" + @cookie.send(:initialize, "test-cookie") + @cookie.path.should == "" + + ENV["SCRIPT_NAME"] = nil + @cookie.send(:initialize, "test-cookie") + @cookie.path.should == "" + ensure + ENV["SCRIPT_NAME"] = old_script_name + end + end + + it "does not set self's expiration date" do + @cookie.expires.should be_nil + end + + it "does not set self's domain" do + @cookie.domain.should be_nil + end +end + +describe "CGI::Cookie#initialize when passed Hash" do + before :each do + @cookie = CGI::Cookie.allocate + end + + it "sets self's contents based on the passed Hash" do + @cookie.send(:initialize, + 'name' => 'test-cookie', + 'value' => ["one", "two", "three"], + 'path' => 'some/path/', + 'domain' => 'example.com', + 'expires' => Time.at(1196524602), + 'secure' => true) + + @cookie.name.should == "test-cookie" + @cookie.value.should == ["one", "two", "three"] + @cookie.path.should == "some/path/" + @cookie.domain.should == "example.com" + @cookie.expires.should == Time.at(1196524602) + @cookie.secure.should be_true + end + + it "does set self's path based on ENV[\"SCRIPT_NAME\"] when the Hash has no 'path' entry" do + old_script_name = ENV["SCRIPT_NAME"] + + begin + ENV["SCRIPT_NAME"] = "some/path/script.rb" + @cookie.send(:initialize, 'name' => 'test-cookie') + @cookie.path.should == "some/path/" + + ENV["SCRIPT_NAME"] = "script.rb" + @cookie.send(:initialize, 'name' => 'test-cookie') + @cookie.path.should == "" + + ENV["SCRIPT_NAME"] = nil + @cookie.send(:initialize, 'name' => 'test-cookie') + @cookie.path.should == "" + ensure + ENV["SCRIPT_NAME"] = old_script_name + end + end + + it "tries to convert the Hash's 'value' to an Array using #Array" do + obj = mock("Converted To Array") + obj.should_receive(:to_ary).and_return(["1", "2", "3"]) + @cookie.send(:initialize, + 'name' => 'test-cookie', + 'value' => obj) + @cookie.value.should == [ "1", "2", "3" ] + + obj = mock("Converted To Array") + obj.should_receive(:to_a).and_return(["one", "two", "three"]) + @cookie.send(:initialize, + 'name' => 'test-cookie', + 'value' => obj) + @cookie.value.should == [ "one", "two", "three" ] + + obj = mock("Put into an Array") + @cookie.send(:initialize, + 'name' => 'test-cookie', + 'value' => obj) + @cookie.value.should == [ obj ] + end + + it "raises a ArgumentError when the passed Hash has no 'name' entry" do + lambda { @cookie.send(:initialize, {}) }.should raise_error(ArgumentError) + lambda { @cookie.send(:initialize, "value" => "test") }.should raise_error(ArgumentError) + end +end + +describe "CGI::Cookie#initialize when passed String, values ..." do + before :each do + @cookie = CGI::Cookie.allocate + end + + it "sets the self's name to the passed String" do + @cookie.send(:initialize, "test-cookie", "one", "two", "three") + @cookie.name.should == "test-cookie" + end + + it "sets the self's value to an Array containing all passed values" do + @cookie.send(:initialize, "test-cookie", "one", "two", "three") + @cookie.value.should == ["one", "two", "three"] + end + + it "sets self to a non-secure cookie" do + @cookie.send(:initialize, "test", "one", "two", "three") + @cookie.secure.should be_false + end +end diff --git a/spec/rubyspec/library/cgi/cookie/name_spec.rb b/spec/rubyspec/library/cgi/cookie/name_spec.rb new file mode 100644 index 0000000000..8a6be95944 --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/name_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#name" do + it "returns self's name" do + cookie = CGI::Cookie.new("test-cookie") + cookie.name.should == "test-cookie" + + cookie = CGI::Cookie.new("name" => "another cookie") + cookie.name.should == "another cookie" + end +end + +describe "CGI::Cookie#name=" do + it "sets self's expiration date" do + cookie = CGI::Cookie.new("test-cookie") + cookie.name = "another name" + cookie.name.should == "another name" + + cookie.name = "and one more" + cookie.name.should == "and one more" + end +end diff --git a/spec/rubyspec/library/cgi/cookie/parse_spec.rb b/spec/rubyspec/library/cgi/cookie/parse_spec.rb new file mode 100644 index 0000000000..dc8498dcb4 --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/parse_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie.parse" do + it "parses a raw cookie string into a hash of Cookies" do + expected = { "test-cookie" => ["one", "two", "three"] } + CGI::Cookie.parse("test-cookie=one&two&three").should == expected + + expected = { "second cookie" => ["three", "four"], "first cookie" => ["one", "two"] } + CGI::Cookie.parse("first cookie=one&two;second cookie=three&four").should == expected + end + + it "unescapes the Cookie values" do + cookie = "test-cookie=+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E" + expected = { "test-cookie" => [ " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" ] } + CGI::Cookie.parse(cookie).should == expected + end +end diff --git a/spec/rubyspec/library/cgi/cookie/path_spec.rb b/spec/rubyspec/library/cgi/cookie/path_spec.rb new file mode 100644 index 0000000000..2a21a55264 --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/path_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#path" do + it "returns self's path" do + cookie = CGI::Cookie.new("test-cookie") + cookie.path.should == "" + + cookie = CGI::Cookie.new("name" => "test-cookie", "path" => "/some/path/") + cookie.path.should == "/some/path/" + end +end + +describe "CGI::Cookie#path=" do + it "sets self's path" do + cookie = CGI::Cookie.new("test-cookie") + cookie.path = "/some/path/" + cookie.path.should == "/some/path/" + + cookie.path = "/another/path/" + cookie.path.should == "/another/path/" + end +end diff --git a/spec/rubyspec/library/cgi/cookie/secure_spec.rb b/spec/rubyspec/library/cgi/cookie/secure_spec.rb new file mode 100644 index 0000000000..37e9dbfda9 --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/secure_spec.rb @@ -0,0 +1,70 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#secure" do + before :each do + @cookie = CGI::Cookie.new("test-cookie") + end + + it "returns whether self is a secure cookie or not" do + @cookie.secure = true + @cookie.secure.should be_true + + @cookie.secure = false + @cookie.secure.should be_false + end +end + +describe "CGI::Cookie#secure= when passed true" do + before :each do + @cookie = CGI::Cookie.new("test-cookie") + end + + it "returns true" do + (@cookie.secure = true).should be_true + end + + it "sets self to a secure cookie" do + @cookie.secure = true + @cookie.secure.should be_true + end +end + +describe "CGI::Cookie#secure= when passed false" do + before :each do + @cookie = CGI::Cookie.new("test-cookie") + end + + it "returns false" do + (@cookie.secure = false).should be_false + end + + it "sets self to a non-secure cookie" do + @cookie.secure = false + @cookie.secure.should be_false + end +end + +describe "CGI::Cookie#secure= when passed Object" do + before :each do + @cookie = CGI::Cookie.new("test-cookie") + end + + it "does not change self's secure value" do + @cookie.secure = false + + @cookie.secure = Object.new + @cookie.secure.should be_false + + @cookie.secure = "Test" + @cookie.secure.should be_false + + @cookie.secure = true + + @cookie.secure = Object.new + @cookie.secure.should be_true + + @cookie.secure = "Test" + @cookie.secure.should be_true + end +end diff --git a/spec/rubyspec/library/cgi/cookie/to_s_spec.rb b/spec/rubyspec/library/cgi/cookie/to_s_spec.rb new file mode 100644 index 0000000000..61eb9fb984 --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/to_s_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#to_s" do + it "returns a String representation of self" do + cookie = CGI::Cookie.new("test-cookie") + cookie.to_s.should == "test-cookie=; path=" + + cookie = CGI::Cookie.new("test-cookie", "value") + cookie.to_s.should == "test-cookie=value; path=" + + cookie = CGI::Cookie.new("test-cookie", "one", "two", "three") + cookie.to_s.should == "test-cookie=one&two&three; path=" + + cookie = CGI::Cookie.new( + 'name' => 'test-cookie', + 'value' => ["one", "two", "three"], + 'path' => 'some/path/', + 'domain' => 'example.com', + 'expires' => Time.at(1196524602), + 'secure' => true) + cookie.to_s.should == "test-cookie=one&two&three; domain=example.com; path=some/path/; expires=Sat, 01 Dec 2007 15:56:42 GMT; secure" + end + + it "escapes the self's values" do + cookie = CGI::Cookie.new("test-cookie", " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") + cookie.to_s.should == "test-cookie=+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E; path=" + end +end diff --git a/spec/rubyspec/library/cgi/cookie/value_spec.rb b/spec/rubyspec/library/cgi/cookie/value_spec.rb new file mode 100644 index 0000000000..81e3daf7de --- /dev/null +++ b/spec/rubyspec/library/cgi/cookie/value_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::Cookie#value" do + it "returns self's value" do + cookie = CGI::Cookie.new("test-cookie") + cookie.value.should == [] + + cookie = CGI::Cookie.new("test-cookie", "one") + cookie.value.should == ["one"] + + cookie = CGI::Cookie.new("test-cookie", "one", "two", "three") + cookie.value.should == ["one", "two", "three"] + + cookie = CGI::Cookie.new("name" => "test-cookie", "value" => ["one", "two", "three"]) + cookie.value.should == ["one", "two", "three"] + end + + it "is in synch with self" do + fail = [] + [ + :pop, + :shift, + [:<<, "Hello"], + [:push, "Hello"], + [:unshift, "World"], + [:replace, ["A", "B"]], + [:[]=, 1, "Set"], + [:delete, "first"], + [:delete_at, 0], + ].each do |method, *args| + cookie1 = CGI::Cookie.new("test-cookie", "first", "second") + cookie2 = CGI::Cookie.new("test-cookie", "first", "second") + cookie1.send(method, *args) + cookie2.value.send(method, *args) + fail << method unless cookie1.value == cookie2.value + end + fail.should be_empty + end +end + +describe "CGI::Cookie#value=" do + before :each do + @cookie = CGI::Cookie.new("test-cookie") + end + + it "sets self's value" do + @cookie.value = ["one"] + @cookie.value.should == ["one"] + + @cookie.value = ["one", "two", "three"] + @cookie.value.should == ["one", "two", "three"] + end + + it "automatically converts the passed Object to an Array using #Array" do + @cookie.value = "test" + @cookie.value.should == ["test"] + + obj = mock("to_a") + obj.should_receive(:to_a).and_return(["1", "2"]) + @cookie.value = obj + @cookie.value.should == ["1", "2"] + + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(["1", "2"]) + @cookie.value = obj + @cookie.value.should == ["1", "2"] + end + + it "does keep self and the values in sync" do + @cookie.value = ["one", "two", "three"] + @cookie[0].should == "one" + @cookie[1].should == "two" + @cookie[2].should == "three" + end +end diff --git a/spec/rubyspec/library/cgi/escapeElement_spec.rb b/spec/rubyspec/library/cgi/escapeElement_spec.rb new file mode 100644 index 0000000000..18f804c7da --- /dev/null +++ b/spec/rubyspec/library/cgi/escapeElement_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.escapeElement when passed String, elements, ..." do + it "escapes only the tags of the passed elements in the passed String" do + res = CGI.escapeElement('
    ', "A", "IMG") + res.should == "
    <A HREF="url"></A>" + + res = CGI.escapeElement('
    ', ["A", "IMG"]) + res.should == "
    <A HREF="url"></A>" + end + + it "is case-insensitive" do + res = CGI.escapeElement('
    ', "a", "img") + res.should == '
    <A HREF="url"></A>' + + res = CGI.escapeElement('
    ', "A", "IMG") + res.should == '
    <a href="url"></a>' + end +end diff --git a/spec/rubyspec/library/cgi/escapeHTML_spec.rb b/spec/rubyspec/library/cgi/escapeHTML_spec.rb new file mode 100644 index 0000000000..a3267db4a1 --- /dev/null +++ b/spec/rubyspec/library/cgi/escapeHTML_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.escapeHTML" do + it "escapes special HTML characters (&\"<>') in the passed argument" do + CGI.escapeHTML(%[& < > " ']).should == '& < > " '' + end + + it "does not escape any other characters" do + chars = " !\#$%()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + CGI.escapeHTML(chars).should == chars + end +end diff --git a/spec/rubyspec/library/cgi/escape_spec.rb b/spec/rubyspec/library/cgi/escape_spec.rb new file mode 100644 index 0000000000..dc1e2937e8 --- /dev/null +++ b/spec/rubyspec/library/cgi/escape_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.escape" do + it "url-encodes the passed argument" do + input = " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + expected = "+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E" + CGI.escape(input).should == expected + + input = "http://ja.wikipedia.org/wiki/\343\203\255\343\203\240\343\202\271\343\202\253\343\203\273\343\203\221\343\203\255\343\203\273\343\202\246\343\203\253\343\203\273\343\203\251\343\203\224\343\203\245\343\202\277" + expected = 'http%3A%2F%2Fja.wikipedia.org%2Fwiki%2F%E3%83%AD%E3%83%A0%E3%82%B9%E3%82%AB%E3%83%BB%E3%83%91%E3%83%AD%E3%83%BB%E3%82%A6%E3%83%AB%E3%83%BB%E3%83%A9%E3%83%94%E3%83%A5%E3%82%BF' + CGI.escape(input).should == expected + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/a_spec.rb b/spec/rubyspec/library/cgi/htmlextension/a_spec.rb new file mode 100644 index 0000000000..a4c0ee684c --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/a_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#a" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed a String" do + it "returns an 'a'-element, using the passed String as the 'href'-attribute" do + output = @html.a("http://www.example.com") + output.should equal_element("A", "HREF" => "http://www.example.com") + end + + it "includes the passed block's return value when passed a block" do + output = @html.a("http://www.example.com") { "Example" } + output.should equal_element("A", { "HREF" => "http://www.example.com" }, "Example") + end + end + + describe "when passed a Hash" do + it "returns an 'a'-element, using the passed Hash for attributes" do + attributes = {"HREF" => "http://www.example.com", "TARGET" => "_top"} + @html.a(attributes).should equal_element("A", attributes) + end + + it "includes the passed block's return value when passed a block" do + attributes = {"HREF" => "http://www.example.com", "TARGET" => "_top"} + @html.a(attributes) { "Example" }.should equal_element("A", attributes, "Example") + end + end + + describe "when each HTML generation" do + it "returns the doctype declaration for HTML3" do + CGISpecs.cgi_new("html3").a.should == %() + CGISpecs.cgi_new("html3").a { "link text" }.should == %(link text) + end + + it "returns the doctype declaration for HTML4" do + CGISpecs.cgi_new("html4").a.should == %() + CGISpecs.cgi_new("html4").a { "link text" }.should == %(link text) + end + it "returns the doctype declaration for the Transitional version of HTML4" do + CGISpecs.cgi_new("html4Tr").a.should == %() + CGISpecs.cgi_new("html4Tr").a { "link text" }.should == %(link text) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/base_spec.rb b/spec/rubyspec/library/cgi/htmlextension/base_spec.rb new file mode 100644 index 0000000000..36a8cc55c8 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/base_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#base" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when bassed a String" do + it "returns a 'base'-element, using the passed String as the 'href'-attribute" do + output = @html.base("http://www.example.com") + output.should equal_element("BASE", {"HREF" => "http://www.example.com"}, nil, not_closed: true) + end + + it "ignores a passed block" do + output = @html.base("http://www.example.com") { "Example" } + output.should equal_element("BASE", {"HREF" => "http://www.example.com"}, nil, not_closed: true) + end + end + + describe "when passed a Hash" do + it "returns a 'base'-element, using the passed Hash for attributes" do + output = @html.base("HREF" => "http://www.example.com", "ID" => "test") + output.should equal_element("BASE", {"HREF" => "http://www.example.com", "ID" => "test"}, nil, not_closed: true) + end + + it "ignores a passed block" do + output = @html.base("HREF" => "http://www.example.com", "ID" => "test") { "Example" } + output.should equal_element("BASE", {"HREF" => "http://www.example.com", "ID" => "test"}, nil, not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/blockquote_spec.rb b/spec/rubyspec/library/cgi/htmlextension/blockquote_spec.rb new file mode 100644 index 0000000000..f9848375e8 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/blockquote_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#blockquote" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed a String" do + it "returns a 'blockquote'-element, using the passed String for the 'cite'-attribute" do + output = @html.blockquote("http://www.example.com/quotes/foo.html") + output.should equal_element("BLOCKQUOTE", "CITE" => "http://www.example.com/quotes/foo.html") + end + + it "includes the passed block's return value when passed a block" do + output = @html.blockquote("http://www.example.com/quotes/foo.html") { "Foo!" } + output.should equal_element("BLOCKQUOTE", { "CITE" => "http://www.example.com/quotes/foo.html" }, "Foo!") + end + end + + describe "when passed a Hash" do + it "returns a 'blockquote'-element, using the passed Hash for attributes" do + output = @html.blockquote("CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test") + output.should equal_element("BLOCKQUOTE", "CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test") + end + + it "includes the passed block's return value when passed a block" do + output = @html.blockquote("CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test") { "Foo!" } + output.should equal_element("BLOCKQUOTE", {"CITE" => "http://www.example.com/quotes/foo.html", "ID" => "test"}, "Foo!") + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/br_spec.rb b/spec/rubyspec/library/cgi/htmlextension/br_spec.rb new file mode 100644 index 0000000000..875d335fe5 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/br_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#br" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when each HTML generation" do + it "returns the doctype declaration for HTML3" do + CGISpecs.cgi_new("html3").br.should == "
    " + end + + it "returns the doctype declaration for HTML4" do + CGISpecs.cgi_new("html4").br.should == "
    " + end + it "returns the doctype declaration for the Transitional version of HTML4" do + CGISpecs.cgi_new("html4Tr").br.should == "
    " + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/caption_spec.rb b/spec/rubyspec/library/cgi/htmlextension/caption_spec.rb new file mode 100644 index 0000000000..a1b5f4d964 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/caption_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#caption" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed a String" do + it "returns a 'caption'-element, using the passed String for the 'align'-attribute" do + output = @html.caption("left") + output.should equal_element("CAPTION", "ALIGN" => "left") + end + + it "includes the passed block's return value when passed a block" do + output = @html.caption("left") { "Capital Cities" } + output.should equal_element("CAPTION", {"ALIGN" => "left"}, "Capital Cities") + end + end + + describe "when passed a Hash" do + it "returns a 'caption'-element, using the passed Hash for attributes" do + output = @html.caption("ALIGN" => "left", "ID" => "test") + output.should equal_element("CAPTION", "ALIGN" => "left", "ID" => "test") + end + + it "includes the passed block's return value when passed a block" do + output = @html.caption("ALIGN" => "left", "ID" => "test") { "Capital Cities" } + output.should equal_element("CAPTION", {"ALIGN" => "left", "ID" => "test"}, "Capital Cities") + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/checkbox_group_spec.rb b/spec/rubyspec/library/cgi/htmlextension/checkbox_group_spec.rb new file mode 100644 index 0000000000..f739d92a53 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/checkbox_group_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#checkbox_group" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed name, values ..." do + it "returns a sequence of 'checkbox'-elements with the passed name and the passed values" do + output = CGISpecs.split(@html.checkbox_group("test", "foo", "bar", "baz")) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true) + end + + it "allows passing a value inside an Array" do + output = CGISpecs.split(@html.checkbox_group("test", ["foo"], "bar", ["baz"])) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true) + end + + it "allows passing a value as an Array containing the value and the checked state or a label" do + output = CGISpecs.split(@html.checkbox_group("test", ["foo"], ["bar", true], ["baz", "label for baz"])) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "label for baz", not_closed: true) + end + + it "allows passing a value as an Array containing the value, a label and the checked state" do + output = CGISpecs.split(@html.checkbox_group("test", ["foo", "label for foo", true], ["bar", "label for bar", false], ["baz", "label for baz", true])) + output[0].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "label for foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "label for bar", not_closed: true) + output[2].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "label for baz", not_closed: true) + end + + it "returns an empty String when passed no values" do + @html.checkbox_group("test").should == "" + end + + it "ignores a passed block" do + output = CGISpecs.split(@html.checkbox_group("test", "foo", "bar", "baz") { "test" }) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true) + end + end + + describe "when passed Hash" do + it "uses the passed Hash to generate the checkbox sequence" do + output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"])) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true) + + output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => [["foo"], ["bar", true], "baz"])) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true) + + output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "1"}, "Foo", not_closed: true) + output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "checkbox", "VALUE" => "2"}, "Bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "Baz"}, "Baz", not_closed: true) + end + + it "ignores a passed block" do + output = CGISpecs.split(@html.checkbox_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"]) { "test" }) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "checkbox", "VALUE" => "baz"}, "baz", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/checkbox_spec.rb b/spec/rubyspec/library/cgi/htmlextension/checkbox_spec.rb new file mode 100644 index 0000000000..3abb3b4a31 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/checkbox_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#checkbox" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns a checkbox-'input'-element without a name" do + output = @html.checkbox + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "checkbox"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.checkbox { "test" } + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "checkbox"}, "", not_closed: true) + end + end + + describe "when passed name" do + it "returns a checkbox-'input'-element with the passed name" do + output = @html.checkbox("test") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.checkbox("test") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox"}, "", not_closed: true) + end + end + + describe "CGI::HtmlExtension#checkbox when passed name, value" do + it "returns a checkbox-'input'-element with the passed name and value" do + output = @html.checkbox("test", "test-value") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.checkbox("test", "test-value") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true) + end + end + + describe "when passed name, value, checked" do + it "returns a checked checkbox-'input'-element with the passed name and value when checked is true" do + output = @html.checkbox("test", "test-value", true) + output.should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true) + + output = @html.checkbox("test", "test-value", false) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true) + + output = @html.checkbox("test", "test-value", nil) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.checkbox("test", "test-value", nil) { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "checkbox", "VALUE" => "test-value"}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a checkbox-'input'-element using the passed Hash for attributes" do + attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true} + output = @html.checkbox(attributes) + output.should equal_element("INPUT", attributes, "", not_closed: true) + end + + it "ignores a passed block" do + attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true} + output = @html.checkbox(attributes) { "test" } + output.should equal_element("INPUT", attributes, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/doctype_spec.rb b/spec/rubyspec/library/cgi/htmlextension/doctype_spec.rb new file mode 100644 index 0000000000..339ef20db9 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/doctype_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#doctype" do + describe "when each HTML generation" do + it "returns the doctype declaration for HTML3" do + expect = '' + CGISpecs.cgi_new("html3").doctype.should == expect + end + + it "returns the doctype declaration for HTML4" do + expect = '' + CGISpecs.cgi_new("html4").doctype.should == expect + end + + it "returns the doctype declaration for the Frameset version of HTML4" do + expect = '' + CGISpecs.cgi_new("html4Fr").doctype.should == expect + end + + it "returns the doctype declaration for the Transitional version of HTML4" do + expect = '' + CGISpecs.cgi_new("html4Tr").doctype.should == expect + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/file_field_spec.rb b/spec/rubyspec/library/cgi/htmlextension/file_field_spec.rb new file mode 100644 index 0000000000..57d91b5fd9 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/file_field_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#file_field" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns a file-'input'-element without a name and a size of 20" do + output = @html.file_field + output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "", "TYPE" => "file"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.file_field { "test" } + output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "", "TYPE" => "file"}, "", not_closed: true) + end + end + + describe "when passed name" do + it "returns a checkbox-'input'-element with the passed name" do + output = @html.file_field("Example") + output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.file_field("Example") { "test" } + output.should equal_element("INPUT", {"SIZE" => 20, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true) + end + end + + describe "when passed name, size" do + it "returns a checkbox-'input'-element with the passed name and size" do + output = @html.file_field("Example", 40) + output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.file_field("Example", 40) { "test" } + output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file"}, "", not_closed: true) + end + end + + describe "when passed name, size, maxlength" do + it "returns a checkbox-'input'-element with the passed name, size and maxlength" do + output = @html.file_field("Example", 40, 100) + output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file", "MAXLENGTH" => 100}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.file_field("Example", 40, 100) { "test" } + output.should equal_element("INPUT", {"SIZE" => 40, "NAME" => "Example", "TYPE" => "file", "MAXLENGTH" => 100}, "", not_closed: true) + end + end + + describe "when passed a Hash" do + it "returns a file-'input'-element using the passed Hash for attributes" do + output = @html.file_field("NAME" => "test", "SIZE" => 40) + output.should equal_element("INPUT", {"NAME" => "test", "SIZE" => 40}, "", not_closed: true) + + output = @html.file_field("NAME" => "test", "MAXLENGTH" => 100) + output.should equal_element("INPUT", {"NAME" => "test", "MAXLENGTH" => 100}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.file_field("NAME" => "test", "SIZE" => 40) { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "SIZE" => 40}, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/fixtures/common.rb b/spec/rubyspec/library/cgi/htmlextension/fixtures/common.rb new file mode 100644 index 0000000000..4f951f6389 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/fixtures/common.rb @@ -0,0 +1,15 @@ +module CGISpecs + def self.cgi_new(html = "html4") + old_request_method = ENV['REQUEST_METHOD'] + ENV['REQUEST_METHOD'] = "GET" + begin + CGI.new(tag_maker: html) + ensure + ENV['REQUEST_METHOD'] = old_request_method + end + end + + def self.split(string) + string.split("<").reject { |x| x.empty? }.map { |x| "<#{x}" } + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/form_spec.rb b/spec/rubyspec/library/cgi/htmlextension/form_spec.rb new file mode 100644 index 0000000000..0389910cd6 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/form_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#form" do + before :each do + @html = CGISpecs.cgi_new + @html.stub!(:script_name).and_return("/path/to/some/script") + end + + describe "when passed no arguments" do + it "returns a 'form'-element" do + output = @html.form + output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/path/to/some/script"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.form { "test" } + output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/path/to/some/script"}, "test") + end + end + + describe "when passed method" do + it "returns a 'form'-element with the passed method" do + output = @html.form("get") + output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/path/to/some/script"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.form("get") { "test" } + output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/path/to/some/script"}, "test") + end + end + + describe "when passed method, action" do + it "returns a 'form'-element with the passed method and the passed action" do + output = @html.form("get", "/some/other/script") + output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/some/other/script"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.form("get", "/some/other/script") { "test" } + output.should equal_element("FORM", {"ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ACTION" => "/some/other/script"}, "test") + end + end + + describe "when passed method, action, enctype" do + it "returns a 'form'-element with the passed method, action and enctype" do + output = @html.form("get", "/some/other/script", "multipart/form-data") + output.should equal_element("FORM", {"ENCTYPE" => "multipart/form-data", "METHOD" => "get", "ACTION" => "/some/other/script"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.form("get", "/some/other/script", "multipart/form-data") { "test" } + output.should equal_element("FORM", {"ENCTYPE" => "multipart/form-data", "METHOD" => "get", "ACTION" => "/some/other/script"}, "test") + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/frame_spec.rb b/spec/rubyspec/library/cgi/htmlextension/frame_spec.rb new file mode 100644 index 0000000000..d433d058e7 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/frame_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require 'cgi' + +describe "CGI::HtmlExtension#frame" do + before :each do + @html = CGISpecs.cgi_new("html4Fr") + end + + it "initializes the HTML Generation methods for the Frameset version of HTML4" do + @html.frameset.should == "" + @html.frameset { "link text" }.should == "link text" + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/frameset_spec.rb b/spec/rubyspec/library/cgi/htmlextension/frameset_spec.rb new file mode 100644 index 0000000000..81e92089e4 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/frameset_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require 'cgi' + +describe "CGI::HtmlExtension#frameset" do + before :each do + @html = CGISpecs.cgi_new("html4Fr") + end + + it "initializes the HTML Generation methods for the Frameset version of HTML4" do + @html.frameset.should == "" + @html.frameset { "link text" }.should == "link text" + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/hidden_spec.rb b/spec/rubyspec/library/cgi/htmlextension/hidden_spec.rb new file mode 100644 index 0000000000..dd06dfc354 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/hidden_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#hidden" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns an hidden-'input'-element without a name" do + output = @html.hidden + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "hidden"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.hidden { "test" } + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "hidden"}, "", not_closed: true) + end + end + + describe "when passed name" do + it "returns an hidden-'input'-element with the passed name" do + output = @html.hidden("test") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.hidden("test") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden"}, "", not_closed: true) + end + end + + describe "when passed name, value" do + it "returns an hidden-'input'-element with the passed name and value" do + output = @html.hidden("test", "some value") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden", "VALUE" => "some value"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.hidden("test", "some value") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "hidden", "VALUE" => "some value"}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a checkbox-'input'-element using the passed Hash for attributes" do + attributes = { "NAME" => "test", "VALUE" => "some value" } + output = @html.hidden("test", "some value") + output.should equal_element("INPUT", attributes, "", not_closed: true) + end + + it "ignores a passed block" do + attributes = { "NAME" => "test", "VALUE" => "some value" } + output = @html.hidden("test", "some value") { "test" } + output.should equal_element("INPUT", attributes, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/html_spec.rb b/spec/rubyspec/library/cgi/htmlextension/html_spec.rb new file mode 100644 index 0000000000..0dfba297d2 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/html_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#html" do + before :each do + @html = CGISpecs.cgi_new + @html.stub!(:doctype).and_return("") + end + + describe "when passed no arguments" do + it "returns a self's doctype and an 'html'-element" do + expected = '' + @html.html.should == expected + end + + it "includes the passed block when passed a block" do + expected = 'test' + @html.html { "test" }.should == expected + end + end + + describe "when passed 'PRETTY'" do + it "returns pretty output when the passed String is 'PRETTY" do + expected = "\n\n" + @html.html("PRETTY").should == expected + end + + it "includes the passed block when passed a block" do + expected = "\n\n test\n\n" + @html.html("PRETTY") { "test" }.should == expected + end + end + + describe "when passed a Hash" do + it "returns an 'html'-element using the passed Hash for attributes" do + expected = '' + @html.html("DOCTYPE" => '', "BLA" => "TEST").should == expected + end + + it "omits the doctype when the Hash contains a 'DOCTYPE' entry that's false or nil" do + @html.html("DOCTYPE" => false).should == "" + @html.html("DOCTYPE" => nil).should == "" + end + end + + describe "when each HTML generation" do + it "returns the doctype declaration for HTML3" do + expect = '' + CGISpecs.cgi_new("html3").html.should == expect + "" + CGISpecs.cgi_new("html3").html { "html body" }.should == expect + "html body" + end + + it "returns the doctype declaration for HTML4" do + expect = '' + CGISpecs.cgi_new("html4").html.should == expect + "" + CGISpecs.cgi_new("html4").html { "html body" }.should == expect + "html body" + end + + it "returns the doctype declaration for the Transitional version of HTML4" do + expect = '' + CGISpecs.cgi_new("html4Tr").html.should == expect + "" + CGISpecs.cgi_new("html4Tr").html { "html body" }.should == expect + "html body" + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/image_button_spec.rb b/spec/rubyspec/library/cgi/htmlextension/image_button_spec.rb new file mode 100644 index 0000000000..f4e39d8028 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/image_button_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#image_button" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns an image-'input'-element without a source image" do + output = @html.image_button + output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.image_button { "test" } + output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image"}, "", not_closed: true) + end + end + + describe "when passed src" do + it "returns an image-'input'-element with the passed src" do + output = @html.image_button("/path/to/image.png") + output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.image_button("/path/to/image.png") { "test" } + output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image"}, "", not_closed: true) + end + end + + describe "when passed src, name" do + it "returns an image-'input'-element with the passed src and name" do + output = @html.image_button("/path/to/image.png", "test") + output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.image_button("/path/to/image.png", "test") { "test" } + output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test"}, "", not_closed: true) + end + end + + describe "when passed src, name, alt" do + it "returns an image-'input'-element with the passed src, name and alt" do + output = @html.image_button("/path/to/image.png", "test", "alternative") + output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test", "ALT" => "alternative"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.image_button("/path/to/image.png", "test", "alternative") { "test" } + output.should equal_element("INPUT", {"SRC" => "/path/to/image.png", "TYPE" => "image", "NAME" => "test", "ALT" => "alternative"}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a image-'input'-element using the passed Hash for attributes" do + output = @html.image_button("NAME" => "test", "VALUE" => "test-value") + output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image", "NAME" => "test", "VALUE" => "test-value"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.image_button("NAME" => "test", "VALUE" => "test-value") { "test" } + output.should equal_element("INPUT", {"SRC" => "", "TYPE" => "image", "NAME" => "test", "VALUE" => "test-value"}, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/img_spec.rb b/spec/rubyspec/library/cgi/htmlextension/img_spec.rb new file mode 100644 index 0000000000..8109bcd6c6 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/img_spec.rb @@ -0,0 +1,83 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#img" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns an 'img'-element without an src-url or alt-text" do + output = @html.img + output.should equal_element("IMG", { "SRC" => "", "ALT" => "" }, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.img { "test" } + output.should equal_element("IMG", { "SRC" => "", "ALT" => "" }, "", not_closed: true) + end + end + + describe "when passed src" do + it "returns an 'img'-element with the passed src-url" do + output = @html.img("/path/to/some/image.png") + output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "" }, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.img("/path/to/some/image.png") + output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "" }, "", not_closed: true) + end + end + + describe "when passed src, alt" do + it "returns an 'img'-element with the passed src-url and the passed alt-text" do + output = @html.img("/path/to/some/image.png", "Alternative") + output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative" }, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.img("/path/to/some/image.png", "Alternative") { "test" } + output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative" }, "", not_closed: true) + end + end + + describe "when passed src, alt, width" do + it "returns an 'img'-element with the passed src-url, the passed alt-text and the passed width" do + output = @html.img("/path/to/some/image.png", "Alternative", 40) + output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative", "WIDTH" => "40" }, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.img("/path/to/some/image.png", "Alternative", 40) { "test" } + output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative", "WIDTH" => "40" }, "", not_closed: true) + end + end + + describe "when passed src, alt, width, height" do + it "returns an 'img'-element with the passed src-url, the passed alt-text, the passed width and the passed height" do + output = @html.img("/path/to/some/image.png", "Alternative", 40, 60) + output.should equal_element("IMG", { "SRC" => "/path/to/some/image.png", "ALT" => "Alternative", "WIDTH" => "40", "HEIGHT" => "60" }, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.img { "test" } + output.should equal_element("IMG", { "SRC" => "", "ALT" => "" }, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns an 'img'-element with the passed Hash as attributes" do + attributes = { "SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50 } + output = @html.img(attributes) + output.should equal_element("IMG", attributes, "", not_closed: true) + end + + it "ignores a passed block" do + attributes = { "SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50 } + output = @html.img(attributes) { "test" } + output.should equal_element("IMG", attributes, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/multipart_form_spec.rb b/spec/rubyspec/library/cgi/htmlextension/multipart_form_spec.rb new file mode 100644 index 0000000000..75d1b54666 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/multipart_form_spec.rb @@ -0,0 +1,64 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#multipart_form" do + before :each do + @html = CGISpecs.cgi_new + @html.stub!(:script_name).and_return("/path/to/some/script.rb") + end + + describe "when passed no arguments" do + it "returns a 'form'-element with it's enctype set to multipart" do + output = @html.multipart_form + output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post" }, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.multipart_form { "test" } + output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post" }, "test") + end + end + + describe "when passed action" do + it "returns a 'form'-element with the passed action" do + output = @html.multipart_form("/some/other/script.rb") + output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.multipart_form("/some/other/script.rb") { "test" } + output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "test") + end + end + + describe "when passed action, enctype" do + it "returns a 'form'-element with the passed action and enctype" do + output = @html.multipart_form("/some/other/script.rb", "application/x-www-form-urlencoded") + output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.multipart_form("/some/other/script.rb", "application/x-www-form-urlencoded") { "test" } + output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "post", "ACTION" => "/some/other/script.rb" }, "test") + end + end + + describe "when passed Hash" do + it "returns a 'form'-element with the passed Hash as attributes" do + output = @html.multipart_form("ID" => "test") + output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ID" => "test" }, "") + + output = @html.multipart_form("ID" => "test", "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get") + output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ID" => "test" }, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.multipart_form("ID" => "test") { "test" } + output.should equal_element("FORM", { "ENCTYPE" => "multipart/form-data", "METHOD" => "post", "ID" => "test" }, "test") + + output = @html.multipart_form("ID" => "test", "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get") { "test" } + output.should equal_element("FORM", { "ENCTYPE" => "application/x-www-form-urlencoded", "METHOD" => "get", "ID" => "test" }, "test") + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/password_field_spec.rb b/spec/rubyspec/library/cgi/htmlextension/password_field_spec.rb new file mode 100644 index 0000000000..bb1181de75 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/password_field_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#password_field" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns an password-'input'-element without a name" do + output = @html.password_field + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.password_field { "test" } + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true) + end + end + + describe "when passed name" do + it "returns an password-'input'-element with the passed name" do + output = @html.password_field("test") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.password_field("test") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "SIZE" => "40"}, "", not_closed: true) + end + end + + describe "when passed name, value" do + it "returns an password-'input'-element with the passed name and value" do + output = @html.password_field("test", "some value") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.password_field("test", "some value") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true) + end + end + + describe "when passed name, value, size" do + it "returns an password-'input'-element with the passed name, value and size" do + output = @html.password_field("test", "some value", 60) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.password_field("test", "some value", 60) { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true) + end + end + + describe "when passed name, value, size, maxlength" do + it "returns an password-'input'-element with the passed name, value, size and maxlength" do + output = @html.password_field("test", "some value", 60, 12) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.password_field("test", "some value", 60, 12) { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "password", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a checkbox-'input'-element using the passed Hash for attributes" do + output = @html.password_field("NAME" => "test", "VALUE" => "some value") + output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "password" }, "", not_closed: true) + + output = @html.password_field("TYPE" => "hidden") + output.should equal_element("INPUT", {"TYPE" => "password"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.password_field("NAME" => "test", "VALUE" => "some value") { "test" } + output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "password" }, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/popup_menu_spec.rb b/spec/rubyspec/library/cgi/htmlextension/popup_menu_spec.rb new file mode 100644 index 0000000000..5e94ec1a3e --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/popup_menu_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../shared/popup_menu', __FILE__) + +describe "CGI::HtmlExtension#popup_menu" do + it_behaves_like :cgi_htmlextension_popup_menu, :popup_menu +end diff --git a/spec/rubyspec/library/cgi/htmlextension/radio_button_spec.rb b/spec/rubyspec/library/cgi/htmlextension/radio_button_spec.rb new file mode 100644 index 0000000000..0ce88f20d7 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/radio_button_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#radio_button" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns a radio-'input'-element without a name" do + output = @html.radio_button + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "radio"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.radio_button { "test" } + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "radio"}, "", not_closed: true) + end + end + + describe "when passed name" do + it "returns a radio-'input'-element with the passed name" do + output = @html.radio_button("test") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.radio_button("test") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio"}, "", not_closed: true) + end + end + + describe "CGI::HtmlExtension#checkbox when passed name, value" do + it "returns a radio-'input'-element with the passed name and value" do + output = @html.radio_button("test", "test-value") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.radio_button("test", "test-value") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true) + end + end + + describe "when passed name, value, checked" do + it "returns a checked radio-'input'-element with the passed name and value when checked is true" do + output = @html.radio_button("test", "test-value", true) + output.should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true) + + output = @html.radio_button("test", "test-value", false) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true) + + output = @html.radio_button("test", "test-value", nil) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.radio_button("test", "test-value", nil) { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "test-value"}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a radio-'input'-element using the passed Hash for attributes" do + attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true} + output = @html.radio_button(attributes) + output.should equal_element("INPUT", attributes, "", not_closed: true) + end + + it "ignores a passed block" do + attributes = {"NAME" => "test", "VALUE" => "test-value", "CHECKED" => true} + output = @html.radio_button(attributes) { "test" } + output.should equal_element("INPUT", attributes, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/radio_group_spec.rb b/spec/rubyspec/library/cgi/htmlextension/radio_group_spec.rb new file mode 100644 index 0000000000..69d3444072 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/radio_group_spec.rb @@ -0,0 +1,77 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#radio_group" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed name, values ..." do + it "returns a sequence of 'radio'-elements with the passed name and the passed values" do + output = CGISpecs.split(@html.radio_group("test", "foo", "bar", "baz")) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true) + end + + it "allows passing a value inside an Array" do + output = CGISpecs.split(@html.radio_group("test", ["foo"], "bar", ["baz"])) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true) + end + + it "allows passing a value as an Array containing the value and the checked state or a label" do + output = CGISpecs.split(@html.radio_group("test", ["foo"], ["bar", true], ["baz", "label for baz"])) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "label for baz", not_closed: true) + end + + # TODO: CGI does not like passing false instead of true. + it "allows passing a value as an Array containing the value, a label and the checked state" do + output = CGISpecs.split(@html.radio_group("test", ["foo", "label for foo", true], ["bar", "label for bar", false], ["baz", "label for baz", true])) + output[0].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "label for foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "label for bar", not_closed: true) + output[2].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "label for baz", not_closed: true) + end + + it "returns an empty String when passed no values" do + @html.radio_group("test").should == "" + end + + it "ignores a passed block" do + output = CGISpecs.split(@html.radio_group("test", "foo", "bar", "baz") { "test" }) + output[0].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "test", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true) + end + end + + describe "when passed Hash" do + it "uses the passed Hash to generate the radio sequence" do + output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"])) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true) + + output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => [["foo"], ["bar", true], "baz"])) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true) + + output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "1"}, "Foo", not_closed: true) + output[1].should equal_element("INPUT", {"CHECKED" => true, "NAME" => "name", "TYPE" => "radio", "VALUE" => "2"}, "Bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "Baz"}, "Baz", not_closed: true) + end + + it "ignores a passed block" do + output = CGISpecs.split(@html.radio_group("NAME" => "name", "VALUES" => ["foo", "bar", "baz"]) { "test" }) + output[0].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "foo"}, "foo", not_closed: true) + output[1].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "bar"}, "bar", not_closed: true) + output[2].should equal_element("INPUT", {"NAME" => "name", "TYPE" => "radio", "VALUE" => "baz"}, "baz", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/reset_spec.rb b/spec/rubyspec/library/cgi/htmlextension/reset_spec.rb new file mode 100644 index 0000000000..09184347d0 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/reset_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#reset" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns a reset-'input'-element" do + output = @html.reset + output.should equal_element("INPUT", {"TYPE" => "reset"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.reset { "test" } + output.should equal_element("INPUT", {"TYPE" => "reset"}, "", not_closed: true) + end + end + + describe "when passed value" do + it "returns a reset-'input'-element with the passed value" do + output = @html.reset("Example") + output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.reset("Example") { "test" } + output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true) + end + end + + describe "when passed value, name" do + it "returns a reset-'input'-element with the passed value and the passed name" do + output = @html.reset("Example", "test-name") + output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.reset("Example", "test-name") { "test" } + output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a reset-'input'-element with the passed value" do + output = @html.reset("Example") + output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.reset("Example") { "test" } + output.should equal_element("INPUT", {"TYPE" => "reset", "VALUE" => "Example"}, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/scrolling_list_spec.rb b/spec/rubyspec/library/cgi/htmlextension/scrolling_list_spec.rb new file mode 100644 index 0000000000..da295278b1 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/scrolling_list_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require 'cgi' +require File.expand_path('../shared/popup_menu', __FILE__) + +describe "CGI::HtmlExtension#scrolling_list" do + it_behaves_like :cgi_htmlextension_popup_menu, :scrolling_list +end diff --git a/spec/rubyspec/library/cgi/htmlextension/shared/popup_menu.rb b/spec/rubyspec/library/cgi/htmlextension/shared/popup_menu.rb new file mode 100644 index 0000000000..4787fd3f8e --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/shared/popup_menu.rb @@ -0,0 +1,94 @@ +describe :cgi_htmlextension_popup_menu, shared: true do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns an empty 'select'-element without a name" do + output = @html.send(@method) + output.should equal_element("SELECT", {"NAME" => ""}, "") + end + + it "ignores a passed block" do + output = @html.send(@method) { "test" } + output.should equal_element("SELECT", {"NAME" => ""}, "") + end + end + + describe "when passed name, values ..." do + it "returns a 'select'-element with the passed name containing 'option'-elements based on the passed values" do + content = @html.option("VALUE" => "foo") { "foo" } + content << @html.option("VALUE" => "bar") { "bar" } + content << @html.option("VALUE" => "baz") { "baz" } + + output = @html.send(@method, "test", "foo", "bar", "baz") + output.should equal_element("SELECT", {"NAME" => "test"}, content) + end + + it "allows passing values inside of arrays" do + content = @html.option("VALUE" => "foo") { "foo" } + content << @html.option("VALUE" => "bar") { "bar" } + content << @html.option("VALUE" => "baz") { "baz" } + + output = @html.send(@method, "test", ["foo"], ["bar"], ["baz"]) + output.should equal_element("SELECT", {"NAME" => "test"}, content) + end + + it "allows passing a value as an Array containing the value and the select state or a label" do + content = @html.option("VALUE" => "foo") { "foo" } + content << @html.option("VALUE" => "bar", "SELECTED" => true) { "bar" } + content << @html.option("VALUE" => "baz") { "baz" } + + output = @html.send(@method, "test", ["foo"], ["bar", true], "baz") + output.should equal_element("SELECT", {"NAME" => "test"}, content) + end + + it "allows passing a value as an Array containing the value, a label and the select state" do + content = @html.option("VALUE" => "1") { "Foo" } + content << @html.option("VALUE" => "2", "SELECTED" => true) { "Bar" } + content << @html.option("VALUE" => "Baz") { "Baz" } + + output = @html.send(@method, "test", ["1", "Foo"], ["2", "Bar", true], "Baz") + output.should equal_element("SELECT", {"NAME" => "test"}, content) + end + + it "ignores a passed block" do + content = @html.option("VALUE" => "foo") { "foo" } + content << @html.option("VALUE" => "bar") { "bar" } + content << @html.option("VALUE" => "baz") { "baz" } + + output = @html.send(@method, "test", "foo", "bar", "baz") { "woot" } + output.should equal_element("SELECT", {"NAME" => "test"}, content) + end + end + + describe "when passed a Hash" do + it "uses the passed Hash to generate the 'select'-element and the 'option'-elements" do + attributes = { + "NAME" => "test", "SIZE" => 2, "MULTIPLE" => true, + "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] + } + + content = @html.option("VALUE" => "1") { "Foo" } + content << @html.option("VALUE" => "2", "SELECTED" => true) { "Bar" } + content << @html.option("VALUE" => "Baz") { "Baz" } + + output = @html.send(@method, attributes) + output.should equal_element("SELECT", {"NAME" => "test", "SIZE" => 2, "MULTIPLE" => true}, content) + end + + it "ignores a passed block" do + attributes = { + "NAME" => "test", "SIZE" => 2, "MULTIPLE" => true, + "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] + } + + content = @html.option("VALUE" => "1") { "Foo" } + content << @html.option("VALUE" => "2", "SELECTED" => true) { "Bar" } + content << @html.option("VALUE" => "Baz") { "Baz" } + + output = @html.send(@method, attributes) { "testing" } + output.should equal_element("SELECT", {"NAME" => "test", "SIZE" => 2, "MULTIPLE" => true}, content) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/submit_spec.rb b/spec/rubyspec/library/cgi/htmlextension/submit_spec.rb new file mode 100644 index 0000000000..8b9d9b02d8 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/submit_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#submit" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns a submit-'input'-element" do + output = @html.submit + output.should equal_element("INPUT", {"TYPE" => "submit"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.submit { "test" } + output.should equal_element("INPUT", {"TYPE" => "submit"}, "", not_closed: true) + end + end + + describe "when passed value" do + it "returns a submit-'input'-element with the passed value" do + output = @html.submit("Example") + output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.submit("Example") { "test" } + output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true) + end + end + + describe "when passed value, name" do + it "returns a submit-'input'-element with the passed value and the passed name" do + output = @html.submit("Example", "test-name") + output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.submit("Example", "test-name") { "test" } + output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example", "NAME" => "test-name"}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a submit-'input'-element with the passed value" do + output = @html.submit("Example") + output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.submit("Example") { "test" } + output.should equal_element("INPUT", {"TYPE" => "submit", "VALUE" => "Example"}, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/text_field_spec.rb b/spec/rubyspec/library/cgi/htmlextension/text_field_spec.rb new file mode 100644 index 0000000000..b8031d6ff5 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/text_field_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#text_field" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns an text-'input'-element without a name" do + output = @html.text_field + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.text_field { "test" } + output.should equal_element("INPUT", {"NAME" => "", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true) + end + end + + describe "when passed name" do + it "returns an text-'input'-element with the passed name" do + output = @html.text_field("test") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.text_field("test") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "SIZE" => "40"}, "", not_closed: true) + end + end + + describe "when passed name, value" do + it "returns an text-'input'-element with the passed name and value" do + output = @html.text_field("test", "some value") + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.text_field("test", "some value") { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "40"}, "", not_closed: true) + end + end + + describe "when passed name, value, size" do + it "returns an text-'input'-element with the passed name, value and size" do + output = @html.text_field("test", "some value", 60) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.text_field("test", "some value", 60) { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60"}, "", not_closed: true) + end + end + + describe "when passed name, value, size, maxlength" do + it "returns an text-'input'-element with the passed name, value, size and maxlength" do + output = @html.text_field("test", "some value", 60, 12) + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.text_field("test", "some value", 60, 12) { "test" } + output.should equal_element("INPUT", {"NAME" => "test", "TYPE" => "text", "VALUE" => "some value", "SIZE" => "60", "MAXLENGTH" => 12}, "", not_closed: true) + end + end + + describe "when passed Hash" do + it "returns a checkbox-'input'-element using the passed Hash for attributes" do + output = @html.text_field("NAME" => "test", "VALUE" => "some value") + output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "text" }, "", not_closed: true) + + output = @html.text_field("TYPE" => "hidden") + output.should equal_element("INPUT", {"TYPE" => "text"}, "", not_closed: true) + end + + it "ignores a passed block" do + output = @html.text_field("NAME" => "test", "VALUE" => "some value") { "test" } + output.should equal_element("INPUT", { "NAME" => "test", "VALUE" => "some value", "TYPE" => "text" }, "", not_closed: true) + end + end +end diff --git a/spec/rubyspec/library/cgi/htmlextension/textarea_spec.rb b/spec/rubyspec/library/cgi/htmlextension/textarea_spec.rb new file mode 100644 index 0000000000..e47e6ed417 --- /dev/null +++ b/spec/rubyspec/library/cgi/htmlextension/textarea_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../fixtures/common', __FILE__) + +describe "CGI::HtmlExtension#textarea" do + before :each do + @html = CGISpecs.cgi_new + end + + describe "when passed no arguments" do + it "returns an 'textarea'-element without a name" do + output = @html.textarea + output.should equal_element("TEXTAREA", {"NAME" => "", "COLS" => "70", "ROWS" => "10"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.textarea { "Example" } + output.should equal_element("TEXTAREA", {"NAME" => "", "COLS" => "70", "ROWS" => "10"}, "Example") + end + end + + describe "when passed name" do + it "returns an 'textarea'-element with the passed name" do + output = @html.textarea("test") + output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "70", "ROWS" => "10"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.textarea("test") { "Example" } + output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "70", "ROWS" => "10"}, "Example") + end + end + + describe "when passed name, cols" do + it "returns an 'textarea'-element with the passed name and the passed amount of columns" do + output = @html.textarea("test", 40) + output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "10"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.textarea("test", 40) { "Example" } + output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "10"}, "Example") + end + end + + describe "when passed name, cols, rows" do + it "returns an 'textarea'-element with the passed name, the passed amount of columns and the passed number of rows" do + output = @html.textarea("test", 40, 5) + output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "5"}, "") + end + + it "includes the return value of the passed block when passed a block" do + output = @html.textarea("test", 40, 5) { "Example" } + output.should equal_element("TEXTAREA", {"NAME" => "test", "COLS" => "40", "ROWS" => "5"}, "Example") + end + end + + describe "when passed Hash" do + it "uses the passed Hash as attributes" do + @html.textarea("ID" => "test").should == '' + + attributes = {"ID" => "test-id", "NAME" => "test-name"} + output = @html.textarea(attributes) + output.should equal_element("TEXTAREA", attributes, "") + end + + it "includes the return value of the passed block when passed a block" do + attributes = {"ID" => "test-id", "NAME" => "test-name"} + output = @html.textarea(attributes) { "test" } + output.should equal_element("TEXTAREA", attributes, "test") + end + end +end diff --git a/spec/rubyspec/library/cgi/http_header_spec.rb b/spec/rubyspec/library/cgi/http_header_spec.rb new file mode 100644 index 0000000000..1960d009e4 --- /dev/null +++ b/spec/rubyspec/library/cgi/http_header_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +require File.expand_path('../shared/http_header', __FILE__) + +describe "CGI#http_header" do + it_behaves_like(:cgi_http_header, :http_header) +end diff --git a/spec/rubyspec/library/cgi/initialize_spec.rb b/spec/rubyspec/library/cgi/initialize_spec.rb new file mode 100644 index 0000000000..6526aab5e8 --- /dev/null +++ b/spec/rubyspec/library/cgi/initialize_spec.rb @@ -0,0 +1,133 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI#initialize" do + it "is private" do + CGI.should have_private_instance_method(:initialize) + end +end + +describe "CGI#initialize when passed no arguments" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.allocate + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "extends self with CGI::QueryExtension" do + @cgi.send(:initialize) + @cgi.should be_kind_of(CGI::QueryExtension) + end + + it "does not extend self with CGI::HtmlExtension" do + @cgi.send(:initialize) + @cgi.should_not be_kind_of(CGI::HtmlExtension) + end + + it "does not extend self with any of the other HTML modules" do + @cgi.send(:initialize) + @cgi.should_not be_kind_of(CGI::Html3) + @cgi.should_not be_kind_of(CGI::HtmlExtension) + @cgi.should_not be_kind_of(CGI::Html4) + @cgi.should_not be_kind_of(CGI::Html4Tr) + @cgi.should_not be_kind_of(CGI::Html4Fr) + end + + it "sets #cookies based on ENV['HTTP_COOKIE']" do + begin + old_env, ENV["HTTP_COOKIE"] = ENV["HTTP_COOKIE"], "test=test yay" + @cgi.send(:initialize) + @cgi.cookies.should == { "test"=>[ "test yay" ] } + ensure + ENV["HTTP_COOKIE"] = old_env + end + end + + it "sets #params based on ENV['QUERY_STRING'] when ENV['REQUEST_METHOD'] is GET" do + begin + old_env_query, ENV["QUERY_STRING"] = ENV["QUERY_STRING"], "?test=a&test2=b" + old_env_method, ENV["REQUEST_METHOD"] = ENV["REQUEST_METHOD"], "GET" + @cgi.send(:initialize) + @cgi.params.should == { "test2" => ["b"], "?test" => ["a"] } + ensure + ENV["QUERY_STRING"] = old_env_query + ENV["REQUEST_METHOD"] = old_env_method + end + end + + it "sets #params based on ENV['QUERY_STRING'] when ENV['REQUEST_METHOD'] is HEAD" do + begin + old_env_query, ENV["QUERY_STRING"] = ENV["QUERY_STRING"], "?test=a&test2=b" + old_env_method, ENV["REQUEST_METHOD"] = ENV["REQUEST_METHOD"], "HEAD" + @cgi.send(:initialize) + @cgi.params.should == { "test2" => ["b"], "?test" => ["a"] } + ensure + ENV["QUERY_STRING"] = old_env_query + ENV["REQUEST_METHOD"] = old_env_method + end + end +end + +describe "CGI#initialize when passed type" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.allocate + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + + it "extends self with CGI::QueryExtension" do + @cgi.send(:initialize, "test") + @cgi.should be_kind_of(CGI::QueryExtension) + end + + it "extends self with CGI::QueryExtension, CGI::Html3 and CGI::HtmlExtension when the passed type is 'html3'" do + @cgi.send(:initialize, "html3") + @cgi.should be_kind_of(CGI::Html3) + @cgi.should be_kind_of(CGI::HtmlExtension) + @cgi.should be_kind_of(CGI::QueryExtension) + + @cgi.should_not be_kind_of(CGI::Html4) + @cgi.should_not be_kind_of(CGI::Html4Tr) + @cgi.should_not be_kind_of(CGI::Html4Fr) + end + + it "extends self with CGI::QueryExtension, CGI::Html4 and CGI::HtmlExtension when the passed type is 'html4'" do + @cgi.send(:initialize, "html4") + @cgi.should be_kind_of(CGI::Html4) + @cgi.should be_kind_of(CGI::HtmlExtension) + @cgi.should be_kind_of(CGI::QueryExtension) + + @cgi.should_not be_kind_of(CGI::Html3) + @cgi.should_not be_kind_of(CGI::Html4Tr) + @cgi.should_not be_kind_of(CGI::Html4Fr) + end + + it "extends self with CGI::QueryExtension, CGI::Html4Tr and CGI::HtmlExtension when the passed type is 'html4Tr'" do + @cgi.send(:initialize, "html4Tr") + @cgi.should be_kind_of(CGI::Html4Tr) + @cgi.should be_kind_of(CGI::HtmlExtension) + @cgi.should be_kind_of(CGI::QueryExtension) + + @cgi.should_not be_kind_of(CGI::Html3) + @cgi.should_not be_kind_of(CGI::Html4) + @cgi.should_not be_kind_of(CGI::Html4Fr) + end + + it "extends self with CGI::QueryExtension, CGI::Html4Tr, CGI::Html4Fr and CGI::HtmlExtension when the passed type is 'html4Fr'" do + @cgi.send(:initialize, "html4Fr") + @cgi.should be_kind_of(CGI::Html4Tr) + @cgi.should be_kind_of(CGI::Html4Fr) + @cgi.should be_kind_of(CGI::HtmlExtension) + @cgi.should be_kind_of(CGI::QueryExtension) + + @cgi.should_not be_kind_of(CGI::Html3) + @cgi.should_not be_kind_of(CGI::Html4) + end +end diff --git a/spec/rubyspec/library/cgi/out_spec.rb b/spec/rubyspec/library/cgi/out_spec.rb new file mode 100644 index 0000000000..05fe2662dc --- /dev/null +++ b/spec/rubyspec/library/cgi/out_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI#out" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + $stdout, @old_stdout = IOStub.new, $stdout + end + + after :each do + $stdout = @old_stdout + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "it writes a HTMl header based on the passed argument to $stdout" do + @cgi.out { "" } + $stdout.should == "Content-Type: text/html\r\nContent-Length: 0\r\n\r\n" + end + + it "appends the block's return value to the HTML header" do + @cgi.out { "test!" } + $stdout.should == "Content-Type: text/html\r\nContent-Length: 5\r\n\r\ntest!" + end + + it "automatically sets the Content-Length Header based on the block's return value" do + @cgi.out { "0123456789" } + $stdout.should == "Content-Type: text/html\r\nContent-Length: 10\r\n\r\n0123456789" + end + + it "includes Cookies in the @output_cookies field" do + @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"]) + @cgi.out { "" } + $stdout.should == "Content-Type: text/html\r\nContent-Length: 0\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n" + end +end + +describe "CGI#out when passed no block" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "raises a LocalJumpError" do + lambda { @cgi.out }.should raise_error(LocalJumpError) + end +end diff --git a/spec/rubyspec/library/cgi/parse_spec.rb b/spec/rubyspec/library/cgi/parse_spec.rb new file mode 100644 index 0000000000..8f05c91c7b --- /dev/null +++ b/spec/rubyspec/library/cgi/parse_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.parse when passed String" do + it "parses a HTTP Query String into a Hash" do + CGI.parse("test=test&a=b").should == { "a" => ["b"], "test" => ["test"] } + CGI.parse("test=1,2,3").should == { "test" => ["1,2,3"] } + CGI.parse("test=a&a=&b=").should == { "test" => ["a"], "a" => [""], "b" => [""] } + end + + it "parses query strings with semicolons in place of ampersands" do + CGI.parse("test=test;a=b").should == { "a" => ["b"], "test" => ["test"] } + CGI.parse("test=a;a=;b=").should == { "test" => ["a"], "a" => [""], "b" => [""] } + end + + it "allows passing multiple values for one key" do + CGI.parse("test=1&test=2&test=3").should == { "test" => ["1", "2", "3"] } + CGI.parse("test[]=1&test[]=2&test[]=3").should == { "test[]" => [ "1", "2", "3" ] } + end + + it "unescapes keys and values" do + CGI.parse("hello%3F=hello%21").should == { "hello?" => ["hello!"] } + end +end diff --git a/spec/rubyspec/library/cgi/pretty_spec.rb b/spec/rubyspec/library/cgi/pretty_spec.rb new file mode 100644 index 0000000000..e09c327ef6 --- /dev/null +++ b/spec/rubyspec/library/cgi/pretty_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.pretty when passed html" do + it "indents the passed html String with two spaces" do + CGI.pretty("").should == <<-EOS + + + + +EOS + end +end + +describe "CGI.pretty when passed html, indentation_unit" do + it "indents the passed html String with the passed indentation_unit" do + CGI.pretty("", "\t").should == <<-EOS + +\t +\t + +EOS + end +end diff --git a/spec/rubyspec/library/cgi/print_spec.rb b/spec/rubyspec/library/cgi/print_spec.rb new file mode 100644 index 0000000000..0db5efa0dd --- /dev/null +++ b/spec/rubyspec/library/cgi/print_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI#print" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "passes all arguments to $stdout.print" do + $stdout.should_receive(:print).with("test") + @cgi.print("test") + + $stdout.should_receive(:print).with("one", "two", "three", ["four", "five"]) + @cgi.print("one", "two", "three", ["four", "five"]) + end + + it "returns the result of calling $stdout.print" do + $stdout.should_receive(:print).with("test").and_return(:expected) + @cgi.print("test").should == :expected + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/accept_charset_spec.rb b/spec/rubyspec/library/cgi/queryextension/accept_charset_spec.rb new file mode 100644 index 0000000000..be340b26f0 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/accept_charset_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#accept_charset" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_ACCEPT_CHARSET']" do + old_value, ENV['HTTP_ACCEPT_CHARSET'] = ENV['HTTP_ACCEPT_CHARSET'], "ISO-8859-1,utf-8;q=0.7,*;q=0.7" + begin + @cgi.accept_charset.should == "ISO-8859-1,utf-8;q=0.7,*;q=0.7" + ensure + ENV['HTTP_ACCEPT_CHARSET'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/accept_encoding_spec.rb b/spec/rubyspec/library/cgi/queryextension/accept_encoding_spec.rb new file mode 100644 index 0000000000..a828ae7a42 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/accept_encoding_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#accept_encoding" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_ACCEPT_ENCODING']" do + old_value, ENV['HTTP_ACCEPT_ENCODING'] = ENV['HTTP_ACCEPT_ENCODING'], "gzip,deflate" + begin + @cgi.accept_encoding.should == "gzip,deflate" + ensure + ENV['HTTP_ACCEPT_ENCODING'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/accept_language_spec.rb b/spec/rubyspec/library/cgi/queryextension/accept_language_spec.rb new file mode 100644 index 0000000000..77b4740251 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/accept_language_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#accept_language" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_ACCEPT_LANGUAGE']" do + old_value, ENV['HTTP_ACCEPT_LANGUAGE'] = ENV['HTTP_ACCEPT_LANGUAGE'], "en-us,en;q=0.5" + begin + @cgi.accept_language.should == "en-us,en;q=0.5" + ensure + ENV['HTTP_ACCEPT_LANGUAGE'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/accept_spec.rb b/spec/rubyspec/library/cgi/queryextension/accept_spec.rb new file mode 100644 index 0000000000..0c7aa2ab1f --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/accept_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#accept" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_ACCEPT']" do + old_value, ENV['HTTP_ACCEPT'] = ENV['HTTP_ACCEPT'], "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + begin + @cgi.accept.should == "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + ensure + ENV['HTTP_ACCEPT'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/auth_type_spec.rb b/spec/rubyspec/library/cgi/queryextension/auth_type_spec.rb new file mode 100644 index 0000000000..0ec2835053 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/auth_type_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#auth_type" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['AUTH_TYPE']" do + old_value, ENV['AUTH_TYPE'] = ENV['AUTH_TYPE'], "Basic" + begin + @cgi.auth_type.should == "Basic" + ensure + ENV['AUTH_TYPE'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/cache_control_spec.rb b/spec/rubyspec/library/cgi/queryextension/cache_control_spec.rb new file mode 100644 index 0000000000..f1718b0871 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/cache_control_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#cache_control" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_CACHE_CONTROL']" do + old_value, ENV['HTTP_CACHE_CONTROL'] = ENV['HTTP_CACHE_CONTROL'], "no-cache" + begin + @cgi.cache_control.should == "no-cache" + ensure + ENV['HTTP_CACHE_CONTROL'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/content_length_spec.rb b/spec/rubyspec/library/cgi/queryextension/content_length_spec.rb new file mode 100644 index 0000000000..c9f0708f69 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/content_length_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#content_length" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['CONTENT_LENGTH'] as Integer" do + old_value = ENV['CONTENT_LENGTH'] + begin + ENV['CONTENT_LENGTH'] = nil + @cgi.content_length.should be_nil + + ENV['CONTENT_LENGTH'] = "100" + @cgi.content_length.should eql(100) + ensure + ENV['CONTENT_LENGTH'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/content_type_spec.rb b/spec/rubyspec/library/cgi/queryextension/content_type_spec.rb new file mode 100644 index 0000000000..a65b0a6103 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/content_type_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#content_type" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['CONTENT_TYPE']" do + old_value, ENV['CONTENT_TYPE'] = ENV['CONTENT_TYPE'], "text/html" + begin + @cgi.content_type.should == "text/html" + ensure + ENV['CONTENT_TYPE'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/cookies_spec.rb b/spec/rubyspec/library/cgi/queryextension/cookies_spec.rb new file mode 100644 index 0000000000..5df457f11c --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/cookies_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#cookies" do + it "needs to be reviewed for spec completeness" +end + +describe "CGI::QueryExtension#cookies=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/cgi/queryextension/element_reference_spec.rb b/spec/rubyspec/library/cgi/queryextension/element_reference_spec.rb new file mode 100644 index 0000000000..4aabfaa277 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/element_reference_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#[]" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + ENV['QUERY_STRING'], @old_query_string = "one=a&two=b&two=c", ENV['QUERY_STRING'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + ENV['QUERY_STRING'] = @old_query_string + end + + it "it returns the value for the parameter with the given key" do + @cgi["one"].should == "a" + end + + it "only returns the first value for parameters with multiple values" do + @cgi["two"].should == "b" + end + + it "returns a String" do + @cgi["one"].should be_kind_of(String) + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/from_spec.rb b/spec/rubyspec/library/cgi/queryextension/from_spec.rb new file mode 100644 index 0000000000..aabd9b9bb3 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/from_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#from" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_FROM']" do + old_value, ENV['HTTP_FROM'] = ENV['HTTP_FROM'], "googlebot(at)googlebot.com" + begin + @cgi.from.should == "googlebot(at)googlebot.com" + ensure + ENV['HTTP_FROM'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/gateway_interface_spec.rb b/spec/rubyspec/library/cgi/queryextension/gateway_interface_spec.rb new file mode 100644 index 0000000000..8b006063d8 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/gateway_interface_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#gateway_interface" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['GATEWAY_INTERFACE']" do + old_value, ENV['GATEWAY_INTERFACE'] = ENV['GATEWAY_INTERFACE'], "CGI/1.1" + begin + @cgi.gateway_interface.should == "CGI/1.1" + ensure + ENV['GATEWAY_INTERFACE'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/has_key_spec.rb b/spec/rubyspec/library/cgi/queryextension/has_key_spec.rb new file mode 100644 index 0000000000..86ec3d0a29 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/has_key_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../shared/has_key', __FILE__) + +describe "CGI::QueryExtension#has_key?" do + it_behaves_like :cgi_query_extension_has_key_p, :has_key? +end diff --git a/spec/rubyspec/library/cgi/queryextension/host_spec.rb b/spec/rubyspec/library/cgi/queryextension/host_spec.rb new file mode 100644 index 0000000000..89e2610ba7 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/host_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#host" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_HOST']" do + old_value, ENV['HTTP_HOST'] = ENV['HTTP_HOST'], "localhost" + begin + @cgi.host.should == "localhost" + ensure + ENV['HTTP_HOST'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/include_spec.rb b/spec/rubyspec/library/cgi/queryextension/include_spec.rb new file mode 100644 index 0000000000..e8f1bf14ec --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/include_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../shared/has_key', __FILE__) + +describe "CGI::QueryExtension#include?" do + it_behaves_like :cgi_query_extension_has_key_p, :include? +end diff --git a/spec/rubyspec/library/cgi/queryextension/key_spec.rb b/spec/rubyspec/library/cgi/queryextension/key_spec.rb new file mode 100644 index 0000000000..525a0210b2 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/key_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require File.expand_path('../shared/has_key', __FILE__) + +describe "CGI::QueryExtension#key?" do + it_behaves_like :cgi_query_extension_has_key_p, :key? +end diff --git a/spec/rubyspec/library/cgi/queryextension/keys_spec.rb b/spec/rubyspec/library/cgi/queryextension/keys_spec.rb new file mode 100644 index 0000000000..f60b1fb369 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/keys_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#keys" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + ENV['QUERY_STRING'], @old_query_string = "one=a&two=b", ENV['QUERY_STRING'] + + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + ENV['QUERY_STRING'] = @old_query_string + end + + it "returns all parameter keys as an Array" do + @cgi.keys.sort.should == ["one", "two"] + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/multipart_spec.rb b/spec/rubyspec/library/cgi/queryextension/multipart_spec.rb new file mode 100644 index 0000000000..021c847fa3 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/multipart_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' +require "stringio" + +describe "CGI::QueryExtension#multipart?" do + before :each do + @old_stdin = $stdin + + @old_request_method = ENV['REQUEST_METHOD'] + @old_content_type = ENV['CONTENT_TYPE'] + @old_content_length = ENV['CONTENT_LENGTH'] + + ENV['REQUEST_METHOD'] = "POST" + ENV["CONTENT_TYPE"] = "multipart/form-data; boundary=---------------------------1137522503144128232716531729" + ENV["CONTENT_LENGTH"] = "222" + + $stdin = StringIO.new <<-EOS +-----------------------------1137522503144128232716531729\r +Content-Disposition: form-data; name="file"; filename=""\r +Content-Type: application/octet-stream\r +\r +\r +-----------------------------1137522503144128232716531729--\r +EOS + + @cgi = CGI.new + end + + after :each do + $stdin = @old_stdin + + ENV['REQUEST_METHOD'] = @old_request_method + ENV['CONTENT_TYPE'] = @old_content_type + ENV['CONTENT_LENGTH'] = @old_content_length + end + + it "returns true if the current Request is a multipart request" do + @cgi.multipart?.should be_true + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/negotiate_spec.rb b/spec/rubyspec/library/cgi/queryextension/negotiate_spec.rb new file mode 100644 index 0000000000..503ae583bf --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/negotiate_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#negotiate" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_NEGOTIATE']" do + old_value, ENV['HTTP_NEGOTIATE'] = ENV['HTTP_NEGOTIATE'], "trans" + begin + @cgi.negotiate.should == "trans" + ensure + ENV['HTTP_NEGOTIATE'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/params_spec.rb b/spec/rubyspec/library/cgi/queryextension/params_spec.rb new file mode 100644 index 0000000000..6d47b3eeee --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/params_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#params" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + ENV['QUERY_STRING'], @old_query_string = "one=a&two=b&two=c&three", ENV['QUERY_STRING'] + @cgi = CGI.new + end + + after :each do + ENV['QUERY_STRING'] = @old_query_string + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns the parsed HTTP Query Params" do + @cgi.params.should == {"three"=>[], "two"=>["b", "c"], "one"=>["a"]} + end +end + +describe "CGI::QueryExtension#params=" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "sets the HTTP Query Params to the passed argument" do + @cgi.params.should == {} + + @cgi.params = {"one"=>["a"], "two"=>["b", "c"]} + @cgi.params.should == {"one"=>["a"], "two"=>["b", "c"]} + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/path_info_spec.rb b/spec/rubyspec/library/cgi/queryextension/path_info_spec.rb new file mode 100644 index 0000000000..8c8af27fc9 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/path_info_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#path_info" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['PATH_INFO']" do + old_value, ENV['PATH_INFO'] = ENV['PATH_INFO'], "/test/path" + begin + @cgi.path_info.should == "/test/path" + ensure + ENV['PATH_INFO'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/path_translated_spec.rb b/spec/rubyspec/library/cgi/queryextension/path_translated_spec.rb new file mode 100644 index 0000000000..6e9db707b3 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/path_translated_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#path_translated" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['PATH_TRANSLATED']" do + old_value, ENV['PATH_TRANSLATED'] = ENV['PATH_TRANSLATED'], "/full/path/to/dir" + begin + @cgi.path_translated.should == "/full/path/to/dir" + ensure + ENV['PATH_TRANSLATED'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/pragma_spec.rb b/spec/rubyspec/library/cgi/queryextension/pragma_spec.rb new file mode 100644 index 0000000000..c0c7b20514 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/pragma_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#pragma" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_PRAGMA']" do + old_value, ENV['HTTP_PRAGMA'] = ENV['HTTP_PRAGMA'], "no-cache" + begin + @cgi.pragma.should == "no-cache" + ensure + ENV['HTTP_PRAGMA'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/query_string_spec.rb b/spec/rubyspec/library/cgi/queryextension/query_string_spec.rb new file mode 100644 index 0000000000..1065bac7ef --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/query_string_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#query_string" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['QUERY_STRING']" do + old_value, ENV['QUERY_STRING'] = ENV['QUERY_STRING'], "one=a&two=b" + begin + @cgi.query_string.should == "one=a&two=b" + ensure + ENV['QUERY_STRING'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/raw_cookie2_spec.rb b/spec/rubyspec/library/cgi/queryextension/raw_cookie2_spec.rb new file mode 100644 index 0000000000..84b0e0c0dc --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/raw_cookie2_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#raw_cookie2" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_COOKIE2']" do + old_value, ENV['HTTP_COOKIE2'] = ENV['HTTP_COOKIE2'], "some_cookie=data" + begin + @cgi.raw_cookie2.should == "some_cookie=data" + ensure + ENV['HTTP_COOKIE2'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/raw_cookie_spec.rb b/spec/rubyspec/library/cgi/queryextension/raw_cookie_spec.rb new file mode 100644 index 0000000000..096bcf9fab --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/raw_cookie_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#raw_cookie" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_COOKIE']" do + old_value, ENV['HTTP_COOKIE'] = ENV['HTTP_COOKIE'], "some_cookie=data" + begin + @cgi.raw_cookie.should == "some_cookie=data" + ensure + ENV['HTTP_COOKIE'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/referer_spec.rb b/spec/rubyspec/library/cgi/queryextension/referer_spec.rb new file mode 100644 index 0000000000..d52b3a501a --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/referer_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#referer" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_REFERER']" do + old_value, ENV['HTTP_REFERER'] = ENV['HTTP_REFERER'], "example.com" + begin + @cgi.referer.should == "example.com" + ensure + ENV['HTTP_REFERER'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/remote_addr_spec.rb b/spec/rubyspec/library/cgi/queryextension/remote_addr_spec.rb new file mode 100644 index 0000000000..dc94e2c953 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/remote_addr_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#remote_addr" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['REMOTE_ADDR']" do + old_value, ENV['REMOTE_ADDR'] = ENV['REMOTE_ADDR'], "127.0.0.1" + begin + @cgi.remote_addr.should == "127.0.0.1" + ensure + ENV['REMOTE_ADDR'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/remote_host_spec.rb b/spec/rubyspec/library/cgi/queryextension/remote_host_spec.rb new file mode 100644 index 0000000000..f62664b9a7 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/remote_host_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#remote_host" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['REMOTE_HOST']" do + old_value, ENV['REMOTE_HOST'] = ENV['REMOTE_HOST'], "test.host" + begin + @cgi.remote_host.should == "test.host" + ensure + ENV['REMOTE_HOST'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/remote_ident_spec.rb b/spec/rubyspec/library/cgi/queryextension/remote_ident_spec.rb new file mode 100644 index 0000000000..3aab059a7e --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/remote_ident_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#remote_ident" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['REMOTE_IDENT']" do + old_value, ENV['REMOTE_IDENT'] = ENV['REMOTE_IDENT'], "no-idea-what-this-is-for" + begin + @cgi.remote_ident.should == "no-idea-what-this-is-for" + ensure + ENV['REMOTE_IDENT'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/remote_user_spec.rb b/spec/rubyspec/library/cgi/queryextension/remote_user_spec.rb new file mode 100644 index 0000000000..5aae6bc755 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/remote_user_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#remote_user" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['REMOTE_USER']" do + old_value, ENV['REMOTE_USER'] = ENV['REMOTE_USER'], "username" + begin + @cgi.remote_user.should == "username" + ensure + ENV['REMOTE_USER'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/request_method_spec.rb b/spec/rubyspec/library/cgi/queryextension/request_method_spec.rb new file mode 100644 index 0000000000..7fa85a3b34 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/request_method_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#request_method" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['REQUEST_METHOD']" do + old_value, ENV['REQUEST_METHOD'] = ENV['REQUEST_METHOD'], "GET" + begin + @cgi.request_method.should == "GET" + ensure + ENV['REQUEST_METHOD'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/script_name_spec.rb b/spec/rubyspec/library/cgi/queryextension/script_name_spec.rb new file mode 100644 index 0000000000..7509e002d4 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/script_name_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#script_name" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['SCRIPT_NAME']" do + old_value, ENV['SCRIPT_NAME'] = ENV['SCRIPT_NAME'], "/path/to/script.rb" + begin + @cgi.script_name.should == "/path/to/script.rb" + ensure + ENV['SCRIPT_NAME'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/server_name_spec.rb b/spec/rubyspec/library/cgi/queryextension/server_name_spec.rb new file mode 100644 index 0000000000..acc8f9e4aa --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/server_name_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#server_name" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['SERVER_NAME']" do + old_value, ENV['SERVER_NAME'] = ENV['SERVER_NAME'], "localhost" + begin + @cgi.server_name.should == "localhost" + ensure + ENV['SERVER_NAME'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/server_port_spec.rb b/spec/rubyspec/library/cgi/queryextension/server_port_spec.rb new file mode 100644 index 0000000000..adeabfda65 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/server_port_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#server_port" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['SERVER_PORT'] as Integer" do + old_value = ENV['SERVER_PORT'] + begin + ENV['SERVER_PORT'] = nil + @cgi.server_port.should be_nil + + ENV['SERVER_PORT'] = "3000" + @cgi.server_port.should eql(3000) + ensure + ENV['SERVER_PORT'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/server_protocol_spec.rb b/spec/rubyspec/library/cgi/queryextension/server_protocol_spec.rb new file mode 100644 index 0000000000..f1285bbd20 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/server_protocol_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#server_protocol" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['SERVER_PROTOCOL']" do + old_value, ENV['SERVER_PROTOCOL'] = ENV['SERVER_PROTOCOL'], "HTTP/1.1" + begin + @cgi.server_protocol.should == "HTTP/1.1" + ensure + ENV['SERVER_PROTOCOL'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/server_software_spec.rb b/spec/rubyspec/library/cgi/queryextension/server_software_spec.rb new file mode 100644 index 0000000000..e982a6f31c --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/server_software_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#server_software" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['SERVER_SOFTWARE']" do + old_value, ENV['SERVER_SOFTWARE'] = ENV['SERVER_SOFTWARE'], "Server/1.0.0" + begin + @cgi.server_software.should == "Server/1.0.0" + ensure + ENV['SERVER_SOFTWARE'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/shared/has_key.rb b/spec/rubyspec/library/cgi/queryextension/shared/has_key.rb new file mode 100644 index 0000000000..cfac5865fa --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/shared/has_key.rb @@ -0,0 +1,19 @@ +describe :cgi_query_extension_has_key_p, shared: true do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + ENV['QUERY_STRING'], @old_query_string = "one=a&two=b", ENV['QUERY_STRING'] + + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + ENV['QUERY_STRING'] = @old_query_string + end + + it "returns true when the passed key exists in the HTTP Query" do + @cgi.send(@method, "one").should be_true + @cgi.send(@method, "two").should be_true + @cgi.send(@method, "three").should be_false + end +end diff --git a/spec/rubyspec/library/cgi/queryextension/user_agent_spec.rb b/spec/rubyspec/library/cgi/queryextension/user_agent_spec.rb new file mode 100644 index 0000000000..8bbfed17c5 --- /dev/null +++ b/spec/rubyspec/library/cgi/queryextension/user_agent_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI::QueryExtension#user_agent" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + it "returns ENV['HTTP_USER_AGENT']" do + old_value, ENV['HTTP_USER_AGENT'] = ENV['HTTP_USER_AGENT'], "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; de-de) AppleWebKit/527+ (KHTML, like Gecko) Version/3.1 Safari/525.13" + begin + @cgi.user_agent.should == "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; de-de) AppleWebKit/527+ (KHTML, like Gecko) Version/3.1 Safari/525.13" + ensure + ENV['HTTP_USER_AGENT'] = old_value + end + end +end diff --git a/spec/rubyspec/library/cgi/rfc1123_date_spec.rb b/spec/rubyspec/library/cgi/rfc1123_date_spec.rb new file mode 100644 index 0000000000..73e07c6fbd --- /dev/null +++ b/spec/rubyspec/library/cgi/rfc1123_date_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.rfc1123_date when passsed Time" do + it "returns the passed Time formatted in RFC1123 ('Sat, 01 Dec 2007 15:56:42 GMT')" do + input = Time.at(1196524602) + expected = 'Sat, 01 Dec 2007 15:56:42 GMT' + CGI.rfc1123_date(input).should == expected + end +end diff --git a/spec/rubyspec/library/cgi/shared/http_header.rb b/spec/rubyspec/library/cgi/shared/http_header.rb new file mode 100644 index 0000000000..ed65b20abd --- /dev/null +++ b/spec/rubyspec/library/cgi/shared/http_header.rb @@ -0,0 +1,112 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'cgi' + +describe :cgi_http_header, shared: true do + describe "CGI#http_header when passed no arguments" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + + it "returns a HTTP header specifiying the Content-Type as text/html" do + @cgi.send(@method).should == "Content-Type: text/html\r\n\r\n" + end + + it "includes Cookies in the @output_cookies field" do + @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"]) + @cgi.send(@method).should == "Content-Type: text/html\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n" + end + end + + describe "CGI#http_header when passed String" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + + it "returns a HTTP header specifiying the Content-Type as the passed String's content" do + @cgi.send(@method, "text/plain").should == "Content-Type: text/plain\r\n\r\n" + end + + it "includes Cookies in the @output_cookies field" do + @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"]) + @cgi.send(@method, "text/plain").should == "Content-Type: text/plain\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n" + end + end + + describe "CGI#http_header when passed Hash" do + before :each do + ENV['REQUEST_METHOD'], @old_request_method = "GET", ENV['REQUEST_METHOD'] + @cgi = CGI.new + end + + after :each do + ENV['REQUEST_METHOD'] = @old_request_method + end + + + it "returns a HTTP header based on the Hash's key/value pairs" do + header = @cgi.send(@method, "type" => "text/plain") + header.should == "Content-Type: text/plain\r\n\r\n" + + header = @cgi.send(@method, "type" => "text/plain", "charset" => "UTF-8") + header.should == "Content-Type: text/plain; charset=UTF-8\r\n\r\n" + + header = @cgi.send(@method, "nph" => true) + header.should include("HTTP/1.0 200 OK\r\n") + header.should include("Date: ") + header.should include("Server: ") + header.should include("Connection: close\r\n") + header.should include("Content-Type: text/html\r\n") + + header = @cgi.send(@method, "status" => "OK") + header.should == "Status: 200 OK\r\nContent-Type: text/html\r\n\r\n" + + header = @cgi.send(@method, "status" => "PARTIAL_CONTENT") + header.should == "Status: 206 Partial Content\r\nContent-Type: text/html\r\n\r\n" + + header = @cgi.send(@method, "status" => "MULTIPLE_CHOICES") + header.should == "Status: 300 Multiple Choices\r\nContent-Type: text/html\r\n\r\n" + + header = @cgi.send(@method, "server" => "Server Software used") + header.should == "Server: Server Software used\r\nContent-Type: text/html\r\n\r\n" + + header = @cgi.send(@method, "connection" => "connection type") + header.should == "Connection: connection type\r\nContent-Type: text/html\r\n\r\n" + + header = @cgi.send(@method, "length" => 103) + header.should == "Content-Type: text/html\r\nContent-Length: 103\r\n\r\n" + + header = @cgi.send(@method, "language" => "ja") + header.should == "Content-Type: text/html\r\nContent-Language: ja\r\n\r\n" + + header = @cgi.send(@method, "expires" => Time.at(0)) + header.should == "Content-Type: text/html\r\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\r\n\r\n" + + header = @cgi.send(@method, "cookie" => "my cookie's content") + header.should == "Content-Type: text/html\r\nSet-Cookie: my cookie's content\r\n\r\n" + + header = @cgi.send(@method, "cookie" => ["multiple", "cookies"]) + header.should == "Content-Type: text/html\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n" + end + + it "includes Cookies in the @output_cookies field" do + @cgi.instance_variable_set(:@output_cookies, ["multiple", "cookies"]) + @cgi.send(@method, {}).should == "Content-Type: text/html\r\nSet-Cookie: multiple\r\nSet-Cookie: cookies\r\n\r\n" + end + + it "returns a HTTP header specifiying the Content-Type as text/html when passed an empty Hash" do + @cgi.send(@method, {}).should == "Content-Type: text/html\r\n\r\n" + end + end +end diff --git a/spec/rubyspec/library/cgi/unescapeElement_spec.rb b/spec/rubyspec/library/cgi/unescapeElement_spec.rb new file mode 100644 index 0000000000..cc26f9b484 --- /dev/null +++ b/spec/rubyspec/library/cgi/unescapeElement_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.unescapeElement when passed String, elements, ..." do + it "unescapes only the tags of the passed elements in the passed String" do + res = CGI.unescapeElement("<BR><A HREF="url"></A>", "A", "IMG") + res.should == '<BR>' + + res = CGI.unescapeElement('<BR><A HREF="url"></A>', ["A", "IMG"]) + res.should == '<BR>' + end + + it "is case-insensitive" do + res = CGI.unescapeElement("<BR><A HREF="url"></A>", "a", "img") + res.should == '<BR>' + + res = CGI.unescapeElement("<br><a href="url"></a>", "A", "IMG") + res.should == '<br>' + end +end diff --git a/spec/rubyspec/library/cgi/unescapeHTML_spec.rb b/spec/rubyspec/library/cgi/unescapeHTML_spec.rb new file mode 100644 index 0000000000..611ce0a6f1 --- /dev/null +++ b/spec/rubyspec/library/cgi/unescapeHTML_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.unescapeHTML" do + it "unescapes '& < > "' to '& < > \"'" do + input = '& < > "' + expected = '& < > "' + CGI.unescapeHTML(input).should == expected + end + + it "doesn't unescape other html entities such as '©' or '&heart'" do + input = '©&heart;' + expected = input + CGI.unescapeHTML(input).should == expected + end + + it "unescapes 'c' format entities" do + input = '"&'<>' + expected = '"&\'<>' + CGI.unescapeHTML(input).should == expected + end + + it "unescapes '香' format entities" do + input = '"&'<>' + expected = '"&\'<>' + CGI.unescapeHTML(input).should == expected + end + + it "leaves invalid formatted strings" do + input = '&<&>"&abcdefghijklmn' + expected = '&<&>"&abcdefghijklmn' + CGI.unescapeHTML(input).should == expected + end + + it "leaves partial invalid &# at end of string" do + input = "fooooooo&#" + CGI.unescapeHTML(input).should == input + end +end diff --git a/spec/rubyspec/library/cgi/unescape_spec.rb b/spec/rubyspec/library/cgi/unescape_spec.rb new file mode 100644 index 0000000000..8cf988b9dd --- /dev/null +++ b/spec/rubyspec/library/cgi/unescape_spec.rb @@ -0,0 +1,15 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require 'cgi' + +describe "CGI.unescape" do + it "url-decodes the passed argument" do + input = "+%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E" + expected = " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + CGI.unescape(input).should == expected + + input = 'http%3A%2F%2Fja.wikipedia.org%2Fwiki%2F%E3%83%AD%E3%83%A0%E3%82%B9%E3%82%AB%E3%83%BB%E3%83%91%E3%83%AD%E3%83%BB%E3%82%A6%E3%83%AB%E3%83%BB%E3%83%A9%E3%83%94%E3%83%A5%E3%82%BF' + expected = "http://ja.wikipedia.org/wiki/\343\203\255\343\203\240\343\202\271\343\202\253\343\203\273\343\203\221\343\203\255\343\203\273\343\202\246\343\203\253\343\203\273\343\203\251\343\203\224\343\203\245\343\202\277" + CGI.unescape(input).should == expected + end +end diff --git a/spec/rubyspec/library/complex/math/acos_spec.rb b/spec/rubyspec/library/complex/math/acos_spec.rb new file mode 100644 index 0000000000..84425dbaa1 --- /dev/null +++ b/spec/rubyspec/library/complex/math/acos_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/acos', __FILE__) + +describe "Math#acos" do + it_behaves_like :complex_math_acos, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:acos) + end +end + +describe "Math.acos" do + it_behaves_like :complex_math_acos, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/acosh_spec.rb b/spec/rubyspec/library/complex/math/acosh_spec.rb new file mode 100644 index 0000000000..cda9ce38d6 --- /dev/null +++ b/spec/rubyspec/library/complex/math/acosh_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/acosh', __FILE__) + +describe "Math#acosh" do + it_behaves_like :complex_math_acosh, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:acosh) + end +end + +describe "Math.acosh" do + it_behaves_like :complex_math_acosh, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/asin_spec.rb b/spec/rubyspec/library/complex/math/asin_spec.rb new file mode 100644 index 0000000000..aa26bed11d --- /dev/null +++ b/spec/rubyspec/library/complex/math/asin_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/asin', __FILE__) + +describe "Math#asin" do + it_behaves_like :complex_math_asin, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:asin) + end +end + +describe "Math.asin" do + it_behaves_like :complex_math_asin, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/asinh_spec.rb b/spec/rubyspec/library/complex/math/asinh_spec.rb new file mode 100644 index 0000000000..a1b0816b33 --- /dev/null +++ b/spec/rubyspec/library/complex/math/asinh_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/asinh', __FILE__) + +describe "Math#asinh" do + it_behaves_like :complex_math_asinh, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:asinh) + end +end + +describe "Math.asinh" do + it_behaves_like :complex_math_asinh, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/atan2_spec.rb b/spec/rubyspec/library/complex/math/atan2_spec.rb new file mode 100644 index 0000000000..7111b4a8ec --- /dev/null +++ b/spec/rubyspec/library/complex/math/atan2_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/atan2', __FILE__) + +describe "Math#atan2" do + it_behaves_like :complex_math_atan2, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:atan2) + end +end + +describe "Math.atan2" do + it_behaves_like :complex_math_atan2, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/atan_spec.rb b/spec/rubyspec/library/complex/math/atan_spec.rb new file mode 100644 index 0000000000..6659b309e4 --- /dev/null +++ b/spec/rubyspec/library/complex/math/atan_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/atan', __FILE__) + +describe "Math#atan" do + it_behaves_like :complex_math_atan, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:atan) + end +end + +describe "Math.atan" do + it_behaves_like :complex_math_atan, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/atanh_spec.rb b/spec/rubyspec/library/complex/math/atanh_spec.rb new file mode 100644 index 0000000000..a68674f82c --- /dev/null +++ b/spec/rubyspec/library/complex/math/atanh_spec.rb @@ -0,0 +1,17 @@ +require 'complex' +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../../../fixtures/math/common', __FILE__) +require File.expand_path('../../../../shared/math/atanh', __FILE__) +require File.expand_path('../shared/atanh', __FILE__) + +describe "Math#atanh" do + it_behaves_like :math_atanh_base, :atanh, IncludesMath.new + it_behaves_like :complex_math_atanh_complex, :atanh, IncludesMath.new + + it_behaves_like :math_atanh_private, :atanh, IncludesMath.new +end + +describe "Math.atanh" do + it_behaves_like :math_atanh_base, :atanh, CMath + it_behaves_like :complex_math_atanh_complex, :atanh, CMath +end diff --git a/spec/rubyspec/library/complex/math/cos_spec.rb b/spec/rubyspec/library/complex/math/cos_spec.rb new file mode 100644 index 0000000000..4267c601a6 --- /dev/null +++ b/spec/rubyspec/library/complex/math/cos_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/cos', __FILE__) + +describe "Math#cos" do + it_behaves_like :complex_math_cos, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:cos) + end +end + +describe "Math.cos" do + it_behaves_like :complex_math_cos, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/cosh_spec.rb b/spec/rubyspec/library/complex/math/cosh_spec.rb new file mode 100644 index 0000000000..b3aa1bdb69 --- /dev/null +++ b/spec/rubyspec/library/complex/math/cosh_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/cosh', __FILE__) + +describe "Math#cosh" do + it_behaves_like :complex_math_cosh, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:cosh) + end +end + +describe "Math.cosh" do + it_behaves_like :complex_math_cosh, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/exp_spec.rb b/spec/rubyspec/library/complex/math/exp_spec.rb new file mode 100644 index 0000000000..df1d12bbb5 --- /dev/null +++ b/spec/rubyspec/library/complex/math/exp_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/exp', __FILE__) + +describe "Math#exp" do + it_behaves_like :complex_math_exp, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:exp) + end +end + +describe "Math.exp" do + it_behaves_like :complex_math_exp, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/fixtures/classes.rb b/spec/rubyspec/library/complex/math/fixtures/classes.rb new file mode 100644 index 0000000000..443c1a9ace --- /dev/null +++ b/spec/rubyspec/library/complex/math/fixtures/classes.rb @@ -0,0 +1,4 @@ +require 'cmath' +class IncludesMath + include CMath +end diff --git a/spec/rubyspec/library/complex/math/log10_spec.rb b/spec/rubyspec/library/complex/math/log10_spec.rb new file mode 100644 index 0000000000..c7c5717873 --- /dev/null +++ b/spec/rubyspec/library/complex/math/log10_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/log10', __FILE__) + +describe "Math#log10" do + it_behaves_like :complex_math_log10, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:log10) + end +end + +describe "Math.log10" do + it_behaves_like :complex_math_log10, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/log_spec.rb b/spec/rubyspec/library/complex/math/log_spec.rb new file mode 100644 index 0000000000..f55b406af8 --- /dev/null +++ b/spec/rubyspec/library/complex/math/log_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/log', __FILE__) + +describe "Math#log" do + it_behaves_like :complex_math_log, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:log) + end +end + +describe "Math.log" do + it_behaves_like :complex_math_log, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/shared/acos.rb b/spec/rubyspec/library/complex/math/shared/acos.rb new file mode 100644 index 0000000000..7a9e0fe1b2 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/acos.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_acos, shared: true do + it "returns the arccosine of the passed argument" do + @object.send(:acos, 1).should be_close(0.0, TOLERANCE) + @object.send(:acos, 0).should be_close(1.5707963267949, TOLERANCE) + @object.send(:acos, -1).should be_close(Math::PI,TOLERANCE) + end + + it "returns the arccosine for Complex numbers" do + @object.send(:acos, Complex(3, 4)).should be_close(Complex(0.93681246115572, -2.30550903124348), TOLERANCE) + end + + it "returns the arccosine for numbers greater than 1.0 as a Complex number" do + @object.send(:acos, 1.0001).should be_close(Complex(0.0, 0.0141420177752494), TOLERANCE) + end + + it "returns the arccosine for numbers less than -1.0 as a Complex number" do + @object.send(:acos, -1.0001).should be_close(Complex(3.14159265358979, -0.0141420177752495), TOLERANCE) + end +end + +describe :complex_math_acos_bang, shared: true do + it "returns the arccosine of the argument" do + @object.send(:acos!, 1).should be_close(0.0, TOLERANCE) + @object.send(:acos!, 0).should be_close(1.5707963267949, TOLERANCE) + @object.send(:acos!, -1).should be_close(Math::PI,TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:acos!, Complex(4, 5)) }.should raise_error(TypeError) + end + + it "raises an Errno::EDOM for numbers greater than 1.0" do + lambda { @object.send(:acos!, 1.0001) }.should raise_error(Errno::EDOM) + end + + it "raises an Errno::EDOM for numbers less than -1.0" do + lambda { @object.send(:acos!, -1.0001) }.should raise_error(Errno::EDOM) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/acosh.rb b/spec/rubyspec/library/complex/math/shared/acosh.rb new file mode 100644 index 0000000000..b8be750f13 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/acosh.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_acosh, shared: true do + it "returns the principle value of the inverse hyperbolic cosine of the argument" do + @object.send(:acosh, 14.2).should be_close(3.345146999647, TOLERANCE) + @object.send(:acosh, 1.0).should be_close(0.0, TOLERANCE) + end + + it "returns the principle value of the inverse hyperbolic cosine for numbers less than 1.0 as a Complex number" do + @object.send(:acosh, 1.0 - TOLERANCE).should be_close(Complex(0.0, 0.00774598605746135), TOLERANCE) + @object.send(:acosh, 0).should be_close(Complex(0.0, 1.5707963267949), TOLERANCE) + @object.send(:acosh, -1.0).should be_close(Complex(0.0, 3.14159265358979), TOLERANCE) + end + + it "returns the principle value of the inverse hyperbolic cosine for Complex numbers" do + @object.send(:acosh, Complex(3, 4)) + @object.send(:acosh, Complex(3, 4)).imaginary.should be_close(0.93681246115572, TOLERANCE) + @object.send(:acosh, Complex(3, 4)).real.should be_close(2.305509031243477, TOLERANCE) + end +end + +describe :complex_math_acosh_bang, shared: true do + it "returns the principle value of the inverse hyperbolic cosine of the argument" do + @object.send(:acosh!, 14.2).should be_close(3.345146999647, TOLERANCE) + @object.send(:acosh!, 1.0).should be_close(0.0, TOLERANCE) + end + + it "raises Errno::EDOM for numbers less than 1.0" do + lambda { @object.send(:acosh!, 1.0 - TOLERANCE) }.should raise_error(Errno::EDOM) + lambda { @object.send(:acosh!, 0) }.should raise_error(Errno::EDOM) + lambda { @object.send(:acosh!, -1.0) }.should raise_error(Errno::EDOM) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:acosh!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/asin.rb b/spec/rubyspec/library/complex/math/shared/asin.rb new file mode 100644 index 0000000000..3b446cbe24 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/asin.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_asin, shared: true do + it "returns the arcsine of the argument" do + @object.send(:asin, 1).should be_close(Math::PI/2, TOLERANCE) + @object.send(:asin, 0).should be_close(0.0, TOLERANCE) + @object.send(:asin, -1).should be_close(-Math::PI/2, TOLERANCE) + @object.send(:asin, 0.25).should be_close(0.252680255142079, TOLERANCE) + @object.send(:asin, 0.50).should be_close(0.523598775598299, TOLERANCE) + @object.send(:asin, 0.75).should be_close(0.8480620789814816,TOLERANCE) + end + + it "returns the arcsine for Complex numbers" do + @object.send(:asin, Complex(3, 4)).should be_close(Complex(0.633983865639174, 2.30550903124347), TOLERANCE) + end + + it "returns a Complex number when the argument is greater than 1.0" do + @object.send(:asin, 1.0001).should be_close(Complex(1.5707963267949, -0.0141420177752494), TOLERANCE) + end + + it "returns a Complex number when the argument is less than -1.0" do + @object.send(:asin, -1.0001).should be_close(Complex(-1.5707963267949, 0.0141420177752494), TOLERANCE) + end +end + +describe :complex_math_asin_bang, shared: true do + it "returns the arcsine of the argument" do + @object.send(:asin!, 1).should be_close(Math::PI/2, TOLERANCE) + @object.send(:asin!, 0).should be_close(0.0, TOLERANCE) + @object.send(:asin!, -1).should be_close(-Math::PI/2, TOLERANCE) + @object.send(:asin!, 0.25).should be_close(0.252680255142079, TOLERANCE) + @object.send(:asin!, 0.50).should be_close(0.523598775598299, TOLERANCE) + @object.send(:asin!, 0.75).should be_close(0.8480620789814816,TOLERANCE) + end + + it "raises an Errno::EDOM if the argument is greater than 1.0" do + lambda { @object.send(:asin!, 1.0001) }.should raise_error( Errno::EDOM) + end + + it "raises an Errno::EDOM if the argument is less than -1.0" do + lambda { @object.send(:asin!, -1.0001) }.should raise_error( Errno::EDOM) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:asin!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/asinh.rb b/spec/rubyspec/library/complex/math/shared/asinh.rb new file mode 100644 index 0000000000..4c2f6c8b5d --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/asinh.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_asinh, shared: true do + it "returns the inverse hyperbolic sin of the argument" do + @object.send(:asinh, 1.5).should be_close(1.19476321728711, TOLERANCE) + @object.send(:asinh, -2.97).should be_close(-1.8089166921397, TOLERANCE) + @object.send(:asinh, 0.0).should == 0.0 + @object.send(:asinh, -0.0).should == -0.0 + @object.send(:asinh, 1.05367e-08).should be_close(1.05367e-08, TOLERANCE) + @object.send(:asinh, -1.05367e-08).should be_close(-1.05367e-08, TOLERANCE) + end + + it "returns the inverse hyperbolic sin for Complex numbers" do + @object.send(:asinh, Complex(3, 4)).should be_close(Complex(2.29991404087927, 0.917616853351479), TOLERANCE) + @object.send(:asinh, Complex(3.5, -4)).should be_close(Complex(2.36263337274419, -0.843166327537659), TOLERANCE) + end +end + +describe :complex_math_asinh_bang, shared: true do + it "returns the inverse hyperbolic sin of the argument" do + @object.send(:asinh!, 1.5).should be_close(1.19476321728711, TOLERANCE) + @object.send(:asinh!, -2.97).should be_close(-1.8089166921397, TOLERANCE) + @object.send(:asinh!, 0.0).should == 0.0 + @object.send(:asinh!, -0.0).should == -0.0 + @object.send(:asinh!, 1.05367e-08).should be_close(1.05367e-08, TOLERANCE) + @object.send(:asinh!, -1.05367e-08).should be_close(-1.05367e-08, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:asinh!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/atan.rb b/spec/rubyspec/library/complex/math/shared/atan.rb new file mode 100644 index 0000000000..44fa65e1e9 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/atan.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_atan, shared: true do + it "returns the arctangent of the argument" do + @object.send(:atan, 1).should be_close(Math::PI/4, TOLERANCE) + @object.send(:atan, 0).should be_close(0.0, TOLERANCE) + @object.send(:atan, -1).should be_close(-Math::PI/4, TOLERANCE) + @object.send(:atan, 0.25).should be_close(0.244978663126864, TOLERANCE) + @object.send(:atan, 0.50).should be_close(0.463647609000806, TOLERANCE) + @object.send(:atan, 0.75).should be_close(0.643501108793284, TOLERANCE) + end + + it "returns the arctangent for Complex numbers" do + @object.send(:atan, Complex(3, 4)).should be_close(Complex(1.44830699523146, 0.158997191679999), TOLERANCE) + @object.send(:atan, Complex(3.5, -4)).should be_close(Complex(1.44507428165589, -0.140323762363786), TOLERANCE) + end +end + +describe :complex_math_atan_bang, shared: true do + it "returns the arctangent of the argument" do + @object.send(:atan!, 1).should be_close(Math::PI/4, TOLERANCE) + @object.send(:atan!, 0).should be_close(0.0, TOLERANCE) + @object.send(:atan!, -1).should be_close(-Math::PI/4, TOLERANCE) + @object.send(:atan!, 0.25).should be_close(0.244978663126864, TOLERANCE) + @object.send(:atan!, 0.50).should be_close(0.463647609000806, TOLERANCE) + @object.send(:atan!, 0.75).should be_close(0.643501108793284, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:atan!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/atan2.rb b/spec/rubyspec/library/complex/math/shared/atan2.rb new file mode 100644 index 0000000000..add1dcd6fa --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/atan2.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_atan2, shared: true do + it "returns the arc tangent of the passed arguments" do + @object.send(:atan2, 4.2, 0.3).should be_close(1.49948886200961, TOLERANCE) + @object.send(:atan2, 0.0, 1.0).should be_close(0.0, TOLERANCE) + @object.send(:atan2, -9.1, 3.2).should be_close(-1.23265379809025, TOLERANCE) + @object.send(:atan2, 7.22, -3.3).should be_close(1.99950888779256, TOLERANCE) + end + + it "returns the arc tangent for two Complex numbers" do + CMath.atan2(Complex(3, 4), Complex(3.5, -4)).should be_close(Complex(-0.641757436698881, 1.10829873031207), TOLERANCE) + end + + it "returns the arc tangent for Complex and real numbers" do + CMath.atan2(Complex(3, 4), -7).should be_close(Complex(2.61576754731561, -0.494290673139855), TOLERANCE) + CMath.atan2(5, Complex(3.5, -4)).should be_close(Complex(0.739102348493673, 0.487821626522923), TOLERANCE) + end +end + +describe :complex_math_atan2_bang, shared: true do + it "returns the arc tangent of the passed arguments" do + @object.send(:atan2!, 4.2, 0.3).should be_close(1.49948886200961, TOLERANCE) + @object.send(:atan2!, 0.0, 1.0).should be_close(0.0, TOLERANCE) + @object.send(:atan2!, -9.1, 3.2).should be_close(-1.23265379809025, TOLERANCE) + @object.send(:atan2!, 7.22, -3.3).should be_close(1.99950888779256, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:atan2!, Complex(4, 5), Complex(4, 5)) }.should raise_error(TypeError) + lambda { @object.send(:atan2!, 4, Complex(4, 5)) }.should raise_error(TypeError) + lambda { @object.send(:atan2!, Complex(4, 5), 5) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/atanh.rb b/spec/rubyspec/library/complex/math/shared/atanh.rb new file mode 100644 index 0000000000..4051af081f --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/atanh.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_atanh_complex, shared: true do + it "returns the inverse hyperbolic tangent as a Complex number for arguments greater than 1.0" do + value = Complex(18.36840028483855, 1.5707963267948966) + @object.send(@method, 1.0 + Float::EPSILON).should be_close(value, TOLERANCE) + + value = Complex(0.100335347731076, 1.5707963267949) + @object.send(@method, 10).should be_close(value, TOLERANCE) + end + + it "returns the inverse hyperbolic tangent as a Complex number for arguments greater than 1.0" do + value = Complex(-18.36840028483855, 1.5707963267948966) + @object.send(@method, -1.0 - Float::EPSILON).should be_close(value, TOLERANCE) + + value = Complex(0.100335347731076, 1.5707963267949) + @object.send(@method, 10).should be_close(value, TOLERANCE) + end + + it "returns the inverse hyperbolic tangent for Complex numbers" do + value = Complex(0.117500907311434, 1.40992104959658) + @object.send(@method, Complex(3, 4)).should be_close(value, TOLERANCE) + end +end + +describe :complex_math_atanh_no_complex, shared: true do + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:atanh!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/cos.rb b/spec/rubyspec/library/complex/math/shared/cos.rb new file mode 100644 index 0000000000..ab198e1a3b --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/cos.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_cos, shared: true do + it "returns the cosine of the argument expressed in radians" do + @object.send(:cos, CMath::PI).should be_close(-1.0, TOLERANCE) + @object.send(:cos, 0).should be_close(1.0, TOLERANCE) + @object.send(:cos, CMath::PI/2).should be_close(0.0, TOLERANCE) + @object.send(:cos, 3*Math::PI/2).should be_close(0.0, TOLERANCE) + @object.send(:cos, 2*Math::PI).should be_close(1.0, TOLERANCE) + end + + it "returns the cosine for Complex numbers" do + @object.send(:cos, Complex(0, CMath::PI)).should be_close(Complex(11.5919532755215, 0.0), TOLERANCE) + @object.send(:cos, Complex(3, 4)).should be_close(Complex(-27.0349456030742, -3.85115333481178), TOLERANCE) + end +end + +describe :complex_math_cos_bang, shared: true do + it "returns the cosine of the argument expressed in radians" do + @object.send(:cos!, CMath::PI).should be_close(-1.0, TOLERANCE) + @object.send(:cos!, 0).should be_close(1.0, TOLERANCE) + @object.send(:cos!, CMath::PI/2).should be_close(0.0, TOLERANCE) + @object.send(:cos!, 3*Math::PI/2).should be_close(0.0, TOLERANCE) + @object.send(:cos!, 2*Math::PI).should be_close(1.0, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:cos!, Complex(3, 4)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/cosh.rb b/spec/rubyspec/library/complex/math/shared/cosh.rb new file mode 100644 index 0000000000..a5f98e4e83 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/cosh.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_cosh, shared: true do + it "returns the hyperbolic cosine of the passed argument" do + @object.send(:cosh, 0.0).should == 1.0 + @object.send(:cosh, -0.0).should == 1.0 + @object.send(:cosh, 1.5).should be_close(2.35240961524325, TOLERANCE) + @object.send(:cosh, -2.99).should be_close(9.96798496414416, TOLERANCE) + end + + it "returns the hyperbolic cosine for Complex numbers" do + @object.send(:cosh, Complex(0, CMath::PI)).should be_close(Complex(-1.0, 0.0), TOLERANCE) + @object.send(:cosh, Complex(3, 4)).should be_close(Complex(-6.58066304055116, -7.58155274274654), TOLERANCE) + end +end + +describe :complex_math_cosh_bang, shared: true do + it "returns the hyperbolic cosine of the passed argument" do + @object.send(:cosh!, 0.0).should == 1.0 + @object.send(:cosh!, -0.0).should == 1.0 + @object.send(:cosh!, 1.5).should be_close(2.35240961524325, TOLERANCE) + @object.send(:cosh!, -2.99).should be_close(9.96798496414416, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:cosh!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/exp.rb b/spec/rubyspec/library/complex/math/shared/exp.rb new file mode 100644 index 0000000000..f4e73dfb4d --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/exp.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_exp, shared: true do + it "returns the base-e exponential of the passed argument" do + @object.send(:exp, 0.0).should == 1.0 + @object.send(:exp, -0.0).should == 1.0 + @object.send(:exp, -1.8).should be_close(0.165298888221587, TOLERANCE) + @object.send(:exp, 1.25).should be_close(3.49034295746184, TOLERANCE) + end + + it "returns the base-e exponential for Complex numbers" do + @object.send(:exp, Complex(0, 0)).should == Complex(1.0, 0.0) + @object.send(:exp, Complex(1, 3)).should be_close(Complex(-2.69107861381979, 0.383603953541131), TOLERANCE) + end +end + +describe :complex_math_exp_bang, shared: true do + it "returns the base-e exponential of the passed argument" do + @object.send(:exp!, 0.0).should == 1.0 + @object.send(:exp!, -0.0).should == 1.0 + @object.send(:exp!, -1.8).should be_close(0.165298888221587, TOLERANCE) + @object.send(:exp!, 1.25).should be_close(3.49034295746184, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:exp!, Complex(1, 3)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/log.rb b/spec/rubyspec/library/complex/math/shared/log.rb new file mode 100644 index 0000000000..4e5385748a --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/log.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_log, shared: true do + it "returns the natural logarithm of the passed argument" do + @object.send(:log, 0.0001).should be_close(-9.21034037197618, TOLERANCE) + @object.send(:log, 0.000000000001e-15).should be_close(-62.1697975108392, TOLERANCE) + @object.send(:log, 1).should be_close(0.0, TOLERANCE) + @object.send(:log, 10).should be_close( 2.30258509299405, TOLERANCE) + @object.send(:log, 10e15).should be_close(36.8413614879047, TOLERANCE) + end + + it "returns the natural logarithm for Complex numbers" do + @object.send(:log, Complex(3, 4)).should be_close(Complex(1.6094379124341, 0.927295218001612), TOLERANCE) + @object.send(:log, Complex(-3, 4)).should be_close(Complex(1.6094379124341, 2.21429743558818), TOLERANCE) + end + + it "returns the natural logarithm for negative numbers as a Complex number" do + @object.send(:log, -10).should be_close(Complex(2.30258509299405, 3.14159265358979), TOLERANCE) + @object.send(:log, -20).should be_close(Complex(2.99573227355399, 3.14159265358979), TOLERANCE) + end +end + +describe :complex_math_log_bang, shared: true do + it "returns the natural logarithm of the argument" do + @object.send(:log!, 0.0001).should be_close(-9.21034037197618, TOLERANCE) + @object.send(:log!, 0.000000000001e-15).should be_close(-62.1697975108392, TOLERANCE) + @object.send(:log!, 1).should be_close(0.0, TOLERANCE) + @object.send(:log!, 10).should be_close( 2.30258509299405, TOLERANCE) + @object.send(:log!, 10e15).should be_close(36.8413614879047, TOLERANCE) + end + + it "raises an Errno::EDOM if the argument is less than 0" do + lambda { @object.send(:log!, -10) }.should raise_error(Errno::EDOM) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:log!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/log10.rb b/spec/rubyspec/library/complex/math/shared/log10.rb new file mode 100644 index 0000000000..13518f243e --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/log10.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_log10, shared: true do + it "returns the base-10 logarithm of the passed argument" do + @object.send(:log10, 0.0001).should be_close(-4.0, TOLERANCE) + @object.send(:log10, 0.000000000001e-15).should be_close(-27.0, TOLERANCE) + @object.send(:log10, 1).should be_close(0.0, TOLERANCE) + @object.send(:log10, 10).should be_close(1.0, TOLERANCE) + @object.send(:log10, 10e15).should be_close(16.0, TOLERANCE) + end + + it "returns the base-10 logarithm for Complex numbers" do + @object.send(:log10, Complex(3, 4)).should be_close(Complex(0.698970004336019, 0.402719196273373), TOLERANCE) + @object.send(:log10, Complex(-3, 4)).should be_close(Complex(0.698970004336019, 0.961657157568468), TOLERANCE) + end + + # BUG: does not work correctly, because Math#log10 + # does not check for negative values + #it "returns the base-10 logarithm for negative numbers as a Complex number" do + # @object.send(:log10, -10).should be_close(Complex(2.30258509299405, 3.14159265358979), TOLERANCE) + # @object.send(:log10, -20).should be_close(Complex(2.99573227355399, 3.14159265358979), TOLERANCE) + #end +end + +describe :complex_math_log10_bang, shared: true do + it "returns the base-10 logarithm of the argument" do + @object.send(:log10!, 0.0001).should be_close(-4.0, TOLERANCE) + @object.send(:log10!, 0.000000000001e-15).should be_close(-27.0, TOLERANCE) + @object.send(:log10!, 1).should be_close(0.0, TOLERANCE) + @object.send(:log10!, 10).should be_close(1.0, TOLERANCE) + @object.send(:log10!, 10e15).should be_close(16.0, TOLERANCE) + end + + it "raises an Errno::EDOM when the passed argument is negative" do + lambda { @object.send(:log10!, -10) }.should raise_error(Errno::EDOM) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:log10!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/sin.rb b/spec/rubyspec/library/complex/math/shared/sin.rb new file mode 100644 index 0000000000..e5b207b456 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/sin.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_sin, shared: true do + it "returns the sine of the passed argument expressed in radians" do + @object.send(:sin, CMath::PI).should be_close(0.0, TOLERANCE) + @object.send(:sin, 0).should be_close(0.0, TOLERANCE) + @object.send(:sin, CMath::PI/2).should be_close(1.0, TOLERANCE) + @object.send(:sin, 3*Math::PI/2).should be_close(-1.0, TOLERANCE) + @object.send(:sin, 2*Math::PI).should be_close(0.0, TOLERANCE) + end + + it "returns the sine for Complex numbers" do + @object.send(:sin, Complex(0, CMath::PI)).should be_close(Complex(0.0, 11.5487393572577), TOLERANCE) + @object.send(:sin, Complex(3, 4)).should be_close(Complex(3.85373803791938, -27.0168132580039), TOLERANCE) + end +end + +describe :complex_math_sin_bang, shared: true do + it "returns the sine of the passed argument expressed in radians" do + @object.send(:sin!, CMath::PI).should be_close(0.0, TOLERANCE) + @object.send(:sin!, 0).should be_close(0.0, TOLERANCE) + @object.send(:sin!, CMath::PI/2).should be_close(1.0, TOLERANCE) + @object.send(:sin!, 3*Math::PI/2).should be_close(-1.0, TOLERANCE) + @object.send(:sin!, 2*Math::PI).should be_close(0.0, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:sin!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/sinh.rb b/spec/rubyspec/library/complex/math/shared/sinh.rb new file mode 100644 index 0000000000..abbe2c6da0 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/sinh.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_sinh, shared: true do + it "returns the hyperbolic sin of the argument" do + @object.send(:sinh, 0.0).should == 0.0 + @object.send(:sinh, -0.0).should == 0.0 + @object.send(:sinh, 1.5).should be_close(2.12927945509482, TOLERANCE) + @object.send(:sinh, -2.8).should be_close(-8.19191835423591, TOLERANCE) + end + + it "returns the hyperbolic sin for Complex numbers" do + @object.send(:sinh, Complex(0, CMath::PI)).should be_close(Complex(-0.0, 1.22464679914735e-16), TOLERANCE) + @object.send(:sinh, Complex(3, 4)).should be_close(Complex(-6.548120040911, -7.61923172032141), TOLERANCE) + end +end + +describe :complex_math_sinh_bang, shared: true do + it "returns the hyperbolic sin of the argument" do + @object.send(:sinh!, 0.0).should == 0.0 + @object.send(:sinh!, -0.0).should == 0.0 + @object.send(:sinh!, 1.5).should be_close(2.12927945509482, TOLERANCE) + @object.send(:sinh!, -2.8).should be_close(-8.19191835423591, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:sinh!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/sqrt.rb b/spec/rubyspec/library/complex/math/shared/sqrt.rb new file mode 100644 index 0000000000..5125710119 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/sqrt.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_sqrt, shared: true do + it "returns the square root for positive numbers" do + @object.send(:sqrt, 4).should == 2 + @object.send(:sqrt, 19.36).should == 4.4 + end + + it "returns the square root for negative numbers" do + @object.send(:sqrt, -4).should == Complex(0, 2.0) + @object.send(:sqrt, -19.36).should == Complex(0, 4.4) + end + + it "returns the square root for Complex numbers" do + @object.send(:sqrt, Complex(4, 5)).should be_close(Complex(2.2806933416653, 1.09615788950152), TOLERANCE) + @object.send(:sqrt, Complex(4, -5)).should be_close(Complex(2.2806933416653, -1.09615788950152), TOLERANCE) + end +end + +describe :complex_math_sqrt_bang, shared: true do + it "returns the square root for positive numbers" do + @object.send(:sqrt!, 4).should == 2 + @object.send(:sqrt!, 19.36).should == 4.4 + end + + it "raises Errno::EDOM when the passed argument is negative" do + lambda { @object.send(:sqrt!, -4) }.should raise_error(Errno::EDOM) + lambda { @object.send(:sqrt!, -19.36) }.should raise_error(Errno::EDOM) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:sqrt!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/tan.rb b/spec/rubyspec/library/complex/math/shared/tan.rb new file mode 100644 index 0000000000..02a1880d27 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/tan.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_tan, shared: true do + it "returns the tangent of the argument" do + @object.send(:tan, 0.0).should == 0.0 + @object.send(:tan, -0.0).should == -0.0 + @object.send(:tan, 4.22).should be_close(1.86406937682395, TOLERANCE) + @object.send(:tan, -9.65).should be_close(-0.229109052606441, TOLERANCE) + end + + it "returns the tangent for Complex numbers" do + @object.send(:tan, Complex(0, CMath::PI)).should be_close(Complex(0.0, 0.99627207622075), TOLERANCE) + @object.send(:tan, Complex(3, 4)).should be_close(Complex(-0.000187346204629452, 0.999355987381473), TOLERANCE) + end +end + +describe :complex_math_tan_bang, shared: true do + it "returns the tangent of the argument" do + @object.send(:tan!, 0.0).should == 0.0 + @object.send(:tan!, -0.0).should == -0.0 + @object.send(:tan!, 4.22).should be_close(1.86406937682395, TOLERANCE) + @object.send(:tan!, -9.65).should be_close(-0.229109052606441, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:tan!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/shared/tanh.rb b/spec/rubyspec/library/complex/math/shared/tanh.rb new file mode 100644 index 0000000000..a26b1349f6 --- /dev/null +++ b/spec/rubyspec/library/complex/math/shared/tanh.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :complex_math_tanh, shared: true do + it "returns the hyperbolic tangent of the argument" do + @object.send(:tanh, 0.0).should == 0.0 + @object.send(:tanh, -0.0).should == -0.0 + @object.send(:tanh, infinity_value).should == 1.0 + @object.send(:tanh, -infinity_value).should == -1.0 + @object.send(:tanh, 2.5).should be_close(0.98661429815143, TOLERANCE) + @object.send(:tanh, -4.892).should be_close(-0.999887314427707, TOLERANCE) + end + + it "returns the hyperbolic tangent for Complex numbers" do + @object.send(:tanh, Complex(0, CMath::PI)).should be_close(Complex(0.0, -1.22464679914735e-16), TOLERANCE) + @object.send(:tanh, Complex(3, 4)).should be_close(Complex(1.00070953606723, 0.00490825806749599), TOLERANCE) + end +end + +describe :complex_math_tanh_bang, shared: true do + it "returns the hyperbolic tangent of the argument" do + @object.send(:tanh!, 0.0).should == 0.0 + @object.send(:tanh!, -0.0).should == -0.0 + @object.send(:tanh!, infinity_value).should == 1.0 + @object.send(:tanh!, -infinity_value).should == -1.0 + @object.send(:tanh!, 2.5).should be_close(0.98661429815143, TOLERANCE) + @object.send(:tanh!, -4.892).should be_close(-0.999887314427707, TOLERANCE) + end + + it "raises a TypeError when passed a Complex number" do + lambda { @object.send(:tanh!, Complex(4, 5)) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/complex/math/sin_spec.rb b/spec/rubyspec/library/complex/math/sin_spec.rb new file mode 100644 index 0000000000..bbc36ecaab --- /dev/null +++ b/spec/rubyspec/library/complex/math/sin_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/sin', __FILE__) + +describe "Math#sin" do + it_behaves_like :complex_math_sin, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:sin) + end +end + +describe "Math.sin" do + it_behaves_like :complex_math_sin, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/sinh_spec.rb b/spec/rubyspec/library/complex/math/sinh_spec.rb new file mode 100644 index 0000000000..25a41fbc45 --- /dev/null +++ b/spec/rubyspec/library/complex/math/sinh_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/sinh', __FILE__) + +describe "Math#sinh" do + it_behaves_like :complex_math_sinh, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:sinh) + end +end + +describe "Math.sinh" do + it_behaves_like :complex_math_sinh, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/sqrt_spec.rb b/spec/rubyspec/library/complex/math/sqrt_spec.rb new file mode 100644 index 0000000000..19d58ce373 --- /dev/null +++ b/spec/rubyspec/library/complex/math/sqrt_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/sqrt', __FILE__) + +describe "Math#sqrt" do + it_behaves_like :complex_math_sqrt, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:sqrt) + end +end + +describe "Math.sqrt" do + it_behaves_like :complex_math_sqrt, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/tan_spec.rb b/spec/rubyspec/library/complex/math/tan_spec.rb new file mode 100644 index 0000000000..3c5bc6b129 --- /dev/null +++ b/spec/rubyspec/library/complex/math/tan_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/tan', __FILE__) + +describe "Math#tan" do + it_behaves_like :complex_math_tan, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:tan) + end +end + +describe "Math.tan" do + it_behaves_like :complex_math_tan, :_, CMath +end diff --git a/spec/rubyspec/library/complex/math/tanh_spec.rb b/spec/rubyspec/library/complex/math/tanh_spec.rb new file mode 100644 index 0000000000..f9d99347b8 --- /dev/null +++ b/spec/rubyspec/library/complex/math/tanh_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'complex' +require File.expand_path('../shared/tanh', __FILE__) + +describe "Math#tanh" do + it_behaves_like :complex_math_tanh, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:tanh) + end +end + +describe "Math.tanh" do + it_behaves_like :complex_math_tanh, :_, CMath +end diff --git a/spec/rubyspec/library/complex/numeric/im_spec.rb b/spec/rubyspec/library/complex/numeric/im_spec.rb new file mode 100644 index 0000000000..f76796a947 --- /dev/null +++ b/spec/rubyspec/library/complex/numeric/im_spec.rb @@ -0,0 +1,3 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +require 'complex' diff --git a/spec/rubyspec/library/conditionvariable/broadcast_spec.rb b/spec/rubyspec/library/conditionvariable/broadcast_spec.rb new file mode 100644 index 0000000000..129b124c1a --- /dev/null +++ b/spec/rubyspec/library/conditionvariable/broadcast_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'thread' + +describe "ConditionVariable#broadcast" do + it "returns self if nothing to broadcast to" do + cv = ConditionVariable.new + cv.broadcast.should == cv + end + + it "returns self if something is waiting for a broadcast" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + + th = Thread.new do + m.synchronize do + in_synchronize = true + cv.wait(m) + end + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass while th.status and th.status != "sleep" + + m.synchronize { cv.broadcast }.should == cv + + th.join + end + + it "releases all threads waiting in line for this resource" do + m = Mutex.new + cv = ConditionVariable.new + threads = [] + r1 = [] + r2 = [] + + # large number to attempt to cause race conditions + 100.times do |i| + threads << Thread.new(i) do |tid| + m.synchronize do + r1 << tid + cv.wait(m) + r2 << tid + end + end + end + + # wait for all threads to acquire the mutex the first time + Thread.pass until m.synchronize { r1.size == threads.size } + # wait until all threads are sleeping (ie waiting) + Thread.pass until threads.all? {|th| th.status == "sleep" } + + r2.should be_empty + m.synchronize do + cv.broadcast + end + + threads.each {|t| t.join } + + # ensure that all threads that enter cv.wait are released + r2.sort.should == r1.sort + # note that order is not specified as broadcast results in a race + # condition on regaining the lock m + end +end diff --git a/spec/rubyspec/library/conditionvariable/signal_spec.rb b/spec/rubyspec/library/conditionvariable/signal_spec.rb new file mode 100644 index 0000000000..38bee8984b --- /dev/null +++ b/spec/rubyspec/library/conditionvariable/signal_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'thread' + +describe "ConditionVariable#signal" do + it "returns self if nothing to signal" do + cv = ConditionVariable.new + cv.signal.should == cv + end + + it "returns self if something is waiting for a signal" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + + th = Thread.new do + m.synchronize do + in_synchronize = true + cv.wait(m) + end + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass while th.status and th.status != "sleep" + + m.synchronize { cv.signal }.should == cv + + th.join + end + + it "releases the first thread waiting in line for this resource" do + m = Mutex.new + cv = ConditionVariable.new + threads = [] + r1 = [] + r2 = [] + + # large number to attempt to cause race conditions + 100.times do |i| + threads << Thread.new(i) do |tid| + m.synchronize do + r1 << tid + cv.wait(m) + r2 << tid + end + end + end + + # wait for all threads to acquire the mutex the first time + Thread.pass until m.synchronize { r1.size == threads.size } + # wait until all threads are sleeping (ie waiting) + Thread.pass until threads.all? {|th| th.status == "sleep" } + + r2.should be_empty + 100.times do |i| + m.synchronize do + cv.signal + end + Thread.pass until r2.size == i+1 + end + + threads.each {|t| t.join } + + # ensure that all the threads that went into the cv.wait are + # released in the same order + r2.should == r1 + end +end diff --git a/spec/rubyspec/library/conditionvariable/wait_spec.rb b/spec/rubyspec/library/conditionvariable/wait_spec.rb new file mode 100644 index 0000000000..ddf6474212 --- /dev/null +++ b/spec/rubyspec/library/conditionvariable/wait_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'thread' + +describe "ConditionVariable#wait" do + it "returns self" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + + th = Thread.new do + m.synchronize do + in_synchronize = true + cv.wait(m).should == cv + end + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass while th.status and th.status != "sleep" + + m.synchronize { cv.signal } + th.join + end +end diff --git a/spec/rubyspec/library/coverage/fixtures/second_class.rb b/spec/rubyspec/library/coverage/fixtures/second_class.rb new file mode 100644 index 0000000000..0318ac26ff --- /dev/null +++ b/spec/rubyspec/library/coverage/fixtures/second_class.rb @@ -0,0 +1,5 @@ +class SecondClass + def some_method + 42 + end +end diff --git a/spec/rubyspec/library/coverage/fixtures/some_class.rb b/spec/rubyspec/library/coverage/fixtures/some_class.rb new file mode 100644 index 0000000000..52629f0332 --- /dev/null +++ b/spec/rubyspec/library/coverage/fixtures/some_class.rb @@ -0,0 +1,16 @@ + +#Class comment +class SomeClass + + # Method comment + def some_method + + # Inline method comment + actual_code = true + + end + +end + +# Trailing comment and extra blank line + diff --git a/spec/rubyspec/library/coverage/fixtures/spec_helper.rb b/spec/rubyspec/library/coverage/fixtures/spec_helper.rb new file mode 100644 index 0000000000..19094e5c36 --- /dev/null +++ b/spec/rubyspec/library/coverage/fixtures/spec_helper.rb @@ -0,0 +1,11 @@ +module CoverageSpecs + # Clear old results from the result hash + # https://bugs.ruby-lang.org/issues/12220 + def self.filtered_result + result = Coverage.result + ruby_version_is ""..."2.4" do + result = result.reject { |_k, v| v.empty? } + end + result + end +end diff --git a/spec/rubyspec/library/coverage/fixtures/start_coverage.rb b/spec/rubyspec/library/coverage/fixtures/start_coverage.rb new file mode 100644 index 0000000000..8a0c56c50a --- /dev/null +++ b/spec/rubyspec/library/coverage/fixtures/start_coverage.rb @@ -0,0 +1,3 @@ +2 + 2 +Coverage.start +1 + 1 diff --git a/spec/rubyspec/library/coverage/peek_result_spec.rb b/spec/rubyspec/library/coverage/peek_result_spec.rb new file mode 100644 index 0000000000..44a2e2b83e --- /dev/null +++ b/spec/rubyspec/library/coverage/peek_result_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require fixture __FILE__, 'spec_helper' +require 'coverage' + +ruby_version_is '2.3' do + describe 'Coverage.peek_result' do + before :all do + @class_file = fixture __FILE__, 'some_class.rb' + @second_class_file = fixture __FILE__, 'second_class.rb' + end + + after :each do + $LOADED_FEATURES.delete(@class_file) + $LOADED_FEATURES.delete(@second_class_file) + end + + it 'returns the result so far' do + Coverage.start + require @class_file.chomp('.rb') + result = Coverage.peek_result + Coverage.result + + result.should == { + @class_file => [ + nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil + ] + } + end + + it 'immediate second call returns same result' do + Coverage.start + require @class_file.chomp('.rb') + result1 = Coverage.peek_result + result2 = Coverage.peek_result + Coverage.result + + result2.should == result1 + end + + it 'second call after require returns accumulated result' do + Coverage.start + require @class_file.chomp('.rb') + Coverage.peek_result + require @second_class_file.chomp('.rb') + result = Coverage.peek_result + Coverage.result + + result.should == { + @class_file => [ + nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil + ], + @second_class_file => [ + 1, 1, 0, nil, nil + ] + } + end + + it 'call right before Coverage.result should give equal result' do + Coverage.start + require @class_file.chomp('.rb') + result1 = Coverage.peek_result + result2 = Coverage.result + + result1.should == result2 + end + end +end diff --git a/spec/rubyspec/library/coverage/result_spec.rb b/spec/rubyspec/library/coverage/result_spec.rb new file mode 100644 index 0000000000..adcc51dc80 --- /dev/null +++ b/spec/rubyspec/library/coverage/result_spec.rb @@ -0,0 +1,78 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require fixture __FILE__, 'spec_helper' +require 'coverage' + +describe 'Coverage.result' do + before :all do + @class_file = fixture __FILE__, 'some_class.rb' + @config_file = fixture __FILE__, 'start_coverage.rb' + end + + after :each do + $LOADED_FEATURES.delete(@class_file) + $LOADED_FEATURES.delete(@config_file) + end + + it 'gives the covered files as a hash with arrays of count or nil' do + Coverage.start + require @class_file.chomp('.rb') + result = CoverageSpecs.filtered_result + + result.should == { + @class_file => [ + nil, nil, 1, nil, nil, 1, nil, nil, 0, nil, nil, nil, nil, nil, nil, nil + ] + } + end + + it 'no requires/loads should give empty hash' do + Coverage.start + result = CoverageSpecs.filtered_result + + result.should == {} + end + + it 'second call should give exception' do + Coverage.start + require @class_file.chomp('.rb') + Coverage.result + -> { Coverage.result } + .should raise_error(RuntimeError, 'coverage measurement is not enabled') + end + + it 'second run should give same result' do + Coverage.start + load @class_file + result1 = CoverageSpecs.filtered_result + + Coverage.start + load @class_file + result2 = CoverageSpecs.filtered_result + + result2.should == result1 + end + + it 'second run without load/require should give empty hash' do + Coverage.start + require @class_file.chomp('.rb') + Coverage.result + + Coverage.start + result = CoverageSpecs.filtered_result + + result.should == {} + end + + it 'second Coverage.start does nothing' do + Coverage.start + require @config_file.chomp('.rb') + result = CoverageSpecs.filtered_result + + result.should == { @config_file => [1, 1, 1] } + end + + it 'does not include the file starting coverage since it is not tracked' do + require @config_file.chomp('.rb') + CoverageSpecs.filtered_result.should_not include(@config_file) + end +end diff --git a/spec/rubyspec/library/coverage/start_spec.rb b/spec/rubyspec/library/coverage/start_spec.rb new file mode 100644 index 0000000000..6b4ccbb043 --- /dev/null +++ b/spec/rubyspec/library/coverage/start_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'coverage' + +describe 'Coverage.start' do + it 'needs to be reviewed for spec completeness' +end diff --git a/spec/rubyspec/library/csv/basicwriter/close_on_terminate_spec.rb b/spec/rubyspec/library/csv/basicwriter/close_on_terminate_spec.rb new file mode 100644 index 0000000000..3a5dc85ed7 --- /dev/null +++ b/spec/rubyspec/library/csv/basicwriter/close_on_terminate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::BasicWriter#close_on_terminate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/basicwriter/initialize_spec.rb b/spec/rubyspec/library/csv/basicwriter/initialize_spec.rb new file mode 100644 index 0000000000..8ccef3fc0d --- /dev/null +++ b/spec/rubyspec/library/csv/basicwriter/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::BasicWriter#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/basicwriter/terminate_spec.rb b/spec/rubyspec/library/csv/basicwriter/terminate_spec.rb new file mode 100644 index 0000000000..80581e4d61 --- /dev/null +++ b/spec/rubyspec/library/csv/basicwriter/terminate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::BasicWriter#terminate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/cell/data_spec.rb b/spec/rubyspec/library/csv/cell/data_spec.rb new file mode 100644 index 0000000000..b2be38a5d6 --- /dev/null +++ b/spec/rubyspec/library/csv/cell/data_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Cell#data" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/cell/initialize_spec.rb b/spec/rubyspec/library/csv/cell/initialize_spec.rb new file mode 100644 index 0000000000..3d10d7ada7 --- /dev/null +++ b/spec/rubyspec/library/csv/cell/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Cell#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/fixtures/one_line.csv b/spec/rubyspec/library/csv/fixtures/one_line.csv new file mode 100644 index 0000000000..d72f2010a8 --- /dev/null +++ b/spec/rubyspec/library/csv/fixtures/one_line.csv @@ -0,0 +1 @@ +1,2 diff --git a/spec/rubyspec/library/csv/foreach_spec.rb b/spec/rubyspec/library/csv/foreach_spec.rb new file mode 100644 index 0000000000..a967c450bf --- /dev/null +++ b/spec/rubyspec/library/csv/foreach_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV.foreach" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/generate_line_spec.rb b/spec/rubyspec/library/csv/generate_line_spec.rb new file mode 100644 index 0000000000..cc7c8de4f0 --- /dev/null +++ b/spec/rubyspec/library/csv/generate_line_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV.generate_line" do + + it "generates an empty string" do + result = CSV.generate_line([]) + result.should == "\n" + end + + it "generates the string 'foo,bar'" do + result = CSV.generate_line(["foo", "bar"]) + result.should == "foo,bar\n" + end + + it "generates the string 'foo;bar'" do + result = CSV.generate_line(["foo", "bar"], col_sep: ?;) + result.should == "foo;bar\n" + end + + it "generates the string 'foo,,bar'" do + result = CSV.generate_line(["foo", nil, "bar"]) + result.should == "foo,,bar\n" + end + + it "generates the string 'foo;;bar'" do + result = CSV.generate_line(["foo", nil, "bar"], col_sep: ?;) + result.should == "foo;;bar\n" + end +end diff --git a/spec/rubyspec/library/csv/generate_row_spec.rb b/spec/rubyspec/library/csv/generate_row_spec.rb new file mode 100644 index 0000000000..d42c031ab1 --- /dev/null +++ b/spec/rubyspec/library/csv/generate_row_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV.generate_row" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/generate_spec.rb b/spec/rubyspec/library/csv/generate_spec.rb new file mode 100644 index 0000000000..f583b5f536 --- /dev/null +++ b/spec/rubyspec/library/csv/generate_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' +require 'tempfile' + +describe "CSV.generate" do + + it "returns CSV string" do + csv_str = CSV.generate do |csv| + csv.add_row [1, 2, 3] + csv << [4, 5, 6] + end + csv_str.should == "1,2,3\n4,5,6\n" + end + + it "accepts a col separator" do + csv_str = CSV.generate(col_sep: ";") do |csv| + csv.add_row [1, 2, 3] + csv << [4, 5, 6] + end + csv_str.should == "1;2;3\n4;5;6\n" + end + + it "appends and returns the argument itself" do + str = "" + csv_str = CSV.generate(str) do |csv| + csv.add_row [1, 2, 3] + csv << [4, 5, 6] + end + csv_str.object_id.should == str.object_id + str.should == "1,2,3\n4,5,6\n" + end +end diff --git a/spec/rubyspec/library/csv/iobuf/close_spec.rb b/spec/rubyspec/library/csv/iobuf/close_spec.rb new file mode 100644 index 0000000000..d97841ad98 --- /dev/null +++ b/spec/rubyspec/library/csv/iobuf/close_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOBuf#close" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/iobuf/initialize_spec.rb b/spec/rubyspec/library/csv/iobuf/initialize_spec.rb new file mode 100644 index 0000000000..5155e9047a --- /dev/null +++ b/spec/rubyspec/library/csv/iobuf/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOBuf#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/iobuf/read_spec.rb b/spec/rubyspec/library/csv/iobuf/read_spec.rb new file mode 100644 index 0000000000..0dbcccb7c2 --- /dev/null +++ b/spec/rubyspec/library/csv/iobuf/read_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOBuf#read" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/iobuf/terminate_spec.rb b/spec/rubyspec/library/csv/iobuf/terminate_spec.rb new file mode 100644 index 0000000000..b74108cedc --- /dev/null +++ b/spec/rubyspec/library/csv/iobuf/terminate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOBuf#terminate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/ioreader/close_on_terminate_spec.rb b/spec/rubyspec/library/csv/ioreader/close_on_terminate_spec.rb new file mode 100644 index 0000000000..75ce325a8b --- /dev/null +++ b/spec/rubyspec/library/csv/ioreader/close_on_terminate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOReader#close_on_terminate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/ioreader/get_row_spec.rb b/spec/rubyspec/library/csv/ioreader/get_row_spec.rb new file mode 100644 index 0000000000..91049f014a --- /dev/null +++ b/spec/rubyspec/library/csv/ioreader/get_row_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOReader#get_row" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/ioreader/initialize_spec.rb b/spec/rubyspec/library/csv/ioreader/initialize_spec.rb new file mode 100644 index 0000000000..63a47eff73 --- /dev/null +++ b/spec/rubyspec/library/csv/ioreader/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOReader#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/ioreader/terminate_spec.rb b/spec/rubyspec/library/csv/ioreader/terminate_spec.rb new file mode 100644 index 0000000000..95259afd59 --- /dev/null +++ b/spec/rubyspec/library/csv/ioreader/terminate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::IOReader#terminate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/open_spec.rb b/spec/rubyspec/library/csv/open_spec.rb new file mode 100644 index 0000000000..e0667921b9 --- /dev/null +++ b/spec/rubyspec/library/csv/open_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV.open" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/parse_spec.rb b/spec/rubyspec/library/csv/parse_spec.rb new file mode 100644 index 0000000000..41d37ca9a4 --- /dev/null +++ b/spec/rubyspec/library/csv/parse_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV.parse" do + + it "parses '' into []" do + result = CSV.parse '' + result.should be_kind_of(Array) + result.should == [] + end + + it "parses '\n' into [[]]" do + result = CSV.parse "\n" + result.should == [[]] + end + + it "parses 'foo' into [['foo']]" do + result = CSV.parse 'foo' + result.should == [['foo']] + end + + it "parses 'foo,bar,baz' into [['foo','bar','baz']]" do + result = CSV.parse 'foo,bar,baz' + result.should == [['foo','bar','baz']] + end + + it "parses 'foo,baz' into [[foo,nil,baz]]" do + result = CSV.parse 'foo,,baz' + result.should == [['foo',nil,'baz']] + end + + it "parses '\nfoo' into [[],['foo']]" do + result = CSV.parse "\nfoo" + result.should == [[],['foo']] + end + + it "parses 'foo\n' into [['foo']]" do + result = CSV.parse "foo\n" + result.should == [['foo']] + end + + it "parses 'foo\nbar' into [['foo'],['bar']]" do + result = CSV.parse "foo\nbar" + result.should == [['foo'],['bar']] + end + + it "parses 'foo,bar\nbaz,quz' into [['foo','bar'],['baz','quz']]" do + result = CSV.parse "foo,bar\nbaz,quz" + result.should == [['foo','bar'],['baz','quz']] + end + + it "parses 'foo,bar'\nbaz' into [['foo','bar'],['baz']]" do + result = CSV.parse "foo,bar\nbaz" + result.should == [['foo','bar'],['baz']] + end + + it "parses 'foo\nbar,baz' into [['foo'],['bar','baz']]" do + result = CSV.parse "foo\nbar,baz" + result.should == [['foo'],['bar','baz']] + end + + it "parses '\n\nbar' into [[],[],'bar']]" do + result = CSV.parse "\n\nbar" + result.should == [[],[],['bar']] + end + + it "parses 'foo' into [['foo']] with a separator of ;" do + result = CSV.parse "foo", col_sep: ?; + result.should == [['foo']] + end + + it "parses 'foo;bar' into [['foo','bar']] with a separator of ;" do + result = CSV.parse "foo;bar", col_sep: ?; + result.should == [['foo','bar']] + end + + it "parses 'foo;bar\nbaz;quz' into [['foo','bar'],['baz','quz']] with a separator of ;" do + result = CSV.parse "foo;bar\nbaz;quz", col_sep: ?; + result.should == [['foo','bar'],['baz','quz']] + end +end diff --git a/spec/rubyspec/library/csv/read_spec.rb b/spec/rubyspec/library/csv/read_spec.rb new file mode 100644 index 0000000000..9bdb214a6a --- /dev/null +++ b/spec/rubyspec/library/csv/read_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV.read" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/readlines_spec.rb b/spec/rubyspec/library/csv/readlines_spec.rb new file mode 100644 index 0000000000..387730db16 --- /dev/null +++ b/spec/rubyspec/library/csv/readlines_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV.readlines" do + it "needs to be reviewed for spec completeness" +end + +describe "CSV#readlines" do + it "returns an Array of Array containing each element in a one-line CSV file" do + file = CSV.new "a, b, c" + file.readlines.should == [["a", " b", " c"]] + end + + it "returns an Array of Arrays containing each element in a multi-line CSV file" do + file = CSV.new "a, b, c\nd, e, f" + file.readlines.should == [["a", " b", " c"], ["d", " e", " f"]] + end + + it "returns nil for a missing value" do + file = CSV.new "a,, b, c" + file.readlines.should == [["a", nil, " b", " c"]] + end +end diff --git a/spec/rubyspec/library/csv/streambuf/add_buf_spec.rb b/spec/rubyspec/library/csv/streambuf/add_buf_spec.rb new file mode 100644 index 0000000000..274f40d496 --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/add_buf_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#add_buf" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/buf_size_spec.rb b/spec/rubyspec/library/csv/streambuf/buf_size_spec.rb new file mode 100644 index 0000000000..30af8a44c0 --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/buf_size_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#buf_size" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/drop_spec.rb b/spec/rubyspec/library/csv/streambuf/drop_spec.rb new file mode 100644 index 0000000000..47f32fc8c5 --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/drop_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#drop" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/element_reference_spec.rb b/spec/rubyspec/library/csv/streambuf/element_reference_spec.rb new file mode 100644 index 0000000000..4e4bab9a1e --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/element_reference_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#[]" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/get_spec.rb b/spec/rubyspec/library/csv/streambuf/get_spec.rb new file mode 100644 index 0000000000..4a8db3a970 --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/get_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#get" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/idx_is_eos_spec.rb b/spec/rubyspec/library/csv/streambuf/idx_is_eos_spec.rb new file mode 100644 index 0000000000..d48e6735f0 --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/idx_is_eos_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#idx_is_eos?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/initialize_spec.rb b/spec/rubyspec/library/csv/streambuf/initialize_spec.rb new file mode 100644 index 0000000000..0f76220d3d --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/is_eos_spec.rb b/spec/rubyspec/library/csv/streambuf/is_eos_spec.rb new file mode 100644 index 0000000000..30167f30ff --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/is_eos_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#is_eos?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/read_spec.rb b/spec/rubyspec/library/csv/streambuf/read_spec.rb new file mode 100644 index 0000000000..b07467bdb7 --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/read_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#read" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/rel_buf_spec.rb b/spec/rubyspec/library/csv/streambuf/rel_buf_spec.rb new file mode 100644 index 0000000000..e01dc39cf5 --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/rel_buf_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#rel_buf" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/streambuf/terminate_spec.rb b/spec/rubyspec/library/csv/streambuf/terminate_spec.rb new file mode 100644 index 0000000000..0c2bdedf6d --- /dev/null +++ b/spec/rubyspec/library/csv/streambuf/terminate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StreamBuf#terminate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/stringreader/get_row_spec.rb b/spec/rubyspec/library/csv/stringreader/get_row_spec.rb new file mode 100644 index 0000000000..8532fc234c --- /dev/null +++ b/spec/rubyspec/library/csv/stringreader/get_row_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StringReader#get_row" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/stringreader/initialize_spec.rb b/spec/rubyspec/library/csv/stringreader/initialize_spec.rb new file mode 100644 index 0000000000..5ce8f652da --- /dev/null +++ b/spec/rubyspec/library/csv/stringreader/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::StringReader#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/writer/add_row_spec.rb b/spec/rubyspec/library/csv/writer/add_row_spec.rb new file mode 100644 index 0000000000..2ff5bdeee6 --- /dev/null +++ b/spec/rubyspec/library/csv/writer/add_row_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Writer#add_row" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/writer/append_spec.rb b/spec/rubyspec/library/csv/writer/append_spec.rb new file mode 100644 index 0000000000..c6d96c72d6 --- /dev/null +++ b/spec/rubyspec/library/csv/writer/append_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Writer#<<" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/writer/close_spec.rb b/spec/rubyspec/library/csv/writer/close_spec.rb new file mode 100644 index 0000000000..f51b9de73d --- /dev/null +++ b/spec/rubyspec/library/csv/writer/close_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Writer#close" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/writer/create_spec.rb b/spec/rubyspec/library/csv/writer/create_spec.rb new file mode 100644 index 0000000000..18cff887a6 --- /dev/null +++ b/spec/rubyspec/library/csv/writer/create_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Writer.create" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/writer/generate_spec.rb b/spec/rubyspec/library/csv/writer/generate_spec.rb new file mode 100644 index 0000000000..8930e26389 --- /dev/null +++ b/spec/rubyspec/library/csv/writer/generate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Writer.generate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/writer/initialize_spec.rb b/spec/rubyspec/library/csv/writer/initialize_spec.rb new file mode 100644 index 0000000000..31b7d0fad8 --- /dev/null +++ b/spec/rubyspec/library/csv/writer/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Writer#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/csv/writer/terminate_spec.rb b/spec/rubyspec/library/csv/writer/terminate_spec.rb new file mode 100644 index 0000000000..9629c672b5 --- /dev/null +++ b/spec/rubyspec/library/csv/writer/terminate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'csv' + +describe "CSV::Writer#terminate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/accessor_spec.rb b/spec/rubyspec/library/date/accessor_spec.rb new file mode 100644 index 0000000000..91e0c3fc88 --- /dev/null +++ b/spec/rubyspec/library/date/accessor_spec.rb @@ -0,0 +1,91 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#ajd" do + it "determines the Astronomical Julian day" do + Date.civil(2007, 1, 17).ajd.should == 4908235.to_r / 2 + end +end + +describe "Date#amjd" do + it "determines the Astronomical Modified Julian day" do + Date.civil(2007, 1, 17).amjd.should == 54117 + end +end + +describe "Date#day_fraction" do + it "determines the day fraction" do + Date.civil(2007, 1, 17).day_fraction.should == 0 + end +end + +describe "Date#mjd" do + it "determines the Modified Julian day" do + Date.civil(2007, 1, 17).mjd.should == 54117 + end +end + +describe "Date#ld" do + it "determines the Modified Julian day" do + Date.civil(2007, 1, 17).ld.should == 154958 + end +end + +describe "Date#year" do + it "determines the year" do + Date.civil(2007, 1, 17).year.should == 2007 + end +end + +describe "Date#yday" do + it "determines the year" do + Date.civil(2007, 1, 17).yday.should == 17 + Date.civil(2008, 10, 28).yday.should == 302 + end +end + +describe "Date#mon" do + it "determines the month" do + Date.civil(2007, 1, 17).mon.should == 1 + Date.civil(2008, 10, 28).mon.should == 10 + end +end + +describe "Date#mday" do + it "determines the day of the month" do + Date.civil(2007, 1, 17).mday.should == 17 + Date.civil(2008, 10, 28).mday.should == 28 + end +end + +describe "Date#wday" do + it "determines the week day" do + Date.civil(2007, 1, 17).wday.should == 3 + Date.civil(2008, 10, 26).wday.should == 0 + end +end + +describe "Date#cwyear" do + it "determines the commercial year" do + Date.civil(2007, 1, 17).cwyear.should == 2007 + Date.civil(2008, 10, 28).cwyear.should == 2008 + Date.civil(2007, 12, 31).cwyear.should == 2008 + Date.civil(2010, 1, 1).cwyear.should == 2009 + end +end + +describe "Date#cweek" do + it "determines the commercial week" do + Date.civil(2007, 1, 17).cweek.should == 3 + Date.civil(2008, 10, 28).cweek.should == 44 + Date.civil(2007, 12, 31).cweek.should == 1 + Date.civil(2010, 1, 1).cweek.should == 53 + end +end + +describe "Date#cwday" do + it "determines the commercial week day" do + Date.civil(2007, 1, 17).cwday.should == 3 + Date.civil(2008, 10, 26).cwday.should == 7 + end +end diff --git a/spec/rubyspec/library/date/add_month_spec.rb b/spec/rubyspec/library/date/add_month_spec.rb new file mode 100644 index 0000000000..46f1915b70 --- /dev/null +++ b/spec/rubyspec/library/date/add_month_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#>>" do + it "adds the number of months to a Date" do + d = Date.civil(2007,2,27) >> 10 + d.should == Date.civil(2007, 12, 27) + end + + it "sets the day to the last day of a month if the day doesn't exist" do + d = Date.civil(2008,3,31) >> 1 + d.should == Date.civil(2008, 4, 30) + end + + it "returns the day of the reform if date falls within calendar reform" do + calendar_reform_italy = Date.new(1582, 10, 4) + d1 = Date.new(1582, 9, 9) >> 1 + d2 = Date.new(1582, 9, 10) >> 1 + d1.should == calendar_reform_italy + d2.should == calendar_reform_italy + end + + it "raise a TypeError when passed a Symbol" do + lambda { Date.civil(2007,2,27) >> :hello }.should raise_error(TypeError) + end + + it "raise a TypeError when passed a String" do + lambda { Date.civil(2007,2,27) >> "hello" }.should raise_error(TypeError) + end + + it "raise a TypeError when passed a Date" do + lambda { Date.civil(2007,2,27) >> Date.new }.should raise_error(TypeError) + end + + it "raise a TypeError when passed an Object" do + lambda { Date.civil(2007,2,27) >> Object.new }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/date/add_spec.rb b/spec/rubyspec/library/date/add_spec.rb new file mode 100644 index 0000000000..022b793318 --- /dev/null +++ b/spec/rubyspec/library/date/add_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#+" do + it "adds the number of days to a Date" do + d = Date.civil(2007,2,27) + 10 + d.should == Date.civil(2007, 3, 9) + end + + it "adds a negative number of days to a Date" do + d = Date.civil(2007,2,27).+(-10) + d.should == Date.civil(2007, 2, 17) + end + + it "raises a TypeError when passed a Symbol" do + lambda { Date.civil(2007,2,27) + :hello }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { Date.civil(2007,2,27) + "hello" }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a Date" do + lambda { Date.civil(2007,2,27) + Date.new }.should raise_error(TypeError) + end + + it "raises a TypeError when passed an Object" do + lambda { Date.civil(2007,2,27) + Object.new }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/date/ajd_spec.rb b/spec/rubyspec/library/date/ajd_spec.rb new file mode 100644 index 0000000000..fcbcea2426 --- /dev/null +++ b/spec/rubyspec/library/date/ajd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#ajd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/ajd_to_amjd_spec.rb b/spec/rubyspec/library/date/ajd_to_amjd_spec.rb new file mode 100644 index 0000000000..fc6a35d0c6 --- /dev/null +++ b/spec/rubyspec/library/date/ajd_to_amjd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.ajd_to_amjd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/ajd_to_jd_spec.rb b/spec/rubyspec/library/date/ajd_to_jd_spec.rb new file mode 100644 index 0000000000..1d8d6d0eb5 --- /dev/null +++ b/spec/rubyspec/library/date/ajd_to_jd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.ajd_to_jd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/amjd_spec.rb b/spec/rubyspec/library/date/amjd_spec.rb new file mode 100644 index 0000000000..212871ee18 --- /dev/null +++ b/spec/rubyspec/library/date/amjd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#amjd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/amjd_to_ajd_spec.rb b/spec/rubyspec/library/date/amjd_to_ajd_spec.rb new file mode 100644 index 0000000000..f45f202a40 --- /dev/null +++ b/spec/rubyspec/library/date/amjd_to_ajd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.amjd_to_ajd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/append_spec.rb b/spec/rubyspec/library/date/append_spec.rb new file mode 100644 index 0000000000..d90eff9a7a --- /dev/null +++ b/spec/rubyspec/library/date/append_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#<<" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/asctime_spec.rb b/spec/rubyspec/library/date/asctime_spec.rb new file mode 100644 index 0000000000..13dede0f05 --- /dev/null +++ b/spec/rubyspec/library/date/asctime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#asctime" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/boat_spec.rb b/spec/rubyspec/library/date/boat_spec.rb new file mode 100644 index 0000000000..3004c6bfbc --- /dev/null +++ b/spec/rubyspec/library/date/boat_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#<=>" do + it "returns 0 when two dates are equal" do + (Date.civil(2000, 04, 06) <=> Date.civil(2000, 04, 06)).should == 0 + end + + it "returns -1 when self is less than another date" do + (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06)).should == -1 + end + + it "returns -1 when self is less than a Numeric" do + (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == -1 + end + + it "returns 1 when self is greater than another date" do + (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06)).should == 1 + end + + it "returns 1 when self is greater than a Numeric" do + (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == 1 + end +end diff --git a/spec/rubyspec/library/date/case_compare_spec.rb b/spec/rubyspec/library/date/case_compare_spec.rb new file mode 100644 index 0000000000..2144a616a3 --- /dev/null +++ b/spec/rubyspec/library/date/case_compare_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#===" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/civil_spec.rb b/spec/rubyspec/library/date/civil_spec.rb new file mode 100644 index 0000000000..36e790aecd --- /dev/null +++ b/spec/rubyspec/library/date/civil_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/civil', __FILE__) +require 'date' + +describe "Date#civil" do + it_behaves_like(:date_civil, :civil) +end + + +describe "Date.civil" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/commercial_spec.rb b/spec/rubyspec/library/date/commercial_spec.rb new file mode 100644 index 0000000000..bb6671eda1 --- /dev/null +++ b/spec/rubyspec/library/date/commercial_spec.rb @@ -0,0 +1,18 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/commercial', __FILE__) + +describe "Date#commercial" do + + it_behaves_like(:date_commercial, :commercial) + +end + +# reference: +# October 1582 (the Gregorian calendar, Civil Date) +# S M Tu W Th F S +# 1 2 3 4 15 16 +# 17 18 19 20 21 22 23 +# 24 25 26 27 28 29 30 +# 31 + diff --git a/spec/rubyspec/library/date/commercial_to_jd_spec.rb b/spec/rubyspec/library/date/commercial_to_jd_spec.rb new file mode 100644 index 0000000000..61631a7977 --- /dev/null +++ b/spec/rubyspec/library/date/commercial_to_jd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.commercial_to_jd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/comparison_spec.rb b/spec/rubyspec/library/date/comparison_spec.rb new file mode 100644 index 0000000000..04bfa2e8f7 --- /dev/null +++ b/spec/rubyspec/library/date/comparison_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#<=>" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/constants_spec.rb b/spec/rubyspec/library/date/constants_spec.rb new file mode 100644 index 0000000000..8e564fe665 --- /dev/null +++ b/spec/rubyspec/library/date/constants_spec.rb @@ -0,0 +1,44 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date constants" do + + it "defines JULIAN" do + (Date::JULIAN <=> Date::Infinity.new).should == 0 + end + + it "defines GREGORIAN" do + (Date::GREGORIAN <=> -Date::Infinity.new).should == 0 + end + + it "defines ITALY" do + Date::ITALY.should == 2299161 # 1582-10-15 + end + + it "defines ENGLAND" do + Date::ENGLAND.should == 2361222 # 1752-09-14 + end + + it "defines MONTHNAMES" do + Date::MONTHNAMES.should == [nil] + %w(January February March April May June July + August September October November December) + end + + it "defines DAYNAMES" do + Date::DAYNAMES.should == %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday) + end + + it "defines ABBR_MONTHNAMES" do + Date::ABBR_DAYNAMES.should == %w(Sun Mon Tue Wed Thu Fri Sat) + end + + it "freezes MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYSNAMES" do + [Date::MONTHNAMES, Date::DAYNAMES, Date::ABBR_MONTHNAMES, Date::ABBR_DAYNAMES].each do |ary| + lambda { ary << "Unknown" }.should raise_error + ary.compact.each do |name| + lambda { name << "modified" }.should raise_error + end + end + end + +end diff --git a/spec/rubyspec/library/date/conversions_spec.rb b/spec/rubyspec/library/date/conversions_spec.rb new file mode 100644 index 0000000000..c52ade7012 --- /dev/null +++ b/spec/rubyspec/library/date/conversions_spec.rb @@ -0,0 +1,43 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + + +describe "Date#new_start" do + it "converts a date object into another with a new calendar reform" do + Date.civil(1582, 10, 14, Date::ENGLAND).new_start.should == Date.civil(1582, 10, 24) + Date.civil(1582, 10, 4, Date::ENGLAND).new_start.should == Date.civil(1582, 10, 4) + Date.civil(1582, 10, 15).new_start(Date::ENGLAND).should == Date.civil(1582, 10, 5, Date::ENGLAND) + Date.civil(1752, 9, 14).new_start(Date::ENGLAND).should == Date.civil(1752, 9, 14, Date::ENGLAND) + Date.civil(1752, 9, 13).new_start(Date::ENGLAND).should == Date.civil(1752, 9, 2, Date::ENGLAND) + end +end + +describe "Date#italy" do + it "converts a date object into another with the Italian calendar reform" do + Date.civil(1582, 10, 14, Date::ENGLAND).italy.should == Date.civil(1582, 10, 24) + Date.civil(1582, 10, 4, Date::ENGLAND).italy.should == Date.civil(1582, 10, 4) + end +end + +describe "Date#england" do + it "converts a date object into another with the English calendar reform" do + Date.civil(1582, 10, 15).england.should == Date.civil(1582, 10, 5, Date::ENGLAND) + Date.civil(1752, 9, 14).england.should == Date.civil(1752, 9, 14, Date::ENGLAND) + Date.civil(1752, 9, 13).england.should == Date.civil(1752, 9, 2, Date::ENGLAND) + end +end + +describe "Date#julian" do + it "converts a date object into another with the Julian calendar" do + Date.civil(1582, 10, 15).julian.should == Date.civil(1582, 10, 5, Date::JULIAN) + Date.civil(1752, 9, 14).julian.should == Date.civil(1752, 9, 3, Date::JULIAN) + Date.civil(1752, 9, 13).julian.should == Date.civil(1752, 9, 2, Date::JULIAN) + end +end + +describe "Date#gregorian" do + it "converts a date object into another with the Gregorian calendar" do + Date.civil(1582, 10, 4).gregorian.should == Date.civil(1582, 10, 14, Date::GREGORIAN) + Date.civil(1752, 9, 14).gregorian.should == Date.civil(1752, 9, 14, Date::GREGORIAN) + end +end diff --git a/spec/rubyspec/library/date/ctime_spec.rb b/spec/rubyspec/library/date/ctime_spec.rb new file mode 100644 index 0000000000..0f5d594842 --- /dev/null +++ b/spec/rubyspec/library/date/ctime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#ctime" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/cwday_spec.rb b/spec/rubyspec/library/date/cwday_spec.rb new file mode 100644 index 0000000000..33ede37b2c --- /dev/null +++ b/spec/rubyspec/library/date/cwday_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#cwday" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/cweek_spec.rb b/spec/rubyspec/library/date/cweek_spec.rb new file mode 100644 index 0000000000..d988bdd9db --- /dev/null +++ b/spec/rubyspec/library/date/cweek_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#cweek" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/cwyear_spec.rb b/spec/rubyspec/library/date/cwyear_spec.rb new file mode 100644 index 0000000000..00544927bc --- /dev/null +++ b/spec/rubyspec/library/date/cwyear_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#cwyear" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/day_fraction_spec.rb b/spec/rubyspec/library/date/day_fraction_spec.rb new file mode 100644 index 0000000000..64896a421e --- /dev/null +++ b/spec/rubyspec/library/date/day_fraction_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#day_fraction" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/day_fraction_to_time_spec.rb b/spec/rubyspec/library/date/day_fraction_to_time_spec.rb new file mode 100644 index 0000000000..609367371a --- /dev/null +++ b/spec/rubyspec/library/date/day_fraction_to_time_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.day_fraction_to_time" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/day_spec.rb b/spec/rubyspec/library/date/day_spec.rb new file mode 100644 index 0000000000..7dfca4d77c --- /dev/null +++ b/spec/rubyspec/library/date/day_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#day" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/downto_spec.rb b/spec/rubyspec/library/date/downto_spec.rb new file mode 100644 index 0000000000..ab9bf11952 --- /dev/null +++ b/spec/rubyspec/library/date/downto_spec.rb @@ -0,0 +1,18 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#downto" do + + it "creates earlier dates when passed a negative step" do + ds = Date.civil(2000, 4, 14) + de = Date.civil(2000, 3, 29) + count = 0 + ds.step(de, -1) do |d| + d.should <= ds + d.should >= de + count += 1 + end + count.should == 17 + end + +end diff --git a/spec/rubyspec/library/date/england_spec.rb b/spec/rubyspec/library/date/england_spec.rb new file mode 100644 index 0000000000..6c67c6ee86 --- /dev/null +++ b/spec/rubyspec/library/date/england_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#england" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/eql_spec.rb b/spec/rubyspec/library/date/eql_spec.rb new file mode 100644 index 0000000000..efecde8944 --- /dev/null +++ b/spec/rubyspec/library/date/eql_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#eql?" do + it "returns true if self is equal to another date" do + Date.civil(2007, 10, 11).eql?(Date.civil(2007, 10, 11)).should be_true + end + + it "returns false if self is not equal to another date" do + Date.civil(2007, 10, 11).eql?(Date.civil(2007, 10, 12)).should be_false + end +end diff --git a/spec/rubyspec/library/date/format/bag/method_missing_spec.rb b/spec/rubyspec/library/date/format/bag/method_missing_spec.rb new file mode 100644 index 0000000000..529fde05d2 --- /dev/null +++ b/spec/rubyspec/library/date/format/bag/method_missing_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Format::Bag#method_missing" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/format/bag/to_hash_spec.rb b/spec/rubyspec/library/date/format/bag/to_hash_spec.rb new file mode 100644 index 0000000000..ba9525e5e8 --- /dev/null +++ b/spec/rubyspec/library/date/format/bag/to_hash_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Format::Bag#to_hash" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/gregorian_leap_spec.rb b/spec/rubyspec/library/date/gregorian_leap_spec.rb new file mode 100644 index 0000000000..043d57aa93 --- /dev/null +++ b/spec/rubyspec/library/date/gregorian_leap_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#gregorian_leap?" do + it "returns true if a year is a leap year in the Gregorian calendar" do + Date.gregorian_leap?(2000).should be_true + Date.gregorian_leap?(2004).should be_true + end + + it "returns false if a year is not a leap year in the Gregorian calendar" do + Date.gregorian_leap?(1900).should be_false + Date.gregorian_leap?(1999).should be_false + Date.gregorian_leap?(2002).should be_false + end +end + diff --git a/spec/rubyspec/library/date/gregorian_spec.rb b/spec/rubyspec/library/date/gregorian_spec.rb new file mode 100644 index 0000000000..5d8b38b2d8 --- /dev/null +++ b/spec/rubyspec/library/date/gregorian_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#gregorian?" do + + it "marks a day before the calendar reform as Julian" do + Date.civil(1007, 2, 27).gregorian?.should be_false + Date.civil(1907, 2, 27, Date.civil(1930, 1, 1).jd).gregorian?.should be_false + end + + it "marks a day after the calendar reform as Julian" do + Date.civil(2007, 2, 27).gregorian?.should == true + Date.civil(1607, 2, 27, Date.civil(1582, 1, 1).jd).gregorian?.should be_true + end + +end diff --git a/spec/rubyspec/library/date/hash_spec.rb b/spec/rubyspec/library/date/hash_spec.rb new file mode 100644 index 0000000000..271d565253 --- /dev/null +++ b/spec/rubyspec/library/date/hash_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#hash" do + it "returns the same value for equal dates" do + Date.civil(2004, 7, 12).hash.should == Date.civil(2004, 7, 12).hash + end +end diff --git a/spec/rubyspec/library/date/infinity/abs_spec.rb b/spec/rubyspec/library/date/infinity/abs_spec.rb new file mode 100644 index 0000000000..a1107679f6 --- /dev/null +++ b/spec/rubyspec/library/date/infinity/abs_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#abs" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/coerce_spec.rb b/spec/rubyspec/library/date/infinity/coerce_spec.rb new file mode 100644 index 0000000000..e09b948064 --- /dev/null +++ b/spec/rubyspec/library/date/infinity/coerce_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#coerce" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/comparison_spec.rb b/spec/rubyspec/library/date/infinity/comparison_spec.rb new file mode 100644 index 0000000000..7bf8fb7b9f --- /dev/null +++ b/spec/rubyspec/library/date/infinity/comparison_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#<=>" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/d_spec.rb b/spec/rubyspec/library/date/infinity/d_spec.rb new file mode 100644 index 0000000000..9ef1f71408 --- /dev/null +++ b/spec/rubyspec/library/date/infinity/d_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#d" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/finite_spec.rb b/spec/rubyspec/library/date/infinity/finite_spec.rb new file mode 100644 index 0000000000..92806935fc --- /dev/null +++ b/spec/rubyspec/library/date/infinity/finite_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#finite?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/infinite_spec.rb b/spec/rubyspec/library/date/infinity/infinite_spec.rb new file mode 100644 index 0000000000..8c7e0bc86a --- /dev/null +++ b/spec/rubyspec/library/date/infinity/infinite_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#infinite?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/nan_spec.rb b/spec/rubyspec/library/date/infinity/nan_spec.rb new file mode 100644 index 0000000000..19c7ae0af3 --- /dev/null +++ b/spec/rubyspec/library/date/infinity/nan_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#nan?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/uminus_spec.rb b/spec/rubyspec/library/date/infinity/uminus_spec.rb new file mode 100644 index 0000000000..110e197231 --- /dev/null +++ b/spec/rubyspec/library/date/infinity/uminus_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#-@" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/uplus_spec.rb b/spec/rubyspec/library/date/infinity/uplus_spec.rb new file mode 100644 index 0000000000..dfc60b6b61 --- /dev/null +++ b/spec/rubyspec/library/date/infinity/uplus_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#+@" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity/zero_spec.rb b/spec/rubyspec/library/date/infinity/zero_spec.rb new file mode 100644 index 0000000000..2f4347255b --- /dev/null +++ b/spec/rubyspec/library/date/infinity/zero_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'date' + +describe "Date::Infinity#zero?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/infinity_spec.rb b/spec/rubyspec/library/date/infinity_spec.rb new file mode 100644 index 0000000000..127fb8c2f4 --- /dev/null +++ b/spec/rubyspec/library/date/infinity_spec.rb @@ -0,0 +1,67 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date::Infinity" do + + it "should be able to check whether Infinity is zero" do + i = Date::Infinity.new + i.zero?.should == false + end + + it "should be able to check whether Infinity is finite" do + i1 = Date::Infinity.new + i1.finite?.should == false + i2 = Date::Infinity.new(-1) + i2.finite?.should == false + i3 = Date::Infinity.new(0) + i3.finite?.should == false + end + + it "should be able to check whether Infinity is infinite" do + i1 = Date::Infinity.new + i1.infinite?.should == 1 + i2 = Date::Infinity.new(-1) + i2.infinite?.should == -1 + i3 = Date::Infinity.new(0) + i3.infinite?.should == nil + end + + it "should be able to check whether Infinity is not a number" do + i1 = Date::Infinity.new + i1.nan?.should == false + i2 = Date::Infinity.new(-1) + i2.nan?.should == false + i3 = Date::Infinity.new(0) + i3.nan?.should == true + end + + it "should be able to compare Infinity objects" do + i1 = Date::Infinity.new + i2 = Date::Infinity.new(-1) + i3 = Date::Infinity.new(0) + i4 = Date::Infinity.new + (i4 <=> i1).should == 0 + (i3 <=> i1).should == -1 + (i2 <=> i1).should == -1 + (i3 <=> i2).should == 1 + end + + it "should be able to return plus Infinity for abs" do + i1 = Date::Infinity.new + i2 = Date::Infinity.new(-1) + i3 = Date::Infinity.new(0) + (i2.abs <=> i1).should == 0 + (i3.abs <=> i1).should == 0 + end + + it "should be able to use -@ and +@ for Date::Infinity" do + (Date::Infinity.new <=> +Date::Infinity.new).should == 0 + (Date::Infinity.new(-1) <=> -Date::Infinity.new).should == 0 + end + + it "should be able to coerce a Date::Infinity object" do + Date::Infinity.new.coerce(1).should == [-1, 1] + Date::Infinity.new(0).coerce(2).should == [0, 0] + Date::Infinity.new(-1).coerce(1.5).should == [1, -1] + end +end diff --git a/spec/rubyspec/library/date/inspect_spec.rb b/spec/rubyspec/library/date/inspect_spec.rb new file mode 100644 index 0000000000..150eb6bf24 --- /dev/null +++ b/spec/rubyspec/library/date/inspect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#inspect" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/italy_spec.rb b/spec/rubyspec/library/date/italy_spec.rb new file mode 100644 index 0000000000..2d251db1b0 --- /dev/null +++ b/spec/rubyspec/library/date/italy_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#italy" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/jd_spec.rb b/spec/rubyspec/library/date/jd_spec.rb new file mode 100644 index 0000000000..ccf6b93e06 --- /dev/null +++ b/spec/rubyspec/library/date/jd_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/jd', __FILE__) +require 'date' + +describe "Date#jd" do + + it "determines the Julian day for a Date object" do + Date.civil(2008, 1, 16).jd.should == 2454482 + end + +end + +describe "Date.jd" do + it_behaves_like :date_jd, :jd +end diff --git a/spec/rubyspec/library/date/jd_to_ajd_spec.rb b/spec/rubyspec/library/date/jd_to_ajd_spec.rb new file mode 100644 index 0000000000..38a12bd65d --- /dev/null +++ b/spec/rubyspec/library/date/jd_to_ajd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.jd_to_ajd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/jd_to_civil_spec.rb b/spec/rubyspec/library/date/jd_to_civil_spec.rb new file mode 100644 index 0000000000..8608de2698 --- /dev/null +++ b/spec/rubyspec/library/date/jd_to_civil_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.jd_to_civil" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/jd_to_commercial_spec.rb b/spec/rubyspec/library/date/jd_to_commercial_spec.rb new file mode 100644 index 0000000000..97d76130f2 --- /dev/null +++ b/spec/rubyspec/library/date/jd_to_commercial_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.jd_to_commercial" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/jd_to_ld_spec.rb b/spec/rubyspec/library/date/jd_to_ld_spec.rb new file mode 100644 index 0000000000..d27ada6b6c --- /dev/null +++ b/spec/rubyspec/library/date/jd_to_ld_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.jd_to_ld" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/jd_to_mjd_spec.rb b/spec/rubyspec/library/date/jd_to_mjd_spec.rb new file mode 100644 index 0000000000..064134c7ed --- /dev/null +++ b/spec/rubyspec/library/date/jd_to_mjd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.jd_to_mjd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/jd_to_ordinal_spec.rb b/spec/rubyspec/library/date/jd_to_ordinal_spec.rb new file mode 100644 index 0000000000..a5f5c79641 --- /dev/null +++ b/spec/rubyspec/library/date/jd_to_ordinal_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.jd_to_ordinal" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/jd_to_wday_spec.rb b/spec/rubyspec/library/date/jd_to_wday_spec.rb new file mode 100644 index 0000000000..569a53e409 --- /dev/null +++ b/spec/rubyspec/library/date/jd_to_wday_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.jd_to_wday" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/julian_leap_spec.rb b/spec/rubyspec/library/date/julian_leap_spec.rb new file mode 100644 index 0000000000..3915f97693 --- /dev/null +++ b/spec/rubyspec/library/date/julian_leap_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.julian_leap?" do + it "determines whether a year is a leap year in the Julian calendar" do + Date.julian_leap?(1900).should be_true + Date.julian_leap?(2000).should be_true + Date.julian_leap?(2004).should be_true + end + + it "determines whether a year is not a leap year in the Julian calendar" do + Date.julian_leap?(1999).should be_false + Date.julian_leap?(2002).should be_false + end +end diff --git a/spec/rubyspec/library/date/julian_spec.rb b/spec/rubyspec/library/date/julian_spec.rb new file mode 100644 index 0000000000..8cbe27b881 --- /dev/null +++ b/spec/rubyspec/library/date/julian_spec.rb @@ -0,0 +1,16 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#julian?" do + + it "marks a day before the calendar reform as Julian" do + Date.civil(1007, 2, 27).julian?.should == true + Date.civil(1907, 2, 27, Date.civil(1930, 1, 1).jd).julian?.should be_true + end + + it "marks a day after the calendar reform as Julian" do + Date.civil(2007, 2, 27).julian?.should == false + Date.civil(1607, 2, 27, Date.civil(1582, 1, 1).jd).julian?.should be_false + end + +end diff --git a/spec/rubyspec/library/date/ld_spec.rb b/spec/rubyspec/library/date/ld_spec.rb new file mode 100644 index 0000000000..a59b519c04 --- /dev/null +++ b/spec/rubyspec/library/date/ld_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#ld" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/ld_to_jd_spec.rb b/spec/rubyspec/library/date/ld_to_jd_spec.rb new file mode 100644 index 0000000000..7adbbede37 --- /dev/null +++ b/spec/rubyspec/library/date/ld_to_jd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.ld_to_jd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/leap_spec.rb b/spec/rubyspec/library/date/leap_spec.rb new file mode 100644 index 0000000000..bb8e920a3e --- /dev/null +++ b/spec/rubyspec/library/date/leap_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#leap?" do + it "needs to be reviewed for spec completeness" +end + +describe "Date.leap?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/mday_spec.rb b/spec/rubyspec/library/date/mday_spec.rb new file mode 100644 index 0000000000..8a1d6e8d59 --- /dev/null +++ b/spec/rubyspec/library/date/mday_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#mday" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/minus_month_spec.rb b/spec/rubyspec/library/date/minus_month_spec.rb new file mode 100644 index 0000000000..b6b20c5578 --- /dev/null +++ b/spec/rubyspec/library/date/minus_month_spec.rb @@ -0,0 +1,34 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#<<" do + + it "substracts a number of months from a date" do + d = Date.civil(2007,2,27) << 10 + d.should == Date.civil(2006, 4, 27) + end + + it "returns the last day of a month if the day doesn't exist" do + d = Date.civil(2008,3,31) << 1 + d.should == Date.civil(2008, 2, 29) + end + + ruby_version_is ""..."2.3" do + it "raises an error on non numeric parameters" do + lambda { Date.civil(2007,2,27) << :hello }.should raise_error + lambda { Date.civil(2007,2,27) << "hello" }.should raise_error + lambda { Date.civil(2007,2,27) << Date.new }.should raise_error + lambda { Date.civil(2007,2,27) << Object.new }.should raise_error + end + end + + ruby_version_is "2.3" do + it "raises an error on non numeric parameters" do + lambda { Date.civil(2007,2,27) << :hello }.should raise_error(TypeError) + lambda { Date.civil(2007,2,27) << "hello" }.should raise_error(TypeError) + lambda { Date.civil(2007,2,27) << Date.new }.should raise_error(TypeError) + lambda { Date.civil(2007,2,27) << Object.new }.should raise_error(TypeError) + end + end + +end diff --git a/spec/rubyspec/library/date/minus_spec.rb b/spec/rubyspec/library/date/minus_spec.rb new file mode 100644 index 0000000000..09da595872 --- /dev/null +++ b/spec/rubyspec/library/date/minus_spec.rb @@ -0,0 +1,30 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#-" do + + it "substracts a number of days from a Date" do + d = Date.civil(2007, 5 ,2) - 13 + d.should == Date.civil(2007, 4, 19) + end + + it "substracts a negative number of days from a Date" do + d = Date.civil(2007, 4, 19).-(-13) + d.should == Date.civil(2007, 5 ,2) + end + + it "computes the difference between two dates" do + (Date.civil(2007,2,27) - Date.civil(2007,2,27)).should == 0 + (Date.civil(2007,2,27) - Date.civil(2007,2,26)).should == 1 + (Date.civil(2006,2,27) - Date.civil(2007,2,27)).should == -365 + (Date.civil(2008,2,27) - Date.civil(2007,2,27)).should == 365 + + end + + it "raises an error for non Numeric arguments" do + lambda { Date.civil(2007,2,27) - :hello }.should raise_error(TypeError) + lambda { Date.civil(2007,2,27) - "hello" }.should raise_error(TypeError) + lambda { Date.civil(2007,2,27) - Object.new }.should raise_error(TypeError) + end + +end diff --git a/spec/rubyspec/library/date/mjd_spec.rb b/spec/rubyspec/library/date/mjd_spec.rb new file mode 100644 index 0000000000..7de39f0047 --- /dev/null +++ b/spec/rubyspec/library/date/mjd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#mjd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/mjd_to_jd_spec.rb b/spec/rubyspec/library/date/mjd_to_jd_spec.rb new file mode 100644 index 0000000000..fdda1330e5 --- /dev/null +++ b/spec/rubyspec/library/date/mjd_to_jd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.mjd_to_jd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/mon_spec.rb b/spec/rubyspec/library/date/mon_spec.rb new file mode 100644 index 0000000000..c3508b53bf --- /dev/null +++ b/spec/rubyspec/library/date/mon_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#mon" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/month_spec.rb b/spec/rubyspec/library/date/month_spec.rb new file mode 100644 index 0000000000..7a98f572b6 --- /dev/null +++ b/spec/rubyspec/library/date/month_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#month" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/new_spec.rb b/spec/rubyspec/library/date/new_spec.rb new file mode 100644 index 0000000000..f468036a01 --- /dev/null +++ b/spec/rubyspec/library/date/new_spec.rb @@ -0,0 +1,8 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/civil', __FILE__) +require File.expand_path('../shared/new_bang', __FILE__) + +describe "Date.new" do + it_behaves_like(:date_civil, :new) +end diff --git a/spec/rubyspec/library/date/new_start_spec.rb b/spec/rubyspec/library/date/new_start_spec.rb new file mode 100644 index 0000000000..94ec6bee46 --- /dev/null +++ b/spec/rubyspec/library/date/new_start_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#new_start" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/next_spec.rb b/spec/rubyspec/library/date/next_spec.rb new file mode 100644 index 0000000000..d88d31e974 --- /dev/null +++ b/spec/rubyspec/library/date/next_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#next" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/next_year_spec.rb b/spec/rubyspec/library/date/next_year_spec.rb new file mode 100644 index 0000000000..70f2f7ab77 --- /dev/null +++ b/spec/rubyspec/library/date/next_year_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#next_year" do + it "returns the day of the reform if date falls within calendar reform" do + calendar_reform_italy = Date.new(1582, 10, 4) + d1 = Date.new(1581, 10, 9).next_year + d2 = Date.new(1581, 10, 10).next_year + d1.should == calendar_reform_italy + d2.should == calendar_reform_italy + end +end diff --git a/spec/rubyspec/library/date/ordinal_spec.rb b/spec/rubyspec/library/date/ordinal_spec.rb new file mode 100644 index 0000000000..a373692a7b --- /dev/null +++ b/spec/rubyspec/library/date/ordinal_spec.rb @@ -0,0 +1,8 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/ordinal', __FILE__) + +describe "Date.ordinal" do + it_behaves_like :date_ordinal, :ordinal +end + diff --git a/spec/rubyspec/library/date/ordinal_to_jd_spec.rb b/spec/rubyspec/library/date/ordinal_to_jd_spec.rb new file mode 100644 index 0000000000..0a76c69c00 --- /dev/null +++ b/spec/rubyspec/library/date/ordinal_to_jd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.ordinal_to_jd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/parse_spec.rb b/spec/rubyspec/library/date/parse_spec.rb new file mode 100644 index 0000000000..092c658809 --- /dev/null +++ b/spec/rubyspec/library/date/parse_spec.rb @@ -0,0 +1,137 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/parse', __FILE__) +require File.expand_path('../shared/parse_us', __FILE__) +require File.expand_path('../shared/parse_eu', __FILE__) +require 'date' + +describe "Date#parse" do + # The space separator is also different, doesn't work for only numbers + it "parses a day name into a Date object" do + d = Date.parse("friday") + d.should == Date.commercial(d.cwyear, d.cweek, 5) + end + + it "parses a month name into a Date object" do + d = Date.parse("october") + d.should == Date.civil(Date.today.year, 10) + end + + it "parses a month day into a Date object" do + d = Date.parse("5th") + d.should == Date.civil(Date.today.year, Date.today.month, 5) + end + + # Specs using numbers + it "throws an argument error for a single digit" do + lambda{ Date.parse("1") }.should raise_error(ArgumentError) + end + + it "parses DD as month day number" do + d = Date.parse("10") + d.should == Date.civil(Date.today.year, Date.today.month, 10) + end + + it "parses DDD as year day number" do + d = Date.parse("100") + if Date.gregorian_leap?(Date.today.year) + d.should == Date.civil(Date.today.year, 4, 9) + else + d.should == Date.civil(Date.today.year, 4, 10) + end + end + + it "parses MMDD as month and day" do + d = Date.parse("1108") + d.should == Date.civil(Date.today.year, 11, 8) + end + + it "parses YYDDD as year and day number in 1969--2068" do + d = Date.parse("10100") + d.should == Date.civil(2010, 4, 10) + end + + it "parses YYMMDD as year, month and day in 1969--2068" do + d = Date.parse("201023") + d.should == Date.civil(2020, 10, 23) + end + + it "parses YYYYDDD as year and day number" do + d = Date.parse("1910100") + d.should == Date.civil(1910, 4, 10) + end + + it "parses YYYYMMDD as year, month and day number" do + d = Date.parse("19101101") + d.should == Date.civil(1910, 11, 1) + end +end + +describe "Date#parse with '.' separator" do + before :all do + @sep = '.' + end + + it_should_behave_like "date_parse" +end + +describe "Date#parse with '/' separator" do + before :all do + @sep = '/' + end + + it_should_behave_like "date_parse" +end + +describe "Date#parse with ' ' separator" do + before :all do + @sep = ' ' + end + + it_should_behave_like "date_parse" +end + +describe "Date#parse with '/' separator US-style" do + before :all do + @sep = '/' + end + + it_should_behave_like "date_parse_us" +end + +describe "Date#parse with '-' separator EU-style" do + before :all do + @sep = '-' + end + + it_should_behave_like "date_parse_eu" +end + +describe "Date#parse(.)" do + it "parses YYYY.MM.DD into a Date object" do + d = Date.parse("2007.10.01") + d.year.should == 2007 + d.month.should == 10 + d.day.should == 1 + end + + it "parses DD.MM.YYYY into a Date object" do + d = Date.parse("10.01.2007") + d.year.should == 2007 + d.month.should == 1 + d.day.should == 10 + end + + it "parses YY.MM.DD into a Date object using the year 20YY" do + d = Date.parse("10.01.07") + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end + + it "parses YY.MM.DD using the year digits as 20YY when given true as additional argument" do + d = Date.parse("10.01.07", true) + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end +end diff --git a/spec/rubyspec/library/date/plus_spec.rb b/spec/rubyspec/library/date/plus_spec.rb new file mode 100644 index 0000000000..e33fb199eb --- /dev/null +++ b/spec/rubyspec/library/date/plus_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#+" do + before :all do + @date = Date.civil(2000, 1, 1) + end + + it "returns a new Date object that is n days later than the current one" do + (@date + 31).should == Date.civil(2000, 2, 1) + end + + it "accepts a negative argument and returns a new Date that is earlier than the current one" do + (@date + -1).should == Date.civil(1999, 12, 31) + end + + it "raises TypeError if argument is not Numeric" do + lambda { Date.today + Date.today }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/date/prev_year_spec.rb b/spec/rubyspec/library/date/prev_year_spec.rb new file mode 100644 index 0000000000..4f27d1d1f9 --- /dev/null +++ b/spec/rubyspec/library/date/prev_year_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#prev_year" do + it "returns the day of the reform if date falls within calendar reform" do + calendar_reform_italy = Date.new(1582, 10, 4) + d1 = Date.new(1583, 10, 9).prev_year + d2 = Date.new(1583, 10, 10).prev_year + d1.should == calendar_reform_italy + d2.should == calendar_reform_italy + end +end diff --git a/spec/rubyspec/library/date/relationship_spec.rb b/spec/rubyspec/library/date/relationship_spec.rb new file mode 100644 index 0000000000..7c09457228 --- /dev/null +++ b/spec/rubyspec/library/date/relationship_spec.rb @@ -0,0 +1,20 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#===" do + + it "returns 0 when comparing two equal dates" do + (Date.civil(2000, 04, 06) <=> Date.civil(2000, 04, 06)).should == 0 + end + + it "computes the difference between two dates" do + (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06)).should == -1 + (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06)).should == 1 + end + + it "compares to another numeric" do + (Date.civil(2000, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == -1 + (Date.civil(2001, 04, 05) <=> Date.civil(2000, 04, 06).jd).should == 1 + end + +end diff --git a/spec/rubyspec/library/date/right_shift_spec.rb b/spec/rubyspec/library/date/right_shift_spec.rb new file mode 100644 index 0000000000..3d55e5abed --- /dev/null +++ b/spec/rubyspec/library/date/right_shift_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#>>" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/shared/civil.rb b/spec/rubyspec/library/date/shared/civil.rb new file mode 100644 index 0000000000..47dbed49fc --- /dev/null +++ b/spec/rubyspec/library/date/shared/civil.rb @@ -0,0 +1,57 @@ +describe :date_civil, shared: true do + it "creates a Date for -4712 by default" do + # the #chomp calls are necessary because of RSpec + d = Date.send(@method) + d.year.should == -4712 + d.month.should == 1 + d.day.should == 1 + d.julian?.should == true + d.jd.should == 0 + end + + it "creates a date with arguments" do + d = Date.send(@method, 2000, 3, 5) + d.year.should == 2000 + d.month.should == 3 + d.day.should == 5 + d.julian?.should == false + d.jd.should == 2451609 + + # Should also work with years far in the past and future + + d = Date.send(@method, -9000, 7, 5) + d.year.should == -9000 + d.month.should == 7 + d.day.should == 5 + d.julian?.should == true + d.jd.should == -1566006 + + d = Date.send(@method, 9000, 10, 14) + d.year.should == 9000 + d.month.should == 10 + d.day.should == 14 + d.julian?.should == false + d.jd.should == 5008529 + + end + + it "doesn't create dates for invalid arguments" do + lambda { Date.send(@method, 2000, 13, 31) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2000, 12, 32) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2000, 2, 30) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 1900, 2, 29) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2000, 2, 29) }.should_not raise_error(ArgumentError) + + lambda { Date.send(@method, 1582, 10, 14) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 1582, 10, 15) }.should_not raise_error(ArgumentError) + + end + + it "creates a Date for different calendar reform dates" do + d1 = Date.send(@method, 1582, 10, 4) + d1.succ.day.should == 15 + + d2 = Date.send(@method, 1582, 10, 4, Date::ENGLAND) + d2.succ.day.should == 5 + end +end diff --git a/spec/rubyspec/library/date/shared/commercial.rb b/spec/rubyspec/library/date/shared/commercial.rb new file mode 100644 index 0000000000..354a5d5cd0 --- /dev/null +++ b/spec/rubyspec/library/date/shared/commercial.rb @@ -0,0 +1,39 @@ +describe :date_commercial, shared: true do + it "creates a Date for Julian Day Number day 0 by default" do + d = Date.send(@method) + d.year.should == -4712 + d.month.should == 1 + d.day.should == 1 + end + + it "creates a Date for the monday in the year and week given" do + d = Date.send(@method, 2000, 1) + d.year.should == 2000 + d.month.should == 1 + d.day.should == 3 + d.cwday.should == 1 + end + + it "creates a Date for the correct day given the year, week and day number" do + d = Date.send(@method, 2004, 1, 1) + d.year.should == 2003 + d.month.should == 12 + d.day.should == 29 + d.cwday.should == 1 + d.cweek.should == 1 + d.cwyear.should == 2004 + end + + it "creates only Date objects for valid weeks" do + lambda { Date.send(@method, 2004, 53, 1) }.should_not raise_error(ArgumentError) + lambda { Date.send(@method, 2004, 53, 0) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2004, 53, 8) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2004, 54, 1) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2004, 0, 1) }.should raise_error(ArgumentError) + + lambda { Date.send(@method, 2003, 52, 1) }.should_not raise_error(ArgumentError) + lambda { Date.send(@method, 2003, 53, 1) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2003, 52, 0) }.should raise_error(ArgumentError) + lambda { Date.send(@method, 2003, 52, 8) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/date/shared/jd.rb b/spec/rubyspec/library/date/shared/jd.rb new file mode 100644 index 0000000000..e47dbae1b8 --- /dev/null +++ b/spec/rubyspec/library/date/shared/jd.rb @@ -0,0 +1,14 @@ +describe :date_jd, shared: true do + it "constructs a Date object if passed a Julian day" do + Date.send(@method, 2454482).should == Date.civil(2008, 1, 16) + end + + it "returns a Date object representing Julian day 0 (-4712-01-01) if no arguments passed"do + Date.send(@method).should == Date.civil(-4712, 1, 1) + end + + it "constructs a Date object if passed a negative number" do + Date.send(@method, -1).should == Date.civil(-4713, 12, 31) + end + +end diff --git a/spec/rubyspec/library/date/shared/new_bang.rb b/spec/rubyspec/library/date/shared/new_bang.rb new file mode 100644 index 0000000000..90f1b432f0 --- /dev/null +++ b/spec/rubyspec/library/date/shared/new_bang.rb @@ -0,0 +1,14 @@ +describe :date_new_bang, shared: true do + + it "returns a new Date object set to Astronomical Julian Day 0 if no arguments passed" do + d = Date.send(@method) + d.ajd.should == 0 + end + + it "accepts astronomical julian day number, offset as a fraction of a day and returns a new Date object" do + d = Date.send(@method, 10, 0.5) + d.ajd.should == 10 + d.jd.should == 11 + end + +end diff --git a/spec/rubyspec/library/date/shared/ordinal.rb b/spec/rubyspec/library/date/shared/ordinal.rb new file mode 100644 index 0000000000..4b182d5a25 --- /dev/null +++ b/spec/rubyspec/library/date/shared/ordinal.rb @@ -0,0 +1,22 @@ +# reference: +# October 1582 (the Gregorian calendar, Civil Date) +# S M Tu W Th F S +# 1 2 3 4 15 16 +# 17 18 19 20 21 22 23 +# 24 25 26 27 28 29 30 +# 31 + +describe :date_ordinal, shared: true do + it "constructs a Date object from an ordinal date" do + # October 1582 (the Gregorian calendar, Ordinal Date) + # S M Tu W Th F S + # 274 275 276 277 278 279 + # 280 281 282 283 284 285 286 + # 287 288 289 290 291 292 293 + # 294 + Date.send(@method, 1582, 274).should == Date.civil(1582, 10, 1) + Date.send(@method, 1582, 277).should == Date.civil(1582, 10, 4) + Date.send(@method, 1582, 278).should == Date.civil(1582, 10, 15) + Date.send(@method, 1582, 287, Date::ENGLAND).should == Date.civil(1582, 10, 14, Date::ENGLAND) + end +end diff --git a/spec/rubyspec/library/date/shared/parse.rb b/spec/rubyspec/library/date/shared/parse.rb new file mode 100644 index 0000000000..1015285e04 --- /dev/null +++ b/spec/rubyspec/library/date/shared/parse.rb @@ -0,0 +1,54 @@ +describe :date_parse, shared: true do + it "can parse a mmm-YYYY string into a Date object" do + d = Date.parse("feb#{@sep}2008") + d.year.should == 2008 + d.month.should == 2 + d.day.should == 1 + end + + it "can parse a 'DD mmm YYYY' string into a Date object" do + d = Date.parse("23#{@sep}feb#{@sep}2008") + d.year.should == 2008 + d.month.should == 2 + d.day.should == 23 + end + + it "can parse a 'mmm DD YYYY' string into a Date object" do + d = Date.parse("23#{@sep}feb#{@sep}2008") + d.year.should == 2008 + d.month.should == 2 + d.day.should == 23 + end + + it "can parse a 'YYYY mmm DD' string into a Date object" do + d = Date.parse("2008#{@sep}feb#{@sep}23") + d.year.should == 2008 + d.month.should == 2 + d.day.should == 23 + end + + it "can parse a month name and day into a Date object" do + d = Date.parse("november#{@sep}5th") + d.should == Date.civil(Date.today.year, 11, 5) + end + + it "can parse a month name, day and year into a Date object" do + d = Date.parse("november#{@sep}5th#{@sep}2005") + d.should == Date.civil(2005, 11, 5) + end + + it "can parse a year, month name and day into a Date object" do + d = Date.parse("2005#{@sep}november#{@sep}5th") + d.should == Date.civil(2005, 11, 5) + end + + it "can parse a year, day and month name into a Date object" do + d = Date.parse("5th#{@sep}november#{@sep}2005") + d.should == Date.civil(2005, 11, 5) + end + + it "can handle negative year numbers" do + d = Date.parse("5th#{@sep}november#{@sep}-2005") + d.should == Date.civil(-2005, 11, 5) + end +end diff --git a/spec/rubyspec/library/date/shared/parse_eu.rb b/spec/rubyspec/library/date/shared/parse_eu.rb new file mode 100644 index 0000000000..ecb15e3c0e --- /dev/null +++ b/spec/rubyspec/library/date/shared/parse_eu.rb @@ -0,0 +1,37 @@ +describe :date_parse_eu, shared: true do + # The - separator let's it work like European format, so it as a different spec + it "can parse a YYYY-MM-DD string into a Date object" do + d = Date.parse("2007#{@sep}10#{@sep}01") + d.year.should == 2007 + d.month.should == 10 + d.day.should == 1 + end + + it "can parse a MM-DD-YYYY string into a Date object" do + d = Date.parse("10#{@sep}01#{@sep}2007") + d.year.should == 2007 + d.month.should == 1 + d.day.should == 10 + end + + it "can parse a MM-DD-YY string into a Date object" do + d = Date.parse("10#{@sep}01#{@sep}07") + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end + + it "can parse a MM-DD-YY string into a Date object NOT using the year digits as 20XX" do + d = Date.parse("10#{@sep}01#{@sep}07", false) + d.year.should == 10 + d.month.should == 1 + d.day.should == 7 + end + + it "can parse a MM-DD-YY string into a Date object using the year digits as 20XX" do + d = Date.parse("10#{@sep}01#{@sep}07", true) + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end +end diff --git a/spec/rubyspec/library/date/shared/parse_us.rb b/spec/rubyspec/library/date/shared/parse_us.rb new file mode 100644 index 0000000000..7be62b1af1 --- /dev/null +++ b/spec/rubyspec/library/date/shared/parse_us.rb @@ -0,0 +1,36 @@ +describe :date_parse_us, shared: true do + it "parses a YYYY#{@sep}MM#{@sep}DD string into a Date object" do + d = Date.parse("2007#{@sep}10#{@sep}01") + d.year.should == 2007 + d.month.should == 10 + d.day.should == 1 + end + + it "parses a MM#{@sep}DD#{@sep}YYYY string into a Date object" do + d = Date.parse("10#{@sep}01#{@sep}2007") + d.year.should == 2007 + d.month.should == 1 + d.day.should == 10 + end + + it "parses a MM#{@sep}DD#{@sep}YY string into a Date object" do + d = Date.parse("10#{@sep}01#{@sep}07") + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end + + it "parses a MM#{@sep}DD#{@sep}YY string into a Date object NOT using the year digits as 20XX" do + d = Date.parse("10#{@sep}01#{@sep}07", false) + d.year.should == 10 + d.month.should == 1 + d.day.should == 7 + end + + it "parses a MM#{@sep}DD#{@sep}YY string into a Date object using the year digits as 20XX" do + d = Date.parse("10#{@sep}01#{@sep}07", true) + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end +end diff --git a/spec/rubyspec/library/date/shared/valid_civil.rb b/spec/rubyspec/library/date/shared/valid_civil.rb new file mode 100644 index 0000000000..545c207bbe --- /dev/null +++ b/spec/rubyspec/library/date/shared/valid_civil.rb @@ -0,0 +1,36 @@ +describe :date_valid_civil?, shared: true do + + # reference: + # October 1582 (the Gregorian calendar, Civil Date) + # S M Tu W Th F S + # 1 2 3 4 15 16 + # 17 18 19 20 21 22 23 + # 24 25 26 27 28 29 30 + # 31 + + it "returns true if it is a valid civil date" do + Date.send(@method, 1582, 10, 15).should be_true + Date.send(@method, 1582, 10, 14, Date::ENGLAND).should be_true + end + + it "returns false if it is not a valid civil date" do + Date.send(@method, 1582, 10, 14).should == false + end + + it "handles negative months and days" do + # October 1582 (the Gregorian calendar, Civil Date) + # S M Tu W Th F S + # -21 -20 -19 -18 -17 -16 + # -15 -14 -13 -12 -11 -10 -9 + # -8 -7 -6 -5 -4 -3 -2 + # -1 + Date.send(@method, 1582, -3, -22).should be_false + Date.send(@method, 1582, -3, -21).should be_true + Date.send(@method, 1582, -3, -18).should be_true + Date.send(@method, 1582, -3, -17).should be_true + + Date.send(@method, 2007, -11, -10).should be_true + Date.send(@method, 2008, -11, -10).should be_true + end + +end diff --git a/spec/rubyspec/library/date/shared/valid_commercial.rb b/spec/rubyspec/library/date/shared/valid_commercial.rb new file mode 100644 index 0000000000..117dfe1d3d --- /dev/null +++ b/spec/rubyspec/library/date/shared/valid_commercial.rb @@ -0,0 +1,34 @@ +describe :date_valid_commercial?, shared: true do + + it "returns true if it is a valid commercial date" do + # October 1582 (the Gregorian calendar, Commercial Date) + # M Tu W Th F Sa Su + # 39: 1 2 3 4 5 6 7 + # 40: 1 2 3 4 5 6 7 + # 41: 1 2 3 4 5 6 7 + Date.send(@method, 1582, 39, 4).should be_true + Date.send(@method, 1582, 39, 5).should be_true + Date.send(@method, 1582, 41, 4).should be_true + Date.send(@method, 1582, 41, 5).should be_true + Date.send(@method, 1582, 41, 4, Date::ENGLAND).should be_true + Date.send(@method, 1752, 37, 4, Date::ENGLAND).should be_true + end + + it "returns false it is not a valid commercial date" do + Date.send(@method, 1999, 53, 1).should be_false + end + + it "handles negative week and day numbers" do + # October 1582 (the Gregorian calendar, Commercial Date) + # M Tu W Th F Sa Su + # -12: -7 -6 -5 -4 -3 -2 -1 + # -11: -7 -6 -5 -4 -3 -2 -1 + # -10: -7 -6 -5 -4 -3 -2 -1 + Date.send(@method, 1582, -12, -4).should be_true + Date.send(@method, 1582, -12, -3).should be_true + Date.send(@method, 2007, -44, -2).should be_true + Date.send(@method, 2008, -44, -2).should be_true + Date.send(@method, 1999, -53, -1).should be_false + end + +end diff --git a/spec/rubyspec/library/date/shared/valid_jd.rb b/spec/rubyspec/library/date/shared/valid_jd.rb new file mode 100644 index 0000000000..d8a35992b3 --- /dev/null +++ b/spec/rubyspec/library/date/shared/valid_jd.rb @@ -0,0 +1,15 @@ +describe :date_valid_jd?, shared: true do + it "returns true if passed any value other than nil" do + Date.send(@method, -100).should be_true + Date.send(@method, :number).should be_true + Date.send(@method, Rational(1,2)).should be_true + end + + it "returns false if passed nil" do + Date.send(@method, nil).should be_false + end + + it "returns true if passed false" do + Date.send(@method, false).should be_true + end +end diff --git a/spec/rubyspec/library/date/shared/valid_ordinal.rb b/spec/rubyspec/library/date/shared/valid_ordinal.rb new file mode 100644 index 0000000000..1ed961be23 --- /dev/null +++ b/spec/rubyspec/library/date/shared/valid_ordinal.rb @@ -0,0 +1,26 @@ +describe :date_valid_ordinal?, shared: true do + it "determines if the date is a valid ordinal date" do + # October 1582 (the Gregorian calendar, Ordinal Date) + # S M Tu W Th F S + # 274 275 276 277 278 279 + # 280 281 282 283 284 285 286 + # 287 288 289 290 291 292 293 + # 294 + Date.send(@method, 1582, 277).should == true + Date.send(@method, 1582, 278).should == true + Date.send(@method, 1582, 287).should == true + Date.send(@method, 1582, 288).should == true + end + + it "handles negative day numbers" do + # October 1582 (the Gregorian calendar, Ordinal Date) + # S M Tu W Th F S + # -82 -81 -80 -79 -78 -77 + # -76 -75 -74 -73 -72 -71 -70 + # -69 -68 -67 -66 -65 -64 -63 + # -62 + Date.send(@method, 1582, -79).should == true + Date.send(@method, 1582, -78).should == true + Date.send(@method, 2007, -100).should == true + end +end diff --git a/spec/rubyspec/library/date/start_spec.rb b/spec/rubyspec/library/date/start_spec.rb new file mode 100644 index 0000000000..285037b094 --- /dev/null +++ b/spec/rubyspec/library/date/start_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#start" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/step_spec.rb b/spec/rubyspec/library/date/step_spec.rb new file mode 100644 index 0000000000..249633e807 --- /dev/null +++ b/spec/rubyspec/library/date/step_spec.rb @@ -0,0 +1,56 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#step" do + + it "steps forward in time" do + ds = Date.civil(2008, 10, 11) + de = Date.civil(2008, 9, 29) + count = 0 + de.step(ds) do |d| + d.should <= ds + d.should >= de + count += 1 + end + count.should == 13 + + count = 0 + de.step(ds, 5) do |d| + d.should <= ds + d.should >= de + count += 1 + end + count.should == 3 + + count = 0 + ds.step(de) do |d|; count += 1; end + count.should == 0 + + end + + it "steps backward in time" do + ds = Date.civil(2000, 4, 14) + de = Date.civil(2000, 3, 29) + count = 0 + ds.step(de, -1) do |d| + d.should <= ds + d.should >= de + count += 1 + end + count.should == 17 + + count = 0 + ds.step(de, -5) do |d| + d.should <= ds + d.should >= de + count += 1 + end + count.should == 4 + + count = 0 + de.step(ds, -1) do |d|; count += 1; end + count.should == 0 + + end + +end diff --git a/spec/rubyspec/library/date/strftime_spec.rb b/spec/rubyspec/library/date/strftime_spec.rb new file mode 100644 index 0000000000..81bb162ff7 --- /dev/null +++ b/spec/rubyspec/library/date/strftime_spec.rb @@ -0,0 +1,40 @@ +require 'date' +require File.expand_path('../../../shared/time/strftime_for_date', __FILE__) + +describe "Date#strftime" do + before :all do + @new_date = lambda { |y,m,d| Date.civil(y,m,d) } + + @date = Date.civil(2000, 4, 9) + end + + it_behaves_like :strftime_date, :strftime + + # Differences with Time + it "should be able to print the date with no argument" do + @date.strftime.should == "2000-04-09" + @date.strftime.should == @date.to_s + end + + # %Z is %:z for Date/DateTime + it "should be able to show the timezone with a : separator" do + @date.strftime("%Z").should == "+00:00" + end + + # %v is %e-%b-%Y for Date/DateTime + it "should be able to show the commercial week" do + @date.strftime("%v").should == " 9-Apr-2000" + @date.strftime("%v").should == @date.strftime('%e-%b-%Y') + end + + # additional conversion specifiers only in Date/DateTime + it 'shows the number of milliseconds since epoch' do + DateTime.new(1970, 1, 1).strftime('%Q').should == "0" + @date.strftime("%Q").should == "955238400000" + end + + it "should be able to show a full notation" do + @date.strftime("%+").should == "Sun Apr 9 00:00:00 +00:00 2000" + @date.strftime("%+").should == @date.strftime('%a %b %e %H:%M:%S %Z %Y') + end +end diff --git a/spec/rubyspec/library/date/strptime_spec.rb b/spec/rubyspec/library/date/strptime_spec.rb new file mode 100644 index 0000000000..21a73da086 --- /dev/null +++ b/spec/rubyspec/library/date/strptime_spec.rb @@ -0,0 +1,149 @@ +require 'date' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Date#strptime" do + + it "returns January 1, 4713 BCE when given no arguments" do + Date.strptime.should == Date.civil(-4712, 1, 1) + end + + it "uses the default format when not given a date format" do + Date.strptime("2000-04-06").should == Date.civil(2000, 4, 6) + Date.civil(2000, 4, 6).strftime.should == Date.civil(2000, 4, 6).to_s + end + + it "parses a full day name" do + d = Date.today + expected_date = Date.commercial(d.cwyear, d.cweek, 4) + # strptime assumed week that start on sunday, not monday + expected_date += 7 if d.cwday == 7 + Date.strptime("Thursday", "%A").should == expected_date + end + + it "parses a short day name" do + d = Date.today + expected_date = Date.commercial(d.cwyear, d.cweek, 4) + # strptime assumed week that start on sunday, not monday + expected_date += 7 if d.cwday == 7 + Date.strptime("Thu", "%a").should == expected_date + end + + it "parses a full month name" do + d = Date.today + Date.strptime("April", "%B").should == Date.civil(d.year, 4, 1) + end + + it "parses a short month name" do + d = Date.today + Date.strptime("Apr", "%b").should == Date.civil(d.year, 4, 1) + Date.strptime("Apr", "%h").should == Date.civil(d.year, 4, 1) + end + + it "parses a century" do + Date.strptime("06 20", "%y %C").should == Date.civil(2006, 1, 1) + end + + it "parses a month day with leading zeroes" do + d = Date.today + Date.strptime("06", "%d").should == Date.civil(d.year, d.month, 6) + end + + it "parses a month day with leading spaces" do + d = Date.today + Date.strptime(" 6", "%e").should == Date.civil(d.year, d.month, 6) + end + + it "parses a commercial year with leading zeroes" do + Date.strptime("2000", "%G").should == Date.civil(2000, 1, 3) + Date.strptime("2002", "%G").should == Date.civil(2001, 12, 31) + end + + it "parses a commercial year with only two digits" do + Date.strptime("68", "%g").should == Date.civil(2068, 1, 2) + Date.strptime("69", "%g").should == Date.civil(1968, 12, 30) + end + + it "parses a year day with leading zeroes" do + d = Date.today + if Date.gregorian_leap?(Date.today.year) + Date.strptime("097", "%j").should == Date.civil(d.year, 4, 6) + else + Date.strptime("097", "%j").should == Date.civil(d.year, 4, 7) + end + end + + it "parses a month with leading zeroes" do + d = Date.today + Date.strptime("04", "%m").should == Date.civil(d.year, 4, 1) + end + + it "parses a week number for a week starting on Sunday" do + Date.strptime("2010/1", "%Y/%U").should == Date.civil(2010, 1, 3) + end + + # See http://redmine.ruby-lang.org/repositories/diff/ruby-19?rev=24500 + it "parses a week number for a week starting on Monday" do + Date.strptime("2010/1", "%Y/%W").should == Date.civil(2010, 1, 4) + end + + it "parses a commercial week day" do + Date.strptime("2008 1", "%G %u").should == Date.civil(2007, 12, 31) + end + + it "parses a commercial week" do + d = Date.commercial(Date.today.cwyear,1,1) + Date.strptime("1", "%V").should == d + Date.strptime("15", "%V").should == Date.commercial(d.cwyear, 15, 1) + end + + it "parses a week day" do + Date.strptime("2007 4", "%Y %w").should == Date.civil(2007, 1, 4) + end + + it "parses a year in YYYY format" do + Date.strptime("2007", "%Y").should == Date.civil(2007, 1, 1) + end + + it "parses a year in YY format" do + Date.strptime("00", "%y").should == Date.civil(2000, 1, 1) + end + + ############################ + # Specs that combine stuff # + ############################ + + it "parses a full date" do + Date.strptime("Thu Apr 6 00:00:00 2000", "%c").should == Date.civil(2000, 4, 6) + Date.strptime("Thu Apr 6 00:00:00 2000", "%a %b %e %H:%M:%S %Y").should == Date.civil(2000, 4, 6) + end + + it "parses a date with slashes" do + Date.strptime("04/06/00", "%D").should == Date.civil(2000, 4, 6) + Date.strptime("04/06/00", "%m/%d/%y").should == Date.civil(2000, 4, 6) + end + + it "parses a date given as YYYY-MM-DD" do + Date.strptime("2000-04-06", "%F").should == Date.civil(2000, 4, 6) + Date.strptime("2000-04-06", "%Y-%m-%d").should == Date.civil(2000, 4, 6) + end + + it "parses a commercial week" do + Date.strptime(" 9-Apr-2000", "%v").should == Date.civil(2000, 4, 9) + Date.strptime(" 9-Apr-2000", "%e-%b-%Y").should == Date.civil(2000, 4, 9) + end + + it "parses a date given MM/DD/YY" do + Date.strptime("04/06/00", "%x").should == Date.civil(2000, 4, 6) + Date.strptime("04/06/00", "%m/%d/%y").should == Date.civil(2000, 4, 6) + end + + it "parses a date given in full notation" do + Date.strptime("Sun Apr 9 00:00:00 +00:00 2000", "%+").should == Date.civil(2000, 4, 9) + Date.strptime("Sun Apr 9 00:00:00 +00:00 2000", "%a %b %e %H:%M:%S %Z %Y").should == Date.civil(2000, 4, 9) + end + +end + +describe "Date.strptime" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/succ_spec.rb b/spec/rubyspec/library/date/succ_spec.rb new file mode 100644 index 0000000000..2650810e73 --- /dev/null +++ b/spec/rubyspec/library/date/succ_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#succ" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/time_to_day_fraction_spec.rb b/spec/rubyspec/library/date/time_to_day_fraction_spec.rb new file mode 100644 index 0000000000..06d477b601 --- /dev/null +++ b/spec/rubyspec/library/date/time_to_day_fraction_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.time_to_day_fraction" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/to_s_spec.rb b/spec/rubyspec/library/date/to_s_spec.rb new file mode 100644 index 0000000000..a81297d689 --- /dev/null +++ b/spec/rubyspec/library/date/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#to_s" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/today_spec.rb b/spec/rubyspec/library/date/today_spec.rb new file mode 100644 index 0000000000..09e8ed6006 --- /dev/null +++ b/spec/rubyspec/library/date/today_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.today" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/upto_spec.rb b/spec/rubyspec/library/date/upto_spec.rb new file mode 100644 index 0000000000..c99aabd1d7 --- /dev/null +++ b/spec/rubyspec/library/date/upto_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#upto" do + it "returns future dates for the default step value" do + ds = Date.civil(2008, 10, 11) + de = Date.civil(2008, 9, 29) + count = 0 + de.upto(ds) do |d| + d.should <= ds + d.should >= de + count += 1 + end + count.should == 13 + end +end diff --git a/spec/rubyspec/library/date/valid_civil_spec.rb b/spec/rubyspec/library/date/valid_civil_spec.rb new file mode 100644 index 0000000000..09185674ee --- /dev/null +++ b/spec/rubyspec/library/date/valid_civil_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/valid_civil', __FILE__) +require 'date' + +describe "Date#valid_civil?" do + + it_behaves_like :date_valid_civil?, :valid_civil? + +end + diff --git a/spec/rubyspec/library/date/valid_commercial_spec.rb b/spec/rubyspec/library/date/valid_commercial_spec.rb new file mode 100644 index 0000000000..187d818233 --- /dev/null +++ b/spec/rubyspec/library/date/valid_commercial_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/valid_commercial', __FILE__) +require 'date' + +describe "Date#valid_commercial?" do + + it_behaves_like :date_valid_commercial?, :valid_commercial? +end + + diff --git a/spec/rubyspec/library/date/valid_date_spec.rb b/spec/rubyspec/library/date/valid_date_spec.rb new file mode 100644 index 0000000000..ffaf007cd1 --- /dev/null +++ b/spec/rubyspec/library/date/valid_date_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/valid_civil', __FILE__) +require 'date' + +describe "Date#valid_date?" do + it_behaves_like :date_valid_civil?, :valid_date? +end diff --git a/spec/rubyspec/library/date/valid_jd_spec.rb b/spec/rubyspec/library/date/valid_jd_spec.rb new file mode 100644 index 0000000000..9764c02f2b --- /dev/null +++ b/spec/rubyspec/library/date/valid_jd_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/valid_jd', __FILE__) +require 'date' + +describe "Date.valid_jd?" do + + it_behaves_like :date_valid_jd?, :valid_jd? + +end + diff --git a/spec/rubyspec/library/date/valid_ordinal_spec.rb b/spec/rubyspec/library/date/valid_ordinal_spec.rb new file mode 100644 index 0000000000..e197bb2051 --- /dev/null +++ b/spec/rubyspec/library/date/valid_ordinal_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/valid_ordinal', __FILE__) +require 'date' + +describe "Date.valid_ordinal?" do + + it_behaves_like :date_valid_ordinal?, :valid_ordinal? + +end + diff --git a/spec/rubyspec/library/date/valid_time_spec.rb b/spec/rubyspec/library/date/valid_time_spec.rb new file mode 100644 index 0000000000..e96f9041b7 --- /dev/null +++ b/spec/rubyspec/library/date/valid_time_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.valid_time?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/wday_spec.rb b/spec/rubyspec/library/date/wday_spec.rb new file mode 100644 index 0000000000..1d40b0c96c --- /dev/null +++ b/spec/rubyspec/library/date/wday_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#wday" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/yday_spec.rb b/spec/rubyspec/library/date/yday_spec.rb new file mode 100644 index 0000000000..92bf616406 --- /dev/null +++ b/spec/rubyspec/library/date/yday_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#yday" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/year_spec.rb b/spec/rubyspec/library/date/year_spec.rb new file mode 100644 index 0000000000..cca95dbe2a --- /dev/null +++ b/spec/rubyspec/library/date/year_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date#year" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/date/zone_to_diff_spec.rb b/spec/rubyspec/library/date/zone_to_diff_spec.rb new file mode 100644 index 0000000000..a39de0b58e --- /dev/null +++ b/spec/rubyspec/library/date/zone_to_diff_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "Date.zone_to_diff" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/_strptime_spec.rb b/spec/rubyspec/library/datetime/_strptime_spec.rb new file mode 100644 index 0000000000..c8c910618d --- /dev/null +++ b/spec/rubyspec/library/datetime/_strptime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime._strptime" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/civil_spec.rb b/spec/rubyspec/library/datetime/civil_spec.rb new file mode 100644 index 0000000000..77df021e33 --- /dev/null +++ b/spec/rubyspec/library/datetime/civil_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.civil" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/commercial_spec.rb b/spec/rubyspec/library/datetime/commercial_spec.rb new file mode 100644 index 0000000000..2984dd5334 --- /dev/null +++ b/spec/rubyspec/library/datetime/commercial_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.commercial" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/hour_spec.rb b/spec/rubyspec/library/datetime/hour_spec.rb new file mode 100644 index 0000000000..db89016937 --- /dev/null +++ b/spec/rubyspec/library/datetime/hour_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#hour" do + it "returns 0 if no argument is passed" do + DateTime.new.hour.should == 0 + end + + it "returns the hour given as argument" do + new_datetime(hour: 5).hour.should == 5 + end + + it "adds 24 to negative hours" do + new_datetime(hour: -10).hour.should == 14 + end + + it "raises an error for Rational" do + lambda { new_datetime(hour: 1 + Rational(1,2)) }.should raise_error(ArgumentError) + end + + it "raises an error for Float" do + lambda { new_datetime(hour: 1.5).hour }.should raise_error(ArgumentError) + end + + it "raises an error for Rational" do + lambda { new_datetime(day: 1 + Rational(1,2)) }.should raise_error(ArgumentError) + end + + it "raises an error, when the hour is smaller than -24" do + lambda { new_datetime(hour: -25) }.should raise_error(ArgumentError) + end + + it "raises an error, when the hour is larger than 24" do + lambda { new_datetime(hour: 25) }.should raise_error(ArgumentError) + end + + it "raises an error for hour fractions smaller than -24" do + lambda { new_datetime(hour: -24 - Rational(1,2)) }.should( + raise_error(ArgumentError)) + end + + it "adds 1 to day, when 24 hours given" do + d = new_datetime day: 1, hour: 24 + d.hour.should == 0 + d.day.should == 2 + end +end diff --git a/spec/rubyspec/library/datetime/httpdate_spec.rb b/spec/rubyspec/library/datetime/httpdate_spec.rb new file mode 100644 index 0000000000..e97fda7fb4 --- /dev/null +++ b/spec/rubyspec/library/datetime/httpdate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.httpdate" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/iso8601_spec.rb b/spec/rubyspec/library/datetime/iso8601_spec.rb new file mode 100644 index 0000000000..44887148d3 --- /dev/null +++ b/spec/rubyspec/library/datetime/iso8601_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.iso8601" do + it "needs to be reviewed for spec completeness" +end + +describe "DateTime#iso8601" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/jd_spec.rb b/spec/rubyspec/library/datetime/jd_spec.rb new file mode 100644 index 0000000000..e8271089f7 --- /dev/null +++ b/spec/rubyspec/library/datetime/jd_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.jd" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/jisx0301_spec.rb b/spec/rubyspec/library/datetime/jisx0301_spec.rb new file mode 100644 index 0000000000..2402a21cb0 --- /dev/null +++ b/spec/rubyspec/library/datetime/jisx0301_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.jisx0301" do + it "needs to be reviewed for spec completeness" +end + +describe "DateTime#jisx0301" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/min_spec.rb b/spec/rubyspec/library/datetime/min_spec.rb new file mode 100644 index 0000000000..5f7d7e7810 --- /dev/null +++ b/spec/rubyspec/library/datetime/min_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/min', __FILE__) + +describe "DateTime.min" do + it_behaves_like :datetime_min, :min +end diff --git a/spec/rubyspec/library/datetime/minute_spec.rb b/spec/rubyspec/library/datetime/minute_spec.rb new file mode 100644 index 0000000000..3ab01572fe --- /dev/null +++ b/spec/rubyspec/library/datetime/minute_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/min', __FILE__) + +describe "DateTime.minute" do + it_behaves_like :datetime_min, :minute +end diff --git a/spec/rubyspec/library/datetime/new_offset_spec.rb b/spec/rubyspec/library/datetime/new_offset_spec.rb new file mode 100644 index 0000000000..b900de6097 --- /dev/null +++ b/spec/rubyspec/library/datetime/new_offset_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#new_offset" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/new_spec.rb b/spec/rubyspec/library/datetime/new_spec.rb new file mode 100644 index 0000000000..a8275a1951 --- /dev/null +++ b/spec/rubyspec/library/datetime/new_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.new" do + it "sets all values to default if passed no arguments" do + d = DateTime.new + d.year.should == -4712 + d.month.should == 1 + d.day.should == 1 + d.hour.should == 0 + d.min.should == 0 + d.sec.should == 0 + d.sec_fraction.should == 0 + d.offset.should == 0 + end + + it "takes the first argument as year" do + DateTime.new(2011).year.should == 2011 + end + + it "takes the second argument as month" do + DateTime.new(2011, 2).month.should == 2 + end + + it "takes the third argument as day" do + DateTime.new(2011, 2, 3).day.should == 3 + end + + it "takes the forth argument as hour" do + DateTime.new(2011, 2, 3, 4).hour.should == 4 + end + + it "takes the fifth argument as minute" do + DateTime.new(1, 2, 3, 4, 5).min.should == 5 + end + + it "takes the sixth argument as second" do + DateTime.new(1, 2, 3, 4, 5, 6).sec.should == 6 + end + + it "takes the seventh argument as an offset" do + DateTime.new(1, 2, 3, 4, 5, 6, 0.7).offset.should == 0.7 + end + + it "takes the eigth argument as the date of calendar reform" do + DateTime.new(1, 2, 3, 4, 5, 6, 0.7, Date::ITALY).start().should == Date::ITALY + end + + it "raises an error on invalid arguments" do + lambda { new_datetime(minute: 999) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/datetime/now_spec.rb b/spec/rubyspec/library/datetime/now_spec.rb new file mode 100644 index 0000000000..e8c93aa604 --- /dev/null +++ b/spec/rubyspec/library/datetime/now_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.now" do + it "creates an instance of DateTime" do + DateTime.now.should be_an_instance_of(DateTime) + end +end diff --git a/spec/rubyspec/library/datetime/offset_spec.rb b/spec/rubyspec/library/datetime/offset_spec.rb new file mode 100644 index 0000000000..c11d28bad2 --- /dev/null +++ b/spec/rubyspec/library/datetime/offset_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#offset" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/ordinal_spec.rb b/spec/rubyspec/library/datetime/ordinal_spec.rb new file mode 100644 index 0000000000..2ada512e05 --- /dev/null +++ b/spec/rubyspec/library/datetime/ordinal_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.ordinal" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/parse_spec.rb b/spec/rubyspec/library/datetime/parse_spec.rb new file mode 100644 index 0000000000..14a68f1499 --- /dev/null +++ b/spec/rubyspec/library/datetime/parse_spec.rb @@ -0,0 +1,127 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.parse" do + + it "parses a day name into a DateTime object" do + d = DateTime.parse("friday") + d.should == DateTime.commercial(d.cwyear, d.cweek, 5) + end + + it "parses a month name into a DateTime object" do + d = DateTime.parse("october") + d.should == DateTime.civil(Date.today.year, 10) + end + + it "parses a month day into a DateTime object" do + d = DateTime.parse("5th") + d.should == DateTime.civil(Date.today.year, Date.today.month, 5) + end + + # Specs using numbers + it "throws an argument error for a single digit" do + lambda{ DateTime.parse("1") }.should raise_error(ArgumentError) + end + + it "parses DD as month day number" do + d = DateTime.parse("10") + d.should == DateTime.civil(Date.today.year, Date.today.month, 10) + end + + it "parses DDD as year day number" do + d = DateTime.parse("100") + if DateTime.gregorian_leap?(Date.today.year) + d.should == DateTime.civil(Date.today.year, 4, 9) + else + d.should == DateTime.civil(Date.today.year, 4, 10) + end + end + + it "parses MMDD as month and day" do + d = DateTime.parse("1108") + d.should == DateTime.civil(Date.today.year, 11, 8) + end + + it "parses YYYYMMDD as year, month and day" do + d = DateTime.parse("20121108") + d.should == DateTime.civil(2012, 11, 8) + end + + describe "YYYY-MM-DDTHH:MM:SS format" do + it "parses YYYY-MM-DDTHH:MM:SS into a DateTime object" do + d = DateTime.parse("2012-11-08T15:43:59") + d.should == DateTime.civil(2012, 11, 8, 15, 43, 59) + end + + it "throws an argument error for invalid month values" do + lambda{DateTime.parse("2012-13-08T15:43:59")}.should raise_error(ArgumentError) + end + + it "throws an argument error for invalid day values" do + lambda{DateTime.parse("2012-12-32T15:43:59")}.should raise_error(ArgumentError) + end + + it "throws an argument error for invalid hour values" do + lambda{DateTime.parse("2012-12-31T25:43:59")}.should raise_error(ArgumentError) + end + + it "throws an argument error for invalid minute values" do + lambda{DateTime.parse("2012-12-31T25:43:59")}.should raise_error(ArgumentError) + end + + it "throws an argument error for invalid second values" do + lambda{DateTime.parse("2012-11-08T15:43:61")}.should raise_error(ArgumentError) + end + + end + + it "parses YYDDD as year and day number in 1969--2068" do + d = DateTime.parse("10100") + d.should == DateTime.civil(2010, 4, 10) + end + + it "parses YYMMDD as year, month and day in 1969--2068" do + d = DateTime.parse("201023") + d.should == DateTime.civil(2020, 10, 23) + end + + it "parses YYYYDDD as year and day number" do + d = DateTime.parse("1910100") + d.should == DateTime.civil(1910, 4, 10) + end + + it "parses YYYYMMDD as year, month and day number" do + d = DateTime.parse("19101101") + d.should == DateTime.civil(1910, 11, 1) + end +end + +describe "DateTime.parse(.)" do + it "parses YYYY.MM.DD into a DateTime object" do + d = DateTime.parse("2007.10.01") + d.year.should == 2007 + d.month.should == 10 + d.day.should == 1 + end + + it "parses DD.MM.YYYY into a DateTime object" do + d = DateTime.parse("10.01.2007") + d.year.should == 2007 + d.month.should == 1 + d.day.should == 10 + end + + it "parses YY.MM.DD into a DateTime object using the year 20YY" do + d = DateTime.parse("10.01.07") + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end + + it "parses YY.MM.DD using the year digits as 20YY when given true as additional argument" do + d = DateTime.parse("10.01.07", true) + d.year.should == 2010 + d.month.should == 1 + d.day.should == 7 + end +end diff --git a/spec/rubyspec/library/datetime/rfc2822_spec.rb b/spec/rubyspec/library/datetime/rfc2822_spec.rb new file mode 100644 index 0000000000..3fc4dc14b8 --- /dev/null +++ b/spec/rubyspec/library/datetime/rfc2822_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.rfc2822" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/rfc3339_spec.rb b/spec/rubyspec/library/datetime/rfc3339_spec.rb new file mode 100644 index 0000000000..4fea795d9e --- /dev/null +++ b/spec/rubyspec/library/datetime/rfc3339_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.rfc3339" do + it "needs to be reviewed for spec completeness" +end + +describe "DateTime#rfc3339" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/rfc822_spec.rb b/spec/rubyspec/library/datetime/rfc822_spec.rb new file mode 100644 index 0000000000..f86ee77379 --- /dev/null +++ b/spec/rubyspec/library/datetime/rfc822_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.rfc822" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/sec_fraction_spec.rb b/spec/rubyspec/library/datetime/sec_fraction_spec.rb new file mode 100644 index 0000000000..3ed274a5bc --- /dev/null +++ b/spec/rubyspec/library/datetime/sec_fraction_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#sec_fraction" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/sec_spec.rb b/spec/rubyspec/library/datetime/sec_spec.rb new file mode 100644 index 0000000000..c7e9af2650 --- /dev/null +++ b/spec/rubyspec/library/datetime/sec_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/sec', __FILE__) + +describe "DateTime.sec" do + it_behaves_like :datetime_sec, :sec +end diff --git a/spec/rubyspec/library/datetime/second_fraction_spec.rb b/spec/rubyspec/library/datetime/second_fraction_spec.rb new file mode 100644 index 0000000000..32dc9333f6 --- /dev/null +++ b/spec/rubyspec/library/datetime/second_fraction_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#second_fraction" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/second_spec.rb b/spec/rubyspec/library/datetime/second_spec.rb new file mode 100644 index 0000000000..08cdf463f7 --- /dev/null +++ b/spec/rubyspec/library/datetime/second_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/sec', __FILE__) + +describe "DateTime#second" do + it_behaves_like :datetime_sec, :second +end diff --git a/spec/rubyspec/library/datetime/shared/min.rb b/spec/rubyspec/library/datetime/shared/min.rb new file mode 100644 index 0000000000..e69d86ab02 --- /dev/null +++ b/spec/rubyspec/library/datetime/shared/min.rb @@ -0,0 +1,40 @@ +require 'date' + +describe :datetime_min, shared: true do + it "returns 0 if no argument is passed" do + DateTime.new.send(@method).should == 0 + end + + it "returns the minute passed as argument" do + new_datetime(minute: 5).send(@method).should == 5 + end + + it "adds 60 to negative minutes" do + new_datetime(minute: -20).send(@method).should == 40 + end + + it "raises an error for Rational" do + lambda { new_datetime minute: 5 + Rational(1,2) }.should raise_error(ArgumentError) + end + + it "raises an error for Float" do + lambda { new_datetime minute: 5.5 }.should raise_error(ArgumentError) + end + + it "raises an error for Rational" do + lambda { new_datetime(hour: 2 + Rational(1,2)) }.should raise_error(ArgumentError) + end + + it "raises an error, when the minute is smaller than -60" do + lambda { new_datetime(minute: -61) }.should raise_error(ArgumentError) + end + + it "raises an error, when the minute is greater or equal than 60" do + lambda { new_datetime(minute: 60) }.should raise_error(ArgumentError) + end + + it "raises an error for minute fractions smaller than -60" do + lambda { new_datetime(minute: -60 - Rational(1,2))}.should( + raise_error(ArgumentError)) + end +end diff --git a/spec/rubyspec/library/datetime/shared/sec.rb b/spec/rubyspec/library/datetime/shared/sec.rb new file mode 100644 index 0000000000..68e8af8e40 --- /dev/null +++ b/spec/rubyspec/library/datetime/shared/sec.rb @@ -0,0 +1,45 @@ +require 'date' + +describe :datetime_sec, shared: true do + it "returns 0 seconds if passed no arguments" do + d = DateTime.new + d.send(@method).should == 0 + end + + it "returns the seconds passed in the arguments" do + new_datetime(second: 5).send(@method).should == 5 + end + + it "adds 60 to negative values" do + new_datetime(second: -20).send(@method).should == 40 + end + + it "returns the absolute value of a Rational" do + new_datetime(second: 5 + Rational(1,2)).send(@method).should == 5 + end + + it "returns the absolute value of a float" do + new_datetime(second: 5.5).send(@method).should == 5 + end + + it "raises an error when minute is given as a rational" do + lambda { new_datetime(minute: 5 + Rational(1,2)) }.should raise_error(ArgumentError) + end + + it "raises an error, when the second is smaller than -60" do + lambda { new_datetime(second: -61) }.should raise_error(ArgumentError) + end + + it "raises an error, when the second is greater or equal than 60" do + lambda { new_datetime(second: 60) }.should raise_error(ArgumentError) + end + + it "raises an error for second fractions smaller than -60" do + lambda { new_datetime(second: -60 - Rational(1,2))}.should( + raise_error(ArgumentError)) + end + + it "takes a second fraction near 60" do + new_datetime(second: 59 + Rational(1,2)).send(@method).should == 59 + end +end diff --git a/spec/rubyspec/library/datetime/strftime_spec.rb b/spec/rubyspec/library/datetime/strftime_spec.rb new file mode 100644 index 0000000000..37705788a1 --- /dev/null +++ b/spec/rubyspec/library/datetime/strftime_spec.rb @@ -0,0 +1,51 @@ +require 'date' +require File.expand_path('../../../shared/time/strftime_for_date', __FILE__) +require File.expand_path('../../../shared/time/strftime_for_time', __FILE__) + +describe "DateTime#strftime" do + before :all do + @new_date = lambda { |y,m,d| DateTime.civil(y,m,d) } + @new_time = lambda { |*args| DateTime.civil(*args) } + @new_time_in_zone = lambda { |zone,offset,*args| + y, m, d, h, min, s = args + DateTime.new(y, m||1, d||1, h||0, min||0, s||0, Rational(offset, 24)) + } + @new_time_with_offset = lambda { |y,m,d,h,min,s,offset| + DateTime.new(y,m,d,h,min,s, Rational(offset, 86_400)) + } + + @time = DateTime.civil(2001, 2, 3, 4, 5, 6) + end + + it_behaves_like :strftime_date, :strftime + it_behaves_like :strftime_time, :strftime + + # Differences with Time + it "should be able to print the datetime with no argument" do + @time.strftime.should == "2001-02-03T04:05:06+00:00" + @time.strftime.should == @time.to_s + end + + # %Z is %:z for Date/DateTime + it "should be able to show the timezone with a : separator" do + @time.strftime("%Z").should == "+00:00" + end + + # %v is %e-%b-%Y for Date/DateTime + it "should be able to show the commercial week" do + @time.strftime("%v").should == " 3-Feb-2001" + @time.strftime("%v").should == @time.strftime('%e-%b-%Y') + end + + # additional conversion specifiers only in Date/DateTime + it 'shows the number of milliseconds since epoch' do + DateTime.new(1970, 1, 1, 0, 0, 0).strftime("%Q").should == "0" + @time.strftime("%Q").should == "981173106000" + DateTime.civil(2001, 2, 3, 4, 5, Rational(6123, 1000)).strftime("%Q").should == "981173106123" + end + + it "should be able to show a full notation" do + @time.strftime("%+").should == "Sat Feb 3 04:05:06 +00:00 2001" + @time.strftime("%+").should == @time.strftime('%a %b %e %H:%M:%S %Z %Y') + end +end diff --git a/spec/rubyspec/library/datetime/strptime_spec.rb b/spec/rubyspec/library/datetime/strptime_spec.rb new file mode 100644 index 0000000000..f3098f6f6b --- /dev/null +++ b/spec/rubyspec/library/datetime/strptime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.strptime" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/to_date_spec.rb b/spec/rubyspec/library/datetime/to_date_spec.rb new file mode 100644 index 0000000000..915e1ac87a --- /dev/null +++ b/spec/rubyspec/library/datetime/to_date_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#to_date" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/to_datetime_spec.rb b/spec/rubyspec/library/datetime/to_datetime_spec.rb new file mode 100644 index 0000000000..e289f8ce36 --- /dev/null +++ b/spec/rubyspec/library/datetime/to_datetime_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#to_datetime" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/to_s_spec.rb b/spec/rubyspec/library/datetime/to_s_spec.rb new file mode 100644 index 0000000000..893cc93283 --- /dev/null +++ b/spec/rubyspec/library/datetime/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#to_s" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/to_time_spec.rb b/spec/rubyspec/library/datetime/to_time_spec.rb new file mode 100644 index 0000000000..aa2902930c --- /dev/null +++ b/spec/rubyspec/library/datetime/to_time_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#to_time" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/xmlschema_spec.rb b/spec/rubyspec/library/datetime/xmlschema_spec.rb new file mode 100644 index 0000000000..5f33d33436 --- /dev/null +++ b/spec/rubyspec/library/datetime/xmlschema_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime.xmlschema" do + it "needs to be reviewed for spec completeness" +end + +describe "DateTime#xmlschema" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/datetime/zone_spec.rb b/spec/rubyspec/library/datetime/zone_spec.rb new file mode 100644 index 0000000000..fa8c9a982d --- /dev/null +++ b/spec/rubyspec/library/datetime/zone_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'date' + +describe "DateTime#zone" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/delegate/delegate_class/instance_method_spec.rb b/spec/rubyspec/library/delegate/delegate_class/instance_method_spec.rb new file mode 100644 index 0000000000..61680b9d5a --- /dev/null +++ b/spec/rubyspec/library/delegate/delegate_class/instance_method_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "DelegateClass.instance_method" do + before :all do + @klass = DelegateSpecs::DelegateClass + @obj = @klass.new(DelegateSpecs::Simple.new) + end + + it "returns a method object for public instance methods of the delegated class" do + m = @klass.instance_method(:pub) + m.should be_an_instance_of(UnboundMethod) + m.bind(@obj).call.should == :foo + end + + it "returns a method object for protected instance methods of the delegated class" do + m = @klass.instance_method(:prot) + m.should be_an_instance_of(UnboundMethod) + m.bind(@obj).call.should == :protected + end + + it "raises a NameError for a private instance methods of the delegated class" do + lambda { + @klass.instance_method(:priv) + }.should raise_error(NameError) + end + + it "returns a method object for public instance methods of the DelegateClass class" do + m = @klass.instance_method(:extra) + m.should be_an_instance_of(UnboundMethod) + m.bind(@obj).call.should == :cheese + end + + it "returns a method object for protected instance methods of the DelegateClass class" do + m = @klass.instance_method(:extra_protected) + m.should be_an_instance_of(UnboundMethod) + m.bind(@obj).call.should == :baz + end + + it "returns a method object for private instance methods of the DelegateClass class" do + m = @klass.instance_method(:extra_private) + m.should be_an_instance_of(UnboundMethod) + m.bind(@obj).call.should == :bar + end + + it "raises a NameError for an invalid method name" do + lambda { + @klass.instance_method(:invalid_and_silly_method_name) + }.should raise_error(NameError) + end + +end diff --git a/spec/rubyspec/library/delegate/delegate_class/instance_methods_spec.rb b/spec/rubyspec/library/delegate/delegate_class/instance_methods_spec.rb new file mode 100644 index 0000000000..ae329ab8eb --- /dev/null +++ b/spec/rubyspec/library/delegate/delegate_class/instance_methods_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "DelegateClass.instance_methods" do + before :all do + @methods = DelegateSpecs::DelegateClass.instance_methods + end + + it "includes all public methods of the delegated class" do + @methods.should include :pub + end + + it "includes all protected methods of the delegated class" do + @methods.should include :prot + end + + it "includes instance methods of the DelegateClass class" do + @methods.should include :extra + @methods.should include :extra_protected + end + + it "does not include private methods" do + @methods.should_not include :priv + @methods.should_not include :extra_private + end +end diff --git a/spec/rubyspec/library/delegate/delegate_class/private_instance_methods_spec.rb b/spec/rubyspec/library/delegate/delegate_class/private_instance_methods_spec.rb new file mode 100644 index 0000000000..d93b6d0e3d --- /dev/null +++ b/spec/rubyspec/library/delegate/delegate_class/private_instance_methods_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "DelegateClass.private_instance_methods" do + before :all do + @methods = DelegateSpecs::DelegateClass.private_instance_methods + end + + it "does not include any instance methods of the delegated class" do + @methods.should_not include :pub + @methods.should_not include :prot + @methods.should_not include :priv # since these are not forwarded... + end + + it "includes private instance methods of the DelegateClass class" do + @methods.should include :extra_private + end + + it "does not include public or protected instance methods of the DelegateClass class" do + @methods.should_not include :extra + @methods.should_not include :extra_protected + end +end diff --git a/spec/rubyspec/library/delegate/delegate_class/protected_instance_methods_spec.rb b/spec/rubyspec/library/delegate/delegate_class/protected_instance_methods_spec.rb new file mode 100644 index 0000000000..3ae0270dbd --- /dev/null +++ b/spec/rubyspec/library/delegate/delegate_class/protected_instance_methods_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "DelegateClass.protected_instance_methods" do + before :all do + @methods = DelegateSpecs::DelegateClass.protected_instance_methods + end + + it "does not include public methods of the delegated class" do + @methods.should_not include :pub + end + + it "includes the protected methods of the delegated class" do + @methods.should include :prot + end + + it "includes protected instance methods of the DelegateClass class" do + @methods.should include :extra_protected + end + + it "does not include public instance methods of the DelegateClass class" do + @methods.should_not include :extra + end + + it "does not include private methods" do + @methods.should_not include :priv + @methods.should_not include :extra_private + end +end diff --git a/spec/rubyspec/library/delegate/delegate_class/public_instance_methods_spec.rb b/spec/rubyspec/library/delegate/delegate_class/public_instance_methods_spec.rb new file mode 100644 index 0000000000..e06b55612e --- /dev/null +++ b/spec/rubyspec/library/delegate/delegate_class/public_instance_methods_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "DelegateClass.public_instance_methods" do + before :all do + @methods = DelegateSpecs::DelegateClass.public_instance_methods + end + + it "includes all public methods of the delegated class" do + @methods.should include :pub + end + + it "does not include the protected methods of the delegated class" do + @methods.should_not include :prot + end + + it "includes public instance methods of the DelegateClass class" do + @methods.should include :extra + end + + it "does not include private methods" do + @methods.should_not include :priv + @methods.should_not include :extra_private + end +end diff --git a/spec/rubyspec/library/delegate/delegate_class/respond_to_missing_spec.rb b/spec/rubyspec/library/delegate/delegate_class/respond_to_missing_spec.rb new file mode 100644 index 0000000000..729cfc96c6 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegate_class/respond_to_missing_spec.rb @@ -0,0 +1,23 @@ +require 'delegate' + +describe "DelegateClass#respond_to_missing?" do + it "is used for respond_to? behavior of late-bound delegated methods" do + # From jruby/jruby#3151: + # DelegateClass subtracts Delegate's public API from the target class's instance_methods + # to determine which methods to eagerly delegate. If respond_to_missing? shows up in + # instance_methods, it will get delegated and skip the delegate-aware implementation + # in Delegate. Any methods that must be delegated through method_missing, like methods + # defined after the DelegateClass is created, will fail to dispatch properly. + + cls = Class.new + dcls = DelegateClass(cls) + cdcls = Class.new(dcls) + cdcls_obj = cdcls.new(cls.new) + + cdcls_obj.respond_to?(:foo).should == false + + cls.class_eval { def foo; end } + + cdcls_obj.respond_to?(:foo).should == true + end +end diff --git a/spec/rubyspec/library/delegate/delegator/case_compare_spec.rb b/spec/rubyspec/library/delegate/delegator/case_compare_spec.rb new file mode 100644 index 0000000000..8bf79c1425 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/case_compare_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#===" do + it "is delegated" do + base = mock('base') + delegator = DelegateSpecs::Delegator.new(base) + base.should_receive(:===).with(42).and_return(:foo) + (delegator === 42).should == :foo + end +end diff --git a/spec/rubyspec/library/delegate/delegator/compare_spec.rb b/spec/rubyspec/library/delegate/delegator/compare_spec.rb new file mode 100644 index 0000000000..93431bfeb2 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/compare_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#<=>" do + it "is delegated" do + base = mock('base') + delegator = DelegateSpecs::Delegator.new(base) + base.should_receive(:<=>).with(42).and_return(1) + (delegator <=> 42).should == 1 + end +end diff --git a/spec/rubyspec/library/delegate/delegator/complement_spec.rb b/spec/rubyspec/library/delegate/delegator/complement_spec.rb new file mode 100644 index 0000000000..877a6e99c6 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/complement_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#~" do + it "is delegated" do + base = mock('base') + delegator = DelegateSpecs::Delegator.new(base) + base.should_receive(:~).and_return(:foo) + (~delegator).should == :foo + end +end diff --git a/spec/rubyspec/library/delegate/delegator/eql_spec.rb b/spec/rubyspec/library/delegate/delegator/eql_spec.rb new file mode 100644 index 0000000000..937629f73e --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/eql_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#eql?" do + it "is delegated" do + base = mock('base') + delegator = DelegateSpecs::Delegator.new(base) + base.should_receive(:eql?).with(42).and_return(:foo) + delegator.eql?(42).should == :foo + end +end diff --git a/spec/rubyspec/library/delegate/delegator/equal_spec.rb b/spec/rubyspec/library/delegate/delegator/equal_spec.rb new file mode 100644 index 0000000000..9333d6a303 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/equal_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#equal?" do + it "returns true only when compared with the delegator" do + obj = mock('base') + delegator = DelegateSpecs::Delegator.new(obj) + obj.should_not_receive(:equal?) + delegator.equal?(obj).should be_false + delegator.equal?(nil).should be_false + delegator.equal?(delegator).should be_true + end +end diff --git a/spec/rubyspec/library/delegate/delegator/equal_value_spec.rb b/spec/rubyspec/library/delegate/delegator/equal_value_spec.rb new file mode 100644 index 0000000000..7c965d77d3 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/equal_value_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#==" do + before :all do + @base = mock('base') + @delegator = DelegateSpecs::Delegator.new(@base) + end + + it "is not delegated when passed self" do + @base.should_not_receive(:==) + (@delegator == @delegator).should be_true + end + + it "is delegated when passed the delegated object" do + @base.should_receive(:==).and_return(false) + (@delegator == @base).should be_false + end + + it "is delegated in general" do + @base.should_receive(:==).and_return(true) + (@delegator == 42).should be_true + end +end diff --git a/spec/rubyspec/library/delegate/delegator/frozen_spec.rb b/spec/rubyspec/library/delegate/delegator/frozen_spec.rb new file mode 100644 index 0000000000..1fb561a349 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/frozen_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator when frozen" do + before :all do + @array = [42, :hello] + @delegate = DelegateSpecs::Delegator.new(@array) + @delegate.freeze + end + + it "is still readable" do + @delegate.should == [42, :hello] + @delegate.include?("bar").should be_false + end + + it "is frozen" do + @delegate.frozen?.should be_true + end + + it "is not writeable" do + lambda{ @delegate[0] += 2 }.should raise_error( RuntimeError ) + end + + it "creates a frozen clone" do + @delegate.clone.frozen?.should be_true + end + + it "creates an unfrozen dup" do + @delegate.dup.frozen?.should be_false + end + + it "causes mutative calls to raise RuntimeError" do + lambda{ @delegate.__setobj__("hola!") }.should raise_error( RuntimeError ) + end + + it "returns false if only the delegated object is frozen" do + DelegateSpecs::Delegator.new([1,2,3].freeze).frozen?.should be_false + end +end diff --git a/spec/rubyspec/library/delegate/delegator/hash_spec.rb b/spec/rubyspec/library/delegate/delegator/hash_spec.rb new file mode 100644 index 0000000000..3719d1b249 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/hash_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#hash" do + it "is delegated" do + base = mock('base') + delegator = DelegateSpecs::Delegator.new(base) + base.should_receive(:hash).and_return(42) + delegator.hash.should == 42 + end +end diff --git a/spec/rubyspec/library/delegate/delegator/marshal_spec.rb b/spec/rubyspec/library/delegate/delegator/marshal_spec.rb new file mode 100644 index 0000000000..5af32b5754 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/marshal_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'delegate' + +describe "SimpleDelegator" do + before :all do + @obj = "hello" + @delegate = SimpleDelegator.new(@obj) + end + + it "can be marshalled" do + m = Marshal.load(Marshal.dump(@delegate)) + m.class.should == SimpleDelegator + (m == @obj).should be_true + end + + it "can be marshalled with its instance variables intact" do + @delegate.instance_variable_set(:@foo, "bar") + m = Marshal.load(Marshal.dump(@delegate)) + m.instance_variable_get(:@foo).should == "bar" + end +end diff --git a/spec/rubyspec/library/delegate/delegator/method_spec.rb b/spec/rubyspec/library/delegate/delegator/method_spec.rb new file mode 100644 index 0000000000..1760eda645 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/method_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#method" do + before :each do + @simple = DelegateSpecs::Simple.new + @delegate = DelegateSpecs::Delegator.new(@simple) + end + + it "returns a method object for public methods of the delegate object" do + m = @delegate.method(:pub) + m.should be_an_instance_of(Method) + m.call.should == :foo + end + + it "raises a NameError for protected methods of the delegate object" do + lambda { + -> { + @delegate.method(:prot) + }.should complain(/delegator does not forward private method #prot/) + }.should raise_error(NameError) + end + + it "raises a NameError for a private methods of the delegate object" do + lambda { + -> { + @delegate.method(:priv) + }.should complain(/delegator does not forward private method #priv/) + }.should raise_error(NameError) + end + + it "returns a method object for public methods of the Delegator class" do + m = @delegate.method(:extra) + m.should be_an_instance_of(Method) + m.call.should == :cheese + end + + it "returns a method object for protected methods of the Delegator class" do + m = @delegate.method(:extra_protected) + m.should be_an_instance_of(Method) + m.call.should == :baz + end + + it "returns a method object for private methods of the Delegator class" do + m = @delegate.method(:extra_private) + m.should be_an_instance_of(Method) + m.call.should == :bar + end + + it "raises a NameError for an invalid method name" do + lambda { + @delegate.method(:invalid_and_silly_method_name) + }.should raise_error(NameError) + end + + it "returns a method that respond_to_missing?" do + m = @delegate.method(:pub_too) + m.should be_an_instance_of(Method) + m.call.should == :pub_too + end + + it "raises a NameError if method is no longer valid because object has changed" do + m = @delegate.method(:pub) + @delegate.__setobj__([1,2,3]) + lambda { + m.call + }.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/library/delegate/delegator/methods_spec.rb b/spec/rubyspec/library/delegate/delegator/methods_spec.rb new file mode 100644 index 0000000000..91a6d68bfa --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/methods_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#methods" do + before :all do + @simple = DelegateSpecs::Simple.new + class << @simple + def singleton_method + end + end + + @delegate = DelegateSpecs::Delegator.new(@simple) + @methods = @delegate.methods + end + + it "returns singleton methods when passed false" do + @delegate.methods(false).should include(:singleton_method) + end + + it "includes all public methods of the delegate object" do + @methods.should include :pub + end + + it "includes all protected methods of the delegate object" do + @methods.should include :prot + end + + it "includes instance methods of the Delegator class" do + @methods.should include :extra + @methods.should include :extra_protected + end + + it "does not include private methods" do + @methods.should_not include :priv + @methods.should_not include :extra_private + end +end diff --git a/spec/rubyspec/library/delegate/delegator/not_equal_spec.rb b/spec/rubyspec/library/delegate/delegator/not_equal_spec.rb new file mode 100644 index 0000000000..c2f91dcfa1 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/not_equal_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#!=" do + before :all do + @base = mock('base') + @delegator = DelegateSpecs::Delegator.new(@base) + end + + it "is not delegated when passed self" do + @base.should_not_receive(:"!=") + (@delegator != @delegator).should be_false + end + + it "is delegated when passed the delegated object" do + @base.should_receive(:"!=").and_return(true) + (@delegator != @base).should be_true + end + + it "is delegated in general" do + @base.should_receive(:"!=").and_return(false) + (@delegator != 42).should be_false + end +end diff --git a/spec/rubyspec/library/delegate/delegator/not_spec.rb b/spec/rubyspec/library/delegate/delegator/not_spec.rb new file mode 100644 index 0000000000..678e07e418 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/not_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#!" do + it "is delegated" do + base = mock('base') + delegator = DelegateSpecs::Delegator.new(base) + base.should_receive(:"!").and_return(:foo) + (!delegator).should == :foo + end +end diff --git a/spec/rubyspec/library/delegate/delegator/private_methods_spec.rb b/spec/rubyspec/library/delegate/delegator/private_methods_spec.rb new file mode 100644 index 0000000000..557da9bd02 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/private_methods_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#private_methods" do + before :all do + @simple = DelegateSpecs::Simple.new + @delegate = DelegateSpecs::Delegator.new(@simple) + @methods = @delegate.private_methods + end + + it "does not include any method of the delegate object" do # since delegates does not forward private calls + @methods.should_not include :priv + @methods.should_not include :prot + @methods.should_not include :pub + end + + it "includes all private instance methods of the Delegate class" do + @methods.should include :extra_private + end +end diff --git a/spec/rubyspec/library/delegate/delegator/protected_methods_spec.rb b/spec/rubyspec/library/delegate/delegator/protected_methods_spec.rb new file mode 100644 index 0000000000..5f03575f25 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/protected_methods_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#protected_methods" do + before :all do + @simple = DelegateSpecs::Simple.new + @delegate = DelegateSpecs::Delegator.new(@simple) + @methods = @delegate.protected_methods + end + + it "includes protected methods of the delegate object" do + @methods.should include :prot + end + + it "includes protected instance methods of the Delegator class" do + @methods.should include :extra_protected + end +end diff --git a/spec/rubyspec/library/delegate/delegator/public_methods_spec.rb b/spec/rubyspec/library/delegate/delegator/public_methods_spec.rb new file mode 100644 index 0000000000..4ed626be33 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/public_methods_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#public_methods" do + before :all do + @simple = DelegateSpecs::Simple.new + @delegate = DelegateSpecs::Delegator.new(@simple) + @methods = @delegate.public_methods + end + + it "includes public methods of the delegate object" do + @methods.should include :pub + end + + it "includes public instance methods of the Delegator class" do + @methods.should include :extra + end +end diff --git a/spec/rubyspec/library/delegate/delegator/send_spec.rb b/spec/rubyspec/library/delegate/delegator/send_spec.rb new file mode 100644 index 0000000000..b6e66cb74a --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/send_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "SimpleDelegator.new" do + before :all do + @simple = DelegateSpecs::Simple.new + @delegate = SimpleDelegator.new(@simple) + end + + it "forwards public method calls" do + @delegate.pub.should == :foo + end + + it "forwards protected method calls" do + lambda{ @delegate.prot }.should raise_error( NoMethodError ) + end + + it "doesn't forward private method calls" do + lambda{ @delegate.priv }.should raise_error( NoMethodError ) + end + + it "doesn't forward private method calls even via send or __send__" do + lambda{ @delegate.send(:priv, 42) }.should raise_error( NoMethodError ) + lambda{ @delegate.__send__(:priv, 42) }.should raise_error( NoMethodError ) + end +end diff --git a/spec/rubyspec/library/delegate/delegator/taint_spec.rb b/spec/rubyspec/library/delegate/delegator/taint_spec.rb new file mode 100644 index 0000000000..f78446d018 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/taint_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#taint" do + before :each do + @delegate = DelegateSpecs::Delegator.new("") + end + + it "returns self" do + @delegate.taint.equal?(@delegate).should be_true + end + + it "taints the delegator" do + @delegate.__setobj__(nil) + @delegate.taint + @delegate.tainted?.should be_true + end + + it "taints the delegated object" do + @delegate.taint + @delegate.__getobj__.tainted?.should be_true + end +end diff --git a/spec/rubyspec/library/delegate/delegator/tap_spec.rb b/spec/rubyspec/library/delegate/delegator/tap_spec.rb new file mode 100644 index 0000000000..1da6d82b01 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/tap_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#tap" do + it "yield the delegator object" do + obj = mock('base') + delegator = DelegateSpecs::Delegator.new(obj) + obj.should_not_receive(:tap) + yielded = [] + delegator.tap do |x| + yielded << x + end + yielded.size.should == 1 + yielded[0].equal?(delegator).should be_true + end +end diff --git a/spec/rubyspec/library/delegate/delegator/trust_spec.rb b/spec/rubyspec/library/delegate/delegator/trust_spec.rb new file mode 100644 index 0000000000..182395c26e --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/trust_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#trust" do + before :each do + @delegate = DelegateSpecs::Delegator.new([]) + end + + it "returns self" do + @delegate.trust.equal?(@delegate).should be_true + end + + it "trusts the delegator" do + @delegate.trust + @delegate.untrusted?.should be_false + end + + it "trusts the delegated object" do + @delegate.trust + @delegate.__getobj__.untrusted?.should be_false + end +end diff --git a/spec/rubyspec/library/delegate/delegator/untaint_spec.rb b/spec/rubyspec/library/delegate/delegator/untaint_spec.rb new file mode 100644 index 0000000000..2cce99e206 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/untaint_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#untaint" do + before :each do + @delegate = lambda { DelegateSpecs::Delegator.new("") }.call + end + + it "returns self" do + @delegate.untaint.equal?(@delegate).should be_true + end + + it "untaints the delegator" do + @delegate.untaint + @delegate.tainted?.should be_false + # No additional meaningful test; that it does or not taint + # "for real" the delegator has no consequence + end + + it "untaints the delegated object" do + @delegate.untaint + @delegate.__getobj__.tainted?.should be_false + end +end diff --git a/spec/rubyspec/library/delegate/delegator/untrust_spec.rb b/spec/rubyspec/library/delegate/delegator/untrust_spec.rb new file mode 100644 index 0000000000..e2bbf1b294 --- /dev/null +++ b/spec/rubyspec/library/delegate/delegator/untrust_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Delegator#untrust" do + before :each do + @delegate = DelegateSpecs::Delegator.new("") + end + + it "returns self" do + @delegate.untrust.equal?(@delegate).should be_true + end + + it "untrusts the delegator" do + @delegate.__setobj__(nil) + @delegate.untrust + @delegate.untrusted?.should be_true + end + + it "untrusts the delegated object" do + @delegate.untrust + @delegate.__getobj__.untrusted?.should be_true + end +end diff --git a/spec/rubyspec/library/delegate/fixtures/classes.rb b/spec/rubyspec/library/delegate/fixtures/classes.rb new file mode 100644 index 0000000000..3cb43eb8b1 --- /dev/null +++ b/spec/rubyspec/library/delegate/fixtures/classes.rb @@ -0,0 +1,60 @@ +require 'delegate' +module DelegateSpecs + class Simple + def pub + :foo + end + + def respond_to_missing?(method, priv=false) + method == :pub_too || + (priv && method == :priv_too) + end + + def method_missing(method, *args) + super unless respond_to_missing?(method, true) + method + end + + def priv(arg=nil) + yield arg if block_given? + [:priv, arg] + end + private :priv + + def prot + :protected + end + protected :prot + end + + module Extra + def extra + :cheese + end + + def extra_private + :bar + end + private :extra_private + + def extra_protected + :baz + end + protected :extra_protected + end + + class Delegator < ::Delegator + attr_accessor :data + + attr_reader :__getobj__ + def __setobj__(o) + @__getobj__ = o + end + + include Extra + end + + class DelegateClass < DelegateClass(Simple) + include Extra + end +end diff --git a/spec/rubyspec/library/digest/bubblebabble_spec.rb b/spec/rubyspec/library/digest/bubblebabble_spec.rb new file mode 100644 index 0000000000..49cc77e623 --- /dev/null +++ b/spec/rubyspec/library/digest/bubblebabble_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'digest/bubblebabble' + +describe "Digest.bubblebabble" do + it "returns a String" do + Digest.bubblebabble('').should be_an_instance_of(String) + end + + it "returns a String in the The Bubble Babble Binary Data Encoding format" do + Digest.bubblebabble('').should == 'xexax' + Digest.bubblebabble('foo').should == 'xinik-zorox' + Digest.bubblebabble('bar').should == 'ximik-cosex' + Digest.bubblebabble('1234567890').should == 'xesef-disof-gytuf-katof-movif-baxux' + end + + it "calls #to_str on an object and returns the bubble babble value of the result" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return('foo') + Digest.bubblebabble(obj).should == 'xinik-zorox' + end + + it "raises a TypeError when passed nil" do + lambda { Digest.bubblebabble(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a Fixnum" do + lambda { Digest.bubblebabble(9001) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/digest/hexencode_spec.rb b/spec/rubyspec/library/digest/hexencode_spec.rb new file mode 100644 index 0000000000..9e59e69fce --- /dev/null +++ b/spec/rubyspec/library/digest/hexencode_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'digest' + +describe "Digest.hexencode" do + before :each do + @string = 'sample string' + @encoded = "73616d706c6520737472696e67" + end + + it "returns '' when passed an empty String" do + Digest.hexencode('').should == '' + end + + it "returns the hex-encoded value of a non-empty String" do + Digest.hexencode(@string).should == @encoded + end + + it "calls #to_str on an object and returns the hex-encoded value of the result" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(@string) + Digest.hexencode(obj).should == @encoded + end + + it "raises a TypeError when passed nil" do + lambda { Digest.hexencode(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a Fixnum" do + lambda { Digest.hexencode(9001) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/digest/md5/append_spec.rb b/spec/rubyspec/library/digest/md5/append_spec.rb new file mode 100644 index 0000000000..ad828c83c1 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::MD5#<<" do + it_behaves_like(:md5_update, :<<) +end diff --git a/spec/rubyspec/library/digest/md5/block_length_spec.rb b/spec/rubyspec/library/digest/md5/block_length_spec.rb new file mode 100644 index 0000000000..acc3108da4 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/block_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#block_length" do + + it "returns the length of digest block" do + cur_digest = Digest::MD5.new + cur_digest.block_length.should == MD5Constants::BlockLength + end + +end + diff --git a/spec/rubyspec/library/digest/md5/digest_bang_spec.rb b/spec/rubyspec/library/digest/md5/digest_bang_spec.rb new file mode 100644 index 0000000000..88b865dcba --- /dev/null +++ b/spec/rubyspec/library/digest/md5/digest_bang_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#digest!" do + + it "returns a digest and can digest!" do + cur_digest = Digest::MD5.new + cur_digest << MD5Constants::Contents + cur_digest.digest!().should == MD5Constants::Digest + cur_digest.digest().should == MD5Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/md5/digest_length_spec.rb b/spec/rubyspec/library/digest/md5/digest_length_spec.rb new file mode 100644 index 0000000000..426e42af76 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/digest_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#digest_length" do + + it "returns the length of computed digests" do + cur_digest = Digest::MD5.new + cur_digest.digest_length.should == MD5Constants::DigestLength + end + +end + diff --git a/spec/rubyspec/library/digest/md5/digest_spec.rb b/spec/rubyspec/library/digest/md5/digest_spec.rb new file mode 100644 index 0000000000..1568c630aa --- /dev/null +++ b/spec/rubyspec/library/digest/md5/digest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#digest" do + + it "returns a digest" do + cur_digest = Digest::MD5.new + cur_digest.digest().should == MD5Constants::BlankDigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.digest(MD5Constants::Contents).should == MD5Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.digest(MD5Constants::Contents).should == MD5Constants::Digest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.digest.should == MD5Constants::BlankDigest + end + +end + +describe "Digest::MD5.digest" do + + it "returns a digest" do + Digest::MD5.digest(MD5Constants::Contents).should == MD5Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + Digest::MD5.digest(MD5Constants::Contents).should == MD5Constants::Digest + Digest::MD5.digest("").should == MD5Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/md5/equal_spec.rb b/spec/rubyspec/library/digest/md5/equal_spec.rb new file mode 100644 index 0000000000..0b776f53c0 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/equal_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#==" do + + it "equals itself" do + cur_digest = Digest::MD5.new + cur_digest.should == cur_digest + end + + it "equals the string representing its hexdigest" do + cur_digest = Digest::MD5.new + cur_digest.should == MD5Constants::BlankHexdigest + end + + it "equals the appropriate object that responds to to_str" do + # blank digest + cur_digest = Digest::MD5.new + obj = mock(MD5Constants::BlankHexdigest) + obj.should_receive(:to_str).and_return(MD5Constants::BlankHexdigest) + cur_digest.should == obj + + # non-blank digest + cur_digest = Digest::MD5.new + cur_digest << "test" + d_value = cur_digest.hexdigest + (obj = mock(d_value)).should_receive(:to_str).and_return(d_value) + cur_digest.should == obj + end + + it "equals the same digest for a different object" do + cur_digest = Digest::MD5.new + cur_digest2 = Digest::MD5.new + cur_digest.should == cur_digest2 + end + +end + diff --git a/spec/rubyspec/library/digest/md5/file_spec.rb b/spec/rubyspec/library/digest/md5/file_spec.rb new file mode 100644 index 0000000000..c7f4328546 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/file_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../../../../core/file/shared/read', __FILE__) + +describe "Digest::MD5.file" do + + describe "when passed a path to a file that exists" do + before :each do + @file = tmp("md5_temp") + touch(@file, 'wb') {|f| f.write MD5Constants::Contents } + end + + after :each do + rm_r @file + end + + it "returns a Digest::MD5 object" do + Digest::MD5.file(@file).should be_kind_of(Digest::MD5) + end + + it "returns a Digest::MD5 object with the correct digest" do + Digest::MD5.file(@file).digest.should == MD5Constants::Digest + end + + it "calls #to_str on an object and returns the Digest::MD5 with the result" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(@file) + result = Digest::MD5.file(obj) + result.should be_kind_of(Digest::MD5) + result.digest.should == MD5Constants::Digest + end + end + + it_behaves_like :file_read_directory, :file, Digest::MD5 + + it "raises a Errno::ENOENT when passed a path that does not exist" do + lambda { Digest::MD5.file("") }.should raise_error(Errno::ENOENT) + end + + it "raises a TypeError when passed nil" do + lambda { Digest::MD5.file(nil) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/digest/md5/hexdigest_bang_spec.rb b/spec/rubyspec/library/digest/md5/hexdigest_bang_spec.rb new file mode 100644 index 0000000000..fe67136c97 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/hexdigest_bang_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#hexdigest!" do + + it "returns a hexdigest and resets the state" do + cur_digest = Digest::MD5.new + + cur_digest << MD5Constants::Contents + cur_digest.hexdigest!.should == MD5Constants::Hexdigest + cur_digest.hexdigest.should == MD5Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/md5/hexdigest_spec.rb b/spec/rubyspec/library/digest/md5/hexdigest_spec.rb new file mode 100644 index 0000000000..9caec29f38 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/hexdigest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#hexdigest" do + + it "returns a hexdigest" do + cur_digest = Digest::MD5.new + cur_digest.hexdigest.should == MD5Constants::BlankHexdigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.hexdigest.should == MD5Constants::BlankHexdigest + end + +end + +describe "Digest::MD5.hexdigest" do + + it "returns a hexdigest" do + Digest::MD5.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + Digest::MD5.hexdigest(MD5Constants::Contents).should == MD5Constants::Hexdigest + Digest::MD5.hexdigest("").should == MD5Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/md5/inspect_spec.rb b/spec/rubyspec/library/digest/md5/inspect_spec.rb new file mode 100644 index 0000000000..e23465337a --- /dev/null +++ b/spec/rubyspec/library/digest/md5/inspect_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#inspect" do + + it "returns a Ruby object representation" do + cur_digest = Digest::MD5.new + cur_digest.inspect.should == "#<#{MD5Constants::Klass}: #{cur_digest.hexdigest()}>" + end + +end + diff --git a/spec/rubyspec/library/digest/md5/length_spec.rb b/spec/rubyspec/library/digest/md5/length_spec.rb new file mode 100644 index 0000000000..13eaf2e8d5 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/length_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::MD5#length" do + it_behaves_like :md5_length, :length +end + diff --git a/spec/rubyspec/library/digest/md5/reset_spec.rb b/spec/rubyspec/library/digest/md5/reset_spec.rb new file mode 100644 index 0000000000..d95ecfaf8c --- /dev/null +++ b/spec/rubyspec/library/digest/md5/reset_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#reset" do + + it "returns digest state to initial conditions" do + cur_digest = Digest::MD5.new + cur_digest.update MD5Constants::Contents + cur_digest.digest().should_not == MD5Constants::BlankDigest + cur_digest.reset + cur_digest.digest().should == MD5Constants::BlankDigest + end + +end + diff --git a/spec/rubyspec/library/digest/md5/shared/constants.rb b/spec/rubyspec/library/digest/md5/shared/constants.rb new file mode 100644 index 0000000000..fdfae56d63 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/shared/constants.rb @@ -0,0 +1,16 @@ +# -*- encoding: binary -*- +require 'digest/md5' + +module MD5Constants + + Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + + Klass = ::Digest::MD5 + BlockLength = 64 + DigestLength = 16 + BlankDigest = "\324\035\214\331\217\000\262\004\351\200\t\230\354\370B~" + Digest = "\2473\267qw\276\364\343\345\320\304\350\313\314\217n" + BlankHexdigest = "d41d8cd98f00b204e9800998ecf8427e" + Hexdigest = "a733b77177bef4e3e5d0c4e8cbcc8f6e" + +end diff --git a/spec/rubyspec/library/digest/md5/shared/length.rb b/spec/rubyspec/library/digest/md5/shared/length.rb new file mode 100644 index 0000000000..c5b2b97b58 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/shared/length.rb @@ -0,0 +1,8 @@ +describe :md5_length, shared: true do + it "returns the length of the digest" do + cur_digest = Digest::MD5.new + cur_digest.send(@method).should == MD5Constants::BlankDigest.size + cur_digest << MD5Constants::Contents + cur_digest.send(@method).should == MD5Constants::Digest.size + end +end diff --git a/spec/rubyspec/library/digest/md5/shared/sample.rb b/spec/rubyspec/library/digest/md5/shared/sample.rb new file mode 100644 index 0000000000..2bb4f658b1 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/shared/sample.rb @@ -0,0 +1,17 @@ +# -*- encoding: binary -*- + +require 'digest/md5' + +module MD5Constants + + Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + + Klass = ::Digest::MD5 + BlockLength = 64 + DigestLength = 16 + BlankDigest = "\324\035\214\331\217\000\262\004\351\200\t\230\354\370B~" + Digest = "\2473\267qw\276\364\343\345\320\304\350\313\314\217n" + BlankHexdigest = "d41d8cd98f00b204e9800998ecf8427e" + Hexdigest = "a733b77177bef4e3e5d0c4e8cbcc8f6e" + +end diff --git a/spec/rubyspec/library/digest/md5/shared/update.rb b/spec/rubyspec/library/digest/md5/shared/update.rb new file mode 100644 index 0000000000..be8622aed5 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/shared/update.rb @@ -0,0 +1,7 @@ +describe :md5_update, shared: true do + it "can update" do + cur_digest = Digest::MD5.new + cur_digest.send @method, MD5Constants::Contents + cur_digest.digest.should == MD5Constants::Digest + end +end diff --git a/spec/rubyspec/library/digest/md5/size_spec.rb b/spec/rubyspec/library/digest/md5/size_spec.rb new file mode 100644 index 0000000000..311286e679 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/size_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::MD5#size" do + it_behaves_like :md5_length, :size +end + diff --git a/spec/rubyspec/library/digest/md5/to_s_spec.rb b/spec/rubyspec/library/digest/md5/to_s_spec.rb new file mode 100644 index 0000000000..59c17ec821 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/to_s_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +require 'digest/md5' + +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::MD5#to_s" do + + it "returns a hexdigest" do + cur_digest = Digest::MD5.new + cur_digest.to_s.should == MD5Constants::BlankHexdigest + end + + it "does not change the internal state" do + cur_digest = Digest::MD5.new + cur_digest.to_s.should == MD5Constants::BlankHexdigest + cur_digest.to_s.should == MD5Constants::BlankHexdigest + + cur_digest << MD5Constants::Contents + cur_digest.to_s.should == MD5Constants::Hexdigest + cur_digest.to_s.should == MD5Constants::Hexdigest + end + +end diff --git a/spec/rubyspec/library/digest/md5/update_spec.rb b/spec/rubyspec/library/digest/md5/update_spec.rb new file mode 100644 index 0000000000..5a271481f7 --- /dev/null +++ b/spec/rubyspec/library/digest/md5/update_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::MD5#update" do + it_behaves_like :md5_update, :update +end diff --git a/spec/rubyspec/library/digest/sha1/digest_spec.rb b/spec/rubyspec/library/digest/sha1/digest_spec.rb new file mode 100644 index 0000000000..abb4034a69 --- /dev/null +++ b/spec/rubyspec/library/digest/sha1/digest_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA1#digest" do + + it "returns a digest" do + cur_digest = Digest::SHA1.new + cur_digest.digest().should == SHA1Constants::BlankDigest + cur_digest.digest(SHA1Constants::Contents).should == SHA1Constants::Digest + end + +end + +describe "Digest::SHA1.digest" do + + it "returns a digest" do + Digest::SHA1.digest(SHA1Constants::Contents).should == SHA1Constants::Digest + end + +end diff --git a/spec/rubyspec/library/digest/sha1/file_spec.rb b/spec/rubyspec/library/digest/sha1/file_spec.rb new file mode 100644 index 0000000000..2c9fd2cb52 --- /dev/null +++ b/spec/rubyspec/library/digest/sha1/file_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../../../../core/file/shared/read', __FILE__) + +describe "Digest::SHA1.file" do + + describe "when passed a path to a file that exists" do + before :each do + @file = tmp("md5_temp") + touch(@file, 'wb') {|f| f.write SHA1Constants::Contents } + end + + after :each do + rm_r @file + end + + it "returns a Digest::SHA1 object" do + Digest::SHA1.file(@file).should be_kind_of(Digest::SHA1) + end + + it "returns a Digest::SHA1 object with the correct digest" do + Digest::SHA1.file(@file).digest.should == SHA1Constants::Digest + end + + it "calls #to_str on an object and returns the Digest::SHA1 with the result" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(@file) + result = Digest::SHA1.file(obj) + result.should be_kind_of(Digest::SHA1) + result.digest.should == SHA1Constants::Digest + end + end + + it_behaves_like :file_read_directory, :file, Digest::SHA1 + + it "raises a Errno::ENOENT when passed a path that does not exist" do + lambda { Digest::SHA1.file("") }.should raise_error(Errno::ENOENT) + end + + it "raises a TypeError when passed nil" do + lambda { Digest::SHA1.file(nil) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/digest/sha1/shared/constants.rb b/spec/rubyspec/library/digest/sha1/shared/constants.rb new file mode 100644 index 0000000000..add86b1dd3 --- /dev/null +++ b/spec/rubyspec/library/digest/sha1/shared/constants.rb @@ -0,0 +1,17 @@ +# -*- encoding: binary -*- + +require 'digest/sha1' + +module SHA1Constants + + Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + + Klass = ::Digest::SHA1 + BlockLength = 64 + DigestLength = 20 + BlankDigest = "\3329\243\356^kK\r2U\277\357\225`\030\220\257\330\a\t" + Digest = "X!\255b\323\035\352\314a|q\344+\376\317\361V9\324\343" + BlankHexdigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709" + Hexdigest = "e907d2ba21c6c74bc0efd76e44d11fb9bbb7a75e" + +end diff --git a/spec/rubyspec/library/digest/sha256/append_spec.rb b/spec/rubyspec/library/digest/sha256/append_spec.rb new file mode 100644 index 0000000000..53e623743a --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::SHA256#<<" do + it_behaves_like(:sha256_update, :<<) +end diff --git a/spec/rubyspec/library/digest/sha256/block_length_spec.rb b/spec/rubyspec/library/digest/sha256/block_length_spec.rb new file mode 100644 index 0000000000..4fea959da1 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/block_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#block_length" do + + it "returns the length of digest block" do + cur_digest = Digest::SHA256.new + cur_digest.block_length.should == SHA256Constants::BlockLength + end + +end + diff --git a/spec/rubyspec/library/digest/sha256/digest_bang_spec.rb b/spec/rubyspec/library/digest/sha256/digest_bang_spec.rb new file mode 100644 index 0000000000..b876c2ceed --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/digest_bang_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#digest!" do + + it "returns a digest and can digest!" do + cur_digest = Digest::SHA256.new + cur_digest << SHA256Constants::Contents + cur_digest.digest!().should == SHA256Constants::Digest + cur_digest.digest().should == SHA256Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/sha256/digest_length_spec.rb b/spec/rubyspec/library/digest/sha256/digest_length_spec.rb new file mode 100644 index 0000000000..f3d0d66877 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/digest_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#digest_length" do + + it "returns the length of computed digests" do + cur_digest = Digest::SHA256.new + cur_digest.digest_length.should == SHA256Constants::DigestLength + end + +end + diff --git a/spec/rubyspec/library/digest/sha256/digest_spec.rb b/spec/rubyspec/library/digest/sha256/digest_spec.rb new file mode 100644 index 0000000000..de30916d57 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/digest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#digest" do + + it "returns a digest" do + cur_digest = Digest::SHA256.new + cur_digest.digest().should == SHA256Constants::BlankDigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.digest(SHA256Constants::Contents).should == SHA256Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.digest(SHA256Constants::Contents).should == SHA256Constants::Digest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.digest.should == SHA256Constants::BlankDigest + end + +end + +describe "Digest::SHA256.digest" do + + it "returns a digest" do + Digest::SHA256.digest(SHA256Constants::Contents).should == SHA256Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + Digest::SHA256.digest(SHA256Constants::Contents).should == SHA256Constants::Digest + Digest::SHA256.digest("").should == SHA256Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/sha256/equal_spec.rb b/spec/rubyspec/library/digest/sha256/equal_spec.rb new file mode 100644 index 0000000000..7932b6c13d --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/equal_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#==" do + + it "equals itself" do + cur_digest = Digest::SHA256.new + cur_digest.should == cur_digest + end + + it "equals the string representing its hexdigest" do + cur_digest = Digest::SHA256.new + cur_digest.should == SHA256Constants::BlankHexdigest + end + + it "equals the appropriate object that responds to to_str" do + # blank digest + cur_digest = Digest::SHA256.new + (obj = mock(SHA256Constants::BlankHexdigest)).should_receive(:to_str).and_return(SHA256Constants::BlankHexdigest) + cur_digest.should == obj + + # non-blank digest + cur_digest = Digest::SHA256.new + cur_digest << "test" + d_value = cur_digest.hexdigest + (obj = mock(d_value)).should_receive(:to_str).and_return(d_value) + cur_digest.should == obj + end + + it "equals the same digest for a different object" do + cur_digest = Digest::SHA256.new + cur_digest2 = Digest::SHA256.new + cur_digest.should == cur_digest2 + end + +end + diff --git a/spec/rubyspec/library/digest/sha256/file_spec.rb b/spec/rubyspec/library/digest/sha256/file_spec.rb new file mode 100644 index 0000000000..a52b7939f3 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/file_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../../../../core/file/shared/read', __FILE__) + +describe "Digest::SHA256.file" do + + describe "when passed a path to a file that exists" do + before :each do + @file = tmp("md5_temp") + touch(@file, 'wb') {|f| f.write SHA256Constants::Contents } + end + + after :each do + rm_r @file + end + + it "returns a Digest::SHA256 object" do + Digest::SHA256.file(@file).should be_kind_of(Digest::SHA256) + end + + it "returns a Digest::SHA256 object with the correct digest" do + Digest::SHA256.file(@file).digest.should == SHA256Constants::Digest + end + + it "calls #to_str on an object and returns the Digest::SHA256 with the result" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(@file) + result = Digest::SHA256.file(obj) + result.should be_kind_of(Digest::SHA256) + result.digest.should == SHA256Constants::Digest + end + end + + it_behaves_like :file_read_directory, :file, Digest::SHA256 + + it "raises a Errno::ENOENT when passed a path that does not exist" do + lambda { Digest::SHA256.file("") }.should raise_error(Errno::ENOENT) + end + + it "raises a TypeError when passed nil" do + lambda { Digest::SHA256.file(nil) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/digest/sha256/hexdigest_bang_spec.rb b/spec/rubyspec/library/digest/sha256/hexdigest_bang_spec.rb new file mode 100644 index 0000000000..98bf38f773 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/hexdigest_bang_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#hexdigest!" do + + it "returns a hexdigest and resets the state" do + cur_digest = Digest::SHA256.new + + cur_digest << SHA256Constants::Contents + cur_digest.hexdigest!.should == SHA256Constants::Hexdigest + cur_digest.hexdigest.should == SHA256Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha256/hexdigest_spec.rb b/spec/rubyspec/library/digest/sha256/hexdigest_spec.rb new file mode 100644 index 0000000000..3ee7844a93 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/hexdigest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#hexdigest" do + + it "returns a hexdigest" do + cur_digest = Digest::SHA256.new + cur_digest.hexdigest.should == SHA256Constants::BlankHexdigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.hexdigest.should == SHA256Constants::BlankHexdigest + end + +end + +describe "Digest::SHA256.hexdigest" do + + it "returns a hexdigest" do + Digest::SHA256.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + Digest::SHA256.hexdigest(SHA256Constants::Contents).should == SHA256Constants::Hexdigest + Digest::SHA256.hexdigest("").should == SHA256Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha256/inspect_spec.rb b/spec/rubyspec/library/digest/sha256/inspect_spec.rb new file mode 100644 index 0000000000..5e44b58c63 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/inspect_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#inspect" do + + it "returns a Ruby object representation" do + cur_digest = Digest::SHA256.new + cur_digest.inspect.should == "#<#{SHA256Constants::Klass}: #{cur_digest.hexdigest()}>" + end + +end + diff --git a/spec/rubyspec/library/digest/sha256/length_spec.rb b/spec/rubyspec/library/digest/sha256/length_spec.rb new file mode 100644 index 0000000000..6760511093 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/length_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::SHA256#length" do + it_behaves_like :sha256_length, :length +end + diff --git a/spec/rubyspec/library/digest/sha256/reset_spec.rb b/spec/rubyspec/library/digest/sha256/reset_spec.rb new file mode 100644 index 0000000000..82bb08d354 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/reset_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#reset" do + + it "returns digest state to initial conditions" do + cur_digest = Digest::SHA256.new + cur_digest.update SHA256Constants::Contents + cur_digest.digest().should_not == SHA256Constants::BlankDigest + cur_digest.reset + cur_digest.digest().should == SHA256Constants::BlankDigest + end + +end + diff --git a/spec/rubyspec/library/digest/sha256/shared/constants.rb b/spec/rubyspec/library/digest/sha256/shared/constants.rb new file mode 100644 index 0000000000..dd5b48dca9 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/shared/constants.rb @@ -0,0 +1,17 @@ +# -*- encoding: binary -*- + +require 'digest/sha2' + +module SHA256Constants + + Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + + Klass = ::Digest::SHA256 + BlockLength = 64 + DigestLength = 32 + BlankDigest = "\343\260\304B\230\374\034\024\232\373\364\310\231o\271$'\256A\344d\233\223L\244\225\231\exR\270U" + Digest = "\230b\265\344_\337\357\337\242\004\314\311A\211jb\350\373\254\370\365M\230B\002\372\020j\as\270\376" + BlankHexdigest = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + Hexdigest = "9862b5e45fdfefdfa204ccc941896a62e8fbacf8f54d984202fa106a0773b8fe" + +end diff --git a/spec/rubyspec/library/digest/sha256/shared/length.rb b/spec/rubyspec/library/digest/sha256/shared/length.rb new file mode 100644 index 0000000000..996673a5bd --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/shared/length.rb @@ -0,0 +1,8 @@ +describe :sha256_length, shared: true do + it "returns the length of the digest" do + cur_digest = Digest::SHA256.new + cur_digest.send(@method).should == SHA256Constants::BlankDigest.size + cur_digest << SHA256Constants::Contents + cur_digest.send(@method).should == SHA256Constants::Digest.size + end +end diff --git a/spec/rubyspec/library/digest/sha256/shared/update.rb b/spec/rubyspec/library/digest/sha256/shared/update.rb new file mode 100644 index 0000000000..0edc07935b --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/shared/update.rb @@ -0,0 +1,7 @@ +describe :sha256_update, shared: true do + it "can update" do + cur_digest = Digest::SHA256.new + cur_digest.send @method, SHA256Constants::Contents + cur_digest.digest.should == SHA256Constants::Digest + end +end diff --git a/spec/rubyspec/library/digest/sha256/size_spec.rb b/spec/rubyspec/library/digest/sha256/size_spec.rb new file mode 100644 index 0000000000..77db876956 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/size_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::SHA256#size" do + it_behaves_like :sha256_length, :size +end + diff --git a/spec/rubyspec/library/digest/sha256/to_s_spec.rb b/spec/rubyspec/library/digest/sha256/to_s_spec.rb new file mode 100644 index 0000000000..b91983d157 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/to_s_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA256#to_s" do + + it "returns a hexdigest" do + cur_digest = Digest::SHA256.new + cur_digest.to_s.should == SHA256Constants::BlankHexdigest + end + + it "does not change the internal state" do + cur_digest = Digest::SHA256.new + cur_digest.to_s.should == SHA256Constants::BlankHexdigest + cur_digest.to_s.should == SHA256Constants::BlankHexdigest + + cur_digest << SHA256Constants::Contents + cur_digest.to_s.should == SHA256Constants::Hexdigest + cur_digest.to_s.should == SHA256Constants::Hexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha256/update_spec.rb b/spec/rubyspec/library/digest/sha256/update_spec.rb new file mode 100644 index 0000000000..3e3eaf57a3 --- /dev/null +++ b/spec/rubyspec/library/digest/sha256/update_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::SHA256#update" do + it_behaves_like :sha256_update, :update +end diff --git a/spec/rubyspec/library/digest/sha384/append_spec.rb b/spec/rubyspec/library/digest/sha384/append_spec.rb new file mode 100644 index 0000000000..d694812e85 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::SHA384#<<" do + it_behaves_like(:sha384_update, :<<) +end diff --git a/spec/rubyspec/library/digest/sha384/block_length_spec.rb b/spec/rubyspec/library/digest/sha384/block_length_spec.rb new file mode 100644 index 0000000000..070715b27e --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/block_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#block_length" do + + it "returns the length of digest block" do + cur_digest = Digest::SHA384.new + cur_digest.block_length.should == SHA384Constants::BlockLength + end + +end + diff --git a/spec/rubyspec/library/digest/sha384/digest_bang_spec.rb b/spec/rubyspec/library/digest/sha384/digest_bang_spec.rb new file mode 100644 index 0000000000..83b68ae7c2 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/digest_bang_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#digest!" do + + it "returns a digest and can digest!" do + cur_digest = Digest::SHA384.new + cur_digest << SHA384Constants::Contents + cur_digest.digest!().should == SHA384Constants::Digest + cur_digest.digest().should == SHA384Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/sha384/digest_length_spec.rb b/spec/rubyspec/library/digest/sha384/digest_length_spec.rb new file mode 100644 index 0000000000..a57616b44c --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/digest_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#digest_length" do + + it "returns the length of computed digests" do + cur_digest = Digest::SHA384.new + cur_digest.digest_length.should == SHA384Constants::DigestLength + end + +end + diff --git a/spec/rubyspec/library/digest/sha384/digest_spec.rb b/spec/rubyspec/library/digest/sha384/digest_spec.rb new file mode 100644 index 0000000000..3a5cd3a5d7 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/digest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#digest" do + + it "returns a digest" do + cur_digest = Digest::SHA384.new + cur_digest.digest().should == SHA384Constants::BlankDigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.digest(SHA384Constants::Contents).should == SHA384Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.digest(SHA384Constants::Contents).should == SHA384Constants::Digest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.digest.should == SHA384Constants::BlankDigest + end + +end + +describe "Digest::SHA384.digest" do + + it "returns a digest" do + Digest::SHA384.digest(SHA384Constants::Contents).should == SHA384Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + Digest::SHA384.digest(SHA384Constants::Contents).should == SHA384Constants::Digest + Digest::SHA384.digest("").should == SHA384Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/sha384/equal_spec.rb b/spec/rubyspec/library/digest/sha384/equal_spec.rb new file mode 100644 index 0000000000..a54d328edc --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/equal_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#==" do + + it "equals itself" do + cur_digest = Digest::SHA384.new + cur_digest.should == cur_digest + end + + it "equals the string representing its hexdigest" do + cur_digest = Digest::SHA384.new + cur_digest.should == SHA384Constants::BlankHexdigest + end + + it "equals the appropriate object that responds to to_str" do + # blank digest + cur_digest = Digest::SHA384.new + (obj = mock(SHA384Constants::BlankHexdigest)).should_receive(:to_str).and_return(SHA384Constants::BlankHexdigest) + cur_digest.should == obj + + # non-blank digest + cur_digest = Digest::SHA384.new + cur_digest << "test" + d_value = cur_digest.hexdigest + (obj = mock(d_value)).should_receive(:to_str).and_return(d_value) + cur_digest.should == obj + end + + it "equals the same digest for a different object" do + cur_digest = Digest::SHA384.new + cur_digest2 = Digest::SHA384.new + cur_digest.should == cur_digest2 + end + +end + diff --git a/spec/rubyspec/library/digest/sha384/file_spec.rb b/spec/rubyspec/library/digest/sha384/file_spec.rb new file mode 100644 index 0000000000..7e7c8f9565 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/file_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../../../../core/file/shared/read', __FILE__) + +describe "Digest::SHA384.file" do + + describe "when passed a path to a file that exists" do + before :each do + @file = tmp("md5_temp") + touch(@file, 'wb') {|f| f.write SHA384Constants::Contents } + end + + after :each do + rm_r @file + end + + it "returns a Digest::SHA384 object" do + Digest::SHA384.file(@file).should be_kind_of(Digest::SHA384) + end + + it "returns a Digest::SHA384 object with the correct digest" do + Digest::SHA384.file(@file).digest.should == SHA384Constants::Digest + end + + it "calls #to_str on an object and returns the Digest::SHA384 with the result" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(@file) + result = Digest::SHA384.file(obj) + result.should be_kind_of(Digest::SHA384) + result.digest.should == SHA384Constants::Digest + end + end + + it_behaves_like :file_read_directory, :file, Digest::SHA384 + + it "raises a Errno::ENOENT when passed a path that does not exist" do + lambda { Digest::SHA384.file("") }.should raise_error(Errno::ENOENT) + end + + it "raises a TypeError when passed nil" do + lambda { Digest::SHA384.file(nil) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/digest/sha384/hexdigest_bang_spec.rb b/spec/rubyspec/library/digest/sha384/hexdigest_bang_spec.rb new file mode 100644 index 0000000000..68da8c7200 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/hexdigest_bang_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#hexdigest!" do + + it "returns a hexdigest and resets the state" do + cur_digest = Digest::SHA384.new + + cur_digest << SHA384Constants::Contents + cur_digest.hexdigest!.should == SHA384Constants::Hexdigest + cur_digest.hexdigest.should == SHA384Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha384/hexdigest_spec.rb b/spec/rubyspec/library/digest/sha384/hexdigest_spec.rb new file mode 100644 index 0000000000..a7724d1663 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/hexdigest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#hexdigest" do + + it "returns a hexdigest" do + cur_digest = Digest::SHA384.new + cur_digest.hexdigest.should == SHA384Constants::BlankHexdigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.hexdigest.should == SHA384Constants::BlankHexdigest + end + +end + +describe "Digest::SHA384.hexdigest" do + + it "returns a hexdigest" do + Digest::SHA384.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + Digest::SHA384.hexdigest(SHA384Constants::Contents).should == SHA384Constants::Hexdigest + Digest::SHA384.hexdigest("").should == SHA384Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha384/inspect_spec.rb b/spec/rubyspec/library/digest/sha384/inspect_spec.rb new file mode 100644 index 0000000000..554a22d135 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/inspect_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#inspect" do + + it "returns a Ruby object representation" do + cur_digest = Digest::SHA384.new + cur_digest.inspect.should == "#<#{SHA384Constants::Klass}: #{cur_digest.hexdigest()}>" + end + +end + diff --git a/spec/rubyspec/library/digest/sha384/length_spec.rb b/spec/rubyspec/library/digest/sha384/length_spec.rb new file mode 100644 index 0000000000..63a57ce9ca --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/length_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::SHA384#length" do + it_behaves_like :sha384_length, :length +end + diff --git a/spec/rubyspec/library/digest/sha384/reset_spec.rb b/spec/rubyspec/library/digest/sha384/reset_spec.rb new file mode 100644 index 0000000000..8abe39d7e2 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/reset_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#reset" do + + it "returns digest state to initial conditions" do + cur_digest = Digest::SHA384.new + cur_digest.update SHA384Constants::Contents + cur_digest.digest().should_not == SHA384Constants::BlankDigest + cur_digest.reset + cur_digest.digest().should == SHA384Constants::BlankDigest + end + +end + diff --git a/spec/rubyspec/library/digest/sha384/shared/constants.rb b/spec/rubyspec/library/digest/sha384/shared/constants.rb new file mode 100644 index 0000000000..3697384fc3 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/shared/constants.rb @@ -0,0 +1,18 @@ +# -*- encoding: binary -*- + +require 'digest/sha2' + +module SHA384Constants + + Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + + + Klass = ::Digest::SHA384 + BlockLength = 128 + DigestLength = 48 + BlankDigest = "8\260`\247Q\254\2268L\3312~\261\261\343j!\375\267\021\024\276\aCL\f\307\277c\366\341\332'N\336\277\347oe\373\325\032\322\361H\230\271[" + Digest = "B&\266:\314\216z\361!TD\001{`\355\323\320MW%\270\272\0034n\034\026g\a\217\"\333s\202\275\002Y*\217]\207u\f\034\244\231\266f" + BlankHexdigest = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" + Hexdigest = "4226b63acc8e7af1215444017b60edd3d04d5725b8ba03346e1c1667078f22db7382bd02592a8f5d87750c1ca499b666" + +end diff --git a/spec/rubyspec/library/digest/sha384/shared/length.rb b/spec/rubyspec/library/digest/sha384/shared/length.rb new file mode 100644 index 0000000000..0c88288bcf --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/shared/length.rb @@ -0,0 +1,8 @@ +describe :sha384_length, shared: true do + it "returns the length of the digest" do + cur_digest = Digest::SHA384.new + cur_digest.send(@method).should == SHA384Constants::BlankDigest.size + cur_digest << SHA384Constants::Contents + cur_digest.send(@method).should == SHA384Constants::Digest.size + end +end diff --git a/spec/rubyspec/library/digest/sha384/shared/update.rb b/spec/rubyspec/library/digest/sha384/shared/update.rb new file mode 100644 index 0000000000..1c6e31cf6a --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/shared/update.rb @@ -0,0 +1,7 @@ +describe :sha384_update, shared: true do + it "can update" do + cur_digest = Digest::SHA384.new + cur_digest.send @method, SHA384Constants::Contents + cur_digest.digest.should == SHA384Constants::Digest + end +end diff --git a/spec/rubyspec/library/digest/sha384/size_spec.rb b/spec/rubyspec/library/digest/sha384/size_spec.rb new file mode 100644 index 0000000000..9aea3ef592 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/size_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::SHA384#size" do + it_behaves_like :sha384_length, :size +end + diff --git a/spec/rubyspec/library/digest/sha384/to_s_spec.rb b/spec/rubyspec/library/digest/sha384/to_s_spec.rb new file mode 100644 index 0000000000..f45f2ee915 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/to_s_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA384#to_s" do + + it "returns a hexdigest" do + cur_digest = Digest::SHA384.new + cur_digest.to_s.should == SHA384Constants::BlankHexdigest + end + + it "does not change the internal state" do + cur_digest = Digest::SHA384.new + cur_digest.to_s.should == SHA384Constants::BlankHexdigest + cur_digest.to_s.should == SHA384Constants::BlankHexdigest + + cur_digest << SHA384Constants::Contents + cur_digest.to_s.should == SHA384Constants::Hexdigest + cur_digest.to_s.should == SHA384Constants::Hexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha384/update_spec.rb b/spec/rubyspec/library/digest/sha384/update_spec.rb new file mode 100644 index 0000000000..9917f86b86 --- /dev/null +++ b/spec/rubyspec/library/digest/sha384/update_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::SHA384#update" do + it_behaves_like :sha384_update, :update +end diff --git a/spec/rubyspec/library/digest/sha512/append_spec.rb b/spec/rubyspec/library/digest/sha512/append_spec.rb new file mode 100644 index 0000000000..642e565bf6 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::SHA512#<<" do + it_behaves_like(:sha512_update, :<<) +end diff --git a/spec/rubyspec/library/digest/sha512/block_length_spec.rb b/spec/rubyspec/library/digest/sha512/block_length_spec.rb new file mode 100644 index 0000000000..95bb98548a --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/block_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#block_length" do + + it "returns the length of digest block" do + cur_digest = Digest::SHA512.new + cur_digest.block_length.should == SHA512Constants::BlockLength + end + +end + diff --git a/spec/rubyspec/library/digest/sha512/digest_bang_spec.rb b/spec/rubyspec/library/digest/sha512/digest_bang_spec.rb new file mode 100644 index 0000000000..260595152d --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/digest_bang_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#digest!" do + + it "returns a digest and can digest!" do + cur_digest = Digest::SHA512.new + cur_digest << SHA512Constants::Contents + cur_digest.digest!().should == SHA512Constants::Digest + cur_digest.digest().should == SHA512Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/sha512/digest_length_spec.rb b/spec/rubyspec/library/digest/sha512/digest_length_spec.rb new file mode 100644 index 0000000000..5185b6e906 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/digest_length_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#digest_length" do + + it "returns the length of computed digests" do + cur_digest = Digest::SHA512.new + cur_digest.digest_length.should == SHA512Constants::DigestLength + end + +end + diff --git a/spec/rubyspec/library/digest/sha512/digest_spec.rb b/spec/rubyspec/library/digest/sha512/digest_spec.rb new file mode 100644 index 0000000000..9f4264579f --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/digest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#digest" do + + it "returns a digest" do + cur_digest = Digest::SHA512.new + cur_digest.digest().should == SHA512Constants::BlankDigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.digest(SHA512Constants::Contents).should == SHA512Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.digest(SHA512Constants::Contents).should == SHA512Constants::Digest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.digest.should == SHA512Constants::BlankDigest + end + +end + +describe "Digest::SHA512.digest" do + + it "returns a digest" do + Digest::SHA512.digest(SHA512Constants::Contents).should == SHA512Constants::Digest + # second invocation is intentional, to make sure there are no side-effects + Digest::SHA512.digest(SHA512Constants::Contents).should == SHA512Constants::Digest + Digest::SHA512.digest("").should == SHA512Constants::BlankDigest + end + +end diff --git a/spec/rubyspec/library/digest/sha512/equal_spec.rb b/spec/rubyspec/library/digest/sha512/equal_spec.rb new file mode 100644 index 0000000000..ec4c55f118 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/equal_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#==" do + + it "equals itself" do + cur_digest = Digest::SHA512.new + cur_digest.should == cur_digest + end + + it "equals the string representing its hexdigest" do + cur_digest = Digest::SHA512.new + cur_digest.should == SHA512Constants::BlankHexdigest + end + + it "equals the appropriate object that responds to to_str" do + # blank digest + cur_digest = Digest::SHA512.new + (obj = mock(SHA512Constants::BlankHexdigest)).should_receive(:to_str).and_return(SHA512Constants::BlankHexdigest) + cur_digest.should == obj + + # non-blank digest + cur_digest = Digest::SHA512.new + cur_digest << "test" + d_value = cur_digest.hexdigest + (obj = mock(d_value)).should_receive(:to_str).and_return(d_value) + cur_digest.should == obj + end + + it "equals the same digest for a different object" do + cur_digest = Digest::SHA512.new + cur_digest2 = Digest::SHA512.new + cur_digest.should == cur_digest2 + end + +end + diff --git a/spec/rubyspec/library/digest/sha512/file_spec.rb b/spec/rubyspec/library/digest/sha512/file_spec.rb new file mode 100644 index 0000000000..365f3625b6 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/file_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../../../../core/file/shared/read', __FILE__) + +describe "Digest::SHA512.file" do + + describe "when passed a path to a file that exists" do + before :each do + @file = tmp("md5_temp") + touch(@file, 'wb') {|f| f.write SHA512Constants::Contents } + end + + after :each do + rm_r @file + end + + it "returns a Digest::SHA512 object" do + Digest::SHA512.file(@file).should be_kind_of(Digest::SHA512) + end + + it "returns a Digest::SHA512 object with the correct digest" do + Digest::SHA512.file(@file).digest.should == SHA512Constants::Digest + end + + it "calls #to_str on an object and returns the Digest::SHA512 with the result" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(@file) + result = Digest::SHA512.file(obj) + result.should be_kind_of(Digest::SHA512) + result.digest.should == SHA512Constants::Digest + end + end + + it_behaves_like :file_read_directory, :file, Digest::SHA512 + + it "raises a Errno::ENOENT when passed a path that does not exist" do + lambda { Digest::SHA512.file("") }.should raise_error(Errno::ENOENT) + end + + it "raises a TypeError when passed nil" do + lambda { Digest::SHA512.file(nil) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/digest/sha512/hexdigest_bang_spec.rb b/spec/rubyspec/library/digest/sha512/hexdigest_bang_spec.rb new file mode 100644 index 0000000000..6eda150949 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/hexdigest_bang_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#hexdigest!" do + + it "returns a hexdigest and resets the state" do + cur_digest = Digest::SHA512.new + + cur_digest << SHA512Constants::Contents + cur_digest.hexdigest!.should == SHA512Constants::Hexdigest + cur_digest.hexdigest.should == SHA512Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha512/hexdigest_spec.rb b/spec/rubyspec/library/digest/sha512/hexdigest_spec.rb new file mode 100644 index 0000000000..405d380490 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/hexdigest_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#hexdigest" do + + it "returns a hexdigest" do + cur_digest = Digest::SHA512.new + cur_digest.hexdigest.should == SHA512Constants::BlankHexdigest + + # add something to check that the state is reset later + cur_digest << "test" + + cur_digest.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + cur_digest.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest + + # after all is done, verify that the digest is in the original, blank state + cur_digest.hexdigest.should == SHA512Constants::BlankHexdigest + end + +end + +describe "Digest::SHA512.hexdigest" do + + it "returns a hexdigest" do + Digest::SHA512.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest + # second invocation is intentional, to make sure there are no side-effects + Digest::SHA512.hexdigest(SHA512Constants::Contents).should == SHA512Constants::Hexdigest + Digest::SHA512.hexdigest("").should == SHA512Constants::BlankHexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha512/inspect_spec.rb b/spec/rubyspec/library/digest/sha512/inspect_spec.rb new file mode 100644 index 0000000000..97806000d2 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/inspect_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#inspect" do + + it "returns a Ruby object representation" do + cur_digest = Digest::SHA512.new + cur_digest.inspect.should == "#<#{SHA512Constants::Klass}: #{cur_digest.hexdigest()}>" + end + +end + diff --git a/spec/rubyspec/library/digest/sha512/length_spec.rb b/spec/rubyspec/library/digest/sha512/length_spec.rb new file mode 100644 index 0000000000..b0b4d7e56c --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/length_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::SHA512#length" do + it_behaves_like :sha512_length, :length +end + diff --git a/spec/rubyspec/library/digest/sha512/reset_spec.rb b/spec/rubyspec/library/digest/sha512/reset_spec.rb new file mode 100644 index 0000000000..b2f28dc670 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/reset_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#reset" do + + it "returns digest state to initial conditions" do + cur_digest = Digest::SHA512.new + cur_digest.update SHA512Constants::Contents + cur_digest.digest().should_not == SHA512Constants::BlankDigest + cur_digest.reset + cur_digest.digest().should == SHA512Constants::BlankDigest + end + +end + diff --git a/spec/rubyspec/library/digest/sha512/shared/constants.rb b/spec/rubyspec/library/digest/sha512/shared/constants.rb new file mode 100644 index 0000000000..80f5b7bc1d --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/shared/constants.rb @@ -0,0 +1,17 @@ +# -*- encoding: binary -*- + +require 'digest/sha2' + +module SHA512Constants + + Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + + Klass = ::Digest::SHA512 + BlockLength = 128 + DigestLength = 64 + BlankDigest = "\317\203\3415~\357\270\275\361T(P\326m\200\a\326 \344\005\vW\025\334\203\364\251!\323l\351\316G\320\321<]\205\362\260\377\203\030\322\207~\354/c\2711\275GAz\201\24582z\371'\332>" + Digest = "\241\231\232\365\002z\241\331\242\310=\367F\272\004\326\331g\315n\251Q\222\250\374E\257\254=\325\225\003SM\350\244\234\220\233=\031\230A;\000\203\233\340\323t\333\271\222w\266\307\2678\344\255j\003\216\300" + BlankHexdigest = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" + Hexdigest = "a1999af5027aa1d9a2c83df746ba04d6d967cd6ea95192a8fc45afac3dd59503534de8a49c909b3d1998413b00839be0d374dbb99277b6c7b738e4ad6a038ec0" + +end diff --git a/spec/rubyspec/library/digest/sha512/shared/length.rb b/spec/rubyspec/library/digest/sha512/shared/length.rb new file mode 100644 index 0000000000..c0609d5386 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/shared/length.rb @@ -0,0 +1,8 @@ +describe :sha512_length, shared: true do + it "returns the length of the digest" do + cur_digest = Digest::SHA512.new + cur_digest.send(@method).should == SHA512Constants::BlankDigest.size + cur_digest << SHA512Constants::Contents + cur_digest.send(@method).should == SHA512Constants::Digest.size + end +end diff --git a/spec/rubyspec/library/digest/sha512/shared/update.rb b/spec/rubyspec/library/digest/sha512/shared/update.rb new file mode 100644 index 0000000000..ca74dbf4df --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/shared/update.rb @@ -0,0 +1,7 @@ +describe :sha512_update, shared: true do + it "can update" do + cur_digest = Digest::SHA512.new + cur_digest.send @method, SHA512Constants::Contents + cur_digest.digest.should == SHA512Constants::Digest + end +end diff --git a/spec/rubyspec/library/digest/sha512/size_spec.rb b/spec/rubyspec/library/digest/sha512/size_spec.rb new file mode 100644 index 0000000000..a882fe55a1 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/size_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "Digest::SHA512#size" do + it_behaves_like :sha512_length, :size +end + diff --git a/spec/rubyspec/library/digest/sha512/to_s_spec.rb b/spec/rubyspec/library/digest/sha512/to_s_spec.rb new file mode 100644 index 0000000000..b45f553e8c --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/to_s_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) + +describe "Digest::SHA512#to_s" do + + it "returns a hexdigest" do + cur_digest = Digest::SHA512.new + cur_digest.to_s.should == SHA512Constants::BlankHexdigest + end + + it "does not change the internal state" do + cur_digest = Digest::SHA512.new + cur_digest.to_s.should == SHA512Constants::BlankHexdigest + cur_digest.to_s.should == SHA512Constants::BlankHexdigest + + cur_digest << SHA512Constants::Contents + cur_digest.to_s.should == SHA512Constants::Hexdigest + cur_digest.to_s.should == SHA512Constants::Hexdigest + end + +end diff --git a/spec/rubyspec/library/digest/sha512/update_spec.rb b/spec/rubyspec/library/digest/sha512/update_spec.rb new file mode 100644 index 0000000000..3b82f51853 --- /dev/null +++ b/spec/rubyspec/library/digest/sha512/update_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require File.expand_path('../shared/update', __FILE__) + +describe "Digest::SHA512#update" do + it_behaves_like :sha512_update, :update +end diff --git a/spec/rubyspec/library/drb/config_spec.rb b/spec/rubyspec/library/drb/config_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/config_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/current_server_spec.rb b/spec/rubyspec/library/drb/current_server_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/current_server_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/__drbref_spec.rb b/spec/rubyspec/library/drb/drbobject/__drbref_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/__drbref_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/__drburi_spec.rb b/spec/rubyspec/library/drb/drbobject/__drburi_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/__drburi_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/_dump_spec.rb b/spec/rubyspec/library/drb/drbobject/_dump_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/_dump_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/_load_spec.rb b/spec/rubyspec/library/drb/drbobject/_load_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/_load_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/eql_spec.rb b/spec/rubyspec/library/drb/drbobject/eql_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/eql_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/equal_value_spec.rb b/spec/rubyspec/library/drb/drbobject/equal_value_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/equal_value_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/hash_spec.rb b/spec/rubyspec/library/drb/drbobject/hash_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/hash_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/method_missing_spec.rb b/spec/rubyspec/library/drb/drbobject/method_missing_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/method_missing_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/new_spec.rb b/spec/rubyspec/library/drb/drbobject/new_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/new_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/new_with_spec.rb b/spec/rubyspec/library/drb/drbobject/new_with_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/new_with_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/new_with_uri_spec.rb b/spec/rubyspec/library/drb/drbobject/new_with_uri_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/new_with_uri_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/prepare_backtrace_spec.rb b/spec/rubyspec/library/drb/drbobject/prepare_backtrace_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/prepare_backtrace_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/pretty_print_cycle_spec.rb b/spec/rubyspec/library/drb/drbobject/pretty_print_cycle_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/pretty_print_cycle_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/pretty_print_spec.rb b/spec/rubyspec/library/drb/drbobject/pretty_print_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/pretty_print_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/respond_to_spec.rb b/spec/rubyspec/library/drb/drbobject/respond_to_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/respond_to_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/drbobject/with_friend_spec.rb b/spec/rubyspec/library/drb/drbobject/with_friend_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/drb/drbobject/with_friend_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/fetch_server_spec.rb b/spec/rubyspec/library/drb/fetch_server_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/fetch_server_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/fixtures/test_server.rb b/spec/rubyspec/library/drb/fixtures/test_server.rb new file mode 100644 index 0000000000..9d412f4ac7 --- /dev/null +++ b/spec/rubyspec/library/drb/fixtures/test_server.rb @@ -0,0 +1,8 @@ +class TestServer + def add(*args) + args.inject {|n,v| n+v} + end + def add_yield(x) + return (yield x)+1 + end +end diff --git a/spec/rubyspec/library/drb/front_spec.rb b/spec/rubyspec/library/drb/front_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/front_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/here_spec.rb b/spec/rubyspec/library/drb/here_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/here_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/install_acl_spec.rb b/spec/rubyspec/library/drb/install_acl_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/install_acl_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/install_id_conv_spec.rb b/spec/rubyspec/library/drb/install_id_conv_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/install_id_conv_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/primary_server_spec.rb b/spec/rubyspec/library/drb/primary_server_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/primary_server_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/regist_server_spec.rb b/spec/rubyspec/library/drb/regist_server_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/regist_server_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/remove_server_spec.rb b/spec/rubyspec/library/drb/remove_server_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/remove_server_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/start_service_spec.rb b/spec/rubyspec/library/drb/start_service_spec.rb new file mode 100644 index 0000000000..e3ef48b3e3 --- /dev/null +++ b/spec/rubyspec/library/drb/start_service_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/test_server', __FILE__) +require 'drb' + +describe "DRb.start_service" do + before :all do + @port = 9001 + (Process.pid & 7 ) + end + + before :each do + @url = "druby://localhost:#{@port}" + @port += 1 + end + + it "runs a basic remote call" do + lambda { DRb.current_server }.should raise_error(DRb::DRbServerNotFound) + server = DRb.start_service(@url, TestServer.new) + DRb.current_server.should == server + obj = DRbObject.new(nil, @url) + obj.add(1,2,3).should == 6 + DRb.stop_service + lambda { DRb.current_server }.should raise_error(DRb::DRbServerNotFound) + end + + it "runs a basic remote call passing a block" do + lambda { DRb.current_server }.should raise_error(DRb::DRbServerNotFound) + server = DRb.start_service(@url, TestServer.new) + DRb.current_server.should == server + obj = DRbObject.new(nil, @url) + obj.add_yield(2) do |i| + i.should == 2 + i+1 + end.should == 4 + DRb.stop_service + lambda { DRb.current_server }.should raise_error(DRb::DRbServerNotFound) + end +end diff --git a/spec/rubyspec/library/drb/stop_service_spec.rb b/spec/rubyspec/library/drb/stop_service_spec.rb new file mode 100644 index 0000000000..234cf5a588 --- /dev/null +++ b/spec/rubyspec/library/drb/stop_service_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/test_server', __FILE__) +require 'drb' + +describe "DRb.stop_service" do + before :all do + # for concurrent processes + @port = 9001 + (Process.pid & 7 ) + end + + before :each do + # because each spec needs it's own port since DRb is broken that way as exhibited below + @url = "druby://localhost:#{@port}" + @port += 1 + end + + it "clears the port so a new server can start" do + 10.times do + server = nil + lambda { server = DRb.start_service(@url, TestServer.new) }.should_not raise_error + DRb.current_server.should == server + lambda { DRb.stop_service }.should_not raise_error + end + end +end diff --git a/spec/rubyspec/library/drb/thread_spec.rb b/spec/rubyspec/library/drb/thread_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/thread_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/to_id_spec.rb b/spec/rubyspec/library/drb/to_id_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/to_id_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/to_obj_spec.rb b/spec/rubyspec/library/drb/to_obj_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/to_obj_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/drb/uri_spec.rb b/spec/rubyspec/library/drb/uri_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/drb/uri_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/erb/def_class_spec.rb b/spec/rubyspec/library/erb/def_class_spec.rb new file mode 100644 index 0000000000..ae2dcbd1e4 --- /dev/null +++ b/spec/rubyspec/library/erb/def_class_spec.rb @@ -0,0 +1,29 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB#def_class" do + + it "return an unnamed class which has instance method to render eRuby script" do + input = <<'END' +@arg1=<%=@arg1.inspect%> +@arg2=<%=@arg2.inspect%> +END + expected = <<'END' +@arg1="foo" +@arg2=123 +END + class MyClass1ForErb_ + def initialize(arg1, arg2) + @arg1 = arg1; @arg2 = arg2 + end + end + filename = 'example.rhtml' + #erb = ERB.new(File.read(filename)) + erb = ERB.new(input) + erb.filename = filename + MyClass1ForErb = erb.def_class(MyClass1ForErb_, 'render()') + MyClass1ForErb.method_defined?(:render).should == true + MyClass1ForErb.new('foo', 123).render().should == expected + end + +end diff --git a/spec/rubyspec/library/erb/def_method_spec.rb b/spec/rubyspec/library/erb/def_method_spec.rb new file mode 100644 index 0000000000..e4ddedea4c --- /dev/null +++ b/spec/rubyspec/library/erb/def_method_spec.rb @@ -0,0 +1,26 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB#def_method" do + + it "define module's instance method to render eRuby file" do + input = <<'END' +arg1=<%= arg1.inspect %> +arg2=<%= arg2.inspect %> +END + expected = <<'END' +arg1="foo" +arg2=123 +END + # + filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml + #erb = ERB.new(File.read(filename)) + erb = ERB.new(input) + class MyClass0ForErb + end + erb.def_method(MyClass0ForErb, 'render(arg1, arg2)', filename) + MyClass0ForErb.method_defined?(:render) + MyClass0ForErb.new.render('foo', 123).should == expected + end + +end diff --git a/spec/rubyspec/library/erb/def_module_spec.rb b/spec/rubyspec/library/erb/def_module_spec.rb new file mode 100644 index 0000000000..ed52fdfc15 --- /dev/null +++ b/spec/rubyspec/library/erb/def_module_spec.rb @@ -0,0 +1,27 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB#def_module" do + + it "return unnamed module which has instance method to render eRuby" do + input = <<'END' +arg1=<%= arg1.inspect %> +arg2=<%= arg2.inspect %> +END + expected = <<'END' +arg1="foo" +arg2=123 +END + filename = 'example.rhtml' + #erb = ERB.new(File.read(filename)) + erb = ERB.new(input) + erb.filename = filename + MyModule2ForErb = erb.def_module('render(arg1, arg2)') + MyModule2ForErb.method_defined?(':render') + class MyClass2ForErb + include MyModule2ForErb + end + MyClass2ForErb.new.render('foo', 123).should == expected + end + +end diff --git a/spec/rubyspec/library/erb/defmethod/def_erb_method_spec.rb b/spec/rubyspec/library/erb/defmethod/def_erb_method_spec.rb new file mode 100644 index 0000000000..e1eca2fbef --- /dev/null +++ b/spec/rubyspec/library/erb/defmethod/def_erb_method_spec.rb @@ -0,0 +1,63 @@ +require 'erb' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "ERB::DefMethod.def_erb_method" do + + + input = <<'END' +<% for item in @items %> +<%= item %> +<% end %> +END + + + it "define method to render eRuby file as an instance method of current module" do + expected = <<'END' + +10 + +20 + +30 + +END + # + begin + file = tmp('_example.rhtml') + File.open(file, 'w') {|f| f.write(input) } + klass = Class.new do + extend ERB::DefMethod + def_erb_method('render()', file) + def initialize(items) + @items = items + end + end + klass.new([10,20,30]).render().should == expected + ensure + rm_r file + end + + end + + + it "define method to render eRuby object as an instance method of current module" do + expected = <<'END' +10 +20 +30 +END + # + MY_INPUT4_FOR_ERB = input + class MyClass4ForErb + extend ERB::DefMethod + erb = ERB.new(MY_INPUT4_FOR_ERB, nil, '<>') + def_erb_method('render()', erb) + def initialize(items) + @items = items + end + end + MyClass4ForErb.new([10,20,30]).render().should == expected + end + + +end diff --git a/spec/rubyspec/library/erb/filename_spec.rb b/spec/rubyspec/library/erb/filename_spec.rb new file mode 100644 index 0000000000..4615f5d808 --- /dev/null +++ b/spec/rubyspec/library/erb/filename_spec.rb @@ -0,0 +1,40 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB#filename" do + it "raises an exception if there are errors processing content" do + filename = 'foobar.rhtml' + erb = ERB.new('<% if true %>') # will raise SyntaxError + erb.filename = filename + lambda { + begin + erb.result(binding) + rescue Exception => e + @ex = e + raise e + end + }.should raise_error(SyntaxError) + expected = filename + + @ex.message =~ /^(.*?):(\d+): / + $1.should == expected + $2.to_i.should == 1 + end + + it "uses '(erb)' as filename when filename is not set" do + erb = ERB.new('<% if true %>') # will raise SyntaxError + lambda { + begin + erb.result(binding) + rescue Exception => e + @ex = e + raise e + end + }.should raise_error(SyntaxError) + expected = '(erb)' + + @ex.message =~ /^(.*?):(\d+): / + $1.should == expected + $2.to_i.should == 1 + end +end diff --git a/spec/rubyspec/library/erb/new_spec.rb b/spec/rubyspec/library/erb/new_spec.rb new file mode 100644 index 0000000000..917fd470b7 --- /dev/null +++ b/spec/rubyspec/library/erb/new_spec.rb @@ -0,0 +1,132 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB.new" do + before :all do + @eruby_str = <<'END' +
      +<% list = [1,2,3] %> +<% for item in list %> +<% if item %> +
    • <%= item %>
    • +<% end %> +<% end %> +
    +END + + @eruby_str2 = <<'END' +
      +% list = [1,2,3] +%for item in list +% if item +
    • <%= item %> + <% end %> +<% end %> +
    +%%% +END + + end + + it "compiles eRuby script into ruby code when trim mode is 0 or not specified" do + expected = "
      \n\n\n\n
    • 1
    • \n\n\n\n
    • 2
    • \n\n\n\n
    • 3
    • \n\n\n
    \n" + [0, '', nil].each do |trim_mode| + ERB.new(@eruby_str, nil, trim_mode).result.should == expected + end + end + + it "removes '\n' when trim_mode is 1 or '>'" do + expected = "
      \n
    • 1
    • \n
    • 2
    • \n
    • 3
    • \n
    \n" + [1, '>'].each do |trim_mode| + ERB.new(@eruby_str, nil, trim_mode).result.should == expected + end + end + + it "removes spaces at beginning of line and '\n' when trim_mode is 2 or '<>'" do + expected = "
      \n
    • 1
    • \n
    • 2
    • \n
    • 3
    • \n
    \n" + [2, '<>'].each do |trim_mode| + ERB.new(@eruby_str, nil, trim_mode).result.should == expected + end + end + + it "removes spaces around '<%- -%>' when trim_mode is '-'" do + expected = "
      \n
    • 1
    • 2
    • 3
    \n" + input = <<'END' +
      +<%- for item in [1,2,3] -%> + <%- if item -%> +
    • <%= item -%> + <%- end -%> +<%- end -%> +
    +END + + ERB.new(input, nil, '-').result.should == expected + end + + + it "not support '<%-= expr %> even when trim_mode is '-'" do + + input = <<'END' +

    + <%= expr -%> + <%-= expr -%> +

    +END + + lambda { ERB.new(input, nil, '-').result }.should raise_error + end + + it "regards lines starting with '%' as '<% ... %>' when trim_mode is '%'" do + expected = "
      \n
    • 1\n \n
    • 2\n \n
    • 3\n \n\n
    \n%%\n" + ERB.new(@eruby_str2, nil, "%").result.should == expected + end + it "regards lines starting with '%' as '<% ... %>' and remove \"\\n\" when trim_mode is '%>'" do + expected = "
      \n
    • 1
    • 2
    • 3
    \n%%\n" + ERB.new(@eruby_str2, nil, '%>').result.should == expected + end + + + it "regard lines starting with '%' as '<% ... %>' and remove \"\\n\" when trim_mode is '%<>'" do + expected = "
      \n
    • 1\n \n
    • 2\n \n
    • 3\n \n
    \n%%\n" + ERB.new(@eruby_str2, nil, '%<>').result.should == expected + end + + + it "regard lines starting with '%' as '<% ... %>' and spaces around '<%- -%>' when trim_mode is '%-'" do + expected = "
      \n
    • 1
    • \n
    • 2
    • \n
    \n%%\n" + input = <<'END' +
      +%list = [1,2] +%for item in list +
    • <%= item %>
    • +<% end %>
    +%%% +END + + ERB.new(input, nil, '%-').result.should == expected + end + + it "changes '_erbout' variable name in the produced source" do + input = @eruby_str + match_erbout = ERB.new(input, nil, nil).src + match_buf = ERB.new(input, nil, nil, 'buf').src + match_erbout.gsub("_erbout", "buf").should == match_buf + end + + + it "ignores '<%# ... %>'" do + input = <<'END' +<%# for item in list %> +<%#= item %> +<%# end %> +END + ERB.new(input).result.should == "\n\n\n" + ERB.new(input, nil, '<>').result.should == "\n" + end + + it "forget local variables defined previous one" do + ERB.new(@eruby_str).result + lambda{ ERB.new("<%= list %>").result }.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/library/erb/result_spec.rb b/spec/rubyspec/library/erb/result_spec.rb new file mode 100644 index 0000000000..bc429da381 --- /dev/null +++ b/spec/rubyspec/library/erb/result_spec.rb @@ -0,0 +1,86 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB#result" do + + + it "return the result of compiled ruby code" do + input = <<'END' +
      +<% for item in list %> +
    • <%= item %> +<% end %> +
    +END + expected = <<'END' +
      + +
    • AAA + +
    • BBB + +
    • CCC + +
    +END + erb = ERB.new(input) + list = %w[AAA BBB CCC] + actual = erb.result(binding) + actual.should == expected + end + + + it "share local variables" do + input = "<% var = 456 %>" + expected = 456 + var = 123 + ERB.new(input).result(binding) + var.should == expected + end + + + it "is not able to h() or u() unless including ERB::Util" do + input = "<%=h '<>' %>" + lambda { + ERB.new(input).result() + }.should raise_error(NameError) + end + + + it "is able to h() or u() if ERB::Util is included" do + class MyERB1 + include ERB::Util + def main + input = "<%=h '<>' %>" + return ERB.new(input).result(binding) + end + end + expected = '<>' + actual = MyERB1.new.main() + actual.should == expected + end + + + it "use TOPLEVEL_BINDING if binding is not passed" do + class MyERB2 + include ERB::Util + def main1 + #input = "<%= binding.to_s %>" + input = "<%= _xxx_var_ %>" + return ERB.new(input).result() + end + def main2 + input = "<%=h '<>' %>" + return ERB.new(input).result() + end + end + + eval '_xxx_var_ = 123', TOPLEVEL_BINDING + expected = '123' + MyERB2.new.main1().should == expected + + lambda { + MyERB2.new.main2() + }.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/library/erb/run_spec.rb b/spec/rubyspec/library/erb/run_spec.rb new file mode 100644 index 0000000000..3110aac1b1 --- /dev/null +++ b/spec/rubyspec/library/erb/run_spec.rb @@ -0,0 +1,97 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB#run" do + # TODO: what is this? why does it not use + # lambda { ... }.should output + def _steal_stdout + orig = $stdout + s = '' + def s.write(arg); self << arg.to_s; end + $stdout = s + begin + yield + ensure + $stdout = orig + end + return s + end + + it "print the result of compiled ruby code" do + input = < +<% for item in list %> +
  • <%= item %> +<% end %> + +END + expected = < + +
  • AAA + +
  • BBB + +
  • CCC + + +END + erb = ERB.new(input) + list = %w[AAA BBB CCC] + actual = _steal_stdout { erb.run(binding) } + actual.should == expected + end + + it "share local variables" do + input = "<% var = 456 %>" + expected = 456 + var = 123 + _steal_stdout { ERB.new(input).run(binding) } + var.should == expected + end + + it "is not able to h() or u() unless including ERB::Util" do + input = "<%=h '<>' %>" + lambda { + _steal_stdout { ERB.new(input).run() } + }.should raise_error(NameError) + end + + it "is able to h() or u() if ERB::Util is included" do + class MyERB1 + include ERB::Util + def main + input = "<%=h '<>' %>" + ERB.new(input).run(binding) + end + end + expected = '<>' + actual = _steal_stdout { MyERB1.new.main() } + actual.should == expected + end + + it "use TOPLEVEL_BINDING if binding is not passed" do + class MyERB2 + include ERB::Util + def main1 + #input = "<%= binding.to_s %>" + input = "<%= _xxx_var_ %>" + return ERB.new(input).run() + end + def main2 + input = "<%=h '<>' %>" + return ERB.new(input).run() + end + end + + eval '_xxx_var_ = 123', TOPLEVEL_BINDING + expected = '123' + actual = _steal_stdout { MyERB2.new.main1() } + actual.should == expected + + lambda { + _steal_stdout { MyERB2.new.main2() } + }.should raise_error(NameError) + end +end + diff --git a/spec/rubyspec/library/erb/src_spec.rb b/spec/rubyspec/library/erb/src_spec.rb new file mode 100644 index 0000000000..cf5b67dcbf --- /dev/null +++ b/spec/rubyspec/library/erb/src_spec.rb @@ -0,0 +1,33 @@ +require 'erb' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "ERB#src" do + + it "returns the compiled ruby code evaluated to a String" do + # note that what concrete code is emitted is not guaranteed. + + input = <<'END' +
      +<% for item in list %> +
    • <%= item %> +<% end %> +
    +END + + expected = <<'END' +
      + +
    • AAA + +
    • BBB + +
    • CCC + +
    +END + + list = %w[AAA BBB CCC] + eval(ERB.new(input).src).should == expected + end + +end diff --git a/spec/rubyspec/library/erb/util/h_spec.rb b/spec/rubyspec/library/erb/util/h_spec.rb new file mode 100644 index 0000000000..ba36574433 --- /dev/null +++ b/spec/rubyspec/library/erb/util/h_spec.rb @@ -0,0 +1,7 @@ +require 'erb' +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/html_escape', __FILE__) + +describe "ERB::Util.h" do + it_behaves_like :erb_util_html_escape, :h +end diff --git a/spec/rubyspec/library/erb/util/html_escape_spec.rb b/spec/rubyspec/library/erb/util/html_escape_spec.rb new file mode 100644 index 0000000000..9bc9359f2c --- /dev/null +++ b/spec/rubyspec/library/erb/util/html_escape_spec.rb @@ -0,0 +1,8 @@ +require 'erb' +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/html_escape', __FILE__) + +describe "ERB::Util.html_escape" do + it_behaves_like :erb_util_html_escape, :html_escape +end + diff --git a/spec/rubyspec/library/erb/util/shared/html_escape.rb b/spec/rubyspec/library/erb/util/shared/html_escape.rb new file mode 100644 index 0000000000..71b378755e --- /dev/null +++ b/spec/rubyspec/library/erb/util/shared/html_escape.rb @@ -0,0 +1,42 @@ +describe :erb_util_html_escape, shared: true do + it "escape (& < > \" ') to (& < > " ')" do + input = '& < > " \'' + expected = '& < > " '' + ERB::Util.__send__(@method, input).should == expected + end + + it "not escape characters except (& < > \" ')" do + input = (0x20..0x7E).to_a.collect {|ch| ch.chr}.join('') + expected = input. + gsub(/&/,'&'). + gsub(//,'>'). + gsub(/'/,'''). + gsub(/"/,'"') + ERB::Util.__send__(@method, input).should == expected + end + + it "return empty string when argument is nil" do + input = nil + expected = '' + ERB::Util.__send__(@method, input).should == expected + end + + it "returns string when argument is number" do + input = 123 + expected = '123' + ERB::Util.__send__(@method, input).should == expected + input = 3.14159 + expected = '3.14159' + ERB::Util.__send__(@method, input).should == expected + end + + it "returns string when argument is boolean" do + input = true + expected = 'true' + ERB::Util.__send__(@method, input).should == expected + input = false + expected = 'false' + ERB::Util.__send__(@method, input).should == expected + end +end diff --git a/spec/rubyspec/library/erb/util/shared/url_encode.rb b/spec/rubyspec/library/erb/util/shared/url_encode.rb new file mode 100644 index 0000000000..f2c2d63080 --- /dev/null +++ b/spec/rubyspec/library/erb/util/shared/url_encode.rb @@ -0,0 +1,38 @@ +describe :erb_util_url_encode, shared: true do + it "encode characters" do + #input = (0x20..0x7E).to_a.collect{|ch| ch.chr}.join + input = " !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + expected = "%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7E" + ERB::Util.__send__(@method, input).should == expected + end + + it "encode unicode string" do + input = "http://ja.wikipedia.org/wiki/\343\203\255\343\203\240\343\202\271\343\202\253\343\203\273\343\203\221\343\203\255\343\203\273\343\202\246\343\203\253\343\203\273\343\203\251\343\203\224\343\203\245\343\202\277" + expected = 'http%3A%2F%2Fja.wikipedia.org%2Fwiki%2F%E3%83%AD%E3%83%A0%E3%82%B9%E3%82%AB%E3%83%BB%E3%83%91%E3%83%AD%E3%83%BB%E3%82%A6%E3%83%AB%E3%83%BB%E3%83%A9%E3%83%94%E3%83%A5%E3%82%BF' + ERB::Util.__send__(@method, input).should == expected + end + + it "returns empty string when argument is nil" do + input = nil + expected = '' + ERB::Util.__send__(@method, input).should == expected + end + + it "returns string when argument is number" do + input = 123 + expected = '123' + ERB::Util.__send__(@method, input).should == expected + input = 3.14159 + expected = '3.14159' + ERB::Util.__send__(@method, input).should == expected + end + + it "returns string when argument is boolean" do + input = true + expected = 'true' + ERB::Util.__send__(@method, input).should == expected + input = false + expected = 'false' + ERB::Util.__send__(@method, input).should == expected + end +end diff --git a/spec/rubyspec/library/erb/util/u_spec.rb b/spec/rubyspec/library/erb/util/u_spec.rb new file mode 100644 index 0000000000..9200244c8e --- /dev/null +++ b/spec/rubyspec/library/erb/util/u_spec.rb @@ -0,0 +1,8 @@ +require 'erb' +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/url_encode', __FILE__) + +describe "ERB::Util.u" do + it_behaves_like :erb_util_url_encode, :u +end + diff --git a/spec/rubyspec/library/erb/util/url_encode_spec.rb b/spec/rubyspec/library/erb/util/url_encode_spec.rb new file mode 100644 index 0000000000..303a2e3cd7 --- /dev/null +++ b/spec/rubyspec/library/erb/util/url_encode_spec.rb @@ -0,0 +1,7 @@ +require 'erb' +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/url_encode', __FILE__) + +describe "ERB::Util.url_encode" do + it_behaves_like :erb_util_url_encode, :url_encode +end diff --git a/spec/rubyspec/library/etc/endgrent_spec.rb b/spec/rubyspec/library/etc/endgrent_spec.rb new file mode 100644 index 0000000000..95f0dc05e3 --- /dev/null +++ b/spec/rubyspec/library/etc/endgrent_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/windows', __FILE__) +require 'etc' + +describe "Etc.endgrent" do + it_behaves_like(:etc_on_windows, :endgrent) +end diff --git a/spec/rubyspec/library/etc/endpwent_spec.rb b/spec/rubyspec/library/etc/endpwent_spec.rb new file mode 100644 index 0000000000..7ce8f1925b --- /dev/null +++ b/spec/rubyspec/library/etc/endpwent_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/windows', __FILE__) +require 'etc' + +describe "Etc.endpwent" do + it_behaves_like(:etc_on_windows, :endpwent) +end diff --git a/spec/rubyspec/library/etc/getgrent_spec.rb b/spec/rubyspec/library/etc/getgrent_spec.rb new file mode 100644 index 0000000000..96225e351a --- /dev/null +++ b/spec/rubyspec/library/etc/getgrent_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/windows', __FILE__) +require 'etc' + +describe "Etc.getgrent" do + it_behaves_like(:etc_on_windows, :getgrent) +end diff --git a/spec/rubyspec/library/etc/getgrgid_spec.rb b/spec/rubyspec/library/etc/getgrgid_spec.rb new file mode 100644 index 0000000000..9b6b283d52 --- /dev/null +++ b/spec/rubyspec/library/etc/getgrgid_spec.rb @@ -0,0 +1,70 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +platform_is :windows do + describe "Etc.getgrgid" do + it "returns nil" do + Etc.getgrgid(1).should == nil + Etc.getgrgid(nil).should == nil + Etc.getgrgid('nil').should == nil + end + end +end + +# TODO: verify these on non-windows, non-darwin OS +platform_is_not :windows do + describe "Etc.getgrgid" do + before :all do + @gid = `id -g`.strip.to_i + @name = `id -gn`.strip + end + + it "returns a Etc::Group struct instance for the given user" do + gr = Etc.getgrgid(@gid) + + gr.is_a?(Etc::Group).should == true + gr.gid.should == @gid + gr.name.should == @name + end + + it "returns the Etc::Group for a given gid if it exists" do + grp = Etc.getgrgid(@gid) + grp.should be_kind_of(Etc::Group) + grp.gid.should == @gid + grp.name.should == @name + end + + it "uses Process.gid as the default value for the argument" do + gr = Etc.getgrgid + + gr.gid.should == @gid + gr.name.should == @name + end + + it "returns the Group for a given gid if it exists" do + grp = Etc.getgrgid(@gid) + grp.should be_kind_of(Struct::Group) + grp.gid.should == @gid + grp.name.should == @name + end + + it "raises if the group does not exist" do + lambda { Etc.getgrgid(9876)}.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed an Integer" do + lambda { Etc.getgrgid("foo") }.should raise_error(TypeError) + lambda { Etc.getgrgid(nil) }.should raise_error(TypeError) + end + + it "can be called safely by multiple threads" do + 20.times.map do + Thread.new do + 100.times do + Etc.getgrgid(@gid).gid.should == @gid + end + end + end.each(&:join) + end + end +end diff --git a/spec/rubyspec/library/etc/getgrnam_spec.rb b/spec/rubyspec/library/etc/getgrnam_spec.rb new file mode 100644 index 0000000000..46193f49d6 --- /dev/null +++ b/spec/rubyspec/library/etc/getgrnam_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +platform_is :windows do + describe "Etc.getgrnam" do + it "returns nil" do + Etc.getgrnam(1).should == nil + Etc.getgrnam(nil).should == nil + Etc.getgrnam('nil').should == nil + end + end +end + +platform_is_not :windows do + describe "Etc.getgrnam" do + it "returns a Etc::Group struct instance for the given group" do + gr_name = Etc.getgrent.name + Etc.endgrent + gr = Etc.getgrnam(gr_name) + gr.is_a?(Etc::Group).should == true + end + + it "only accepts strings as argument" do + lambda { + Etc.getgrnam(123) + Etc.getgrnam(nil) + }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/library/etc/getlogin_spec.rb b/spec/rubyspec/library/etc/getlogin_spec.rb new file mode 100644 index 0000000000..ae52942a92 --- /dev/null +++ b/spec/rubyspec/library/etc/getlogin_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +describe "Etc.getlogin" do + it "returns the name associated with the current login activity" do + getlogin_null = false + + # POSIX logname(1) shows getlogin(2)'s result + # NOTE: Etc.getlogin returns ENV['USER'] if getlogin(2) returns NULL + begin + # make Etc.getlogin to return nil if getlogin(3) returns NULL + envuser, ENV['USER'] = ENV['USER'], nil + if Etc.getlogin + # Etc.getlogin returns the same result of logname(2) + # if it returns non NULL + Etc.getlogin.should == `id -un`.chomp + else + # Etc.getlogin may return nil if the login name is not set + # because of chroot or sudo or something. + Etc.getlogin.should be_nil + getlogin_null = true + end + ensure + ENV['USER'] = envuser + end + + # if getlogin(2) returns NULL, Etc.getlogin returns ENV['USER'] + if getlogin_null + Etc.getlogin.should == ENV['USER'] + end + end +end diff --git a/spec/rubyspec/library/etc/getpwent_spec.rb b/spec/rubyspec/library/etc/getpwent_spec.rb new file mode 100644 index 0000000000..1c8057c9e5 --- /dev/null +++ b/spec/rubyspec/library/etc/getpwent_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/windows', __FILE__) +require 'etc' + +describe "Etc.getpwent" do + it_behaves_like(:etc_on_windows, :getpwent) +end diff --git a/spec/rubyspec/library/etc/getpwnam_spec.rb b/spec/rubyspec/library/etc/getpwnam_spec.rb new file mode 100644 index 0000000000..8610b5da91 --- /dev/null +++ b/spec/rubyspec/library/etc/getpwnam_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +platform_is :windows do + describe "Etc.getpwnam" do + it "returns nil" do + Etc.getpwnam(1).should == nil + Etc.getpwnam(nil).should == nil + Etc.getpwnam('nil').should == nil + end + end +end + +platform_is_not :windows do + describe "Etc.getpwnam" do + it "returns a Etc::Passwd struct instance for the given user" do + pw = Etc.getpwnam(`whoami`.strip) + pw.is_a?(Etc::Passwd).should == true + end + + it "only accepts strings as argument" do + lambda { + Etc.getpwnam(123) + Etc.getpwnam(nil) + }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/library/etc/getpwuid_spec.rb b/spec/rubyspec/library/etc/getpwuid_spec.rb new file mode 100644 index 0000000000..f1c7218c0b --- /dev/null +++ b/spec/rubyspec/library/etc/getpwuid_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +platform_is :windows do + describe "Etc.getpwuid" do + it "returns nil" do + Etc.getpwuid(1).should == nil + Etc.getpwuid(nil).should == nil + Etc.getpwuid('nil').should == nil + end + end +end + +platform_is_not :windows do + describe "Etc.getpwuid" do + before :all do + @pw = Etc.getpwuid(`id -u`.strip.to_i) + end + + it "returns a Etc::Passwd struct instance for the given user" do + @pw.is_a?(Etc::Passwd).should == true + end + + it "uses Process.uid as the default value for the argument" do + pw = Etc.getpwuid + pw.should == @pw + end + + it "only accepts integers as argument" do + lambda { + Etc.getpwuid("foo") + Etc.getpwuid(nil) + }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/library/etc/group_spec.rb b/spec/rubyspec/library/etc/group_spec.rb new file mode 100644 index 0000000000..8b92cb7bf0 --- /dev/null +++ b/spec/rubyspec/library/etc/group_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/windows', __FILE__) +require 'etc' + +describe "Etc.group" do + it_behaves_like(:etc_on_windows, :group) + + platform_is_not :windows do + it "raises a RuntimeError for parallel iteration" do + proc { + Etc.group do | group | + Etc.group do | group2 | + end + end + }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/rubyspec/library/etc/nprocessors_spec.rb b/spec/rubyspec/library/etc/nprocessors_spec.rb new file mode 100644 index 0000000000..bce11d06c5 --- /dev/null +++ b/spec/rubyspec/library/etc/nprocessors_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +ruby_version_is "2.2" do + describe "Etc.nprocessors" do + it "returns the number of online processors" do + Etc.nprocessors.should be_kind_of(Integer) + Etc.nprocessors.should >= 1 + end + end +end diff --git a/spec/rubyspec/library/etc/shared/windows.rb b/spec/rubyspec/library/etc/shared/windows.rb new file mode 100644 index 0000000000..8bae235199 --- /dev/null +++ b/spec/rubyspec/library/etc/shared/windows.rb @@ -0,0 +1,7 @@ +describe :etc_on_windows, shared: true do + platform_is :windows do + it "returns nil" do + Etc.send(@method).should == nil + end + end +end diff --git a/spec/rubyspec/library/etc/struct_group_spec.rb b/spec/rubyspec/library/etc/struct_group_spec.rb new file mode 100644 index 0000000000..c33a177a98 --- /dev/null +++ b/spec/rubyspec/library/etc/struct_group_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +describe "Struct::Group" do + platform_is_not :windows do + before :all do + @g = Etc.getgrgid(`id -g`.strip.to_i) + end + + it "returns group name" do + @g.name.should == `id -gn`.strip + end + + it "returns group password" do + @g.passwd.is_a?(String).should == true + end + + it "returns group id" do + @g.gid.should == `id -g`.strip.to_i + end + + it "returns an array of users belonging to the group" do + @g.mem.is_a?(Array).should == true + end + + it "can be compared to another object" do + (@g == nil).should == false + (@g == Object.new).should == false + end + end +end diff --git a/spec/rubyspec/library/etc/struct_passwd_spec.rb b/spec/rubyspec/library/etc/struct_passwd_spec.rb new file mode 100644 index 0000000000..3e4bfee828 --- /dev/null +++ b/spec/rubyspec/library/etc/struct_passwd_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'etc' + +describe "Struct::Passwd" do + platform_is_not :windows do + before :all do + @pw = Etc.getpwuid(`id -u`.strip.to_i) + end + + it "returns user name" do + @pw.name.should == `id -un`.strip + end + + it "returns user password" do + @pw.passwd.is_a?(String).should == true + end + + it "returns user id" do + @pw.uid.should == `id -u`.strip.to_i + end + + it "returns user group id" do + @pw.gid.should == `id -g`.strip.to_i + end + + it "returns user personal information (gecos field)" do + @pw.gecos.is_a?(String).should == true + end + + it "returns user home directory" do + @pw.dir.is_a?(String).should == true + end + + it "returns user shell" do + @pw.shell.is_a?(String).should == true + end + + it "can be compared to another object" do + (@pw == nil).should == false + (@pw == Object.new).should == false + end + end +end diff --git a/spec/rubyspec/library/expect/expect_spec.rb b/spec/rubyspec/library/expect/expect_spec.rb new file mode 100644 index 0000000000..080f3d7af4 --- /dev/null +++ b/spec/rubyspec/library/expect/expect_spec.rb @@ -0,0 +1,62 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'expect' + + describe "IO#expect" do + before :each do + @read, @write = IO.pipe + end + + after :each do + @read.close unless @read.closed? + @write.close unless @write.closed? + end + + it "matches data against a Regexp" do + @write << "prompt> hello" + + result = @read.expect(/[pf]rompt>/) + result.should == ["prompt>"] + end + + it "matches data against a String" do + @write << "prompt> hello" + + result = @read.expect("prompt>") + result.should == ["prompt>"] + end + + it "returns any captures of the Regexp" do + @write << "prompt> hello" + + result = @read.expect(/(pro)mpt(>)/) + result.should == ["prompt>", "pro", ">"] + end + + it "returns raises IOError if the IO is closed" do + @write << "prompt> hello" + @read.close + + lambda { + @read.expect("hello") + }.should raise_error(IOError) + end + + it "returns nil if eof is hit" do + @write << "pro" + @write.close + + @read.expect("prompt").should be_nil + end + + it "yields the result if a block is given" do + @write << "prompt> hello" + + res = nil + + @read.expect("prompt>") { |x| res = x } + + res.should == ["prompt>"] + end + end +end diff --git a/spec/rubyspec/library/fiber/alive_spec.rb b/spec/rubyspec/library/fiber/alive_spec.rb new file mode 100644 index 0000000000..fbd5c4bc1a --- /dev/null +++ b/spec/rubyspec/library/fiber/alive_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :fiber_library do + require 'fiber' + + describe "Fiber#alive?" do + it "returns true for a Fiber that hasn't had #resume called" do + fiber = Fiber.new { true } + fiber.alive?.should be_true + end + + # FIXME: Better description? + it "returns true for a Fiber that's yielded to the caller" do + fiber = Fiber.new { Fiber.yield } + fiber.resume + fiber.alive?.should be_true + end + + it "returns true when called from its Fiber" do + fiber = Fiber.new { fiber.alive?.should be_true } + fiber.resume + end + + it "doesn't invoke the block associated with the Fiber" do + offthehook = mock('do not call') + offthehook.should_not_receive(:ring) + fiber = Fiber.new { offthehook.ring } + fiber.alive? + end + + it "returns false for a Fiber that's dead" do + fiber = Fiber.new { true } + fiber.resume + lambda { fiber.resume }.should raise_error(FiberError) + fiber.alive?.should be_false + end + + it "always returns false for a dead Fiber" do + fiber = Fiber.new { true } + fiber.resume + lambda { fiber.resume }.should raise_error(FiberError) + fiber.alive?.should be_false + lambda { fiber.resume }.should raise_error(FiberError) + fiber.alive?.should be_false + fiber.alive?.should be_false + end + end +end diff --git a/spec/rubyspec/library/fiber/current_spec.rb b/spec/rubyspec/library/fiber/current_spec.rb new file mode 100644 index 0000000000..48345fb7cf --- /dev/null +++ b/spec/rubyspec/library/fiber/current_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :fiber_library do + require 'fiber' + + describe "Fiber.current" do + it "returns the root Fiber when called outside of a Fiber" do + root = Fiber.current + root.should be_an_instance_of(Fiber) + # We can always transfer to the root Fiber; it will never die + 5.times do + root.transfer.should be_nil + root.alive?.should_not be_false #Workaround for bug #1547 + end + end + + it "returns the current Fiber when called from a Fiber" do + fiber = Fiber.new do + this = Fiber.current + this.should be_an_instance_of(Fiber) + this.should == fiber + this.alive?.should_not be_false # Workaround for bug #1547 + end + fiber.resume + end + + it "returns the current Fiber when called from a Fiber that transferred to another" do + + states = [] + fiber = Fiber.new do + states << :fiber + this = Fiber.current + this.should be_an_instance_of(Fiber) + this.should === fiber + this.alive?.should_not be_false # Workaround for bug #1547 + end + + fiber2 = Fiber.new do + states << :fiber2 + fiber.transfer + this = Fiber.current + this.should be_an_instance_of(Fiber) + this.should === fiber2 + this.alive?.should_not be_false # Workaround for bug #1547 + end + + fiber3 = Fiber.new do + states << :fiber3 + fiber2.transfer + this = Fiber.current + this.should be_an_instance_of(Fiber) + this.should === fiber3 + this.alive?.should_not be_false # Workaround for bug #1547 + fiber2.transfer + end + + fiber3.resume + states.should == [:fiber3, :fiber2, :fiber] + end + end +end diff --git a/spec/rubyspec/library/fiber/resume_spec.rb b/spec/rubyspec/library/fiber/resume_spec.rb new file mode 100644 index 0000000000..8709d01142 --- /dev/null +++ b/spec/rubyspec/library/fiber/resume_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +with_feature :fiber_library do + require 'fiber' + + describe "Fiber#resume" do + it "raises a FiberError if the Fiber has transfered control to another Fiber" do + fiber1 = Fiber.new { true } + fiber2 = Fiber.new { fiber1.transfer; Fiber.yield } + fiber2.resume + lambda { fiber2.resume }.should raise_error(FiberError) + end + end +end diff --git a/spec/rubyspec/library/fiber/transfer_spec.rb b/spec/rubyspec/library/fiber/transfer_spec.rb new file mode 100644 index 0000000000..79319a7d39 --- /dev/null +++ b/spec/rubyspec/library/fiber/transfer_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/fiber/resume', __FILE__) + +with_feature :fiber_library do + require 'fiber' + + describe "Fiber#transfer" do + it_behaves_like :fiber_resume, :transfer + end + + describe "Fiber#transfer" do + it "transfers control from one Fiber to another when called from a Fiber" do + fiber1 = Fiber.new { :fiber1 } + fiber2 = Fiber.new { fiber1.transfer; :fiber2 } + fiber2.resume.should == :fiber1 + end + + it "returns to the root Fiber when finished" do + f1 = Fiber.new { :fiber_1 } + f2 = Fiber.new { f1.transfer; :fiber_2 } + + f2.transfer.should == :fiber_1 + f2.transfer.should == :fiber_2 + end + + it "can be invoked from the same Fiber it transfers control to" do + states = [] + fiber = Fiber.new { states << :start; fiber.transfer; states << :end } + fiber.transfer + states.should == [:start, :end] + + states = [] + fiber = Fiber.new { states << :start; fiber.transfer; states << :end } + fiber.resume + states.should == [:start, :end] + end + + it "can transfer control to a Fiber that has transfered to another Fiber" do + states = [] + fiber1 = Fiber.new { states << :fiber1 } + fiber2 = Fiber.new { states << :fiber2_start; fiber1.transfer; states << :fiber2_end} + fiber2.resume.should == [:fiber2_start, :fiber1] + fiber2.transfer.should == [:fiber2_start, :fiber1, :fiber2_end] + end + + it "raises a FiberError when transferring to a Fiber which resumes itself" do + fiber = Fiber.new { fiber.resume } + lambda { fiber.transfer }.should raise_error(FiberError) + end + end +end diff --git a/spec/rubyspec/library/find/find_spec.rb b/spec/rubyspec/library/find/find_spec.rb new file mode 100644 index 0000000000..14c9e2926e --- /dev/null +++ b/spec/rubyspec/library/find/find_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require 'find' + +describe "Find.find" do + before :each do + FindDirSpecs.create_mock_dirs + end + + after :each do + FindDirSpecs.delete_mock_dirs + end + + describe "when called without a block" do + it "returns an Enumerator" do + Find.find(FindDirSpecs.mock_dir).should be_an_instance_of(Enumerator) + Find.find(FindDirSpecs.mock_dir).to_a.sort.should == FindDirSpecs.expected_paths + end + end + + it "should recursively yield every file in the directory" do + a = [] + + Find.find(FindDirSpecs.mock_dir) do |file| + a << file + end + + a.sort.should == FindDirSpecs.expected_paths + end +end diff --git a/spec/rubyspec/library/find/fixtures/common.rb b/spec/rubyspec/library/find/fixtures/common.rb new file mode 100644 index 0000000000..14a7edb09a --- /dev/null +++ b/spec/rubyspec/library/find/fixtures/common.rb @@ -0,0 +1,174 @@ +module FindDirSpecs + def self.mock_dir(dirs = ['find_specs_mock']) + @mock_dir ||= tmp("") + File.join @mock_dir, dirs + end + + # The names of the fixture directories and files used by + # various Find specs. + def self.mock_dir_files + unless @mock_dir_files + @mock_dir_files = %w[ + .dotfile + .dotsubdir/.dotfile + .dotsubdir/nondotfile + + deeply/.dotfile + deeply/nested/.dotfile.ext + deeply/nested/directory/structure/.ext + deeply/nested/directory/structure/bar + deeply/nested/directory/structure/baz + deeply/nested/directory/structure/file_one + deeply/nested/directory/structure/file_one.ext + deeply/nested/directory/structure/foo + deeply/nondotfile + + file_one.ext + file_two.ext + + dir_filename_ordering + dir/filename_ordering + + nondotfile + + subdir_one/.dotfile + subdir_one/nondotfile + subdir_two/nondotfile + subdir_two/nondotfile.ext + + brace/a + brace/a.js + brace/a.erb + brace/a.js.rjs + brace/a.html.erb + + special/+ + + special/^ + special/$ + + special/( + special/) + special/[ + special/] + special/{ + special/} + + special/test{1}/file[1] + ] + + platform_is_not :windows do + @mock_dir_files += %w[ + special/* + special/? + + special/| + ] + end + end + + @mock_dir_files + end + + def self.create_mock_dirs + umask = File.umask 0 + mock_dir_files.each do |name| + file = File.join mock_dir, name + mkdir_p File.dirname(file) + touch file + end + File.umask umask + end + + def self.delete_mock_dirs + rm_r mock_dir + end + + def self.expected_paths + unless @expected_paths + @expected_paths = %w[ + .dotfile + + .dotsubdir + .dotsubdir/.dotfile + .dotsubdir/nondotfile + + deeply + deeply/.dotfile + + deeply/nested + deeply/nested/.dotfile.ext + + deeply/nested/directory + + deeply/nested/directory/structure + deeply/nested/directory/structure/.ext + deeply/nested/directory/structure/bar + deeply/nested/directory/structure/baz + deeply/nested/directory/structure/file_one + deeply/nested/directory/structure/file_one.ext + deeply/nested/directory/structure/foo + deeply/nondotfile + + file_one.ext + file_two.ext + + dir_filename_ordering + + dir + dir/filename_ordering + + nondotfile + + subdir_one + subdir_one/.dotfile + subdir_one/nondotfile + + subdir_two + subdir_two/nondotfile + subdir_two/nondotfile.ext + + brace + brace/a + brace/a.js + brace/a.erb + brace/a.js.rjs + brace/a.html.erb + + special + special/+ + + special/^ + special/$ + + special/( + special/) + special/[ + special/] + special/{ + special/} + + special/test{1} + special/test{1}/file[1] + ] + + platform_is_not :windows do + @expected_paths += %w[ + special/* + special/? + + special/| + ] + end + + @expected_paths.map! do |file| + File.join(mock_dir, file) + end + + @expected_paths << mock_dir + @expected_paths.sort! + end + + @expected_paths + end +end diff --git a/spec/rubyspec/library/find/prune_spec.rb b/spec/rubyspec/library/find/prune_spec.rb new file mode 100644 index 0000000000..1aef7d3d23 --- /dev/null +++ b/spec/rubyspec/library/find/prune_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'find' + +describe "Find.prune" do + it "should throw :prune" do + msg = catch(:prune) do + Find.prune + end + + msg.should == nil + end +end diff --git a/spec/rubyspec/library/getoptlong/each_option_spec.rb b/spec/rubyspec/library/getoptlong/each_option_spec.rb new file mode 100644 index 0000000000..c58815bfa9 --- /dev/null +++ b/spec/rubyspec/library/getoptlong/each_option_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' +require File.expand_path('../shared/each', __FILE__) + +describe "GetoptLong#each_option" do + it_behaves_like(:getoptlong_each, :each_option) +end diff --git a/spec/rubyspec/library/getoptlong/each_spec.rb b/spec/rubyspec/library/getoptlong/each_spec.rb new file mode 100644 index 0000000000..d09f84a6db --- /dev/null +++ b/spec/rubyspec/library/getoptlong/each_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' +require File.expand_path('../shared/each', __FILE__) + +describe "GetoptLong#each" do + it_behaves_like(:getoptlong_each, :each) +end diff --git a/spec/rubyspec/library/getoptlong/error_message_spec.rb b/spec/rubyspec/library/getoptlong/error_message_spec.rb new file mode 100644 index 0000000000..3f44f538c6 --- /dev/null +++ b/spec/rubyspec/library/getoptlong/error_message_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' + +describe "GetoptLong#error_message" do + it "returns nil if no error occurred" do + opts = GetoptLong.new + opts.error_message.should == nil + end + + it "returns the error message of the last error that occurred" do + argv [] do + opts = GetoptLong.new + opts.quiet = true + opts.get + -> { + opts.ordering = GetoptLong::PERMUTE + }.should raise_error(ArgumentError) { |e| + e.message.should == "argument error" + opts.error_message.should == "argument error" + } + end + end +end diff --git a/spec/rubyspec/library/getoptlong/get_option_spec.rb b/spec/rubyspec/library/getoptlong/get_option_spec.rb new file mode 100644 index 0000000000..c56903e68e --- /dev/null +++ b/spec/rubyspec/library/getoptlong/get_option_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' +require File.expand_path('../shared/get', __FILE__) + +describe "GetoptLong#get_option" do + it_behaves_like(:getoptlong_get, :get_option) +end diff --git a/spec/rubyspec/library/getoptlong/get_spec.rb b/spec/rubyspec/library/getoptlong/get_spec.rb new file mode 100644 index 0000000000..ba1a1be6ad --- /dev/null +++ b/spec/rubyspec/library/getoptlong/get_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' +require File.expand_path('../shared/get', __FILE__) + +describe "GetoptLong#get" do + it_behaves_like(:getoptlong_get, :get) +end diff --git a/spec/rubyspec/library/getoptlong/initialize_spec.rb b/spec/rubyspec/library/getoptlong/initialize_spec.rb new file mode 100644 index 0000000000..6ac46b8b5d --- /dev/null +++ b/spec/rubyspec/library/getoptlong/initialize_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' + +describe "GetoptLong#initialize" do + it "sets ordering to REQUIRE_ORDER if ENV['POSIXLY_CORRECT'] is set" do + begin + old_env_value = ENV["POSIXLY_CORRECT"] + ENV["POSIXLY_CORRECT"] = "" + + opt = GetoptLong.new + opt.ordering.should == GetoptLong::REQUIRE_ORDER + ensure + ENV["POSIXLY_CORRECT"] = old_env_value + end + end + + it "sets ordering to PERMUTE if ENV['POSIXLY_CORRECT'] is not set" do + begin + old_env_value = ENV["POSIXLY_CORRECT"] + ENV["POSIXLY_CORRECT"] = nil + + opt = GetoptLong.new + opt.ordering.should == GetoptLong::PERMUTE + ensure + ENV["POSIXLY_CORRECT"] = old_env_value + end + end +end diff --git a/spec/rubyspec/library/getoptlong/ordering_spec.rb b/spec/rubyspec/library/getoptlong/ordering_spec.rb new file mode 100644 index 0000000000..e445de2255 --- /dev/null +++ b/spec/rubyspec/library/getoptlong/ordering_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' + +describe "GetoptLong#ordering=" do + it "raises an ArgumentError if called after processing has started" do + argv [ "--size", "10k", "--verbose" ] do + opts = GetoptLong.new([ '--size', GetoptLong::REQUIRED_ARGUMENT ], + [ '--verbose', GetoptLong::NO_ARGUMENT ]) + opts.quiet = true + opts.get + + lambda { + opts.ordering = GetoptLong::PERMUTE + }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if given an invalid value" do + opts = GetoptLong.new + + lambda { + opts.ordering = 12345 + }.should raise_error(ArgumentError) + end + + it "does not allow changing ordering to PERMUTE if ENV['POSIXLY_CORRECT'] is set" do + begin + old_env_value = ENV['POSIXLY_CORRECT'] + ENV['POSIXLY_CORRECT'] = "" + + opts = GetoptLong.new + opts.ordering = GetoptLong::PERMUTE + opts.ordering.should == GetoptLong::REQUIRE_ORDER + ensure + ENV['POSIXLY_CORRECT'] = old_env_value + end + end +end diff --git a/spec/rubyspec/library/getoptlong/set_options_spec.rb b/spec/rubyspec/library/getoptlong/set_options_spec.rb new file mode 100644 index 0000000000..39d6991bf5 --- /dev/null +++ b/spec/rubyspec/library/getoptlong/set_options_spec.rb @@ -0,0 +1,98 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' + +describe "GetoptLong#set_options" do + before :each do + @opts = GetoptLong.new + end + + it "allows setting command line options" do + argv ["--size", "10k", "-v", "arg1", "arg2"] do + @opts.set_options( + ["--size", GetoptLong::REQUIRED_ARGUMENT], + ["--verbose", "-v", GetoptLong::NO_ARGUMENT] + ) + + @opts.get.should == ["--size", "10k"] + @opts.get.should == ["--verbose", ""] + @opts.get.should == nil + end + end + + it "discards previously defined command line options" do + argv ["--size", "10k", "-v", "arg1", "arg2"] do + @opts.set_options( + ["--size", GetoptLong::REQUIRED_ARGUMENT], + ["--verbose", "-v", GetoptLong::NO_ARGUMENT] + ) + + @opts.set_options( + ["-s", "--size", GetoptLong::REQUIRED_ARGUMENT], + ["-v", GetoptLong::NO_ARGUMENT] + ) + + @opts.get.should == ["-s", "10k"] + @opts.get.should == ["-v", ""] + @opts.get.should == nil + end + end + + it "raises an ArgumentError if too many argument flags where given" do + argv [] do + lambda { + @opts.set_options(["--size", GetoptLong::NO_ARGUMENT, GetoptLong::REQUIRED_ARGUMENT]) + }.should raise_error(ArgumentError) + end + end + + it "raises a RuntimeError if processing has already started" do + argv [] do + @opts.get + lambda { + @opts.set_options() + }.should raise_error(RuntimeError) + end + end + + it "raises an ArgumentError if no argument flag was given" do + argv [] do + lambda { + @opts.set_options(["--size"]) + }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if one of the given arguments is not an Array" do + argv [] do + lambda { + @opts.set_options( + ["--size", GetoptLong::REQUIRED_ARGUMENT], + "test") + }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if the same option is given twice" do + argv [] do + lambda { + @opts.set_options( + ["--size", GetoptLong::NO_ARGUMENT], + ["--size", GetoptLong::OPTIONAL_ARGUMENT]) + }.should raise_error(ArgumentError) + + lambda { + @opts.set_options( + ["--size", GetoptLong::NO_ARGUMENT], + ["-s", "--size", GetoptLong::OPTIONAL_ARGUMENT]) + }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if the given option is invalid" do + argv [] do + lambda { + @opts.set_options(["-size", GetoptLong::NO_ARGUMENT]) + }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/library/getoptlong/shared/each.rb b/spec/rubyspec/library/getoptlong/shared/each.rb new file mode 100644 index 0000000000..b534e24c0f --- /dev/null +++ b/spec/rubyspec/library/getoptlong/shared/each.rb @@ -0,0 +1,18 @@ +describe :getoptlong_each, shared: true do + before :each do + @opts = GetoptLong.new( + [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ], + [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], + [ '--query', '-q', GetoptLong::NO_ARGUMENT ], + [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ] + ) + end + + it "passes each argument/value pair to the block" do + argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do + pairs = [] + @opts.send(@method) { |arg, val| pairs << [ arg, val ] } + pairs.should == [ [ "--size", "10k" ], [ "--verbose", "" ], [ "--query", ""] ] + end + end +end diff --git a/spec/rubyspec/library/getoptlong/shared/get.rb b/spec/rubyspec/library/getoptlong/shared/get.rb new file mode 100644 index 0000000000..276da5abb4 --- /dev/null +++ b/spec/rubyspec/library/getoptlong/shared/get.rb @@ -0,0 +1,55 @@ +describe :getoptlong_get, shared: true do + before :each do + @opts = GetoptLong.new( + [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ], + [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], + [ '--query', '-q', GetoptLong::NO_ARGUMENT ], + [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ] + ) + @opts.quiet = true # silence using $deferr + end + + it "returns the next option name and its argument as an Array" do + argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do + @opts.send(@method).should == [ "--size", "10k" ] + @opts.send(@method).should == [ "--verbose", "" ] + @opts.send(@method).should == [ "--query", ""] + @opts.send(@method).should == nil + end + end + + it "shifts ARGV on each call" do + argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do + @opts.send(@method) + ARGV.should == [ "-v", "-q", "a.txt", "b.txt" ] + + @opts.send(@method) + ARGV.should == [ "-q", "a.txt", "b.txt" ] + + @opts.send(@method) + ARGV.should == [ "a.txt", "b.txt" ] + + @opts.send(@method) + ARGV.should == [ "a.txt", "b.txt" ] + end + end + + it "terminates processing when encountering '--'" do + argv [ "--size", "10k", "--", "-v", "-q", "a.txt", "b.txt" ] do + @opts.send(@method) + ARGV.should == ["--", "-v", "-q", "a.txt", "b.txt"] + + @opts.send(@method) + ARGV.should == ["-v", "-q", "a.txt", "b.txt"] + + @opts.send(@method) + ARGV.should == ["-v", "-q", "a.txt", "b.txt"] + end + end + + it "raises a if an argument was required, but none given" do + argv [ "--size" ] do + lambda { @opts.send(@method) }.should raise_error(GetoptLong::MissingArgument) + end + end +end diff --git a/spec/rubyspec/library/getoptlong/terminate_spec.rb b/spec/rubyspec/library/getoptlong/terminate_spec.rb new file mode 100644 index 0000000000..ad9f9a1623 --- /dev/null +++ b/spec/rubyspec/library/getoptlong/terminate_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' + +describe "GetoptLong#terminate" do + before :each do + @opts = GetoptLong.new( + [ '--size', '-s', GetoptLong::REQUIRED_ARGUMENT ], + [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], + [ '--query', '-q', GetoptLong::NO_ARGUMENT ], + [ '--check', '--valid', '-c', GetoptLong::NO_ARGUMENT ] + ) + end + + it "terminates option proccessing" do + argv [ "--size", "10k", "-v", "-q", "a.txt", "b.txt" ] do + @opts.get.should == [ "--size", "10k" ] + @opts.terminate + @opts.get.should == nil + end + end + + it "returns self when option processsing is terminated" do + @opts.terminate.should == @opts + end + + it "returns nil when option processing was already terminated" do + @opts.terminate + @opts.terminate.should == nil + end +end diff --git a/spec/rubyspec/library/getoptlong/terminated_spec.rb b/spec/rubyspec/library/getoptlong/terminated_spec.rb new file mode 100644 index 0000000000..feaf2bc09e --- /dev/null +++ b/spec/rubyspec/library/getoptlong/terminated_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'getoptlong' + +describe "GetoptLong#terminated?" do + it "returns true if option processing has terminated" do + argv [ "--size", "10k" ] do + opts = GetoptLong.new(["--size", GetoptLong::REQUIRED_ARGUMENT]) + opts.terminated?.should == false + + opts.get.should == ["--size", "10k"] + opts.terminated?.should == false + + opts.get.should == nil + opts.terminated?.should == true + end + end +end diff --git a/spec/rubyspec/library/ipaddr/hton_spec.rb b/spec/rubyspec/library/ipaddr/hton_spec.rb new file mode 100644 index 0000000000..037bb3d328 --- /dev/null +++ b/spec/rubyspec/library/ipaddr/hton_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ipaddr' + +describe "IPAddr#hton" do + + it "converts IPAddr to network byte order" do + addr = '' + IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte do |c| + addr += sprintf("%02x", c) + end + addr.should == "123456789abcdef0123456789abcdef0" + addr = '' + IPAddr.new("123.45.67.89").hton.each_byte do |c| + addr += sprintf("%02x", c) + end + addr.should == sprintf("%02x%02x%02x%02x", 123, 45, 67, 89) + end + +end + +describe "IPAddr#new_ntoh" do + + it "creates a new IPAddr using hton notation" do + a = IPAddr.new("3ffe:505:2::") + IPAddr.new_ntoh(a.hton).to_s.should == "3ffe:505:2::" + a = IPAddr.new("192.168.2.1") + IPAddr.new_ntoh(a.hton).to_s.should == "192.168.2.1" + end + +end diff --git a/spec/rubyspec/library/ipaddr/ipv4_conversion_spec.rb b/spec/rubyspec/library/ipaddr/ipv4_conversion_spec.rb new file mode 100644 index 0000000000..b69be82c13 --- /dev/null +++ b/spec/rubyspec/library/ipaddr/ipv4_conversion_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ipaddr' + +describe "IPAddr#ipv4_compat" do + + it "should ipv4_compat?" do + a = IPAddr.new("::192.168.1.2") + a.to_s.should == "::192.168.1.2" + a.to_string.should == "0000:0000:0000:0000:0000:0000:c0a8:0102" + a.family.should == Socket::AF_INET6 + a.ipv4_compat?.should == true + b = a.native + b.to_s.should == "192.168.1.2" + b.family.should == Socket::AF_INET + b.ipv4_compat?.should == false + + a = IPAddr.new("192.168.1.2") + b = a.ipv4_compat + b.to_s.should == "::192.168.1.2" + b.family.should == Socket::AF_INET6 + end + +end + +describe "IPAddr#ipv4_mapped" do + + it "should ipv4_mapped" do + a = IPAddr.new("::ffff:192.168.1.2") + a.to_s.should == "::ffff:192.168.1.2" + a.to_string.should == "0000:0000:0000:0000:0000:ffff:c0a8:0102" + a.family.should == Socket::AF_INET6 + a.ipv4_mapped?.should == true + b = a.native + b.to_s.should == "192.168.1.2" + b.family.should == Socket::AF_INET + b.ipv4_mapped?.should == false + + a = IPAddr.new("192.168.1.2") + b = a.ipv4_mapped + b.to_s.should == "::ffff:192.168.1.2" + b.family.should == Socket::AF_INET6 + end + +end + + diff --git a/spec/rubyspec/library/ipaddr/new_spec.rb b/spec/rubyspec/library/ipaddr/new_spec.rb new file mode 100644 index 0000000000..d0b91af87d --- /dev/null +++ b/spec/rubyspec/library/ipaddr/new_spec.rb @@ -0,0 +1,93 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ipaddr' + +describe "IPAddr#new" do + it "initializes IPAddr" do + lambda{ IPAddr.new("3FFE:505:ffff::/48") }.should_not raise_error + lambda{ IPAddr.new("0:0:0:1::") }.should_not raise_error + lambda{ IPAddr.new("2001:200:300::/48") }.should_not raise_error + end + + it "initializes IPAddr ipv6 address with short notation" do + a = IPAddr.new + a.to_s.should == "::" + a.to_string.should == "0000:0000:0000:0000:0000:0000:0000:0000" + a.family.should == Socket::AF_INET6 + end + + it "initializes IPAddr ipv6 address with long notation" do + a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678") + a.to_s.should == "123:4567:89ab:cdef:abc:def0:1234:5678" + a.to_string.should == "0123:4567:89ab:cdef:0abc:def0:1234:5678" + a.family.should == Socket::AF_INET6 + end + + it "initializes IPAddr ipv6 address with / subnet notation" do + a = IPAddr.new("3ffe:505:2::/48") + a.to_s.should == "3ffe:505:2::" + a.to_string.should == "3ffe:0505:0002:0000:0000:0000:0000:0000" + a.family.should == Socket::AF_INET6 + a.ipv4?.should == false + a.ipv6?.should == true + a.inspect.should == "#" + end + + it "initializes IPAddr ipv6 address with mask subnet notation" do + a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::") + a.to_s.should == "3ffe:505:2::" + a.to_string.should == "3ffe:0505:0002:0000:0000:0000:0000:0000" + a.family.should == Socket::AF_INET6 + end + + it "initializes IPAddr ipv4 address with all zeroes" do + a = IPAddr.new("0.0.0.0") + a.to_s.should == "0.0.0.0" + a.to_string.should == "0.0.0.0" + a.family.should == Socket::AF_INET + end + + it "initializes IPAddr ipv4 address" do + a = IPAddr.new("192.168.1.2") + a.to_s.should == "192.168.1.2" + a.to_string.should == "192.168.1.2" + a.family.should == Socket::AF_INET + a.ipv4?.should == true + a.ipv6?.should == false + end + + it "initializes IPAddr ipv4 address with / subnet notation" do + a = IPAddr.new("192.168.1.2/24") + a.to_s.should == "192.168.1.0" + a.to_string.should == "192.168.1.0" + a.family.should == Socket::AF_INET + a.inspect.should == "#" + end + + it "initializes IPAddr ipv4 address with subnet mask" do + a = IPAddr.new("192.168.1.2/255.255.255.0") + a.to_s.should == "192.168.1.0" + a.to_string.should == "192.168.1.0" + a.family.should == Socket::AF_INET + end + + it "initializes IPAddr ipv4 mapped address with subnet mask" do + a = IPAddr.new("::1:192.168.1.2/120") + a.to_s.should == "::1:c0a8:100" + a.to_string.should == "0000:0000:0000:0000:0000:0001:c0a8:0100" + a.family.should == Socket::AF_INET6 + end + + it "raises on incorrect IPAddr strings" do + [ + ["fe80::1%fxp0"], + ["::1/255.255.255.0"], + [IPAddr.new("::1").to_i], + ["::ffff:192.168.1.2/120", Socket::AF_INET], + ["[192.168.1.2]/120"], + ].each { |args| + lambda{ + IPAddr.new(*args) + }.should raise_error(ArgumentError) + } + end +end diff --git a/spec/rubyspec/library/ipaddr/operator_spec.rb b/spec/rubyspec/library/ipaddr/operator_spec.rb new file mode 100644 index 0000000000..3f54efd486 --- /dev/null +++ b/spec/rubyspec/library/ipaddr/operator_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ipaddr' + +describe "IPAddr Operator" do + IN6MASK32 = "ffff:ffff::" + IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + + before do + @in6_addr_any = IPAddr.new() + @a = IPAddr.new("3ffe:505:2::/48") + @b = IPAddr.new("0:0:0:1::") + @c = IPAddr.new(IN6MASK32) + end + + it "bitwises or" do + (@a | @b).to_s.should == "3ffe:505:2:1::" + a = @a + a |= @b + a.to_s.should == "3ffe:505:2:1::" + @a.to_s.should == "3ffe:505:2::" + (@a | 0x00000000000000010000000000000000).to_s.should == "3ffe:505:2:1::" + end + + it "bitwises and" do + (@a & @c).to_s.should == "3ffe:505::" + a = @a + a &= @c + a.to_s.should == "3ffe:505::" + @a.to_s.should == "3ffe:505:2::" + (@a & 0xffffffff000000000000000000000000).to_s.should == "3ffe:505::" + end + + it "bitshifts right" do + (@a >> 16).to_s.should == "0:3ffe:505:2::" + a = @a + a >>= 16 + a.to_s.should == "0:3ffe:505:2::" + @a.to_s.should == "3ffe:505:2::" + end + + it "bitshifts left" do + (@a << 16).to_s.should == "505:2::" + a = @a + a <<= 16 + a.to_s.should == "505:2::" + @a.to_s.should == "3ffe:505:2::" + end + + it "inverts" do + a = ~@in6_addr_any + a.to_s.should == IN6MASK128 + @in6_addr_any.to_s.should == "::" + end + + it "tests for equality" do + @a.should == IPAddr.new("3ffe:505:2::") + @a.should_not == IPAddr.new("3ffe:505:3::") + end + + it "sets a mask" do + a = @a.mask(32) + a.to_s.should == "3ffe:505::" + @a.to_s.should == "3ffe:505:2::" + end + + it "checks whether an addres is included in a range" do + @a.should include(IPAddr.new("3ffe:505:2::")) + @a.should include(IPAddr.new("3ffe:505:2::1")) + @a.should_not include(IPAddr.new("3ffe:505:3::")) + net1 = IPAddr.new("192.168.2.0/24") + net1.should include(IPAddr.new("192.168.2.0")) + net1.should include(IPAddr.new("192.168.2.255")) + net1.should_not include(IPAddr.new("192.168.3.0")) + # test with integer parameter + int = (192 << 24) + (168 << 16) + (2 << 8) + 13 + + net1.should include(int) + net1.should_not include(int+255) + end +end diff --git a/spec/rubyspec/library/ipaddr/reverse_spec.rb b/spec/rubyspec/library/ipaddr/reverse_spec.rb new file mode 100644 index 0000000000..dec5c65178 --- /dev/null +++ b/spec/rubyspec/library/ipaddr/reverse_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ipaddr' + +describe "IPAddr#reverse" do + it "generates the reverse DNS lookup entry" do + IPAddr.new("3ffe:505:2::f").reverse.should == "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa" + IPAddr.new("192.168.2.1").reverse.should == "1.2.168.192.in-addr.arpa" + end +end + +describe "IPAddr#ip6_arpa" do + it "converts an IPv6 address into the reverse DNS lookup representation according to RFC3172" do + IPAddr.new("3ffe:505:2::f").ip6_arpa.should == "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa" + lambda{ + IPAddr.new("192.168.2.1").ip6_arpa + }.should raise_error(ArgumentError) + end +end + +describe "IPAddr#ip6_int" do + it "converts an IPv6 address into the reverse DNS lookup representation according to RFC1886" do + IPAddr.new("3ffe:505:2::f").ip6_int.should == "f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int" + lambda{ + IPAddr.new("192.168.2.1").ip6_int + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/ipaddr/to_s_spec.rb b/spec/rubyspec/library/ipaddr/to_s_spec.rb new file mode 100644 index 0000000000..30e5237436 --- /dev/null +++ b/spec/rubyspec/library/ipaddr/to_s_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ipaddr' + +describe "IPAddr#to_s" do + + it "displays IPAddr using short notation" do + IPAddr.new("0:0:0:1::").to_s.should == "0:0:0:1::" + IPAddr.new("2001:200:300::/48").to_s.should == "2001:200:300::" + IPAddr.new("[2001:200:300::]/48").to_s.should == "2001:200:300::" + IPAddr.new("3ffe:505:2::1").to_s.should == "3ffe:505:2::1" + end + +end + +describe "IPAddr#to_string" do + it "displays an IPAddr using full notation" do + IPAddr.new("3ffe:505:2::1").to_string.should == "3ffe:0505:0002:0000:0000:0000:0000:0001" + end + +end diff --git a/spec/rubyspec/library/logger/device/close_spec.rb b/spec/rubyspec/library/logger/device/close_spec.rb new file mode 100644 index 0000000000..777b20baf0 --- /dev/null +++ b/spec/rubyspec/library/logger/device/close_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger::LogDevice#close" do + before :each do + @file_path = tmp("test_log.log") + @log_file = File.open(@file_path, "w+") + + # Avoid testing this with STDERR, we don't want to be closing that. + @device = Logger::LogDevice.new(@log_file) + end + + after :each do + @log_file.close unless @log_file.closed? + rm_r @file_path + end + + it "closes the LogDevice's stream" do + @device.close + lambda { @device.write("Test") }.should complain(/\Alog writing failed\./) + end +end diff --git a/spec/rubyspec/library/logger/device/new_spec.rb b/spec/rubyspec/library/logger/device/new_spec.rb new file mode 100644 index 0000000000..0e08b743ed --- /dev/null +++ b/spec/rubyspec/library/logger/device/new_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger::LogDevice#new" do + before :each do + @file_path = tmp("test_log.log") + @log_file = File.open(@file_path, "w+") + end + + after :each do + @log_file.close unless @log_file.closed? + rm_r @file_path + end + + it "creates a new log device" do + l = Logger::LogDevice.new(@log_file) + l.dev.should be_kind_of(File) + end + + it "receives an IO object to log there as first argument" do + @log_file.should be_kind_of(IO) + l = Logger::LogDevice.new(@log_file) + l.write("foo") + @log_file.rewind + @log_file.readlines.first.should == "foo" + end + + it "creates a File if the IO object does not exist" do + path = tmp("test_logger_file") + l = Logger::LogDevice.new(path) + l.write("Test message") + l.close + + File.exist?(path).should be_true + File.open(path) do |f| + f.readlines.should_not be_empty + end + + rm_r path + end + + it "receives options via a hash as second argument" do + lambda { Logger::LogDevice.new(STDERR, + { shift_age: 8, shift_size: 10 + })}.should_not raise_error + end +end diff --git a/spec/rubyspec/library/logger/device/write_spec.rb b/spec/rubyspec/library/logger/device/write_spec.rb new file mode 100644 index 0000000000..e3ddd61b1f --- /dev/null +++ b/spec/rubyspec/library/logger/device/write_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger::LogDevice#write" do + before :each do + @file_path = tmp("test_log.log") + @log_file = File.open(@file_path, "w+") + # Avoid testing this with STDERR, we don't want to be closing that. + @device = Logger::LogDevice.new(@log_file) + end + + after :each do + @log_file.close unless @log_file.closed? + rm_r @file_path + end + + it "writes a message to the device" do + @device.write "This is a test message" + @log_file.rewind + @log_file.readlines.first.should == "This is a test message" + end + + it "can create a file and writes empty message" do + path = tmp("you_should_not_see_me") + logdevice = Logger::LogDevice.new(path) + logdevice.write("") + logdevice.close + + File.open(path) do |f| + messages = f.readlines + messages.size.should == 1 + messages.first.should =~ /#.*/ # only a comment + end + + rm_r path + end + + it "fails if the device is already closed" do + @device.close + lambda { @device.write "foo" }.should complain(/\Alog writing failed\./) + end +end diff --git a/spec/rubyspec/library/logger/fixtures/common.rb b/spec/rubyspec/library/logger/fixtures/common.rb new file mode 100644 index 0000000000..d369c64a24 --- /dev/null +++ b/spec/rubyspec/library/logger/fixtures/common.rb @@ -0,0 +1,9 @@ +require 'logger' + +module LoggerSpecs + + def self.strip_date(str) + str.gsub(/[A-Z].*\[.*\]/, "").lstrip + end + +end diff --git a/spec/rubyspec/library/logger/logger/add_spec.rb b/spec/rubyspec/library/logger/logger/add_spec.rb new file mode 100644 index 0000000000..235247e9e8 --- /dev/null +++ b/spec/rubyspec/library/logger/logger/add_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#add" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "writes a new message to the logger" do + @logger.add(Logger::WARN, "Test") + @log_file.rewind + message = @log_file.readlines.last + LoggerSpecs.strip_date(message).should == "WARN -- : Test\n" + end + + it "receives a severity" do + @logger.log(Logger::INFO, "Info message") + @logger.log(Logger::DEBUG, "Debug message") + @logger.log(Logger::WARN, "Warn message") + @logger.log(Logger::ERROR, "Error message") + @logger.log(Logger::FATAL, "Fatal message") + + @log_file.rewind + + info, debug, warn, error, fatal = @log_file.readlines + + LoggerSpecs.strip_date(info).should == "INFO -- : Info message\n" + LoggerSpecs.strip_date(debug).should == "DEBUG -- : Debug message\n" + LoggerSpecs.strip_date(warn).should == "WARN -- : Warn message\n" + LoggerSpecs.strip_date(error).should == "ERROR -- : Error message\n" + LoggerSpecs.strip_date(fatal).should == "FATAL -- : Fatal message\n" + end + + it "receives a message" do + @logger.log(nil, "test") + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readline).should == "ANY -- : test\n" + end + + it "receives a program name" do + @logger.log(nil, "test", "TestApp") + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readline).should == "ANY -- TestApp: test\n" + end + + it "receives a block" do + lambda { + @logger.log(nil, "test", "TestApp") do + 1+1 + end + }.should_not raise_error + end + + it "calls the block if message is nil" do + temp = 0 + lambda { + @logger.log(nil, nil, "TestApp") do + temp = 1+1 + end + }.should_not raise_error + temp.should == 2 + end + + it "ignores the block if the message is not nil" do + temp = 0 + lambda { + @logger.log(nil, "not nil", "TestApp") do + temp = 1+1 + end + }.should_not raise_error + temp.should == 0 + end +end diff --git a/spec/rubyspec/library/logger/logger/close_spec.rb b/spec/rubyspec/library/logger/logger/close_spec.rb new file mode 100644 index 0000000000..a4c006150b --- /dev/null +++ b/spec/rubyspec/library/logger/logger/close_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#close" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "closes the logging device" do + @logger.close + lambda { @logger.add(nil, "Foo") }.should complain(/\Alog writing failed\./) + end +end diff --git a/spec/rubyspec/library/logger/logger/datetime_format_spec.rb b/spec/rubyspec/library/logger/logger/datetime_format_spec.rb new file mode 100644 index 0000000000..2bc1f867c3 --- /dev/null +++ b/spec/rubyspec/library/logger/logger/datetime_format_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#datetime_format" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "returns the date format used for the logs" do + format = "%Y-%d" + @logger.datetime_format = format + @logger.datetime_format.should == format + end + + it "returns nil logger is using the default date format" do + @logger.datetime_format.should == nil + end +end + +describe "Logger#datetime_format=" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "sets the date format for the logs" do + @logger.datetime_format = "%Y" + @logger.datetime_format.should == "%Y" + @logger.add(Logger::WARN, "Test message") + @log_file.rewind + + regex = /2[0-9]{3}.*Test message/ + @log_file.readlines.first.should =~ regex + end + + it "follows the Time#strftime format" do + lambda { @logger.datetime_format = "%Y-%m" }.should_not raise_error + + regex = /\d{4}-\d{2}-\d{2}oo-\w+ar/ + @logger.datetime_format = "%Foo-%Bar" + @logger.add(nil, "Test message") + @log_file.rewind + @log_file.readlines.first.should =~ regex + end +end diff --git a/spec/rubyspec/library/logger/logger/debug_spec.rb b/spec/rubyspec/library/logger/logger/debug_spec.rb new file mode 100644 index 0000000000..2260587b23 --- /dev/null +++ b/spec/rubyspec/library/logger/logger/debug_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#debug?" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "returns true if severity level allows debug messages" do + @logger.level = Logger::DEBUG + @logger.debug?.should == true + end + + it "returns false if severity level does not allow debug messages" do + @logger.level = Logger::WARN + @logger.debug?.should == false + end +end + +describe "Logger#debug" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "logs a DEBUG message" do + @logger.debug("test") + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "DEBUG -- : test\n" + end + + it "accepts an application name with a block" do + @logger.debug("MyApp") { "Test message" } + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "DEBUG -- MyApp: Test message\n" + end +end diff --git a/spec/rubyspec/library/logger/logger/error_spec.rb b/spec/rubyspec/library/logger/logger/error_spec.rb new file mode 100644 index 0000000000..e165396bab --- /dev/null +++ b/spec/rubyspec/library/logger/logger/error_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#error?" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "returns true if severity level allows printing errors" do + @logger.level = Logger::INFO + @logger.error?.should == true + end + + it "returns false if severity level does not allow errors" do + @logger.level = Logger::FATAL + @logger.error?.should == false + end +end + +describe "Logger#error" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "logs a ERROR message" do + @logger.error("test") + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "ERROR -- : test\n" + end + + it "accepts an application name with a block" do + @logger.error("MyApp") { "Test message" } + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "ERROR -- MyApp: Test message\n" + end + +end diff --git a/spec/rubyspec/library/logger/logger/fatal_spec.rb b/spec/rubyspec/library/logger/logger/fatal_spec.rb new file mode 100644 index 0000000000..ebbe8a04a5 --- /dev/null +++ b/spec/rubyspec/library/logger/logger/fatal_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#fatal?" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "returns true if severity level allows fatal messages" do + @logger.level = Logger::FATAL + @logger.fatal?.should == true + end + + it "returns false if severity level does not allow fatal messages" do + @logger.level = Logger::UNKNOWN + @logger.fatal?.should == false + end +end + +describe "Logger#fatal" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "logs a FATAL message" do + @logger.fatal("test") + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "FATAL -- : test\n" + end + + it "accepts an application name with a block" do + @logger.fatal("MyApp") { "Test message" } + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "FATAL -- MyApp: Test message\n" + end + +end diff --git a/spec/rubyspec/library/logger/logger/info_spec.rb b/spec/rubyspec/library/logger/logger/info_spec.rb new file mode 100644 index 0000000000..7f299ea0da --- /dev/null +++ b/spec/rubyspec/library/logger/logger/info_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#info?" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "returns true if severity level allows info messages" do + @logger.level = Logger::INFO + @logger.info?.should == true + end + + it "returns false if severity level does not allow info messages" do + @logger.level = Logger::FATAL + @logger.info?.should == false + end +end + +describe "Logger#info" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "logs a INFO message" do + @logger.info("test") + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "INFO -- : test\n" + end + + it "accepts an application name with a block" do + @logger.info("MyApp") { "Test message" } + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "INFO -- MyApp: Test message\n" + end + +end diff --git a/spec/rubyspec/library/logger/logger/new_spec.rb b/spec/rubyspec/library/logger/logger/new_spec.rb new file mode 100644 index 0000000000..255f686d25 --- /dev/null +++ b/spec/rubyspec/library/logger/logger/new_spec.rb @@ -0,0 +1,63 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#new" do + + before :each do + @file_path = tmp("test_log.log") + @log_file = File.open(@file_path, "w+") + end + + after :each do + @log_file.close unless @log_file.closed? + rm_r @file_path + end + + it "creates a new logger object" do + l = Logger.new(STDERR) + lambda { l.add(Logger::WARN, "Foo") }.should output_to_fd(/Foo/, STDERR) + end + + it "receives a logging device as first argument" do + l = Logger.new(@log_file) + l.add(Logger::WARN, "Test message") + + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readline).should == "WARN -- : Test message\n" + l.close + end + + it "receives a frequency rotation as second argument" do + lambda { Logger.new(@log_file, "daily") }.should_not raise_error + lambda { Logger.new(@log_file, "weekly") }.should_not raise_error + lambda { Logger.new(@log_file, "monthly") }.should_not raise_error + end + + it "also receives a number of log files to keep as second argument" do + lambda { Logger.new(@log_file, 1).close }.should_not raise_error + end + + it "receivs a maximum logfile size as third argument" do + # This should create 2 small log files, logfile_test and logfile_test.0 + # in /tmp, each one with a different message. + path = tmp("logfile_test.log") + + l = Logger.new(path, 2, 5) + l.add Logger::WARN, "foo" + l.add Logger::WARN, "bar" + + File.exist?(path).should be_true + File.exist?(path + ".0").should be_true + + # first line will be a comment so we'll have to skip it. + f = File.open(path) + f1 = File.open("#{path}.0") + LoggerSpecs.strip_date(f1.readlines.last).should == "WARN -- : foo\n" + LoggerSpecs.strip_date(f.readlines.last).should == "WARN -- : bar\n" + + l.close + f.close + f1.close + rm_r path, "#{path}.0" + end +end diff --git a/spec/rubyspec/library/logger/logger/unknown_spec.rb b/spec/rubyspec/library/logger/logger/unknown_spec.rb new file mode 100644 index 0000000000..5ef9659a9c --- /dev/null +++ b/spec/rubyspec/library/logger/logger/unknown_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#unknown" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "logs a message with unknown severity" do + @logger.unknown "Test" + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "ANY -- : Test\n" + end + + it "defaults the priority value to 5 and text value to ANY" do + @logger.unknown "Test" + @log_file.rewind + message = LoggerSpecs.strip_date(@log_file.readlines.first)[0..2] + message.should == "ANY" + Logger::UNKNOWN.should == 5 + end + + it "receives empty messages" do + lambda { @logger.unknown("") }.should_not raise_error + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "ANY -- : \n" + end +end diff --git a/spec/rubyspec/library/logger/logger/warn_spec.rb b/spec/rubyspec/library/logger/logger/warn_spec.rb new file mode 100644 index 0000000000..d34f19fb8e --- /dev/null +++ b/spec/rubyspec/library/logger/logger/warn_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/common', __FILE__) + +describe "Logger#warn?" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "returns true if severity level allows printing warn messages" do + @logger.level = Logger::WARN + @logger.warn?.should == true + end + + it "returns false if severity level does not allow printing warn messages" do + @logger.level = Logger::FATAL + @logger.warn?.should == false + end +end + +describe "Logger#warn" do + before :each do + @path = tmp("test_log.log") + @log_file = File.open(@path, "w+") + @logger = Logger.new(@path) + end + + after :each do + @logger.close + @log_file.close unless @log_file.closed? + rm_r @path + end + + it "logs a WARN message" do + @logger.warn("test") + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "WARN -- : test\n" + end + + it "accepts an application name with a block" do + @logger.warn("MyApp") { "Test message" } + @log_file.rewind + LoggerSpecs.strip_date(@log_file.readlines.first).should == "WARN -- MyApp: Test message\n" + end + +end diff --git a/spec/rubyspec/library/logger/severity_spec.rb b/spec/rubyspec/library/logger/severity_spec.rb new file mode 100644 index 0000000000..a4219365dd --- /dev/null +++ b/spec/rubyspec/library/logger/severity_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'logger' + +describe "Logger::Severity" do + it "defines Logger severity constants" do + Logger::DEBUG.should == 0 + Logger::INFO.should == 1 + Logger::WARN.should == 2 + Logger::ERROR.should == 3 + Logger::FATAL.should == 4 + Logger::UNKNOWN.should == 5 + end +end diff --git a/spec/rubyspec/library/mathn/bignum/exponent_spec.rb b/spec/rubyspec/library/mathn/bignum/exponent_spec.rb new file mode 100644 index 0000000000..758c2f27fd --- /dev/null +++ b/spec/rubyspec/library/mathn/bignum/exponent_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Bignum#**" do + before :each do + @bignum = bignum_value(47) + end + + it "returns self raised to other (positive) power" do + (@bignum ** 4).should == 7237005577332262361485077344629993318496048279512298547155833600056910050625 + (@bignum ** 1.2).should be_close(57262152889751597425762.57804, TOLERANCE) + end + + it "returns a complex number when negative and raised to a fractional power" do + ((-@bignum) ** (1/3)).should be_close(Complex(1048576,1816186.907597341), TOLERANCE) + ((-@bignum) ** (1.0/3)).should be_close(Complex(1048576,1816186.907597341), TOLERANCE) + end + end +end diff --git a/spec/rubyspec/library/mathn/complex/Complex_spec.rb b/spec/rubyspec/library/mathn/complex/Complex_spec.rb new file mode 100644 index 0000000000..93bb3f0c60 --- /dev/null +++ b/spec/rubyspec/library/mathn/complex/Complex_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Kernel#Complex" do + it "returns an Integer if imaginary part is 0" do + Complex(42,0).should == 42 + Complex(42,0).should be_kind_of(Fixnum) + Complex(bignum_value,0).should == bignum_value + Complex(bignum_value,0).should be_kind_of(Bignum) + end + end +end diff --git a/spec/rubyspec/library/mathn/fixnum/exponent_spec.rb b/spec/rubyspec/library/mathn/fixnum/exponent_spec.rb new file mode 100644 index 0000000000..d72bc5aa00 --- /dev/null +++ b/spec/rubyspec/library/mathn/fixnum/exponent_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Fixnum#**" do + it "returns self raised to other (positive) power" do + (2 ** 4).should == 16 + (2 ** 1.2).should be_close(2.2973967, TOLERANCE) + end + + it "returns a complex number when negative and raised to a fractional power" do + ((-8) ** (1/3)).should be_close(Complex(1, 1.73205), TOLERANCE) + ((-8) ** (1.0/3)).should be_close(Complex(1, 1.73205), TOLERANCE) + end + end +end diff --git a/spec/rubyspec/library/mathn/float/exponent_spec.rb b/spec/rubyspec/library/mathn/float/exponent_spec.rb new file mode 100644 index 0000000000..55af43ce5b --- /dev/null +++ b/spec/rubyspec/library/mathn/float/exponent_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Float#**" do + it "returns self raised to other (positive) power" do + (2.0 ** 4).should == 16.0 + (2.0 ** 1.2).should be_close(2.2973967, TOLERANCE) + end + + it "returns a complex number when negative and raised to a fractional power" do + ((-8.0) ** (1/3)).should be_close(Complex(1, 1.73205), TOLERANCE) + ((-8.0) ** (1.0/3)).should be_close(Complex(1, 1.73205), TOLERANCE) + end + end +end diff --git a/spec/rubyspec/library/mathn/integer/from_prime_division_spec.rb b/spec/rubyspec/library/mathn/integer/from_prime_division_spec.rb new file mode 100644 index 0000000000..47aaf47797 --- /dev/null +++ b/spec/rubyspec/library/mathn/integer/from_prime_division_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Integer.from_prime_division" do + it "reverses a prime factorization of an integer" do + Integer.from_prime_division([[2, 1], [3, 2], [7, 1]]).should == 126 + end + end +end diff --git a/spec/rubyspec/library/mathn/integer/prime_division_spec.rb b/spec/rubyspec/library/mathn/integer/prime_division_spec.rb new file mode 100644 index 0000000000..63a5c39733 --- /dev/null +++ b/spec/rubyspec/library/mathn/integer/prime_division_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Integer#prime_division" do + it "performs a prime factorization of a positive integer" do + 100.prime_division.should == [[2, 2], [5, 2]] + end + + # Proper handling of negative integers has been added to MRI trunk + # in revision 24091. Prior to that, all versions of MRI returned nonsense. + it "performs a prime factorization of a negative integer" do + -26.prime_division.should == [[-1, 1], [2, 1], [13, 1]] + end + + it "raises a ZeroDivisionError when is called on zero" do + lambda { 0.prime_division }.should raise_error(ZeroDivisionError) + end + end +end diff --git a/spec/rubyspec/library/mathn/math/fixtures/classes.rb b/spec/rubyspec/library/mathn/math/fixtures/classes.rb new file mode 100644 index 0000000000..024732fa7a --- /dev/null +++ b/spec/rubyspec/library/mathn/math/fixtures/classes.rb @@ -0,0 +1,3 @@ +class IncludesMath + include Math +end diff --git a/spec/rubyspec/library/mathn/math/rsqrt_spec.rb b/spec/rubyspec/library/mathn/math/rsqrt_spec.rb new file mode 100644 index 0000000000..a49efa6ff9 --- /dev/null +++ b/spec/rubyspec/library/mathn/math/rsqrt_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require File.expand_path('../shared/rsqrt', __FILE__) + + describe "Math#rsqrt" do + it_behaves_like :mathn_math_rsqrt, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:rsqrt) + end + end + + describe "Math.rsqrt" do + it_behaves_like :mathn_math_rsqrt, :_, Math + end +end diff --git a/spec/rubyspec/library/mathn/math/shared/rsqrt.rb b/spec/rubyspec/library/mathn/math/shared/rsqrt.rb new file mode 100644 index 0000000000..68e2f7d02f --- /dev/null +++ b/spec/rubyspec/library/mathn/math/shared/rsqrt.rb @@ -0,0 +1,21 @@ +require 'mathn' +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :mathn_math_rsqrt, shared: true do + it "returns the square root for Rational numbers" do + @object.send(:rsqrt, Rational(9, 25)).should == Rational(3, 5) + @object.send(:rsqrt, 16/64).should == Rational(1, 2) + end + + it "returns the square root for positive numbers" do + @object.send(:rsqrt, 1).should == 1 + @object.send(:rsqrt, 4.0).should == 2.0 + @object.send(:rsqrt, 12.34).should == Math.sqrt!(12.34) + end + + it "raises an Math::DomainError if the argument is a negative number" do + lambda { @object.send(:rsqrt, -1) }.should raise_error(Math::DomainError) + lambda { @object.send(:rsqrt, -4.0) }.should raise_error(Math::DomainError) + lambda { @object.send(:rsqrt, -16/64) }.should raise_error(Math::DomainError) + end +end diff --git a/spec/rubyspec/library/mathn/math/shared/sqrt.rb b/spec/rubyspec/library/mathn/math/shared/sqrt.rb new file mode 100644 index 0000000000..6aab25fc5d --- /dev/null +++ b/spec/rubyspec/library/mathn/math/shared/sqrt.rb @@ -0,0 +1,25 @@ +require 'mathn' +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :mathn_math_sqrt, shared: true do + it "returns the square root for Rational numbers" do + @object.send(:sqrt, Rational(9, 25)).should == Rational(3, 5) + @object.send(:sqrt, 16/64).should == Rational(1, 2) + end + + it "returns the square root for Complex numbers" do + @object.send(:sqrt, Complex(1, 0)).should == 1 + end + + it "returns the square root for positive numbers" do + @object.send(:sqrt, 1).should == 1 + @object.send(:sqrt, 4.0).should == 2.0 + @object.send(:sqrt, 12.34).should == Math.sqrt!(12.34) + end + + it "returns the square root for negative numbers" do + @object.send(:sqrt, -9).should == Complex(0, 3) + @object.send(:sqrt, -5.29).should == Complex(0, 2.3) + @object.send(:sqrt, -16/64).should == Complex(0, 1/2) + end +end diff --git a/spec/rubyspec/library/mathn/math/sqrt_spec.rb b/spec/rubyspec/library/mathn/math/sqrt_spec.rb new file mode 100644 index 0000000000..b723360891 --- /dev/null +++ b/spec/rubyspec/library/mathn/math/sqrt_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require File.expand_path('../shared/sqrt', __FILE__) + + describe "Math#rsqrt" do + it_behaves_like :mathn_math_sqrt, :_, IncludesMath.new + + it "is a private instance method" do + IncludesMath.should have_private_instance_method(:sqrt) + end + end + + describe "Math.rsqrt" do + it_behaves_like :mathn_math_sqrt, :_, Math + end +end diff --git a/spec/rubyspec/library/mathn/rational/Rational_spec.rb b/spec/rubyspec/library/mathn/rational/Rational_spec.rb new file mode 100644 index 0000000000..71433529c5 --- /dev/null +++ b/spec/rubyspec/library/mathn/rational/Rational_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Kernel#Rational" do + it "returns an Integer if denominator divides numerator evenly" do + Rational(42,6).should == 7 + Rational(42,6).should be_kind_of(Fixnum) + Rational(bignum_value,1).should == bignum_value + Rational(bignum_value,1).should be_kind_of(Bignum) + end + end +end diff --git a/spec/rubyspec/library/mathn/rational/inspect_spec.rb b/spec/rubyspec/library/mathn/rational/inspect_spec.rb new file mode 100644 index 0000000000..ce1205faaa --- /dev/null +++ b/spec/rubyspec/library/mathn/rational/inspect_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +ruby_version_is ''...'2.5' do + require 'mathn' + + describe "Rational#inspect" do + it "returns a string representation of self" do + Rational(3, 4).inspect.should == "(3/4)" + Rational(-5, 8).inspect.should == "(-5/8)" + Rational(-1, -2).inspect.should == "(1/2)" + Rational(0, 2).inspect.should == "0" + Rational(bignum_value, 1).inspect.should == "#{bignum_value}" + end + end +end diff --git a/spec/rubyspec/library/matrix/I_spec.rb b/spec/rubyspec/library/matrix/I_spec.rb new file mode 100644 index 0000000000..f83cc3cec4 --- /dev/null +++ b/spec/rubyspec/library/matrix/I_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/identity', __FILE__) + +describe "Matrix.I" do + it_behaves_like(:matrix_identity, :I) +end diff --git a/spec/rubyspec/library/matrix/build_spec.rb b/spec/rubyspec/library/matrix/build_spec.rb new file mode 100644 index 0000000000..29fd72206f --- /dev/null +++ b/spec/rubyspec/library/matrix/build_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.build" do + + it "returns a Matrix object of the given size" do + m = Matrix.build(3, 4){1} + m.should be_an_instance_of(Matrix) + m.row_size.should == 3 + m.column_size.should == 4 + end + + it "builds the Matrix using the given block" do + Matrix.build(2, 3){|col, row| 10*col - row}.should == + Matrix[[0, -1, -2], [10, 9, 8]] + end + + it "iterates through the first row, then the second, ..." do + acc = [] + Matrix.build(2, 3){|*args| acc << args} + acc.should == [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]] + end + + it "returns an Enumerator is no block is given" do + enum = Matrix.build(2, 1) + enum.should be_an_instance_of(Enumerator) + enum.each{1}.should == Matrix[[1], [1]] + end + + it "requires integers as parameters" do + lambda { Matrix.build("1", "2"){1} }.should raise_error(TypeError) + lambda { Matrix.build(nil, nil){1} }.should raise_error(TypeError) + lambda { Matrix.build(1..2){1} }.should raise_error(TypeError) + end + + it "requires non-negative integers" do + lambda { Matrix.build(-1, 1){1} }.should raise_error(ArgumentError) + lambda { Matrix.build(+1,-1){1} }.should raise_error(ArgumentError) + end + + it "returns empty Matrix if one argument is zero" do + m = Matrix.build(0, 3){ + raise "Should not yield" + } + m.should be_empty + m.column_size.should == 3 + + m = Matrix.build(3, 0){ + raise "Should not yield" + } + m.should be_empty + m.row_size.should == 3 + end + + it "tries to calls :to_int on arguments" do + int = mock('int') + int.should_receive(:to_int).twice.and_return(2) + Matrix.build(int, int){ 1 }.should == Matrix[ [1,1], [1,1] ] + end + + it "builds an nxn Matrix when given only one argument" do + m = Matrix.build(3){1} + m.row_size.should == 3 + m.column_size.should == 3 + end +end + +describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.build(3){1}.should be_an_instance_of(MatrixSub) + end +end diff --git a/spec/rubyspec/library/matrix/clone_spec.rb b/spec/rubyspec/library/matrix/clone_spec.rb new file mode 100644 index 0000000000..8819fc9b40 --- /dev/null +++ b/spec/rubyspec/library/matrix/clone_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#clone" do + before :each do + @a = Matrix[[1, 2], [3, 4], [5, 6]] + end + + it "returns a shallow copy of the matrix" do + b = @a.clone + @a.should_not equal(b) + b.should be_kind_of(Matrix) + b.should == @a + 0.upto(@a.row_size - 1) do |i| + @a.row(i).should_not equal(b.row(i)) + end + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.clone.should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/coerce_spec.rb b/spec/rubyspec/library/matrix/coerce_spec.rb new file mode 100644 index 0000000000..6e653315a6 --- /dev/null +++ b/spec/rubyspec/library/matrix/coerce_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#coerce" do + it "needs to be reviewed for spec completeness" + + it "allows the division of fixnum by a Matrix " do + (1/Matrix[[0,1],[-1,0]]).should == Matrix[[0,-1],[1,0]] + end +end diff --git a/spec/rubyspec/library/matrix/collect_spec.rb b/spec/rubyspec/library/matrix/collect_spec.rb new file mode 100644 index 0000000000..1830aed103 --- /dev/null +++ b/spec/rubyspec/library/matrix/collect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Matrix#collect" do + it_behaves_like(:collect, :collect) +end diff --git a/spec/rubyspec/library/matrix/column_size_spec.rb b/spec/rubyspec/library/matrix/column_size_spec.rb new file mode 100644 index 0000000000..b1aae01bbc --- /dev/null +++ b/spec/rubyspec/library/matrix/column_size_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#column_size" do + it "returns the number of columns" do + Matrix[ [1,2], [3,4] ].column_size.should == 2 + end + + it "returns 0 for empty matrices" do + Matrix[ [], [] ].column_size.should == 0 + Matrix[ ].column_size.should == 0 + end +end diff --git a/spec/rubyspec/library/matrix/column_spec.rb b/spec/rubyspec/library/matrix/column_spec.rb new file mode 100644 index 0000000000..de84e33e8d --- /dev/null +++ b/spec/rubyspec/library/matrix/column_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#column" do + before :all do + @m = Matrix[[1,2,3], [2,3,4]] + end + + it "returns a Vector when called without a block" do + @m.column(1).should == Vector[2,3] + end + + it "yields each element in the column to the block" do + a = [] + @m.column(1) {|n| a << n } + a.should == [2,3] + end + + it "counts backwards for negative argument" do + @m.column(-1).should == Vector[3, 4] + end + + it "returns self when called with a block" do + @m.column(0) { |x| x }.should equal(@m) + end + + it "returns nil when out of bounds" do + @m.column(3).should == nil + end + + it "never yields when out of bounds" do + lambda { @m.column(3){ raise } }.should_not raise_error + lambda { @m.column(-4){ raise } }.should_not raise_error + end +end diff --git a/spec/rubyspec/library/matrix/column_vector_spec.rb b/spec/rubyspec/library/matrix/column_vector_spec.rb new file mode 100644 index 0000000000..f0cc46d646 --- /dev/null +++ b/spec/rubyspec/library/matrix/column_vector_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.column_vector" do + + it "returns a single column Matrix when called with an Array" do + m = Matrix.column_vector([4,5,6]) + m.should be_an_instance_of(Matrix) + m.should == Matrix[ [4],[5],[6] ] + end + + it "returns an empty Matrix when called with an empty Array" do + m = Matrix.column_vector([]) + m.should be_an_instance_of(Matrix) + m.row_size.should == 0 + m.column_size.should == 1 + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.column_vector([4,5,6]).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/column_vectors_spec.rb b/spec/rubyspec/library/matrix/column_vectors_spec.rb new file mode 100644 index 0000000000..8af64f83c8 --- /dev/null +++ b/spec/rubyspec/library/matrix/column_vectors_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#column_vectors" do + + before :each do + @vectors = Matrix[ [1,2], [3,4] ].column_vectors + end + + it "returns an Array" do + Matrix[ [1,2], [3,4] ].column_vectors.should be_an_instance_of(Array) + end + + it "returns an Array of Vectors" do + @vectors.all? {|v| v.should be_an_instance_of(Vector)} + end + + it "returns each column as a Vector" do + @vectors.should == [Vector[1,3], Vector[2,4]] + end + + it "returns an empty Array for empty matrices" do + Matrix[ [] ].column_vectors.should == [] + end + +end diff --git a/spec/rubyspec/library/matrix/columns_spec.rb b/spec/rubyspec/library/matrix/columns_spec.rb new file mode 100644 index 0000000000..b5fd5633bf --- /dev/null +++ b/spec/rubyspec/library/matrix/columns_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.columns" do + before :each do + @a = [1, 2] + @b = [3, 4] + @m = Matrix.columns([@a, @b]) + end + + it "creates a Matrix from argument columns" do + @m.should be_an_instance_of(Matrix) + @m.column(0).to_a.should == @a + @m.column(1).to_a.should == @b + end + + it "accepts Vectors as argument columns" do + m = Matrix.columns([Vector[*@a], Vector[*@b]]) + m.should == @m + m.column(0).to_a.should == @a + m.column(1).to_a.should == @b + end + + it "handles empty matrices" do + e = Matrix.columns([]) + e.row_size.should == 0 + e.column_size.should == 0 + e.should == Matrix[] + + v = Matrix.columns([[],[],[]]) + v.row_size.should == 0 + v.column_size.should == 3 + v.should == Matrix[[], [], []].transpose + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.columns([[1]]).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/conj_spec.rb b/spec/rubyspec/library/matrix/conj_spec.rb new file mode 100644 index 0000000000..33221f7055 --- /dev/null +++ b/spec/rubyspec/library/matrix/conj_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/conjugate', __FILE__) + +describe "Matrix#conj" do + it_behaves_like(:matrix_conjugate, :conj) +end diff --git a/spec/rubyspec/library/matrix/conjugate_spec.rb b/spec/rubyspec/library/matrix/conjugate_spec.rb new file mode 100644 index 0000000000..fd19f7689c --- /dev/null +++ b/spec/rubyspec/library/matrix/conjugate_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/conjugate', __FILE__) + +describe "Matrix#conjugate" do + it_behaves_like(:matrix_conjugate, :conjugate) +end diff --git a/spec/rubyspec/library/matrix/constructor_spec.rb b/spec/rubyspec/library/matrix/constructor_spec.rb new file mode 100644 index 0000000000..ae707166cd --- /dev/null +++ b/spec/rubyspec/library/matrix/constructor_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.[]" do + + it "requires arrays as parameters" do + lambda { Matrix[5] }.should raise_error(TypeError) + lambda { Matrix[nil] }.should raise_error(TypeError) + lambda { Matrix[1..2] }.should raise_error(TypeError) + lambda { Matrix[[1, 2], 3] }.should raise_error(TypeError) + end + + it "creates an empty Matrix with no arguments" do + m = Matrix[] + m.column_size.should == 0 + m.row_size.should == 0 + end + + it "raises for non-rectangular matrices" do + lambda{ Matrix[ [0], [0,1] ] }.should \ + raise_error(Matrix::ErrDimensionMismatch) + lambda{ Matrix[ [0,1], [0,1,2], [0,1] ]}.should \ + raise_error(Matrix::ErrDimensionMismatch) + end + + it "accepts vector arguments" do + a = Matrix[Vector[1, 2], Vector[3, 4]] + a.should be_an_instance_of(Matrix) + a.should == Matrix[ [1, 2], [3, 4] ] + end + + it "tries to calls :to_ary on arguments" do + array = mock('ary') + array.should_receive(:to_ary).and_return([1,2]) + Matrix[array, [3,4] ].should == Matrix[ [1,2], [3,4] ] + end + + + it "returns a Matrix object" do + Matrix[ [1] ].should be_an_instance_of(Matrix) + end + + it "can create an nxn Matrix" do + m = Matrix[ [20,30], [40.5, 9] ] + m.row_size.should == 2 + m.column_size.should == 2 + m.column(0).should == Vector[20, 40.5] + m.column(1).should == Vector[30, 9] + m.row(0).should == Vector[20, 30] + m.row(1).should == Vector[40.5, 9] + end + + it "can create a 0xn Matrix" do + m = Matrix[ [], [], [] ] + m.row_size.should == 3 + m.column_size.should == 0 + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub[ [20,30], [40.5, 9] ].should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/det_spec.rb b/spec/rubyspec/library/matrix/det_spec.rb new file mode 100644 index 0000000000..698de34fd1 --- /dev/null +++ b/spec/rubyspec/library/matrix/det_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/determinant', __FILE__) +require 'matrix' + +describe "Matrix#det" do + it_behaves_like(:determinant, :det) +end diff --git a/spec/rubyspec/library/matrix/determinant_spec.rb b/spec/rubyspec/library/matrix/determinant_spec.rb new file mode 100644 index 0000000000..9ad34c6fc3 --- /dev/null +++ b/spec/rubyspec/library/matrix/determinant_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/determinant', __FILE__) +require 'matrix' + +describe "Matrix#determinant" do + it_behaves_like(:determinant, :determinant) +end diff --git a/spec/rubyspec/library/matrix/diagonal_spec.rb b/spec/rubyspec/library/matrix/diagonal_spec.rb new file mode 100644 index 0000000000..c88a92b5cd --- /dev/null +++ b/spec/rubyspec/library/matrix/diagonal_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.diagonal" do + before :each do + @m = Matrix.diagonal(10, 11, 12, 13, 14) + end + + it "returns an object of type Matrix" do + @m.should be_kind_of(Matrix) + end + + it "returns a square Matrix of the right size" do + @m.column_size.should == 5 + @m.row_size.should == 5 + end + + it "sets the diagonal to the arguments" do + (0..4).each do |i| + @m[i, i].should == i + 10 + end + end + + it "fills all non-diagonal cells with 0" do + (0..4).each do |i| + (0..4).each do |j| + if i != j + @m[i, j].should == 0 + end + end + end + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.diagonal(1).should be_an_instance_of(MatrixSub) + end + end +end + +describe "Matrix.diagonal?" do + it "returns true for a diagonal Matrix" do + Matrix.diagonal([1, 2, 3]).diagonal?.should be_true + end + + it "returns true for a zero square Matrix" do + Matrix.zero(3).diagonal?.should be_true + end + + it "returns false for a non diagonal square Matrix" do + Matrix[[0, 1], [0, 0]].diagonal?.should be_false + Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].diagonal?.should be_false + end + + it "returns true for an empty 0x0 matrix" do + Matrix.empty(0,0).diagonal?.should be_true + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[0], [0]], + Matrix[[0, 0]], + Matrix.empty(0, 2), + Matrix.empty(2, 0), + ].each do |rectangual_matrix| + lambda { + rectangual_matrix.diagonal? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end +end diff --git a/spec/rubyspec/library/matrix/divide_spec.rb b/spec/rubyspec/library/matrix/divide_spec.rb new file mode 100644 index 0000000000..b602d7d10f --- /dev/null +++ b/spec/rubyspec/library/matrix/divide_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#/" do + before :each do + @a = Matrix[ [1, 2], [3, 4] ] + @b = Matrix[ [4, 5], [6, 7] ] + @c = Matrix[ [1.2, 2.4], [3.6, 4.8] ] + end + + it "returns the result of dividing self by another Matrix" do + (@a / @b).should be_close_to_matrix([[2.5, -1.5], [1.5, -0.5]]) + end + + conflicts_with :Prime do + it "returns the result of dividing self by a Fixnum" do + (@a / 2).should == Matrix[ [0, 1], [1, 2] ] + end + end + + conflicts_with :Prime do + it "returns the result of dividing self by a Bignum" do + (@a / bignum_value).should == Matrix[ [0, 0], [0, 0] ] + end + end + + it "returns the result of dividing self by a Float" do + (@c / 1.2).should == Matrix[ [1, 2], [3, 4] ] + end + + it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do + lambda { @a / Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch) + end + + it "returns an instance of Matrix" do + (@a / @b).should be_kind_of(Matrix) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + m = MatrixSub.ins + (m/m).should be_an_instance_of(MatrixSub) + (m/1).should be_an_instance_of(MatrixSub) + end + end + + it "raises a TypeError if other is of wrong type" do + lambda { @a / nil }.should raise_error(TypeError) + lambda { @a / "a" }.should raise_error(TypeError) + lambda { @a / [ [1, 2] ] }.should raise_error(TypeError) + lambda { @a / Object.new }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/matrix/each_spec.rb b/spec/rubyspec/library/matrix/each_spec.rb new file mode 100644 index 0000000000..18875692e6 --- /dev/null +++ b/spec/rubyspec/library/matrix/each_spec.rb @@ -0,0 +1,74 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#each" do + before :all do + @m = Matrix[ [1, 2, 3], [4, 5, 6] ] + @result = (1..6).to_a + end + + it "returns an Enumerator when called without a block" do + enum = @m.each + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == @result + end + + it "returns self" do + @m.each{}.should equal(@m) + end + + it "yields the elements starting with the those of the first row" do + a = [] + @m.each {|x| a << x} + a.should == @result + end +end + +describe "Matrix#each with an argument" do + before :all do + @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ] + @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ] + end + + it "raises an ArgumentError for unrecognized argument" do + lambda { + @m.each("all"){} + }.should raise_error(ArgumentError) + lambda { + @m.each(nil){} + }.should raise_error(ArgumentError) + lambda { + @m.each(:left){} + }.should raise_error(ArgumentError) + end + + it "yields the rights elements when passed :diagonal" do + @m.each(:diagonal).to_a.should == [1, 6] + @t.each(:diagonal).to_a.should == [1, 4] + end + + it "yields the rights elements when passed :off_diagonal" do + @m.each(:off_diagonal).to_a.should == [2, 3, 4, 5, 7, 8] + @t.each(:off_diagonal).to_a.should == [2, 3, 5, 6, 7, 8] + end + + it "yields the rights elements when passed :lower" do + @m.each(:lower).to_a.should == [1, 5, 6] + @t.each(:lower).to_a.should == [1, 3, 4, 5, 6, 7, 8] + end + + it "yields the rights elements when passed :strict_lower" do + @m.each(:strict_lower).to_a.should == [5] + @t.each(:strict_lower).to_a.should == [3, 5, 6, 7, 8] + end + + it "yields the rights elements when passed :strict_upper" do + @m.each(:strict_upper).to_a.should == [2, 3, 4, 7, 8] + @t.each(:strict_upper).to_a.should == [2] + end + + it "yields the rights elements when passed :upper" do + @m.each(:upper).to_a.should == [1, 2, 3, 4, 6, 7, 8] + @t.each(:upper).to_a.should == [1, 2, 4] + end +end diff --git a/spec/rubyspec/library/matrix/each_with_index_spec.rb b/spec/rubyspec/library/matrix/each_with_index_spec.rb new file mode 100644 index 0000000000..796a6c2a96 --- /dev/null +++ b/spec/rubyspec/library/matrix/each_with_index_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#each_with_index" do + before :all do + @m = Matrix[ [1, 2, 3], [4, 5, 6] ] + @result = [ + [1, 0, 0], + [2, 0, 1], + [3, 0, 2], + [4, 1, 0], + [5, 1, 1], + [6, 1, 2] + ] + end + + it "returns an Enumerator when called without a block" do + enum = @m.each_with_index + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == @result + end + + it "returns self" do + @m.each_with_index{}.should equal(@m) + end + + it "yields the elements starting with the those of the first row" do + a = [] + @m.each_with_index {|x, r, c| a << [x, r, c]} + a.should == @result + end +end + +describe "Matrix#each_with_index with an argument" do + before :all do + @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ] + @t = Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ] + end + + it "raises an ArgumentError for unrecognized argument" do + lambda { + @m.each_with_index("all"){} + }.should raise_error(ArgumentError) + lambda { + @m.each_with_index(nil){} + }.should raise_error(ArgumentError) + lambda { + @m.each_with_index(:left){} + }.should raise_error(ArgumentError) + end + + it "yields the rights elements when passed :diagonal" do + @m.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [6, 1, 1]] + @t.each_with_index(:diagonal).to_a.should == [[1, 0, 0], [4, 1, 1]] + end + + it "yields the rights elements when passed :off_diagonal" do + @m.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [5, 1, 0], [7, 1, 2], [8, 1, 3]] + @t.each_with_index(:off_diagonal).to_a.should == [[2, 0, 1], [3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]] + end + + it "yields the rights elements when passed :lower" do + @m.each_with_index(:lower).to_a.should == [[1, 0, 0], [5, 1, 0], [6, 1, 1]] + @t.each_with_index(:lower).to_a.should == [[1, 0, 0], [3, 1, 0], [4, 1, 1], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]] + end + + it "yields the rights elements when passed :strict_lower" do + @m.each_with_index(:strict_lower).to_a.should == [[5, 1, 0]] + @t.each_with_index(:strict_lower).to_a.should == [[3, 1, 0], [5, 2, 0], [6, 2, 1], [7, 3, 0], [8, 3, 1]] + end + + it "yields the rights elements when passed :strict_upper" do + @m.each_with_index(:strict_upper).to_a.should == [[2, 0, 1], [3, 0, 2], [4, 0, 3], [7, 1, 2], [8, 1, 3]] + @t.each_with_index(:strict_upper).to_a.should == [[2, 0, 1]] + end + + it "yields the rights elements when passed :upper" do + @m.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [3, 0, 2], [4, 0, 3], [6, 1, 1], [7, 1, 2], [8, 1, 3]] + @t.each_with_index(:upper).to_a.should == [[1, 0, 0], [2, 0, 1], [4, 1, 1]] + end +end diff --git a/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb new file mode 100644 index 0000000000..3443eeaaf9 --- /dev/null +++ b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalue_matrix_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::EigenvalueDecomposition#eigenvalue_matrix" do + it "returns a diagonal matrix with the eigenvalues on the diagonal" do + Matrix[[14, 16], [-6, -6]].eigensystem.eigenvalue_matrix.should == Matrix[[6, 0],[0, 2]] + Matrix[[1, 1], [-1, 1]].eigensystem.eigenvalue_matrix.should == Matrix[[Complex(1,1), 0],[0, Complex(1,-1)]] + end +end diff --git a/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb new file mode 100644 index 0000000000..c175a29947 --- /dev/null +++ b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvalues_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::EigenvalueDecomposition#eigenvalues" do + it "returns an array of complex eigenvalues for a rotation matrix" do + Matrix[[ 1, 1], + [-1, 1]].eigensystem.eigenvalues.sort_by{|v| v.imag}.should == + [ Complex(1, -1), Complex(1, 1)] + end + + it "returns an array of real eigenvalues for a symetric matrix" do + Matrix[[1, 2], + [2, 1]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should == + [ -1, 3 ] + end + + it "returns an array of real eigenvalues for a matrix" do + Matrix[[14, 16], + [-6, -6]].eigensystem.eigenvalues.sort.map!{|x| x.round(10)}.should == + [ 2, 6 ] + end +end diff --git a/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb new file mode 100644 index 0000000000..709ec68d0c --- /dev/null +++ b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvector_matrix_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::EigenvalueDecomposition#eigenvector_matrix" do + it "returns a complex eigenvector matrix given a rotation matrix" do + # Fix me: should test for linearity, not for equality + Matrix[[ 1, 1], + [-1, 1]].eigensystem.eigenvector_matrix.should == + Matrix[[1, 1], + [Complex(0, 1), Complex(0, -1)]] + end + + it "returns an real eigenvector matrix for a symetric matrix" do + # Fix me: should test for linearity, not for equality + Matrix[[1, 2], + [2, 1]].eigensystem.eigenvector_matrix.should == + Matrix[[0.7071067811865475, 0.7071067811865475], + [-0.7071067811865475, 0.7071067811865475]] + end +end diff --git a/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb new file mode 100644 index 0000000000..163bebe709 --- /dev/null +++ b/spec/rubyspec/library/matrix/eigenvalue_decomposition/eigenvectors_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::EigenvalueDecomposition#eigenvectors" do + it "returns an array of complex eigenvectors for a rotation matrix" do + # Fix me: should test for linearity, not for equality + Matrix[[ 1, 1], + [-1, 1]].eigensystem.eigenvectors.should == + [ Vector[1, Complex(0, 1)], + Vector[1, Complex(0, -1)] + ] + end + + it "returns an array of real eigenvectors for a symetric matrix" do + # Fix me: should test for linearity, not for equality + Matrix[[1, 2], + [2, 1]].eigensystem.eigenvectors.should == + [ Vector[0.7071067811865475, -0.7071067811865475], + Vector[0.7071067811865475, 0.7071067811865475] + ] + end +end diff --git a/spec/rubyspec/library/matrix/eigenvalue_decomposition/initialize_spec.rb b/spec/rubyspec/library/matrix/eigenvalue_decomposition/initialize_spec.rb new file mode 100644 index 0000000000..97d9be8f2d --- /dev/null +++ b/spec/rubyspec/library/matrix/eigenvalue_decomposition/initialize_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::EigenvalueDecomposition#initialize" do + it "raises an error if argument is not a matrix" do + lambda { + Matrix::EigenvalueDecomposition.new([[]]) + }.should raise_error(TypeError) + lambda { + Matrix::EigenvalueDecomposition.new(42) + }.should raise_error(TypeError) + end + + it "raises an error if matrix is not square" do + lambda { + Matrix::EigenvalueDecomposition.new(Matrix[[1, 2]]) + }.should raise_error(Matrix::ErrDimensionMismatch) + end + + it "never hangs" do + m = Matrix[ [0,0,0,0,0], [0,0,0,0,1], [0,0,0,1,0], [1,1,0,0,1], [1,0,1,0,1] ] + Matrix::EigenvalueDecomposition.new(m).should_not == "infinite loop" + end +end diff --git a/spec/rubyspec/library/matrix/eigenvalue_decomposition/to_a_spec.rb b/spec/rubyspec/library/matrix/eigenvalue_decomposition/to_a_spec.rb new file mode 100644 index 0000000000..e9b849eb97 --- /dev/null +++ b/spec/rubyspec/library/matrix/eigenvalue_decomposition/to_a_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::EigenvalueDecomposition#to_a" do + before :each do + @a = Matrix[[14, 16], [-6, -6]] + @e = Matrix::EigenvalueDecomposition.new(@a) + end + + it "returns an array of with [V, D, V.inv]" do + @e.to_a.should == [@e.v, @e.d, @e.v_inv] + end + + it "returns a factorization" do + v, d, v_inv = @e.to_a + (v * d * v_inv).map{|e| e.round(10)}.should == @a + end +end diff --git a/spec/rubyspec/library/matrix/element_reference_spec.rb b/spec/rubyspec/library/matrix/element_reference_spec.rb new file mode 100644 index 0000000000..f4f63c0f65 --- /dev/null +++ b/spec/rubyspec/library/matrix/element_reference_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#[]" do + + before :all do + @m = Matrix[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]] + end + + it "returns element at (i, j)" do + (0..3).each do |i| + (0..2).each do |j| + @m[i, j].should == (i * 3) + j + end + end + end + + it "returns nil for an invalid index pair" do + @m[8,1].should be_nil + @m[1,8].should be_nil + end + +end diff --git a/spec/rubyspec/library/matrix/empty_spec.rb b/spec/rubyspec/library/matrix/empty_spec.rb new file mode 100644 index 0000000000..cead6126ff --- /dev/null +++ b/spec/rubyspec/library/matrix/empty_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#empty?" do + it "returns true when the Matrix is empty" do + Matrix[ ].empty?.should be_true + Matrix[ [], [], [] ].empty?.should be_true + Matrix[ [], [], [] ].transpose.empty?.should be_true + end + + it "returns false when the Matrix has elements" do + Matrix[ [1, 2] ].empty?.should be_false + Matrix[ [1], [2] ].empty?.should be_false + end + + it "doesn't accept any parameter" do + lambda{ + Matrix[ [1, 2] ].empty?(42) + }.should raise_error(ArgumentError) + end +end + +describe "Matrix.empty" do + it "returns an empty matrix of the requested size" do + m = Matrix.empty(3, 0) + m.row_size.should == 3 + m.column_size.should == 0 + + m = Matrix.empty(0, 3) + m.row_size.should == 0 + m.column_size.should == 3 + end + + it "has arguments defaulting to 0" do + Matrix.empty.should == Matrix.empty(0, 0) + Matrix.empty(42).should == Matrix.empty(42, 0) + end + + it "does not accept more than two parameters" do + lambda{ + Matrix.empty(1, 2, 3) + }.should raise_error(ArgumentError) + end + + it "raises an error if both dimensions are > 0" do + lambda{ + Matrix.empty(1, 2) + }.should raise_error(ArgumentError) + end + + it "raises an error if any dimension is < 0" do + lambda{ + Matrix.empty(-2, 0) + }.should raise_error(ArgumentError) + + lambda{ + Matrix.empty(0, -2) + }.should raise_error(ArgumentError) + end + +end + +describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.empty(0, 1).should be_an_instance_of(MatrixSub) + end +end diff --git a/spec/rubyspec/library/matrix/eql_spec.rb b/spec/rubyspec/library/matrix/eql_spec.rb new file mode 100644 index 0000000000..e76d26753a --- /dev/null +++ b/spec/rubyspec/library/matrix/eql_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) +require 'matrix' + +describe "Matrix#eql?" do + it_behaves_like(:equal, :eql?) + + it "returns false if some elements are == but not eql?" do + Matrix[[1, 2],[3, 4]].eql?(Matrix[[1, 2],[3, 4.0]]).should be_false + end +end diff --git a/spec/rubyspec/library/matrix/equal_value_spec.rb b/spec/rubyspec/library/matrix/equal_value_spec.rb new file mode 100644 index 0000000000..e14a38b872 --- /dev/null +++ b/spec/rubyspec/library/matrix/equal_value_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/equal_value', __FILE__) +require 'matrix' + +describe "Matrix#==" do + it_behaves_like(:equal, :==) + + it "returns true if some elements are == but not eql?" do + Matrix[[1, 2],[3, 4]].should == Matrix[[1, 2],[3, 4.0]] + end +end diff --git a/spec/rubyspec/library/matrix/exponent_spec.rb b/spec/rubyspec/library/matrix/exponent_spec.rb new file mode 100644 index 0000000000..188a1b34d2 --- /dev/null +++ b/spec/rubyspec/library/matrix/exponent_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#**" do + + describe "given an integer _n_" do + it "multiples the Matrix by itself _n_ times" do + m = Matrix[ [7,6], [3,9] ] + (m ** 1).should == m + (m ** 2).should == Matrix[ [67, 96], [48,99] ] + (m ** 2).should == m * m + (m ** 3).should == m * m * m + (m ** 4).should == m * m * m * m + (m ** 5).should == m * m * m * m * m + end + + it "raises a ErrDimensionMismatch for non square matrices" do + m = Matrix[ [1, 1], [1, 2], [2, 3]] + lambda { m ** 3 }.should raise_error(Matrix::ErrDimensionMismatch) + lambda { m ** 0 }.should raise_error(Matrix::ErrDimensionMismatch) + end + + describe "that is <= 0" do + it "returns the inverse of **(-n)" do + m = Matrix[ [1, 1], [1, 2] ] + (m ** -2).should == Matrix[ [5, -3], [-3, 2]] + (m ** -4).should == (m.inverse ** 4) + end + + it "raises a ErrDimensionMismatch for irregular matrices" do + m = Matrix[ [1, 1], [1, 1] ] + lambda { m ** -2 }.should raise_error(Matrix::ErrNotRegular) + lambda { m ** 0 }.should raise_error(Matrix::ErrNotRegular) + end + end + end + + it "returns the power for non integer powers" do + a = Matrix[[5, 4], [4, 5]] + ((a ** 0.5) ** 2).round(8).should == a + a = Matrix[[7, 10], [15, 22]] + ((a ** 0.25) ** 4).round(8).should == a + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + (MatrixSub.ins ** 1).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/find_index_spec.rb b/spec/rubyspec/library/matrix/find_index_spec.rb new file mode 100644 index 0000000000..8ea891644a --- /dev/null +++ b/spec/rubyspec/library/matrix/find_index_spec.rb @@ -0,0 +1,146 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#find_index without any argument" do + before :all do + @m = Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ] + end + + it "returns an Enumerator when called without a block" do + enum = @m.find_index + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == [1, 2, 3, 4, 5, 6, 7, 8] + end + + it "returns nil if the block is always false" do + @m.find_index{false}.should be_nil + end + + it "returns the first index for which the block is true" do + @m.find_index{|x| x >= 3}.should == [0, 2] + end +end + +describe "Matrix#find_index with a subselection argument" do + before :all do + @tests = [ + [ Matrix[ [1, 2, 3, 4], [5, 6, 7, 8] ], { + diagonal: [1, 6] , + off_diagonal: [2, 3, 4, 5, 7, 8], + lower: [1, 5, 6] , + strict_lower: [5] , + strict_upper: [2, 3, 4, 7, 8] , + upper: [1, 2, 3, 4, 6, 7, 8] , + } + ], + [ Matrix[ [1, 2], [3, 4], [5, 6], [7, 8] ], { + diagonal: [1, 4] , + off_diagonal: [2, 3, 5, 6, 7, 8], + lower: [1, 3, 4, 5, 6, 7, 8] , + strict_lower: [3, 5, 6, 7, 8] , + strict_upper: [2] , + upper: [1, 2, 4] , + } + ]] + end + + describe "and no generic argument" do + it "returns an Enumerator when called without a block" do + @tests.each do |matrix, h| + h.each do |selector, result| + matrix.find_index(selector).should be_an_instance_of(Enumerator) + end + end + end + + it "yields the rights elements" do + @tests.each do |matrix, h| + h.each do |selector, result| + matrix.find_index(selector).to_a.should == result + end + end + end + + it "returns the first index for which the block returns true" do + @tests.each do |matrix, h| + h.each do |selector, result| + cnt = result.size.div 2 + which = result[cnt] + idx = matrix.find_index(selector){|x| cnt -= 1; x == which} + matrix[*idx].should == which + cnt.should == -1 + end + end + end + + it "returns nil if the block is always false" do + @tests.each do |matrix, h| + h.each do |selector, result| + matrix.find_index(selector){ nil }.should == nil + end + end + end + + end + + describe "and a generic argument" do + it "ignores a block" do + @m.find_index(42, :diagonal){raise "oups"}.should == nil + end + + it "returns the index of the requested value" do + @tests.each do |matrix, h| + h.each do |selector, result| + cnt = result.size / 2 + which = result[cnt] + idx = matrix.find_index(which, selector) + matrix[*idx].should == which + end + end + end + + it "returns nil if the requested value is not found" do + @tests.each do |matrix, h| + h.each do |selector, result| + matrix.find_index(42, selector).should == nil + end + end + end + end + +end + +describe "Matrix#find_index with only a generic argument" do + before :all do + @m = Matrix[ [1, 2, 3, 4], [1, 2, 3, 4] ] + end + + it "returns nil if the value is not found" do + @m.find_index(42).should be_nil + end + + it "returns the first index for of the requested value" do + @m.find_index(3).should == [0, 2] + end + + it "ignores a block" do + @m.find_index(4){raise "oups"}.should == [0, 3] + end +end + +describe "Matrix#find_index with two arguments" do + it "raises an ArgumentError for an unrecognized last argument" do + lambda { + @m.find_index(1, "all"){} + }.should raise_error(ArgumentError) + lambda { + @m.find_index(1, nil){} + }.should raise_error(ArgumentError) + lambda { + @m.find_index(1, :left){} + }.should raise_error(ArgumentError) + lambda { + @m.find_index(:diagonal, 1){} + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/matrix/fixtures/classes.rb b/spec/rubyspec/library/matrix/fixtures/classes.rb new file mode 100644 index 0000000000..394377135f --- /dev/null +++ b/spec/rubyspec/library/matrix/fixtures/classes.rb @@ -0,0 +1,7 @@ +require 'matrix' + +class MatrixSub < Matrix + def self.ins + rows([[1, 0], [0, 1]]) + end +end diff --git a/spec/rubyspec/library/matrix/hash_spec.rb b/spec/rubyspec/library/matrix/hash_spec.rb new file mode 100644 index 0000000000..b2e5b579b7 --- /dev/null +++ b/spec/rubyspec/library/matrix/hash_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#hash" do + + it "returns a Fixnum" do + Matrix[ [1,2] ].hash.should be_an_instance_of(Fixnum) + end + + it "returns the same value for the same matrix" do + data = [ [40,5], [2,7] ] + Matrix[ *data ].hash.should == Matrix[ *data ].hash + end + +end diff --git a/spec/rubyspec/library/matrix/hermitian_spec.rb b/spec/rubyspec/library/matrix/hermitian_spec.rb new file mode 100644 index 0000000000..cbfea433c2 --- /dev/null +++ b/spec/rubyspec/library/matrix/hermitian_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.hermitian?" do + it "returns true for a hermitian Matrix" do + Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, -3), 5, 6]].hermitian?.should be_true + end + + it "returns true for a 0x0 empty matrix" do + Matrix.empty.hermitian?.should be_true + end + + it "returns false for an assymetric Matrix" do + Matrix[[1, 2],[-2, 1]].hermitian?.should be_false + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[0], [0]], + Matrix[[0, 0]], + Matrix.empty(0, 2), + Matrix.empty(2, 0), + ].each do |rectangual_matrix| + lambda { + rectangual_matrix.hermitian? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end + + it "returns false for a matrix with complex values on the diagonal" do + Matrix[[Complex(1,1)]].hermitian?.should be_false + Matrix[[Complex(1,0)]].hermitian?.should be_true + end +end diff --git a/spec/rubyspec/library/matrix/identity_spec.rb b/spec/rubyspec/library/matrix/identity_spec.rb new file mode 100644 index 0000000000..bc7df98dde --- /dev/null +++ b/spec/rubyspec/library/matrix/identity_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/identity', __FILE__) + +describe "Matrix.identity" do + it_behaves_like(:matrix_identity, :identity) +end diff --git a/spec/rubyspec/library/matrix/imag_spec.rb b/spec/rubyspec/library/matrix/imag_spec.rb new file mode 100644 index 0000000000..41083879e4 --- /dev/null +++ b/spec/rubyspec/library/matrix/imag_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/imaginary', __FILE__) + +describe "Matrix#imag" do + it_behaves_like(:matrix_imaginary, :imag) +end diff --git a/spec/rubyspec/library/matrix/imaginary_spec.rb b/spec/rubyspec/library/matrix/imaginary_spec.rb new file mode 100644 index 0000000000..2a05f1d5c3 --- /dev/null +++ b/spec/rubyspec/library/matrix/imaginary_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/imaginary', __FILE__) + +describe "Matrix#imaginary" do + it_behaves_like(:matrix_imaginary, :imaginary) +end diff --git a/spec/rubyspec/library/matrix/inspect_spec.rb b/spec/rubyspec/library/matrix/inspect_spec.rb new file mode 100644 index 0000000000..bf623e1a85 --- /dev/null +++ b/spec/rubyspec/library/matrix/inspect_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#inspect" do + + it "returns a stringified representation of the Matrix" do + Matrix[ [1,2], [2,1] ].inspect.should == "Matrix[[1, 2], [2, 1]]" + end + + it "returns 'Matrix.empty(...)' for empty matrices" do + Matrix[ [], [], [] ].inspect.should == "Matrix.empty(3, 0)" + Matrix.columns([ [], [], [] ]).inspect.should == "Matrix.empty(0, 3)" + end + + it "calls inspect on its contents" do + obj = mock("some_value") + obj.should_receive(:inspect).and_return("some_value") + Matrix[ [1, 2], [3, obj] ].inspect.should == "Matrix[[1, 2], [3, some_value]]" + end + + describe "for a subclass of Matrix" do + it "returns a string using the subclass' name" do + MatrixSub.ins.inspect.should == "MatrixSub[[1, 0], [0, 1]]" + end + end +end diff --git a/spec/rubyspec/library/matrix/inv_spec.rb b/spec/rubyspec/library/matrix/inv_spec.rb new file mode 100644 index 0000000000..0491aa7b07 --- /dev/null +++ b/spec/rubyspec/library/matrix/inv_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../shared/inverse', __FILE__) + +describe "Matrix#inv" do + it_behaves_like(:inverse, :inv) +end diff --git a/spec/rubyspec/library/matrix/inverse_from_spec.rb b/spec/rubyspec/library/matrix/inverse_from_spec.rb new file mode 100644 index 0000000000..958b3b7408 --- /dev/null +++ b/spec/rubyspec/library/matrix/inverse_from_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#inverse_from" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/inverse_spec.rb b/spec/rubyspec/library/matrix/inverse_spec.rb new file mode 100644 index 0000000000..33a1f2f5de --- /dev/null +++ b/spec/rubyspec/library/matrix/inverse_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../shared/inverse', __FILE__) + +describe "Matrix#inverse" do + it_behaves_like(:inverse, :inverse) +end diff --git a/spec/rubyspec/library/matrix/lower_triangular_spec.rb b/spec/rubyspec/library/matrix/lower_triangular_spec.rb new file mode 100644 index 0000000000..62b3df2104 --- /dev/null +++ b/spec/rubyspec/library/matrix/lower_triangular_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.lower_triangular?" do + it "returns true for a square lower triangular Matrix" do + Matrix[[1, 0, 0], [1, 2, 0], [1, 2, 3]].lower_triangular?.should be_true + Matrix.diagonal([1, 2, 3]).lower_triangular?.should be_true + Matrix[[1, 0], [1, 2], [1, 2], [1, 2]].lower_triangular?.should be_true + Matrix[[1, 0, 0, 0], [1, 2, 0, 0]].lower_triangular?.should be_true + end + + it "returns true for an empty Matrix" do + Matrix.empty(3, 0).lower_triangular?.should be_true + Matrix.empty(0, 3).lower_triangular?.should be_true + Matrix.empty(0, 0).lower_triangular?.should be_true + end + + it "returns false for a non lower triangular square Matrix" do + Matrix[[0, 1], [0, 0]].lower_triangular?.should be_false + Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].lower_triangular?.should be_false + Matrix[[0, 1], [0, 0], [0, 0], [0, 0]].lower_triangular?.should be_false + Matrix[[0, 0, 0, 1], [0, 0, 0, 0]].lower_triangular?.should be_false + end +end diff --git a/spec/rubyspec/library/matrix/lup_decomposition/determinant_spec.rb b/spec/rubyspec/library/matrix/lup_decomposition/determinant_spec.rb new file mode 100644 index 0000000000..f73c65ba8f --- /dev/null +++ b/spec/rubyspec/library/matrix/lup_decomposition/determinant_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::LUPDecomposition#determinant" do + it "returns the determinant when the matrix is square" do + a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]] + a.lup.determinant.should == 15120 # == a.determinant + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[7, 8, 9], [14, 46, 51]], + Matrix[[7, 8], [14, 46], [28, 82]], + ].each do |m| + lup = m.lup + lambda { + lup.determinant + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end +end diff --git a/spec/rubyspec/library/matrix/lup_decomposition/initialize_spec.rb b/spec/rubyspec/library/matrix/lup_decomposition/initialize_spec.rb new file mode 100644 index 0000000000..953389083f --- /dev/null +++ b/spec/rubyspec/library/matrix/lup_decomposition/initialize_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::LUPDecomposition#initialize" do + it "raises an error if argument is not a matrix" do + lambda { + Matrix::LUPDecomposition.new([[]]) + }.should raise_error(TypeError) + lambda { + Matrix::LUPDecomposition.new(42) + }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/matrix/lup_decomposition/l_spec.rb b/spec/rubyspec/library/matrix/lup_decomposition/l_spec.rb new file mode 100644 index 0000000000..c50b5f712e --- /dev/null +++ b/spec/rubyspec/library/matrix/lup_decomposition/l_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::LUPDecomposition#l" do + before :each do + @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]] + @lu = Matrix::LUPDecomposition.new(@a) + @l = @lu.l + end + + it "returns the first element of to_a" do + @l.should == @lu.to_a[0] + end + + it "returns a lower triangular matrix" do + @l.lower_triangular?.should be_true + end +end diff --git a/spec/rubyspec/library/matrix/lup_decomposition/p_spec.rb b/spec/rubyspec/library/matrix/lup_decomposition/p_spec.rb new file mode 100644 index 0000000000..837b65a915 --- /dev/null +++ b/spec/rubyspec/library/matrix/lup_decomposition/p_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::LUPDecomposition#p" do + before :each do + @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]] + @lu = Matrix::LUPDecomposition.new(@a) + @p = @lu.p + end + + it "returns the third element of to_a" do + @p.should == @lu.to_a[2] + end + + it "returns a permutation matrix" do + @p.permutation?.should be_true + end +end diff --git a/spec/rubyspec/library/matrix/lup_decomposition/solve_spec.rb b/spec/rubyspec/library/matrix/lup_decomposition/solve_spec.rb new file mode 100644 index 0000000000..c4ef42bcea --- /dev/null +++ b/spec/rubyspec/library/matrix/lup_decomposition/solve_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::LUPDecomposition#solve" do + describe "for rectangular matrices" do + it "raises an error for singular matrices" do + a = Matrix[[1, 2, 3], [1, 3, 5], [2, 5, 8]] + lu = Matrix::LUPDecomposition.new(a) + lambda { + lu.solve(a) + }.should raise_error(Matrix::ErrNotRegular) + end + + describe "for non singular matrices" do + before :each do + @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]] + @lu = Matrix::LUPDecomposition.new(@a) + end + + it "returns the appropriate empty matrix when given an empty matrix" do + @lu.solve(Matrix.empty(3,0)).should == Matrix.empty(3,0) + empty = Matrix::LUPDecomposition.new(Matrix.empty(0, 0)) + empty.solve(Matrix.empty(0,3)).should == Matrix.empty(0,3) + end + + it "returns the right matrix when given a matrix of the appropriate size" do + solution = Matrix[[1, 2, 3, 4], [0, 1, 2, 3], [-1, -2, -3, -4]] + values = Matrix[[-2, 4, 10, 16], [-37, -28, -19, -10], [-135, -188, -241, -294]] # == @a * solution + @lu.solve(values).should == solution + end + + it "raises an error when given a matrix of the wrong size" do + values = Matrix[[1, 2, 3, 4], [0, 1, 2, 3]] + lambda { + @lu.solve(values) + }.should raise_error(Matrix::ErrDimensionMismatch) + end + + it "returns the right vector when given a vector of the appropriate size" do + solution = Vector[1, 2, -1] + values = Vector[14, 55, 29] # == @a * solution + @lu.solve(values).should == solution + end + + it "raises an error when given a vector of the wrong size" do + values = Vector[14, 55] + lambda { + @lu.solve(values) + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end + end +end diff --git a/spec/rubyspec/library/matrix/lup_decomposition/to_a_spec.rb b/spec/rubyspec/library/matrix/lup_decomposition/to_a_spec.rb new file mode 100644 index 0000000000..20be26eb9c --- /dev/null +++ b/spec/rubyspec/library/matrix/lup_decomposition/to_a_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::LUPDecomposition#to_a" do + before :each do + @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]] + @lu = Matrix::LUPDecomposition.new(@a) + @to_a = @lu.to_a + @l, @u, @p = @to_a + end + + it "returns an array of three matrices" do + @to_a.should be_kind_of(Array) + @to_a.length.should == 3 + @to_a.each{|m| m.should be_kind_of(Matrix)} + end + + it "returns [l, u, p] such that l*u == a*p" do + (@l * @u).should == (@p * @a) + end + + it "returns the right values for rectangular matrices" do + [ + Matrix[[7, 8, 9], [14, 46, 51]], + Matrix[[4, 11], [5, 8], [3, 4]], + ].each do |a| + l, u, p = Matrix::LUPDecomposition.new(a).to_a + (l * u).should == (p * a) + end + end + + it "has other properties implied by the specs of #l, #u and #p" +end diff --git a/spec/rubyspec/library/matrix/lup_decomposition/u_spec.rb b/spec/rubyspec/library/matrix/lup_decomposition/u_spec.rb new file mode 100644 index 0000000000..97e8580c58 --- /dev/null +++ b/spec/rubyspec/library/matrix/lup_decomposition/u_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::LUPDecomposition#u" do + before :each do + @a = Matrix[[7, 8, 9], [14, 46, 51], [28, 82, 163]] + @lu = Matrix::LUPDecomposition.new(@a) + @u = @lu.u + end + + it "returns the second element of to_a" do + @u.should == @lu.to_a[1] + end + + it "returns an upper triangular matrix" do + @u.upper_triangular?.should be_true + end +end diff --git a/spec/rubyspec/library/matrix/map_spec.rb b/spec/rubyspec/library/matrix/map_spec.rb new file mode 100644 index 0000000000..e18ab6eb7a --- /dev/null +++ b/spec/rubyspec/library/matrix/map_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/collect', __FILE__) + +describe "Matrix#map" do + it_behaves_like(:collect, :map) +end diff --git a/spec/rubyspec/library/matrix/minor_spec.rb b/spec/rubyspec/library/matrix/minor_spec.rb new file mode 100644 index 0000000000..e02e0de07d --- /dev/null +++ b/spec/rubyspec/library/matrix/minor_spec.rb @@ -0,0 +1,85 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#minor" do + before :each do + @matrix = Matrix[ [1,2], [3,4], [5,6] ] + end + + describe "with start_row, nrows, start_col, ncols" do + it "returns the given portion of the Matrix" do + @matrix.minor(0,1,0,2).should == Matrix[ [1, 2] ] + @matrix.minor(1,2,1,1).should == Matrix[ [4], [6] ] + end + + it "returns an empty Matrix if nrows or ncols is 0" do + @matrix.minor(0,0,0,0).should == Matrix[] + @matrix.minor(1,0,1,0).should == Matrix[] + @matrix.minor(1,0,1,1).should == Matrix.columns([[]]) + @matrix.minor(1,1,1,0).should == Matrix[[]] + end + + it "returns nil for out-of-bounds start_row/col" do + r = @matrix.row_size + 1 + c = @matrix.column_size + 1 + @matrix.minor(r,0,0,10).should == nil + @matrix.minor(0,10,c,9).should == nil + @matrix.minor(-r,0,0,10).should == nil + @matrix.minor(0,10,-c,9).should == nil + end + + it "returns nil for negative nrows or ncols" do + @matrix.minor(0,1,0,-1).should == nil + @matrix.minor(0,-1,0,1).should == nil + end + + it "start counting backwards for start_row or start_col below zero" do + @matrix.minor(0, 1, -1, 1).should == @matrix.minor(0, 1, 1, 1) + @matrix.minor(-1, 1, 0, 1).should == @matrix.minor(2, 1, 0, 1) + end + + it "returns empty matrices for extreme start_row/col" do + @matrix.minor(3,10,1,10).should == Matrix.columns([[]]) + @matrix.minor(1,10,2,10).should == Matrix[[], []] + @matrix.minor(3,0,0,10).should == Matrix.columns([[], []]) + end + + it "ignores big nrows or ncols" do + @matrix.minor(0,1,0,20).should == Matrix[ [1, 2] ] + @matrix.minor(1,20,1,1).should == Matrix[ [4], [6] ] + end + end + + describe "with col_range, row_range" do + it "returns the given portion of the Matrix" do + @matrix.minor(0..0, 0..1).should == Matrix[ [1, 2] ] + @matrix.minor(1..2, 1..2).should == Matrix[ [4], [6] ] + @matrix.minor(1...3, 1...3).should == Matrix[ [4], [6] ] + end + + it "returns nil if col_range or row_range is out of range" do + r = @matrix.row_size + 1 + c = @matrix.column_size + 1 + @matrix.minor(r..6, c..6).should == nil + @matrix.minor(0..1, c..6).should == nil + @matrix.minor(r..6, 0..1).should == nil + @matrix.minor(-r..6, -c..6).should == nil + @matrix.minor(0..1, -c..6).should == nil + @matrix.minor(-r..6, 0..1).should == nil + end + + it "start counting backwards for col_range or row_range below zero" do + @matrix.minor(0..1, -2..-1).should == @matrix.minor(0..1, 0..1) + @matrix.minor(0..1, -2..1).should == @matrix.minor(0..1, 0..1) + @matrix.minor(-2..-1, 0..1).should == @matrix.minor(1..2, 0..1) + @matrix.minor(-2..2, 0..1).should == @matrix.minor(1..2, 0..1) + end + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.minor(0, 1, 0, 1).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/minus_spec.rb b/spec/rubyspec/library/matrix/minus_spec.rb new file mode 100644 index 0000000000..fe2d716882 --- /dev/null +++ b/spec/rubyspec/library/matrix/minus_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#-" do + before :each do + @a = Matrix[ [1, 2], [3, 4] ] + @b = Matrix[ [4, 5], [6, 7] ] + end + + it "returns the result of subtracting the corresponding elements of other from self" do + (@a - @b).should == Matrix[ [-3,-3], [-3,-3] ] + end + + it "returns an instance of Matrix" do + (@a - @b).should be_kind_of(Matrix) + end + + it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do + lambda { @a - Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch) + end + + it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do + lambda { @a - 2 }.should raise_error(Matrix::ErrOperationNotDefined) + lambda { @a - 1.2 }.should raise_error(Matrix::ErrOperationNotDefined) + lambda { @a - bignum_value }.should raise_error(Matrix::ErrOperationNotDefined) + end + + it "raises a TypeError if other is of wrong type" do + lambda { @a - nil }.should raise_error(TypeError) + lambda { @a - "a" }.should raise_error(TypeError) + lambda { @a - [ [1, 2] ] }.should raise_error(TypeError) + lambda { @a - Object.new }.should raise_error(TypeError) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + m = MatrixSub.ins + (m-m).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/multiply_spec.rb b/spec/rubyspec/library/matrix/multiply_spec.rb new file mode 100644 index 0000000000..dae87f5434 --- /dev/null +++ b/spec/rubyspec/library/matrix/multiply_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#*" do + before :each do + @a = Matrix[ [1, 2], [3, 4] ] + @b = Matrix[ [4, 5], [6, 7] ] + end + + it "returns the result of multiplying the corresponding elements of self and a Matrix" do + (@a * @b).should == Matrix[ [16,19], [36,43] ] + end + + it "returns the result of multiplying the corresponding elements of self and a Vector" do + (@a * Vector[1,2]).should == Vector[5, 11] + end + + it "returns the result of multiplying the elements of self and a Fixnum" do + (@a * 2).should == Matrix[ [2, 4], [6, 8] ] + end + + it "returns the result of multiplying the elements of self and a Bignum" do + (@a * bignum_value).should == Matrix[ + [9223372036854775808, 18446744073709551616], + [27670116110564327424, 36893488147419103232] + ] + end + + it "returns the result of multiplying the elements of self and a Float" do + (@a * 2.0).should == Matrix[ [2.0, 4.0], [6.0, 8.0] ] + end + + it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do + lambda { @a * Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch) + end + + it "returns a zero matrix if (nx0) * (0xn)" do + (Matrix[[],[],[]] * Matrix.columns([[],[],[]])).should == Matrix.zero(3) + end + + it "returns an empty matrix if (0xn) * (nx0)" do + (Matrix.columns([[],[],[]]) * Matrix[[],[],[]]).should == Matrix[] + end + + it "returns a mx0 matrix if (mxn) * (nx0)" do + (Matrix[[1,2],[3,4],[5,6]] * Matrix[[],[]]).should == Matrix[[],[],[]] + end + + it "returns a 0xm matrix if (0xm) * (mxn)" do + (Matrix.columns([[], [], []]) * Matrix[[1,2],[3,4],[5,6]]).should == Matrix.columns([[],[]]) + end + + it "raises a TypeError if other is of wrong type" do + lambda { @a * nil }.should raise_error(TypeError) + lambda { @a * "a" }.should raise_error(TypeError) + lambda { @a * [ [1, 2] ] }.should raise_error(TypeError) + lambda { @a * Object.new }.should raise_error(TypeError) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + m = MatrixSub.ins + (m*m).should be_an_instance_of(MatrixSub) + (m*1).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/new_spec.rb b/spec/rubyspec/library/matrix/new_spec.rb new file mode 100644 index 0000000000..82d2bd88a7 --- /dev/null +++ b/spec/rubyspec/library/matrix/new_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.new" do + it "is private" do + Matrix.should have_private_method(:new) + end +end diff --git a/spec/rubyspec/library/matrix/normal_spec.rb b/spec/rubyspec/library/matrix/normal_spec.rb new file mode 100644 index 0000000000..140909dcc2 --- /dev/null +++ b/spec/rubyspec/library/matrix/normal_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.normal?" do + # it "returns false for non normal matrices" do + # Matrix[[0, 1], [1, 2]].normal?.should == false + # end + + it "returns true for normal matrices" do + Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].normal?.should == true + Matrix[[0, Complex(0, 2)], [Complex(0, -2), 0]].normal?.should == true + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[0], [0]], + Matrix[[0, 0]], + Matrix.empty(0, 2), + Matrix.empty(2, 0), + ].each do |rectangual_matrix| + lambda { + rectangual_matrix.normal? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end +end diff --git a/spec/rubyspec/library/matrix/orthogonal_spec.rb b/spec/rubyspec/library/matrix/orthogonal_spec.rb new file mode 100644 index 0000000000..2e76b5924c --- /dev/null +++ b/spec/rubyspec/library/matrix/orthogonal_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.orthogonal?" do + it "returns false for non orthogonal matrices" do + Matrix[[0, 1], [1, 2]].orthogonal?.should == false + Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].orthogonal?.should == false + end + + it "returns true for orthogonal matrices" do + Matrix[[0, 1], [1, 0]].orthogonal?.should == true + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[0], [0]], + Matrix[[0, 0]], + Matrix.empty(0, 2), + Matrix.empty(2, 0), + ].each do |rectangual_matrix| + lambda { + rectangual_matrix.orthogonal? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end +end diff --git a/spec/rubyspec/library/matrix/permutation_spec.rb b/spec/rubyspec/library/matrix/permutation_spec.rb new file mode 100644 index 0000000000..7098c46015 --- /dev/null +++ b/spec/rubyspec/library/matrix/permutation_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#permutation?" do + it "returns true for a permutation Matrix" do + Matrix[[0, 1, 0], [0, 0, 1], [1, 0, 0]].permutation?.should be_true + end + + it "returns false for a non permutation square Matrix" do + Matrix[[0, 1], [0, 0]].permutation?.should be_false + Matrix[[-1, 0], [0, -1]].permutation?.should be_false + Matrix[[1, 0], [1, 0]].permutation?.should be_false + Matrix[[1, 0], [1, 1]].permutation?.should be_false + end + + it "returns true for an empty 0x0 matrix" do + Matrix.empty(0,0).permutation?.should be_true + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[0], [0]], + Matrix[[0, 0]], + Matrix.empty(0, 2), + Matrix.empty(2, 0), + ].each do |rectangual_matrix| + lambda { + rectangual_matrix.permutation? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end +end diff --git a/spec/rubyspec/library/matrix/plus_spec.rb b/spec/rubyspec/library/matrix/plus_spec.rb new file mode 100644 index 0000000000..59addfdf62 --- /dev/null +++ b/spec/rubyspec/library/matrix/plus_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#+" do + before :each do + @a = Matrix[ [1,2], [3,4] ] + @b = Matrix[ [4,5], [6,7] ] + end + + it "returns the result of adding the corresponding elements of self and other" do + (@a + @b).should == Matrix[ [5,7], [9,11] ] + end + + it "returns an instance of Matrix" do + (@a + @b).should be_kind_of(Matrix) + end + + it "raises a Matrix::ErrDimensionMismatch if the matrices are different sizes" do + lambda { @a + Matrix[ [1] ] }.should raise_error(Matrix::ErrDimensionMismatch) + end + + it "raises a ExceptionForMatrix::ErrOperationNotDefined if other is a Numeric Type" do + lambda { @a + 2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined) + lambda { @a + 1.2 }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined) + lambda { @a + bignum_value }.should raise_error(ExceptionForMatrix::ErrOperationNotDefined) + end + + it "raises a TypeError if other is of wrong type" do + lambda { @a + nil }.should raise_error(TypeError) + lambda { @a + "a" }.should raise_error(TypeError) + lambda { @a + [ [1, 2] ] }.should raise_error(TypeError) + lambda { @a + Object.new }.should raise_error(TypeError) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + m = MatrixSub.ins + (m+m).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/rank_spec.rb b/spec/rubyspec/library/matrix/rank_spec.rb new file mode 100644 index 0000000000..42b6de1ab8 --- /dev/null +++ b/spec/rubyspec/library/matrix/rank_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#rank" do + it "returns the rank of the Matrix" do + Matrix[ [7,6], [3,9] ].rank.should == 2 + end + + it "doesn't loop forever" do + Matrix[ [1,2,3], [4,5,6], [7,8,9] ].rank.should == 2 + Matrix[ [1, 2, 0, 3], [1, -2, 3, 0], [0, 0, 4, 8], [2, 4, 0, 6] ].rank. + should == 3 + end + + it "works for some easy rectangular matrices" do + Matrix[[0,0],[0,0],[1,0]].rank.should == 1 + Matrix[[0,1],[0,0],[1,0]].rank.should == 2 + end +end diff --git a/spec/rubyspec/library/matrix/real_spec.rb b/spec/rubyspec/library/matrix/real_spec.rb new file mode 100644 index 0000000000..98da7f8a7c --- /dev/null +++ b/spec/rubyspec/library/matrix/real_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#real?" do + it "returns true for matrices with all real entries" do + Matrix[ [1, 2], [3, 4] ].real?.should be_true + Matrix[ [1.9, 2], [3, 4] ].real?.should be_true + end + + it "returns true for empty matrices" do + Matrix.empty.real?.should be_true + end + + it "returns false if one element is a Complex" do + Matrix[ [Complex(1,1), 2], [3, 4] ].real?.should be_false + end + + conflicts_with :CMath do + it "returns false if one element is a Complex whose imaginary part is 0" do + Matrix[ [Complex(1,0), 2], [3, 4] ].real?.should be_false + end + end +end + +describe "Matrix#real" do + it "returns a matrix with the real part of the elements of the receiver" do + Matrix[ [1, 2], [3, 4] ].real.should == Matrix[ [1, 2], [3, 4] ] + Matrix[ [1.9, Complex(1,1)], [Complex(-0.42, 0), 4] ].real.should == Matrix[ [1.9, 1], [-0.42, 4] ] + end + + it "returns empty matrices on the same size if empty" do + Matrix.empty(0, 3).real.should == Matrix.empty(0, 3) + Matrix.empty(3, 0).real.should == Matrix.empty(3, 0) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.real.should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/rect_spec.rb b/spec/rubyspec/library/matrix/rect_spec.rb new file mode 100644 index 0000000000..d0a3b2705b --- /dev/null +++ b/spec/rubyspec/library/matrix/rect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rectangular', __FILE__) + +describe "Matrix#rect" do + it_behaves_like(:matrix_rectangular, :rect) +end diff --git a/spec/rubyspec/library/matrix/rectangular_spec.rb b/spec/rubyspec/library/matrix/rectangular_spec.rb new file mode 100644 index 0000000000..7af446cb18 --- /dev/null +++ b/spec/rubyspec/library/matrix/rectangular_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rectangular', __FILE__) + +describe "Matrix#rectangular" do + it_behaves_like(:matrix_rectangular, :rectangular) +end diff --git a/spec/rubyspec/library/matrix/regular_spec.rb b/spec/rubyspec/library/matrix/regular_spec.rb new file mode 100644 index 0000000000..a62a200d6d --- /dev/null +++ b/spec/rubyspec/library/matrix/regular_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#regular?" do + + it "returns false for singular matrices" do + m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ] + m.regular?.should be_false + + m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ] + m.regular?.should be_false + end + + it "returns true if the Matrix is regular" do + Matrix[ [0,1], [1,0] ].regular?.should be_true + end + + it "returns true for an empty 0x0 matrix" do + Matrix.empty(0,0).regular?.should be_true + end + + it "raises an error for rectangular matrices" do + lambda { + Matrix[[1], [2], [3]].regular? + }.should raise_error(Matrix::ErrDimensionMismatch) + + lambda { + Matrix.empty(3,0).regular? + }.should raise_error(Matrix::ErrDimensionMismatch) + end +end diff --git a/spec/rubyspec/library/matrix/round_spec.rb b/spec/rubyspec/library/matrix/round_spec.rb new file mode 100644 index 0000000000..f502a35c68 --- /dev/null +++ b/spec/rubyspec/library/matrix/round_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix#round" do + it "returns a matrix with all entries rounded" do + Matrix[ [1, 2.34], [5.67, 8] ].round.should == Matrix[ [1, 2], [6, 8] ] + Matrix[ [1, 2.34], [5.67, 8] ].round(1).should == Matrix[ [1, 2.3], [5.7, 8] ] + end + + it "returns empty matrices on the same size if empty" do + Matrix.empty(0, 3).round.should == Matrix.empty(0, 3) + Matrix.empty(3, 0).round(42).should == Matrix.empty(3, 0) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.round.should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/row_size_spec.rb b/spec/rubyspec/library/matrix/row_size_spec.rb new file mode 100644 index 0000000000..ee685ba5fb --- /dev/null +++ b/spec/rubyspec/library/matrix/row_size_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#row_size" do + it "returns the number rows" do + Matrix[ [1,2], [3, 4], [5, 6] ].row_size.should == 3 + end + + it "returns the number rows even for some empty matrices" do + Matrix[ [], [], [] ].row_size.should == 3 + end + +end diff --git a/spec/rubyspec/library/matrix/row_spec.rb b/spec/rubyspec/library/matrix/row_spec.rb new file mode 100644 index 0000000000..e165e48f5f --- /dev/null +++ b/spec/rubyspec/library/matrix/row_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#row" do + before :all do + @m = Matrix[ [1, 2], [2, 3], [3, 4] ] + end + + it "returns a Vector when called without a block" do + @m.row(0).should == Vector[1,2] + end + + it "yields the elements of the row when called with a block" do + a = [] + @m.row(0) {|x| a << x} + a.should == [1,2] + end + + it "counts backwards for negative argument" do + @m.row(-1).should == Vector[3, 4] + end + + it "returns self when called with a block" do + @m.row(0) { |x| x }.should equal(@m) + end + + it "returns nil when out of bounds" do + @m.row(3).should == nil + @m.row(-4).should == nil + end + + it "never yields when out of bounds" do + lambda { @m.row(3){ raise } }.should_not raise_error + lambda { @m.row(-4){ raise } }.should_not raise_error + end +end diff --git a/spec/rubyspec/library/matrix/row_vector_spec.rb b/spec/rubyspec/library/matrix/row_vector_spec.rb new file mode 100644 index 0000000000..60907e9247 --- /dev/null +++ b/spec/rubyspec/library/matrix/row_vector_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.row_vector" do + + it "returns a Matrix" do + Matrix.row_vector([]).should be_an_instance_of(Matrix) + end + + it "returns a single-row Matrix with the specified values" do + Matrix.row_vector([1,2]).should == Matrix[ [1,2] ] + end + + it "returns a 1x0 matrix when called with an empty Array" do + Matrix.row_vector([]).should == Matrix[ [] ] + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.row_vector([1]).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/row_vectors_spec.rb b/spec/rubyspec/library/matrix/row_vectors_spec.rb new file mode 100644 index 0000000000..46f97bb748 --- /dev/null +++ b/spec/rubyspec/library/matrix/row_vectors_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#row_vectors" do + + before :each do + @vectors = Matrix[ [1,2], [3,4] ].row_vectors + end + + it "returns an Array" do + Matrix[ [1,2], [3,4] ].row_vectors.should be_an_instance_of(Array) + end + + it "returns an Array of Vectors" do + @vectors.all? {|v| v.should be_an_instance_of(Vector)} + end + + it "returns each row as a Vector" do + @vectors.should == [Vector[1,2], Vector[3,4]] + end + + it "returns an empty Array for empty matrices" do + Matrix[].row_vectors.should == [] + Matrix[ [] ].row_vectors.should == [ Vector[] ] + end +end diff --git a/spec/rubyspec/library/matrix/rows_spec.rb b/spec/rubyspec/library/matrix/rows_spec.rb new file mode 100644 index 0000000000..d583a07b30 --- /dev/null +++ b/spec/rubyspec/library/matrix/rows_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.rows" do + before :each do + @a = [1, 2] + @b = [3, 4] + @m = Matrix.rows([@a, @b]) + end + + it "returns a Matrix" do + @m.should be_kind_of(Matrix) + end + + it "creates a matrix from argument rows" do + @m.row(0).to_a.should == @a + @m.row(1).to_a.should == @b + end + + it "copies the original rows by default" do + @a << 3 + @b << 6 + @m.row(0).should_not equal(@a) + @m.row(1).should_not equal(@b) + end + + it "references the original rows if copy is false" do + @m_ref = Matrix.rows([@a, @b], false) + @a << 3 + @b << 6 + @m_ref.row(0).to_a.should == @a + @m_ref.row(1).to_a.should == @b + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.rows([[0, 1], [0, 1]]).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/scalar/Fail_spec.rb b/spec/rubyspec/library/matrix/scalar/Fail_spec.rb new file mode 100644 index 0000000000..fbd0f3013a --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/Fail_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#Fail" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/Raise_spec.rb b/spec/rubyspec/library/matrix/scalar/Raise_spec.rb new file mode 100644 index 0000000000..3e98fcb22a --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/Raise_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#Raise" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/divide_spec.rb b/spec/rubyspec/library/matrix/scalar/divide_spec.rb new file mode 100644 index 0000000000..6b8e0b6bcc --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/divide_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#/" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/exponent_spec.rb b/spec/rubyspec/library/matrix/scalar/exponent_spec.rb new file mode 100644 index 0000000000..55997793eb --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/exponent_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#**" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/included_spec.rb b/spec/rubyspec/library/matrix/scalar/included_spec.rb new file mode 100644 index 0000000000..58ee233eb3 --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/included_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar.included" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/initialize_spec.rb b/spec/rubyspec/library/matrix/scalar/initialize_spec.rb new file mode 100644 index 0000000000..fd6ef00211 --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/initialize_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#initialize" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/minus_spec.rb b/spec/rubyspec/library/matrix/scalar/minus_spec.rb new file mode 100644 index 0000000000..19a7b4f1a1 --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/minus_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#-" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/multiply_spec.rb b/spec/rubyspec/library/matrix/scalar/multiply_spec.rb new file mode 100644 index 0000000000..247cc1447c --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/multiply_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#*" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar/plus_spec.rb b/spec/rubyspec/library/matrix/scalar/plus_spec.rb new file mode 100644 index 0000000000..7cdaa1c7f3 --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar/plus_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix::Scalar#+" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/scalar_spec.rb b/spec/rubyspec/library/matrix/scalar_spec.rb new file mode 100644 index 0000000000..3da8771471 --- /dev/null +++ b/spec/rubyspec/library/matrix/scalar_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.scalar" do + + before :each do + @side = 3 + @value = 8 + @a = Matrix.scalar(@side, @value) + end + + it "returns a Matrix" do + @a.should be_kind_of(Matrix) + end + + it "returns a n x n matrix" do + @a.row_size.should == @side + @a.column_size.should == @side + end + + it "initializes diagonal to value" do + (0...@a.row_size).each do |i| + @a[i, i].should == @value + end + end + + it "initializes all non-diagonal values to 0" do + (0...@a.row_size).each do |i| + (0...@a.column_size).each do |j| + if i != j + @a[i, j].should == 0 + end + end + end + end + + before :each do + @side = 3 + @value = 8 + @a = Matrix.scalar(@side, @value) + end + + it "returns a Matrix" do + @a.should be_kind_of(Matrix) + end + + it "returns a square matrix, where the first argument specifies the side of the square" do + @a.row_size.should == @side + @a.column_size.should == @side + end + + it "puts the second argument in all diagonal values" do + (0...@a.row_size).each do |i| + @a[i, i].should == @value + end + end + + it "fills all values not on the main diagonal with 0" do + (0...@a.row_size).each do |i| + (0...@a.column_size).each do |j| + if i != j + @a[i, j].should == 0 + end + end + end + end +end diff --git a/spec/rubyspec/library/matrix/shared/collect.rb b/spec/rubyspec/library/matrix/shared/collect.rb new file mode 100644 index 0000000000..256cd6a190 --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/collect.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :collect, shared: true do + before :all do + @m = Matrix[ [1, 2], [1, 2] ] + end + + it "returns an instance of Matrix" do + @m.send(@method){|n| n * 2 }.should be_kind_of(Matrix) + end + + it "returns a Matrix where each element is the result of the block" do + @m.send(@method) { |n| n * 2 }.should == Matrix[ [2, 4], [2, 4] ] + end + + it "returns an enumerator if no block is given" do + @m.send(@method).should be_an_instance_of(Enumerator) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.send(@method){1}.should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/shared/conjugate.rb b/spec/rubyspec/library/matrix/shared/conjugate.rb new file mode 100644 index 0000000000..180ff4fa98 --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/conjugate.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :matrix_conjugate, shared: true do + it "returns a matrix with all entries 'conjugated'" do + Matrix[ [1, 2], [3, 4] ].send(@method).should == Matrix[ [1, 2], [3, 4] ] + Matrix[ [1.9, Complex(1,1)], [3, 4] ].send(@method).should == Matrix[ [1.9, Complex(1,-1)], [3, 4] ] + end + + it "returns empty matrices on the same size if empty" do + Matrix.empty(0, 3).send(@method).should == Matrix.empty(0, 3) + Matrix.empty(3, 0).send(@method).should == Matrix.empty(3, 0) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/shared/determinant.rb b/spec/rubyspec/library/matrix/shared/determinant.rb new file mode 100644 index 0000000000..47a58c62a6 --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/determinant.rb @@ -0,0 +1,38 @@ +require 'matrix' + +describe :determinant, shared: true do + it "returns the determinant of a square Matrix" do + m = Matrix[ [7,6], [3,9] ] + m.send(@method).should == 45 + + m = Matrix[ [9, 8], [6,5] ] + m.send(@method).should == -3 + + m = Matrix[ [9,8,3], [4,20,5], [1,1,1] ] + m.send(@method).should == 95 + end + + it "returns the determinant of a single-element Matrix" do + m = Matrix[ [2] ] + m.send(@method).should == 2 + end + + it "returns 1 for an empty Matrix" do + m = Matrix[ ] + m.send(@method).should == 1 + end + + it "returns the determinant even for Matrices containing 0 as first entry" do + Matrix[[0,1],[1,0]].send(@method).should == -1 + end + + it "raises an error for rectangular matrices" do + lambda { + Matrix[[1], [2], [3]].send(@method) + }.should raise_error(Matrix::ErrDimensionMismatch) + + lambda { + Matrix.empty(3,0).send(@method) + }.should raise_error(Matrix::ErrDimensionMismatch) + end +end diff --git a/spec/rubyspec/library/matrix/shared/equal_value.rb b/spec/rubyspec/library/matrix/shared/equal_value.rb new file mode 100644 index 0000000000..e2102e823a --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/equal_value.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :equal, shared: true do + before do + @matrix = Matrix[ [1, 2, 3, 4, 5], [2, 3, 4, 5, 6] ] + end + + it "returns true for self" do + @matrix.send(@method, @matrix).should be_true + end + + it "returns true for equal matrices" do + @matrix.send(@method, Matrix[ [1, 2, 3, 4, 5], [2, 3, 4, 5, 6] ]).should be_true + end + + it "returns false for different matrices" do + @matrix.send(@method, Matrix[ [42, 2, 3, 4, 5], [2, 3, 4, 5, 6] ]).should be_false + @matrix.send(@method, Matrix[ [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7] ]).should be_false + @matrix.send(@method, Matrix[ [1, 2, 3], [2, 3, 4] ]).should be_false + end + + it "returns false for different empty matrices" do + Matrix.empty(42, 0).send(@method, Matrix.empty(6, 0)).should be_false + Matrix.empty(0, 42).send(@method, Matrix.empty(0, 6)).should be_false + Matrix.empty(0, 0).send(@method, Matrix.empty(6, 0)).should be_false + Matrix.empty(0, 0).send(@method, Matrix.empty(0, 6)).should be_false + end + + it "doesn't distinguish on subclasses" do + MatrixSub.ins.send(@method, Matrix.I(2)).should be_true + end +end diff --git a/spec/rubyspec/library/matrix/shared/identity.rb b/spec/rubyspec/library/matrix/shared/identity.rb new file mode 100644 index 0000000000..20b35ae8e3 --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/identity.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :matrix_identity, shared: true do + it "returns a Matrix" do + Matrix.send(@method, 2).should be_kind_of(Matrix) + end + + it "returns a n x n identity matrix" do + Matrix.send(@method, 3).should == Matrix.scalar(3, 1) + Matrix.send(@method, 100).should == Matrix.scalar(100, 1) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.send(@method, 2).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/shared/imaginary.rb b/spec/rubyspec/library/matrix/shared/imaginary.rb new file mode 100644 index 0000000000..61a65a62ec --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/imaginary.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :matrix_imaginary, shared: true do + it "returns a matrix with the imaginary part of the elements of the receiver" do + Matrix[ [1, 2], [3, 4] ].send(@method).should == Matrix[ [0, 0], [0, 0] ] + Matrix[ [1.9, Complex(1,1)], [Complex(-2,0.42), 4] ].send(@method).should == Matrix[ [0, 1], [0.42, 0] ] + end + + it "returns empty matrices on the same size if empty" do + Matrix.empty(0, 3).send(@method).should == Matrix.empty(0, 3) + Matrix.empty(3, 0).send(@method).should == Matrix.empty(3, 0) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/shared/inverse.rb b/spec/rubyspec/library/matrix/shared/inverse.rb new file mode 100644 index 0000000000..c6996df4a3 --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/inverse.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :inverse, shared: true do + + it "returns a Matrix" do + Matrix[ [1,2], [2,1] ].send(@method).should be_an_instance_of(Matrix) + end + + it "returns the inverse of the Matrix" do + Matrix[ + [1, 3, 3], [1, 4, 3], [1, 3, 4] + ].send(@method).should == + Matrix[ + [7, -3, -3], [-1, 1, 0], [-1, 0, 1] + ] + end + + it "returns the inverse of the Matrix (other case)" do + Matrix[ + [1, 2, 3], [0, 1, 4], [5, 6, 0] + ].send(@method).should be_close_to_matrix([ + [-24, 18, 5], [20, -15, -4], [-5, 4, 1] + ]) + end + + it "raises a ErrDimensionMismatch if the Matrix is not square" do + lambda{ + Matrix[ [1,2,3], [1,2,3] ].send(@method) + }.should raise_error(Matrix::ErrDimensionMismatch) + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/shared/rectangular.rb b/spec/rubyspec/library/matrix/shared/rectangular.rb new file mode 100644 index 0000000000..4206311586 --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/rectangular.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :matrix_rectangular, shared: true do + it "returns [receiver.real, receiver.imag]" do + m = Matrix[ [1.2, Complex(1,2)], [Complex(-2,0.42), 4] ] + m.send(@method).should == [m.real, m.imag] + + m = Matrix.empty(3, 0) + m.send(@method).should == [m.real, m.imag] + end + + describe "for a subclass of Matrix" do + it "returns instances of that subclass" do + MatrixSub.ins.send(@method).each{|m| m.should be_an_instance_of(MatrixSub) } + end + end +end diff --git a/spec/rubyspec/library/matrix/shared/trace.rb b/spec/rubyspec/library/matrix/shared/trace.rb new file mode 100644 index 0000000000..2a42839f5d --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/trace.rb @@ -0,0 +1,12 @@ +require 'matrix' + +describe :trace, shared: true do + it "returns the sum of diagonal elements in a square Matrix" do + Matrix[[7,6], [3,9]].trace.should == 16 + end + + it "returns the sum of diagonal elements in a rectangular Matrix" do + lambda{ Matrix[[1,2,3], [4,5,6]].trace}.should raise_error(Matrix::ErrDimensionMismatch) + end + +end diff --git a/spec/rubyspec/library/matrix/shared/transpose.rb b/spec/rubyspec/library/matrix/shared/transpose.rb new file mode 100644 index 0000000000..dba6c71041 --- /dev/null +++ b/spec/rubyspec/library/matrix/shared/transpose.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require 'matrix' + +describe :matrix_transpose, shared: true do + it "returns a transposed matrix" do + Matrix[[1, 2], [3, 4], [5, 6]].send(@method).should == Matrix[[1, 3, 5], [2, 4, 6]] + end + + it "can transpose empty matrices" do + m = Matrix[[], [], []] + m.send(@method).send(@method).should == m + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.ins.send(@method).should be_an_instance_of(MatrixSub) + end + end +end diff --git a/spec/rubyspec/library/matrix/singular_spec.rb b/spec/rubyspec/library/matrix/singular_spec.rb new file mode 100644 index 0000000000..480621998f --- /dev/null +++ b/spec/rubyspec/library/matrix/singular_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#singular?" do + it "returns true for singular matrices" do + m = Matrix[ [1,2,3], [3,4,3], [0,0,0] ] + m.singular?.should be_true + + m = Matrix[ [1,2,9], [3,4,9], [1,2,9] ] + m.singular?.should be_true + end + + it "returns false if the Matrix is regular" do + Matrix[ [0,1], [1,0] ].singular?.should be_false + end + + it "returns false for an empty 0x0 matrix" do + Matrix.empty(0,0).singular?.should be_false + end + + it "raises an error for rectangular matrices" do + lambda { + Matrix[[1], [2], [3]].singular? + }.should raise_error(Matrix::ErrDimensionMismatch) + + lambda { + Matrix.empty(3,0).singular? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + +end diff --git a/spec/rubyspec/library/matrix/spec_helper.rb b/spec/rubyspec/library/matrix/spec_helper.rb new file mode 100644 index 0000000000..d44612981a --- /dev/null +++ b/spec/rubyspec/library/matrix/spec_helper.rb @@ -0,0 +1,35 @@ +class BeCloseToMatrixMatcher + def initialize(expected, tolerance = TOLERANCE) + SpecExpectation.matcher! rescue "Used with the balance_should_and_match branch of mspec" + @expected = Matrix[*expected] + @tolerance = tolerance + end + + def matches?(actual) + @actual = actual + return false unless @actual.is_a? Matrix + return false unless @actual.row_size == @expected.row_size + @actual.row_size.times do |i| + a, e = @actual.row(i), @expected.row(i) + return false unless a.size == e.size + a.size.times do |j| + return false unless (a[j] - e[j]).abs < @tolerance + end + end + true + end + + def failure_message + ["Expected #{@expected}", "to be within +/- #{@tolerance} of #{@actual}"] + end + + def negative_failure_message + ["Expected #{@expected}", "not to be within +/- #{@tolerance} of #{@actual}"] + end +end + +class Object + def be_close_to_matrix(expected, tolerance = TOLERANCE) + BeCloseToMatrixMatcher.new(expected, tolerance) + end +end diff --git a/spec/rubyspec/library/matrix/square_spec.rb b/spec/rubyspec/library/matrix/square_spec.rb new file mode 100644 index 0000000000..a117916a4c --- /dev/null +++ b/spec/rubyspec/library/matrix/square_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#square?" do + + it "returns true when the Matrix is square" do + Matrix[ [1,2], [2,4] ].square?.should be_true + Matrix[ [100,3,5], [9.5, 4.9, 8], [2,0,77] ].square?.should be_true + end + + it "returns true when the Matrix has only one element" do + Matrix[ [9] ].square?.should be_true + end + + it "returns false when the Matrix is rectangular" do + Matrix[ [1, 2] ].square?.should be_false + end + + it "returns false when the Matrix is rectangular" do + Matrix[ [1], [2] ].square?.should be_false + end + + it "returns handles empty matrices" do + Matrix[].square?.should be_true + Matrix[[]].square?.should be_false + Matrix.columns([[]]).square?.should be_false + end +end diff --git a/spec/rubyspec/library/matrix/symmetric_spec.rb b/spec/rubyspec/library/matrix/symmetric_spec.rb new file mode 100644 index 0000000000..53f962c0e2 --- /dev/null +++ b/spec/rubyspec/library/matrix/symmetric_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.symmetric?" do + it "returns true for a symmetric Matrix" do + Matrix[[1, 2, Complex(0, 3)], [2, 4, 5], [Complex(0, 3), 5, 6]].symmetric?.should be_true + end + + it "returns true for a 0x0 empty matrix" do + Matrix.empty.symmetric?.should be_true + end + + it "returns false for an assymetric Matrix" do + Matrix[[1, 2],[-2, 1]].symmetric?.should be_false + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[0], [0]], + Matrix[[0, 0]], + Matrix.empty(0, 2), + Matrix.empty(2, 0), + ].each do |rectangual_matrix| + lambda { + rectangual_matrix.symmetric? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end +end diff --git a/spec/rubyspec/library/matrix/t_spec.rb b/spec/rubyspec/library/matrix/t_spec.rb new file mode 100644 index 0000000000..1c57c25de3 --- /dev/null +++ b/spec/rubyspec/library/matrix/t_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/transpose', __FILE__) + +describe "Matrix#transpose" do + it_behaves_like(:matrix_transpose, :t) +end diff --git a/spec/rubyspec/library/matrix/to_a_spec.rb b/spec/rubyspec/library/matrix/to_a_spec.rb new file mode 100644 index 0000000000..70db580312 --- /dev/null +++ b/spec/rubyspec/library/matrix/to_a_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#to_a" do + it "returns the array of arrays that describe the rows of the matrix" do + Matrix[].to_a.should == [] + Matrix[[]].to_a.should == [[]] + Matrix[[1]].to_a.should == [[1]] + Matrix[[1, 2], [3, 4]].to_a.should == [[1, 2],[3, 4]] + end +end diff --git a/spec/rubyspec/library/matrix/to_s_spec.rb b/spec/rubyspec/library/matrix/to_s_spec.rb new file mode 100644 index 0000000000..eb175d486b --- /dev/null +++ b/spec/rubyspec/library/matrix/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix#to_s" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/matrix/tr_spec.rb b/spec/rubyspec/library/matrix/tr_spec.rb new file mode 100644 index 0000000000..4b07a70203 --- /dev/null +++ b/spec/rubyspec/library/matrix/tr_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/trace', __FILE__) +require 'matrix' + +describe "Matrix#tr" do + it_behaves_like(:trace, :tr) +end diff --git a/spec/rubyspec/library/matrix/trace_spec.rb b/spec/rubyspec/library/matrix/trace_spec.rb new file mode 100644 index 0000000000..08adb256c0 --- /dev/null +++ b/spec/rubyspec/library/matrix/trace_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/trace', __FILE__) +require 'matrix' + +describe "Matrix#trace" do + it_behaves_like(:trace, :trace) +end diff --git a/spec/rubyspec/library/matrix/transpose_spec.rb b/spec/rubyspec/library/matrix/transpose_spec.rb new file mode 100644 index 0000000000..2a30a80efc --- /dev/null +++ b/spec/rubyspec/library/matrix/transpose_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/transpose', __FILE__) + +describe "Matrix#transpose" do + it_behaves_like(:matrix_transpose, :transpose) +end diff --git a/spec/rubyspec/library/matrix/unit_spec.rb b/spec/rubyspec/library/matrix/unit_spec.rb new file mode 100644 index 0000000000..058d719043 --- /dev/null +++ b/spec/rubyspec/library/matrix/unit_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/identity', __FILE__) + +describe "Matrix.unit" do + it_behaves_like(:matrix_identity, :unit) +end diff --git a/spec/rubyspec/library/matrix/unitary_spec.rb b/spec/rubyspec/library/matrix/unitary_spec.rb new file mode 100644 index 0000000000..e322a5a3ce --- /dev/null +++ b/spec/rubyspec/library/matrix/unitary_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.unitary?" do + it "returns false for non unitary matrices" do + Matrix[[0, 1], [1, 2]].unitary?.should == false + Matrix[[0, Complex(0, 2)], [Complex(0, 2), 0]].unitary?.should == false + Matrix[[0, Complex(0, 1)], [Complex(0, -1), 0]].unitary?.should == false + Matrix[[1, 1, 0], [0, 1, 1], [1, 0, 1]].unitary?.should == false + end + + it "returns true for unitary matrices" do + Matrix[[0, Complex(0, 1)], [Complex(0, 1), 0]].unitary?.should == true + end + + it "raises an error for rectangular matrices" do + [ + Matrix[[0], [0]], + Matrix[[0, 0]], + Matrix.empty(0, 2), + Matrix.empty(2, 0), + ].each do |rectangual_matrix| + lambda { + rectangual_matrix.unitary? + }.should raise_error(Matrix::ErrDimensionMismatch) + end + end +end diff --git a/spec/rubyspec/library/matrix/upper_triangular_spec.rb b/spec/rubyspec/library/matrix/upper_triangular_spec.rb new file mode 100644 index 0000000000..be88150b85 --- /dev/null +++ b/spec/rubyspec/library/matrix/upper_triangular_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'matrix' + +describe "Matrix.upper_triangular?" do + it "returns true for an upper triangular Matrix" do + Matrix[[1, 2, 3], [0, 2, 3], [0, 0, 3]].upper_triangular?.should be_true + Matrix.diagonal([1, 2, 3]).upper_triangular?.should be_true + Matrix[[1, 2], [0, 2], [0, 0], [0, 0]].upper_triangular?.should be_true + Matrix[[1, 2, 3, 4], [0, 2, 3, 4]].upper_triangular?.should be_true + end + + it "returns false for a non upper triangular square Matrix" do + Matrix[[0, 0], [1, 0]].upper_triangular?.should be_false + Matrix[[1, 2, 3], [1, 2, 3], [1, 2, 3]].upper_triangular?.should be_false + Matrix[[0, 0], [0, 0], [0, 0], [0, 1]].upper_triangular?.should be_false + Matrix[[0, 0, 0, 0], [1, 0, 0, 0]].upper_triangular?.should be_false + end + + it "returns true for an empty matrix" do + Matrix.empty(3,0).upper_triangular?.should be_true + Matrix.empty(0,3).upper_triangular?.should be_true + Matrix.empty(0,0).upper_triangular?.should be_true + end +end diff --git a/spec/rubyspec/library/matrix/vector/cross_product_spec.rb b/spec/rubyspec/library/matrix/vector/cross_product_spec.rb new file mode 100644 index 0000000000..f26cf585da --- /dev/null +++ b/spec/rubyspec/library/matrix/vector/cross_product_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Vector#cross_product" do + it "returns the cross product of a vector" do + Vector[1, 2, 3].cross_product(Vector[0, -4, 5]).should == Vector[22, -5, -4] + end + + it "raises an error unless both vectors have dimension 3" do + lambda { + Vector[1, 2, 3].cross_product(Vector[0, -4]) + }.should raise_error(Vector::ErrDimensionMismatch) + end +end diff --git a/spec/rubyspec/library/matrix/vector/each2_spec.rb b/spec/rubyspec/library/matrix/vector/each2_spec.rb new file mode 100644 index 0000000000..e9d89e21c4 --- /dev/null +++ b/spec/rubyspec/library/matrix/vector/each2_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Vector.each2" do + before :all do + @v = Vector[1, 2, 3] + @v2 = Vector[4, 5, 6] + end + + it "requires one argument" do + lambda { @v.each2(@v2, @v2){} }.should raise_error(ArgumentError) + lambda { @v.each2(){} }.should raise_error(ArgumentError) + end + + describe "given one argument" do + it "accepts an Array argument" do + a = [] + @v.each2([7, 8, 9]){|x, y| a << x << y} + a.should == [1, 7, 2, 8, 3, 9] + end + + it "raises a DimensionMismatch error if the Vector size is different" do + lambda { @v.each2(Vector[1,2]){} }.should raise_error(Vector::ErrDimensionMismatch) + lambda { @v.each2(Vector[1,2,3,4]){} }.should raise_error(Vector::ErrDimensionMismatch) + end + + it "yields arguments in sequence" do + a = [] + @v.each2(@v2){|first, second| a << [first, second]} + a.should == [[1, 4], [2, 5], [3, 6]] + end + + it "yield arguments in pairs" do + a = [] + @v.each2(@v2){|*pair| a << pair} + a.should == [[1, 4], [2, 5], [3, 6]] + end + + it "returns self when given a block" do + @v.each2(@v2){}.should equal(@v) + end + + it "returns an enumerator if no block given" do + enum = @v.each2(@v2) + enum.should be_an_instance_of(Enumerator) + enum.to_a.should == [[1, 4], [2, 5], [3, 6]] + end + end +end diff --git a/spec/rubyspec/library/matrix/vector/eql_spec.rb b/spec/rubyspec/library/matrix/vector/eql_spec.rb new file mode 100644 index 0000000000..6cc69bbf7d --- /dev/null +++ b/spec/rubyspec/library/matrix/vector/eql_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Vector#eql?" do + before do + @vector = Vector[1, 2, 3, 4, 5] + end + + it "returns true for self" do + @vector.eql?(@vector).should be_true + end + + it "returns false when there are a pair corresponding elements which are not equal in the sense of Kernel#eql?" do + @vector.eql?(Vector[1, 2, 3, 4, 5.0]).should be_false + end +end diff --git a/spec/rubyspec/library/matrix/vector/inner_product_spec.rb b/spec/rubyspec/library/matrix/vector/inner_product_spec.rb new file mode 100644 index 0000000000..a953598b51 --- /dev/null +++ b/spec/rubyspec/library/matrix/vector/inner_product_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Vector#inner_product" do + it "returns the inner product of a vector" do + Vector[1, 2, 3].inner_product(Vector[0, -4, 5]).should == 7 + end + + it "returns 0 for empty vectors" do + Vector[].inner_product(Vector[]).should == 0 + end + + it "raises an error for mismatched vectors" do + lambda { + Vector[1, 2, 3].inner_product(Vector[0, -4]) + }.should raise_error(Vector::ErrDimensionMismatch) + end + + it "uses the conjugate of its argument" do + Vector[Complex(1,2)].inner_product(Vector[Complex(3,4)]).should == Complex(11, 2) + end +end diff --git a/spec/rubyspec/library/matrix/vector/normalize_spec.rb b/spec/rubyspec/library/matrix/vector/normalize_spec.rb new file mode 100644 index 0000000000..14aac1f5e3 --- /dev/null +++ b/spec/rubyspec/library/matrix/vector/normalize_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'matrix' + +describe "Vector#normalize" do + it "returns a normalized copy of the vector" do + x = 0.2672612419124244 + Vector[1, 2, 3].normalize.should == Vector[x, x * 2, x * 3] + end + + it "raises an error for zero vectors" do + lambda { + Vector[].normalize + }.should raise_error(Vector::ZeroVectorError) + lambda { + Vector[0, 0, 0].normalize + }.should raise_error(Vector::ZeroVectorError) + end +end diff --git a/spec/rubyspec/library/matrix/zero_spec.rb b/spec/rubyspec/library/matrix/zero_spec.rb new file mode 100644 index 0000000000..f83d29e837 --- /dev/null +++ b/spec/rubyspec/library/matrix/zero_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require 'matrix' + +describe "Matrix.zero" do + it "returns an object of type Matrix" do + Matrix.zero(3).should be_kind_of(Matrix) + end + + it "creates a n x n matrix" do + m3 = Matrix.zero(3) + m3.row_size.should == 3 + m3.column_size.should == 3 + + m8 = Matrix.zero(8) + m8.row_size.should == 8 + m8.column_size.should == 8 + end + + it "initializes all cells to 0" do + size = 10 + m = Matrix.zero(size) + + (0...size).each do |i| + (0...size).each do |j| + m[i, j].should == 0 + end + end + end + + describe "for a subclass of Matrix" do + it "returns an instance of that subclass" do + MatrixSub.zero(3).should be_an_instance_of(MatrixSub) + end + end +end + +describe "Matrix.zero?" do + it "returns true for empty matrices" do + Matrix.empty.zero?.should == true + Matrix.empty(3,0).zero?.should == true + Matrix.empty(0,3).zero?.should == true + end + + it "returns true for matrices with zero entries" do + Matrix.zero(2,3).zero?.should == true + end + + it "returns false for matrices with non zero entries" do + Matrix[[1]].zero?.should == false + end +end diff --git a/spec/rubyspec/library/net/FTPError_spec.rb b/spec/rubyspec/library/net/FTPError_spec.rb new file mode 100644 index 0000000000..7cc06f681a --- /dev/null +++ b/spec/rubyspec/library/net/FTPError_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'net/ftp' + +describe "Net::FTPError" do + it "is an Exception" do + Net::FTPError.should < Exception + end +end diff --git a/spec/rubyspec/library/net/FTPPermError_spec.rb b/spec/rubyspec/library/net/FTPPermError_spec.rb new file mode 100644 index 0000000000..d9a7189468 --- /dev/null +++ b/spec/rubyspec/library/net/FTPPermError_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'net/ftp' + +describe "Net::FTPPermError" do + it "is an Exception" do + Net::FTPPermError.should < Exception + end + + it "is a subclass of Net::FTPError" do + Net::FTPPermError.should < Net::FTPError + end +end diff --git a/spec/rubyspec/library/net/FTPProtoError_spec.rb b/spec/rubyspec/library/net/FTPProtoError_spec.rb new file mode 100644 index 0000000000..445ba1cd2f --- /dev/null +++ b/spec/rubyspec/library/net/FTPProtoError_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'net/ftp' + +describe "Net::FTPProtoError" do + it "is an Exception" do + Net::FTPProtoError.should < Exception + end + + it "is a subclass of Net::FTPError" do + Net::FTPPermError.should < Net::FTPError + end +end diff --git a/spec/rubyspec/library/net/FTPReplyError_spec.rb b/spec/rubyspec/library/net/FTPReplyError_spec.rb new file mode 100644 index 0000000000..b697f0b95a --- /dev/null +++ b/spec/rubyspec/library/net/FTPReplyError_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'net/ftp' + +describe "Net::FTPReplyError" do + it "is an Exception" do + Net::FTPReplyError.should < Exception + end + + it "is a subclass of Net::FTPError" do + Net::FTPPermError.should < Net::FTPError + end +end diff --git a/spec/rubyspec/library/net/FTPTempError_spec.rb b/spec/rubyspec/library/net/FTPTempError_spec.rb new file mode 100644 index 0000000000..8d74d371da --- /dev/null +++ b/spec/rubyspec/library/net/FTPTempError_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'net/ftp' + +describe "Net::FTPTempError" do + it "is an Exception" do + Net::FTPTempError.should < Exception + end + + it "is a subclass of Net::FTPError" do + Net::FTPPermError.should < Net::FTPError + end +end diff --git a/spec/rubyspec/library/net/ftp/abort_spec.rb b/spec/rubyspec/library/net/ftp/abort_spec.rb new file mode 100644 index 0000000000..ea47eaf9bf --- /dev/null +++ b/spec/rubyspec/library/net/ftp/abort_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#abort" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the ABOR command to the server" do + lambda { @ftp.abort }.should_not raise_error + end + + it "ignores the response" do + @ftp.abort + @ftp.last_response.should == "220 Dummy FTP Server ready!\n" + end + + it "returns the full response" do + @ftp.abort.should == "226 Closing data connection. (ABOR)\n" + end + + it "does not raise any error when the response code is 225" do + @server.should_receive(:abor).and_respond("225 Data connection open; no transfer in progress.") + lambda { @ftp.abort }.should_not raise_error + end + + it "does not raise any error when the response code is 226" do + @server.should_receive(:abor).and_respond("226 Closing data connection.") + lambda { @ftp.abort }.should_not raise_error + end + + it "raises a Net::FTPProtoError when the response code is 500" do + @server.should_receive(:abor).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.abort }.should raise_error(Net::FTPProtoError) + end + + it "raises a Net::FTPProtoError when the response code is 501" do + @server.should_receive(:abor).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.abort }.should raise_error(Net::FTPProtoError) + end + + it "raises a Net::FTPProtoError when the response code is 502" do + @server.should_receive(:abor).and_respond("502 Command not implemented.") + lambda { @ftp.abort }.should raise_error(Net::FTPProtoError) + end + + it "raises a Net::FTPProtoError when the response code is 421" do + @server.should_receive(:abor).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.abort }.should raise_error(Net::FTPProtoError) + end +end diff --git a/spec/rubyspec/library/net/ftp/acct_spec.rb b/spec/rubyspec/library/net/ftp/acct_spec.rb new file mode 100644 index 0000000000..e60c6b3088 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/acct_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#acct" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "writes the ACCT command to the server" do + @ftp.acct("my_account") + @ftp.last_response.should == "230 User 'my_account' logged in, proceed. (ACCT)\n" + end + + it "returns nil" do + @ftp.acct("my_account").should == nil + end + + it "does not raise any error when the response code is 230" do + @server.should_receive(:acct).and_respond("230 User logged in, proceed.") + lambda { @ftp.acct("my_account") }.should_not raise_error + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:acct).and_respond("530 Not logged in.") + lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 503" do + @server.should_receive(:acct).and_respond("503 Bad sequence of commands.") + lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.acct("my_account") }.should raise_error(Net::FTPTempError) + end +end diff --git a/spec/rubyspec/library/net/ftp/binary_spec.rb b/spec/rubyspec/library/net/ftp/binary_spec.rb new file mode 100644 index 0000000000..6f1a1ab8ba --- /dev/null +++ b/spec/rubyspec/library/net/ftp/binary_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#binary" do + it "returns true when self is in binary mode" do + ftp = Net::FTP.new + ftp.binary.should be_true + + ftp.binary = false + ftp.binary.should be_false + end +end + +describe "Net::FTP#binary=" do + it "sets self to binary mode when passed true" do + ftp = Net::FTP.new + + ftp.binary = true + ftp.binary.should be_true + + ftp.binary = false + ftp.binary.should be_false + end +end diff --git a/spec/rubyspec/library/net/ftp/chdir_spec.rb b/spec/rubyspec/library/net/ftp/chdir_spec.rb new file mode 100644 index 0000000000..f6cfac5b66 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/chdir_spec.rb @@ -0,0 +1,99 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#chdir" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + describe "when switching to the parent directory" do + it "sends the 'CDUP' command to the server" do + @ftp.chdir("..") + @ftp.last_response.should == "200 Command okay. (CDUP)\n" + end + + it "returns nil" do + @ftp.chdir("..").should be_nil + end + + it "does not raise a Net::FTPPermError when the response code is 500" do + @server.should_receive(:cdup).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.chdir("..") }.should_not raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:cdup).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:cdup).and_respond("502 Command not implemented.") + lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:cdup).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.chdir("..") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:cdup).and_respond("530 Not logged in.") + lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:cdup).and_respond("550 Requested action not taken.") + lambda { @ftp.chdir("..") }.should raise_error(Net::FTPPermError) + end + end + + it "writes the 'CWD' command with the passed directory to the socket" do + @ftp.chdir("test") + @ftp.last_response.should == "200 Command okay. (CWD test)\n" + end + + it "returns nil" do + @ftp.chdir("test").should be_nil + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:cwd).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:cwd).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:cwd).and_respond("502 Command not implemented.") + lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:cwd).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.chdir("test") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:cwd).and_respond("530 Not logged in.") + lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:cwd).and_respond("550 Requested action not taken.") + lambda { @ftp.chdir("test") }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/close_spec.rb b/spec/rubyspec/library/net/ftp/close_spec.rb new file mode 100644 index 0000000000..0f6866221f --- /dev/null +++ b/spec/rubyspec/library/net/ftp/close_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#close" do + before :each do + @socket = mock("Socket") + @socket.stub!(:closed?).and_return(false) + @socket.stub!(:read_timeout).and_return(60) + @socket.stub!(:read_timeout=).and_return(3) + + @ftp = Net::FTP.new + @ftp.instance_variable_set(:@sock, @socket) + end + + it "closes the socket" do + @socket.should_receive(:close) + @ftp.close.should be_nil + end + + it "does not try to close the socket if it has already been closed" do + @socket.should_receive(:closed?).and_return(true) + @socket.should_not_receive(:close) + @ftp.close.should be_nil + end + + it "does not try to close the socket if it is nil" do + @ftp.instance_variable_set(:@sock, nil) + @ftp.close.should be_nil + end +end diff --git a/spec/rubyspec/library/net/ftp/closed_spec.rb b/spec/rubyspec/library/net/ftp/closed_spec.rb new file mode 100644 index 0000000000..7b31717c00 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/closed_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#closed?" do + before :each do + @socket = mock("Socket") + + @ftp = Net::FTP.new + @ftp.instance_variable_set(:@sock, @socket) + end + + it "returns true when the socket is closed" do + @socket.should_receive(:closed?).and_return(true) + @ftp.closed?.should be_true + end + + it "returns true when the socket is nil" do + @ftp.instance_variable_set(:@sock, nil) + @ftp.closed?.should be_true + end +end diff --git a/spec/rubyspec/library/net/ftp/connect_spec.rb b/spec/rubyspec/library/net/ftp/connect_spec.rb new file mode 100644 index 0000000000..f3c1565fc2 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/connect_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +# TODO: Add specs for using the SOCKSSocket +describe "Net::FTP#connect" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + end + + after :each do + @server.connect_message = nil + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "tries to connect to the FTP Server on the given host and port" do + lambda { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error + end + + it "returns nil" do + @ftp.connect(@server.hostname, @server.server_port).should be_nil + end + + it "prints a small debug line when in debug mode" do + @ftp.debug_mode = true + lambda { @ftp.connect(@server.hostname, @server.server_port) }.should output(/#{"connect: "}#{@server.hostname}#{", "}#{@server.server_port}#{"\\nget: 220 Dummy FTP Server ready!"}/) + @ftp.debug_mode = false + end + + it "does not raise any error when the response code is 220" do + @server.connect_message = "220 Dummy FTP Server ready!" + lambda { @ftp.connect(@server.hostname, @server.server_port) }.should_not raise_error + end + + it "raises a Net::FTPReplyError when the response code is 120" do + @server.connect_message = "120 Service ready in nnn minutes." + lambda { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPReplyError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.connect_message = "421 Service not available, closing control connection." + lambda { @ftp.connect(@server.hostname, @server.server_port) }.should raise_error(Net::FTPTempError) + end +end diff --git a/spec/rubyspec/library/net/ftp/debug_mode_spec.rb b/spec/rubyspec/library/net/ftp/debug_mode_spec.rb new file mode 100644 index 0000000000..c4aa5eee3c --- /dev/null +++ b/spec/rubyspec/library/net/ftp/debug_mode_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#debug_mode" do + it "returns true when self is in debug mode" do + ftp = Net::FTP.new + ftp.debug_mode.should be_false + + ftp.debug_mode = true + ftp.debug_mode.should be_true + end +end + +describe "Net::FTP#debug_mode=" do + it "sets self into debug mode when passed true" do + ftp = Net::FTP.new + ftp.debug_mode = true + ftp.debug_mode.should be_true + + ftp.debug_mode = false + ftp.debug_mode.should be_false + end +end diff --git a/spec/rubyspec/library/net/ftp/default_passive_spec.rb b/spec/rubyspec/library/net/ftp/default_passive_spec.rb new file mode 100644 index 0000000000..f526373b82 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/default_passive_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +ruby_version_is "2.3" do + describe "Net::FTP#default_passive" do + it "is true by default" do + ruby_exe(fixture(__FILE__, "default_passive.rb")).should == "true\ntrue\n" + end + end +end diff --git a/spec/rubyspec/library/net/ftp/delete_spec.rb b/spec/rubyspec/library/net/ftp/delete_spec.rb new file mode 100644 index 0000000000..b20ef32651 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/delete_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#delete" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the DELE command with the passed filename to the server" do + @ftp.delete("test.file") + @ftp.last_response.should == "250 Requested file action okay, completed. (DELE test.file)\n" + end + + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:dele).and_respond("450 Requested file action not taken.") + lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:dele).and_respond("550 Requested action not taken.") + lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:dele).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:dele).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:dele).and_respond("502 Command not implemented.") + lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:dele).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:dele).and_respond("530 Not logged in.") + lambda { @ftp.delete("test.file") }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/dir_spec.rb b/spec/rubyspec/library/net/ftp/dir_spec.rb new file mode 100644 index 0000000000..6a2b766ca2 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/dir_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/list', __FILE__) + +describe "Net::FTP#dir" do + it_behaves_like :net_ftp_list, :dir +end diff --git a/spec/rubyspec/library/net/ftp/fixtures/default_passive.rb b/spec/rubyspec/library/net/ftp/fixtures/default_passive.rb new file mode 100644 index 0000000000..b6995d6f34 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/fixtures/default_passive.rb @@ -0,0 +1,3 @@ +require "net/ftp" +puts Net::FTP.default_passive +puts Net::FTP.new.passive diff --git a/spec/rubyspec/library/net/ftp/fixtures/passive.rb b/spec/rubyspec/library/net/ftp/fixtures/passive.rb new file mode 100644 index 0000000000..6b5cde82df --- /dev/null +++ b/spec/rubyspec/library/net/ftp/fixtures/passive.rb @@ -0,0 +1,2 @@ +require "net/ftp" +print Net::FTP.new.passive diff --git a/spec/rubyspec/library/net/ftp/fixtures/putbinaryfile b/spec/rubyspec/library/net/ftp/fixtures/putbinaryfile new file mode 100644 index 0000000000..eabb8fcedd --- /dev/null +++ b/spec/rubyspec/library/net/ftp/fixtures/putbinaryfile @@ -0,0 +1,3 @@ +This is an example file +which is going to be transmitted +using #putbinaryfile. \ No newline at end of file diff --git a/spec/rubyspec/library/net/ftp/fixtures/puttextfile b/spec/rubyspec/library/net/ftp/fixtures/puttextfile new file mode 100644 index 0000000000..dce0d35f8d --- /dev/null +++ b/spec/rubyspec/library/net/ftp/fixtures/puttextfile @@ -0,0 +1,3 @@ +This is an example file +which is going to be transmitted +using #puttextfile. \ No newline at end of file diff --git a/spec/rubyspec/library/net/ftp/fixtures/server.rb b/spec/rubyspec/library/net/ftp/fixtures/server.rb new file mode 100644 index 0000000000..69dc151333 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/fixtures/server.rb @@ -0,0 +1,276 @@ +module NetFTPSpecs + class DummyFTP + attr_accessor :connect_message + attr_reader :login_user, :login_pass, :login_acct + + # hostname or IP address + attr_reader :hostname + # port number + attr_reader :server_port + + def initialize(hostname = "localhost", server_port = 0) + @hostname = hostname + @server = TCPServer.new(@hostname, server_port) + @server_port = @server.addr[1] + + @handlers = {} + @commands = [] + @connect_message = nil + end + + def serve_once + @thread = Thread.new do + @socket = @server.accept + @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1) + begin + handle_request + ensure + @socket.close + end + end + end + + def handle_request + # Send out the welcome message. + response @connect_message || "220 Dummy FTP Server ready!" + + begin + loop do + command = @socket.recv(1024) + break if command.nil? + + command, argument = command.chomp.split(" ", 2) + + if command == "QUIT" + self.response("221 OK, bye") + break + elsif proc_handler = @handlers[command.downcase.to_sym] + if argument.nil? + proc_handler.call(self) + else + proc_handler.call(self, argument) + end + else + if argument.nil? + self.send(command.downcase.to_sym) + else + self.send(command.downcase.to_sym, argument) + end + end + end + rescue => e + self.error_response("Exception: #{e} #{e.backtrace.inspect}") + end + end + + def error_response(text) + self.response("451 #{text}") + end + + def response(text) + @socket.puts(text) unless @socket.closed? + end + + def stop + @datasocket.close unless @datasocket.nil? || @datasocket.closed? + @server.close + @thread.join + end + + + ## + def handle(sym, &block) + @handlers[sym] = block + end + + def should_receive(method) + @handler_for = method + self + end + + def and_respond(text) + @handlers[@handler_for] = lambda { |s, *args| s.response(text) } + end + + ## + # FTP methods + ## + + def abor + self.response("226 Closing data connection. (ABOR)") + end + + def acct(account) + @login_acct = account + self.response("230 User '#{account}' logged in, proceed. (ACCT)") + end + + def cdup + self.response("200 Command okay. (CDUP)") + end + + def cwd(dir) + self.response("200 Command okay. (CWD #{dir})") + end + + def dele(file) + self.response("250 Requested file action okay, completed. (DELE #{file})") + end + + def eprt(arg) + _, _, host, port = arg.split("|") + + @datasocket = TCPSocket.new(host, port) + self.response("200 port opened") + end + + def help(param = :default) + if param == :default + self.response("211 System status, or system help reply. (HELP)") + else + self.response("211 System status, or system help reply. (HELP #{param})") + end + end + + def list(folder) + self.response("150 opening ASCII connection for file list") + @datasocket.puts("-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb") + @datasocket.puts("-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb") + @datasocket.puts("-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb") + @datasocket.close() + self.response("226 transfer complete (LIST #{folder})") + end + + def mdtm(filename) + self.response("213 19980705132316") + end + + def mkd(foldername) + self.response(%Q{257 "#{foldername.gsub('"', '""')}" created.}) + end + + def nlst(folder = nil) + self.response("150 opening ASCII connection for file list") + @datasocket.puts("last_response_code.rb") + @datasocket.puts("list.rb") + @datasocket.puts("pwd.rb") + @datasocket.close() + self.response("226 transfer complete (NLST#{folder ? " #{folder}" : ""})") + end + + def noop + self.response("200 Command okay. (NOOP)") + end + + def pass(password) + @login_pass = password + self.response("230 User logged in, proceed. (PASS #{password})") + end + + def port(arg) + nums = arg.split(",") + + if nums[0] == "::1" + # IPv6 + port = nums[1].to_i * 256 + nums[2].to_i + host = nums[0] + else + # IPv4 + port = nums[4].to_i * 256 + nums[5].to_i + host = nums[0..3].join(".") + end + + @datasocket = TCPSocket.new(host, port) + self.response("200 port opened") + end + + def pwd + self.response('257 "/some/dir/" - current directory') + end + + def retr(file) + self.response("125 Data transfer starting") + if @restart_at && @restart_at == 20 + @datasocket.puts("of the file named '#{file}'.") + @restart_at = nil + else + @datasocket.puts("This is the content") + @datasocket.puts("of the file named '#{file}'.") + end + @datasocket.close() + self.response("226 Closing data connection. (RETR #{file})") + end + + def rest(at_bytes) + @restart_at = at_bytes.to_i + self.response("350 Requested file action pending further information. (REST)") + end + + def rmd(folder) + self.response("250 Requested file action okay, completed. (RMD #{folder})") + end + + def rnfr(from) + @rename_from = from + self.response("350 Requested file action pending further information.") + end + + def rnto(to) + self.response("250 Requested file action okay, completed. (Renamed #{@rename_from} to #{to})") + @rename_from = nil + end + + def site(param) + self.response("200 Command okay. (SITE #{param})") + end + + def size(filename) + if filename == "binary" + self.response("213 24") + else + self.response("213 1024") + end + end + + def stat + self.response("211 System status, or system help reply. (STAT)") + end + + def stor(file) + tmp_file = tmp("#{file}file", false) + + self.response("125 Data transfer starting.") + + mode = @restart_at ? "a" : "w" + + File.open(tmp_file, mode + "b") do |f| + loop do + data = @datasocket.recv(1024) + break if !data || data.empty? + f << data + end + end + + #@datasocket.close() + self.response("200 OK, Data received. (STOR #{file})") + end + + def appe(file) + @restart_at = true + stor(file) + end + + def syst + self.response("215 FTP Dummy Server (SYST)") + end + + def type(type) + self.response("200 TYPE switched to #{type}") + end + + def user(name) + @login_user = name + self.response("230 User logged in, proceed. (USER #{name})") + end + end +end diff --git a/spec/rubyspec/library/net/ftp/get_spec.rb b/spec/rubyspec/library/net/ftp/get_spec.rb new file mode 100644 index 0000000000..59fc45d010 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/get_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/gettextfile', __FILE__) +require File.expand_path('../shared/getbinaryfile', __FILE__) + +describe "Net::FTP#get (binary mode)" do + before :each do + @binary_mode = true + end + + it_behaves_like :net_ftp_getbinaryfile, :get +end + +describe "Net::FTP#get (text mode)" do + before :each do + @binary_mode = false + end + + it_behaves_like :net_ftp_gettextfile, :get +end diff --git a/spec/rubyspec/library/net/ftp/getbinaryfile_spec.rb b/spec/rubyspec/library/net/ftp/getbinaryfile_spec.rb new file mode 100644 index 0000000000..0f921c8b1d --- /dev/null +++ b/spec/rubyspec/library/net/ftp/getbinaryfile_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/getbinaryfile', __FILE__) + +describe "Net::FTP#getbinaryfile" do + it_behaves_like :net_ftp_getbinaryfile, :getbinaryfile +end diff --git a/spec/rubyspec/library/net/ftp/getdir_spec.rb b/spec/rubyspec/library/net/ftp/getdir_spec.rb new file mode 100644 index 0000000000..19ace4dff9 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/getdir_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../shared/pwd', __FILE__) + +describe "Net::FTP#getdir" do + it_behaves_like :net_ftp_pwd, :getdir +end diff --git a/spec/rubyspec/library/net/ftp/gettextfile_spec.rb b/spec/rubyspec/library/net/ftp/gettextfile_spec.rb new file mode 100644 index 0000000000..29077df253 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/gettextfile_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/gettextfile', __FILE__) + +describe "Net::FTP#gettextfile" do + it_behaves_like :net_ftp_gettextfile, :gettextfile +end diff --git a/spec/rubyspec/library/net/ftp/help_spec.rb b/spec/rubyspec/library/net/ftp/help_spec.rb new file mode 100644 index 0000000000..6f5f901024 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/help_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#help" do + def with_connection + yield + end + + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "writes the HELP command to the server" do + @ftp.help + @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n" + end + + it "returns the server's response" do + @ftp.help.should == "211 System status, or system help reply. (HELP)\n" + end + + it "writes the HELP command with an optional parameter to the socket" do + @ftp.help("some parameter").should == "211 System status, or system help reply. (HELP some parameter)\n" + end + + it "does not raise any error when the response code is 211" do + @server.should_receive(:help).and_respond("211 System status, or system help reply.") + lambda { @ftp.help }.should_not raise_error + end + + it "does not raise any error when the response code is 214" do + @server.should_receive(:help).and_respond("214 Help message.") + lambda { @ftp.help }.should_not raise_error + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.help }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:help).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.help }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:help).and_respond("502 Command not implemented.") + lambda { @ftp.help }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:help).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.help }.should raise_error(Net::FTPTempError) + end +end diff --git a/spec/rubyspec/library/net/ftp/initialize_spec.rb b/spec/rubyspec/library/net/ftp/initialize_spec.rb new file mode 100644 index 0000000000..767bbecedb --- /dev/null +++ b/spec/rubyspec/library/net/ftp/initialize_spec.rb @@ -0,0 +1,87 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#initialize" do + before :each do + @ftp = Net::FTP.allocate + @ftp.stub!(:connect) + end + + it "is private" do + Net::FTP.should have_private_instance_method(:initialize) + end + + it "sets self into binary mode" do + @ftp.binary.should be_nil + @ftp.send(:initialize) + @ftp.binary.should be_true + end + + it "sets self into active mode" do + @ftp.passive.should be_nil + @ftp.send(:initialize) + @ftp.passive.should be_false + end + + it "sets self into non-debug mode" do + @ftp.debug_mode.should be_nil + @ftp.send(:initialize) + @ftp.debug_mode.should be_false + end + + it "sets self to not resume file uploads/downloads" do + @ftp.resume.should be_nil + @ftp.send(:initialize) + @ftp.resume.should be_false + end + + describe "when passed no arguments" do + it "does not try to connect" do + @ftp.should_not_receive(:connect) + @ftp.send(:initialize) + end + end + + describe "when passed host" do + it "tries to connect to the passed host" do + @ftp.should_receive(:connect).with("localhost") + @ftp.send(:initialize, "localhost") + end + end + + describe "when passed host, user" do + it "tries to connect to the passed host" do + @ftp.should_receive(:connect).with("localhost") + @ftp.send(:initialize, "localhost") + end + + it "tries to login with the passed username" do + @ftp.should_receive(:login).with("rubyspec", nil, nil) + @ftp.send(:initialize, "localhost", "rubyspec") + end + end + + describe "when passed host, user, password" do + it "tries to connect to the passed host" do + @ftp.should_receive(:connect).with("localhost") + @ftp.send(:initialize, "localhost") + end + + it "tries to login with the passed username and password" do + @ftp.should_receive(:login).with("rubyspec", "rocks", nil) + @ftp.send(:initialize, "localhost", "rubyspec", "rocks") + end + end + + describe "when passed host, user" do + it "tries to connect to the passed host" do + @ftp.should_receive(:connect).with("localhost") + @ftp.send(:initialize, "localhost") + end + + it "tries to login with the passed username, password and account" do + @ftp.should_receive(:login).with("rubyspec", "rocks", "account") + @ftp.send(:initialize, "localhost", "rubyspec", "rocks", "account") + end + end +end diff --git a/spec/rubyspec/library/net/ftp/last_response_code_spec.rb b/spec/rubyspec/library/net/ftp/last_response_code_spec.rb new file mode 100644 index 0000000000..77651a9be4 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/last_response_code_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../shared/last_response_code', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#last_response_code" do + it_behaves_like :net_ftp_last_response_code, :last_response_code +end diff --git a/spec/rubyspec/library/net/ftp/last_response_spec.rb b/spec/rubyspec/library/net/ftp/last_response_spec.rb new file mode 100644 index 0000000000..eb53f92774 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/last_response_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#last_response" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "returns the last response" do + @ftp.last_response.should == "220 Dummy FTP Server ready!\n" + @ftp.help + @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n" + end +end diff --git a/spec/rubyspec/library/net/ftp/lastresp_spec.rb b/spec/rubyspec/library/net/ftp/lastresp_spec.rb new file mode 100644 index 0000000000..d2c6f44e48 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/lastresp_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../shared/last_response_code', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#lastresp" do + it_behaves_like :net_ftp_last_response_code, :lastresp +end diff --git a/spec/rubyspec/library/net/ftp/list_spec.rb b/spec/rubyspec/library/net/ftp/list_spec.rb new file mode 100644 index 0000000000..41c55c42ac --- /dev/null +++ b/spec/rubyspec/library/net/ftp/list_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/list', __FILE__) + +describe "Net::FTP#list" do + it_behaves_like :net_ftp_list, :list +end diff --git a/spec/rubyspec/library/net/ftp/login_spec.rb b/spec/rubyspec/library/net/ftp/login_spec.rb new file mode 100644 index 0000000000..9768d9cf33 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/login_spec.rb @@ -0,0 +1,195 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#login" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + describe "when passed no arguments" do + it "sends the USER command with 'anonymous' as name to the server" do + @ftp.login + @server.login_user.should == "anonymous" + end + + it "sends 'anonymous@' as a password when required" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @ftp.login + @server.login_pass.should == "anonymous@" + end + + it "raises a Net::FTPReplyError when the server requests an account" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @server.should_receive(:pass).and_respond("332 Need account for login.") + lambda { @ftp.login }.should raise_error(Net::FTPReplyError) + end + end + + describe "when passed name" do + it "sends the USER command with the passed name to the server" do + @ftp.login("rubyspec") + @server.login_user.should == "rubyspec" + end + + it "raises a Net::FTPReplyError when the server requests a password, but none was given" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + lambda { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError) + end + + it "raises a Net::FTPReplyError when the server requests an account, but none was given" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @server.should_receive(:pass).and_respond("332 Need account for login.") + lambda { @ftp.login("rubyspec") }.should raise_error(Net::FTPReplyError) + end + end + + describe "when passed name, password" do + it "sends the USER command with the passed name to the server" do + @ftp.login("rubyspec", "rocks") + @server.login_user.should == "rubyspec" + end + + it "sends the passed password when required" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @ftp.login("rubyspec", "rocks") + @server.login_pass.should == "rocks" + end + + it "raises a Net::FTPReplyError when the server requests an account" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @server.should_receive(:pass).and_respond("332 Need account for login.") + lambda { @ftp.login("rubyspec", "rocks") }.should raise_error(Net::FTPReplyError) + end + end + + describe "when passed name, password, account" do + it "sends the USER command with the passed name to the server" do + @ftp.login("rubyspec", "rocks", "account") + @server.login_user.should == "rubyspec" + end + + it "sends the passed password when required" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @ftp.login("rubyspec", "rocks", "account") + @server.login_pass.should == "rocks" + end + + it "sends the passed account when required" do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @server.should_receive(:pass).and_respond("332 Need account for login.") + @ftp.login("rubyspec", "rocks", "account") + @server.login_acct.should == "account" + end + end + + describe "when the USER command fails" do + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:user).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:user).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:user).and_respond("502 Command not implemented.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:user).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:user).and_respond("530 Not logged in.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + end + + describe "when the PASS command fails" do + before :each do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + end + + it "does not raise an Error when the response code is 202" do + @server.should_receive(:pass).and_respond("202 Command not implemented, superfluous at this site.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:pass).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:pass).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:pass).and_respond("502 Command not implemented.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:pass).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:pass).and_respond("530 Not logged in.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + end + + describe "when the ACCT command fails" do + before :each do + @server.should_receive(:user).and_respond("331 User name okay, need password.") + @server.should_receive(:pass).and_respond("332 Need account for login.") + end + + it "does not raise an Error when the response code is 202" do + @server.should_receive(:acct).and_respond("202 Command not implemented, superfluous at this site.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should_not raise_error + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:acct).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:acct).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:acct).and_respond("502 Command not implemented.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:acct).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:acct).and_respond("530 Not logged in.") + lambda { @ftp.login("rubyspec", "rocks", "account") }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/ls_spec.rb b/spec/rubyspec/library/net/ftp/ls_spec.rb new file mode 100644 index 0000000000..f713fb46aa --- /dev/null +++ b/spec/rubyspec/library/net/ftp/ls_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/list', __FILE__) + +describe "Net::FTP#ls" do + it_behaves_like :net_ftp_list, :ls +end diff --git a/spec/rubyspec/library/net/ftp/mdtm_spec.rb b/spec/rubyspec/library/net/ftp/mdtm_spec.rb new file mode 100644 index 0000000000..bbd28d10b1 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/mdtm_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#mdtm" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the MDTM with the passed filename command to the server" do + @ftp.mdtm("test.file") + @ftp.last_response.should == "213 19980705132316\n" + end + + it "returns the last modification time of the passed file" do + @ftp.mdtm("test.file").should == "19980705132316" + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:mdtm).and_respond("550 Requested action not taken.") + lambda { @ftp.mdtm("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.mdtm("test.file") }.should raise_error(Net::FTPTempError) + end +end diff --git a/spec/rubyspec/library/net/ftp/mkdir_spec.rb b/spec/rubyspec/library/net/ftp/mkdir_spec.rb new file mode 100644 index 0000000000..2a7088e5d9 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/mkdir_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#mkdir" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the MKD command with the passed pathname to the server" do + @ftp.mkdir("test.folder") + @ftp.last_response.should == %{257 "test.folder" created.\n} + end + + it "returns the path to the newly created directory" do + @ftp.mkdir("test.folder").should == "test.folder" + @ftp.mkdir("/absolute/path/to/test.folder").should == "/absolute/path/to/test.folder" + @ftp.mkdir("relative/path/to/test.folder").should == "relative/path/to/test.folder" + @ftp.mkdir('/usr/dm/foo"bar').should == '/usr/dm/foo"bar' + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:mkd).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:mkd).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:mkd).and_respond("502 Command not implemented.") + lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:mkd).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:mkd).and_respond("530 Not logged in.") + lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:mkd).and_respond("550 Requested action not taken.") + lambda { @ftp.mkdir("test.folder") }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/mtime_spec.rb b/spec/rubyspec/library/net/ftp/mtime_spec.rb new file mode 100644 index 0000000000..0e76aa079b --- /dev/null +++ b/spec/rubyspec/library/net/ftp/mtime_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#mtime" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the MDTM with the passed filename command to the server" do + @ftp.mtime("test.file") + @ftp.last_response.should == "213 19980705132316\n" + end + + describe "when passed filename" do + it "returns the last modification time of the passed file as a Time object in the local time" do + @ftp.mtime("test.file").should == Time.gm("1998", "07", "05", "13", "23", "16") + end + end + + describe "when passed filename, local_time" do + it "returns the last modification time as a Time object in UTC when local_time is true" do + @ftp.mtime("test.file", true).should == Time.local("1998", "07", "05", "13", "23", "16") + end + + it "returns the last modification time as a Time object in the local time when local_time is false" do + @ftp.mtime("test.file", false).should == Time.gm("1998", "07", "05", "13", "23", "16") + end + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:mdtm).and_respond("550 Requested action not taken.") + lambda { @ftp.mtime("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:mdtm).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.mtime("test.file") }.should raise_error(Net::FTPTempError) + end +end diff --git a/spec/rubyspec/library/net/ftp/nlst_spec.rb b/spec/rubyspec/library/net/ftp/nlst_spec.rb new file mode 100644 index 0000000000..4723e9f6c6 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/nlst_spec.rb @@ -0,0 +1,92 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#nlst" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.passive = false + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + describe "when passed no arguments" do + it "returns an Array containing a list of files in the current dir" do + @ftp.nlst.should == ["last_response_code.rb", "list.rb", "pwd.rb"] + @ftp.last_response.should == "226 transfer complete (NLST)\n" + end + end + + describe "when passed dir" do + it "returns an Array containing a list of files in the passed dir" do + @ftp.nlst("test.folder").should == ["last_response_code.rb", "list.rb", "pwd.rb"] + @ftp.last_response.should == "226 transfer complete (NLST test.folder)\n" + end + end + + describe "when the NLST command fails" do + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:nlst).and_respond("450 Requested file action not taken..") + lambda { @ftp.nlst }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:nlst).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.nlst }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:nlst).and_respond("501 Syntax error, command unrecognized.") + lambda { @ftp.nlst }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:nlst).and_respond("502 Command not implemented.") + lambda { @ftp.nlst }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:nlst).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.nlst }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:nlst).and_respond("530 Not logged in.") + lambda { @ftp.nlst }.should raise_error(Net::FTPPermError) + end + end + + describe "when opening the data port fails" do + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.") + @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.nlst }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.") + @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.nlst }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.") + @server.should_receive(:port).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.nlst }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:eprt).and_respond("530 Not logged in.") + @server.should_receive(:port).and_respond("530 Not logged in.") + lambda { @ftp.nlst }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/noop_spec.rb b/spec/rubyspec/library/net/ftp/noop_spec.rb new file mode 100644 index 0000000000..1bc6ccd913 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/noop_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#noop" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the NOOP command to the server" do + @ftp.noop + @ftp.last_response.should == "200 Command okay. (NOOP)\n" + end + + it "returns nil" do + @ftp.noop.should be_nil + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:noop).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.noop }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:noop).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.noop }.should raise_error(Net::FTPTempError) + end +end diff --git a/spec/rubyspec/library/net/ftp/open_spec.rb b/spec/rubyspec/library/net/ftp/open_spec.rb new file mode 100644 index 0000000000..b422e64c87 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/open_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP.open" do + before :each do + @ftp = mock("Net::FTP instance") + Net::FTP.stub!(:new).and_return(@ftp) + end + + describe "when passed no block" do + it "returns a new Net::FTP instance" do + Net::FTP.open("localhost").should equal(@ftp) + end + + it "passes the passed arguments down to Net::FTP.new" do + Net::FTP.should_receive(:new).with("localhost", "user", "password", "account") + Net::FTP.open("localhost", "user", "password", "account") + end + end + + describe "when passed a block" do + before :each do + @ftp.stub!(:close) + end + + it "yields a new Net::FTP instance to the passed block" do + yielded = false + Net::FTP.open("localhost") do |ftp| + yielded = true + ftp.should equal(@ftp) + end + yielded.should be_true + end + + it "closes the Net::FTP instance after yielding" do + Net::FTP.open("localhost") do |ftp| + ftp.should_receive(:close) + end + end + + it "closes the Net::FTP instance even if an exception is raised while yielding" do + begin + Net::FTP.open("localhost") do |ftp| + ftp.should_receive(:close) + raise ArgumentError, "some exception" + end + rescue ArgumentError + end + end + + it "returns the block's return value" do + Net::FTP.open("localhost") { :test }.should == :test + end + end +end diff --git a/spec/rubyspec/library/net/ftp/passive_spec.rb b/spec/rubyspec/library/net/ftp/passive_spec.rb new file mode 100644 index 0000000000..fd9a0dffe3 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/passive_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#passive" do + it "returns true when self is in passive mode" do + ftp = Net::FTP.new + ftp.passive.should be_false + + ftp.passive = true + ftp.passive.should be_true + end + + ruby_version_is ""..."2.3" do + it "is false by default" do + ruby_exe(fixture(__FILE__, "passive.rb")).should == "false" + end + end + + ruby_version_is "2.3" do + it "is the value of Net::FTP.default_value by default" do + ruby_exe(fixture(__FILE__, "passive.rb")).should == "true" + end + end +end + +describe "Net::FTP#passive=" do + it "sets self to passive mode when passed true" do + ftp = Net::FTP.new + + ftp.passive = true + ftp.passive.should be_true + + ftp.passive = false + ftp.passive.should be_false + end +end diff --git a/spec/rubyspec/library/net/ftp/put_spec.rb b/spec/rubyspec/library/net/ftp/put_spec.rb new file mode 100644 index 0000000000..61a8d00543 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/put_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/puttextfile', __FILE__) +require File.expand_path('../shared/putbinaryfile', __FILE__) + +describe "Net::FTP#put (binary mode)" do + before :each do + @binary_mode = true + end + + it_behaves_like :net_ftp_putbinaryfile, :put +end + +describe "Net::FTP#put (text mode)" do + before :each do + @binary_mode = false + end + + it_behaves_like :net_ftp_puttextfile, :put +end diff --git a/spec/rubyspec/library/net/ftp/putbinaryfile_spec.rb b/spec/rubyspec/library/net/ftp/putbinaryfile_spec.rb new file mode 100644 index 0000000000..18955409b6 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/putbinaryfile_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/putbinaryfile', __FILE__) + +describe "Net::FTP#putbinaryfile" do + it_behaves_like :net_ftp_putbinaryfile, :putbinaryfile +end diff --git a/spec/rubyspec/library/net/ftp/puttextfile_spec.rb b/spec/rubyspec/library/net/ftp/puttextfile_spec.rb new file mode 100644 index 0000000000..2ca6e98c92 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/puttextfile_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) +require File.expand_path('../shared/puttextfile', __FILE__) + +describe "Net::FTP#puttextfile" do + it_behaves_like :net_ftp_puttextfile, :puttextfile +end diff --git a/spec/rubyspec/library/net/ftp/pwd_spec.rb b/spec/rubyspec/library/net/ftp/pwd_spec.rb new file mode 100644 index 0000000000..d5438d8e56 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/pwd_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#pwd" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the PWD command to the server" do + @ftp.pwd + @ftp.last_response.should == "257 \"/some/dir/\" - current directory\n" + end + + it "returns the current directory" do + @ftp.pwd.should == "/some/dir/" + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:pwd).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.pwd }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:pwd).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.pwd }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:pwd).and_respond("502 Command not implemented.") + lambda { @ftp.pwd }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:pwd).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.pwd }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:pwd).and_respond("550 Requested action not taken.") + lambda { @ftp.pwd }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/quit_spec.rb b/spec/rubyspec/library/net/ftp/quit_spec.rb new file mode 100644 index 0000000000..c053641939 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/quit_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#quit" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the QUIT command to the server" do + @ftp.quit + @ftp.last_response.should == "221 OK, bye\n" + end + + it "does not close the socket automagically" do + @ftp.quit + @ftp.closed?.should be_false + end + + it "returns nil" do + @ftp.quit.should be_nil + end +end diff --git a/spec/rubyspec/library/net/ftp/rename_spec.rb b/spec/rubyspec/library/net/ftp/rename_spec.rb new file mode 100644 index 0000000000..0216d2059a --- /dev/null +++ b/spec/rubyspec/library/net/ftp/rename_spec.rb @@ -0,0 +1,94 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#rename" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + describe "when passed from_name, to_name" do + it "sends the RNFR command with the passed from_name and the RNTO command with the passed to_name to the server" do + @ftp.rename("from.file", "to.file") + @ftp.last_response.should == "250 Requested file action okay, completed. (Renamed from.file to to.file)\n" + end + + it "returns something" do + @ftp.rename("from.file", "to.file").should be_nil + end + end + + describe "when the RNFR command fails" do + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:rnfr).and_respond("450 Requested file action not taken.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:rnfr).and_respond("550 Requested action not taken.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:rnfr).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:rnfr).and_respond("502 Command not implemented.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:rnfr).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:rnfr).and_respond("530 Not logged in.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + end + + describe "when the RNTO command fails" do + it "raises a Net::FTPPermError when the response code is 532" do + @server.should_receive(:rnfr).and_respond("532 Need account for storing files.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 553" do + @server.should_receive(:rnto).and_respond("553 Requested action not taken.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:rnto).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:rnto).and_respond("502 Command not implemented.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:rnto).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:rnto).and_respond("530 Not logged in.") + lambda { @ftp.rename("from.file", "to.file") }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/resume_spec.rb b/spec/rubyspec/library/net/ftp/resume_spec.rb new file mode 100644 index 0000000000..9ec4a921aa --- /dev/null +++ b/spec/rubyspec/library/net/ftp/resume_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#resume" do + it "returns true when self is set to resume uploads/downloads" do + ftp = Net::FTP.new + ftp.resume.should be_false + + ftp.resume = true + ftp.resume.should be_true + end +end + +describe "Net::FTP#resume=" do + it "sets self to resume uploads/downloads when set to true" do + ftp = Net::FTP.new + ftp.resume = true + ftp.resume.should be_true + + ftp.resume = false + ftp.resume.should be_false + end +end diff --git a/spec/rubyspec/library/net/ftp/retrbinary_spec.rb b/spec/rubyspec/library/net/ftp/retrbinary_spec.rb new file mode 100644 index 0000000000..fd7af0b62a --- /dev/null +++ b/spec/rubyspec/library/net/ftp/retrbinary_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#retrbinary" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the passed command to the server" do + @ftp.retrbinary("RETR test", 4096) {} + @ftp.last_response.should == "226 Closing data connection. (RETR test)\n" + end + + it "yields the received content as binary blocks of the passed size" do + res = [] + @ftp.retrbinary("RETR test", 10) { |bin| res << bin } + res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ] + end +end diff --git a/spec/rubyspec/library/net/ftp/retrlines_spec.rb b/spec/rubyspec/library/net/ftp/retrlines_spec.rb new file mode 100644 index 0000000000..cb8d48ecf7 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/retrlines_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#retrlines" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the passed command over the socket" do + @ftp.retrlines("LIST test.dir") {} + @ftp.last_response.should == "226 transfer complete (LIST test.dir)\n" + end + + it "yields each received line to the passed block" do + res = [] + @ftp.retrlines("LIST test.dir") { |x| res << x } + res.should == [ + "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb", + "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb", + "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb" + ] + end +end diff --git a/spec/rubyspec/library/net/ftp/return_code_spec.rb b/spec/rubyspec/library/net/ftp/return_code_spec.rb new file mode 100644 index 0000000000..b6090aeb61 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/return_code_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#return_code" do + before :each do + @ftp = Net::FTP.new + end + + it "outputs a warning and returns a newline" do + lambda do + @ftp.return_code.should == "\n" + end.should complain("warning: Net::FTP#return_code is obsolete and do nothing\n") + end +end + +describe "Net::FTP#return_code=" do + before :each do + @ftp = Net::FTP.new + end + + it "outputs a warning" do + lambda { @ftp.return_code = 123 }.should complain("warning: Net::FTP#return_code= is obsolete and do nothing\n") + end +end diff --git a/spec/rubyspec/library/net/ftp/rmdir_spec.rb b/spec/rubyspec/library/net/ftp/rmdir_spec.rb new file mode 100644 index 0000000000..453c7823c4 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/rmdir_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#rmdir" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the RMD command with the passed pathname to the server" do + @ftp.rmdir("test.folder") + @ftp.last_response.should == "250 Requested file action okay, completed. (RMD test.folder)\n" + end + + it "returns nil" do + @ftp.rmdir("test.folder").should be_nil + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:rmd).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:rmd).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:rmd).and_respond("502 Command not implemented.") + lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:rmd).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:rmd).and_respond("530 Not logged in.") + lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:rmd).and_respond("550 Requested action not taken.") + lambda { @ftp.rmdir("test.folder") }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/sendcmd_spec.rb b/spec/rubyspec/library/net/ftp/sendcmd_spec.rb new file mode 100644 index 0000000000..9bdca621bc --- /dev/null +++ b/spec/rubyspec/library/net/ftp/sendcmd_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#sendcmd" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the passed command to the server" do + @ftp.sendcmd("HELP") + @ftp.last_response.should == "211 System status, or system help reply. (HELP)\n" + end + + it "returns the server's response" do + @ftp.sendcmd("HELP").should == "211 System status, or system help reply. (HELP)\n" + end + + it "raises no error when the response code is 1xx, 2xx or 3xx" do + @server.should_receive(:help).and_respond("120 Service ready in nnn minutes.") + lambda { @ftp.sendcmd("HELP") }.should_not raise_error + + @server.should_receive(:help).and_respond("200 Command okay.") + lambda { @ftp.sendcmd("HELP") }.should_not raise_error + + @server.should_receive(:help).and_respond("350 Requested file action pending further information.") + lambda { @ftp.sendcmd("HELP") }.should_not raise_error + end + + it "raises a Net::FTPTempError when the response code is 4xx" do + @server.should_receive(:help).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 5xx" do + @server.should_receive(:help).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPProtoError when the response code is not between 1xx-5xx" do + @server.should_receive(:help).and_respond("999 Invalid response.") + lambda { @ftp.sendcmd("HELP") }.should raise_error(Net::FTPProtoError) + end +end diff --git a/spec/rubyspec/library/net/ftp/set_socket_spec.rb b/spec/rubyspec/library/net/ftp/set_socket_spec.rb new file mode 100644 index 0000000000..1553445219 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/set_socket_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) + +describe "Net::FTP#set_socket" do + # TODO: I won't spec this method, as it is not used + # anywhere and it should be private anyway. + #it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/net/ftp/shared/getbinaryfile.rb b/spec/rubyspec/library/net/ftp/shared/getbinaryfile.rb new file mode 100644 index 0000000000..2252935b2d --- /dev/null +++ b/spec/rubyspec/library/net/ftp/shared/getbinaryfile.rb @@ -0,0 +1,150 @@ +describe :net_ftp_getbinaryfile, shared: :true do + before :each do + @fixture_file = File.dirname(__FILE__) + "/../fixtures/getbinaryfile" + @tmp_file = tmp("getbinaryfile") + + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + @ftp.binary = @binary_mode + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + + rm_r @tmp_file + end + + it "sends the RETR command to the server" do + @ftp.send(@method, "test", @tmp_file) + @ftp.last_response.should == "226 Closing data connection. (RETR test)\n" + end + + it "returns nil" do + @ftp.send(@method, "test", @tmp_file).should be_nil + end + + it "saves the contents of the passed remote file to the passed local file" do + @ftp.send(@method, "test", @tmp_file) + File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n" + end + + describe "when passed a block" do + it "yields the received content as binary blocks of the passed size" do + res = [] + @ftp.send(@method, "test", @tmp_file, 10) { |bin| res << bin } + res.should == [ "This is th", "e content\n", "of the fil", "e named 't", "est'.\n" ] + end + end + + describe "when resuming an existing file" do + before :each do + @tmp_file = tmp("getbinaryfile_resume") + + File.open(@tmp_file, "wb") do |f| + f << "This is the content\n" + end + + @ftp.resume = true + end + + it "saves the remaining content of the passed remote file to the passed local file" do + @ftp.send(@method, "test", @tmp_file) + File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n" + end + + describe "and the REST command fails" do + it "raises a Net::FTPProtoError when the response code is 550" do + @server.should_receive(:rest).and_respond("Requested action not taken.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:rest).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:rest).and_respond("501 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:rest).and_respond("502 Command not implemented.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:rest).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:rest).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + end + end + + describe "when the RETR command fails" do + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:retr).and_respond("450 Requested file action not taken.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPProtoError when the response code is 550" do + @server.should_receive(:retr).and_respond("Requested action not taken.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:retr).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + end + + describe "when opening the data port fails" do + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.") + @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.") + @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.") + @server.should_receive(:port).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:eprt).and_respond("530 Not logged in.") + @server.should_receive(:port).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/shared/gettextfile.rb b/spec/rubyspec/library/net/ftp/shared/gettextfile.rb new file mode 100644 index 0000000000..6219581d12 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/shared/gettextfile.rb @@ -0,0 +1,100 @@ +describe :net_ftp_gettextfile, shared: :true do + before :each do + @tmp_file = tmp("gettextfile") + + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + @ftp.binary = @binary_mode + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + + rm_r @tmp_file + end + + it "sends the RETR command to the server" do + @ftp.send(@method, "test", @tmp_file) + @ftp.last_response.should == "226 Closing data connection. (RETR test)\n" + end + + it "returns nil" do + @ftp.send(@method, "test", @tmp_file).should be_nil + end + + it "saves the contents of the passed remote file to the passed local file" do + @ftp.send(@method, "test", @tmp_file) + File.read(@tmp_file).should == "This is the content\nof the file named 'test'.\n" + end + + describe "when passed a block" do + it "yields each line of the retrieved file to the passed block" do + res = [] + @ftp.send(@method, "test", @tmp_file) { |line| res << line } + res.should == [ "This is the content", "of the file named 'test'."] + end + end + + describe "when the RETR command fails" do + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:retr).and_respond("450 Requested file action not taken.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPProtoError when the response code is 550" do + @server.should_receive(:retr).and_respond("Requested action not taken.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPProtoError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:retr).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:retr).and_respond("501 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:retr).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:retr).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + end + + describe "when opening the data port fails" do + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.") + @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.") + @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.") + @server.should_receive(:port).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:eprt).and_respond("530 Not logged in.") + @server.should_receive(:port).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, "test", @tmp_file) }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/shared/last_response_code.rb b/spec/rubyspec/library/net/ftp/shared/last_response_code.rb new file mode 100644 index 0000000000..4fe53677db --- /dev/null +++ b/spec/rubyspec/library/net/ftp/shared/last_response_code.rb @@ -0,0 +1,25 @@ +describe :net_ftp_last_response_code, shared: true do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "returns the response code for the last response" do + @server.should_receive(:help).and_respond("200 Command okay.") + @ftp.help + @ftp.send(@method).should == "200" + + @server.should_receive(:help).and_respond("212 Directory status.") + @ftp.help + @ftp.send(@method).should == "212" + end +end diff --git a/spec/rubyspec/library/net/ftp/shared/list.rb b/spec/rubyspec/library/net/ftp/shared/list.rb new file mode 100644 index 0000000000..50ca8ad119 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/shared/list.rb @@ -0,0 +1,104 @@ +describe :net_ftp_list, shared: true do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.passive = false + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + describe "when passed a block" do + it "yields each file in the list of files in the passed dir" do + expected = [ + "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb", + "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb", + "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb" + ] + + res = [] + @ftp.send(@method, "test.folder") { |line| res << line} + res.should == expected + + @ftp.last_response.should == "226 transfer complete (LIST test.folder)\n" + end + end + + describe "when passed no block" do + it "returns an Array containing a list of files in the passed dir" do + expected = [ + "-rw-r--r-- 1 spec staff 507 17 Jul 18:41 last_response_code.rb", + "-rw-r--r-- 1 spec staff 50 17 Jul 18:41 list.rb", + "-rw-r--r-- 1 spec staff 48 17 Jul 18:41 pwd.rb" + ] + + @ftp.send(@method, "test.folder").should == expected + + @ftp.last_response.should == "226 transfer complete (LIST test.folder)\n" + end + end + + describe "when the LIST command fails" do + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:list).and_respond("450 Requested file action not taken..") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:list).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:list).and_respond("501 Syntax error, command unrecognized.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:list).and_respond("502 Command not implemented.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:list).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:list).and_respond("530 Not logged in.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError) + end + end + + describe "when opening the data port fails" do + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.") + @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.") + @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.") + @server.should_receive(:port).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:eprt).and_respond("530 Not logged in.") + @server.should_receive(:port).and_respond("530 Not logged in.") + lambda { @ftp.send(@method) }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/shared/putbinaryfile.rb b/spec/rubyspec/library/net/ftp/shared/putbinaryfile.rb new file mode 100644 index 0000000000..f52e0c87e1 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/shared/putbinaryfile.rb @@ -0,0 +1,167 @@ +describe :net_ftp_putbinaryfile, shared: :true do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @local_fixture_file = File.dirname(__FILE__) + "/../fixtures/putbinaryfile" + @remote_tmp_file = tmp("binaryfile", false) + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + @ftp.binary = @binary_mode + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + + rm_r @remote_tmp_file + end + + it "sends the STOR command to the server" do + @ftp.send(@method, @local_fixture_file, "binary") + @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n" + end + + it "sends the contents of the passed local_file, without modifications" do + @ftp.send(@method, @local_fixture_file, "binary") + + remote_lines = File.readlines(@remote_tmp_file) + local_lines = File.readlines(@local_fixture_file) + + remote_lines.should == local_lines + end + + it "returns nil" do + @ftp.send(@method, @local_fixture_file, "binary").should be_nil + end + + describe "when passed a block" do + it "yields the transmitted content as binary blocks of the passed size" do + res = [] + @ftp.send(@method, @local_fixture_file, "binary", 10) { |x| res << x } + res.should == [ + "This is an", " example f", + "ile\nwhich ", "is going t", + "o be trans", "mitted\nusi", + "ng #putbin", "aryfile." + ] + end + end + + describe "when resuming an existing file" do + before :each do + File.open(@remote_tmp_file, "w") do |f| + f << "This is an example file\n" + end + + @ftp.resume = true + end + + it "sends the remaining content of the passed local_file to the passed remote_file" do + @ftp.send(@method, @local_fixture_file, "binary") + File.read(@remote_tmp_file).should == File.read(@local_fixture_file) + end + + describe "and the APPE command fails" do + it "raises a Net::FTPProtoError when the response code is 550" do + @server.should_receive(:appe).and_respond("Requested action not taken.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPProtoError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:appe).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:appe).and_respond("501 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:appe).and_respond("502 Command not implemented.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:appe).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:appe).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + end + end + + describe "when the STOR command fails" do + it "raises a Net::FTPPermError when the response code is 532" do + @server.should_receive(:stor).and_respond("532 Need account for storing files.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:stor).and_respond("450 Requested file action not taken.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPTempError when the response code is 452" do + @server.should_receive(:stor).and_respond("452 Requested action not taken.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 553" do + @server.should_receive(:stor).and_respond("553 Requested action not taken.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:stor).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + end + + describe "when opening the data port fails" do + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.") + @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.") + @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.") + @server.should_receive(:port).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:eprt).and_respond("530 Not logged in.") + @server.should_receive(:port).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, @local_fixture_file, "binary") }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/shared/puttextfile.rb b/spec/rubyspec/library/net/ftp/shared/puttextfile.rb new file mode 100644 index 0000000000..3c6b9a31ab --- /dev/null +++ b/spec/rubyspec/library/net/ftp/shared/puttextfile.rb @@ -0,0 +1,120 @@ +describe :net_ftp_puttextfile, shared: true do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @local_fixture_file = File.dirname(__FILE__) + "/../fixtures/puttextfile" + @remote_tmp_file = tmp("textfile", false) + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + @ftp.binary = @binary_mode + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + + rm_r @remote_tmp_file + end + + it "sends the STOR command to the server" do + @ftp.send(@method, @local_fixture_file, "text") + @ftp.last_response.should == "200 OK, Data received. (STOR text)\n" + end + + it "sends the contents of the passed local_file, using \\r\\n as the newline separator" do + @ftp.send(@method, @local_fixture_file, "text") + + remote_lines = open(@remote_tmp_file, "rb") {|f| f.read } + local_lines = open(@local_fixture_file, "rb") {|f| f.read } + "\n" + + remote_lines.should_not == local_lines + remote_lines.should == local_lines.gsub("\n", "\r\n") + end + + it "returns nil" do + @ftp.send(@method, @local_fixture_file, "text").should be_nil + end + + describe "when passed a block" do + it "yields each transmitted line" do + res = [] + @ftp.send(@method, @local_fixture_file, "text") { |x| res << x } + res.should == [ + "This is an example file\r\n", + "which is going to be transmitted\r\n", + "using #puttextfile.\r\n" + ] + end + end + + describe "when the STOR command fails" do + it "raises a Net::FTPPermError when the response code is 532" do + @server.should_receive(:stor).and_respond("532 Need account for storing files.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 450" do + @server.should_receive(:stor).and_respond("450 Requested file action not taken.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPTempError when the response code is 452" do + @server.should_receive(:stor).and_respond("452 Requested action not taken.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 553" do + @server.should_receive(:stor).and_respond("553 Requested action not taken.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:stor).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:stor).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:stor).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:stor).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + end + + describe "when opening the data port fails" do + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:eprt).and_respond("500 Syntax error, command unrecognized.") + @server.should_receive(:port).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:eprt).and_respond("501 Syntax error in parameters or arguments.") + @server.should_receive(:port).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:eprt).and_respond("421 Service not available, closing control connection.") + @server.should_receive(:port).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:eprt).and_respond("530 Not logged in.") + @server.should_receive(:port).and_respond("530 Not logged in.") + lambda { @ftp.send(@method, @local_fixture_file, "text") }.should raise_error(Net::FTPPermError) + end + end +end diff --git a/spec/rubyspec/library/net/ftp/shared/pwd.rb b/spec/rubyspec/library/net/ftp/shared/pwd.rb new file mode 100644 index 0000000000..951d020f2d --- /dev/null +++ b/spec/rubyspec/library/net/ftp/shared/pwd.rb @@ -0,0 +1,3 @@ +describe :net_ftp_pwd, shared: true do + +end diff --git a/spec/rubyspec/library/net/ftp/site_spec.rb b/spec/rubyspec/library/net/ftp/site_spec.rb new file mode 100644 index 0000000000..d02b94e899 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/site_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#site" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the SITE command with the passed argument to the server" do + @ftp.site("param") + @ftp.last_response.should == "200 Command okay. (SITE param)\n" + end + + it "returns nil" do + @ftp.site("param").should be_nil + end + + it "does not raise an error when the response code is 202" do + @server.should_receive(:site).and_respond("202 Command not implemented, superfluous at this site.") + lambda { @ftp.site("param") }.should_not raise_error + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:site).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.site("param") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:site).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.site("param") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:site).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.site("param") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:site).and_respond("530 Requested action not taken.") + lambda { @ftp.site("param") }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/size_spec.rb b/spec/rubyspec/library/net/ftp/size_spec.rb new file mode 100644 index 0000000000..a3d5db50b4 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/size_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#size" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the SIZE command to the server" do + @ftp.size("test.file") + @ftp.last_response.should == "213 1024\n" + end + + it "returns the size of the passed file as Integer" do + @ftp.size("test.file").should eql(1024) + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:size).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.size("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:size).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.size("test.file") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:size).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.size("test.file") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 550" do + @server.should_receive(:size).and_respond("550 Requested action not taken.") + lambda { @ftp.size("test.file") }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/spec_helper.rb b/spec/rubyspec/library/net/ftp/spec_helper.rb new file mode 100644 index 0000000000..c87d16218b --- /dev/null +++ b/spec/rubyspec/library/net/ftp/spec_helper.rb @@ -0,0 +1,5 @@ +require "net/ftp" + +if defined?(Net::FTP.default_passive) + Net::FTP.default_passive = false +end diff --git a/spec/rubyspec/library/net/ftp/status_spec.rb b/spec/rubyspec/library/net/ftp/status_spec.rb new file mode 100644 index 0000000000..7e9927c3c8 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/status_spec.rb @@ -0,0 +1,63 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#status" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the STAT command to the server" do + @ftp.status + @ftp.last_response.should == "211 System status, or system help reply. (STAT)\n" + end + + it "returns the received information" do + @ftp.status.should == "211 System status, or system help reply. (STAT)\n" + end + + it "does not raise an error when the response code is 212" do + @server.should_receive(:stat).and_respond("212 Directory status.") + lambda { @ftp.status }.should_not raise_error + end + + it "does not raise an error when the response code is 213" do + @server.should_receive(:stat).and_respond("213 File status.") + lambda { @ftp.status }.should_not raise_error + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:stat).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.status }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:stat).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.status }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:stat).and_respond("502 Command not implemented.") + lambda { @ftp.status }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:stat).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.status }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 530" do + @server.should_receive(:stat).and_respond("530 Requested action not taken.") + lambda { @ftp.status }.should raise_error(Net::FTPPermError) + end +end diff --git a/spec/rubyspec/library/net/ftp/storbinary_spec.rb b/spec/rubyspec/library/net/ftp/storbinary_spec.rb new file mode 100644 index 0000000000..b000b9bf6c --- /dev/null +++ b/spec/rubyspec/library/net/ftp/storbinary_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#storbinary" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @local_fixture_file = File.dirname(__FILE__) + "/fixtures/putbinaryfile" + @tmp_file = tmp("binaryfile", false) + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + + rm_r @tmp_file + end + + it "sends the passed command and the passed File object's content to the server" do + File.open(@local_fixture_file) do |f| + f.binmode + + @ftp.storbinary("STOR binary", f, 4096) {} + @ftp.last_response.should == "200 OK, Data received. (STOR binary)\n" + end + end + + it "yields the transmitted content as binary blocks of the passed size" do + File.open(@local_fixture_file) do |f| + f.binmode + + res = [] + @ftp.storbinary("STOR binary", f, 10) { |x| res << x } + res.should == [ + "This is an", " example f", + "ile\nwhich ", "is going t", + "o be trans", "mitted\nusi", + "ng #putbin", "aryfile." + ] + end + end +end diff --git a/spec/rubyspec/library/net/ftp/storlines_spec.rb b/spec/rubyspec/library/net/ftp/storlines_spec.rb new file mode 100644 index 0000000000..9ea4b83603 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/storlines_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#storlines" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @local_fixture_file = File.dirname(__FILE__) + "/fixtures/puttextfile" + @tmp_file = tmp("textfile", false) + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + + rm_r @tmp_file + end + + it "sends the passed command and the passed File object's content to the server" do + File.open(@local_fixture_file) do |f| + @ftp.storlines("STOR text", f) {} + @ftp.last_response.should == "200 OK, Data received. (STOR text)\n" + end + end + + it "yields each line of the transmitted content" do + File.open(@local_fixture_file) do |f| + res = [] + @ftp.storlines("STOR text", f) { |x| res << x } + res.should == [ + "This is an example file\r\n", + "which is going to be transmitted\r\n", + "using #puttextfile.\r\n" + ] + end + end +end diff --git a/spec/rubyspec/library/net/ftp/system_spec.rb b/spec/rubyspec/library/net/ftp/system_spec.rb new file mode 100644 index 0000000000..603dd09152 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/system_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#system" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the SYST command to the server" do + @ftp.system + @ftp.last_response.should =~ /\A215 FTP Dummy Server \(SYST\)\Z/ + end + + it "returns the received information" do + @ftp.system.should =~ /\AFTP Dummy Server \(SYST\)\Z/ + end + + it "raises a Net::FTPPermError when the response code is 500" do + @server.should_receive(:syst).and_respond("500 Syntax error, command unrecognized.") + lambda { @ftp.system }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 501" do + @server.should_receive(:syst).and_respond("501 Syntax error in parameters or arguments.") + lambda { @ftp.system }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPPermError when the response code is 502" do + @server.should_receive(:syst).and_respond("502 Command not implemented.") + lambda { @ftp.system }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPTempError when the response code is 421" do + @server.should_receive(:syst).and_respond("421 Service not available, closing control connection.") + lambda { @ftp.system }.should raise_error(Net::FTPTempError) + end +end diff --git a/spec/rubyspec/library/net/ftp/voidcmd_spec.rb b/spec/rubyspec/library/net/ftp/voidcmd_spec.rb new file mode 100644 index 0000000000..8b3e0c17c9 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/voidcmd_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#voidcmd" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "sends the passed command to the server" do + @server.should_receive(:help).and_respond("2xx Does not raise.") + lambda { @ftp.voidcmd("HELP") }.should_not raise_error + end + + it "returns nil" do + @server.should_receive(:help).and_respond("2xx Does not raise.") + @ftp.voidcmd("HELP").should be_nil + end + + it "raises a Net::FTPReplyError when the response code is 1xx" do + @server.should_receive(:help).and_respond("1xx Does raise a Net::FTPReplyError.") + lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError) + end + + it "raises a Net::FTPReplyError when the response code is 3xx" do + @server.should_receive(:help).and_respond("3xx Does raise a Net::FTPReplyError.") + lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPReplyError) + end + + it "raises a Net::FTPTempError when the response code is 4xx" do + @server.should_receive(:help).and_respond("4xx Does raise a Net::FTPTempError.") + lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPTempError) + end + + it "raises a Net::FTPPermError when the response code is 5xx" do + @server.should_receive(:help).and_respond("5xx Does raise a Net::FTPPermError.") + lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPPermError) + end + + it "raises a Net::FTPProtoError when the response code is not valid" do + @server.should_receive(:help).and_respond("999 Does raise a Net::FTPProtoError.") + lambda { @ftp.voidcmd("HELP") }.should raise_error(Net::FTPProtoError) + end +end diff --git a/spec/rubyspec/library/net/ftp/welcome_spec.rb b/spec/rubyspec/library/net/ftp/welcome_spec.rb new file mode 100644 index 0000000000..5505623ce3 --- /dev/null +++ b/spec/rubyspec/library/net/ftp/welcome_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/server', __FILE__) + +describe "Net::FTP#welcome" do + before :each do + @server = NetFTPSpecs::DummyFTP.new + @server.serve_once + + @ftp = Net::FTP.new + @ftp.connect(@server.hostname, @server.server_port) + end + + after :each do + @ftp.quit rescue nil + @ftp.close + @server.stop + end + + it "returns the server's welcome message" do + @ftp.welcome.should be_nil + @ftp.login + @ftp.welcome.should == "230 User logged in, proceed. (USER anonymous)\n" + end +end diff --git a/spec/rubyspec/library/net/http/HTTPBadResponse_spec.rb b/spec/rubyspec/library/net/http/HTTPBadResponse_spec.rb new file mode 100644 index 0000000000..78bb0c8420 --- /dev/null +++ b/spec/rubyspec/library/net/http/HTTPBadResponse_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPBadResponse" do + it "is a subclass of StandardError" do + Net::HTTPBadResponse.should < StandardError + end +end diff --git a/spec/rubyspec/library/net/http/HTTPError_spec.rb b/spec/rubyspec/library/net/http/HTTPError_spec.rb new file mode 100644 index 0000000000..5fa87f140b --- /dev/null +++ b/spec/rubyspec/library/net/http/HTTPError_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPError" do + it "is a subclass of Net::ProtocolError" do + Net::HTTPError.should < Net::ProtocolError + end + + it "includes the Net::HTTPExceptions module" do + Net::HTTPError.should < Net::HTTPExceptions + end +end diff --git a/spec/rubyspec/library/net/http/HTTPFatalError_spec.rb b/spec/rubyspec/library/net/http/HTTPFatalError_spec.rb new file mode 100644 index 0000000000..72468faf8d --- /dev/null +++ b/spec/rubyspec/library/net/http/HTTPFatalError_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPFatalError" do + it "is a subclass of Net::ProtoFatalError" do + Net::HTTPFatalError.should < Net::ProtoFatalError + end + + it "includes the Net::HTTPExceptions module" do + Net::HTTPFatalError.should < Net::HTTPExceptions + end +end diff --git a/spec/rubyspec/library/net/http/HTTPHeaderSyntaxError_spec.rb b/spec/rubyspec/library/net/http/HTTPHeaderSyntaxError_spec.rb new file mode 100644 index 0000000000..3e7ee24365 --- /dev/null +++ b/spec/rubyspec/library/net/http/HTTPHeaderSyntaxError_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPHeaderSyntaxError" do + it "is a subclass of StandardError" do + Net::HTTPHeaderSyntaxError.should < StandardError + end +end diff --git a/spec/rubyspec/library/net/http/HTTPRetriableError_spec.rb b/spec/rubyspec/library/net/http/HTTPRetriableError_spec.rb new file mode 100644 index 0000000000..71f6bdb196 --- /dev/null +++ b/spec/rubyspec/library/net/http/HTTPRetriableError_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPRetriableError" do + it "is a subclass of Net::ProtoRetriableError" do + Net::HTTPRetriableError.should < Net::ProtoRetriableError + end + + it "includes the Net::HTTPExceptions module" do + Net::HTTPRetriableError.should < Net::HTTPExceptions + end +end diff --git a/spec/rubyspec/library/net/http/HTTPServerException_spec.rb b/spec/rubyspec/library/net/http/HTTPServerException_spec.rb new file mode 100644 index 0000000000..35566ab0c5 --- /dev/null +++ b/spec/rubyspec/library/net/http/HTTPServerException_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPServerException" do + it "is a subclass of Net::ProtoServerError" do + Net::HTTPServerException.should < Net::ProtoServerError + end + + it "includes the Net::HTTPExceptions module" do + Net::HTTPServerException.should < Net::HTTPExceptions + end +end diff --git a/spec/rubyspec/library/net/http/http/Proxy_spec.rb b/spec/rubyspec/library/net/http/http/Proxy_spec.rb new file mode 100644 index 0000000000..2de3fb2d75 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/Proxy_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.Proxy" do + it "returns a new subclass of Net::HTTP" do + Net::HTTP.Proxy("localhost").should < Net::HTTP + end + + it "returns Net::HTTP when the passed address is nil" do + Net::HTTP.Proxy(nil).should == Net::HTTP + end + + it "sets the returned subclasses' proxy options based on the passed arguments" do + http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks") + http_with_proxy.proxy_address.should == "localhost" + http_with_proxy.proxy_port.should eql(1234) + http_with_proxy.proxy_user.should == "rspec" + http_with_proxy.proxy_pass.should == "rocks" + end +end + +describe "Net::HTTP#proxy?" do + describe "when self is no proxy class instance" do + it "returns false" do + Net::HTTP.new("localhost", 3333).proxy?.should be_false + end + end + + describe "when self is a proxy class instance" do + it "returns false" do + http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks") + http_with_proxy.new("localhost", 3333).proxy?.should be_true + end + end +end diff --git a/spec/rubyspec/library/net/http/http/active_spec.rb b/spec/rubyspec/library/net/http/http/active_spec.rb new file mode 100644 index 0000000000..28f1872fa9 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/active_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/started', __FILE__) + +describe "Net::HTTP#active?" do + it_behaves_like :net_http_started_p, :active? +end diff --git a/spec/rubyspec/library/net/http/http/address_spec.rb b/spec/rubyspec/library/net/http/http/address_spec.rb new file mode 100644 index 0000000000..a0fe28fb9e --- /dev/null +++ b/spec/rubyspec/library/net/http/http/address_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP#address" do + it "returns the current host name" do + net = Net::HTTP.new("localhost") + net.address.should == "localhost" + end +end diff --git a/spec/rubyspec/library/net/http/http/close_on_empty_response_spec.rb b/spec/rubyspec/library/net/http/http/close_on_empty_response_spec.rb new file mode 100644 index 0000000000..52bede524e --- /dev/null +++ b/spec/rubyspec/library/net/http/http/close_on_empty_response_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP#close_on_empty_response" do + it "needs to be reviewed for spec completeness" +end + +describe "Net::HTTP#close_on_empty_response=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/net/http/http/copy_spec.rb b/spec/rubyspec/library/net/http/http/copy_spec.rb new file mode 100644 index 0000000000..c3c5e784b1 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/copy_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#copy" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends a COPY request to the passed path and returns the response" do + response = @http.copy("/request") + response.should be_kind_of(Net::HTTPResponse) + response.body.should == "Request type: COPY" + end +end diff --git a/spec/rubyspec/library/net/http/http/default_port_spec.rb b/spec/rubyspec/library/net/http/http/default_port_spec.rb new file mode 100644 index 0000000000..8d9b3ac64a --- /dev/null +++ b/spec/rubyspec/library/net/http/http/default_port_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.default_port" do + it "returns 80" do + Net::HTTP.http_default_port.should eql(80) + end +end diff --git a/spec/rubyspec/library/net/http/http/delete_spec.rb b/spec/rubyspec/library/net/http/http/delete_spec.rb new file mode 100644 index 0000000000..856d3b3af3 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/delete_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#delete" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends a DELETE request to the passed path and returns the response" do + response = @http.delete("/request") + response.should be_kind_of(Net::HTTPResponse) + response.body.should == "Request type: DELETE" + end +end diff --git a/spec/rubyspec/library/net/http/http/finish_spec.rb b/spec/rubyspec/library/net/http/http/finish_spec.rb new file mode 100644 index 0000000000..ccc6878961 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/finish_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#finish" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.new("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when self has been started" do + it "closes the tcp connection" do + @http.start + @http.finish + @http.started?.should be_false + end + end + + describe "when self has not been started yet" do + it "raises an IOError" do + lambda { @http.finish }.should raise_error(IOError) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/fixtures/http_server.rb b/spec/rubyspec/library/net/http/http/fixtures/http_server.rb new file mode 100644 index 0000000000..9c6fe2696f --- /dev/null +++ b/spec/rubyspec/library/net/http/http/fixtures/http_server.rb @@ -0,0 +1,90 @@ +require 'webrick' +require 'webrick/httpservlet/abstract' + +module NetHTTPSpecs + class NullWriter + def <<(s) end + def puts(*args) end + def print(*args) end + def printf(*args) end + end + + class SpecServlet < WEBrick::HTTPServlet::AbstractServlet + def handle(req, res) + reply(req, res) + end + + %w{ do_GET do_HEAD do_POST do_PUT do_PROPPATCH do_LOCK do_UNLOCK + do_OPTIONS do_PROPFIND do_DELETE do_MOVE do_COPY + do_MKCOL do_TRACE }.each do |method| + alias_method method.to_sym, :handle + end + end + + class RequestServlet < SpecServlet + def reply(req, res) + res.content_type = "text/plain" + res.body = "Request type: #{req.request_method}" + end + end + + class RequestBodyServlet < SpecServlet + def reply(req, res) + res.content_type = "text/plain" + res.body = req.body + end + end + + class RequestHeaderServlet < SpecServlet + def reply(req, res) + res.content_type = "text/plain" + res.body = req.header.inspect + end + end + + class << self + @server = nil + @server_thread = nil + + def port + @server ? @server.config[:Port] : 3333 + end + + def start_server + server_config = { + BindAddress: "localhost", + Port: 0, + Logger: WEBrick::Log.new(NullWriter.new), + AccessLog: [], + ServerType: Thread + } + + @server = WEBrick::HTTPServer.new(server_config) + + @server.mount_proc('/') do |req, res| + res.content_type = "text/plain" + res.body = "This is the index page." + end + @server.mount('/request', RequestServlet) + @server.mount("/request/body", RequestBodyServlet) + @server.mount("/request/header", RequestHeaderServlet) + + @server_thread = @server.start + end + + def stop_server + if @server + begin + @server.shutdown + rescue Errno::EPIPE + # Because WEBrick is not thread-safe and only catches IOError + end + end + if @server_thread + @server_thread.join + end + timeout = WEBrick::Utils::TimeoutHandler + timeout.terminate if timeout.respond_to?(:terminate) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/get2_spec.rb b/spec/rubyspec/library/net/http/http/get2_spec.rb new file mode 100644 index 0000000000..2173156adf --- /dev/null +++ b/spec/rubyspec/library/net/http/http/get2_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_get', __FILE__) + +describe "Net::HTTP#get2" do + it_behaves_like :net_ftp_request_get, :get2 +end diff --git a/spec/rubyspec/library/net/http/http/get_print_spec.rb b/spec/rubyspec/library/net/http/http/get_print_spec.rb new file mode 100644 index 0000000000..d91a584aaa --- /dev/null +++ b/spec/rubyspec/library/net/http/http/get_print_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP.get_print" do + before :each do + NetHTTPSpecs.start_server + @port = NetHTTPSpecs.port + end + + after :each do + NetHTTPSpecs.stop_server + end + + describe "when passed URI" do + it "it prints the body of the specified uri to $stdout" do + lambda do + Net::HTTP.get_print URI.parse("http://localhost:#{@port}/") + end.should output(/This is the index page\./) + end + end + + describe "when passed host, path, port" do + it "it prints the body of the specified uri to $stdout" do + lambda do + Net::HTTP.get_print 'localhost', "/", @port + end.should output(/This is the index page\./) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/get_response_spec.rb b/spec/rubyspec/library/net/http/http/get_response_spec.rb new file mode 100644 index 0000000000..fdc1ae1f66 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/get_response_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP.get_response" do + before :each do + NetHTTPSpecs.start_server + @port = NetHTTPSpecs.port + end + + after :each do + NetHTTPSpecs.stop_server + end + + describe "when passed URI" do + it "returns the response for the specified uri" do + res = Net::HTTP.get_response(URI.parse("http://localhost:#{@port}/")) + res.content_type.should == "text/plain" + res.body.should == "This is the index page." + end + end + + describe "when passed host, path, port" do + it "returns the response for the specified host-path-combination" do + res = Net::HTTP.get_response('localhost', "/", @port) + res.content_type.should == "text/plain" + res.body.should == "This is the index page." + end + end +end diff --git a/spec/rubyspec/library/net/http/http/get_spec.rb b/spec/rubyspec/library/net/http/http/get_spec.rb new file mode 100644 index 0000000000..d434191d2d --- /dev/null +++ b/spec/rubyspec/library/net/http/http/get_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP.get when passed URI" do + before :each do + NetHTTPSpecs.start_server + @port = NetHTTPSpecs.port + end + + after :each do + NetHTTPSpecs.stop_server + end + + describe "when passed URI" do + it "returns the body of the specified uri" do + Net::HTTP.get(URI.parse("http://localhost:#{@port}/")).should == "This is the index page." + end + end + + describe "when passed host, path, port" do + it "returns the body of the specified host-path-combination" do + Net::HTTP.get('localhost', "/", @port).should == "This is the index page." + end + end +end diff --git a/spec/rubyspec/library/net/http/http/head2_spec.rb b/spec/rubyspec/library/net/http/http/head2_spec.rb new file mode 100644 index 0000000000..66d533c84d --- /dev/null +++ b/spec/rubyspec/library/net/http/http/head2_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_head', __FILE__) + +describe "Net::HTTP#head2" do + it_behaves_like :net_ftp_request_head, :head2 +end + diff --git a/spec/rubyspec/library/net/http/http/head_spec.rb b/spec/rubyspec/library/net/http/http/head_spec.rb new file mode 100644 index 0000000000..d0d13a6451 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/head_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#head" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends a HEAD request to the passed path and returns the response" do + response = @http.head("/request") + # HEAD requests have no responses + response.body.should be_nil + end + + it "returns a Net::HTTPResponse" do + @http.head("/request").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/http_default_port_spec.rb b/spec/rubyspec/library/net/http/http/http_default_port_spec.rb new file mode 100644 index 0000000000..6b840bbf30 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/http_default_port_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.http_default_port" do + it "returns 80" do + Net::HTTP.http_default_port.should eql(80) + end +end diff --git a/spec/rubyspec/library/net/http/http/https_default_port_spec.rb b/spec/rubyspec/library/net/http/http/https_default_port_spec.rb new file mode 100644 index 0000000000..bd213f2325 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/https_default_port_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.https_default_port" do + it "returns 443" do + Net::HTTP.https_default_port.should eql(443) + end +end diff --git a/spec/rubyspec/library/net/http/http/initialize_spec.rb b/spec/rubyspec/library/net/http/http/initialize_spec.rb new file mode 100644 index 0000000000..0cd59f493d --- /dev/null +++ b/spec/rubyspec/library/net/http/http/initialize_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP#initialize" do + it "is private" do + Net::HTTP.should have_private_instance_method(:initialize) + end + + describe "when passed address" do + before :each do + @net = Net::HTTP.allocate + @net.send(:initialize, "localhost") + end + + it "sets the new Net::HTTP instance's address to the passed address" do + @net.address.should == "localhost" + end + + it "sets the new Net::HTTP instance's port to the default HTTP port" do + @net.port.should eql(Net::HTTP.default_port) + end + + it "does not start the new Net::HTTP instance" do + @net.started?.should be_false + end + end + + describe "when passed address, port" do + before :each do + @net = Net::HTTP.allocate + @net.send(:initialize, "localhost", 3333) + end + + it "sets the new Net::HTTP instance's address to the passed address" do + @net.address.should == "localhost" + end + + it "sets the new Net::HTTP instance's port to the passed port" do + @net.port.should eql(3333) + end + + it "does not start the new Net::HTTP instance" do + @net.started?.should be_false + end + end +end diff --git a/spec/rubyspec/library/net/http/http/inspect_spec.rb b/spec/rubyspec/library/net/http/http/inspect_spec.rb new file mode 100644 index 0000000000..7c5d9bf0eb --- /dev/null +++ b/spec/rubyspec/library/net/http/http/inspect_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#inspect" do + before :each do + NetHTTPSpecs.start_server + @port = NetHTTPSpecs.port + @http = Net::HTTP.new("localhost", @port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "returns a String representation of self" do + @http.inspect.should be_kind_of(String) + @http.inspect.should == "#" + + @http.start + @http.inspect.should == "#" + end +end diff --git a/spec/rubyspec/library/net/http/http/is_version_1_1_spec.rb b/spec/rubyspec/library/net/http/http/is_version_1_1_spec.rb new file mode 100644 index 0000000000..8fa5da6be7 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/is_version_1_1_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../shared/version_1_1', __FILE__) + +describe "Net::HTTP.is_version_1_1?" do + it_behaves_like :net_http_version_1_1_p, :is_version_1_1? +end diff --git a/spec/rubyspec/library/net/http/http/is_version_1_2_spec.rb b/spec/rubyspec/library/net/http/http/is_version_1_2_spec.rb new file mode 100644 index 0000000000..344ac7f7c0 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/is_version_1_2_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../shared/version_1_2', __FILE__) + +describe "Net::HTTP.is_version_1_2?" do + it_behaves_like :net_http_version_1_2_p, :is_version_1_2? +end diff --git a/spec/rubyspec/library/net/http/http/lock_spec.rb b/spec/rubyspec/library/net/http/http/lock_spec.rb new file mode 100644 index 0000000000..e44099f9e5 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/lock_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#lock" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends a LOCK request to the passed path and returns the response" do + response = @http.lock("/request", "test=test") + response.should be_kind_of(Net::HTTPResponse) + response.body.should == "Request type: LOCK" + end +end diff --git a/spec/rubyspec/library/net/http/http/mkcol_spec.rb b/spec/rubyspec/library/net/http/http/mkcol_spec.rb new file mode 100644 index 0000000000..51b0a5b9c0 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/mkcol_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#mkcol" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends a MKCOL request to the passed path and returns the response" do + response = @http.mkcol("/request") + response.should be_kind_of(Net::HTTPResponse) + response.body.should == "Request type: MKCOL" + end +end diff --git a/spec/rubyspec/library/net/http/http/move_spec.rb b/spec/rubyspec/library/net/http/http/move_spec.rb new file mode 100644 index 0000000000..0aa00195f8 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/move_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#head" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends a MOVE request to the passed path and returns the response" do + response = @http.move("/request") + # HEAD requests have no responses + response.body.should == "Request type: MOVE" + end + + it "returns a Net::HTTPResponse" do + @http.move("/request").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/new_spec.rb b/spec/rubyspec/library/net/http/http/new_spec.rb new file mode 100644 index 0000000000..b741eb0a4c --- /dev/null +++ b/spec/rubyspec/library/net/http/http/new_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.new" do + describe "when passed address" do + before :each do + @http = Net::HTTP.new("localhost") + end + + it "returns a Net::HTTP instance" do + @http.proxy?.should be_false + @http.instance_of?(Net::HTTP).should be_true + end + + it "sets the new Net::HTTP instance's address to the passed address" do + @http.address.should == "localhost" + end + + it "sets the new Net::HTTP instance's port to the default HTTP port" do + @http.port.should eql(Net::HTTP.default_port) + end + + it "does not start the new Net::HTTP instance" do + @http.started?.should be_false + end + end + + describe "when passed address, port" do + before :each do + @http = Net::HTTP.new("localhost", 3333) + end + + it "returns a Net::HTTP instance" do + @http.proxy?.should be_false + @http.instance_of?(Net::HTTP).should be_true + end + + it "sets the new Net::HTTP instance's address to the passed address" do + @http.address.should == "localhost" + end + + it "sets the new Net::HTTP instance's port to the passed port" do + @http.port.should eql(3333) + end + + it "does not start the new Net::HTTP instance" do + @http.started?.should be_false + end + end + + describe "when passed address, port, *proxy_options" do + it "returns a Net::HTTP instance" do + http = Net::HTTP.new("localhost", 3333, "localhost") + http.proxy?.should be_true + http.instance_of?(Net::HTTP).should be_true + http.should be_kind_of(Net::HTTP) + end + + it "correctly sets the passed Proxy options" do + http = Net::HTTP.new("localhost", 3333, "localhost") + http.proxy_address.should == "localhost" + http.proxy_port.should eql(80) + http.proxy_user.should be_nil + http.proxy_pass.should be_nil + + http = Net::HTTP.new("localhost", 3333, "localhost", 1234) + http.proxy_address.should == "localhost" + http.proxy_port.should eql(1234) + http.proxy_user.should be_nil + http.proxy_pass.should be_nil + + http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec") + http.proxy_address.should == "localhost" + http.proxy_port.should eql(1234) + http.proxy_user.should == "rubyspec" + http.proxy_pass.should be_nil + + http = Net::HTTP.new("localhost", 3333, "localhost", 1234, "rubyspec", "rocks") + http.proxy_address.should == "localhost" + http.proxy_port.should eql(1234) + http.proxy_user.should == "rubyspec" + http.proxy_pass.should == "rocks" + end + end + +end diff --git a/spec/rubyspec/library/net/http/http/newobj_spec.rb b/spec/rubyspec/library/net/http/http/newobj_spec.rb new file mode 100644 index 0000000000..88d5881725 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/newobj_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.newobj" do + before :each do + @net = Net::HTTP.newobj("localhost") + end + + describe "when passed address" do + it "returns a new Net::HTTP instance" do + @net.should be_kind_of(Net::HTTP) + end + + it "sets the new Net::HTTP instance's address to the passed address" do + @net.address.should == "localhost" + end + + it "sets the new Net::HTTP instance's port to the default HTTP port" do + @net.port.should eql(Net::HTTP.default_port) + end + + it "does not start the new Net::HTTP instance" do + @net.started?.should be_false + end + end + + describe "when passed address, port" do + before :each do + @net = Net::HTTP.newobj("localhost", 3333) + end + + it "returns a new Net::HTTP instance" do + @net.should be_kind_of(Net::HTTP) + end + + it "sets the new Net::HTTP instance's address to the passed address" do + @net.address.should == "localhost" + end + + it "sets the new Net::HTTP instance's port to the passed port" do + @net.port.should eql(3333) + end + + it "does not start the new Net::HTTP instance" do + @net.started?.should be_false + end + end +end diff --git a/spec/rubyspec/library/net/http/http/open_timeout_spec.rb b/spec/rubyspec/library/net/http/http/open_timeout_spec.rb new file mode 100644 index 0000000000..0142ae6c06 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/open_timeout_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP#open_timeout" do + ruby_version_is ""..."2.3" do + it "returns the seconds to wait till the connection is open" do + net = Net::HTTP.new("localhost") + net.open_timeout.should be_nil + net.open_timeout = 10 + net.open_timeout.should eql(10) + end + end + + ruby_version_is "2.3" do + it "returns the seconds to wait till the connection is open" do + net = Net::HTTP.new("localhost") + net.open_timeout.should eql(60) + net.open_timeout = 10 + net.open_timeout.should eql(10) + end + end +end + +describe "Net::HTTP#open_timeout=" do + it "sets the seconds to wait till the connection is open" do + net = Net::HTTP.new("localhost") + net.open_timeout = 10 + net.open_timeout.should eql(10) + end + + it "returns the newly set value" do + net = Net::HTTP.new("localhost") + (net.open_timeout = 10).should eql(10) + end +end diff --git a/spec/rubyspec/library/net/http/http/options_spec.rb b/spec/rubyspec/library/net/http/http/options_spec.rb new file mode 100644 index 0000000000..9c5e810105 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/options_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#options" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends an options request to the passed path and returns the response" do + response = @http.options("/request") + + response.body.should == "Request type: OPTIONS" + end + + it "returns a Net::HTTPResponse" do + @http.options("/request").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/port_spec.rb b/spec/rubyspec/library/net/http/http/port_spec.rb new file mode 100644 index 0000000000..a916f722bf --- /dev/null +++ b/spec/rubyspec/library/net/http/http/port_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP#port" do + it "returns the current port number" do + net = Net::HTTP.new("localhost", 3333) + net.port.should eql(3333) + end +end diff --git a/spec/rubyspec/library/net/http/http/post2_spec.rb b/spec/rubyspec/library/net/http/http/post2_spec.rb new file mode 100644 index 0000000000..298b2277bc --- /dev/null +++ b/spec/rubyspec/library/net/http/http/post2_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_post', __FILE__) + +describe "Net::HTTP#post2" do + it_behaves_like :net_ftp_request_post, :post2 +end diff --git a/spec/rubyspec/library/net/http/http/post_form_spec.rb b/spec/rubyspec/library/net/http/http/post_form_spec.rb new file mode 100644 index 0000000000..4a5d7ec731 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/post_form_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP.post_form when passed URI" do + before :each do + NetHTTPSpecs.start_server + @port = NetHTTPSpecs.port + end + + after :each do + NetHTTPSpecs.stop_server + end + + it "POSTs the passed form data to the given uri" do + uri = URI.parse("http://localhost:#{@port}/request/body") + data = { test: :data } + + res = Net::HTTP.post_form(uri, data) + res.body.should == "test=data" + end +end diff --git a/spec/rubyspec/library/net/http/http/post_spec.rb b/spec/rubyspec/library/net/http/http/post_spec.rb new file mode 100644 index 0000000000..a50663a01d --- /dev/null +++ b/spec/rubyspec/library/net/http/http/post_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#post" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends an post request to the passed path and returns the response" do + response = @http.post("/request", "test=test") + response.body.should == "Request type: POST" + end + + it "returns a Net::HTTPResponse" do + @http.post("/request", "test=test").should be_kind_of(Net::HTTPResponse) + end + + describe "when passed a block" do + it "yields fragments of the response body to the passed block" do + str = "" + @http.post("/request", "test=test") do |res| + str << res + end + str.should == "Request type: POST" + end + + it "returns a Net::HTTPResponse" do + @http.post("/request", "test=test") {}.should be_kind_of(Net::HTTPResponse) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/propfind_spec.rb b/spec/rubyspec/library/net/http/http/propfind_spec.rb new file mode 100644 index 0000000000..c242eeec68 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/propfind_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#propfind" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends an propfind request to the passed path and returns the response" do + response = @http.propfind("/request", "test=test") + response.body.should == "Request type: PROPFIND" + end + + it "returns a Net::HTTPResponse" do + @http.propfind("/request", "test=test").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/proppatch_spec.rb b/spec/rubyspec/library/net/http/http/proppatch_spec.rb new file mode 100644 index 0000000000..8567f60719 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/proppatch_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#proppatch" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends an proppatch request to the passed path and returns the response" do + response = @http.proppatch("/request", "test=test") + response.body.should == "Request type: PROPPATCH" + end + + it "returns a Net::HTTPResponse" do + @http.proppatch("/request", "test=test").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/proxy_address_spec.rb b/spec/rubyspec/library/net/http/http/proxy_address_spec.rb new file mode 100644 index 0000000000..85f8dfedb4 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/proxy_address_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.proxy_address" do + describe "when self is no proxy class" do + it "returns nil" do + Net::HTTP.proxy_address.should be_nil + end + end + + describe "when self is a proxy class" do + it "returns the address for self's proxy connection" do + Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_address.should == "localhost" + end + end +end + +describe "Net::HTTP#proxy_address" do + describe "when self is no proxy class instance" do + it "returns nil" do + Net::HTTP.new("localhost", 3333).proxy_address.should be_nil + end + end + + describe "when self is a proxy class instance" do + it "returns the password for self's proxy connection" do + http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks") + http_with_proxy.new("localhost", 3333).proxy_address.should == "localhost" + end + end +end diff --git a/spec/rubyspec/library/net/http/http/proxy_class_spec.rb b/spec/rubyspec/library/net/http/http/proxy_class_spec.rb new file mode 100644 index 0000000000..30d38a93e7 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/proxy_class_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.proxy_class?" do + it "returns true if sels is a class created with Net::HTTP.Proxy" do + Net::HTTP.proxy_class?.should be_false + Net::HTTP.Proxy("localhost").proxy_class?.should be_true + end +end diff --git a/spec/rubyspec/library/net/http/http/proxy_pass_spec.rb b/spec/rubyspec/library/net/http/http/proxy_pass_spec.rb new file mode 100644 index 0000000000..09db6f2877 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/proxy_pass_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.proxy_pass" do + describe "when self is no proxy class" do + it "returns nil" do + Net::HTTP.proxy_pass.should be_nil + end + end + + describe "when self is a proxy class" do + it "returns nil if no password was set for self's proxy connection" do + Net::HTTP.Proxy("localhost").proxy_pass.should be_nil + end + + it "returns the password for self's proxy connection" do + Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_pass.should == "rocks" + end + end +end + +describe "Net::HTTP#proxy_pass" do + describe "when self is no proxy class instance" do + it "returns nil" do + Net::HTTP.new("localhost", 3333).proxy_pass.should be_nil + end + end + + describe "when self is a proxy class instance" do + it "returns nil if no password was set for self's proxy connection" do + Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_pass.should be_nil + end + + it "returns the password for self's proxy connection" do + http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks") + http_with_proxy.new("localhost", 3333).proxy_pass.should == "rocks" + end + end +end diff --git a/spec/rubyspec/library/net/http/http/proxy_port_spec.rb b/spec/rubyspec/library/net/http/http/proxy_port_spec.rb new file mode 100644 index 0000000000..0655232c6b --- /dev/null +++ b/spec/rubyspec/library/net/http/http/proxy_port_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.proxy_port" do + describe "when self is no proxy class" do + it "returns nil" do + Net::HTTP.proxy_port.should be_nil + end + end + + describe "when self is a proxy class" do + it "returns 80 if no port was set for self's proxy connection" do + Net::HTTP.Proxy("localhost").proxy_port.should eql(80) + end + + it "returns the port for self's proxy connection" do + Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_port.should eql(1234) + end + end +end + +describe "Net::HTTP#proxy_port" do + describe "when self is no proxy class instance" do + it "returns nil" do + Net::HTTP.new("localhost", 3333).proxy_port.should be_nil + end + end + + describe "when self is a proxy class instance" do + it "returns 80 if no port was set for self's proxy connection" do + Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_port.should eql(80) + end + + it "returns the port for self's proxy connection" do + http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks") + http_with_proxy.new("localhost", 3333).proxy_port.should eql(1234) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/proxy_user_spec.rb b/spec/rubyspec/library/net/http/http/proxy_user_spec.rb new file mode 100644 index 0000000000..1beacca642 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/proxy_user_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.proxy_user" do + describe "when self is no proxy class" do + it "returns nil" do + Net::HTTP.proxy_user.should be_nil + end + end + + describe "when self is a proxy class" do + it "returns nil if no username was set for self's proxy connection" do + Net::HTTP.Proxy("localhost").proxy_user.should be_nil + end + + it "returns the username for self's proxy connection" do + Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks").proxy_user.should == "rspec" + end + end +end + +describe "Net::HTTP#proxy_user" do + describe "when self is no proxy class instance" do + it "returns nil" do + Net::HTTP.new("localhost", 3333).proxy_user.should be_nil + end + end + + describe "when self is a proxy class instance" do + it "returns nil if no username was set for self's proxy connection" do + Net::HTTP.Proxy("localhost").new("localhost", 3333).proxy_user.should be_nil + end + + it "returns the username for self's proxy connection" do + http_with_proxy = Net::HTTP.Proxy("localhost", 1234, "rspec", "rocks") + http_with_proxy.new("localhost", 3333).proxy_user.should == "rspec" + end + end +end diff --git a/spec/rubyspec/library/net/http/http/put2_spec.rb b/spec/rubyspec/library/net/http/http/put2_spec.rb new file mode 100644 index 0000000000..a0e832d170 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/put2_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_put', __FILE__) + +describe "Net::HTTP#put2" do + it_behaves_like :net_ftp_request_put, :put2 +end diff --git a/spec/rubyspec/library/net/http/http/put_spec.rb b/spec/rubyspec/library/net/http/http/put_spec.rb new file mode 100644 index 0000000000..ab7e794db0 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/put_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#put" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends an put request to the passed path and returns the response" do + response = @http.put("/request", "test=test") + response.body.should == "Request type: PUT" + end + + it "returns a Net::HTTPResponse" do + @http.put("/request", "test=test").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/read_timeout_spec.rb b/spec/rubyspec/library/net/http/http/read_timeout_spec.rb new file mode 100644 index 0000000000..86f2e0246d --- /dev/null +++ b/spec/rubyspec/library/net/http/http/read_timeout_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP#read_timeout" do + it "returns the seconds to wait until reading one block" do + net = Net::HTTP.new("localhost") + net.read_timeout.should eql(60) + net.read_timeout = 10 + net.read_timeout.should eql(10) + end +end + +describe "Net::HTTP#read_timeout=" do + it "sets the seconds to wait till the connection is open" do + net = Net::HTTP.new("localhost") + net.read_timeout = 10 + net.read_timeout.should eql(10) + end + + it "returns the newly set value" do + net = Net::HTTP.new("localhost") + (net.read_timeout = 10).should eql(10) + end +end diff --git a/spec/rubyspec/library/net/http/http/request_get_spec.rb b/spec/rubyspec/library/net/http/http/request_get_spec.rb new file mode 100644 index 0000000000..33b040c622 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/request_get_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_get', __FILE__) + +describe "Net::HTTP#request_get" do + it_behaves_like :net_ftp_request_get, :get2 +end diff --git a/spec/rubyspec/library/net/http/http/request_head_spec.rb b/spec/rubyspec/library/net/http/http/request_head_spec.rb new file mode 100644 index 0000000000..85ff56dcb5 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/request_head_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_head', __FILE__) + +describe "Net::HTTP#request_head" do + it_behaves_like :net_ftp_request_head, :request_head +end diff --git a/spec/rubyspec/library/net/http/http/request_post_spec.rb b/spec/rubyspec/library/net/http/http/request_post_spec.rb new file mode 100644 index 0000000000..937b4d5d59 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/request_post_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_post', __FILE__) + +describe "Net::HTTP#request_post" do + it_behaves_like :net_ftp_request_post, :request_post +end diff --git a/spec/rubyspec/library/net/http/http/request_put_spec.rb b/spec/rubyspec/library/net/http/http/request_put_spec.rb new file mode 100644 index 0000000000..c3a4b6d538 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/request_put_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/request_put', __FILE__) + +describe "Net::HTTP#request_put" do + it_behaves_like :net_ftp_request_put, :request_put +end diff --git a/spec/rubyspec/library/net/http/http/request_spec.rb b/spec/rubyspec/library/net/http/http/request_spec.rb new file mode 100644 index 0000000000..d1c754df8c --- /dev/null +++ b/spec/rubyspec/library/net/http/http/request_spec.rb @@ -0,0 +1,109 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#request" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed request_object" do + it "makes a HTTP Request based on the passed request_object" do + response = @http.request(Net::HTTP::Get.new("/request"), "test=test") + response.body.should == "Request type: GET" + + response = @http.request(Net::HTTP::Head.new("/request"), "test=test") + response.body.should be_nil + + response = @http.request(Net::HTTP::Post.new("/request"), "test=test") + response.body.should == "Request type: POST" + + response = @http.request(Net::HTTP::Put.new("/request"), "test=test") + response.body.should == "Request type: PUT" + + response = @http.request(Net::HTTP::Proppatch.new("/request"), "test=test") + response.body.should == "Request type: PROPPATCH" + + response = @http.request(Net::HTTP::Lock.new("/request"), "test=test") + response.body.should == "Request type: LOCK" + + response = @http.request(Net::HTTP::Unlock.new("/request"), "test=test") + response.body.should == "Request type: UNLOCK" + + # TODO: Does not work? + #response = @http.request(Net::HTTP::Options.new("/request"), "test=test") + #response.body.should be_nil + + response = @http.request(Net::HTTP::Propfind.new("/request"), "test=test") + response.body.should == "Request type: PROPFIND" + + response = @http.request(Net::HTTP::Delete.new("/request"), "test=test") + response.body.should == "Request type: DELETE" + + response = @http.request(Net::HTTP::Move.new("/request"), "test=test") + response.body.should == "Request type: MOVE" + + response = @http.request(Net::HTTP::Copy.new("/request"), "test=test") + response.body.should == "Request type: COPY" + + response = @http.request(Net::HTTP::Mkcol.new("/request"), "test=test") + response.body.should == "Request type: MKCOL" + + response = @http.request(Net::HTTP::Trace.new("/request"), "test=test") + response.body.should == "Request type: TRACE" + end + end + + describe "when passed request_object and request_body" do + it "sends the passed request_body when making the HTTP Request" do + response = @http.request(Net::HTTP::Get.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Head.new("/request/body"), "test=test") + response.body.should be_nil + + response = @http.request(Net::HTTP::Post.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Put.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Proppatch.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Lock.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Unlock.new("/request/body"), "test=test") + response.body.should == "test=test" + + # TODO: Does not work? + #response = @http.request(Net::HTTP::Options.new("/request/body"), "test=test") + #response.body.should be_nil + + response = @http.request(Net::HTTP::Propfind.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Delete.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Move.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Copy.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Mkcol.new("/request/body"), "test=test") + response.body.should == "test=test" + + response = @http.request(Net::HTTP::Trace.new("/request/body"), "test=test") + response.body.should == "test=test" + end + end +end diff --git a/spec/rubyspec/library/net/http/http/request_types_spec.rb b/spec/rubyspec/library/net/http/http/request_types_spec.rb new file mode 100644 index 0000000000..8855a7db66 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/request_types_spec.rb @@ -0,0 +1,254 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP::Get" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Get.should < Net::HTTPRequest + end + + it "represents the 'GET'-Request-Method" do + Net::HTTP::Get::METHOD.should == "GET" + end + + it "has no Request Body" do + Net::HTTP::Get::REQUEST_HAS_BODY.should be_false + end + + it "has a Respone Body" do + Net::HTTP::Get::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Head" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Head.should < Net::HTTPRequest + end + + it "represents the 'HEAD'-Request-Method" do + Net::HTTP::Head::METHOD.should == "HEAD" + end + + it "has no Request Body" do + Net::HTTP::Head::REQUEST_HAS_BODY.should be_false + end + + it "has no Respone Body" do + Net::HTTP::Head::RESPONSE_HAS_BODY.should be_false + end +end + +describe "Net::HTTP::Post" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Post.should < Net::HTTPRequest + end + + it "represents the 'POST'-Request-Method" do + Net::HTTP::Post::METHOD.should == "POST" + end + + it "has a Request Body" do + Net::HTTP::Post::REQUEST_HAS_BODY.should be_true + end + + it "has a Respone Body" do + Net::HTTP::Post::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Put" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Put.should < Net::HTTPRequest + end + + it "represents the 'PUT'-Request-Method" do + Net::HTTP::Put::METHOD.should == "PUT" + end + + it "has a Request Body" do + Net::HTTP::Put::REQUEST_HAS_BODY.should be_true + end + + it "has a Respone Body" do + Net::HTTP::Put::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Delete" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Delete.should < Net::HTTPRequest + end + + it "represents the 'DELETE'-Request-Method" do + Net::HTTP::Delete::METHOD.should == "DELETE" + end + + it "has no Request Body" do + Net::HTTP::Delete::REQUEST_HAS_BODY.should be_false + end + + it "has a Respone Body" do + Net::HTTP::Delete::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Options" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Options.should < Net::HTTPRequest + end + + it "represents the 'OPTIONS'-Request-Method" do + Net::HTTP::Options::METHOD.should == "OPTIONS" + end + + it "has no Request Body" do + Net::HTTP::Options::REQUEST_HAS_BODY.should be_false + end + + it "has no Respone Body" do + Net::HTTP::Options::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Trace" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Trace.should < Net::HTTPRequest + end + + it "represents the 'TRACE'-Request-Method" do + Net::HTTP::Trace::METHOD.should == "TRACE" + end + + it "has no Request Body" do + Net::HTTP::Trace::REQUEST_HAS_BODY.should be_false + end + + it "has a Respone Body" do + Net::HTTP::Trace::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Propfind" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Propfind.should < Net::HTTPRequest + end + + it "represents the 'PROPFIND'-Request-Method" do + Net::HTTP::Propfind::METHOD.should == "PROPFIND" + end + + it "has a Request Body" do + Net::HTTP::Propfind::REQUEST_HAS_BODY.should be_true + end + + it "has a Respone Body" do + Net::HTTP::Propfind::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Proppatch" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Proppatch.should < Net::HTTPRequest + end + + it "represents the 'PROPPATCH'-Request-Method" do + Net::HTTP::Proppatch::METHOD.should == "PROPPATCH" + end + + it "has a Request Body" do + Net::HTTP::Proppatch::REQUEST_HAS_BODY.should be_true + end + + it "has a Respone Body" do + Net::HTTP::Proppatch::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Mkcol" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Mkcol.should < Net::HTTPRequest + end + + it "represents the 'MKCOL'-Request-Method" do + Net::HTTP::Mkcol::METHOD.should == "MKCOL" + end + + it "has a Request Body" do + Net::HTTP::Mkcol::REQUEST_HAS_BODY.should be_true + end + + it "has a Respone Body" do + Net::HTTP::Mkcol::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Copy" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Copy.should < Net::HTTPRequest + end + + it "represents the 'COPY'-Request-Method" do + Net::HTTP::Copy::METHOD.should == "COPY" + end + + it "has no Request Body" do + Net::HTTP::Copy::REQUEST_HAS_BODY.should be_false + end + + it "has a Respone Body" do + Net::HTTP::Copy::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Move" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Move.should < Net::HTTPRequest + end + + it "represents the 'MOVE'-Request-Method" do + Net::HTTP::Move::METHOD.should == "MOVE" + end + + it "has no Request Body" do + Net::HTTP::Move::REQUEST_HAS_BODY.should be_false + end + + it "has a Respone Body" do + Net::HTTP::Move::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Lock" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Lock.should < Net::HTTPRequest + end + + it "represents the 'LOCK'-Request-Method" do + Net::HTTP::Lock::METHOD.should == "LOCK" + end + + it "has a Request Body" do + Net::HTTP::Lock::REQUEST_HAS_BODY.should be_true + end + + it "has a Respone Body" do + Net::HTTP::Lock::RESPONSE_HAS_BODY.should be_true + end +end + +describe "Net::HTTP::Unlock" do + it "is a subclass of Net::HTTPRequest" do + Net::HTTP::Unlock.should < Net::HTTPRequest + end + + it "represents the 'UNLOCK'-Request-Method" do + Net::HTTP::Unlock::METHOD.should == "UNLOCK" + end + + it "has a Request Body" do + Net::HTTP::Unlock::REQUEST_HAS_BODY.should be_true + end + + it "has a Respone Body" do + Net::HTTP::Unlock::RESPONSE_HAS_BODY.should be_true + end +end diff --git a/spec/rubyspec/library/net/http/http/send_request_spec.rb b/spec/rubyspec/library/net/http/http/send_request_spec.rb new file mode 100644 index 0000000000..8d21b6aa3b --- /dev/null +++ b/spec/rubyspec/library/net/http/http/send_request_spec.rb @@ -0,0 +1,118 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#send_request" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + # TODO: Does only work with GET and POST requests + describe "when passed type, path" do + it "sends a HTTP Request of the passed type to the passed path" do + response = @http.send_request("GET", "/request") + response.body.should == "Request type: GET" + + # response = @http.send_request("HEAD", "/request") + # response.body.should be_nil + + response = @http.send_request("POST", "/request") + response.body.should == "Request type: POST" + + # response = @http.send_request("PUT", "/request") + # response.body.should == "Request type: PUT" + + # response = @http.send_request("DELETE", "/request") + # response.body.should == "Request type: DELETE" + + # response = @http.send_request("PROPGET", "/request") + # response.body.should == "Request type: DELETE" + + # response = @http.send_request("PROPSET", "/request") + # response.body.should == "Request type: DELETE" + + # response = @http.send_request("OPTIONS", "/request") + # response.body.should be_nil + + # response = @http.send_request("LOCK", "/request") + # response.body.should == "Request type: LOCK + + # response = @http.send_request("UNLOCK", "/request") + # response.body.should == "Request type: UNLOCK + end + end + + describe "when passed type, path, body" do + it "sends a HTTP Request with the passed body" do + response = @http.send_request("GET", "/request/body", "test=test") + response.body.should == "test=test" + + # response = @http.send_request("HEAD", "/request/body", "test=test") + # response.body.should be_nil + + response = @http.send_request("POST", "/request/body", "test=test") + response.body.should == "test=test" + + # response = @http.send_request("PUT", "/request/body", "test=test") + # response.body.should == "test=test" + + # response = @http.send_request("DELETE", "/request/body", "test=test") + # response.body.should == "test=test" + + # response = @http.send_request("PROPGET", "/request/body", "test=test") + # response.body.should == "test=test" + + # response = @http.send_request("PROPSET", "/request/body", "test=test") + # response.body.should == "test=test" + + # response = @http.send_request("OPTIONS", "/request/body", "test=test") + # response.body.should be_nil + + # response = @http.send_request("LOCK", "/request/body", "test=test") + # response.body.should == "test=test" + + # response = @http.send_request("UNLOCK", "/request/body", "test=test") + # response.body.should == "test=test" + end + end + + describe "when passed type, path, body, headers" do + it "sends a HTTP Request with the passed headers" do + response = @http.send_request("GET", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + response.body.should include('"referer"=>["http://www.rubyspec.org"]') + + # response = @http.send_request("HEAD", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should be_nil + + response = @http.send_request("POST", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + response.body.should include('"referer"=>["http://www.rubyspec.org"]') + + # response = @http.send_request("PUT", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should include('"referer"=>["http://www.rubyspec.org"]') + + # response = @http.send_request("DELETE", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should include('"referer"=>["http://www.rubyspec.org"]') + + # response = @http.send_request("PROPGET", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should include('"referer"=>["http://www.rubyspec.org"]') + + # response = @http.send_request("PROPSET", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should include('"referer"=>["http://www.rubyspec.org"]') + + # response = @http.send_request("OPTIONS", "/request/body", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should be_nil + + # response = @http.send_request("LOCK", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should include('"referer"=>["http://www.rubyspec.org"]') + + # response = @http.send_request("UNLOCK", "/request/header", "test=test", "referer" => "http://www.rubyspec.org") + # response.body.should include('"referer"=>["http://www.rubyspec.org"]') + end + end +end diff --git a/spec/rubyspec/library/net/http/http/set_debug_output_spec.rb b/spec/rubyspec/library/net/http/http/set_debug_output_spec.rb new file mode 100644 index 0000000000..806f468a84 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/set_debug_output_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require "stringio" +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#set_debug_output when passed io" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.new("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sets the passed io as output stream for debugging" do + io = StringIO.new + + @http.set_debug_output(io) + @http.start + io.string.should_not be_empty + size = io.string.size + + @http.get("/") + io.string.size.should > size + end + + it "outputs a warning when the connection has already been started" do + @http.start + lambda { @http.set_debug_output(StringIO.new) }.should complain("Net::HTTP#set_debug_output called after HTTP started\n") + end +end diff --git a/spec/rubyspec/library/net/http/http/shared/request_get.rb b/spec/rubyspec/library/net/http/http/shared/request_get.rb new file mode 100644 index 0000000000..b0eca665d6 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/shared/request_get.rb @@ -0,0 +1,41 @@ +describe :net_ftp_request_get, shared: true do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a GET request to the passed path and returns the response" do + response = @http.send(@method, "/request") + response.body.should == "Request type: GET" + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request") + response.should be_kind_of(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a GET request to the passed path and returns the response" do + response = @http.send(@method, "/request") {} + response.body.should == "Request type: GET" + end + + it "yields the response to the passed block" do + @http.send(@method, "/request") do |response| + response.body.should == "Request type: GET" + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request") {} + response.should be_kind_of(Net::HTTPResponse) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/shared/request_head.rb b/spec/rubyspec/library/net/http/http/shared/request_head.rb new file mode 100644 index 0000000000..0e669de9ac --- /dev/null +++ b/spec/rubyspec/library/net/http/http/shared/request_head.rb @@ -0,0 +1,41 @@ +describe :net_ftp_request_head, shared: true do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a head request to the passed path and returns the response" do + response = @http.send(@method, "/request") + response.body.should be_nil + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request") + response.should be_kind_of(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a head request to the passed path and returns the response" do + response = @http.send(@method, "/request") {} + response.body.should be_nil + end + + it "yields the response to the passed block" do + @http.send(@method, "/request") do |response| + response.body.should be_nil + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request") {} + response.should be_kind_of(Net::HTTPResponse) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/shared/request_post.rb b/spec/rubyspec/library/net/http/http/shared/request_post.rb new file mode 100644 index 0000000000..06c5e139f9 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/shared/request_post.rb @@ -0,0 +1,41 @@ +describe :net_ftp_request_post, shared: true do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a post request to the passed path and returns the response" do + response = @http.send(@method, "/request", "test=test") + response.body.should == "Request type: POST" + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request", "test=test") + response.should be_kind_of(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a post request to the passed path and returns the response" do + response = @http.send(@method, "/request", "test=test") {} + response.body.should == "Request type: POST" + end + + it "yields the response to the passed block" do + @http.send(@method, "/request", "test=test") do |response| + response.body.should == "Request type: POST" + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request", "test=test") {} + response.should be_kind_of(Net::HTTPResponse) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/shared/request_put.rb b/spec/rubyspec/library/net/http/http/shared/request_put.rb new file mode 100644 index 0000000000..6ae791d7e7 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/shared/request_put.rb @@ -0,0 +1,41 @@ +describe :net_ftp_request_put, shared: true do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + describe "when passed no block" do + it "sends a put request to the passed path and returns the response" do + response = @http.send(@method, "/request", "test=test") + response.body.should == "Request type: PUT" + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request", "test=test") + response.should be_kind_of(Net::HTTPResponse) + end + end + + describe "when passed a block" do + it "sends a put request to the passed path and returns the response" do + response = @http.send(@method, "/request", "test=test") {} + response.body.should == "Request type: PUT" + end + + it "yields the response to the passed block" do + @http.send(@method, "/request", "test=test") do |response| + response.body.should == "Request type: PUT" + end + end + + it "returns a Net::HTTPResponse object" do + response = @http.send(@method, "/request", "test=test") {} + response.should be_kind_of(Net::HTTPResponse) + end + end +end diff --git a/spec/rubyspec/library/net/http/http/shared/started.rb b/spec/rubyspec/library/net/http/http/shared/started.rb new file mode 100644 index 0000000000..9ff6272c31 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/shared/started.rb @@ -0,0 +1,26 @@ +describe :net_http_started_p, shared: true do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.new("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "returns true when self has been started" do + @http.start + @http.send(@method).should be_true + end + + it "returns false when self has not been started yet" do + @http.send(@method).should be_false + end + + it "returns false when self has been stopped again" do + @http.start + @http.finish + @http.send(@method).should be_false + end +end diff --git a/spec/rubyspec/library/net/http/http/shared/version_1_1.rb b/spec/rubyspec/library/net/http/http/shared/version_1_1.rb new file mode 100644 index 0000000000..db3d6a986d --- /dev/null +++ b/spec/rubyspec/library/net/http/http/shared/version_1_1.rb @@ -0,0 +1,6 @@ +describe :net_http_version_1_1_p, shared: true do + it "returns the state of net/http 1.1 features" do + Net::HTTP.version_1_2 + Net::HTTP.send(@method).should be_false + end +end diff --git a/spec/rubyspec/library/net/http/http/shared/version_1_2.rb b/spec/rubyspec/library/net/http/http/shared/version_1_2.rb new file mode 100644 index 0000000000..b044182c60 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/shared/version_1_2.rb @@ -0,0 +1,6 @@ +describe :net_http_version_1_2_p, shared: true do + it "returns the state of net/http 1.2 features" do + Net::HTTP.version_1_2 + Net::HTTP.send(@method).should be_true + end +end diff --git a/spec/rubyspec/library/net/http/http/socket_type_spec.rb b/spec/rubyspec/library/net/http/http/socket_type_spec.rb new file mode 100644 index 0000000000..4ec9b68571 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/socket_type_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP.socket_type" do + it "returns BufferedIO" do + Net::HTTP.socket_type.should == Net::BufferedIO + end +end diff --git a/spec/rubyspec/library/net/http/http/start_spec.rb b/spec/rubyspec/library/net/http/http/start_spec.rb new file mode 100644 index 0000000000..407d57e494 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/start_spec.rb @@ -0,0 +1,111 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP.start" do + before :each do + NetHTTPSpecs.start_server + @port = NetHTTPSpecs.port + end + + after :each do + NetHTTPSpecs.stop_server + end + + describe "when not passed a block" do + before :each do + @http = Net::HTTP.start("localhost", @port) + end + + after :each do + @http.finish if @http.started? + end + + it "returns a new Net::HTTP object for the passed address and port" do + @http.should be_kind_of(Net::HTTP) + @http.address.should == "localhost" + @http.port.should == @port + end + + it "opens the tcp connection" do + @http.started?.should be_true + end + end + + describe "when passed a block" do + it "returns the blocks return value" do + Net::HTTP.start("localhost", @port) { :test }.should == :test + end + + it "yields the new Net::HTTP object to the block" do + yielded = false + Net::HTTP.start("localhost", @port) do |net| + yielded = true + net.should be_kind_of(Net::HTTP) + end + yielded.should be_true + end + + it "opens the tcp connection before yielding" do + Net::HTTP.start("localhost", @port) { |http| http.started?.should be_true } + end + + it "closes the tcp connection after yielding" do + net = nil + Net::HTTP.start("localhost", @port) { |x| net = x } + net.started?.should be_false + end + end +end + +describe "Net::HTTP#start" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.new("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "returns self" do + @http.start.should equal(@http) + end + + it "opens the tcp connection" do + @http.start + @http.started?.should be_true + end + + describe "when self has already been started" do + it "raises an IOError" do + @http.start + lambda { @http.start }.should raise_error(IOError) + end + end + + describe "when passed a block" do + it "returns the blocks return value" do + @http.start { :test }.should == :test + end + + it "yields the new Net::HTTP object to the block" do + yielded = false + @http.start do |http| + yielded = true + http.should equal(@http) + end + yielded.should be_true + end + + it "opens the tcp connection before yielding" do + @http.start { |http| http.started?.should be_true } + end + + it "closes the tcp connection after yielding" do + @http.start { } + @http.started?.should be_false + end + end +end diff --git a/spec/rubyspec/library/net/http/http/started_spec.rb b/spec/rubyspec/library/net/http/http/started_spec.rb new file mode 100644 index 0000000000..01c17c93c1 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/started_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) +require File.expand_path('../shared/started', __FILE__) + +describe "Net::HTTP#started?" do + it_behaves_like :net_http_started_p, :started? +end diff --git a/spec/rubyspec/library/net/http/http/trace_spec.rb b/spec/rubyspec/library/net/http/http/trace_spec.rb new file mode 100644 index 0000000000..6cce15fc09 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/trace_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#trace" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends a TRACE request to the passed path and returns the response" do + response = @http.trace("/request") + response.body.should == "Request type: TRACE" + end + + it "returns a Net::HTTPResponse" do + @http.trace("/request").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/unlock_spec.rb b/spec/rubyspec/library/net/http/http/unlock_spec.rb new file mode 100644 index 0000000000..12df417e1a --- /dev/null +++ b/spec/rubyspec/library/net/http/http/unlock_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/http_server', __FILE__) + +describe "Net::HTTP#unlock" do + before :each do + NetHTTPSpecs.start_server + @http = Net::HTTP.start("localhost", NetHTTPSpecs.port) + end + + after :each do + @http.finish if @http.started? + NetHTTPSpecs.stop_server + end + + it "sends an UNLOCK request to the passed path and returns the response" do + response = @http.unlock("/request", "test=test") + response.body.should == "Request type: UNLOCK" + end + + it "returns a Net::HTTPResponse" do + @http.unlock("/request", "test=test").should be_kind_of(Net::HTTPResponse) + end +end diff --git a/spec/rubyspec/library/net/http/http/use_ssl_spec.rb b/spec/rubyspec/library/net/http/http/use_ssl_spec.rb new file mode 100644 index 0000000000..19d065ed8e --- /dev/null +++ b/spec/rubyspec/library/net/http/http/use_ssl_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTP#use_ssl?" do + it "returns false" do + http = Net::HTTP.new("localhost") + http.use_ssl?.should be_false + end +end diff --git a/spec/rubyspec/library/net/http/http/version_1_1_spec.rb b/spec/rubyspec/library/net/http/http/version_1_1_spec.rb new file mode 100644 index 0000000000..f13ec9b7f1 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/version_1_1_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../shared/version_1_1', __FILE__) + +describe "Net::HTTP.version_1_1?" do + it_behaves_like :net_http_version_1_1_p, :version_1_1? +end diff --git a/spec/rubyspec/library/net/http/http/version_1_2_spec.rb b/spec/rubyspec/library/net/http/http/version_1_2_spec.rb new file mode 100644 index 0000000000..63c2112422 --- /dev/null +++ b/spec/rubyspec/library/net/http/http/version_1_2_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../shared/version_1_2', __FILE__) + +describe "Net::HTTP.version_1_2" do + it "turns on net/http 1.2 features" do + Net::HTTP.version_1_2 + + Net::HTTP.version_1_2?.should be_true + Net::HTTP.version_1_1?.should be_false + end + + it "returns true" do + Net::HTTP.version_1_2.should be_true + end +end + +describe "Net::HTTP.version_1_2?" do + it_behaves_like :net_http_version_1_2_p, :version_1_2? +end diff --git a/spec/rubyspec/library/net/http/httpexceptions/fixtures/classes.rb b/spec/rubyspec/library/net/http/httpexceptions/fixtures/classes.rb new file mode 100644 index 0000000000..abe8855eff --- /dev/null +++ b/spec/rubyspec/library/net/http/httpexceptions/fixtures/classes.rb @@ -0,0 +1,5 @@ +module NetHTTPExceptionsSpecs + class Simple < StandardError + include Net::HTTPExceptions + end +end diff --git a/spec/rubyspec/library/net/http/httpexceptions/initialize_spec.rb b/spec/rubyspec/library/net/http/httpexceptions/initialize_spec.rb new file mode 100644 index 0000000000..8c0c4934f7 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpexceptions/initialize_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPExceptions#initialize when passed message, response" do + before :each do + @exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response") + end + + it "calls super with the passed message" do + @exception.message.should == "error message" + end + + it "sets self's response to the passed response" do + @exception.response.should == "a http response" + end +end diff --git a/spec/rubyspec/library/net/http/httpexceptions/response_spec.rb b/spec/rubyspec/library/net/http/httpexceptions/response_spec.rb new file mode 100644 index 0000000000..becf74bddf --- /dev/null +++ b/spec/rubyspec/library/net/http/httpexceptions/response_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPExceptions#response" do + it "returns self's response" do + exception = NetHTTPExceptionsSpecs::Simple.new("error message", "a http response") + exception.response.should == "a http response" + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/body_exist_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/body_exist_spec.rb new file mode 100644 index 0000000000..cb3565b899 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/body_exist_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPGenericRequest#body_exist?" do + it "returns true when the response is expected to have a body" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.body_exist?.should be_true + + request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path") + request.body_exist?.should be_false + end + + describe "when $VERBOSE is true" do + it "emits a warning" do + request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path") + lambda { + $VERBOSE = true + request.body_exist? + }.should complain(/body_exist\? is obsolete/) + end + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/body_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/body_spec.rb new file mode 100644 index 0000000000..4aa4fff2bc --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/body_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require "stringio" + +describe "Net::HTTPGenericRequest#body" do + it "returns self's request body" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.body.should be_nil + + request.body = "Some Content" + request.body.should == "Some Content" + end +end + +describe "Net::HTTPGenericRequest#body=" do + before :each do + @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + end + + it "sets self's body content to the passed String" do + @request.body = "Some Content" + @request.body.should == "Some Content" + end + + it "sets self's body stream to nil" do + @request.body_stream = StringIO.new("") + @request.body = "Some Content" + @request.body_stream.should be_nil + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/body_stream_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/body_stream_spec.rb new file mode 100644 index 0000000000..df6e02a8c1 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/body_stream_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require "stringio" + +describe "Net::HTTPGenericRequest#body_stream" do + it "returns self's body stream Object" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.body_stream.should be_nil + + stream = StringIO.new("test") + request.body_stream = stream + request.body_stream.should equal(stream) + end +end + +describe "Net::HTTPGenericRequest#body_stream=" do + before :each do + @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + @stream = StringIO.new("test") + end + + it "sets self's body stream to the passed Object" do + @request.body_stream = @stream + @request.body_stream.should equal(@stream) + end + + it "sets self's body to nil" do + @request.body = "Some Content" + @request.body_stream = @stream + @request.body.should be_nil + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/exec_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/exec_spec.rb new file mode 100644 index 0000000000..b3850d6c74 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/exec_spec.rb @@ -0,0 +1,131 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require "stringio" + +describe "Net::HTTPGenericRequest#exec when passed socket, version, path" do + before :each do + @socket = StringIO.new("") + @buffered_socket = Net::BufferedIO.new(@socket) + end + + it "executes the request over the socket to the path using the HTTP version" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + + request.exec(@buffered_socket, "1.1", "/some/path") + str = @socket.string + + str.should =~ %r[POST /some/path HTTP/1.1\r\n] + str.should =~ %r[Accept: \*/\*\r\n] + str[-4..-1].should == "\r\n\r\n" + + request = Net::HTTPGenericRequest.new("GET", true, true, "/some/path", + "Content-Type" => "text/html") + + request.exec(@buffered_socket, "1.0", "/some/other/path") + str = @socket.string + + str.should =~ %r[GET /some/other/path HTTP/1.0\r\n] + str.should =~ %r[Accept: \*/\*\r\n] + str.should =~ %r[Content-Type: text/html\r\n] + str[-4..-1].should == "\r\n\r\n" + end + + describe "when a request body is set" do + it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.body = "Some Content" + + request.exec(@buffered_socket, "1.1", "/some/other/path") + str = @socket.string + + str.should =~ %r[POST /some/other/path HTTP/1.1\r\n] + str.should =~ %r[Accept: \*/\*\r\n] + str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n] + str.should =~ %r[Content-Length: 12\r\n] + str[-16..-1].should == "\r\n\r\nSome Content" + end + + it "correctly sets the 'Content-Length' header and includes the body" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path", + "Content-Type" => "text/html") + request.body = "Some Content" + + request.exec(@buffered_socket, "1.1", "/some/other/path") + str = @socket.string + + str.should =~ %r[POST /some/other/path HTTP/1.1\r\n] + str.should =~ %r[Accept: \*/\*\r\n] + str.should =~ %r[Content-Type: text/html\r\n] + str.should =~ %r[Content-Length: 12\r\n] + str[-16..-1].should == "\r\n\r\nSome Content" + end + end + + describe "when a body stream is set" do + it "sets the 'Content-Type' header to 'application/x-www-form-urlencoded' unless the 'Content-Type' header is supplied" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path", + "Content-Length" => "10") + request.body_stream = StringIO.new("a" * 20) + + request.exec(@buffered_socket, "1.1", "/some/other/path") + str = @socket.string + + str.should =~ %r[POST /some/other/path HTTP/1.1\r\n] + str.should =~ %r[Accept: \*/\*\r\n] + str.should =~ %r[Content-Type: application/x-www-form-urlencoded\r\n] + str.should =~ %r[Content-Length: 10\r\n] + str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa" + end + + it "sends the whole stream, regardless of the 'Content-Length' header" do + request = Net::HTTPGenericRequest.new("POST", true, true,"/some/path", + "Content-Type" => "text/html", + "Content-Length" => "10") + request.body_stream = StringIO.new("a" * 20) + + request.exec(@buffered_socket, "1.1", "/some/other/path") + str = @socket.string + + str.should =~ %r[POST /some/other/path HTTP/1.1\r\n] + str.should =~ %r[Accept: \*/\*\r\n] + str.should =~ %r[Content-Type: text/html\r\n] + str.should =~ %r[Content-Length: 10\r\n] + str[-24..-1].should == "\r\n\r\naaaaaaaaaaaaaaaaaaaa" + end + + it "sends the request in chunks when 'Transfer-Encoding' is set to 'chunked'" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path", + "Content-Type" => "text/html", + "Transfer-Encoding" => "chunked") + datasize = 1024 * 10 + request.body_stream = StringIO.new("a" * datasize) + + request.exec(@buffered_socket, "1.1", "/some/other/path") + str = @socket.string + + str.should =~ %r[POST /some/other/path HTTP/1.1\r\n] + str.should =~ %r[Accept: \*/\*\r\n] + str.should =~ %r[Content-Type: text/html\r\n] + str.should =~ %r[Transfer-Encoding: chunked\r\n] + str =~ %r[\r\n\r\n] + str = $' + while datasize > 0 + chunk_size_line, str = str.split(/\r\n/, 2) + chunk_size = chunk_size_line[/\A[0-9A-Fa-f]+/].to_i(16) + str.slice!(0, chunk_size).should == 'a' * chunk_size + datasize -= chunk_size + str.slice!(0, 2).should == "\r\n" + end + datasize.should == 0 + str.should == %"0\r\n\r\n" + end + + it "raises an ArgumentError when the 'Content-Length' is not set or 'Transfer-Encoding' is not set to 'chunked'" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path", + "Content-Type" => "text/html") + request.body_stream = StringIO.new("Some Content") + + lambda { request.exec(@buffered_socket, "1.1", "/some/other/path") }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/inspect_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/inspect_spec.rb new file mode 100644 index 0000000000..269ce4fe52 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/inspect_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPGenericRequest#inspect" do + it "returns a String representation of self" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.inspect.should == "#" + + request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path") + request.inspect.should == "#" + + request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path") + request.inspect.should == "#" + + # Subclasses + request = Net::HTTP::Get.new("/some/path") + request.inspect.should == "#" + + request = Net::HTTP::Post.new("/some/path") + request.inspect.should == "#" + + request = Net::HTTP::Trace.new("/some/path") + request.inspect.should == "#" + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/method_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/method_spec.rb new file mode 100644 index 0000000000..eef87ff3a7 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/method_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPGenericRequest#method" do + it "returns self's request method" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.method.should == "POST" + + request = Net::HTTPGenericRequest.new("GET", false, true, "/some/path") + request.method.should == "GET" + + request = Net::HTTPGenericRequest.new("BLA", true, true, "/some/path") + request.method.should == "BLA" + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/path_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/path_spec.rb new file mode 100644 index 0000000000..e48d37b0e9 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/path_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPGenericRequest#path" do + it "returns self's request path" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.path.should == "/some/path" + + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/other/path") + request.path.should == "/some/other/path" + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/request_body_permitted_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/request_body_permitted_spec.rb new file mode 100644 index 0000000000..1e0b3ab028 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/request_body_permitted_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPGenericRequest#request_body_permitted?" do + it "returns true when the request is expected to have a body" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.request_body_permitted?.should be_true + + request = Net::HTTPGenericRequest.new("POST", false, true, "/some/path") + request.request_body_permitted?.should be_false + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/response_body_permitted_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/response_body_permitted_spec.rb new file mode 100644 index 0000000000..ee5a43e637 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/response_body_permitted_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPGenericRequest#response_body_permitted?" do + it "returns true when the response is expected to have a body" do + request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + request.response_body_permitted?.should be_true + + request = Net::HTTPGenericRequest.new("POST", true, false, "/some/path") + request.response_body_permitted?.should be_false + end +end diff --git a/spec/rubyspec/library/net/http/httpgenericrequest/set_body_internal_spec.rb b/spec/rubyspec/library/net/http/httpgenericrequest/set_body_internal_spec.rb new file mode 100644 index 0000000000..bff8646d5a --- /dev/null +++ b/spec/rubyspec/library/net/http/httpgenericrequest/set_body_internal_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPGenericRequest#set_body_internal when passed string" do + before :each do + @request = Net::HTTPGenericRequest.new("POST", true, true, "/some/path") + end + + it "sets self's body to the passed string" do + @request.set_body_internal("Some Content") + @request.body.should == "Some Content" + end + + it "raises an ArgumentError when the body or body_stream of self have already been set" do + @request.body = "Some Content" + lambda { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError) + + @request.body_stream = "Some Content" + lambda { @request.set_body_internal("Some other Content") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/add_field_spec.rb b/spec/rubyspec/library/net/http/httpheader/add_field_spec.rb new file mode 100644 index 0000000000..b736a92694 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/add_field_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#add_field when passed key, value" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "adds the passed value to the header entry with the passed key" do + @headers.add_field("My-Header", "a") + @headers.get_fields("My-Header").should == ["a"] + + @headers.add_field("My-Header", "b") + @headers.get_fields("My-Header").should == ["a", "b"] + + @headers.add_field("My-Header", "c") + @headers.get_fields("My-Header").should == ["a", "b", "c"] + end + + it "is case-insensitive" do + @headers.add_field("My-Header", "a") + @headers.get_fields("My-Header").should == ["a"] + + @headers.add_field("my-header", "b") + @headers.get_fields("My-Header").should == ["a", "b"] + + @headers.add_field("MY-HEADER", "c") + @headers.get_fields("My-Header").should == ["a", "b", "c"] + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/basic_auth_spec.rb b/spec/rubyspec/library/net/http/httpheader/basic_auth_spec.rb new file mode 100644 index 0000000000..b9ed792d46 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/basic_auth_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#basic_auth when passed account, password" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "sets the 'Authorization' Header entry for basic authorization" do + @headers.basic_auth("rubyspec", "rocks") + @headers["Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M=" + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/canonical_each_spec.rb b/spec/rubyspec/library/net/http/httpheader/canonical_each_spec.rb new file mode 100644 index 0000000000..a891c12d80 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/canonical_each_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_capitalized', __FILE__) + +describe "Net::HTTPHeader#canonical_each" do + it_behaves_like :net_httpheader_each_capitalized, :canonical_each +end diff --git a/spec/rubyspec/library/net/http/httpheader/chunked_spec.rb b/spec/rubyspec/library/net/http/httpheader/chunked_spec.rb new file mode 100644 index 0000000000..2b08baf34c --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/chunked_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#chunked?" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns true if the 'Transfer-Encoding' header entry is set to chunked" do + @headers.chunked?.should be_false + + @headers["Transfer-Encoding"] = "bla" + @headers.chunked?.should be_false + + @headers["Transfer-Encoding"] = "blachunkedbla" + @headers.chunked?.should be_false + + @headers["Transfer-Encoding"] = "chunked" + @headers.chunked?.should be_true + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/content_length_spec.rb b/spec/rubyspec/library/net/http/httpheader/content_length_spec.rb new file mode 100644 index 0000000000..009eafde85 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/content_length_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#content_length" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns nil if no 'Content-Length' header entry is set" do + @headers.content_length.should be_nil + end + + it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Length' header entry has an invalid format" do + @headers["Content-Length"] = "invalid" + lambda { @headers.content_length }.should raise_error(Net::HTTPHeaderSyntaxError) + end + + it "returns the value of the 'Content-Length' header entry as an Integer" do + @headers["Content-Length"] = "123" + @headers.content_length.should eql(123) + + @headers["Content-Length"] = "123valid" + @headers.content_length.should eql(123) + + @headers["Content-Length"] = "valid123" + @headers.content_length.should eql(123) + end +end + +describe "Net::HTTPHeader#content_length=" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "removes the 'Content-Length' entry if passed false or nil" do + @headers["Content-Length"] = "123" + @headers.content_length = nil + @headers["Content-Lenght"].should be_nil + end + + it "sets the 'Content-Length' entry to the passed value" do + @headers.content_length = "123" + @headers["Content-Length"].should == "123" + + @headers.content_length = "123valid" + @headers["Content-Length"].should == "123" + end + + it "sets the 'Content-Length' entry to 0 if the passed value is not valid" do + @headers.content_length = "invalid123" + @headers["Content-Length"].should == "0" + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/content_range_spec.rb b/spec/rubyspec/library/net/http/httpheader/content_range_spec.rb new file mode 100644 index 0000000000..dc601fb365 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/content_range_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#content_range" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns a Range object that represents the 'Content-Range' header entry" do + @headers["Content-Range"] = "bytes 0-499/1234" + @headers.content_range.should == (0..499) + + @headers["Content-Range"] = "bytes 500-1233/1234" + @headers.content_range.should == (500..1233) + end + + it "returns nil when there is no 'Content-Range' header entry" do + @headers.content_range.should be_nil + end + + it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do + @headers["Content-Range"] = "invalid" + lambda { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError) + + @headers["Content-Range"] = "bytes 123-abc" + lambda { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError) + + @headers["Content-Range"] = "bytes abc-123" + lambda { @headers.content_range }.should raise_error(Net::HTTPHeaderSyntaxError) + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/content_type_spec.rb b/spec/rubyspec/library/net/http/httpheader/content_type_spec.rb new file mode 100644 index 0000000000..7c3dfba7e7 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/content_type_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_content_type', __FILE__) + +describe "Net::HTTPHeader#content_type" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns the content type string, as per 'Content-Type' header entry" do + @headers["Content-Type"] = "text/html" + @headers.content_type.should == "text/html" + + @headers["Content-Type"] = "text/html;charset=utf-8" + @headers.content_type.should == "text/html" + end + + it "returns nil if the 'Content-Type' header entry does not exist" do + @headers.content_type.should be_nil + end +end + +describe "Net::HTTPHeader#content_type=" do + it_behaves_like :net_httpheader_set_content_type, :content_type= +end diff --git a/spec/rubyspec/library/net/http/httpheader/delete_spec.rb b/spec/rubyspec/library/net/http/httpheader/delete_spec.rb new file mode 100644 index 0000000000..9adbfd7813 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/delete_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#delete when passed key" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "removes the header entry with the passed key" do + @headers["My-Header"] = "test" + @headers.delete("My-Header") + + @headers["My-Header"].should be_nil + @headers.size.should eql(0) + end + + it "returns the removed values" do + @headers["My-Header"] = "test" + @headers.delete("My-Header").should == ["test"] + end + + it "is case-insensitive" do + @headers["My-Header"] = "test" + @headers.delete("my-header") + + @headers["My-Header"].should be_nil + @headers.size.should eql(0) + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/each_capitalized_name_spec.rb b/spec/rubyspec/library/net/http/httpheader/each_capitalized_name_spec.rb new file mode 100644 index 0000000000..7adba9dec5 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/each_capitalized_name_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#each_capitalized_name" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["My-Header"] = "test" + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + end + + describe "when passed a block" do + it "yields each header key to the passed block (keys capitalized)" do + res = [] + @headers.each_capitalized_name do |key| + res << key + end + res.sort.should == ["My-Header", "My-Other-Header"] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.each_capitalized_name + enumerator.should be_an_instance_of(Enumerator) + + res = [] + enumerator.each do |key| + res << key + end + res.sort.should == ["My-Header", "My-Other-Header"] + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/each_capitalized_spec.rb b/spec/rubyspec/library/net/http/httpheader/each_capitalized_spec.rb new file mode 100644 index 0000000000..1dcf18a9d1 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/each_capitalized_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_capitalized', __FILE__) + +describe "Net::HTTPHeader#each_capitalized" do + it_behaves_like :net_httpheader_each_capitalized, :each_capitalized +end + diff --git a/spec/rubyspec/library/net/http/httpheader/each_header_spec.rb b/spec/rubyspec/library/net/http/httpheader/each_header_spec.rb new file mode 100644 index 0000000000..00d5894282 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/each_header_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_header', __FILE__) + +describe "Net::HTTPHeader#each_header" do + it_behaves_like :net_httpheader_each_header, :each_header +end diff --git a/spec/rubyspec/library/net/http/httpheader/each_key_spec.rb b/spec/rubyspec/library/net/http/httpheader/each_key_spec.rb new file mode 100644 index 0000000000..5c81830be6 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/each_key_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_name', __FILE__) + +describe "Net::HTTPHeader#each_key" do + it_behaves_like :net_httpheader_each_name, :each_key +end diff --git a/spec/rubyspec/library/net/http/httpheader/each_name_spec.rb b/spec/rubyspec/library/net/http/httpheader/each_name_spec.rb new file mode 100644 index 0000000000..e785ae249a --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/each_name_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_name', __FILE__) + +describe "Net::HTTPHeader#each_name" do + it_behaves_like :net_httpheader_each_name, :each_name +end diff --git a/spec/rubyspec/library/net/http/httpheader/each_spec.rb b/spec/rubyspec/library/net/http/httpheader/each_spec.rb new file mode 100644 index 0000000000..e8fb6713c3 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/each_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each_header', __FILE__) + +describe "Net::HTTPHeader#each" do + it_behaves_like :net_httpheader_each_header, :each +end diff --git a/spec/rubyspec/library/net/http/httpheader/each_value_spec.rb b/spec/rubyspec/library/net/http/httpheader/each_value_spec.rb new file mode 100644 index 0000000000..1adb723a53 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/each_value_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#each_value" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["My-Header"] = "test" + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + end + + describe "when passed a block" do + it "yields each header entry's joined values" do + res = [] + @headers.each_value do |value| + res << value + end + res.sort.should == ["a, b", "test"] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.each_value + enumerator.should be_an_instance_of(Enumerator) + + res = [] + enumerator.each do |key| + res << key + end + res.sort.should == ["a, b", "test"] + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/element_reference_spec.rb b/spec/rubyspec/library/net/http/httpheader/element_reference_spec.rb new file mode 100644 index 0000000000..bfe6b48ad4 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/element_reference_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#[] when passed key" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns the value of the header entry with the passed key" do + @headers["My-Header"] = "test" + @headers["My-Header"].should == "test" + @headers["My-Other-Header"] = "another test" + @headers["My-Other-Header"].should == "another test" + end + + it "is case-insensitive" do + @headers["My-Header"] = "test" + + @headers['My-Header'].should == "test" + @headers['my-Header'].should == "test" + @headers['My-header'].should == "test" + @headers['my-header'].should == "test" + @headers['MY-HEADER'].should == "test" + end + + it "returns multi-element values joined together" do + @headers["My-Header"] = "test" + @headers.add_field("My-Header", "another test") + @headers.add_field("My-Header", "and one more") + + @headers["My-Header"].should == "test, another test, and one more" + end + + it "returns nil for non-existing entries" do + @headers["My-Header"].should be_nil + @headers["My-Other-Header"].should be_nil + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/element_set_spec.rb b/spec/rubyspec/library/net/http/httpheader/element_set_spec.rb new file mode 100644 index 0000000000..a5a2900e94 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/element_set_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#[]= when passed key, value" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "sets the header entry with the passed key to the passed value" do + @headers["My-Header"] = "test" + @headers["My-Header"].should == "test" + + @headers["My-Header"] = "overwritten" + @headers["My-Header"].should == "overwritten" + + @headers["My-Other-Header"] = "another test" + @headers["My-Other-Header"].should == "another test" + end + + it "is case-insensitive" do + @headers['My-Header'] = "test" + @headers['my-Header'] = "another test" + @headers['My-header'] = "and one more test" + @headers['my-header'] = "and another one" + @headers['MY-HEADER'] = "last one" + + @headers["My-Header"].should == "last one" + @headers.size.should eql(1) + end + + it "removes the header entry with the passed key when the value is false or nil" do + @headers['My-Header'] = "test" + @headers['My-Header'] = nil + @headers['My-Header'].should be_nil + + @headers['My-Header'] = "test" + @headers['My-Header'] = false + @headers['My-Header'].should be_nil + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/fetch_spec.rb b/spec/rubyspec/library/net/http/httpheader/fetch_spec.rb new file mode 100644 index 0000000000..7182c4396e --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/fetch_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#fetch" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + describe "when passed key" do + it "returns the header entry for the passed key" do + @headers["My-Header"] = "test" + @headers.fetch("My-Header").should == "test" + + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + @headers.add_field("My-Other-Header", "c") + @headers.fetch("My-Other-Header").should == "a, b, c" + end + + it "is case-insensitive" do + @headers["My-Header"] = "test" + @headers.fetch("my-header").should == "test" + @headers.fetch("MY-HEADER").should == "test" + end + + it "returns nil when there is no entry for the passed key" do + lambda { @headers.fetch("my-header") }.should raise_error(IndexError) + end + end + + describe "when passed key, default" do + it "returns the header entry for the passed key" do + @headers["My-Header"] = "test" + @headers.fetch("My-Header", "bla").should == "test" + + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + @headers.add_field("My-Other-Header", "c") + @headers.fetch("My-Other-Header", "bla").should == "a, b, c" + end + + # TODO: This raises a NoMethodError: undefined method `join' for "bla":String + it "returns the default value when there is no entry for the passed key" do + @headers.fetch("My-Header", "bla").should == "bla" + end + end + + describe "when passed key and block" do + it "returns the header entry for the passed key" do + @headers["My-Header"] = "test" + @headers.fetch("My-Header") {}.should == "test" + + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + @headers.add_field("My-Other-Header", "c") + -> { + @result = @headers.fetch("My-Other-Header", "bla") {} + }.should complain(/block supersedes default value argument/) + @result.should == "a, b, c" + end + + # TODO: This raises a NoMethodError: undefined method `join' for "redaeh-ym":String + it "yieldsand returns the block's return value when there is no entry for the passed key" do + @headers.fetch("My-Header") { |key| key.reverse }.should == "redaeh-ym" + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/fixtures/classes.rb b/spec/rubyspec/library/net/http/httpheader/fixtures/classes.rb new file mode 100644 index 0000000000..b5ec6abd75 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/fixtures/classes.rb @@ -0,0 +1,11 @@ +module NetHTTPHeaderSpecs + class Example + include Net::HTTPHeader + + attr_accessor :body + + def initialize + initialize_http_header({}) + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/form_data_spec.rb b/spec/rubyspec/library/net/http/httpheader/form_data_spec.rb new file mode 100644 index 0000000000..7781990824 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/form_data_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_form_data', __FILE__) + +describe "Net::HTTPHeader#form_data=" do + it_behaves_like :net_httpheader_set_form_data, :form_data= +end diff --git a/spec/rubyspec/library/net/http/httpheader/get_fields_spec.rb b/spec/rubyspec/library/net/http/httpheader/get_fields_spec.rb new file mode 100644 index 0000000000..f7828f752c --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/get_fields_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#get_fields when passed key" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns an Array containing the values of the header entry with the passed key" do + @headers["My-Header"] = "a" + @headers.get_fields("My-Header").should == ["a"] + + @headers.add_field("My-Header", "b") + @headers.get_fields("My-Header").should == ["a", "b"] + end + + it "returns a copy of the header entry values" do + @headers["My-Header"] = "a" + + @headers.get_fields("My-Header").clear + @headers.get_fields("My-Header").should == ["a"] + + @headers.get_fields("My-Header") << "b" + @headers.get_fields("My-Header").should == ["a"] + end + + it "returns nil for non-existing header entries" do + @headers.get_fields("My-Header").should be_nil + @headers.get_fields("My-Other-header").should be_nil + end + + it "is case-insensitive" do + @headers["My-Header"] = "test" + @headers.get_fields("My-Header").should == ["test"] + @headers.get_fields("my-header").should == ["test"] + @headers.get_fields("MY-HEADER").should == ["test"] + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/initialize_http_header_spec.rb b/spec/rubyspec/library/net/http/httpheader/initialize_http_header_spec.rb new file mode 100644 index 0000000000..ffb56642d4 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/initialize_http_header_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#initialize_http_header when passed Hash" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.allocate + end + + it "initializes the HTTP Header using the passed Hash" do + @headers.initialize_http_header("My-Header" => "test", "My-Other-Header" => "another test") + @headers["My-Header"].should == "test" + @headers["My-Other-Header"].should == "another test" + end + + it "complains about duplicate keys when in verbose mode" do + lambda do + $VERBOSE = true + @headers.initialize_http_header("My-Header" => "test", "my-header" => "another test") + end.should complain(/duplicated HTTP header/) + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/key_spec.rb b/spec/rubyspec/library/net/http/httpheader/key_spec.rb new file mode 100644 index 0000000000..0cb5f711dc --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/key_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#key? when passed key" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns true if the header entry with the passed key exists" do + @headers.key?("My-Header").should be_false + @headers["My-Header"] = "test" + @headers.key?("My-Header").should be_true + end + + it "is case-insensitive" do + @headers["My-Header"] = "test" + @headers.key?("my-header").should be_true + @headers.key?("MY-HEADER").should be_true + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/length_spec.rb b/spec/rubyspec/library/net/http/httpheader/length_spec.rb new file mode 100644 index 0000000000..50cb1c1f7f --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/length_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/size', __FILE__) + +describe "Net::HTTPHeader#length" do + it_behaves_like :net_httpheader_size, :length +end diff --git a/spec/rubyspec/library/net/http/httpheader/main_type_spec.rb b/spec/rubyspec/library/net/http/httpheader/main_type_spec.rb new file mode 100644 index 0000000000..34e2ddbc65 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/main_type_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#main_type" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns the 'main-content-type', as per 'Content-Type' header entry" do + @headers["Content-Type"] = "text/html" + @headers.main_type.should == "text" + + @headers["Content-Type"] = "application/pdf" + @headers.main_type.should == "application" + + @headers["Content-Type"] = "text/html;charset=utf-8" + @headers.main_type.should == "text" + end + + it "returns nil if the 'Content-Type' header entry does not exist" do + @headers.main_type.should be_nil + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/proxy_basic_auth_spec.rb b/spec/rubyspec/library/net/http/httpheader/proxy_basic_auth_spec.rb new file mode 100644 index 0000000000..5e7d87af36 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/proxy_basic_auth_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#proxy_basic_auth when passed account, password" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "sets the 'Proxy-Authorization' Header entry for basic authorization" do + @headers.proxy_basic_auth("rubyspec", "rocks") + @headers["Proxy-Authorization"].should == "Basic cnVieXNwZWM6cm9ja3M=" + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/range_length_spec.rb b/spec/rubyspec/library/net/http/httpheader/range_length_spec.rb new file mode 100644 index 0000000000..ec3b00d805 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/range_length_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#range_length" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns the length of the Range represented by the 'Content-Range' header entry" do + @headers["Content-Range"] = "bytes 0-499/1234" + @headers.range_length.should eql(500) + + @headers["Content-Range"] = "bytes 500-1233/1234" + @headers.range_length.should eql(734) + end + + it "returns nil when there is no 'Content-Range' header entry" do + @headers.range_length.should be_nil + end + + it "raises a Net::HTTPHeaderSyntaxError when the 'Content-Range' has an invalid format" do + @headers["Content-Range"] = "invalid" + lambda { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError) + + @headers["Content-Range"] = "bytes 123-abc" + lambda { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError) + + @headers["Content-Range"] = "bytes abc-123" + lambda { @headers.range_length }.should raise_error(Net::HTTPHeaderSyntaxError) + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/range_spec.rb b/spec/rubyspec/library/net/http/httpheader/range_spec.rb new file mode 100644 index 0000000000..d71d2ed598 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/range_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_range', __FILE__) + +describe "Net::HTTPHeader#range" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns a Range object that represents the 'Range' header entry" do + @headers["Range"] = "bytes=0-499" + @headers.range.should == [0..499] + + @headers["Range"] = "bytes=500-1233" + @headers.range.should == [500..1233] + + @headers["Range"] = "bytes=10-" + @headers.range.should == [10..-1] + + @headers["Range"] = "bytes=-10" + @headers.range.should == [-10..-1] + end + + it "returns nil when there is no 'Range' header entry" do + @headers.range.should be_nil + end + + it "raises a Net::HTTPHeaderSyntaxError when the 'Range' has an invalid format" do + @headers["Range"] = "invalid" + lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError) + + @headers["Range"] = "bytes 123-abc" + lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError) + + @headers["Range"] = "bytes abc-123" + lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when the 'Range' was not specified" do + @headers["Range"] = "bytes=-" + lambda { @headers.range }.should raise_error(Net::HTTPHeaderSyntaxError) + end +end + +describe "Net::HTTPHeader#range=" do + it_behaves_like :net_httpheader_set_range, :range= +end diff --git a/spec/rubyspec/library/net/http/httpheader/set_content_type_spec.rb b/spec/rubyspec/library/net/http/httpheader/set_content_type_spec.rb new file mode 100644 index 0000000000..b9a62154ad --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/set_content_type_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_content_type', __FILE__) + +describe "Net::HTTPHeader#set_content_type" do + it_behaves_like :net_httpheader_set_content_type, :set_content_type +end diff --git a/spec/rubyspec/library/net/http/httpheader/set_form_data_spec.rb b/spec/rubyspec/library/net/http/httpheader/set_form_data_spec.rb new file mode 100644 index 0000000000..e675437ba4 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/set_form_data_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_form_data', __FILE__) + +describe "Net::HTTPHeader#set_form_data" do + it_behaves_like :net_httpheader_set_form_data, :set_form_data +end diff --git a/spec/rubyspec/library/net/http/httpheader/set_range_spec.rb b/spec/rubyspec/library/net/http/httpheader/set_range_spec.rb new file mode 100644 index 0000000000..af3f00f99c --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/set_range_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/set_range', __FILE__) + +describe "Net::HTTPHeader#set_range" do + it_behaves_like :net_httpheader_set_range, :set_range +end diff --git a/spec/rubyspec/library/net/http/httpheader/shared/each_capitalized.rb b/spec/rubyspec/library/net/http/httpheader/shared/each_capitalized.rb new file mode 100644 index 0000000000..3bac409876 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/shared/each_capitalized.rb @@ -0,0 +1,31 @@ +describe :net_httpheader_each_capitalized, shared: true do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["my-header"] = "test" + @headers.add_field("my-Other-Header", "a") + @headers.add_field("My-Other-header", "b") + end + + describe "when passed a block" do + it "yields each header entry to the passed block (capitalized keys, values joined)" do + res = [] + @headers.send(@method) do |key, value| + res << [key, value] + end + res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.send(@method) + enumerator.should be_an_instance_of(Enumerator) + + res = [] + enumerator.each do |*key| + res << key + end + res.sort.should == [["My-Header", "test"], ["My-Other-Header", "a, b"]] + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/shared/each_header.rb b/spec/rubyspec/library/net/http/httpheader/shared/each_header.rb new file mode 100644 index 0000000000..6bf3a6ddfe --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/shared/each_header.rb @@ -0,0 +1,31 @@ +describe :net_httpheader_each_header, shared: true do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["My-Header"] = "test" + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + end + + describe "when passed a block" do + it "yields each header entry to the passed block (keys in lower case, values joined)" do + res = [] + @headers.send(@method) do |key, value| + res << [key, value] + end + res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.send(@method) + enumerator.should be_an_instance_of(Enumerator) + + res = [] + enumerator.each do |*key| + res << key + end + res.sort.should == [["my-header", "test"], ["my-other-header", "a, b"]] + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/shared/each_name.rb b/spec/rubyspec/library/net/http/httpheader/shared/each_name.rb new file mode 100644 index 0000000000..efc6a09dfd --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/shared/each_name.rb @@ -0,0 +1,31 @@ +describe :net_httpheader_each_name, shared: true do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + @headers["My-Header"] = "test" + @headers.add_field("My-Other-Header", "a") + @headers.add_field("My-Other-Header", "b") + end + + describe "when passed a block" do + it "yields each header key to the passed block (keys in lower case)" do + res = [] + @headers.send(@method) do |key| + res << key + end + res.sort.should == ["my-header", "my-other-header"] + end + end + + describe "when passed no block" do + it "returns an Enumerator" do + enumerator = @headers.send(@method) + enumerator.should be_an_instance_of(Enumerator) + + res = [] + enumerator.each do |key| + res << key + end + res.sort.should == ["my-header", "my-other-header"] + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/shared/set_content_type.rb b/spec/rubyspec/library/net/http/httpheader/shared/set_content_type.rb new file mode 100644 index 0000000000..b7359bdca6 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/shared/set_content_type.rb @@ -0,0 +1,18 @@ +describe :net_httpheader_set_content_type, shared: true do + describe "when passed type, params" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "sets the 'Content-Type' header entry based on the passed type and params" do + @headers.send(@method, "text/html") + @headers["Content-Type"].should == "text/html" + + @headers.send(@method, "text/html", "charset" => "utf-8") + @headers["Content-Type"].should == "text/html; charset=utf-8" + + @headers.send(@method, "text/html", "charset" => "utf-8", "rubyspec" => "rocks") + @headers["Content-Type"].split(/; /).sort.should == %w[charset=utf-8 rubyspec=rocks text/html] + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/shared/set_form_data.rb b/spec/rubyspec/library/net/http/httpheader/shared/set_form_data.rb new file mode 100644 index 0000000000..db20b18803 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/shared/set_form_data.rb @@ -0,0 +1,27 @@ +describe :net_httpheader_set_form_data, shared: true do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + describe "when passed params" do + it "automatically set the 'Content-Type' to 'application/x-www-form-urlencoded'" do + @headers.send(@method, "cmd" => "search", "q" => "ruby", "max" => "50") + @headers["Content-Type"].should == "application/x-www-form-urlencoded" + end + + it "sets self's body based on the passed form parameters" do + @headers.send(@method, "cmd" => "search", "q" => "ruby", "max" => "50") + @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"] + end + end + + describe "when passed params, separator" do + it "sets self's body based on the passed form parameters and the passed separator" do + @headers.send(@method, {"cmd" => "search", "q" => "ruby", "max" => "50"}, "&") + @headers.body.split("&").sort.should == ["cmd=search", "max=50", "q=ruby"] + + @headers.send(@method, {"cmd" => "search", "q" => "ruby", "max" => "50"}, ";") + @headers.body.split(";").sort.should == ["cmd=search", "max=50", "q=ruby"] + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/shared/set_range.rb b/spec/rubyspec/library/net/http/httpheader/shared/set_range.rb new file mode 100644 index 0000000000..6a98edbe10 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/shared/set_range.rb @@ -0,0 +1,89 @@ +describe :net_httpheader_set_range, shared: true do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + describe "when passed nil" do + it "returns nil" do + @headers.send(@method, nil).should be_nil + end + + it "deletes the 'Range' header entry" do + @headers["Range"] = "bytes 0-499/1234" + @headers.send(@method, nil) + @headers["Range"].should be_nil + end + end + + describe "when passed Numeric" do + it "sets the 'Range' header entry based on the passed Numeric" do + @headers.send(@method, 10) + @headers["Range"].should == "bytes=0-9" + + @headers.send(@method, -10) + @headers["Range"].should == "bytes=-10" + + @headers.send(@method, 10.9) + @headers["Range"].should == "bytes=0-9" + end + end + + describe "when passed Range" do + it "sets the 'Range' header entry based on the passed Range" do + @headers.send(@method, 10..200) + @headers["Range"].should == "bytes=10-200" + + @headers.send(@method, 1..5) + @headers["Range"].should == "bytes=1-5" + + @headers.send(@method, 1...5) + @headers["Range"].should == "bytes=1-4" + + @headers.send(@method, 234..567) + @headers["Range"].should == "bytes=234-567" + + @headers.send(@method, -5..-1) + @headers["Range"].should == "bytes=-5" + + @headers.send(@method, 1..-1) + @headers["Range"].should == "bytes=1-" + end + + it "raises a Net::HTTPHeaderSyntaxError when the first Range element is negative" do + lambda { @headers.send(@method, -10..5) }.should raise_error(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when the last Range element is negative" do + lambda { @headers.send(@method, 10..-5) }.should raise_error(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when the last Range element is smaller than the first" do + lambda { @headers.send(@method, 10..5) }.should raise_error(Net::HTTPHeaderSyntaxError) + end + end + + describe "when passed start, end" do + it "sets the 'Range' header entry based on the passed start and length values" do + @headers.send(@method, 10, 200) + @headers["Range"].should == "bytes=10-209" + + @headers.send(@method, 1, 5) + @headers["Range"].should == "bytes=1-5" + + @headers.send(@method, 234, 567) + @headers["Range"].should == "bytes=234-800" + end + + it "raises a Net::HTTPHeaderSyntaxError when start is negative" do + lambda { @headers.send(@method, -10, 5) }.should raise_error(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when start + length is negative" do + lambda { @headers.send(@method, 10, -15) }.should raise_error(Net::HTTPHeaderSyntaxError) + end + + it "raises a Net::HTTPHeaderSyntaxError when length is negative" do + lambda { @headers.send(@method, 10, -4) }.should raise_error(Net::HTTPHeaderSyntaxError) + end + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/shared/size.rb b/spec/rubyspec/library/net/http/httpheader/shared/size.rb new file mode 100644 index 0000000000..e2b1e4c22b --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/shared/size.rb @@ -0,0 +1,18 @@ +describe :net_httpheader_size, shared: true do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns the number of header entries in self" do + @headers.send(@method).should eql(0) + + @headers["a"] = "b" + @headers.send(@method).should eql(1) + + @headers["b"] = "b" + @headers.send(@method).should eql(2) + + @headers["c"] = "c" + @headers.send(@method).should eql(3) + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/size_spec.rb b/spec/rubyspec/library/net/http/httpheader/size_spec.rb new file mode 100644 index 0000000000..3ea3969046 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/size_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/size', __FILE__) + +describe "Net::HTTPHeader#size" do + it_behaves_like :net_httpheader_size, :size +end diff --git a/spec/rubyspec/library/net/http/httpheader/sub_type_spec.rb b/spec/rubyspec/library/net/http/httpheader/sub_type_spec.rb new file mode 100644 index 0000000000..65cae5a3f3 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/sub_type_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#sub_type" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns the 'sub-content-type', as per 'Content-Type' header entry" do + @headers["Content-Type"] = "text/html" + @headers.sub_type.should == "html" + + @headers["Content-Type"] = "application/pdf" + @headers.sub_type.should == "pdf" + + @headers["Content-Type"] = "text/html;charset=utf-8" + @headers.sub_type.should == "html" + end + + it "returns nil if no 'sub-content-type' is set" do + @headers["Content-Type"] = "text" + @headers.sub_type.should be_nil + + @headers["Content-Type"] = "text;charset=utf-8" + @headers.sub_type.should be_nil + end + + it "returns nil if the 'Content-Type' header entry does not exist" do + @headers.sub_type.should be_nil + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/to_hash_spec.rb b/spec/rubyspec/library/net/http/httpheader/to_hash_spec.rb new file mode 100644 index 0000000000..4b59c78c90 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/to_hash_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#to_hash" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns a Hash representing all Header entries (keys in lower case, values as arrays)" do + @headers.to_hash.should == {} + + @headers["My-Header"] = "test" + @headers.to_hash.should == { "my-header" => ["test"] } + + @headers.add_field("My-Header", "another test") + @headers.to_hash.should == { "my-header" => ["test", "another test"] } + end + + it "does not allow modifying the headers from the returned hash" do + @headers.to_hash["my-header"] = ["test"] + @headers.to_hash.should == {} + @headers.key?("my-header").should be_false + end +end diff --git a/spec/rubyspec/library/net/http/httpheader/type_params_spec.rb b/spec/rubyspec/library/net/http/httpheader/type_params_spec.rb new file mode 100644 index 0000000000..f4cd54f101 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpheader/type_params_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Net::HTTPHeader#type_params" do + before :each do + @headers = NetHTTPHeaderSpecs::Example.new + end + + it "returns additional 'Content-Type' information as a Hash" do + @headers["Content-Type"] = "text/html;charset=utf-8" + @headers.type_params.should == {"charset" => "utf-8"} + + @headers["Content-Type"] = "text/html; charset=utf-8; rubyspec=rocks" + @headers.type_params.should == {"charset" => "utf-8", "rubyspec" => "rocks"} + end + + it "returns an empty Hash when no additional 'Content-Type' information is set" do + @headers.type_params.should == {} + + @headers["Content-Type"] = "text/html" + @headers.type_params.should == {} + end +end diff --git a/spec/rubyspec/library/net/http/httprequest/initialize_spec.rb b/spec/rubyspec/library/net/http/httprequest/initialize_spec.rb new file mode 100644 index 0000000000..8abbf13c93 --- /dev/null +++ b/spec/rubyspec/library/net/http/httprequest/initialize_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +module NetHTTPRequestSpecs + class TestRequest < Net::HTTPRequest + METHOD = "TEST" + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true + end +end + +describe "Net::HTTPRequest#initialize" do + before :each do + @req = NetHTTPRequestSpecs::TestRequest.allocate + end + + it "uses the METHOD constants to set the request method" do + @req.send(:initialize, "/some/path") + @req.method.should == "TEST" + end + + it "uses the REQUEST_HAS_BODY to set whether the Request has a body or not" do + @req.send(:initialize, "/some/path") + @req.request_body_permitted?.should be_false + end + + it "uses the RESPONSE_HAS_BODY to set whether the Response can have a body or not" do + @req.send(:initialize, "/some/path") + @req.response_body_permitted?.should be_true + end + + describe "when passed path" do + it "sets self's path to the passed path" do + @req.send(:initialize, "/some/path") + @req.path.should == "/some/path" + end + end + + describe "when passed path, headers" do + it "uses the passed headers Hash to initialize self's header entries" do + @req.send(:initialize, "/some/path", "Content-Type" => "text/html") + @req["Content-Type"].should == "text/html" + end + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/body_permitted_spec.rb b/spec/rubyspec/library/net/http/httpresponse/body_permitted_spec.rb new file mode 100644 index 0000000000..9688b0759e --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/body_permitted_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse.body_permitted?" do + it "returns true if this response type can have a response body" do + Net::HTTPUnknownResponse.body_permitted?.should == true + Net::HTTPInformation.body_permitted?.should == false + Net::HTTPSuccess.body_permitted?.should == true + Net::HTTPRedirection.body_permitted?.should == true + Net::HTTPClientError.body_permitted?.should == true + Net::HTTPServerError.body_permitted?.should == true + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/body_spec.rb b/spec/rubyspec/library/net/http/httpresponse/body_spec.rb new file mode 100644 index 0000000000..169a6687a2 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/body_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../shared/body', __FILE__) + +describe "Net::HTTPResponse#body" do + it_behaves_like :net_httpresponse_body, :body +end diff --git a/spec/rubyspec/library/net/http/httpresponse/code_spec.rb b/spec/rubyspec/library/net/http/httpresponse/code_spec.rb new file mode 100644 index 0000000000..9430af3100 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/code_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#code" do + it "returns the result code string" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.code.should == "???" + + res = Net::HTTPInformation.new("1.0", "1xx", "test response") + res.code.should == "1xx" + + res = Net::HTTPSuccess.new("1.0", "2xx", "test response") + res.code.should == "2xx" + + res = Net::HTTPRedirection.new("1.0", "3xx", "test response") + res.code.should == "3xx" + + res = Net::HTTPClientError.new("1.0", "4xx", "test response") + res.code.should == "4xx" + + res = Net::HTTPServerError.new("1.0", "5xx", "test response") + res.code.should == "5xx" + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/code_type_spec.rb b/spec/rubyspec/library/net/http/httpresponse/code_type_spec.rb new file mode 100644 index 0000000000..899c2ed9bf --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/code_type_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#code_type" do + it "returns self's class" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.code_type.should == Net::HTTPUnknownResponse + + res = Net::HTTPInformation.new("1.0", "1xx", "test response") + res.code_type.should == Net::HTTPInformation + + res = Net::HTTPSuccess.new("1.0", "2xx", "test response") + res.code_type.should == Net::HTTPSuccess + + res = Net::HTTPRedirection.new("1.0", "3xx", "test response") + res.code_type.should == Net::HTTPRedirection + + res = Net::HTTPClientError.new("1.0", "4xx", "test response") + res.code_type.should == Net::HTTPClientError + + res = Net::HTTPServerError.new("1.0", "5xx", "test response") + res.code_type.should == Net::HTTPServerError + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/entity_spec.rb b/spec/rubyspec/library/net/http/httpresponse/entity_spec.rb new file mode 100644 index 0000000000..3b6be5eb00 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/entity_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require File.expand_path('../shared/body', __FILE__) + +describe "Net::HTTPResponse#entity" do + it_behaves_like :net_httpresponse_body, :entity +end diff --git a/spec/rubyspec/library/net/http/httpresponse/error_spec.rb b/spec/rubyspec/library/net/http/httpresponse/error_spec.rb new file mode 100644 index 0000000000..7b0f61e520 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/error_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#error!" do + it "raises self's class 'EXCEPTION_TYPE' Exception" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + lambda { res.error! }.should raise_error(Net::HTTPError) + + res = Net::HTTPInformation.new("1.0", "1xx", "test response") + lambda { res.error! }.should raise_error(Net::HTTPError) + + res = Net::HTTPSuccess.new("1.0", "2xx", "test response") + lambda { res.error! }.should raise_error(Net::HTTPError) + + res = Net::HTTPRedirection.new("1.0", "3xx", "test response") + lambda { res.error! }.should raise_error(Net::HTTPRetriableError) + + res = Net::HTTPClientError.new("1.0", "4xx", "test response") + lambda { res.error! }.should raise_error(Net::HTTPServerException) + + res = Net::HTTPServerError.new("1.0", "5xx", "test response") + lambda { res.error! }.should raise_error(Net::HTTPFatalError) + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/error_type_spec.rb b/spec/rubyspec/library/net/http/httpresponse/error_type_spec.rb new file mode 100644 index 0000000000..a6b0155998 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/error_type_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#error_type" do + it "returns self's class 'EXCEPTION_TYPE' constant" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.error_type.should == Net::HTTPError + + res = Net::HTTPInformation.new("1.0", "1xx", "test response") + res.error_type.should == Net::HTTPError + + res = Net::HTTPSuccess.new("1.0", "2xx", "test response") + res.error_type.should == Net::HTTPError + + res = Net::HTTPRedirection.new("1.0", "3xx", "test response") + res.error_type.should == Net::HTTPRetriableError + + res = Net::HTTPClientError.new("1.0", "4xx", "test response") + res.error_type.should == Net::HTTPServerException + + res = Net::HTTPServerError.new("1.0", "5xx", "test response") + res.error_type.should == Net::HTTPFatalError + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/exception_type_spec.rb b/spec/rubyspec/library/net/http/httpresponse/exception_type_spec.rb new file mode 100644 index 0000000000..e1a48e0c12 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/exception_type_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse.exception_type" do + it "returns self's 'EXCEPTION_TYPE' constant" do + Net::HTTPUnknownResponse.exception_type.should == Net::HTTPError + Net::HTTPInformation.exception_type.should == Net::HTTPError + Net::HTTPSuccess.exception_type.should == Net::HTTPError + Net::HTTPRedirection.exception_type.should == Net::HTTPRetriableError + Net::HTTPClientError.exception_type.should == Net::HTTPServerException + Net::HTTPServerError.exception_type.should == Net::HTTPFatalError + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/header_spec.rb b/spec/rubyspec/library/net/http/httpresponse/header_spec.rb new file mode 100644 index 0000000000..21e3dec418 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/header_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#header" do + it "returns self" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.response.should equal(res) + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/http_version_spec.rb b/spec/rubyspec/library/net/http/httpresponse/http_version_spec.rb new file mode 100644 index 0000000000..a1192c1cbe --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/http_version_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#http_version" do + it "returns self's http version" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.http_version.should == "1.0" + + res = Net::HTTPUnknownResponse.new("1.1", "???", "test response") + res.http_version.should == "1.1" + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/initialize_spec.rb b/spec/rubyspec/library/net/http/httpresponse/initialize_spec.rb new file mode 100644 index 0000000000..dcb6da3afe --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/initialize_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#initialize when passed http_version, response_code, response_message" do + it "sets self http_version, response_code and response_message to the passed values" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.http_version.should == "1.0" + res.code.should == "???" + res.message.should == "test response" + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/inspect_spec.rb b/spec/rubyspec/library/net/http/httpresponse/inspect_spec.rb new file mode 100644 index 0000000000..f5463afa96 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/inspect_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' +require "stringio" + +describe "Net::HTTPResponse#inspect" do + it "returns a String representation of self" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.inspect.should == "#" + + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + socket = Net::BufferedIO.new(StringIO.new("test body")) + res.reading_body(socket, true) {} + res.inspect.should == "#" + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/message_spec.rb b/spec/rubyspec/library/net/http/httpresponse/message_spec.rb new file mode 100644 index 0000000000..57a927aff0 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/message_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#message" do + it "returns self's response message" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.message.should == "test response" + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/msg_spec.rb b/spec/rubyspec/library/net/http/httpresponse/msg_spec.rb new file mode 100644 index 0000000000..9b19d34234 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/msg_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#msg" do + it "returns self's response message" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.message.should == "test response" + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/read_body_spec.rb b/spec/rubyspec/library/net/http/httpresponse/read_body_spec.rb new file mode 100644 index 0000000000..d71b4144f4 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/read_body_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#read_body" do + before :each do + @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + @socket = Net::BufferedIO.new(StringIO.new("test body")) + end + + describe "when passed no arguments" do + it "returns the read body" do + @res.reading_body(@socket, true) do + @res.read_body.should == "test body" + end + end + + it "returns the previously read body if called a second time" do + @res.reading_body(@socket, true) do + @res.read_body.should equal(@res.read_body) + end + end + end + + describe "when passed a buffer" do + it "reads the body to the passed buffer" do + @res.reading_body(@socket, true) do + buffer = "" + @res.read_body(buffer) + buffer.should == "test body" + end + end + + it "returns the passed buffer" do + @res.reading_body(@socket, true) do + buffer = "" + @res.read_body(buffer).should equal(buffer) + end + end + + it "raises an IOError if called a second time" do + @res.reading_body(@socket, true) do + @res.read_body("") + lambda { @res.read_body("") }.should raise_error(IOError) + end + end + end + + describe "when passed a block" do + it "reads the body and yields it to the passed block (in chunks)" do + @res.reading_body(@socket, true) do + yielded = false + + buffer = "" + @res.read_body do |body| + yielded = true + buffer << body + end + + yielded.should be_true + buffer.should == "test body" + end + end + + it "returns the ReadAdapter" do + @res.reading_body(@socket, true) do + @res.read_body { nil }.should be_kind_of(Net::ReadAdapter) + end + end + + it "raises an IOError if called a second time" do + @res.reading_body(@socket, true) do + @res.read_body {} + lambda { @res.read_body {} }.should raise_error(IOError) + end + end + end + + describe "when passed buffer and block" do + it "rauses an ArgumentError" do + @res.reading_body(@socket, true) do + lambda { @res.read_body("") {} }.should raise_error(ArgumentError) + end + end + end +end + diff --git a/spec/rubyspec/library/net/http/httpresponse/read_header_spec.rb b/spec/rubyspec/library/net/http/httpresponse/read_header_spec.rb new file mode 100644 index 0000000000..6bbabdfe10 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/read_header_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#read_header" do + it "returns self" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.response.should equal(res) + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/read_new_spec.rb b/spec/rubyspec/library/net/http/httpresponse/read_new_spec.rb new file mode 100644 index 0000000000..e2fb0f1a05 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/read_new_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse.read_new" do + it "creates a HTTPResponse object based on the response read from the passed socket" do + socket = Net::BufferedIO.new(StringIO.new(<" + yielded = true + end + + yielded.should be_true + end + + describe "but the response type is not allowed to have a body" do + before :each do + @res = Net::HTTPInformation.new("1.0", "???", "test response") + end + + it "returns nil" do + @res.reading_body(@socket, false) {}.should be_nil + @res.body.should be_nil + end + + it "yields the passed block" do + yielded = false + @res.reading_body(@socket, true) { yielded = true } + yielded.should be_true + end + end + end + + describe "when body_allowed is false" do + it "returns nil" do + @res.reading_body(@socket, false) {}.should be_nil + @res.body.should be_nil + end + + it "yields the passed block" do + yielded = false + @res.reading_body(@socket, true) { yielded = true } + yielded.should be_true + end + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/response_spec.rb b/spec/rubyspec/library/net/http/httpresponse/response_spec.rb new file mode 100644 index 0000000000..826320be9e --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/response_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#response" do + it "returns self" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + res.response.should equal(res) + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/shared/body.rb b/spec/rubyspec/library/net/http/httpresponse/shared/body.rb new file mode 100644 index 0000000000..91d5fe6375 --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/shared/body.rb @@ -0,0 +1,18 @@ +describe :net_httpresponse_body, shared: true do + before :each do + @res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + @socket = Net::BufferedIO.new(StringIO.new("test body")) + end + + it "returns the read body" do + @res.reading_body(@socket, true) do + @res.send(@method).should == "test body" + end + end + + it "returns the previously read body if called a second time" do + @res.reading_body(@socket, true) do + @res.send(@method).should equal(@res.send(@method)) + end + end +end diff --git a/spec/rubyspec/library/net/http/httpresponse/value_spec.rb b/spec/rubyspec/library/net/http/httpresponse/value_spec.rb new file mode 100644 index 0000000000..7e4f7863eb --- /dev/null +++ b/spec/rubyspec/library/net/http/httpresponse/value_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'net/http' + +describe "Net::HTTPResponse#value" do + it "raises an HTTP error for non 2xx HTTP Responses" do + res = Net::HTTPUnknownResponse.new("1.0", "???", "test response") + lambda { res.value }.should raise_error(Net::HTTPError) + + res = Net::HTTPInformation.new("1.0", "1xx", "test response") + lambda { res.value }.should raise_error(Net::HTTPError) + + res = Net::HTTPSuccess.new("1.0", "2xx", "test response") + lambda { res.value }.should_not raise_error(Net::HTTPError) + + res = Net::HTTPRedirection.new("1.0", "3xx", "test response") + lambda { res.value }.should raise_error(Net::HTTPRetriableError) + + res = Net::HTTPClientError.new("1.0", "4xx", "test response") + lambda { res.value }.should raise_error(Net::HTTPServerException) + + res = Net::HTTPServerError.new("1.0", "5xx", "test response") + lambda { res.value }.should raise_error(Net::HTTPFatalError) + end +end diff --git a/spec/rubyspec/library/observer/add_observer_spec.rb b/spec/rubyspec/library/observer/add_observer_spec.rb new file mode 100644 index 0000000000..7a2d349c85 --- /dev/null +++ b/spec/rubyspec/library/observer/add_observer_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Observer#add_observer" do + + before :each do + @observable = ObservableSpecs.new + @observer = ObserverCallbackSpecs.new + end + + it "adds the observer" do + @observer.value.should == nil + @observable.changed + @observable.notify_observers("test") + @observer.value.should == nil + + @observable.add_observer(@observer) + @observable.changed + @observable.notify_observers("test2") + @observer.value.should == "test2" + end + +end diff --git a/spec/rubyspec/library/observer/count_observers_spec.rb b/spec/rubyspec/library/observer/count_observers_spec.rb new file mode 100644 index 0000000000..68eaccfaaa --- /dev/null +++ b/spec/rubyspec/library/observer/count_observers_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Observer#count_observers" do + before :each do + @observable = ObservableSpecs.new + @observer = ObserverCallbackSpecs.new + @observer2 = ObserverCallbackSpecs.new + end + + it "returns the number of observers" do + @observable.count_observers.should == 0 + @observable.add_observer(@observer) + @observable.count_observers.should == 1 + @observable.add_observer(@observer2) + @observable.count_observers.should == 2 + end + + it "returns the number of unique observers" do + 2.times { @observable.add_observer(@observer) } + @observable.count_observers.should == 1 + end +end diff --git a/spec/rubyspec/library/observer/delete_observer_spec.rb b/spec/rubyspec/library/observer/delete_observer_spec.rb new file mode 100644 index 0000000000..b9dc95167c --- /dev/null +++ b/spec/rubyspec/library/observer/delete_observer_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Observer#delete_observer" do + before :each do + @observable = ObservableSpecs.new + @observer = ObserverCallbackSpecs.new + end + + it "deletes the observer" do + @observable.add_observer(@observer) + @observable.delete_observer(@observer) + + @observable.changed + @observable.notify_observers("test") + @observer.value.should == nil + end + +end diff --git a/spec/rubyspec/library/observer/delete_observers_spec.rb b/spec/rubyspec/library/observer/delete_observers_spec.rb new file mode 100644 index 0000000000..58b10418dd --- /dev/null +++ b/spec/rubyspec/library/observer/delete_observers_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Observer#delete_observers" do + before :each do + @observable = ObservableSpecs.new + @observer = ObserverCallbackSpecs.new + end + + it "deletes the observers" do + @observable.add_observer(@observer) + @observable.delete_observers + + @observable.changed + @observable.notify_observers("test") + @observer.value.should == nil + end + +end diff --git a/spec/rubyspec/library/observer/fixtures/classes.rb b/spec/rubyspec/library/observer/fixtures/classes.rb new file mode 100644 index 0000000000..70cd1b1be2 --- /dev/null +++ b/spec/rubyspec/library/observer/fixtures/classes.rb @@ -0,0 +1,17 @@ +require 'observer' + +class ObserverCallbackSpecs + attr_reader :value + + def initialize + @value = nil + end + + def update(value) + @value = value + end +end + +class ObservableSpecs + include Observable +end diff --git a/spec/rubyspec/library/observer/notify_observers_spec.rb b/spec/rubyspec/library/observer/notify_observers_spec.rb new file mode 100644 index 0000000000..cafbdd56e6 --- /dev/null +++ b/spec/rubyspec/library/observer/notify_observers_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Observer#notify_observers" do + + before :each do + @observable = ObservableSpecs.new + @observer = ObserverCallbackSpecs.new + @observable.add_observer(@observer) + end + + it "must call changed before notifying observers" do + @observer.value.should == nil + @observable.notify_observers("test") + @observer.value.should == nil + end + + it "verifies observer responds to update" do + lambda { + @observable.add_observer(@observable) + }.should raise_error(NoMethodError) + end + + it "receives the callback" do + @observer.value.should == nil + @observable.changed + @observable.notify_observers("test") + @observer.value.should == "test" + end + +end diff --git a/spec/rubyspec/library/open3/capture2_spec.rb b/spec/rubyspec/library/open3/capture2_spec.rb new file mode 100644 index 0000000000..e5bcc67ae0 --- /dev/null +++ b/spec/rubyspec/library/open3/capture2_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.capture2" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/capture2e_spec.rb b/spec/rubyspec/library/open3/capture2e_spec.rb new file mode 100644 index 0000000000..6254914222 --- /dev/null +++ b/spec/rubyspec/library/open3/capture2e_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.capture2e" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/capture3_spec.rb b/spec/rubyspec/library/open3/capture3_spec.rb new file mode 100644 index 0000000000..cbfa023eb5 --- /dev/null +++ b/spec/rubyspec/library/open3/capture3_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.capture3" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/pipeline_r_spec.rb b/spec/rubyspec/library/open3/pipeline_r_spec.rb new file mode 100644 index 0000000000..c5c2f02087 --- /dev/null +++ b/spec/rubyspec/library/open3/pipeline_r_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.pipeline_r" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/pipeline_rw_spec.rb b/spec/rubyspec/library/open3/pipeline_rw_spec.rb new file mode 100644 index 0000000000..6f9830270b --- /dev/null +++ b/spec/rubyspec/library/open3/pipeline_rw_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.pipeline_rw" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/pipeline_spec.rb b/spec/rubyspec/library/open3/pipeline_spec.rb new file mode 100644 index 0000000000..f8ace3d2da --- /dev/null +++ b/spec/rubyspec/library/open3/pipeline_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.pipeline" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/pipeline_start_spec.rb b/spec/rubyspec/library/open3/pipeline_start_spec.rb new file mode 100644 index 0000000000..76af0b88e5 --- /dev/null +++ b/spec/rubyspec/library/open3/pipeline_start_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.pipeline_start" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/pipeline_w_spec.rb b/spec/rubyspec/library/open3/pipeline_w_spec.rb new file mode 100644 index 0000000000..e3336b7fe1 --- /dev/null +++ b/spec/rubyspec/library/open3/pipeline_w_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.pipeline_w" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/popen2_spec.rb b/spec/rubyspec/library/open3/popen2_spec.rb new file mode 100644 index 0000000000..49bab91bef --- /dev/null +++ b/spec/rubyspec/library/open3/popen2_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.popen2" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/popen2e_spec.rb b/spec/rubyspec/library/open3/popen2e_spec.rb new file mode 100644 index 0000000000..2361543afa --- /dev/null +++ b/spec/rubyspec/library/open3/popen2e_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.popen2e" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/open3/popen3_spec.rb b/spec/rubyspec/library/open3/popen3_spec.rb new file mode 100644 index 0000000000..9733ab15cd --- /dev/null +++ b/spec/rubyspec/library/open3/popen3_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'open3' + +describe "Open3.popen3" do + it "returns in, out, err and a thread waiting the process" do + stdin, out, err, waiter = Open3.popen3(ruby_cmd("print :foo")) + begin + stdin.should be_kind_of IO + out.should be_kind_of IO + err.should be_kind_of IO + waiter.should be_kind_of Thread + + out.read.should == "foo" + ensure + stdin.close + out.close + err.close + waiter.join + end + end + + it "executes a process with a pipe to read stdout" do + Open3.popen3(ruby_cmd("print :foo")) do |stdin, out, err| + out.read.should == "foo" + end + end + + it "executes a process with a pipe to read stderr" do + Open3.popen3(ruby_cmd("STDERR.print :foo")) do |stdin, out, err| + err.read.should == "foo" + end + end + + it "executes a process with a pipe to write stdin" do + Open3.popen3(ruby_cmd("print STDIN.read")) do |stdin, out, err| + stdin.write("foo") + stdin.close + out.read.should == "foo" + end + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/openssl/cipher_spec.rb b/spec/rubyspec/library/openssl/cipher_spec.rb new file mode 100644 index 0000000000..c3eb1280a2 --- /dev/null +++ b/spec/rubyspec/library/openssl/cipher_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/constants', __FILE__) +require 'openssl' + +describe "OpenSSL::Cipher's CipherError" do + it "exists under OpenSSL::Cipher namespace" do + OpenSSL::Cipher.should have_constant :CipherError + end +end diff --git a/spec/rubyspec/library/openssl/config/freeze_spec.rb b/spec/rubyspec/library/openssl/config/freeze_spec.rb new file mode 100644 index 0000000000..2ed48ae629 --- /dev/null +++ b/spec/rubyspec/library/openssl/config/freeze_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../shared/constants', __FILE__) + +require 'openssl' + +describe "OpenSSL::Config#freeze" do + it "needs to be reviewed for completeness" + + it "freezes" do + c = OpenSSL::Config.new + lambda{c['foo'] = [ ['key', 'value'] ]}.should_not raise_error + c.freeze + c.frozen?.should be_true + lambda{c['foo'] = [ ['key', 'value'] ]}.should raise_error + end +end diff --git a/spec/rubyspec/library/openssl/hmac/digest_spec.rb b/spec/rubyspec/library/openssl/hmac/digest_spec.rb new file mode 100644 index 0000000000..36edee87bd --- /dev/null +++ b/spec/rubyspec/library/openssl/hmac/digest_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../shared/constants', __FILE__) +require 'openssl' + +describe "OpenSSL::HMAC.digest" do + it "returns an SHA1 digest" do + cur_digest = OpenSSL::Digest::SHA1.new + cur_digest.digest.should == HMACConstants::BlankSHA1Digest + digest = OpenSSL::HMAC.digest(cur_digest, + HMACConstants::Key, + HMACConstants::Contents) + digest.should == HMACConstants::SHA1Digest + end +end + +# Should add in similar specs for MD5, RIPEMD160, and SHA256 diff --git a/spec/rubyspec/library/openssl/hmac/hexdigest_spec.rb b/spec/rubyspec/library/openssl/hmac/hexdigest_spec.rb new file mode 100644 index 0000000000..b61bcf6a8f --- /dev/null +++ b/spec/rubyspec/library/openssl/hmac/hexdigest_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../shared/constants', __FILE__) +require 'openssl' + +describe "OpenSSL::HMAC.hexdigest" do + it "returns an SHA1 hex digest" do + cur_digest = OpenSSL::Digest::SHA1.new + cur_digest.hexdigest.should == HMACConstants::BlankSHA1HexDigest + hexdigest = OpenSSL::HMAC.hexdigest(cur_digest, + HMACConstants::Key, + HMACConstants::Contents) + hexdigest.should == HMACConstants::SHA1Hexdigest + end +end + +# Should add in similar specs for MD5, RIPEMD160, and SHA256 diff --git a/spec/rubyspec/library/openssl/random/pseudo_bytes_spec.rb b/spec/rubyspec/library/openssl/random/pseudo_bytes_spec.rb new file mode 100644 index 0000000000..83c8cc13c8 --- /dev/null +++ b/spec/rubyspec/library/openssl/random/pseudo_bytes_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/random_bytes.rb', __FILE__) + +if defined?(OpenSSL::Random.pseudo_bytes) + describe "OpenSSL::Random.pseudo_bytes" do + it_behaves_like :openssl_random_bytes, :pseudo_bytes + end +end diff --git a/spec/rubyspec/library/openssl/random/random_bytes_spec.rb b/spec/rubyspec/library/openssl/random/random_bytes_spec.rb new file mode 100644 index 0000000000..b8bd209eb0 --- /dev/null +++ b/spec/rubyspec/library/openssl/random/random_bytes_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/random_bytes.rb', __FILE__) + +describe "OpenSSL::Random.random_bytes" do + it_behaves_like :openssl_random_bytes, :random_bytes +end diff --git a/spec/rubyspec/library/openssl/random/shared/random_bytes.rb b/spec/rubyspec/library/openssl/random/shared/random_bytes.rb new file mode 100644 index 0000000000..399e40de39 --- /dev/null +++ b/spec/rubyspec/library/openssl/random/shared/random_bytes.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'openssl' + +describe :openssl_random_bytes, shared: true do |cmd| + it "generates a random binary string of specified length" do + (1..64).each do |idx| + bytes = OpenSSL::Random.send(@method, idx) + bytes.should be_kind_of(String) + bytes.length.should == idx + end + end + + it "generates different binary strings with subsequent invocations" do + # quick and dirty check, but good enough + values = [] + 256.times do + val = OpenSSL::Random.send(@method, 16) + # make sure the random bytes are not repeating + values.include?(val).should == false + values << val + end + end + + it "raises ArgumentError on negative arguments" do + lambda { + OpenSSL::Random.send(@method, -1) + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/openssl/shared/constants.rb b/spec/rubyspec/library/openssl/shared/constants.rb new file mode 100644 index 0000000000..0bed4156a1 --- /dev/null +++ b/spec/rubyspec/library/openssl/shared/constants.rb @@ -0,0 +1,11 @@ +# -*- encoding: binary -*- +module HMACConstants + + Contents = "Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. \nIt has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. \nIt was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum." + Key = 'sekrit' + + BlankSHA1Digest = "\3329\243\356^kK\r2U\277\357\225`\030\220\257\330\a\t" + SHA1Digest = "\236\022\323\341\037\236\262n\344\t\372:\004J\242\330\257\270\363\264" + BlankSHA1HexDigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709" + SHA1Hexdigest = "9e12d3e11f9eb26ee409fa3a044aa2d8afb8f3b4" +end diff --git a/spec/rubyspec/library/openssl/x509/name/parse_spec.rb b/spec/rubyspec/library/openssl/x509/name/parse_spec.rb new file mode 100644 index 0000000000..c42760fdb7 --- /dev/null +++ b/spec/rubyspec/library/openssl/x509/name/parse_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'openssl' + +describe "OpenSSL::X509::Name.parse" do + it "parses a /-delimited string of key-value pairs into a Name" do + dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" + name = OpenSSL::X509::Name.parse(dn) + + name.to_s.should == dn + + ary = name.to_a + + ary[0][0].should == "DC" + ary[1][0].should == "DC" + ary[2][0].should == "CN" + ary[0][1].should == "org" + ary[1][1].should == "ruby-lang" + ary[2][1].should == "www.ruby-lang.org" + ary[0][2].should == OpenSSL::ASN1::IA5STRING + ary[1][2].should == OpenSSL::ASN1::IA5STRING + ary[2][2].should == OpenSSL::ASN1::UTF8STRING + end + + it "parses a comma-delimited string of key-value pairs into a name" do + dn = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org" + name = OpenSSL::X509::Name.parse(dn) + + name.to_s.should == "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" + + ary = name.to_a + + ary[0][1].should == "org" + ary[1][1].should == "ruby-lang" + ary[2][1].should == "www.ruby-lang.org" + end + + it "raises TypeError if the given string contains no key/value pairs" do + lambda do + OpenSSL::X509::Name.parse("hello") + end.should raise_error(TypeError) + end + + it "raises OpenSSL::X509::NameError if the given string contains invalid keys" do + lambda do + OpenSSL::X509::Name.parse("hello=goodbye") + end.should raise_error(OpenSSL::X509::NameError) + end +end diff --git a/spec/rubyspec/library/openstruct/delete_field_spec.rb b/spec/rubyspec/library/openstruct/delete_field_spec.rb new file mode 100644 index 0000000000..a565f61b92 --- /dev/null +++ b/spec/rubyspec/library/openstruct/delete_field_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ostruct' + +describe "OpenStruct#delete_field" do + before :each do + @os = OpenStruct.new(name: "John Smith", age: 70, pension: 300) + end + + it "removes the named field from self's method/value table" do + @os.delete_field(:name) + @os[:name].should be_nil + end + + it "does remove the accessor methods" do + @os.delete_field(:name) + @os.respond_to?(:name).should be_false + @os.respond_to?(:name=).should be_false + end +end diff --git a/spec/rubyspec/library/openstruct/element_reference_spec.rb b/spec/rubyspec/library/openstruct/element_reference_spec.rb new file mode 100644 index 0000000000..431843547d --- /dev/null +++ b/spec/rubyspec/library/openstruct/element_reference_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "ostruct" + +describe "OpenStruct#[]" do + before :each do + @os = OpenStruct.new + end + + it "returns the associated value" do + @os.foo = 42 + @os[:foo].should == 42 + end +end diff --git a/spec/rubyspec/library/openstruct/element_set_spec.rb b/spec/rubyspec/library/openstruct/element_set_spec.rb new file mode 100644 index 0000000000..afa65247e4 --- /dev/null +++ b/spec/rubyspec/library/openstruct/element_set_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "ostruct" + +describe "OpenStruct#[]=" do + before :each do + @os = OpenStruct.new + end + + it "sets the associated value" do + @os[:foo] = 42 + @os.foo.should == 42 + end +end diff --git a/spec/rubyspec/library/openstruct/equal_value_spec.rb b/spec/rubyspec/library/openstruct/equal_value_spec.rb new file mode 100644 index 0000000000..0d2d1d881e --- /dev/null +++ b/spec/rubyspec/library/openstruct/equal_value_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "ostruct" +require File.expand_path('../fixtures/classes', __FILE__) + +describe "OpenStruct#==" do + before :each do + @os = OpenStruct.new(name: "John") + end + + it "returns false when the passed argument is no OpenStruct" do + (@os == Object.new).should be_false + (@os == "Test").should be_false + (@os == 10).should be_false + (@os == :sym).should be_false + end + + it "returns true when self and other are equal method/value wise" do + (@os == @os).should be_true + (@os == OpenStruct.new(name: "John")).should be_true + (@os == OpenStructSpecs::OpenStructSub.new(name: "John")).should be_true + + (@os == OpenStruct.new(name: "Jonny")).should be_false + (@os == OpenStructSpecs::OpenStructSub.new(name: "Jonny")).should be_false + + (@os == OpenStruct.new(name: "John", age: 20)).should be_false + (@os == OpenStructSpecs::OpenStructSub.new(name: "John", age: 20)).should be_false + end +end diff --git a/spec/rubyspec/library/openstruct/fixtures/classes.rb b/spec/rubyspec/library/openstruct/fixtures/classes.rb new file mode 100644 index 0000000000..da42e8511d --- /dev/null +++ b/spec/rubyspec/library/openstruct/fixtures/classes.rb @@ -0,0 +1,4 @@ +module OpenStructSpecs + class OpenStructSub < OpenStruct + end +end diff --git a/spec/rubyspec/library/openstruct/frozen_spec.rb b/spec/rubyspec/library/openstruct/frozen_spec.rb new file mode 100644 index 0000000000..26dd556e97 --- /dev/null +++ b/spec/rubyspec/library/openstruct/frozen_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ostruct' + +describe "OpenStruct.new when frozen" do + before :each do + @os = OpenStruct.new(name: "John Smith", age: 70, pension: 300).freeze + end + # + # method_missing case handled in method_missing_spec.rb + # + it "is still readable" do + @os.age.should eql(70) + @os.pension.should eql(300) + @os.name.should == "John Smith" + end + + it "is not writeable" do + lambda{ @os.age = 42 }.should raise_error( RuntimeError ) + end + + it "cannot create new fields" do + lambda{ @os.state = :new }.should raise_error( RuntimeError ) + end + + it "creates a frozen clone" do + f = @os.clone + f.age.should == 70 + lambda{ f.age = 0 }.should raise_error( RuntimeError ) + lambda{ f.state = :newer }.should raise_error( RuntimeError ) + end + + it "creates an unfrozen dup" do + d = @os.dup + d.age.should == 70 + d.age = 42 + d.age.should == 42 + end +end diff --git a/spec/rubyspec/library/openstruct/initialize_spec.rb b/spec/rubyspec/library/openstruct/initialize_spec.rb new file mode 100644 index 0000000000..b5edde7618 --- /dev/null +++ b/spec/rubyspec/library/openstruct/initialize_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ostruct' + +describe "OpenStruct#initialize" do + it "is private" do + OpenStruct.should have_private_instance_method(:initialize) + end +end diff --git a/spec/rubyspec/library/openstruct/inspect_spec.rb b/spec/rubyspec/library/openstruct/inspect_spec.rb new file mode 100644 index 0000000000..826437b3c1 --- /dev/null +++ b/spec/rubyspec/library/openstruct/inspect_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ostruct' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "OpenStruct#inspect" do + it_behaves_like :ostruct_inspect, :inspect +end diff --git a/spec/rubyspec/library/openstruct/marshal_dump_spec.rb b/spec/rubyspec/library/openstruct/marshal_dump_spec.rb new file mode 100644 index 0000000000..cdc1564699 --- /dev/null +++ b/spec/rubyspec/library/openstruct/marshal_dump_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "ostruct" + +describe "OpenStruct#marshal_dump" do + it "returns the method/value table" do + os = OpenStruct.new("age" => 20, "name" => "John") + os.marshal_dump.should == { age: 20, name: "John" } + end +end diff --git a/spec/rubyspec/library/openstruct/marshal_load_spec.rb b/spec/rubyspec/library/openstruct/marshal_load_spec.rb new file mode 100644 index 0000000000..9c89697d8f --- /dev/null +++ b/spec/rubyspec/library/openstruct/marshal_load_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "ostruct" + +describe "OpenStruct#marshal_load when passed [Hash]" do + it "defines methods based on the passed Hash" do + os = OpenStruct.new + os.marshal_load(age: 20, name: "John") + + os.age.should eql(20) + os.name.should == "John" + end +end diff --git a/spec/rubyspec/library/openstruct/method_missing_spec.rb b/spec/rubyspec/library/openstruct/method_missing_spec.rb new file mode 100644 index 0000000000..6051fd48d8 --- /dev/null +++ b/spec/rubyspec/library/openstruct/method_missing_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "ostruct" + +describe "OpenStruct#method_missing when called with a method name ending in '='" do + before :each do + @os = OpenStruct.new + end + + it "raises an ArgumentError when not passed any additional arguments" do + lambda { @os.method_missing(:test=) }.should raise_error(ArgumentError) + end + + it "raises a TypeError when self is frozen" do + @os.freeze + lambda { @os.method_missing(:test=, "test") }.should raise_error(RuntimeError) + end + + it "creates accessor methods" do + @os.method_missing(:test=, "test") + @os.respond_to?(:test=).should be_true + @os.respond_to?(:test).should be_true + + @os.test.should == "test" + @os.test = "changed" + @os.test.should == "changed" + end + + it "updates the method/value table with the passed method/value" do + @os.method_missing(:test=, "test") + @os.send(:table)[:test].should == "test" + end +end + +describe "OpenStruct#method_missing when passed additional arguments" do + it "raises a NoMethodError" do + os = OpenStruct.new + lambda { os.method_missing(:test, 1, 2, 3) }.should raise_error(NoMethodError) + end +end + +describe "OpenStruct#method_missing when not passed any additional arguments" do + it "returns the value for the passed method from the method/value table" do + os = OpenStruct.new(age: 20) + os.method_missing(:age).should eql(20) + os.method_missing(:name).should be_nil + end +end diff --git a/spec/rubyspec/library/openstruct/new_spec.rb b/spec/rubyspec/library/openstruct/new_spec.rb new file mode 100644 index 0000000000..ce33634e08 --- /dev/null +++ b/spec/rubyspec/library/openstruct/new_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ostruct' + +describe "OpenStruct.new when passed [Hash]" do + before :each do + @os = OpenStruct.new(name: "John Smith", age: 70, pension: 300) + end + + it "creates an attribute for each key of the passed Hash" do + @os.age.should eql(70) + @os.pension.should eql(300) + @os.name.should == "John Smith" + end +end + +describe "OpenStruct.new when passed no arguments" do + it "returns a new OpenStruct Object without any attributes" do + OpenStruct.new.to_s.should == "#" + end +end diff --git a/spec/rubyspec/library/openstruct/shared/inspect.rb b/spec/rubyspec/library/openstruct/shared/inspect.rb new file mode 100644 index 0000000000..ffcd690e1f --- /dev/null +++ b/spec/rubyspec/library/openstruct/shared/inspect.rb @@ -0,0 +1,20 @@ +describe :ostruct_inspect, shared: true do + it "returns a String representation of self" do + os = OpenStruct.new(name: "John Smith") + os.send(@method).should == "#" + + os = OpenStruct.new(age: 20, name: "John Smith") + os.send(@method).should be_kind_of(String) + end + + it "correctly handles self-referential OpenStructs" do + os = OpenStruct.new + os.self = os + os.send(@method).should == "#>" + end + + it "correctly handles OpenStruct subclasses" do + os = OpenStructSpecs::OpenStructSub.new(name: "John Smith") + os.send(@method).should == "#" + end +end diff --git a/spec/rubyspec/library/openstruct/to_h_spec.rb b/spec/rubyspec/library/openstruct/to_h_spec.rb new file mode 100644 index 0000000000..f1bffd6fa6 --- /dev/null +++ b/spec/rubyspec/library/openstruct/to_h_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ostruct' + +describe "OpenStruct#to_h" do + before :each do + @h = {name: "John Smith", age: 70, pension: 300} + @os = OpenStruct.new(@h) + @to_h = @os.to_h + end + + it "returns a Hash with members as keys" do + @to_h.should == @h + end + + it "returns a Hash with keys as symbols" do + os = OpenStruct.new("name" => "John Smith", "age" => 70) + os.pension = 300 + os.to_h.should == @h + end + + it "does not return the hash used as initializer" do + @to_h.should_not equal(@h) + end + + it "returns a Hash that is independent from the struct" do + @to_h[:age] = 71 + @os.age.should == 70 + end +end diff --git a/spec/rubyspec/library/openstruct/to_s_spec.rb b/spec/rubyspec/library/openstruct/to_s_spec.rb new file mode 100644 index 0000000000..8efa3f5aaf --- /dev/null +++ b/spec/rubyspec/library/openstruct/to_s_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'ostruct' +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/inspect', __FILE__) + +describe "OpenStruct#to_s" do + it_behaves_like :ostruct_inspect, :to_s +end diff --git a/spec/rubyspec/library/pathname/absolute_spec.rb b/spec/rubyspec/library/pathname/absolute_spec.rb new file mode 100644 index 0000000000..af0639493a --- /dev/null +++ b/spec/rubyspec/library/pathname/absolute_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#absolute?" do + + it "returns true for the root directory" do + Pathname.new('/').absolute?.should == true + end + + it "returns true for a dir starting with a slash" do + Pathname.new('/usr/local/bin').absolute?.should == true + end + + it "returns false for a dir not starting with a slash" do + Pathname.new('fish').absolute?.should == false + end + + it "returns false for a dir not starting with a slash" do + Pathname.new('fish/dog/cow').absolute?.should == false + end + +end + diff --git a/spec/rubyspec/library/pathname/equal_value_spec.rb b/spec/rubyspec/library/pathname/equal_value_spec.rb new file mode 100644 index 0000000000..afcdb08de8 --- /dev/null +++ b/spec/rubyspec/library/pathname/equal_value_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#==" do + + it "returns true when identical paths are used" do + (Pathname.new('') == Pathname.new('')).should == true + end + + it "returns true when identical paths are used" do + (Pathname.new('') == Pathname.new('/usr/local/bin')).should == false + end + +end + diff --git a/spec/rubyspec/library/pathname/hash_spec.rb b/spec/rubyspec/library/pathname/hash_spec.rb new file mode 100644 index 0000000000..f3201e2f4f --- /dev/null +++ b/spec/rubyspec/library/pathname/hash_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#hash" do + + it "is equal to the hash of the pathname" do + Pathname.new('/usr/local/bin/').hash.should == '/usr/local/bin/'.hash + end + + it "is not equal the hash of a different pathname" do + Pathname.new('/usr/local/bin/').hash.should_not == '/usr/bin/'.hash + end + +end + diff --git a/spec/rubyspec/library/pathname/join_spec.rb b/spec/rubyspec/library/pathname/join_spec.rb new file mode 100644 index 0000000000..8c77bb1f59 --- /dev/null +++ b/spec/rubyspec/library/pathname/join_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#join" do + it "without separators" do + Pathname.new('/usr').join(Pathname.new('foo')).should == Pathname.new('/usr/foo') + end + + it "with separators" do + Pathname.new('/usr').join(Pathname.new('/foo')).should == Pathname.new('/foo') + end + + it "with a string" do + Pathname.new('/usr').join('foo').should == Pathname.new('/usr/foo') + end + + it "with root" do + Pathname.new('/usr').join(Pathname.new('/')).should == Pathname.new('/') + end + + it "with a relative path" do + Pathname.new('/usr').join(Pathname.new('../foo')).should == Pathname.new('/foo') + end + + it "a relative path with current" do + Pathname.new('.').join(Pathname.new('foo')).should == Pathname.new('foo') + end + + it "an absolute path with current" do + Pathname.new('.').join(Pathname.new('/foo')).should == Pathname.new('/foo') + end + + it "a prefixed relative path with current" do + Pathname.new('.').join(Pathname.new('./foo')).should == Pathname.new('foo') + end + + it "multiple paths" do + Pathname.new('.').join(Pathname.new('./foo'), 'bar').should == Pathname.new('foo/bar') + end +end diff --git a/spec/rubyspec/library/pathname/new_spec.rb b/spec/rubyspec/library/pathname/new_spec.rb new file mode 100644 index 0000000000..a888e98736 --- /dev/null +++ b/spec/rubyspec/library/pathname/new_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname.new" do + it "returns a new Pathname Object with 1 argument" do + Pathname.new('').should be_kind_of(Pathname) + end + + it "raises an ArgumentError when called with \0" do + lambda { Pathname.new("\0")}.should raise_error(ArgumentError) + end + + it "is tainted if path is tainted" do + path = '/usr/local/bin'.taint + Pathname.new(path).tainted?.should == true + end + + it "raises a TypeError if not passed a String type" do + lambda { Pathname.new(nil) }.should raise_error(TypeError) + lambda { Pathname.new(0) }.should raise_error(TypeError) + lambda { Pathname.new(true) }.should raise_error(TypeError) + lambda { Pathname.new(false) }.should raise_error(TypeError) + end + + it "initializes with an object with to_path" do + Pathname.new(mock_to_path('foo')).should == Pathname.new('foo') + end +end diff --git a/spec/rubyspec/library/pathname/parent_spec.rb b/spec/rubyspec/library/pathname/parent_spec.rb new file mode 100644 index 0000000000..53d3f1e50e --- /dev/null +++ b/spec/rubyspec/library/pathname/parent_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#parent" do + + it "has parent of root as root" do + Pathname.new('/').parent.to_s.should == '/' + end + + it "has parent of /usr/ as root" do + Pathname.new('/usr/').parent.to_s.should == '/' + end + + it "has parent of /usr/local as root" do + Pathname.new('/usr/local').parent.to_s.should == '/usr' + end + +end + diff --git a/spec/rubyspec/library/pathname/realdirpath_spec.rb b/spec/rubyspec/library/pathname/realdirpath_spec.rb new file mode 100644 index 0000000000..f76e37602c --- /dev/null +++ b/spec/rubyspec/library/pathname/realdirpath_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#realdirpath" do + + it "returns a Pathname" do + Pathname.pwd.realdirpath.should be_an_instance_of(Pathname) + end + +end diff --git a/spec/rubyspec/library/pathname/realpath_spec.rb b/spec/rubyspec/library/pathname/realpath_spec.rb new file mode 100644 index 0000000000..e1c9eb34ea --- /dev/null +++ b/spec/rubyspec/library/pathname/realpath_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#realpath" do + + it "returns a Pathname" do + Pathname.pwd.realpath.should be_an_instance_of(Pathname) + end + +end diff --git a/spec/rubyspec/library/pathname/relative_path_from_spec.rb b/spec/rubyspec/library/pathname/relative_path_from_spec.rb new file mode 100644 index 0000000000..b3bc85e307 --- /dev/null +++ b/spec/rubyspec/library/pathname/relative_path_from_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#relative_path_from" do + def relative_path_str(dest, base) + Pathname.new(dest).relative_path_from(Pathname.new(base)).to_s + end + + it "raises an error when the two paths do not share a common prefix" do + lambda { relative_path_str('/usr', 'foo') }.should raise_error(ArgumentError) + end + + it "raises an error when the base directory has .." do + lambda { relative_path_str('a', '..') }.should raise_error(ArgumentError) + end + + it "retuns a path relative from root" do + relative_path_str('/usr', '/').should == 'usr' + end + + it 'returns 1 level up when both paths are relative' do + relative_path_str('a', 'b').should == '../a' + relative_path_str('a', 'b/').should == '../a' + end + + it 'returns a relative path when both are absolute' do + relative_path_str('/a', '/b').should == '../a' + end + + it "returns a path relative to the current directory" do + relative_path_str('/usr/bin/ls', '/usr').should == 'bin/ls' + end + + it 'returns a . when base and dest are the same' do + relative_path_str('/usr', '/usr').should == '.' + end + + it 'returns the same directory with a non clean base that matches the current dir' do + relative_path_str('/usr', '/stuff/..').should == 'usr' + end + + it 'returns a relative path with a non clean base that matches a different dir' do + relative_path_str('/usr', '/stuff/../foo').should == '../usr' + end + + it 'returns current and pattern when only those patterns are used' do + relative_path_str('.', '.').should == '.' + relative_path_str('..', '..').should == '.' + relative_path_str('..', '.').should == '..' + end +end diff --git a/spec/rubyspec/library/pathname/relative_spec.rb b/spec/rubyspec/library/pathname/relative_spec.rb new file mode 100644 index 0000000000..a44d8c5a66 --- /dev/null +++ b/spec/rubyspec/library/pathname/relative_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#relative?" do + + it "returns false for the root directory" do + Pathname.new('/').relative?.should == false + end + + it "returns false for a dir starting with a slash" do + Pathname.new('/usr/local/bin').relative?.should == false + end + + it "returns true for a dir not starting with a slash" do + Pathname.new('fish').relative?.should == true + end + + it "returns true for a dir not starting with a slash" do + Pathname.new('fish/dog/cow').relative?.should == true + end + +end + diff --git a/spec/rubyspec/library/pathname/root_spec.rb b/spec/rubyspec/library/pathname/root_spec.rb new file mode 100644 index 0000000000..a5efcf69b4 --- /dev/null +++ b/spec/rubyspec/library/pathname/root_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#root?" do + + it "returns true for root directories" do + Pathname.new('/').root?.should == true + end + + it "returns false for empty string" do + Pathname.new('').root?.should == false + end + + it "returns false for a top level directory" do + Pathname.new('/usr').root?.should == false + end + + it "returns false for a top level with .. appended directory" do + Pathname.new('/usr/..').root?.should == false + end + + it "returns false for a directory below top level" do + Pathname.new('/usr/local/bin/').root?.should == false + end + +end + diff --git a/spec/rubyspec/library/pathname/sub_spec.rb b/spec/rubyspec/library/pathname/sub_spec.rb new file mode 100644 index 0000000000..36b6ebb247 --- /dev/null +++ b/spec/rubyspec/library/pathname/sub_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pathname' + +describe "Pathname#sub" do + + it "replaces the pattern with rest" do + Pathname.new('/usr/local/bin/').sub(/local/, 'fish').to_s.should == '/usr/fish/bin/' + end + + it "returns a new object" do + p = Pathname.new('/usr/local/bin/') + p.sub(/local/, 'fish').should_not == p + end + +end + diff --git a/spec/rubyspec/library/pp/pp_spec.rb b/spec/rubyspec/library/pp/pp_spec.rb new file mode 100644 index 0000000000..3a0135270a --- /dev/null +++ b/spec/rubyspec/library/pp/pp_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'pp' + +describe "PP.pp" do + it 'works with default arguments' do + array = [1, 2, 3] + + lambda { + PP.pp array + }.should output "[1, 2, 3]\n" + end + + it 'allows specifying out explicitly' do + array = [1, 2, 3] + other_out = IOStub.new + + lambda { + PP.pp array, other_out + }.should output "" # no output on stdout + + other_out.to_s.should == "[1, 2, 3]\n" + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/prime/each_spec.rb b/spec/rubyspec/library/prime/each_spec.rb new file mode 100644 index 0000000000..bccccfdcdc --- /dev/null +++ b/spec/rubyspec/library/prime/each_spec.rb @@ -0,0 +1,167 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'prime' + +describe :prime_each, shared: true do + before :each do + ScratchPad.record [] + end + + it "enumerates primes" do + primes = Prime.instance + result = [] + + primes.each { |p| + result << p + break if p > 10 + } + + result.should == [2, 3, 5, 7, 11] + end + + it "yields ascending primes to the block" do + previous = 1 + @object.each do |prime| + break if prime > 1000 + ScratchPad << prime + prime.should > previous + previous = prime + end + + all_prime = true + ScratchPad.recorded.all? do |prime| + all_prime &&= (2..Math.sqrt(prime)).all? { |d| prime % d != 0 } + end + + all_prime.should be_true + end + + it "returns the last evaluated expression in the passed block" do + @object.each { break :value }.should equal(:value) + end + + describe "when not passed a block" do + before :each do + @prime_enum = @object.each + end + + it "returns an object that is Enumerable" do + @prime_enum.each.should be_kind_of(Enumerable) + end + + it "returns an object that responds to #with_index" do + @prime_enum.should respond_to(:with_index) + end + + it "returns an object that responds to #with_object" do + @prime_enum.should respond_to(:with_object) + end + + it "returns an object that responds to #next" do + @prime_enum.should respond_to(:next) + end + + it "returns an object that responds to #rewind" do + @prime_enum.should respond_to(:rewind) + end + + it "yields primes starting at 2 independent of prior enumerators" do + @prime_enum.next.should == 2 + @prime_enum.next.should == 3 + + @object.each { |prime| break prime }.should == 2 + end + + it "returns an enumerator that yields previous primes when #rewind is called" do + @prime_enum.next.should == 2 + @prime_enum.next.should == 3 + @prime_enum.rewind + @prime_enum.next.should == 2 + end + + it "returns independent enumerators" do + enum = @object.each + enum.next.should == 2 + enum.next.should == 3 + + @prime_enum.next.should == 2 + + enum.next.should == 5 + end + end +end + +describe :prime_each_with_arguments, shared: true do + before :each do + ScratchPad.record [] + end + + it "yields ascending primes less than or equal to the argument" do + bound = 1000 + previous = 1 + @object.each(bound) do |prime| + ScratchPad << prime + prime.should > previous + previous = prime + end + + ScratchPad.recorded.all? do |prime| + (2..Math.sqrt(prime)).all? { |d| prime % d != 0 } + end.should be_true + + ScratchPad.recorded.all? { |prime| prime <= bound }.should be_true + end + + it "returns nil when no prime is generated" do + @object.each(1) { :value }.should be_nil + end + + it "yields primes starting at 2 independent of prior enumeration" do + @object.each(10) { |prime| prime }.should == 7 + @object.each(10) { |prime| break prime }.should == 2 + end + + it "accepts a pseudo-prime generator as the second argument" do + generator = mock('very bad pseudo-prime generator') + generator.should_receive(:upper_bound=).with(100) + generator.should_receive(:each).and_yield(2).and_yield(3).and_yield(4) + + @object.each(100, generator) { |prime| ScratchPad << prime } + ScratchPad.recorded.should == [2, 3, 4] + end + + describe "when not passed a block" do + it "returns an object that returns primes less than or equal to the bound" do + bound = 100 + @object.each(bound).all? { |prime| prime <= bound }.should be_true + end + end +end + +describe "Prime.each" do + it_behaves_like :prime_each, :each, Prime +end + +describe "Prime.each" do + it_behaves_like :prime_each_with_arguments, :each, Prime +end + +describe "Prime#each with Prime.instance" do + it_behaves_like :prime_each, :each, Prime.instance +end + +describe "Prime#each with Prime.instance" do + it_behaves_like :prime_each_with_arguments, :each, Prime.instance +end + +describe "Prime#each with Prime.instance" do + before :each do + @object = Prime.instance + end + + it_behaves_like :prime_each, :each + + it "resets the enumerator with each call" do + @object.each { |prime| break if prime > 10 } + @object.each { |prime| break prime }.should == 2 + end +end diff --git a/spec/rubyspec/library/prime/instance_spec.rb b/spec/rubyspec/library/prime/instance_spec.rb new file mode 100644 index 0000000000..64497c1b7c --- /dev/null +++ b/spec/rubyspec/library/prime/instance_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'prime' + +describe "Prime.instance" do + it "returns a object representing the set of prime numbers" do + Prime.instance.should be_kind_of(Prime) + end + + it "returns a object with no obsolete features" do + Prime.instance.should_not respond_to(:succ) + Prime.instance.should_not respond_to(:next) + end + + it "does not complain anything" do + lambda { Prime.instance }.should_not complain + end + + it "raises a ArgumentError when is called with some arguments" do + lambda { Prime.instance(1) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/prime/int_from_prime_division_spec.rb b/spec/rubyspec/library/prime/int_from_prime_division_spec.rb new file mode 100644 index 0000000000..df98976f5f --- /dev/null +++ b/spec/rubyspec/library/prime/int_from_prime_division_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'prime' + +describe "Prime.int_from_prime_division" do + it "returns the product of the given factorization" do + Prime.int_from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]). + should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3 + end + + it "returns 1 for an empty factorization" do + Prime.int_from_prime_division([]).should == 1 + end +end diff --git a/spec/rubyspec/library/prime/integer/each_prime_spec.rb b/spec/rubyspec/library/prime/integer/each_prime_spec.rb new file mode 100644 index 0000000000..e1b0092762 --- /dev/null +++ b/spec/rubyspec/library/prime/integer/each_prime_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'prime' + +describe "Integer.each_prime" do + it "is transferred to Prime.each" do + Prime.should_receive(:each).with(100).and_yield(2).and_yield(3).and_yield(5) + yielded = [] + Integer.each_prime(100) do |prime| + yielded << prime + end + yielded.should == [2,3,5] + end +end diff --git a/spec/rubyspec/library/prime/integer/from_prime_division_spec.rb b/spec/rubyspec/library/prime/integer/from_prime_division_spec.rb new file mode 100644 index 0000000000..6ef98a2ecf --- /dev/null +++ b/spec/rubyspec/library/prime/integer/from_prime_division_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'prime' + +describe "Integer.from_prime_division" do + it "returns the product of the given factorization" do + Integer.from_prime_division([[2,3], [3,3], [5,3], [7,3], [11,3], [13,3], [17,3]]). + should == 2**3 * 3**3 * 5**3 * 7**3 * 11**3 * 13**3 * 17**3 + end + + it "returns 1 for an empty factorization" do + Integer.from_prime_division([]).should == 1 + end +end diff --git a/spec/rubyspec/library/prime/integer/prime_division_spec.rb b/spec/rubyspec/library/prime/integer/prime_division_spec.rb new file mode 100644 index 0000000000..db137778ea --- /dev/null +++ b/spec/rubyspec/library/prime/integer/prime_division_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'prime' + +describe "Integer#prime_division" do + it "returns an array of a prime factor and a corresponding exponent" do + (2*3*5*7*11*13*17).prime_division.should == + [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]] + end + + it "returns an empty array for 1" do + 1.prime_division.should == [] + end + it "returns an empty array for -1" do + -1.prime_division.should == [[-1, 1]] + end + it "raises ZeroDivisionError for 0" do + lambda { 0.prime_division }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/library/prime/integer/prime_spec.rb b/spec/rubyspec/library/prime/integer/prime_spec.rb new file mode 100644 index 0000000000..ba869ba60e --- /dev/null +++ b/spec/rubyspec/library/prime/integer/prime_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'prime' + +describe "Integer#prime?" do + it "returns a true value for prime numbers" do + 2.prime?.should be_true + 3.prime?.should be_true + (2**31-1).prime?.should be_true # 8th Mersenne prime (M8) + end + + it "returns a false value for composite numbers" do + 4.prime?.should be_false + 15.prime?.should be_false + (2**32-1).prime?.should be_false + ( (2**17-1)*(2**19-1) ).prime?.should be_false # M6*M7 + end +end diff --git a/spec/rubyspec/library/prime/next_spec.rb b/spec/rubyspec/library/prime/next_spec.rb new file mode 100644 index 0000000000..afff353d6e --- /dev/null +++ b/spec/rubyspec/library/prime/next_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/next', __FILE__) +require 'prime' + +describe "Prime#next" do + it_behaves_like :prime_next, :next +end diff --git a/spec/rubyspec/library/prime/prime_division_spec.rb b/spec/rubyspec/library/prime/prime_division_spec.rb new file mode 100644 index 0000000000..faca8bda02 --- /dev/null +++ b/spec/rubyspec/library/prime/prime_division_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'prime' + +describe "Prime.prime_division" do + it "returns an array of a prime factor and a corresponding exponent" do + Prime.prime_division(2*3*5*7*11*13*17).should == + [[2,1], [3,1], [5,1], [7,1], [11,1], [13,1], [17,1]] + end + + it "returns an empty array for 1" do + Prime.prime_division(1).should == [] + end + + it "returns [[-1, 1]] for -1" do + Prime.prime_division(-1).should == [[-1, 1]] + end + + it "includes [[-1, 1]] in the divisors of a negative number" do + Prime.prime_division(-10).should include([-1, 1]) + end + + it "raises ZeroDivisionError for 0" do + lambda { Prime.prime_division(0) }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/library/prime/prime_spec.rb b/spec/rubyspec/library/prime/prime_spec.rb new file mode 100644 index 0000000000..7a41496f7f --- /dev/null +++ b/spec/rubyspec/library/prime/prime_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'prime' + +describe "Prime#prime?" do + it "returns a true value for prime numbers" do + Prime.prime?(2).should be_true + Prime.prime?(3).should be_true + Prime.prime?(2**31-1).should be_true # 8th Mersenne prime (M8) + end + + it "returns a false value for composite numbers" do + Prime.prime?(4).should be_false + Prime.prime?(15).should be_false + Prime.prime?(2**32-1).should be_false + Prime.prime?( (2**17-1)*(2**19-1) ).should be_false # M6*M7 + end +end diff --git a/spec/rubyspec/library/prime/shared/next.rb b/spec/rubyspec/library/prime/shared/next.rb new file mode 100644 index 0000000000..f79b2c051e --- /dev/null +++ b/spec/rubyspec/library/prime/shared/next.rb @@ -0,0 +1,8 @@ +describe :prime_next, shared: true do + it "returns the element at the current position and moves forward" do + p = Prime.instance.each + p.next.should == 2 + p.next.should == 3 + p.next.next.should == 6 + end +end diff --git a/spec/rubyspec/library/prime/succ_spec.rb b/spec/rubyspec/library/prime/succ_spec.rb new file mode 100644 index 0000000000..85d22ee654 --- /dev/null +++ b/spec/rubyspec/library/prime/succ_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/next', __FILE__) +require 'prime' + +describe "Prime#succ" do + it_behaves_like :prime_next, :succ +end diff --git a/spec/rubyspec/library/readline/basic_quote_characters_spec.rb b/spec/rubyspec/library/readline/basic_quote_characters_spec.rb new file mode 100644 index 0000000000..7d25df2047 --- /dev/null +++ b/spec/rubyspec/library/readline/basic_quote_characters_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../spec_helper', __FILE__) + +platform_is_not :darwin do + with_feature :readline do + describe "Readline.basic_quote_characters" do + it "returns not nil" do + Readline.basic_quote_characters.should_not be_nil + end + end + + describe "Readline.basic_quote_characters=" do + it "returns the passed string" do + Readline.basic_quote_characters = "test" + Readline.basic_quote_characters.should == "test" + end + end + end +end diff --git a/spec/rubyspec/library/readline/basic_word_break_characters_spec.rb b/spec/rubyspec/library/readline/basic_word_break_characters_spec.rb new file mode 100644 index 0000000000..c2ec6cede7 --- /dev/null +++ b/spec/rubyspec/library/readline/basic_word_break_characters_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline.basic_word_break_characters" do + it "returns not nil" do + Readline.basic_word_break_characters.should_not be_nil + end + end + + describe "Readline.basic_word_break_characters=" do + it "returns the passed string" do + Readline.basic_word_break_characters = "test" + Readline.basic_word_break_characters.should == "test" + end + end +end diff --git a/spec/rubyspec/library/readline/completer_quote_characters_spec.rb b/spec/rubyspec/library/readline/completer_quote_characters_spec.rb new file mode 100644 index 0000000000..a836a0591e --- /dev/null +++ b/spec/rubyspec/library/readline/completer_quote_characters_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline.completer_quote_characters" do + it "returns nil" do + Readline.completer_quote_characters.should be_nil + end + end + + describe "Readline.completer_quote_characters=" do + it "returns the passed string" do + Readline.completer_quote_characters = "test" + Readline.completer_quote_characters.should == "test" + end + end +end diff --git a/spec/rubyspec/library/readline/completer_word_break_characters_spec.rb b/spec/rubyspec/library/readline/completer_word_break_characters_spec.rb new file mode 100644 index 0000000000..2e0cc277a6 --- /dev/null +++ b/spec/rubyspec/library/readline/completer_word_break_characters_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline.completer_word_break_characters" do + it "returns nil" do + Readline.completer_word_break_characters.should be_nil + end + end + + describe "Readline.completer_word_break_characters=" do + it "returns the passed string" do + Readline.completer_word_break_characters = "test" + Readline.completer_word_break_characters.should == "test" + end + end +end diff --git a/spec/rubyspec/library/readline/completion_append_character_spec.rb b/spec/rubyspec/library/readline/completion_append_character_spec.rb new file mode 100644 index 0000000000..ee41abea05 --- /dev/null +++ b/spec/rubyspec/library/readline/completion_append_character_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline.completion_append_character" do + it "returns not nil" do + Readline.completion_append_character.should_not be_nil + end + end + + describe "Readline.completion_append_character=" do + it "returns the first character of the passed string" do + Readline.completion_append_character = "test" + Readline.completion_append_character.should == "t" + end + end +end diff --git a/spec/rubyspec/library/readline/completion_case_fold_spec.rb b/spec/rubyspec/library/readline/completion_case_fold_spec.rb new file mode 100644 index 0000000000..604460869e --- /dev/null +++ b/spec/rubyspec/library/readline/completion_case_fold_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline.completion_case_fold" do + it "returns nil" do + Readline.completion_case_fold.should be_nil + end + end + + describe "Readline.completion_case_fold=" do + it "returns the passed boolean" do + Readline.completion_case_fold = true + Readline.completion_case_fold.should == true + Readline.completion_case_fold = false + Readline.completion_case_fold.should == false + end + end +end diff --git a/spec/rubyspec/library/readline/completion_proc_spec.rb b/spec/rubyspec/library/readline/completion_proc_spec.rb new file mode 100644 index 0000000000..e525fbd191 --- /dev/null +++ b/spec/rubyspec/library/readline/completion_proc_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline.completion_proc" do + it "returns nil" do + Readline.completion_proc.should be_nil + end + end + + describe "Readline.completion_proc=" do + it "returns the passed Proc" do + proc = Proc.new do |e| + end + Readline.completion_proc = proc + Readline.completion_proc.should == proc + end + + it "returns an ArgumentError if not given an Proc or #call" do + lambda { Readline.completion_proc = "test" }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/library/readline/constants_spec.rb b/spec/rubyspec/library/readline/constants_spec.rb new file mode 100644 index 0000000000..226a3509b9 --- /dev/null +++ b/spec/rubyspec/library/readline/constants_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + # Note: additional specs for HISTORY are in 'history' subdir. + describe "Readline::HISTORY" do + it "is defined" do + Readline.const_defined?(:HISTORY).should == true + end + end + + describe "Readline::VERSION" do + it "is defined and is a non-empty String" do + Readline.const_defined?(:VERSION).should == true + Readline::VERSION.should be_kind_of(String) + Readline::VERSION.should_not be_empty + end + end +end diff --git a/spec/rubyspec/library/readline/emacs_editing_mode_spec.rb b/spec/rubyspec/library/readline/emacs_editing_mode_spec.rb new file mode 100644 index 0000000000..6c2e84b08f --- /dev/null +++ b/spec/rubyspec/library/readline/emacs_editing_mode_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../spec_helper', __FILE__) + +platform_is_not :darwin do + with_feature :readline do + describe "Readline.emacs_editing_mode" do + it "returns nil" do + Readline.emacs_editing_mode.should be_nil + end + end + end +end diff --git a/spec/rubyspec/library/readline/filename_quote_characters_spec.rb b/spec/rubyspec/library/readline/filename_quote_characters_spec.rb new file mode 100644 index 0000000000..abee5e9700 --- /dev/null +++ b/spec/rubyspec/library/readline/filename_quote_characters_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../spec_helper', __FILE__) + +platform_is_not :darwin do + with_feature :readline do + describe "Readline.filename_quote_characters" do + it "returns nil" do + Readline.filename_quote_characters.should be_nil + end + end + + describe "Readline.filename_quote_characters=" do + it "returns the passed string" do + Readline.filename_quote_characters = "test" + Readline.filename_quote_characters.should == "test" + end + end + end +end diff --git a/spec/rubyspec/library/readline/history/append_spec.rb b/spec/rubyspec/library/readline/history/append_spec.rb new file mode 100644 index 0000000000..3ea0bbf57f --- /dev/null +++ b/spec/rubyspec/library/readline/history/append_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.<<" do + it "appends the given Object to the history" do + Readline::HISTORY << "1" + Readline::HISTORY.size.should == 1 + + Readline::HISTORY << "2" + Readline::HISTORY.size.should == 2 + + Readline::HISTORY.pop.should == "2" + Readline::HISTORY.pop.should == "1" + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("Converted to String") + obj.should_receive(:to_str).and_return("converted") + + Readline::HISTORY << obj + Readline::HISTORY.pop.should == "converted" + end + + it "raises a TypeError when the passed Object can't be converted to a String" do + lambda { Readline::HISTORY << mock("Object") }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/library/readline/history/delete_at_spec.rb b/spec/rubyspec/library/readline/history/delete_at_spec.rb new file mode 100644 index 0000000000..38f180ca08 --- /dev/null +++ b/spec/rubyspec/library/readline/history/delete_at_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.delete_at" do + it "deletes and returns the history entry at the specified index" do + Readline::HISTORY.push("1", "2", "3") + + Readline::HISTORY.delete_at(1).should == "2" + Readline::HISTORY.size.should == 2 + + Readline::HISTORY.delete_at(1).should == "3" + Readline::HISTORY.size.should == 1 + + Readline::HISTORY.delete_at(0).should == "1" + Readline::HISTORY.size.should == 0 + + + Readline::HISTORY.push("1", "2", "3", "4") + + Readline::HISTORY.delete_at(-2).should == "3" + Readline::HISTORY.size.should == 3 + + Readline::HISTORY.delete_at(-2).should == "2" + Readline::HISTORY.size.should == 2 + + Readline::HISTORY.delete_at(0).should == "1" + Readline::HISTORY.size.should == 1 + + Readline::HISTORY.delete_at(0).should == "4" + Readline::HISTORY.size.should == 0 + end + + it "raises an IndexError when the given index is greater than the history size" do + lambda { Readline::HISTORY.delete_at(10) }.should raise_error(IndexError) + lambda { Readline::HISTORY.delete_at(-10) }.should raise_error(IndexError) + end + + it "taints the returned strings" do + Readline::HISTORY.push("1", "2", "3") + Readline::HISTORY.delete_at(0).tainted?.should be_true + Readline::HISTORY.delete_at(0).tainted?.should be_true + Readline::HISTORY.delete_at(0).tainted?.should be_true + end + end +end diff --git a/spec/rubyspec/library/readline/history/each_spec.rb b/spec/rubyspec/library/readline/history/each_spec.rb new file mode 100644 index 0000000000..5057e04970 --- /dev/null +++ b/spec/rubyspec/library/readline/history/each_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.each" do + before :each do + Readline::HISTORY.push("1", "2", "3") + end + + after :each do + Readline::HISTORY.pop + Readline::HISTORY.pop + Readline::HISTORY.pop + end + + it "yields each item in the history" do + result = [] + Readline::HISTORY.each do |x| + result << x + end + result.should == ["1", "2", "3"] + end + + it "yields tainted Objects" do + Readline::HISTORY.each do |x| + x.tainted?.should be_true + end + end + end +end diff --git a/spec/rubyspec/library/readline/history/element_reference_spec.rb b/spec/rubyspec/library/readline/history/element_reference_spec.rb new file mode 100644 index 0000000000..c656179ddd --- /dev/null +++ b/spec/rubyspec/library/readline/history/element_reference_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.[]" do + before :each do + Readline::HISTORY.push("1", "2", "3") + end + + after :each do + Readline::HISTORY.pop + Readline::HISTORY.pop + Readline::HISTORY.pop + end + + it "returns tainted objects" do + Readline::HISTORY[0].tainted?.should be_true + Readline::HISTORY[1].tainted?.should be_true + end + + it "returns the history item at the passed index" do + Readline::HISTORY[0].should == "1" + Readline::HISTORY[1].should == "2" + Readline::HISTORY[2].should == "3" + + Readline::HISTORY[-1].should == "3" + Readline::HISTORY[-2].should == "2" + Readline::HISTORY[-3].should == "1" + end + + it "raises an IndexError when there is no item at the passed index" do + lambda { Readline::HISTORY[-10] }.should raise_error(IndexError) + lambda { Readline::HISTORY[-9] }.should raise_error(IndexError) + lambda { Readline::HISTORY[-8] }.should raise_error(IndexError) + + lambda { Readline::HISTORY[8] }.should raise_error(IndexError) + lambda { Readline::HISTORY[9] }.should raise_error(IndexError) + lambda { Readline::HISTORY[10] }.should raise_error(IndexError) + end + end +end diff --git a/spec/rubyspec/library/readline/history/element_set_spec.rb b/spec/rubyspec/library/readline/history/element_set_spec.rb new file mode 100644 index 0000000000..8d6fe196ef --- /dev/null +++ b/spec/rubyspec/library/readline/history/element_set_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.[]=" do + before :each do + Readline::HISTORY.push("1", "2", "3") + end + + after :each do + Readline::HISTORY.pop + Readline::HISTORY.pop + Readline::HISTORY.pop + end + + it "returns the new value for the passed index" do + (Readline::HISTORY[1] = "second test").should == "second test" + end + + it "raises an IndexError when there is no item at the passed positive index" do + lambda { Readline::HISTORY[10] = "test" }.should raise_error(IndexError) + end + + it "sets the item at the given index" do + Readline::HISTORY[0] = "test" + Readline::HISTORY[0].should == "test" + + Readline::HISTORY[1] = "second test" + Readline::HISTORY[1].should == "second test" + end + + it "raises an IndexError when there is no item at the passed negative index" do + lambda { Readline::HISTORY[10] = "test" }.should raise_error(IndexError) + end + end +end diff --git a/spec/rubyspec/library/readline/history/empty_spec.rb b/spec/rubyspec/library/readline/history/empty_spec.rb new file mode 100644 index 0000000000..92a4fcd063 --- /dev/null +++ b/spec/rubyspec/library/readline/history/empty_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.empty?" do + it "returns true when the history is empty" do + Readline::HISTORY.should be_empty + Readline::HISTORY.push("test") + Readline::HISTORY.should_not be_empty + Readline::HISTORY.pop + Readline::HISTORY.should be_empty + end + end +end diff --git a/spec/rubyspec/library/readline/history/history_spec.rb b/spec/rubyspec/library/readline/history/history_spec.rb new file mode 100644 index 0000000000..dfa6544036 --- /dev/null +++ b/spec/rubyspec/library/readline/history/history_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY" do + it "is extended with the Enumerable module" do + Readline::HISTORY.should be_kind_of(Enumerable) + end + end +end diff --git a/spec/rubyspec/library/readline/history/length_spec.rb b/spec/rubyspec/library/readline/history/length_spec.rb new file mode 100644 index 0000000000..6700d4f234 --- /dev/null +++ b/spec/rubyspec/library/readline/history/length_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + require File.expand_path('../shared/size', __FILE__) + + describe "Readline::HISTORY.length" do + it_behaves_like :readline_history_size, :length + end +end diff --git a/spec/rubyspec/library/readline/history/pop_spec.rb b/spec/rubyspec/library/readline/history/pop_spec.rb new file mode 100644 index 0000000000..34562dff3b --- /dev/null +++ b/spec/rubyspec/library/readline/history/pop_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.pop" do + it "returns nil when the history is empty" do + Readline::HISTORY.pop.should be_nil + end + + it "returns and removes the last item from the history" do + Readline::HISTORY.push("1", "2", "3") + Readline::HISTORY.size.should == 3 + + Readline::HISTORY.pop.should == "3" + Readline::HISTORY.size.should == 2 + + Readline::HISTORY.pop.should == "2" + Readline::HISTORY.size.should == 1 + + Readline::HISTORY.pop.should == "1" + Readline::HISTORY.size.should == 0 + end + + it "taints the returned strings" do + Readline::HISTORY.push("1", "2", "3") + Readline::HISTORY.pop.tainted?.should be_true + Readline::HISTORY.pop.tainted?.should be_true + Readline::HISTORY.pop.tainted?.should be_true + end + end +end diff --git a/spec/rubyspec/library/readline/history/push_spec.rb b/spec/rubyspec/library/readline/history/push_spec.rb new file mode 100644 index 0000000000..b59b17ed03 --- /dev/null +++ b/spec/rubyspec/library/readline/history/push_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.push" do + it "pushes all passed Objects into the history" do + Readline::HISTORY.push("1", "2", "3") + Readline::HISTORY.size.should == 3 + + Readline::HISTORY.pop.should == "3" + Readline::HISTORY.pop.should == "2" + Readline::HISTORY.pop.should == "1" + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("Converted to String") + obj.should_receive(:to_str).and_return("converted") + + Readline::HISTORY.push(obj) + Readline::HISTORY.pop.should == "converted" + end + + it "raises a TypeError when the passed Object can't be converted to a String" do + lambda { Readline::HISTORY.push(mock("Object")) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/library/readline/history/shared/size.rb b/spec/rubyspec/library/readline/history/shared/size.rb new file mode 100644 index 0000000000..1d6df86f78 --- /dev/null +++ b/spec/rubyspec/library/readline/history/shared/size.rb @@ -0,0 +1,14 @@ +describe :readline_history_size, shared: true do + it "returns the size of the history" do + Readline::HISTORY.send(@method).should == 0 + Readline::HISTORY.push("1", "2", "") + Readline::HISTORY.send(@method).should == 3 + + Readline::HISTORY.pop + Readline::HISTORY.send(@method).should == 2 + + Readline::HISTORY.pop + Readline::HISTORY.pop + Readline::HISTORY.send(@method).should == 0 + end +end diff --git a/spec/rubyspec/library/readline/history/shift_spec.rb b/spec/rubyspec/library/readline/history/shift_spec.rb new file mode 100644 index 0000000000..3d4782998d --- /dev/null +++ b/spec/rubyspec/library/readline/history/shift_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.shift" do + it "returns nil when the history is empty" do + Readline::HISTORY.shift.should be_nil + end + + it "returns and removes the first item from the history" do + Readline::HISTORY.push("1", "2", "3") + Readline::HISTORY.size.should == 3 + + Readline::HISTORY.shift.should == "1" + Readline::HISTORY.size.should == 2 + + Readline::HISTORY.shift.should == "2" + Readline::HISTORY.size.should == 1 + + Readline::HISTORY.shift.should == "3" + Readline::HISTORY.size.should == 0 + end + + it "taints the returned strings" do + Readline::HISTORY.push("1", "2", "3") + Readline::HISTORY.shift.tainted?.should be_true + Readline::HISTORY.shift.tainted?.should be_true + Readline::HISTORY.shift.tainted?.should be_true + end + end +end diff --git a/spec/rubyspec/library/readline/history/size_spec.rb b/spec/rubyspec/library/readline/history/size_spec.rb new file mode 100644 index 0000000000..815c68de27 --- /dev/null +++ b/spec/rubyspec/library/readline/history/size_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + require File.expand_path('../shared/size', __FILE__) + + describe "Readline::HISTORY.size" do + it_behaves_like :readline_history_size, :size + end +end diff --git a/spec/rubyspec/library/readline/history/to_s_spec.rb b/spec/rubyspec/library/readline/history/to_s_spec.rb new file mode 100644 index 0000000000..30ba5a1249 --- /dev/null +++ b/spec/rubyspec/library/readline/history/to_s_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline::HISTORY.to_s" do + it "returns 'HISTORY'" do + Readline::HISTORY.to_s.should == "HISTORY" + end + end +end diff --git a/spec/rubyspec/library/readline/readline_spec.rb b/spec/rubyspec/library/readline/readline_spec.rb new file mode 100644 index 0000000000..599f84dffd --- /dev/null +++ b/spec/rubyspec/library/readline/readline_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../spec_helper', __FILE__) + +with_feature :readline do + describe "Readline.readline" do + before :each do + @file = tmp('readline') + @out = tmp('out.txt') + touch(@file) { |f| + f.puts "test" + } + @options = { options: "-rreadline", args: [@out, "< #{@file}"] } + end + + after :each do + rm_r @file, @out + end + + # Somehow those specs block on Windows + platform_is_not :windows do + it "returns the input string" do + ruby_exe('File.write ARGV[0], Readline.readline', @options) + File.read(@out).should == "test" + end + + it "taints the returned strings" do + ruby_exe('File.write ARGV[0], Readline.readline.tainted?', @options) + File.read(@out).should == "true" + end + end + end +end diff --git a/spec/rubyspec/library/readline/spec_helper.rb b/spec/rubyspec/library/readline/spec_helper.rb new file mode 100644 index 0000000000..b5ac16d22a --- /dev/null +++ b/spec/rubyspec/library/readline/spec_helper.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +unless MSpec.retrieve(:features).key?(:readline) + begin + require 'readline' + rescue LoadError + else + # rb-readline behaves quite differently + if $".grep(/\brbreadline\.rb$/).empty? + MSpec.enable_feature :readline + end + end +end diff --git a/spec/rubyspec/library/readline/vi_editing_mode_spec.rb b/spec/rubyspec/library/readline/vi_editing_mode_spec.rb new file mode 100644 index 0000000000..db6d4387f8 --- /dev/null +++ b/spec/rubyspec/library/readline/vi_editing_mode_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../spec_helper', __FILE__) + +platform_is_not :darwin do + with_feature :readline do + describe "Readline.vi_editing_mode" do + it "returns nil" do + Readline.vi_editing_mode.should be_nil + end + end + end +end diff --git a/spec/rubyspec/library/resolv/get_address_spec.rb b/spec/rubyspec/library/resolv/get_address_spec.rb new file mode 100644 index 0000000000..cb71bb6ec4 --- /dev/null +++ b/spec/rubyspec/library/resolv/get_address_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'resolv' + +describe "Resolv#getaddress" do + platform_is_not :windows do + it "resolves localhost" do + res = Resolv.new([Resolv::Hosts.new]) + + lambda { + res.getaddress("localhost") + }.should_not raise_error(Resolv::ResolvError) + end + end + + it "raises ResolvError if the name can not be looked up" do + res = Resolv.new([]) + lambda { + res.getaddress("should.raise.error.") + }.should raise_error(Resolv::ResolvError) + end +end diff --git a/spec/rubyspec/library/resolv/get_addresses_spec.rb b/spec/rubyspec/library/resolv/get_addresses_spec.rb new file mode 100644 index 0000000000..2ab3d35eba --- /dev/null +++ b/spec/rubyspec/library/resolv/get_addresses_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'resolv' + +describe "Resolv#getaddresses" do + platform_is_not :windows do + it "resolves localhost" do + res = Resolv.new([Resolv::Hosts.new]) + + addresses = res.getaddresses("localhost") + addresses.should_not == nil + addresses.size.should > 0 + end + end +end diff --git a/spec/rubyspec/library/resolv/get_name_spec.rb b/spec/rubyspec/library/resolv/get_name_spec.rb new file mode 100644 index 0000000000..06a27e922a --- /dev/null +++ b/spec/rubyspec/library/resolv/get_name_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'resolv' + +describe "Resolv#getname" do + platform_is_not :windows do + it "resolves 127.0.0.1" do + lambda { + Resolv.getname("127.0.0.1") + }.should_not raise_error(Resolv::ResolvError) + end + end + + it "raises ResolvError when there is no result" do + res = Resolv.new([]) + lambda { + res.getname("should.raise.error") + }.should raise_error(Resolv::ResolvError) + end +end diff --git a/spec/rubyspec/library/resolv/get_names_spec.rb b/spec/rubyspec/library/resolv/get_names_spec.rb new file mode 100644 index 0000000000..f2bb08a653 --- /dev/null +++ b/spec/rubyspec/library/resolv/get_names_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'resolv' + +describe "Resolv#getnames" do + platform_is_not :windows do + it "resolves 127.0.0.1" do + res = Resolv.new([Resolv::Hosts.new]) + + names = res.getnames("127.0.0.1") + names.should_not == nil + names.size.should > 0 + end + end +end diff --git a/spec/rubyspec/library/rexml/attribute/clone_spec.rb b/spec/rubyspec/library/rexml/attribute/clone_spec.rb new file mode 100644 index 0000000000..df0d9ca466 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/clone_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#clone" do + it "returns a copy of this Attribute" do + orig = REXML::Attribute.new("name", "value&&") + orig.should == orig.clone + orig.clone.to_s.should == orig.to_s + orig.clone.to_string.should == orig.to_string + end +end diff --git a/spec/rubyspec/library/rexml/attribute/element_spec.rb b/spec/rubyspec/library/rexml/attribute/element_spec.rb new file mode 100644 index 0000000000..c518bb7b55 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/element_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#element" do + it "returns the parent element" do + e = REXML::Element.new "root" + + REXML::Attribute.new("name", "value", e).element.should == e + REXML::Attribute.new("name", "default_constructor").element.should == nil + end +end + +describe "REXML::Attribute#element=" do + it "sets the parent element" do + e = REXML::Element.new "root" + f = REXML::Element.new "temp" + a = REXML::Attribute.new("name", "value", e) + a.element.should == e + + a.element = f + a.element.should == f + end +end diff --git a/spec/rubyspec/library/rexml/attribute/equal_value_spec.rb b/spec/rubyspec/library/rexml/attribute/equal_value_spec.rb new file mode 100644 index 0000000000..2a7aa9e87e --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/equal_value_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#==" do + it "returns true if other has equal name and value" do + a1 = REXML::Attribute.new("foo", "bar") + a1.should == a1.clone + + a2 = REXML::Attribute.new("foo", "bar") + a1.should == a2 + + a3 = REXML::Attribute.new("foo", "bla") + a1.should_not == a3 + + a4 = REXML::Attribute.new("baz", "bar") + a1.should_not == a4 + end +end diff --git a/spec/rubyspec/library/rexml/attribute/hash_spec.rb b/spec/rubyspec/library/rexml/attribute/hash_spec.rb new file mode 100644 index 0000000000..a77c23aada --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/hash_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#hash" do + # These are not really complete, any idea on how to make them more + # "testable" will be appreciated. + it "returns a hashcode made of the name and value of self" do + a = REXML::Attribute.new("name", "value") + a.hash.should be_kind_of(Numeric) + b = REXML::Attribute.new(a) + a.hash.should == b.hash + end +end diff --git a/spec/rubyspec/library/rexml/attribute/initialize_spec.rb b/spec/rubyspec/library/rexml/attribute/initialize_spec.rb new file mode 100644 index 0000000000..637bd1b012 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/initialize_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#initialize" do + before :each do + @e = REXML::Element.new "root" + @name = REXML::Attribute.new("name", "Nicko") + @e.add_attribute @name + end + + it "receives two strings for name and value" do + @e.attributes["name"].should == "Nicko" + @e.add_attribute REXML::Attribute.new("last_name", nil) + @e.attributes["last_name"].should == "" + end + + it "receives an Attribute and clones it" do + copy = REXML::Attribute.new(@name) + copy.should == @name + end + + it "recives a parent node" do + last_name = REXML::Attribute.new("last_name", "McBrain", @e) + last_name.element.should == @e + + last_name = REXML::Attribute.new(@name, @e) + last_name.element.should == @e + end +end diff --git a/spec/rubyspec/library/rexml/attribute/inspect_spec.rb b/spec/rubyspec/library/rexml/attribute/inspect_spec.rb new file mode 100644 index 0000000000..bfc764f663 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/inspect_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#inspect" do + it "returns the name and value as a string" do + a = REXML::Attribute.new("my_name", "my_value") + a.inspect.should == "my_name='my_value'" + end + + it "accepts attributes with no value" do + a = REXML::Attribute.new("my_name") + a.inspect.should == "my_name=''" + end + + it "does not escape text" do + a = REXML::Attribute.new("&&", "<>") + a.inspect.should == "&&='<>'" + end +end + diff --git a/spec/rubyspec/library/rexml/attribute/namespace_spec.rb b/spec/rubyspec/library/rexml/attribute/namespace_spec.rb new file mode 100644 index 0000000000..5df9742cd3 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/namespace_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#namespace" do + it "returns the namespace url" do + e = REXML::Element.new("root") + e.add_attribute REXML::Attribute.new("xmlns:ns", "http://some_uri") + e.namespace("ns").should == "http://some_uri" + end + + it "returns nil if namespace is not defined" do + e = REXML::Element.new("root") + e.add_attribute REXML::Attribute.new("test", "value") + e.namespace("test").should == nil + e.namespace("ns").should == nil + end + + it "defaults arg to nil" do + e = REXML::Element.new("root") + e.add_attribute REXML::Attribute.new("xmlns:ns", "http://some_uri") + e.namespace.should == "" + e.namespace("ns").should == "http://some_uri" + end +end diff --git a/spec/rubyspec/library/rexml/attribute/node_type_spec.rb b/spec/rubyspec/library/rexml/attribute/node_type_spec.rb new file mode 100644 index 0000000000..d44695deff --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/node_type_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#node_type" do + it "always returns :attribute" do + attr = REXML::Attribute.new("foo", "bar") + attr.node_type.should == :attribute + REXML::Attribute.new(attr).node_type.should == :attribute + end +end diff --git a/spec/rubyspec/library/rexml/attribute/prefix_spec.rb b/spec/rubyspec/library/rexml/attribute/prefix_spec.rb new file mode 100644 index 0000000000..698c8d5d7b --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/prefix_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#prefix" do + it "returns the namespace of the Attribute" do + ans = REXML::Attribute.new("ns:someattr", "some_value") + out = REXML::Attribute.new("out:something", "some_other_value") + + ans.prefix.should == "ns" + out.prefix.should == "out" + end + + it "returns an empty string for Attributes with no prefixes" do + attr = REXML::Attribute.new("foo", "bar") + + attr.prefix.should == "" + end +end diff --git a/spec/rubyspec/library/rexml/attribute/remove_spec.rb b/spec/rubyspec/library/rexml/attribute/remove_spec.rb new file mode 100644 index 0000000000..5a08ef3a3c --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/remove_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#remove" do + before :each do + @e = REXML::Element.new "Root" + @attr = REXML::Attribute.new("foo", "bar") + end + + it "deletes this Attribute from parent" do + @e.add_attribute(@attr) + @e.attributes["foo"].should_not == nil + @attr.remove + @e.attributes["foo"].should == nil + end + + it "does not anything if element has no parent" do + lambda {@attr.remove}.should_not raise_error(Exception) + end +end diff --git a/spec/rubyspec/library/rexml/attribute/to_s_spec.rb b/spec/rubyspec/library/rexml/attribute/to_s_spec.rb new file mode 100644 index 0000000000..96831625b6 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/to_s_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#to_s" do + it "returns the value of the Attribute" do + REXML::Attribute.new("name", "some_value").to_s.should == "some_value" + end + + it "returns the escaped value if it was created from Attribute" do + orig = REXML::Attribute.new("name", "<&>") + copy = REXML::Attribute.new(orig) + copy.to_s.should == "<&>" + end +end diff --git a/spec/rubyspec/library/rexml/attribute/to_string_spec.rb b/spec/rubyspec/library/rexml/attribute/to_string_spec.rb new file mode 100644 index 0000000000..f8cc639a9d --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/to_string_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#to_string" do + it "returns the attribute as XML" do + attr = REXML::Attribute.new("name", "value") + attr_empty = REXML::Attribute.new("name") + attr_ns = REXML::Attribute.new("xmlns:ns", "http://uri") + + attr.to_string.should == "name='value'" + attr_empty.to_string.should == "name=''" + attr_ns.to_string.should == "xmlns:ns='http://uri'" + end +end + diff --git a/spec/rubyspec/library/rexml/attribute/value_spec.rb b/spec/rubyspec/library/rexml/attribute/value_spec.rb new file mode 100644 index 0000000000..912509cd75 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/value_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#value" do + it "returns the value of the Attribute unnormalized" do + attr = REXML::Attribute.new("name", "value") + attr_ents = REXML::Attribute.new("name", "<&>") + attr_empty = REXML::Attribute.new("name") + + attr.value.should == "value" + attr_ents.value.should == "<&>" + attr_empty.value.should == "" + end +end + diff --git a/spec/rubyspec/library/rexml/attribute/write_spec.rb b/spec/rubyspec/library/rexml/attribute/write_spec.rb new file mode 100644 index 0000000000..b6b59930e3 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/write_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#write" do + before :each do + @attr = REXML::Attribute.new("name", "Charlotte") + @s = "" + end + + it "writes the name and value to output" do + @attr.write(@s) + @s.should == "name='Charlotte'" + end + + it "currently ignores the second argument" do + @attr.write(@s, 3) + @s.should == "name='Charlotte'" + + @s = "" + @attr.write(@s, "foo") + @s.should == "name='Charlotte'" + end +end diff --git a/spec/rubyspec/library/rexml/attribute/xpath_spec.rb b/spec/rubyspec/library/rexml/attribute/xpath_spec.rb new file mode 100644 index 0000000000..6fb80ead37 --- /dev/null +++ b/spec/rubyspec/library/rexml/attribute/xpath_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attribute#xpath" do + + before :each do + @e = REXML::Element.new "root" + @attr = REXML::Attribute.new("year", "1989") + end + + it "returns the path for Attribute" do + @e.add_attribute @attr + @attr.xpath.should == "root/@year" + end + + it "raises an error if attribute has no parent" do + lambda { @attr.xpath }.should raise_error(Exception) + end +end + diff --git a/spec/rubyspec/library/rexml/attributes/add_spec.rb b/spec/rubyspec/library/rexml/attributes/add_spec.rb new file mode 100644 index 0000000000..72e3c4c823 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/add_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/add', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#add" do + it_behaves_like :rexml_attribute_add, :add +end diff --git a/spec/rubyspec/library/rexml/attributes/append_spec.rb b/spec/rubyspec/library/rexml/attributes/append_spec.rb new file mode 100644 index 0000000000..89f3fc3e81 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/add', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#<<" do + it_behaves_like :rexml_attribute_add, :<< +end diff --git a/spec/rubyspec/library/rexml/attributes/delete_all_spec.rb b/spec/rubyspec/library/rexml/attributes/delete_all_spec.rb new file mode 100644 index 0000000000..f11f0d66a3 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/delete_all_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#delete_all" do + before :each do + @e = REXML::Element.new("root") + end + + it "deletes all attributes that match name" do + uri = REXML::Attribute.new("uri", "http://something") + @e.attributes << uri + @e.attributes.delete_all("uri") + @e.attributes.should be_empty + @e.attributes["uri"].should == nil + end + + it "deletes all attributes that match name with a namespace" do + ns_uri = REXML::Attribute.new("xmlns:uri", "http://something_here_too") + @e.attributes << ns_uri + @e.attributes.delete_all("xmlns:uri") + @e.attributes.should be_empty + @e.attributes["xmlns:uri"].should == nil + end + + it "returns the removed attribute" do + uri = REXML::Attribute.new("uri", "http://something_here_too") + @e.attributes << uri + attrs = @e.attributes.delete_all("uri") + attrs.first.should == uri + end +end diff --git a/spec/rubyspec/library/rexml/attributes/delete_spec.rb b/spec/rubyspec/library/rexml/attributes/delete_spec.rb new file mode 100644 index 0000000000..1c02e5c03b --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/delete_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#delete" do + before :each do + @e = REXML::Element.new("root") + @name = REXML::Attribute.new("name", "Pepe") + end + + it "takes an attribute name and deletes the attribute" do + @e.attributes.delete("name") + @e.attributes["name"].should be_nil + @e.attributes.should be_empty + end + + it "takes an Attribute and deletes it" do + @e.attributes.delete(@name) + @e.attributes["name"].should be_nil + @e.attributes.should be_empty + end + + it "returns the element with the attribute removed" do + ret_val = @e.attributes.delete(@name) + ret_val.should == @e + ret_val.attributes.should be_empty + end +end diff --git a/spec/rubyspec/library/rexml/attributes/each_attribute_spec.rb b/spec/rubyspec/library/rexml/attributes/each_attribute_spec.rb new file mode 100644 index 0000000000..cd1649be21 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/each_attribute_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#each_attribute" do + it "iterates over the attributes yielding actual Attribute objects" do + e = REXML::Element.new("root") + name = REXML::Attribute.new("name", "Joe") + ns_uri = REXML::Attribute.new("xmlns:ns", "http://some_uri") + e.add_attribute name + e.add_attribute ns_uri + + attributes = [] + + e.attributes.each_attribute do |attr| + attributes << attr + end + + attributes = attributes.sort_by {|a| a.name } + attributes.first.should == name + attributes.last.should == ns_uri + end +end + + + diff --git a/spec/rubyspec/library/rexml/attributes/each_spec.rb b/spec/rubyspec/library/rexml/attributes/each_spec.rb new file mode 100644 index 0000000000..f49bc75c0d --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/each_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#each" do + before :each do + @e = REXML::Element.new("root") + @name = REXML::Attribute.new("name", "Joe") + @ns_uri = REXML::Attribute.new("xmlns:ns", "http://some_uri") + @e.add_attribute @name + @e.add_attribute @ns_uri + end + + it "iterates over the attributes yielding expanded-name/value" do + attributes = [] + @e.attributes.each do |attr| + attr.should be_kind_of(Array) + attributes << attr + end + attributes = attributes.sort_by {|a| a.first } + attributes.first.should == ["name", "Joe"] + attributes.last.should == ["xmlns:ns", "http://some_uri"] + end +end + + diff --git a/spec/rubyspec/library/rexml/attributes/element_reference_spec.rb b/spec/rubyspec/library/rexml/attributes/element_reference_spec.rb new file mode 100644 index 0000000000..0e85ecafe8 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/element_reference_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#[]" do + before :each do + @e = REXML::Element.new("root") + @lang = REXML::Attribute.new("language", "english") + @e.attributes << @lang + end + + it "returns the value of an attribute" do + @e.attributes["language"].should == "english" + end + + it "returns nil if the attribute does not exist" do + @e.attributes["chunky bacon"].should == nil + end +end + diff --git a/spec/rubyspec/library/rexml/attributes/element_set_spec.rb b/spec/rubyspec/library/rexml/attributes/element_set_spec.rb new file mode 100644 index 0000000000..659d259df6 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/element_set_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#[]=" do + before :each do + @e = REXML::Element.new("song") + @name = REXML::Attribute.new("name", "Holy Smoke!") + @e.attributes << @name + end + + it "sets an attribute" do + @e.attributes["author"] = "_why's foxes" + @e.attributes["author"].should == "_why's foxes" + end + + it "overwrites an existing attribute" do + @e.attributes["name"] = "Chunky Bacon" + @e.attributes["name"].should == "Chunky Bacon" + end + + it "deletes an attribute is value is nil" do + @e.attributes["name"] = nil + @e.attributes.length.should == 0 + end +end + diff --git a/spec/rubyspec/library/rexml/attributes/get_attribute_ns_spec.rb b/spec/rubyspec/library/rexml/attributes/get_attribute_ns_spec.rb new file mode 100644 index 0000000000..f4aeb76378 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/get_attribute_ns_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#get_attribute_ns" do + it "returns an attribute by name and namespace" do + e = REXML::Element.new("root") + attr = REXML::Attribute.new("xmlns:ns", "http://some_url") + e.attributes << attr + attr.prefix.should == "xmlns" + # This might be a bug in Attribute, commenting until those specs + # are ready + # e.attributes.get_attribute_ns(attr.prefix, "name").should == "http://some_url" + end +end diff --git a/spec/rubyspec/library/rexml/attributes/get_attribute_spec.rb b/spec/rubyspec/library/rexml/attributes/get_attribute_spec.rb new file mode 100644 index 0000000000..b7d83f5944 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/get_attribute_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#get_attribute" do + before :each do + @e = REXML::Element.new("root") + @name = REXML::Attribute.new("name", "Dave") + @e.attributes << @name + end + + it "fetches an attributes" do + @e.attributes.get_attribute("name").should == @name + end + + it "fetches an namespaced attribute" do + ns_name = REXML::Attribute.new("im:name", "Murray") + @e.attributes << ns_name + @e.attributes.get_attribute("name").should == @name + @e.attributes.get_attribute("im:name").should == ns_name + end + + it "returns an Attribute" do + @e.attributes.get_attribute("name").should be_kind_of(REXML::Attribute) + end + + it "returns nil if it attribute does not exist" do + @e.attributes.get_attribute("fake").should be_nil + end +end diff --git a/spec/rubyspec/library/rexml/attributes/initialize_spec.rb b/spec/rubyspec/library/rexml/attributes/initialize_spec.rb new file mode 100644 index 0000000000..2bf59b1f76 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/initialize_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#initialize" do + it "is auto initialized by Element" do + e = REXML::Element.new "root" + e.attributes.should be_kind_of(REXML::Attributes) + + e.attributes << REXML::Attribute.new("name", "Paul") + e.attributes["name"].should == "Paul" + end + + it "receives a parent node" do + e = REXML::Element.new "root" + e.attributes << REXML::Attribute.new("name", "Vic") + e.attributes["name"].should == "Vic" + end +end diff --git a/spec/rubyspec/library/rexml/attributes/length_spec.rb b/spec/rubyspec/library/rexml/attributes/length_spec.rb new file mode 100644 index 0000000000..cd68461e34 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#length" do + it_behaves_like :rexml_attribute_length, :length +end diff --git a/spec/rubyspec/library/rexml/attributes/namespaces_spec.rb b/spec/rubyspec/library/rexml/attributes/namespaces_spec.rb new file mode 100644 index 0000000000..41486d0316 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/namespaces_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#namespaces" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/rexml/attributes/prefixes_spec.rb b/spec/rubyspec/library/rexml/attributes/prefixes_spec.rb new file mode 100644 index 0000000000..9eca67b5ff --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/prefixes_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#prefixes" do + before :each do + @e = REXML::Element.new("root") + a1 = REXML::Attribute.new("xmlns:a", "bar") + a2 = REXML::Attribute.new("xmlns:b", "bla") + a3 = REXML::Attribute.new("xmlns:c", "baz") + @e.attributes << a1 + @e.attributes << a2 + @e.attributes << a3 + + @e.attributes << REXML::Attribute.new("xmlns", "foo") + end + + it "returns an array with the prefixes of each attribute" do + @e.attributes.prefixes.sort.should == ["a", "b", "c"] + end + + it "does not include the default namespace" do + @e.attributes.prefixes.include?("xmlns").should == false + end +end diff --git a/spec/rubyspec/library/rexml/attributes/shared/add.rb b/spec/rubyspec/library/rexml/attributes/shared/add.rb new file mode 100644 index 0000000000..872f149f45 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/shared/add.rb @@ -0,0 +1,17 @@ +describe :rexml_attribute_add, shared: true do + before :each do + @e = REXML::Element.new("root") + @attr = REXML::Attributes.new(@e) + @name = REXML::Attribute.new("name", "Joe") + end + + it "adds an attribute" do + @attr.send(@method, @name) + @attr["name"].should == "Joe" + end + + it "replaces an existing attribute" do + @attr.send(@method, REXML::Attribute.new("name", "Bruce")) + @attr["name"].should == "Bruce" + end +end diff --git a/spec/rubyspec/library/rexml/attributes/shared/length.rb b/spec/rubyspec/library/rexml/attributes/shared/length.rb new file mode 100644 index 0000000000..94681882a6 --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/shared/length.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe :rexml_attribute_length, shared: true do + it "returns the number of attributes" do + e = REXML::Element.new("root") + e.attributes.send(@method).should == 0 + + e.attributes << REXML::Attribute.new("name", "John") + e.attributes << REXML::Attribute.new("another_name", "Leo") + e.attributes.send(@method).should == 2 + end +end diff --git a/spec/rubyspec/library/rexml/attributes/size_spec.rb b/spec/rubyspec/library/rexml/attributes/size_spec.rb new file mode 100644 index 0000000000..761fcc1d5b --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#size" do + it_behaves_like :rexml_attribute_length, :size +end diff --git a/spec/rubyspec/library/rexml/attributes/to_a_spec.rb b/spec/rubyspec/library/rexml/attributes/to_a_spec.rb new file mode 100644 index 0000000000..a3de48cf1c --- /dev/null +++ b/spec/rubyspec/library/rexml/attributes/to_a_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Attributes#to_a" do + it "returns an array with the attributes" do + e = REXML::Element.new("root") + name = REXML::Attribute.new("name", "Dave") + last = REXML::Attribute.new("last_name", "Murray") + + e.attributes << name + e.attributes << last + + e.attributes.to_a.sort{|a,b|a.to_s<=>b.to_s}.should == [name, last] + end + + it "returns an empty array if it has no attributes" do + REXML::Element.new("root").attributes.to_a.should == [] + end +end + diff --git a/spec/rubyspec/library/rexml/cdata/clone_spec.rb b/spec/rubyspec/library/rexml/cdata/clone_spec.rb new file mode 100644 index 0000000000..15dcf13a04 --- /dev/null +++ b/spec/rubyspec/library/rexml/cdata/clone_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::CData#clone" do + it "makes a copy of itself" do + c = REXML::CData.new("some text") + c.clone.to_s.should == c.to_s + c.clone.should == c + end +end diff --git a/spec/rubyspec/library/rexml/cdata/initialize_spec.rb b/spec/rubyspec/library/rexml/cdata/initialize_spec.rb new file mode 100644 index 0000000000..bc0bc5cd6d --- /dev/null +++ b/spec/rubyspec/library/rexml/cdata/initialize_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::CData#initialize" do + it "creates a new CData object" do + c = REXML::CData.new("some text") + c.should be_kind_of(REXML::CData) + c.should be_kind_of(REXML::Text) + end + + it "respects whitespace if whitespace is true" do + c = REXML::CData.new("whitespace test", true) + c1 = REXML::CData.new("whitespace test", false) + + c.to_s.should == "whitespace test" + c1.to_s.should == "whitespace test" + end + + it "receives parent as third argument" do + e = REXML::Element.new("root") + REXML::CData.new("test", true, e) + e.to_s.should == "" + end +end diff --git a/spec/rubyspec/library/rexml/cdata/shared/to_s.rb b/spec/rubyspec/library/rexml/cdata/shared/to_s.rb new file mode 100644 index 0000000000..f8c4951c95 --- /dev/null +++ b/spec/rubyspec/library/rexml/cdata/shared/to_s.rb @@ -0,0 +1,11 @@ +describe :rexml_cdata_to_s, shared: true do + it "returns the contents of the CData" do + c = REXML::CData.new("some text") + c.send(@method).should == "some text" + end + + it "does not escape text" do + c1 = REXML::CData.new("some& text\n") + c1.send(@method).should == "some& text\n" + end +end diff --git a/spec/rubyspec/library/rexml/cdata/to_s_spec.rb b/spec/rubyspec/library/rexml/cdata/to_s_spec.rb new file mode 100644 index 0000000000..3fb233cdaf --- /dev/null +++ b/spec/rubyspec/library/rexml/cdata/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) +require 'rexml/document' + +describe "REXML::CData#to_s" do + it_behaves_like :rexml_cdata_to_s, :to_s +end diff --git a/spec/rubyspec/library/rexml/cdata/value_spec.rb b/spec/rubyspec/library/rexml/cdata/value_spec.rb new file mode 100644 index 0000000000..f9af73c0f6 --- /dev/null +++ b/spec/rubyspec/library/rexml/cdata/value_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_s', __FILE__) +require 'rexml/document' + +describe "REXML::CData#value" do + it_behaves_like :rexml_cdata_to_s, :value +end diff --git a/spec/rubyspec/library/rexml/document/add_element_spec.rb b/spec/rubyspec/library/rexml/document/add_element_spec.rb new file mode 100644 index 0000000000..03c95727e2 --- /dev/null +++ b/spec/rubyspec/library/rexml/document/add_element_spec.rb @@ -0,0 +1,31 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#add_element" do + it "adds arg1 with attributes arg2 as root node" do + d = REXML::Document.new + e = REXML::Element.new("root") + d.add_element e + d.root.should == e + end + + it "sets arg2 as arg1's attributes" do + d = REXML::Document.new + e = REXML::Element.new("root") + attr = {"foo" => "bar"} + d.add_element(e,attr) + d.root.attributes["foo"].should == attr["foo"] + end + + it "accepts a node name as arg1 and adds it as root" do + d = REXML::Document.new + d.add_element "foo" + d.root.name.should == "foo" + end + + it "sets arg1's context to the root's context" do + d = REXML::Document.new("", {"foo" => "bar"}) + d.add_element "foo" + d.root.context.should == d.context + end +end diff --git a/spec/rubyspec/library/rexml/document/add_spec.rb b/spec/rubyspec/library/rexml/document/add_spec.rb new file mode 100644 index 0000000000..491c28259b --- /dev/null +++ b/spec/rubyspec/library/rexml/document/add_spec.rb @@ -0,0 +1,57 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +# This spec defines Document#add and Document#<< + +describe :rexml_document_add, shared: true do + before :each do + @doc = REXML::Document.new("") + @decl = REXML::XMLDecl.new("1.0") + end + + it "sets document's XML declaration" do + @doc.send(@method, @decl) + @doc.xml_decl.should == @decl + end + + it "inserts XML declaration as first node" do + @doc.send(@method, @decl) + @doc.children[0].version.should == "1.0" + end + + it "overwrites existing XML declaration" do + @doc.send(@method, @decl) + @doc.send(@method, REXML::XMLDecl.new("2.0")) + @doc.xml_decl.version.should == "2.0" + end + + it "sets document DocType" do + @doc.send(@method, REXML::DocType.new("transitional")) + @doc.doctype.name.should == "transitional" + end + + it "overwrites existing DocType" do + @doc.send(@method, REXML::DocType.new("transitional")) + @doc.send(@method, REXML::DocType.new("strict")) + @doc.doctype.name.should == "strict" + end + + it "adds root node unless it exists" do + d = REXML::Document.new("") + elem = REXML::Element.new "root" + d.send(@method, elem) + d.root.should == elem + end + + it "refuses to add second root" do + lambda { @doc.send(@method, REXML::Element.new("foo")) }.should raise_error(RuntimeError) + end +end + +describe "REXML::Document#add" do + it_behaves_like(:rexml_document_add, :add) +end + +describe "REXML::Document#<<" do + it_behaves_like(:rexml_document_add, :<<) +end diff --git a/spec/rubyspec/library/rexml/document/clone_spec.rb b/spec/rubyspec/library/rexml/document/clone_spec.rb new file mode 100644 index 0000000000..cf333bf4df --- /dev/null +++ b/spec/rubyspec/library/rexml/document/clone_spec.rb @@ -0,0 +1,20 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +# According to the MRI documentation (http://www.ruby-doc.org/stdlib/libdoc/rexml/rdoc/index.html), +# clone's behavior "should be obvious". Apparently "obvious" means cloning +# only the attributes and the context of the document, not its children. +describe "REXML::Document#clone" do + it "clones document attributes" do + d = REXML::Document.new("foo") + d.attributes["foo"] = "bar" + e = d.clone + e.attributes.should == d.attributes + end + + it "clones document context" do + d = REXML::Document.new("foo", {"foo" => "bar"}) + e = d.clone + e.context.should == d.context + end +end diff --git a/spec/rubyspec/library/rexml/document/doctype_spec.rb b/spec/rubyspec/library/rexml/document/doctype_spec.rb new file mode 100644 index 0000000000..5f277f6ad6 --- /dev/null +++ b/spec/rubyspec/library/rexml/document/doctype_spec.rb @@ -0,0 +1,15 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#doctype" do + it "returns the doctype" do + d = REXML::Document.new + dt = REXML::DocType.new("foo") + d.add dt + d.doctype.should == dt + end + + it "returns nil if there's no doctype" do + REXML::Document.new.doctype.should == nil + end +end diff --git a/spec/rubyspec/library/rexml/document/encoding_spec.rb b/spec/rubyspec/library/rexml/document/encoding_spec.rb new file mode 100644 index 0000000000..d20be0f7b7 --- /dev/null +++ b/spec/rubyspec/library/rexml/document/encoding_spec.rb @@ -0,0 +1,22 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#encoding" do + before :each do + @doc = REXML::Document.new + end + + it "returns encoding from XML declaration" do + @doc.add REXML::XMLDecl.new(nil, "UTF-16", nil) + @doc.encoding.should == "UTF-16" + end + + it "returns encoding from XML declaration (for UTF-16 as well)" do + @doc.add REXML::XMLDecl.new("1.0", "UTF-8", nil) + @doc.encoding.should == "UTF-8" + end + + it "uses UTF-8 as default encoding" do + @doc.encoding.should == "UTF-8" + end +end diff --git a/spec/rubyspec/library/rexml/document/expanded_name_spec.rb b/spec/rubyspec/library/rexml/document/expanded_name_spec.rb new file mode 100644 index 0000000000..e18fd95c14 --- /dev/null +++ b/spec/rubyspec/library/rexml/document/expanded_name_spec.rb @@ -0,0 +1,16 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :document_expanded_name, shared: true do + it "returns an empty string for root" do # root nodes have no expanded name + REXML::Document.new.send(@method).should == "" + end +end + +describe "REXML::Document#expanded_name" do + it_behaves_like(:document_expanded_name, :expanded_name) +end + +describe "REXML::Document#name" do + it_behaves_like(:document_expanded_name, :name) +end diff --git a/spec/rubyspec/library/rexml/document/new_spec.rb b/spec/rubyspec/library/rexml/document/new_spec.rb new file mode 100644 index 0000000000..0caa3fd583 --- /dev/null +++ b/spec/rubyspec/library/rexml/document/new_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Document#new" do + + it "initializes context of {} unless specified" do + d = REXML::Document.new("") + d.context.should == {} + end + + it "has empty attributes if source is nil" do + d = REXML::Document.new(nil) + d.elements.should be_empty + end + + it "can use other document context" do + s = REXML::Document.new("") + d = REXML::Document.new(s) + d.context.should == s.context + end + + it "clones source attributes" do + s = REXML::Document.new("") + s.attributes["some_attr"] = "some_val" + d = REXML::Document.new(s) + d.attributes.should == s.attributes + end + + it "raises an error if source is not a Document, String or IO" do + lambda {REXML::Document.new(3)}.should raise_error(RuntimeError) + end + + it "does not perform XML validation" do + REXML::Document.new("Invalid document").should be_kind_of(REXML::Document) + end +end diff --git a/spec/rubyspec/library/rexml/document/node_type_spec.rb b/spec/rubyspec/library/rexml/document/node_type_spec.rb new file mode 100644 index 0000000000..b18b1a0dfe --- /dev/null +++ b/spec/rubyspec/library/rexml/document/node_type_spec.rb @@ -0,0 +1,8 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#node_type" do + it "returns :document" do + REXML::Document.new.node_type.should == :document + end +end diff --git a/spec/rubyspec/library/rexml/document/root_spec.rb b/spec/rubyspec/library/rexml/document/root_spec.rb new file mode 100644 index 0000000000..55be68da6f --- /dev/null +++ b/spec/rubyspec/library/rexml/document/root_spec.rb @@ -0,0 +1,12 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#root" do + it "returns document root tag name" do + REXML::Document.new("").root.name.should == "foo" + end + + it "returns nil if there is not root" do + REXML::Document.new.root.should == nil + end +end diff --git a/spec/rubyspec/library/rexml/document/stand_alone_spec.rb b/spec/rubyspec/library/rexml/document/stand_alone_spec.rb new file mode 100644 index 0000000000..250c604dad --- /dev/null +++ b/spec/rubyspec/library/rexml/document/stand_alone_spec.rb @@ -0,0 +1,19 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#stand_alone?" do + it "returns the XMLDecl standalone value" do + d = REXML::Document.new + decl = REXML::XMLDecl.new("1.0", "UTF-8", "yes") + d.add decl + d.stand_alone?.should == "yes" + end + + # According to the docs this should return the default XMLDecl but that + # will carry some more problems when printing the document. Currently, it + # returns nil. See http://www.ruby-forum.com/topic/146812#650061 + it "returns the default value when no XML declaration present" do + REXML::Document.new.stand_alone?.should == nil + end + +end diff --git a/spec/rubyspec/library/rexml/document/version_spec.rb b/spec/rubyspec/library/rexml/document/version_spec.rb new file mode 100644 index 0000000000..ca979dbf34 --- /dev/null +++ b/spec/rubyspec/library/rexml/document/version_spec.rb @@ -0,0 +1,14 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#version" do + it "returns XML version from declaration" do + d = REXML::Document.new + d.add REXML::XMLDecl.new("1.1") + d.version.should == "1.1" + end + + it "returns the default version when declaration is not present" do + REXML::Document.new.version.should == REXML::XMLDecl::DEFAULT_VERSION + end +end diff --git a/spec/rubyspec/library/rexml/document/write_spec.rb b/spec/rubyspec/library/rexml/document/write_spec.rb new file mode 100644 index 0000000000..f2a7e2c200 --- /dev/null +++ b/spec/rubyspec/library/rexml/document/write_spec.rb @@ -0,0 +1,35 @@ +require 'rexml/document' +require 'rexml/formatters/transitive' +require File.expand_path('../../../../spec_helper', __FILE__) + +# Maybe this can be cleaned +describe "REXML::Document#write" do + before :each do + @d = REXML::Document.new + city = REXML::Element.new "Springfield" + street = REXML::Element.new "EvergreenTerrace" + address = REXML::Element.new "House742" + @d << city << street << address + @str = "" + end + + it "returns document source as string" do + @d.write(@str) + @str.should == "" + end + + it "returns document indented" do + @d.write(@str, 2) + @str.should =~ /\s*\s*\s*\s*<\/EvergreenTerrace>\s*<\/Springfield>/ + end + + it "returns document with transitive support" do + @d.write(@str, 2, true) + @str.should =~ /\s*<\/EvergreenTerrace\s*><\/Springfield\s*>/ + end + + it "returns document with support for IE" do + @d.write(@str, -1, false, true) + @str.should == "" + end +end diff --git a/spec/rubyspec/library/rexml/document/xml_decl_spec.rb b/spec/rubyspec/library/rexml/document/xml_decl_spec.rb new file mode 100644 index 0000000000..6a5bf07c0b --- /dev/null +++ b/spec/rubyspec/library/rexml/document/xml_decl_spec.rb @@ -0,0 +1,15 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Document#xml_decl" do + it "returns XML declaration of the document" do + d = REXML::Document.new + decl = REXML::XMLDecl.new("1.0", "UTF-16", "yes") + d.add decl + d.xml_decl.should == decl + end + + it "returns default XML declaration unless present" do + REXML::Document.new.xml_decl.should == REXML::XMLDecl.new + end +end diff --git a/spec/rubyspec/library/rexml/element/add_attribute_spec.rb b/spec/rubyspec/library/rexml/element/add_attribute_spec.rb new file mode 100644 index 0000000000..74eceee99b --- /dev/null +++ b/spec/rubyspec/library/rexml/element/add_attribute_spec.rb @@ -0,0 +1,41 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#add_attribute" do + before :each do + @person = REXML::Element.new "person" + @person.attributes["name"] = "Bill" + end + + it "adds a new attribute" do + @person.add_attribute("age", "17") + @person.attributes["age"].should == "17" + end + + it "overwrites an existing attribute" do + @person.add_attribute("name", "Bill") + @person.attributes["name"].should == "Bill" + end + + it "accepts a pair of strings" do + @person.add_attribute("male", "true") + @person.attributes["male"].should == "true" + end + + it "accepts an Attribute for key" do + attr = REXML::Attribute.new("male", "true") + @person.add_attribute attr + @person.attributes["male"].should == "true" + end + + it "ignores value if key is an Attribute" do + attr = REXML::Attribute.new("male", "true") + @person.add_attribute(attr, "false") + @person.attributes["male"].should == "true" + end + + it "returns the attribute added" do + attr = REXML::Attribute.new("name", "Tony") + @person.add_attribute(attr).should == attr + end +end diff --git a/spec/rubyspec/library/rexml/element/add_attributes_spec.rb b/spec/rubyspec/library/rexml/element/add_attributes_spec.rb new file mode 100644 index 0000000000..aa64b677ca --- /dev/null +++ b/spec/rubyspec/library/rexml/element/add_attributes_spec.rb @@ -0,0 +1,22 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#add_attribute" do + before :each do + @person = REXML::Element.new "person" + @person.attributes["name"] = "Bill" + end + + it "adds multiple attributes from a hash" do + @person.add_attributes({"name" => "Joe", "age" => "27"}) + @person.attributes["name"].should == "Joe" + @person.attributes["age"].should == "27" + end + + it "adds multiple attributes from an array" do + attrs = { "name" => "Joe", "age" => "27"} + @person.add_attributes attrs.to_a + @person.attributes["name"].should == "Joe" + @person.attributes["age"].should == "27" + end +end diff --git a/spec/rubyspec/library/rexml/element/add_element_spec.rb b/spec/rubyspec/library/rexml/element/add_element_spec.rb new file mode 100644 index 0000000000..b6aab3da6a --- /dev/null +++ b/spec/rubyspec/library/rexml/element/add_element_spec.rb @@ -0,0 +1,39 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + + +describe "REXML::Element#add_element" do + before :each do + @root = REXML::Element.new("root") + end + + it "adds a child without attributes" do + name = REXML::Element.new("name") + @root.add_element name + @root.elements["name"].name.should == name.name + @root.elements["name"].attributes.should == name.attributes + @root.elements["name"].context.should == name.context + end + + it "adds a child with attributes" do + person = REXML::Element.new("person") + @root.add_element(person, {"name" => "Madonna"}) + @root.elements["person"].name.should == person.name + @root.elements["person"].attributes.should == person.attributes + @root.elements["person"].context.should == person.context + end + + it "adds a child with name" do + @root.add_element "name" + @root.elements["name"].name.should == "name" + @root.elements["name"].attributes.should == {} + @root.elements["name"].context.should == nil + end + + it "returns the added child" do + name = @root.add_element "name" + @root.elements["name"].name.should == name.name + @root.elements["name"].attributes.should == name.attributes + @root.elements["name"].context.should == name.context + end +end diff --git a/spec/rubyspec/library/rexml/element/add_namespace_spec.rb b/spec/rubyspec/library/rexml/element/add_namespace_spec.rb new file mode 100644 index 0000000000..60db839f02 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/add_namespace_spec.rb @@ -0,0 +1,24 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#add_namespace" do + before :each do + @elem = REXML::Element.new("person") + end + + it "adds a namespace to element" do + @elem.add_namespace("foo", "bar") + @elem.namespace("foo").should == "bar" + end + + it "accepts a prefix string as prefix" do + @elem.add_namespace("xmlns:foo", "bar") + @elem.namespace("foo").should == "bar" + end + + it "uses prefix as URI if uri is nil" do + @elem.add_namespace("some_uri", nil) + @elem.namespace.should == "some_uri" + end +end + diff --git a/spec/rubyspec/library/rexml/element/add_text_spec.rb b/spec/rubyspec/library/rexml/element/add_text_spec.rb new file mode 100644 index 0000000000..5d116ee6d3 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/add_text_spec.rb @@ -0,0 +1,24 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#add_namespace" do + before :each do + @name = REXML::Element.new "Name" + end + + it "adds text to an element" do + @name.add_text "Ringo" + @name.to_s.should == "Ringo" + end + + it "accepts a Text" do + @name.add_text(REXML::Text.new("Ringo")) + @name.to_s.should == "Ringo" + end + + it "joins the new text with the old one" do + @name.add_text "Ringo" + @name.add_text " Starr" + @name.to_s.should == "Ringo Starr" + end +end diff --git a/spec/rubyspec/library/rexml/element/attribute_spec.rb b/spec/rubyspec/library/rexml/element/attribute_spec.rb new file mode 100644 index 0000000000..9c5fb7a20e --- /dev/null +++ b/spec/rubyspec/library/rexml/element/attribute_spec.rb @@ -0,0 +1,17 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#attribute" do + it "returns an attribute by name" do + person = REXML::Element.new "Person" + attribute = REXML::Attribute.new("drink", "coffee") + person.add_attribute(attribute) + person.attribute("drink").should == attribute + end + + it "supports attributes inside namespaces" do + e = REXML::Element.new("element") + e.add_attributes({"xmlns:ns" => "http://some_uri"}) + e.attribute("ns", "ns").to_s.should == "http://some_uri" + end +end diff --git a/spec/rubyspec/library/rexml/element/attributes_spec.rb b/spec/rubyspec/library/rexml/element/attributes_spec.rb new file mode 100644 index 0000000000..7cc5310ed1 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/attributes_spec.rb @@ -0,0 +1,19 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#attributes" do + it "returns element's Attributes" do + p = REXML::Element.new "Person" + + name = REXML::Attribute.new("name", "John") + attrs = REXML::Attributes.new(p) + attrs.add name + + p.add_attribute name + p.attributes.should == attrs + end + + it "returns an empty hash if element has no attributes" do + REXML::Element.new("Person").attributes.should == {} + end +end diff --git a/spec/rubyspec/library/rexml/element/cdatas_spec.rb b/spec/rubyspec/library/rexml/element/cdatas_spec.rb new file mode 100644 index 0000000000..1b44abe1e7 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/cdatas_spec.rb @@ -0,0 +1,24 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#cdatas" do + before :each do + @e = REXML::Element.new("Root") + end + + it "returns the array of children cdatas" do + c = REXML::CData.new("Primary") + d = REXML::CData.new("Secondary") + @e << c + @e << d + @e.cdatas.should == [c, d] + end + + it "freezes the returned array" do + @e.cdatas.frozen?.should == true + end + + it "returns an empty array if element has no cdata" do + @e.cdatas.should == [] + end +end diff --git a/spec/rubyspec/library/rexml/element/clone_spec.rb b/spec/rubyspec/library/rexml/element/clone_spec.rb new file mode 100644 index 0000000000..08f97e7793 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/clone_spec.rb @@ -0,0 +1,29 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#clone" do + before :each do + @e = REXML::Element.new "a" + end + it "creates a copy of element" do + @e.clone.to_s.should == @e.to_s + end + + it "copies the attributes" do + @e.add_attribute("foo", "bar") + @e.clone.to_s.should == @e.to_s + end + + it "does not copy the text" do + @e.add_text "some text..." + @e.clone.to_s.should_not == @e + @e.clone.to_s.should == "" + end + + it "does not copy the child elements" do + b = REXML::Element.new "b" + @e << b + @e.clone.should_not == @e + @e.clone.to_s.should == "" + end +end diff --git a/spec/rubyspec/library/rexml/element/comments_spec.rb b/spec/rubyspec/library/rexml/element/comments_spec.rb new file mode 100644 index 0000000000..158b008b4f --- /dev/null +++ b/spec/rubyspec/library/rexml/element/comments_spec.rb @@ -0,0 +1,20 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#comments" do + before :each do + @e = REXML::Element.new "root" + @c1 = REXML::Comment.new "this is a comment" + @c2 = REXML::Comment.new "this is another comment" + @e << @c1 + @e << @c2 + end + + it "returns the array of comments" do + @e.comments.should == [@c1, @c2] + end + + it "returns a frozen object" do + @e.comments.frozen?.should == true + end +end diff --git a/spec/rubyspec/library/rexml/element/delete_attribute_spec.rb b/spec/rubyspec/library/rexml/element/delete_attribute_spec.rb new file mode 100644 index 0000000000..930db603b8 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/delete_attribute_spec.rb @@ -0,0 +1,39 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#delete_attribute" do + before :each do + @e = REXML::Element.new("Person") + @attr = REXML::Attribute.new("name", "Sean") + @e.add_attribute(@attr) + end + + it "deletes an attribute from the element" do + @e.delete_attribute("name") + @e.attributes["name"].should be_nil + end + +# Bug was filled with a patch in Ruby's tracker #20298 + quarantine! do + it "receives an Attribute" do + @e.add_attribute(@attr) + @e.delete_attribute(@attr) + @e.attributes["name"].should be_nil + end + end + + # Docs say that it returns the removed attribute but then examples + # show it returns the element with the attribute removed. + # Also fixed in #20298 + it "returns the element with the attribute removed" do + elem = @e.delete_attribute("name") + elem.attributes.should be_empty + elem.to_s.should eql("") + end + + it "returns nil if the attribute does not exist" do + @e.delete_attribute("name") + at = @e.delete_attribute("name") + at.should be_nil + end +end diff --git a/spec/rubyspec/library/rexml/element/delete_element_spec.rb b/spec/rubyspec/library/rexml/element/delete_element_spec.rb new file mode 100644 index 0000000000..e6e36364ba --- /dev/null +++ b/spec/rubyspec/library/rexml/element/delete_element_spec.rb @@ -0,0 +1,49 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#delete_element" do + before :each do + @root = REXML::Element.new("root") + end + + it "deletes the child element" do + node = REXML::Element.new("some_node") + @root.add_element node + @root.delete_element node + @root.elements.size.should == 0 + end + + it "deletes a child via XPath" do + @root.add_element "some_node" + @root.delete_element "some_node" + @root.elements.size.should == 0 + end + + it "deletes the child at index" do + @root.add_element "some_node" + @root.delete_element 1 + @root.elements.size.should == 0 + end + + # According to the docs this should return the deleted element + # but it won't if it's an Element. + it "deletes Element and returns it" do + node = REXML::Element.new("some_node") + @root.add_element node + del_node = @root.delete_element node + del_node.should == node + end + + # Note how passing the string will return the removed element + # but passing the Element as above won't. + it "deletes an element and returns it" do + node = REXML::Element.new("some_node") + @root.add_element node + del_node = @root.delete_element "some_node" + del_node.should == node + end + + it "returns nil unless element exists" do + @root.delete_element("something").should == nil + end +end diff --git a/spec/rubyspec/library/rexml/element/delete_namespace_spec.rb b/spec/rubyspec/library/rexml/element/delete_namespace_spec.rb new file mode 100644 index 0000000000..10de705076 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/delete_namespace_spec.rb @@ -0,0 +1,25 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#delete_namespace" do + + before :each do + @doc = REXML::Document.new "" + end + + it "deletes a namespace from the element" do + @doc.root.delete_namespace 'foo' + @doc.root.namespace("foo").should be_nil + @doc.root.to_s.should == "" + end + + it "deletes default namespace when called with no args" do + @doc.root.delete_namespace + @doc.root.namespace.should be_empty + @doc.root.to_s.should == "" + end + + it "returns the element" do + @doc.root.delete_namespace.should == @doc.root + end +end diff --git a/spec/rubyspec/library/rexml/element/document_spec.rb b/spec/rubyspec/library/rexml/element/document_spec.rb new file mode 100644 index 0000000000..bdf9205a85 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/document_spec.rb @@ -0,0 +1,18 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#document" do + + it "returns the element's document" do + d = REXML::Document.new("") + d << REXML::XMLDecl.new + d.root.document.should == d + d.root.document.to_s.should == d.to_s + end + + it "returns nil if it belongs to no document" do + REXML::Element.new("standalone").document.should be_nil + end +end + + diff --git a/spec/rubyspec/library/rexml/element/each_element_with_attribute_spec.rb b/spec/rubyspec/library/rexml/element/each_element_with_attribute_spec.rb new file mode 100644 index 0000000000..2769fd2d26 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/each_element_with_attribute_spec.rb @@ -0,0 +1,35 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#each_element_with_attributes" do + before :each do + @document = REXML::Element.new("people") + @father = REXML::Element.new("Person") + @father.attributes["name"] = "Joe" + @son = REXML::Element.new("Child") + @son.attributes["name"] = "Fred" + @document.root << @father + @document.root << @son + @childs = [] + end + + it "returns childs with attribute" do + @document.each_element_with_attribute("name") { |elem| @childs << elem } + @childs[0].should == @father + @childs[1].should == @son + end + + it "takes attribute value as second argument" do + @document.each_element_with_attribute("name", "Fred"){ |elem| elem.should == @son } + end + + it "takes max number of childs as third argument" do + @document.each_element_with_attribute("name", nil, 1) { |elem| @childs << elem } + @childs.size.should == 1 + @childs[0].should == @father + end + + it "takes XPath filter as fourth argument" do + @document.each_element_with_attribute("name", nil, 0, "Child"){ |elem| elem.should == @son} + end +end diff --git a/spec/rubyspec/library/rexml/element/each_element_with_text_spec.rb b/spec/rubyspec/library/rexml/element/each_element_with_text_spec.rb new file mode 100644 index 0000000000..79848c779c --- /dev/null +++ b/spec/rubyspec/library/rexml/element/each_element_with_text_spec.rb @@ -0,0 +1,31 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#each_element_with_text" do + before :each do + @document = REXML::Element.new("people") + + @joe = REXML::Element.new("Person") + @joe.text = "Joe" + @fred = REXML::Element.new("Person") + @fred.text = "Fred" + @another = REXML::Element.new("AnotherPerson") + @another.text = "Fred" + @document.root << @joe + @document.root << @fred + @document.root << @another + @childs = [] + end + + it "returns childs with text" do + @document.each_element_with_text("Joe"){|c| c.should == @joe} + end + + it "takes max as second argument" do + @document.each_element_with_text("Fred", 1){ |c| c.should == @fred} + end + + it "takes XPath filter as third argument" do + @document.each_element_with_text("Fred", 0, "Person"){ |c| c.should == @fred} + end +end diff --git a/spec/rubyspec/library/rexml/element/get_text_spec.rb b/spec/rubyspec/library/rexml/element/get_text_spec.rb new file mode 100644 index 0000000000..9ae343e097 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/get_text_spec.rb @@ -0,0 +1,18 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#get_text" do + before :each do + @doc = REXML::Document.new "

    some textthis is bold! more text

    " + end + + it "returns the first text child node" do + @doc.root.get_text.value.should == "some text" + @doc.root.get_text.should be_kind_of(REXML::Text) + end + + it "returns text from an element matching path" do + @doc.root.get_text("b").value.should == "this is bold!" + @doc.root.get_text("b").should be_kind_of(REXML::Text) + end +end diff --git a/spec/rubyspec/library/rexml/element/has_attributes_spec.rb b/spec/rubyspec/library/rexml/element/has_attributes_spec.rb new file mode 100644 index 0000000000..f6c1c45e31 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/has_attributes_spec.rb @@ -0,0 +1,17 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#has_attributes?" do + before :each do + @e = REXML::Element.new("test_elem") + end + + it "returns true when element has any attributes" do + @e.add_attribute("name", "Joe") + @e.has_attributes?.should be_true + end + + it "returns false if element has no attributes" do + @e.has_attributes?.should be_false + end +end diff --git a/spec/rubyspec/library/rexml/element/has_elements_spec.rb b/spec/rubyspec/library/rexml/element/has_elements_spec.rb new file mode 100644 index 0000000000..54898b0d19 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/has_elements_spec.rb @@ -0,0 +1,18 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#has_elements?" do + before :each do + @e = REXML::Element.new("root") + end + + it "returns true if element has child elements" do + child = REXML::Element.new("child") + @e << child + @e.has_elements?.should be_true + end + + it "returns false if element doesn't have child elements" do + @e.has_elements?.should be_false + end +end diff --git a/spec/rubyspec/library/rexml/element/has_text_spec.rb b/spec/rubyspec/library/rexml/element/has_text_spec.rb new file mode 100644 index 0000000000..4747149ac7 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/has_text_spec.rb @@ -0,0 +1,16 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#has_text?" do + + it "returns true if element has a Text child" do + e = REXML::Element.new("Person") + e.text = "My text" + e.has_text?.should be_true + end + + it "returns false if it has no Text childs" do + e = REXML::Element.new("Person") + e.has_text?.should be_false + end +end diff --git a/spec/rubyspec/library/rexml/element/inspect_spec.rb b/spec/rubyspec/library/rexml/element/inspect_spec.rb new file mode 100644 index 0000000000..8e93afb562 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/inspect_spec.rb @@ -0,0 +1,27 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#inspect" do + + before :each do + @name = REXML::Element.new "name" + end + + it "returns the node as a string" do + @name.inspect.should == "" + end + + it "inserts '...' if the node has children" do + e = REXML::Element.new "last_name" + @name << e + @name.inspect.should == " ... " + # This might make more sense but differs from MRI's default behavior + # @name.inspect.should == " ... " + end + + it "inserts the attributes in the string" do + @name.add_attribute "language" + @name.attributes["language"] = "english" + @name.inspect.should == "" + end +end diff --git a/spec/rubyspec/library/rexml/element/instructions_spec.rb b/spec/rubyspec/library/rexml/element/instructions_spec.rb new file mode 100644 index 0000000000..01a2374820 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/instructions_spec.rb @@ -0,0 +1,21 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#instructions" do + before :each do + @elem = REXML::Element.new("root") + end + it "returns the Instruction children nodes" do + inst = REXML::Instruction.new("xml-stylesheet", "href='headlines.css'") + @elem << inst + @elem.instructions.first.should == inst + end + + it "returns an empty array if it has no Instruction children" do + @elem.instructions.should be_empty + end + + it "freezes the returned array" do + @elem.instructions.frozen?.should be_true + end +end diff --git a/spec/rubyspec/library/rexml/element/namespace_spec.rb b/spec/rubyspec/library/rexml/element/namespace_spec.rb new file mode 100644 index 0000000000..a0b7ba0c83 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/namespace_spec.rb @@ -0,0 +1,27 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#namespace" do + before :each do + @doc = REXML::Document.new("
    ") + @elem = @doc.elements["//b"] + end + + it "returns the default namespace" do + @elem.namespace.should == "1" + end + + it "accepts a namespace prefix" do + @elem.namespace("y").should == "2" + @doc.elements["//c"].namespace("z").should == "3" + end + + it "returns an empty String if default namespace is not defined" do + e = REXML::Document.new("") + e.root.namespace.should be_empty + end + + it "returns nil if namespace is not defined" do + @elem.namespace("z").should be_nil + end +end diff --git a/spec/rubyspec/library/rexml/element/namespaces_spec.rb b/spec/rubyspec/library/rexml/element/namespaces_spec.rb new file mode 100644 index 0000000000..646503e184 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/namespaces_spec.rb @@ -0,0 +1,32 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#namespaces" do + before :each do + doc = REXML::Document.new("") + @elem = doc.elements["//c"] + end + + it "returns a hash of the namespaces" do + ns = {"y"=>"2", "z"=>"3", "xmlns"=>"1"} + @elem.namespaces.keys.sort.should == ns.keys.sort + @elem.namespaces.values.sort.should == ns.values.sort + end + + it "returns an empty hash if no namespaces exist" do + e = REXML::Element.new "element" + e.namespaces.kind_of?(Hash).should == true + e.namespaces.should be_empty + end + + it "uses namespace prefixes as keys" do + prefixes = ["y", "z", "xmlns"] + @elem.namespaces.keys.sort.should == prefixes.sort + end + + it "uses namespace values as the hash values" do + values = ["2", "3", "1"] + @elem.namespaces.values.sort.should == values.sort + end + +end diff --git a/spec/rubyspec/library/rexml/element/new_spec.rb b/spec/rubyspec/library/rexml/element/new_spec.rb new file mode 100644 index 0000000000..c18a33624b --- /dev/null +++ b/spec/rubyspec/library/rexml/element/new_spec.rb @@ -0,0 +1,35 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#new" do + + it "creates element from tag name" do + REXML::Element.new("foo").name.should == "foo" + end + + it "creates element with default attributes" do + e = REXML::Element.new + e.name.should == REXML::Element::UNDEFINED + e.context.should == nil + e.parent.should == nil + end + + it "creates element from another element" do + e = REXML::Element.new "foo" + f = REXML::Element.new e + e.name.should == f.name + e.context.should == f.context + e.parent.should == f.parent + end + + it "takes parent as second argument" do + parent = REXML::Element.new "foo" + child = REXML::Element.new "bar", parent + child.parent.should == parent + end + + it "takes context as third argument" do + context = {"some_key" => "some_value"} + REXML::Element.new("foo", nil, context).context.should == context + end +end diff --git a/spec/rubyspec/library/rexml/element/next_element_spec.rb b/spec/rubyspec/library/rexml/element/next_element_spec.rb new file mode 100644 index 0000000000..51b8438ba7 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/next_element_spec.rb @@ -0,0 +1,19 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#next_element" do + before :each do + @a = REXML::Element.new "a" + @b = REXML::Element.new "b" + @c = REXML::Element.new "c" + @a.root << @b + @a.root << @c + end + it "returns next existing element" do + @a.elements["b"].next_element.should == @c + end + + it "returns nil on last element" do + @a.elements["c"].next_element.should == nil + end +end diff --git a/spec/rubyspec/library/rexml/element/node_type_spec.rb b/spec/rubyspec/library/rexml/element/node_type_spec.rb new file mode 100644 index 0000000000..3c9b713fde --- /dev/null +++ b/spec/rubyspec/library/rexml/element/node_type_spec.rb @@ -0,0 +1,8 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#node_type" do + it "returns :element" do + REXML::Element.new("MyElem").node_type.should == :element + end +end diff --git a/spec/rubyspec/library/rexml/element/prefixes_spec.rb b/spec/rubyspec/library/rexml/element/prefixes_spec.rb new file mode 100644 index 0000000000..03c46eab9e --- /dev/null +++ b/spec/rubyspec/library/rexml/element/prefixes_spec.rb @@ -0,0 +1,23 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#prefixes" do + before :each do + doc = REXML::Document.new("") + @elem = doc.elements["//c"] + end + + it "returns an array of the prefixes of the namespaces" do + @elem.prefixes.should == ["y", "z"] + end + + it "does not include the default namespace" do + @elem.prefixes.include?("xmlns").should == false + end + + it "returns an empty array if no namespace was defined" do + doc = REXML::Document.new "" + root = doc.elements["//root"] + root.prefixes.should == [] + end +end diff --git a/spec/rubyspec/library/rexml/element/previous_element_spec.rb b/spec/rubyspec/library/rexml/element/previous_element_spec.rb new file mode 100644 index 0000000000..49279e8e94 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/previous_element_spec.rb @@ -0,0 +1,20 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#previous_element" do + before :each do + @a = REXML::Element.new "a" + @b = REXML::Element.new "b" + @c = REXML::Element.new "c" + @a.root << @b + @a.root << @c + end + + it "returns previous element" do + @a.elements["c"].previous_element.should == @b + end + + it "returns nil on first element" do + @a.elements["b"].previous_element.should == nil + end +end diff --git a/spec/rubyspec/library/rexml/element/raw_spec.rb b/spec/rubyspec/library/rexml/element/raw_spec.rb new file mode 100644 index 0000000000..a872c36c8b --- /dev/null +++ b/spec/rubyspec/library/rexml/element/raw_spec.rb @@ -0,0 +1,24 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#raw" do + it "returns true if raw mode is set to all" do + REXML::Element.new("MyElem", nil, {raw: :all}).raw.should == true + end + + it "returns true if raw mode is set to expanded_name" do + REXML::Element.new("MyElem", nil, {raw: "MyElem"}).raw.should == true + end + + it "returns false if raw mode is not set" do + REXML::Element.new("MyElem", nil, {raw: ""}).raw.should == false + end + + it "returns false if raw is not :all or expanded_name" do + REXML::Element.new("MyElem", nil, {raw: "Something"}).raw.should == false + end + + it "returns nil if context is not set" do + REXML::Element.new("MyElem").raw.should == nil + end +end diff --git a/spec/rubyspec/library/rexml/element/root_spec.rb b/spec/rubyspec/library/rexml/element/root_spec.rb new file mode 100644 index 0000000000..24e488e701 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/root_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Element#root" do + before :each do + @doc = REXML::Document.new + @root = REXML::Element.new "root" + @node = REXML::Element.new "node" + @doc << @root << @node + end + + it "returns first child on documents" do + @doc.root.should == @root + end + + it "returns self on root nodes" do + @root.root.should == @root + end + + it "returns parent's root on child nodes" do + @node.root.should == @root + end + + it "returns self on standalone nodes" do + e = REXML::Element.new "Elem" # Note that it doesn't have a parent node + e.root.should == e + end +end diff --git a/spec/rubyspec/library/rexml/element/text_spec.rb b/spec/rubyspec/library/rexml/element/text_spec.rb new file mode 100644 index 0000000000..25791d6397 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/text_spec.rb @@ -0,0 +1,46 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#text" do + before :each do + @e = REXML::Element.new "name" + @e.text = "John" + end + + it "returns the text node of element" do + @e.text.should == "John" + end + + it "returns the text node value" do + t = REXML::Text.new "Joe" + @e.text = t + @e.text.should == "Joe" + @e.text.should_not == t + end + + it "returns nil if no text is attached" do + elem = REXML::Element.new "name" + elem.text.should == nil + end +end + +describe "REXML::Element#text=" do + before :each do + @e = REXML::Element.new "name" + @e.text = "John" + end + + it "sets the text node" do + @e.to_s.should == "John" + end + + it "replaces existing text" do + @e.text = "Joe" + @e.to_s.should == "Joe" + end + + it "receives nil as an argument" do + @e.text = nil + @e.to_s.should == "" + end +end diff --git a/spec/rubyspec/library/rexml/element/texts_spec.rb b/spec/rubyspec/library/rexml/element/texts_spec.rb new file mode 100644 index 0000000000..de3a818866 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/texts_spec.rb @@ -0,0 +1,16 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#texts" do + + it "returns an array of the Text children" do + e = REXML::Element.new("root") + e.add_text "First" + e.add_text "Second" + e.texts.should == ["FirstSecond"] + end + + it "returns an empty array if it has no Text children" do + REXML::Element.new("root").texts.should == [] + end +end diff --git a/spec/rubyspec/library/rexml/element/whitespace_spec.rb b/spec/rubyspec/library/rexml/element/whitespace_spec.rb new file mode 100644 index 0000000000..ea9ff42c03 --- /dev/null +++ b/spec/rubyspec/library/rexml/element/whitespace_spec.rb @@ -0,0 +1,23 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "REXML::Element#whitespace" do + it "returns true if whitespace is respected in the element" do + e = REXML::Element.new("root") + e.whitespace.should be_true + + e = REXML::Element.new("root", nil, respect_whitespace: :all) + e.whitespace.should be_true + + e = REXML::Element.new("root", nil, respect_whitespace: ["root"]) + e.whitespace.should be_true + end + + it "returns false if whitespace is ignored inside element" do + e = REXML::Element.new("root", nil, compress_whitespace: :all) + e.whitespace.should be_false + + e = REXML::Element.new("root", nil, compress_whitespace: ["root"]) + e.whitespace.should be_false + end +end diff --git a/spec/rubyspec/library/rexml/node/each_recursive_spec.rb b/spec/rubyspec/library/rexml/node/each_recursive_spec.rb new file mode 100644 index 0000000000..5f26c898ea --- /dev/null +++ b/spec/rubyspec/library/rexml/node/each_recursive_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Node#each_recursive" do + before :each do + @doc = REXML::Document.new + @doc << REXML::XMLDecl.new + @root = REXML::Element.new "root" + @child1 = REXML::Element.new "child1" + @child2 = REXML::Element.new "child2" + @root << @child1 + @root << @child2 + @doc << @root + end + + it "visits all subnodes of self" do + nodes = [] + @doc.each_recursive { |node| nodes << node} + nodes.should == [@root, @child1, @child2] + end +end diff --git a/spec/rubyspec/library/rexml/node/find_first_recursive_spec.rb b/spec/rubyspec/library/rexml/node/find_first_recursive_spec.rb new file mode 100644 index 0000000000..20e87fe9e9 --- /dev/null +++ b/spec/rubyspec/library/rexml/node/find_first_recursive_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Node#find_first_recursive" do + before :each do + @e = REXML::Element.new("root") + @node1 = REXML::Element.new("node") + @node2 = REXML::Element.new("another node") + @subnode = REXML::Element.new("another node") + @node1 << @subnode + @e << @node1 + @e << @node2 + end + + it "finds the first element that matches block" do + found = @e.find_first_recursive { |n| n.to_s == ""} + found.should == @node1 + end + + it "visits the nodes in preorder" do + found = @e.find_first_recursive { |n| n.to_s == ""} + found.should == @subnode + found.should_not == @node2 + end +end diff --git a/spec/rubyspec/library/rexml/node/index_in_parent_spec.rb b/spec/rubyspec/library/rexml/node/index_in_parent_spec.rb new file mode 100644 index 0000000000..018a4c08ac --- /dev/null +++ b/spec/rubyspec/library/rexml/node/index_in_parent_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Node#index_in_parent" do + it "returns the index (starting from 1) of self in parent" do + e = REXML::Element.new("root") + node1 = REXML::Element.new("node") + node2 = REXML::Element.new("another node") + e << node1 + e << node2 + + node1.index_in_parent.should == 1 + node2.index_in_parent.should == 2 + end +end diff --git a/spec/rubyspec/library/rexml/node/next_sibling_node_spec.rb b/spec/rubyspec/library/rexml/node/next_sibling_node_spec.rb new file mode 100644 index 0000000000..9ca91c9149 --- /dev/null +++ b/spec/rubyspec/library/rexml/node/next_sibling_node_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Node#next_sibling_node" do + before :each do + @e = REXML::Element.new("root") + @node1 = REXML::Element.new("node") + @node2 = REXML::Element.new("another node") + @e << @node1 + @e << @node2 + end + + it "returns the next child node in parent" do + @node1.next_sibling_node.should == @node2 + end + + it "returns nil if there are no more child nodes next" do + @node2.next_sibling_node.should == nil + @e.next_sibling_node.should == nil + end +end diff --git a/spec/rubyspec/library/rexml/node/parent_spec.rb b/spec/rubyspec/library/rexml/node/parent_spec.rb new file mode 100644 index 0000000000..ee3c234534 --- /dev/null +++ b/spec/rubyspec/library/rexml/node/parent_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Node#parent?" do + it "returns true for Elements" do + e = REXML::Element.new("foo") + e.parent?.should == true + end + + it "returns true for Documents" do + e = REXML::Document.new + e.parent?.should == true + end + + # This includes attributes, CDatas and declarations. + it "returns false for Texts" do + e = REXML::Text.new("foo") + e.parent?.should == false + end +end + diff --git a/spec/rubyspec/library/rexml/node/previous_sibling_node_spec.rb b/spec/rubyspec/library/rexml/node/previous_sibling_node_spec.rb new file mode 100644 index 0000000000..b8e4465979 --- /dev/null +++ b/spec/rubyspec/library/rexml/node/previous_sibling_node_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Node#previous_sibling_node" do + before :each do + @e = REXML::Element.new("root") + @node1 = REXML::Element.new("node") + @node2 = REXML::Element.new("another node") + @e << @node1 + @e << @node2 + end + + it "returns the previous child node in parent" do + @node2.previous_sibling_node.should == @node1 + end + + it "returns nil if there are no more child nodes before" do + @node1.previous_sibling_node.should == nil + @e.previous_sibling_node.should == nil + end +end diff --git a/spec/rubyspec/library/rexml/shared/each_element.rb b/spec/rubyspec/library/rexml/shared/each_element.rb new file mode 100644 index 0000000000..1cb79c8d3a --- /dev/null +++ b/spec/rubyspec/library/rexml/shared/each_element.rb @@ -0,0 +1,36 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :rexml_each_element, shared: true do + before :each do + @e = REXML::Element.new "root" + s1 = REXML::Element.new "node1" + s2 = REXML::Element.new "node2" + s3 = REXML::Element.new "node3" + s4 = REXML::Element.new "sub_node" + @e << s1 + @e << s2 + @e << s3 + @e << s4 + end + + it "iterates through element" do + str = "" + @e.send(@method) { |elem| str << elem.name << " " } + str.should == "node1 node2 node3 sub_node " + end + + it "iterates through element filtering with XPath" do + str = "" + @e.send(@method, "/*"){ |e| str << e.name << " "} + str.should == "node1 node2 node3 sub_node " + end +end + +describe "REXML::Element#each_element" do + it_behaves_like :rexml_each_element, :each_element +end + +describe "REXML::Elements#each" do + it_behaves_like :rexml_each_element, :each +end diff --git a/spec/rubyspec/library/rexml/shared/elements_to_a.rb b/spec/rubyspec/library/rexml/shared/elements_to_a.rb new file mode 100644 index 0000000000..6299c628c3 --- /dev/null +++ b/spec/rubyspec/library/rexml/shared/elements_to_a.rb @@ -0,0 +1,34 @@ +require 'rexml/document' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :rexml_elements_to_a, shared: true do + before :each do + @e = REXML::Element.new "root" + @first = REXML::Element.new("FirstChild") + @second = REXML::Element.new("SecondChild") + @e << @first + @e << @second + end + + it "returns elements that match xpath" do + @e.elements.send(@method, "FirstChild").first.should == @first + end + + # According to the docs REXML::Element#get_elements is an alias for + # REXML::Elements.to_a. Implementation wise there's a difference, get_elements + # always needs the first param (even if it's nil). + # A patch was submitted: + # http://rubyforge.org/tracker/index.php?func=detail&aid=19354&group_id=426&atid=1698 + it "returns all childs if xpath is nil" do + @e.elements.send(@method).should == [@first, @second] + end + +end + +describe "REXML::REXML::Elements#to_a" do + it_behaves_like :rexml_elements_to_a, :to_a +end + +describe "REXML::REXML::Element#get_elements" do + it_behaves_like :rexml_elements_to_a, :get_elements +end diff --git a/spec/rubyspec/library/rexml/text/append_spec.rb b/spec/rubyspec/library/rexml/text/append_spec.rb new file mode 100644 index 0000000000..c8f73f9393 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/append_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#<<" do + it "appends a string to this text node" do + text = REXML::Text.new("foo") + text << "bar" + text.should == "foobar" + end +end diff --git a/spec/rubyspec/library/rexml/text/clone_spec.rb b/spec/rubyspec/library/rexml/text/clone_spec.rb new file mode 100644 index 0000000000..c7d16e0d85 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/clone_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#clone" do + it "creates a copy of this node" do + text = REXML::Text.new("foo") + text.clone.should == "foo" + text.clone.should == text + end +end diff --git a/spec/rubyspec/library/rexml/text/comparison_spec.rb b/spec/rubyspec/library/rexml/text/comparison_spec.rb new file mode 100644 index 0000000000..ba637ea37e --- /dev/null +++ b/spec/rubyspec/library/rexml/text/comparison_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#<=>" do + before :each do + @first = REXML::Text.new("abc") + @last = REXML::Text.new("def") + end + + it "returns -1 if lvalue is less than rvalue" do + val = @first <=> @last + val.should == -1 + end + + it "returns -1 if lvalue is greater than rvalue" do + val = @last <=> @first + val.should == 1 + end + + it "returns 0 if both values are equal" do + tmp = REXML::Text.new("tmp") + val = tmp <=> tmp + val.should == 0 + end +end diff --git a/spec/rubyspec/library/rexml/text/empty_spec.rb b/spec/rubyspec/library/rexml/text/empty_spec.rb new file mode 100644 index 0000000000..7102e1586e --- /dev/null +++ b/spec/rubyspec/library/rexml/text/empty_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#empty?" do + it "returns true if the text is empty" do + REXML::Text.new("").empty?.should == true + end + + it "returns false if the text is not empty" do + REXML::Text.new("some_text").empty?.should == false + end +end diff --git a/spec/rubyspec/library/rexml/text/indent_text_spec.rb b/spec/rubyspec/library/rexml/text/indent_text_spec.rb new file mode 100644 index 0000000000..2aa908826b --- /dev/null +++ b/spec/rubyspec/library/rexml/text/indent_text_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#indent_text" do + before :each do + @t = REXML::Text.new("") + end + it "indents a string with default parameters" do + @t.indent_text("foo").should == "\tfoo" + end + + it "accepts a custom indentation level as second argument" do + @t.indent_text("foo", 2, "\t", true).should == "\t\tfoo" + end + + it "accepts a custom separator as third argument" do + @t.indent_text("foo", 1, "\n", true).should == "\nfoo" + end + + it "accepts a fourth parameter to skip the first line" do + @t.indent_text("foo", 1, "\t", false).should == "foo" + end +end + diff --git a/spec/rubyspec/library/rexml/text/inspect_spec.rb b/spec/rubyspec/library/rexml/text/inspect_spec.rb new file mode 100644 index 0000000000..655e42fcdf --- /dev/null +++ b/spec/rubyspec/library/rexml/text/inspect_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#inspect" do + it "inspects the string attribute as a string" do + REXML::Text.new("a text").inspect.should == "a text".inspect + end +end diff --git a/spec/rubyspec/library/rexml/text/new_spec.rb b/spec/rubyspec/library/rexml/text/new_spec.rb new file mode 100644 index 0000000000..0d7a750a1d --- /dev/null +++ b/spec/rubyspec/library/rexml/text/new_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text.new" do + + it "creates a Text child node with no parent" do + t = REXML::Text.new("test") + t.should be_kind_of(REXML::Child) + t.should == "test" + t.parent.should == nil + end + + it "respects whitespace if second argument is true" do + t = REXML::Text.new("testing whitespace", true) + t.should == "testing whitespace" + t = REXML::Text.new(" ", true) + t.should == " " + end + + it "receives a parent as third argument" do + e = REXML::Element.new("root") + t = REXML::Text.new("test", false, e) + t.parent.should == e + e.to_s.should == "test" + end + + it "expects escaped text if raw is true" do + t = REXML::Text.new("<&>", false, nil, true) + t.should == "<&>" + + lambda{ REXML::Text.new("<&>", false, nil, true)}.should raise_error(Exception) + end + + it "uses raw value of the parent if raw is nil" do + e1 = REXML::Element.new("root", nil, { raw: :all}) + lambda {REXML::Text.new("<&>", false, e1)}.should raise_error(Exception) + + e2 = REXML::Element.new("root", nil, { raw: []}) + e2.raw.should be_false + t1 = REXML::Text.new("<&>", false, e2) + t1.should == "<&>" + end + + it "escapes the values if raw is false" do + t = REXML::Text.new("<&>", false, nil, false) + t.should == "<&>" + end +end + diff --git a/spec/rubyspec/library/rexml/text/node_type_spec.rb b/spec/rubyspec/library/rexml/text/node_type_spec.rb new file mode 100644 index 0000000000..a1c51b5b91 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/node_type_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#node_type" do + it "returns :text" do + REXML::Text.new("test").node_type.should == :text + end +end diff --git a/spec/rubyspec/library/rexml/text/normalize_spec.rb b/spec/rubyspec/library/rexml/text/normalize_spec.rb new file mode 100644 index 0000000000..1725c38146 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/normalize_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text.normalize" do + it "escapes a string with <, >, &, ' and \" " do + REXML::Text.normalize("< > & \" '").should == "< > & " '" + end +end diff --git a/spec/rubyspec/library/rexml/text/read_with_substitution_spec.rb b/spec/rubyspec/library/rexml/text/read_with_substitution_spec.rb new file mode 100644 index 0000000000..7e42c40248 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/read_with_substitution_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text.read_with_substitution" do + it "reads a text and escapes entities" do + REXML::Text.read_with_substitution("< > & " '").should == "< > & \" '" + end + + it "accepts an regex for invalid expressions and raises an error if text matches" do + lambda {REXML::Text.read_with_substitution("this is illegal", /illegal/)}.should raise_error(Exception) + end +end + diff --git a/spec/rubyspec/library/rexml/text/to_s_spec.rb b/spec/rubyspec/library/rexml/text/to_s_spec.rb new file mode 100644 index 0000000000..94356ff075 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/to_s_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#to_s" do + it "returns the string of this Text node" do + u = REXML::Text.new("sean russell", false, nil, true) + u.to_s.should == "sean russell" + + t = REXML::Text.new("some test text") + t.to_s.should == "some test text" + end + + it "escapes the text" do + t = REXML::Text.new("& < >") + t.to_s.should == "& < >" + end +end + diff --git a/spec/rubyspec/library/rexml/text/unnormalize_spec.rb b/spec/rubyspec/library/rexml/text/unnormalize_spec.rb new file mode 100644 index 0000000000..6406589694 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/unnormalize_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text.unnormalize" do + it "unescapes a string with the values defined in SETUTITSBUS" do + REXML::Text.unnormalize("< > & " '").should == "< > & \" '" + end +end diff --git a/spec/rubyspec/library/rexml/text/value_spec.rb b/spec/rubyspec/library/rexml/text/value_spec.rb new file mode 100644 index 0000000000..d14e8aca6b --- /dev/null +++ b/spec/rubyspec/library/rexml/text/value_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#value" do + it "returns the text value of this node" do + REXML::Text.new("test").value.should == "test" + end + + it "does not escape entities" do + REXML::Text.new("& \"").value.should == "& \"" + end + + it "follows the respect_whitespace attribute" do + REXML::Text.new("test bar", false).value.should == "test bar" + REXML::Text.new("test bar", true).value.should == "test bar" + end + + it "ignores the raw attribute" do + REXML::Text.new("sean russell", false, nil, true).value.should == "sean russell" + end +end + +describe "REXML::Text#value=" do + before :each do + @t = REXML::Text.new("new") + end + + it "sets the text of the node" do + @t.value = "another text" + @t.to_s.should == "another text" + end + + it "escapes entities" do + @t.value = "" + @t.to_s.should == "<a>" + end +end diff --git a/spec/rubyspec/library/rexml/text/wrap_spec.rb b/spec/rubyspec/library/rexml/text/wrap_spec.rb new file mode 100644 index 0000000000..9491e47fc8 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/wrap_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#wrap" do + before :each do + @t = REXML::Text.new("abc def") + end + + it "wraps the text at width" do + @t.wrap("abc def", 3, false).should == "abc\ndef" + end + + it "returns the string if width is greater than the size of the string" do + @t.wrap("abc def", 10, false).should == "abc def" + end + + it "takes a newline at the beginning option as the third parameter"do + @t.wrap("abc def", 3, true).should == "\nabc\ndef" + end +end + diff --git a/spec/rubyspec/library/rexml/text/write_with_substitution_spec.rb b/spec/rubyspec/library/rexml/text/write_with_substitution_spec.rb new file mode 100644 index 0000000000..e5f027f297 --- /dev/null +++ b/spec/rubyspec/library/rexml/text/write_with_substitution_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'rexml/document' + +describe "REXML::Text#write_with_substitution" do + before :each do + @t = REXML::Text.new("test") + @f = tmp("rexml_spec") + @file = File.open(@f, "w+") + end + + after :each do + @file.close + rm_r @f + end + + it "writes out the input to a String" do + s = "" + @t.write_with_substitution(s, "some text") + s.should == "some text" + end + + it "writes out the input to an IO" do + @t.write_with_substitution(@file, "some text") + @file.rewind + @file.gets.should == "some text" + end + + it "escapes characters" do + @t.write_with_substitution(@file, "& < >") + @file.rewind + @file.gets.should == "& < >" + end +end diff --git a/spec/rubyspec/library/scanf/io/block_scanf_spec.rb b/spec/rubyspec/library/scanf/io/block_scanf_spec.rb new file mode 100644 index 0000000000..0f6188f91c --- /dev/null +++ b/spec/rubyspec/library/scanf/io/block_scanf_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/block_scanf.rb', __FILE__) +require 'scanf' + +describe "IO#block_scanf" do + it_behaves_like(:scanf_io_block_scanf, :block_scanf) +end diff --git a/spec/rubyspec/library/scanf/io/fixtures/date.txt b/spec/rubyspec/library/scanf/io/fixtures/date.txt new file mode 100644 index 0000000000..a1bd635c0c --- /dev/null +++ b/spec/rubyspec/library/scanf/io/fixtures/date.txt @@ -0,0 +1,4 @@ +Beethoven 1770 +Bach 1685 +Handel 1685 + diff --git a/spec/rubyspec/library/scanf/io/fixtures/helloworld.txt b/spec/rubyspec/library/scanf/io/fixtures/helloworld.txt new file mode 100644 index 0000000000..3b18e512db --- /dev/null +++ b/spec/rubyspec/library/scanf/io/fixtures/helloworld.txt @@ -0,0 +1 @@ +hello world diff --git a/spec/rubyspec/library/scanf/io/scanf_spec.rb b/spec/rubyspec/library/scanf/io/scanf_spec.rb new file mode 100644 index 0000000000..27c3142678 --- /dev/null +++ b/spec/rubyspec/library/scanf/io/scanf_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/block_scanf.rb', __FILE__) +require 'scanf' + +describe "IO#scanf" do + before :each do + @hw = File.open(File.dirname(__FILE__) + '/fixtures/helloworld.txt', 'r') + @data = File.open(File.dirname(__FILE__) + '/fixtures/date.txt', 'r') + end + + after :each do + @hw.close unless @hw.closed? + @data.close unless @data.closed? + end + + it "returns an array containing the input converted in the specified type" do + @hw.scanf("%s%s").should == ["hello", "world"] + @data.scanf("%s%d").should == ["Beethoven", 1770] + end + + it "returns an array containing the input converted in the specified type with given maximum field width" do + @hw.scanf("%2s").should == ["he"] + @data.scanf("%2c").should == ["Be"] + end + + it "returns an empty array when a wrong specifier is passed" do + @hw.scanf("%a").should == [] + @hw.scanf("%1").should == [] + @data.scanf("abc").should == [] + end +end + +describe "IO#scanf with block" do + it_behaves_like(:scanf_io_block_scanf, :scanf) +end diff --git a/spec/rubyspec/library/scanf/io/shared/block_scanf.rb b/spec/rubyspec/library/scanf/io/shared/block_scanf.rb new file mode 100644 index 0000000000..8c5bffb93b --- /dev/null +++ b/spec/rubyspec/library/scanf/io/shared/block_scanf.rb @@ -0,0 +1,28 @@ +require 'scanf' + +describe :scanf_io_block_scanf, shared: true do + before :each do + @data= File.open(File.dirname(__FILE__) + '/../fixtures/date.txt', 'r') + end + + after :each do + @data.close unless @data.closed? + end + + it "passes each match to the block as an array" do + res = @data.send(@method, "%s%d") { |name, year| "#{name} was born in #{year}." } + res.should == ["Beethoven was born in 1770.", "Bach was born in 1685.", "Handel was born in 1685."] + end + + it "keeps scanning the input and cycling back to the beginning of the input string" do + a = [] + @data.send(@method, "%s"){|w| a << w} + a.should == [["Beethoven"], ["1770"], ["Bach"], ["1685"], ["Handel"], ["1685"]] + end + + it "returns an empty array when a wrong specifier is passed" do + a = [] + @data.send(@method, "%z"){|w| a << w} + a.empty?.should be_true + end +end diff --git a/spec/rubyspec/library/scanf/string/block_scanf_spec.rb b/spec/rubyspec/library/scanf/string/block_scanf_spec.rb new file mode 100644 index 0000000000..1444cc2975 --- /dev/null +++ b/spec/rubyspec/library/scanf/string/block_scanf_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/block_scanf.rb', __FILE__) +require 'scanf' + +describe "String#block_scanf" do + it_behaves_like(:scanf_string_block_scanf, :block_scanf) +end diff --git a/spec/rubyspec/library/scanf/string/scanf_spec.rb b/spec/rubyspec/library/scanf/string/scanf_spec.rb new file mode 100644 index 0000000000..360c72fba8 --- /dev/null +++ b/spec/rubyspec/library/scanf/string/scanf_spec.rb @@ -0,0 +1,53 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/block_scanf.rb', __FILE__) +require 'scanf' + +describe "String#scanf" do + it "returns an array containing the input converted in the specified type" do + "hello world".scanf("%s").should == ["hello"] + "hello world".scanf("%s%d").should == ["hello"] + "hello world".scanf("%s%c").should == ["hello", " "] + "hello world".scanf("%c%s").should == ["h", "ello"] + "hello world".scanf("%s%s").should == ["hello", "world"] + "hello world".scanf("%c").should == ["h"] + "123".scanf("%s").should == ["123"] + "123".scanf("%c").should == ["1"] + "123".scanf("%d").should == [123] + "123".scanf("%u").should == [123] + "123".scanf("%o").should == [83] + "123".scanf("%x").should == [291] + "123".scanf("%i").should == [123] + "0123".scanf("%i").should == [83] + "123".scanf("%f").should == [123.0] + "0X123".scanf("%i").should == [291] + "0x123".scanf("%i").should == [291] + end + + it "returns an array containing the input converted in the specified type with given maximum field width" do + "hello world".scanf("%2s").should == ["he"] + "hello world".scanf("%2c").should == ["he"] + "123".scanf("%2s").should == ["12"] + "123".scanf("%2c").should == ["12"] + "123".scanf("%2d").should == [12] + "123".scanf("%2u").should == [12] + "123".scanf("%2o").should == [10] + "123".scanf("%2x").should == [18] + "123".scanf("%2i").should == [12] + "0123".scanf("%2i").should == [1] + "123".scanf("%2f").should == [12.0] + "0X123".scanf("%2i").should == [0] + "0X123".scanf("%3i").should == [1] + "0X123".scanf("%4i").should == [18] + end + + it "returns an empty array when a wrong specifier is passed" do + "hello world".scanf("%a").should == [] + "123".scanf("%1").should == [] + "123".scanf("abc").should == [] + "123".scanf(:d).should == [] + end +end + +describe "String#scanf with block" do + it_behaves_like(:scanf_string_block_scanf, :scanf) +end diff --git a/spec/rubyspec/library/scanf/string/shared/block_scanf.rb b/spec/rubyspec/library/scanf/string/shared/block_scanf.rb new file mode 100644 index 0000000000..25ab3f442a --- /dev/null +++ b/spec/rubyspec/library/scanf/string/shared/block_scanf.rb @@ -0,0 +1,25 @@ +require 'scanf' + +describe :scanf_string_block_scanf, shared: true do + it "passes each match to the block as an array" do + a = [] + "hello world".send(@method, "%s%s"){|w| a << w} + a.should == [["hello", "world"]] + end + + it "keeps scanning the input and cycling back to the beginning of the input string" do + a = [] + "hello world".send(@method, "%s"){|w| a << w} + a.should == [["hello"], ["world"]] + + string = "123 abc 456 def 789 ghi" + s = string.send(@method, "%d%s"){|num,str| [num * 2, str.upcase]} + s.should == [[246, "ABC"], [912, "DEF"], [1578, "GHI"]] + end + + it "returns an empty array when a wrong specifier is passed" do + a = [] + "hello world".send(@method, "%z"){|w| a << w} + a.empty?.should be_true + end +end diff --git a/spec/rubyspec/library/securerandom/base64_spec.rb b/spec/rubyspec/library/securerandom/base64_spec.rb new file mode 100644 index 0000000000..68e61c9ecb --- /dev/null +++ b/spec/rubyspec/library/securerandom/base64_spec.rb @@ -0,0 +1,55 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'securerandom' + +describe "SecureRandom.base64" do + it "generates a random base64 string out of specified number of random bytes" do + (16..128).each do |idx| + base64 = SecureRandom.base64(idx) + base64.should be_kind_of(String) + base64.length.should < 2 * idx + base64.should =~ /^[A-Za-z0-9\+\/]+={0,2}$/ + end + + base64 = SecureRandom.base64(16.5) + base64.should be_kind_of(String) + base64.length.should < 2 * 16 + end + + it "returns an empty string when argument is 0" do + SecureRandom.base64(0).should == "" + end + + it "generates different base64 strings with subsequent invocations" do + # quick and dirty check, but good enough + values = [] + 256.times do + base64 = SecureRandom.base64 + # make sure the random values are not repeating + values.include?(base64).should == false + values << base64 + end + end + + it "generates a random base64 string out of 32 random bytes" do + SecureRandom.base64.should be_kind_of(String) + SecureRandom.base64.length.should < 32 * 2 + end + + it "treats nil agrument as default one and generates a random base64 string" do + SecureRandom.base64(nil).should be_kind_of(String) + SecureRandom.base64(nil).length.should < 32 * 2 + end + + it "raises ArgumentError on negative arguments" do + lambda { + SecureRandom.base64(-1) + }.should raise_error(ArgumentError) + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(5) + SecureRandom.base64(obj).size.should < 10 + end +end diff --git a/spec/rubyspec/library/securerandom/hex_spec.rb b/spec/rubyspec/library/securerandom/hex_spec.rb new file mode 100644 index 0000000000..691392a7b9 --- /dev/null +++ b/spec/rubyspec/library/securerandom/hex_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'securerandom' + +describe "SecureRandom.hex" do + it "generates a random hex string of length twice the specified argement" do + (1..64).each do |idx| + hex = SecureRandom.hex(idx) + hex.should be_kind_of(String) + hex.length.should == 2 * idx + end + + base64 = SecureRandom.hex(5.5) + base64.should be_kind_of(String) + base64.length.should eql(10) + end + + it "returns an empty string when argument is 0" do + SecureRandom.hex(0).should == "" + end + + it "generates different hex strings with subsequent invocations" do + # quick and dirty check, but good enough + values = [] + 256.times do + hex = SecureRandom.hex + # make sure the random values are not repeating + values.include?(hex).should == false + values << hex + end + end + + it "generates a random hex string of length 32 if no argument is provided" do + SecureRandom.hex.should be_kind_of(String) + SecureRandom.hex.length.should == 32 + end + + it "treats nil agrument as default one and generates a random hex string of length 32" do + SecureRandom.hex(nil).should be_kind_of(String) + SecureRandom.hex(nil).length.should == 32 + end + + it "raises ArgumentError on negative arguments" do + lambda { + SecureRandom.hex(-1) + }.should raise_error(ArgumentError) + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(5) + SecureRandom.hex(obj).size.should eql(10) + end +end diff --git a/spec/rubyspec/library/securerandom/random_bytes_spec.rb b/spec/rubyspec/library/securerandom/random_bytes_spec.rb new file mode 100644 index 0000000000..37d82f55a6 --- /dev/null +++ b/spec/rubyspec/library/securerandom/random_bytes_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'securerandom' + +describe "SecureRandom.random_bytes" do + it "generates a random binary string of length 16 if no argument is provided" do + bytes = SecureRandom.random_bytes + bytes.should be_kind_of(String) + bytes.length.should == 16 + end + + it "generates a random binary string of length 16 if argument is nil" do + bytes = SecureRandom.random_bytes(nil) + bytes.should be_kind_of(String) + bytes.length.should == 16 + end + + it "generates a random binary string of specified length" do + (1..64).each do |idx| + bytes = SecureRandom.random_bytes(idx) + bytes.should be_kind_of(String) + bytes.length.should == idx + end + + SecureRandom.random_bytes(2.2).length.should eql(2) + end + + it "generates different binary strings with subsequent invocations" do + # quick and dirty check, but good enough + values = [] + 256.times do + val = SecureRandom.random_bytes + # make sure the random bytes are not repeating + values.include?(val).should == false + values << val + end + end + + it "raises ArgumentError on negative arguments" do + lambda { + SecureRandom.random_bytes(-1) + }.should raise_error(ArgumentError) + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(5) + SecureRandom.random_bytes(obj).size.should eql(5) + end +end diff --git a/spec/rubyspec/library/securerandom/random_number_spec.rb b/spec/rubyspec/library/securerandom/random_number_spec.rb new file mode 100644 index 0000000000..296da51be2 --- /dev/null +++ b/spec/rubyspec/library/securerandom/random_number_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +require 'securerandom' + +describe "SecureRandom.random_number" do + it "generates a random positive number smaller then the positive integer argument" do + (1..64).each do |idx| + num = SecureRandom.random_number(idx) + num.should be_kind_of(Fixnum) + (0 <= num).should == true + (num < idx).should == true + end + end + + it "generates a random float number between 0.0 and 1.0 if no argument provided" do + 64.times do + num = SecureRandom.random_number + num.should be_kind_of(Float) + (0.0 <= num).should == true + (num < 1.0).should == true + end + end + + it "generates a random float number between 0.0 and 1.0 if argument is negative" do + num = SecureRandom.random_number(-10) + num.should be_kind_of(Float) + (0.0 <= num).should == true + (num < 1.0).should == true + end + + it "generates different float numbers with subsequent invocations" do + # quick and dirty check, but good enough + values = [] + 256.times do + val = SecureRandom.random_number + # make sure the random values are not repeating + values.include?(val).should == false + values << val + end + end + + it "raises ArgumentError if the argument is non-numeric" do + lambda { + SecureRandom.random_number(Object.new) + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/add_spec.rb b/spec/rubyspec/library/set/add_spec.rb new file mode 100644 index 0000000000..a7d6fb8a56 --- /dev/null +++ b/spec/rubyspec/library/set/add_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/add', __FILE__) + +describe "Set#add" do + it_behaves_like :set_add, :add +end + +describe "Set#add?" do + before :each do + @set = Set.new + end + + it "adds the passed Object to self" do + @set.add?("cat") + @set.should include("cat") + end + + it "returns self when the Object has not yet been added to self" do + @set.add?("cat").should equal(@set) + end + + it "returns nil when the Object has already been added to self" do + @set.add?("cat") + @set.add?("cat").should be_nil + end +end diff --git a/spec/rubyspec/library/set/append_spec.rb b/spec/rubyspec/library/set/append_spec.rb new file mode 100644 index 0000000000..b3097c0904 --- /dev/null +++ b/spec/rubyspec/library/set/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/add', __FILE__) + +describe "Set#<<" do + it_behaves_like :set_add, :<< +end diff --git a/spec/rubyspec/library/set/classify_spec.rb b/spec/rubyspec/library/set/classify_spec.rb new file mode 100644 index 0000000000..87e5b62f96 --- /dev/null +++ b/spec/rubyspec/library/set/classify_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#classify" do + before :each do + @set = Set["one", "two", "three", "four"] + end + + it "yields each Object in self" do + res = [] + @set.classify { |x| res << x } + res.sort.should == ["one", "two", "three", "four"].sort + end + + it "returns an Enumerator when passed no block" do + enum = @set.classify + enum.should be_an_instance_of(Enumerator) + + classified = enum.each { |x| x.length } + classified.should == { 3 => Set["one", "two"], 4 => Set["four"], 5 => Set["three"] } + end + + it "classifies the Objects in self based on the block's return value" do + classified = @set.classify { |x| x.length } + classified.should == { 3 => Set["one", "two"], 4 => Set["four"], 5 => Set["three"] } + end +end diff --git a/spec/rubyspec/library/set/clear_spec.rb b/spec/rubyspec/library/set/clear_spec.rb new file mode 100644 index 0000000000..b35ce4fec0 --- /dev/null +++ b/spec/rubyspec/library/set/clear_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#clear" do + before :each do + @set = Set["one", "two", "three", "four"] + end + + it "removes all elements from self" do + @set.clear + @set.should be_empty + end + + it "returns self" do + @set.clear.should equal(@set) + end +end diff --git a/spec/rubyspec/library/set/collect_spec.rb b/spec/rubyspec/library/set/collect_spec.rb new file mode 100644 index 0000000000..03c00c9794 --- /dev/null +++ b/spec/rubyspec/library/set/collect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/collect', __FILE__) + +describe "Set#collect!" do + it_behaves_like :set_collect_bang, :collect! +end diff --git a/spec/rubyspec/library/set/constructor_spec.rb b/spec/rubyspec/library/set/constructor_spec.rb new file mode 100644 index 0000000000..75c7ba8bc8 --- /dev/null +++ b/spec/rubyspec/library/set/constructor_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set[]" do + it "returns a new Set populated with the passed Objects" do + set = Set[1, 2, 3] + + set.instance_of?(Set).should be_true + set.size.should eql(3) + + set.should include(1) + set.should include(2) + set.should include(3) + end +end diff --git a/spec/rubyspec/library/set/delete_if_spec.rb b/spec/rubyspec/library/set/delete_if_spec.rb new file mode 100644 index 0000000000..b6bd28a59f --- /dev/null +++ b/spec/rubyspec/library/set/delete_if_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#delete_if" do + before :each do + @set = Set["one", "two", "three"] + end + + it "yields every element of self" do + ret = [] + @set.delete_if { |x| ret << x } + ret.sort.should == ["one", "two", "three"].sort + end + + it "deletes every element from self for which the passed block returns true" do + @set.delete_if { |x| x.size == 3 } + @set.size.should eql(1) + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end + + it "returns self" do + @set.delete_if { |x| x }.should equal(@set) + end + + it "returns an Enumerator when passed no block" do + enum = @set.delete_if + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size == 3 } + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end +end diff --git a/spec/rubyspec/library/set/delete_spec.rb b/spec/rubyspec/library/set/delete_spec.rb new file mode 100644 index 0000000000..4f0326e37a --- /dev/null +++ b/spec/rubyspec/library/set/delete_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#delete" do + before :each do + @set = Set["a", "b", "c"] + end + + it "deletes the passed Object from self" do + @set.delete("a") + @set.should_not include("a") + end + + it "returns self" do + @set.delete("a").should equal(@set) + @set.delete("x").should equal(@set) + end +end + +describe "Set#delete?" do + before :each do + @set = Set["a", "b", "c"] + end + + it "deletes the passed Object from self" do + @set.delete?("a") + @set.should_not include("a") + end + + it "returns self when the passed Object is in self" do + @set.delete?("a").should equal(@set) + end + + it "returns nil when the passed Object is not in self" do + @set.delete?("x").should be_nil + end +end diff --git a/spec/rubyspec/library/set/difference_spec.rb b/spec/rubyspec/library/set/difference_spec.rb new file mode 100644 index 0000000000..416dffe802 --- /dev/null +++ b/spec/rubyspec/library/set/difference_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/difference', __FILE__) + +describe "Set#difference" do + it_behaves_like :set_difference, :difference +end diff --git a/spec/rubyspec/library/set/divide_spec.rb b/spec/rubyspec/library/set/divide_spec.rb new file mode 100644 index 0000000000..930c69885f --- /dev/null +++ b/spec/rubyspec/library/set/divide_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#divide" do + it "divides self into a set of subsets based on the blocks return values" do + set = Set["one", "two", "three", "four", "five"].divide { |x| x.length } + set.map { |x| x.to_a.sort }.sort.should == [["five", "four"], ["one", "two"], ["three"]] + end + + it "yields each Object to the block" do + ret = [] + Set["one", "two", "three", "four", "five"].divide { |x| ret << x } + ret.sort.should == ["five", "four", "one", "three", "two"] + end + + # BUG: Does not raise a LocalJumpError, but a NoMethodError + # + # it "raises a LocalJumpError when not passed a block" do + # lambda { Set[1].divide }.should raise_error(LocalJumpError) + # end +end + +describe "Set#divide when passed a block with an arity of 2" do + it "divides self into a set of subsets based on the blocks return values" do + set = Set[1, 3, 4, 6, 9, 10, 11].divide { |x, y| (x - y).abs == 1 } + set.map{ |x| x.to_a.sort }.sort.should == [[1], [3, 4], [6], [9, 10, 11]] + end + + it "yields each two Object to the block" do + ret = [] + Set[1, 2].divide { |x, y| ret << [x, y] } + ret.sort.should == [[1, 1], [1, 2], [2, 1], [2, 2]] + end +end diff --git a/spec/rubyspec/library/set/each_spec.rb b/spec/rubyspec/library/set/each_spec.rb new file mode 100644 index 0000000000..867e94ff0b --- /dev/null +++ b/spec/rubyspec/library/set/each_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#each" do + before :each do + @set = Set[1, 2, 3] + end + + it "yields each Object in self" do + ret = [] + @set.each { |x| ret << x } + ret.sort.should == [1, 2, 3] + end + + it "returns self" do + @set.each { |x| x }.should equal(@set) + end + + it "returns an Enumerator when not passed a block" do + enum = @set.each + + ret = [] + enum.each { |x| ret << x } + ret.sort.should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/library/set/empty_spec.rb b/spec/rubyspec/library/set/empty_spec.rb new file mode 100644 index 0000000000..044d58727c --- /dev/null +++ b/spec/rubyspec/library/set/empty_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#empty?" do + it "returns true if self is empty" do + Set[].empty?.should be_true + Set[1].empty?.should be_false + Set[1,2,3].empty?.should be_false + end +end diff --git a/spec/rubyspec/library/set/enumerable/to_set_spec.rb b/spec/rubyspec/library/set/enumerable/to_set_spec.rb new file mode 100644 index 0000000000..41ca039de6 --- /dev/null +++ b/spec/rubyspec/library/set/enumerable/to_set_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "Emumerable#to_set" do + it "returns a new Set created from self" do + [1, 2, 3].to_set.should == Set[1, 2, 3] + {a: 1, b: 2}.to_set.should == Set[[:b, 2], [:a, 1]] + end + + it "allows passing an alternate class for Set" do + sorted_set = [1, 2, 3].to_set(SortedSet) + sorted_set.should == SortedSet[1, 2, 3] + sorted_set.instance_of?(SortedSet).should == true + end + + it "passes down passed blocks" do + [1, 2, 3].to_set { |x| x * x }.should == Set[1, 4, 9] + end +end diff --git a/spec/rubyspec/library/set/eql_spec.rb b/spec/rubyspec/library/set/eql_spec.rb new file mode 100644 index 0000000000..ed62e20d7a --- /dev/null +++ b/spec/rubyspec/library/set/eql_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#eql?" do + it "returns true when the passed argument is a Set and contains the same elements" do + Set[].should eql(Set[]) + Set[1, 2, 3].should eql(Set[1, 2, 3]) + Set[1, 2, 3].should eql(Set[3, 2, 1]) + Set["a", :b, ?c].should eql(Set[?c, :b, "a"]) + + Set[1, 2, 3].should_not eql(Set[1.0, 2, 3]) + Set[1, 2, 3].should_not eql(Set[2, 3]) + Set[1, 2, 3].should_not eql(Set[]) + end +end diff --git a/spec/rubyspec/library/set/equal_value_spec.rb b/spec/rubyspec/library/set/equal_value_spec.rb new file mode 100644 index 0000000000..2e2c875407 --- /dev/null +++ b/spec/rubyspec/library/set/equal_value_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#==" do + it "returns true when the passed Object is a Set and self and the Object contain the same elements" do + Set[].should == Set[] + Set[1, 2, 3].should == Set[1, 2, 3] + Set["1", "2", "3"].should == Set["1", "2", "3"] + + Set[1, 2, 3].should_not == Set[1.0, 2, 3] + Set[1, 2, 3].should_not == [1, 2, 3] + end + + it "does not depend on the order of the elements" do + Set[1, 2, 3].should == Set[3, 2, 1] + Set[:a, "b", ?c].should == Set[?c, "b", :a] + end + + it "does not depend on the order of nested Sets" do + Set[Set[1], Set[2], Set[3]].should == Set[Set[3], Set[2], Set[1]] + + set1 = Set[Set["a", "b"], Set["c", "d"], Set["e", "f"]] + set2 = Set[Set["c", "d"], Set["a", "b"], Set["e", "f"]] + set1.should == set2 + end +end diff --git a/spec/rubyspec/library/set/exclusion_spec.rb b/spec/rubyspec/library/set/exclusion_spec.rb new file mode 100644 index 0000000000..d2923ba2d7 --- /dev/null +++ b/spec/rubyspec/library/set/exclusion_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#^" do + before :each do + @set = Set[1, 2, 3, 4] + end + + it "returns a new Set containing elements that are not in both self and the passed Enumberable" do + (@set ^ Set[3, 4, 5]).should == Set[1, 2, 5] + (@set ^ [3, 4, 5]).should == Set[1, 2, 5] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set ^ 3 }.should raise_error(ArgumentError) + lambda { @set ^ Object.new }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/flatten_merge_spec.rb b/spec/rubyspec/library/set/flatten_merge_spec.rb new file mode 100644 index 0000000000..bbddec7076 --- /dev/null +++ b/spec/rubyspec/library/set/flatten_merge_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#flatten_merge" do + it "is protected" do + Set.should have_protected_instance_method("flatten_merge") + end + + it "flattens the passed Set and merges it into self" do + set1 = Set[1, 2] + set2 = Set[3, 4, Set[5, 6]] + + set1.send(:flatten_merge, set2).should == Set[1, 2, 3, 4, 5, 6] + end + + it "raises an ArgumentError when trying to flatten a recursive Set" do + set1 = Set[1, 2, 3] + set2 = Set[5, 6, 7] + set2 << set2 + + lambda { set1.send(:flatten_merge, set2) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/flatten_spec.rb b/spec/rubyspec/library/set/flatten_spec.rb new file mode 100644 index 0000000000..2fa7f9f6e5 --- /dev/null +++ b/spec/rubyspec/library/set/flatten_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#flatten" do + it "returns a copy of self with each included Set flattened" do + set = Set[1, 2, Set[3, 4, Set[5, 6, Set[7, 8]]], 9, 10] + flattened_set = set.flatten + + flattened_set.should_not equal(set) + flattened_set.should == Set[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + end + + it "raises an ArgumentError when self is recursive" do + (set = Set[]) << set + lambda { set.flatten }.should raise_error(ArgumentError) + end +end + +describe "Set#flatten!" do + it "flattens self" do + set = Set[1, 2, Set[3, 4, Set[5, 6, Set[7, 8]]], 9, 10] + set.flatten! + set.should == Set[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + end + + it "returns self when self was modified" do + set = Set[1, 2, Set[3, 4]] + set.flatten!.should equal(set) + end + + it "returns nil when self was not modified" do + set = Set[1, 2, 3, 4] + set.flatten!.should be_nil + end + + it "raises an ArgumentError when self is recursive" do + (set = Set[]) << set + lambda { set.flatten! }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/hash_spec.rb b/spec/rubyspec/library/set/hash_spec.rb new file mode 100644 index 0000000000..ea39105937 --- /dev/null +++ b/spec/rubyspec/library/set/hash_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#hash" do + it "is static" do + Set[].hash.should == Set[].hash + Set[1, 2, 3].hash.should == Set[1, 2, 3].hash + Set[:a, "b", ?c].hash.should == Set[?c, "b", :a].hash + + Set[].hash.should_not == Set[1, 2, 3].hash + Set[1, 2, 3].hash.should_not == Set[:a, "b", ?c].hash + end +end diff --git a/spec/rubyspec/library/set/include_spec.rb b/spec/rubyspec/library/set/include_spec.rb new file mode 100644 index 0000000000..693b8b997f --- /dev/null +++ b/spec/rubyspec/library/set/include_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/include', __FILE__) +require 'set' + +describe "Set#include?" do + it_behaves_like :set_include, :include? +end diff --git a/spec/rubyspec/library/set/initialize_spec.rb b/spec/rubyspec/library/set/initialize_spec.rb new file mode 100644 index 0000000000..e14cbaacc2 --- /dev/null +++ b/spec/rubyspec/library/set/initialize_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#initialize" do + it "is private" do + Set.should have_private_instance_method(:initialize) + end + + it "adds all elements of the passed Enumerable to self" do + s = Set.new([1, 2, 3]) + s.size.should eql(3) + s.should include(1) + s.should include(2) + s.should include(3) + end + + it "preprocesses all elements by a passed block before adding to self" do + s = Set.new([1, 2, 3]) { |x| x * x } + s.size.should eql(3) + s.should include(1) + s.should include(4) + s.should include(9) + end +end diff --git a/spec/rubyspec/library/set/inspect_spec.rb b/spec/rubyspec/library/set/inspect_spec.rb new file mode 100644 index 0000000000..8a6c565c2e --- /dev/null +++ b/spec/rubyspec/library/set/inspect_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#inspect" do + it "returns a String representation of self" do + Set[].inspect.should be_kind_of(String) + Set[nil, false, true].inspect.should be_kind_of(String) + Set[1, 2, 3].inspect.should be_kind_of(String) + Set["1", "2", "3"].inspect.should be_kind_of(String) + Set[:a, "b", Set[?c]].inspect.should be_kind_of(String) + end + + it "correctly handles self-references" do + (set = Set[]) << set + set.inspect.should be_kind_of(String) + set.inspect.should include("#") + end +end diff --git a/spec/rubyspec/library/set/intersection_spec.rb b/spec/rubyspec/library/set/intersection_spec.rb new file mode 100644 index 0000000000..c5adef5fc4 --- /dev/null +++ b/spec/rubyspec/library/set/intersection_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/intersection', __FILE__) +require 'set' + +describe "Set#intersection" do + it_behaves_like :set_intersection, :intersection +end + +describe "Set#&" do + it_behaves_like :set_intersection, :& +end diff --git a/spec/rubyspec/library/set/keep_if_spec.rb b/spec/rubyspec/library/set/keep_if_spec.rb new file mode 100644 index 0000000000..45f2cbf07a --- /dev/null +++ b/spec/rubyspec/library/set/keep_if_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#keep_if" do + before :each do + @set = Set["one", "two", "three"] + end + + it "yields every element of self" do + ret = [] + @set.keep_if { |x| ret << x } + ret.sort.should == ["one", "two", "three"].sort + end + + it "keeps every element from self for which the passed block returns true" do + @set.keep_if { |x| x.size != 3 } + @set.size.should eql(1) + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end + + it "returns self" do + @set.keep_if {}.should equal(@set) + end + + it "returns an Enumerator when passed no block" do + enum = @set.keep_if + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size != 3 } + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end +end diff --git a/spec/rubyspec/library/set/length_spec.rb b/spec/rubyspec/library/set/length_spec.rb new file mode 100644 index 0000000000..274abcde83 --- /dev/null +++ b/spec/rubyspec/library/set/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'set' + +describe "Set#length" do + it_behaves_like :set_length, :length +end diff --git a/spec/rubyspec/library/set/map_spec.rb b/spec/rubyspec/library/set/map_spec.rb new file mode 100644 index 0000000000..8610982ba9 --- /dev/null +++ b/spec/rubyspec/library/set/map_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/collect', __FILE__) + +describe "Set#map!" do + it_behaves_like :set_collect_bang, :map! +end diff --git a/spec/rubyspec/library/set/member_spec.rb b/spec/rubyspec/library/set/member_spec.rb new file mode 100644 index 0000000000..1a807c061b --- /dev/null +++ b/spec/rubyspec/library/set/member_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/include', __FILE__) +require 'set' + +describe "Set#member?" do + it_behaves_like :set_include, :member? +end diff --git a/spec/rubyspec/library/set/merge_spec.rb b/spec/rubyspec/library/set/merge_spec.rb new file mode 100644 index 0000000000..ecee51b951 --- /dev/null +++ b/spec/rubyspec/library/set/merge_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#merge" do + it "adds the elements of the passed Enumerable to self" do + Set[:a, :b].merge(Set[:b, :c, :d]).should == Set[:a, :b, :c, :d] + Set[1, 2].merge([3, 4]).should == Set[1, 2, 3, 4] + end + + it "returns self" do + set = Set[1, 2] + set.merge([3, 4]).should equal(set) + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { Set[1, 2].merge(1) }.should raise_error(ArgumentError) + lambda { Set[1, 2].merge(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/minus_spec.rb b/spec/rubyspec/library/set/minus_spec.rb new file mode 100644 index 0000000000..3b14d6649d --- /dev/null +++ b/spec/rubyspec/library/set/minus_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/difference', __FILE__) + +describe "Set#-" do + it_behaves_like :set_difference, :- +end diff --git a/spec/rubyspec/library/set/plus_spec.rb b/spec/rubyspec/library/set/plus_spec.rb new file mode 100644 index 0000000000..38b78b6330 --- /dev/null +++ b/spec/rubyspec/library/set/plus_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/union', __FILE__) +require 'set' + +describe "Set#+" do + it_behaves_like :set_union, :+ +end diff --git a/spec/rubyspec/library/set/pretty_print_cycle_spec.rb b/spec/rubyspec/library/set/pretty_print_cycle_spec.rb new file mode 100644 index 0000000000..a7eaab337b --- /dev/null +++ b/spec/rubyspec/library/set/pretty_print_cycle_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#pretty_print_cycle" do + it "passes the 'pretty print' representation of a self-referencing Set to the pretty print writer" do + pp = mock("PrettyPrint") + pp.should_receive(:text).with("#") + Set[1, 2, 3].pretty_print_cycle(pp) + end +end diff --git a/spec/rubyspec/library/set/pretty_print_spec.rb b/spec/rubyspec/library/set/pretty_print_spec.rb new file mode 100644 index 0000000000..60f3ba7d3d --- /dev/null +++ b/spec/rubyspec/library/set/pretty_print_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#pretty_print" do + it "passes the 'pretty print' representation of self to the pretty print writer" do + pp = mock("PrettyPrint") + set = Set[1, 2, 3] + + pp.should_receive(:text).with("#") + + pp.should_receive(:nest).with(1).and_yield + pp.should_receive(:seplist).with(set) + + set.pretty_print(pp) + end +end diff --git a/spec/rubyspec/library/set/proper_subset_spec.rb b/spec/rubyspec/library/set/proper_subset_spec.rb new file mode 100644 index 0000000000..1a1fa13c3c --- /dev/null +++ b/spec/rubyspec/library/set/proper_subset_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#proper_subset?" do + before :each do + @set = Set[1, 2, 3, 4] + end + + it "returns true if passed a Set that self is a proper subset of" do + Set[].proper_subset?(@set).should be_true + Set[].proper_subset?(Set[1, 2, 3]).should be_true + Set[].proper_subset?(Set["a", :b, ?c]).should be_true + + Set[1, 2, 3].proper_subset?(@set).should be_true + Set[1, 3].proper_subset?(@set).should be_true + Set[1, 2].proper_subset?(@set).should be_true + Set[1].proper_subset?(@set).should be_true + + Set[5].proper_subset?(@set).should be_false + Set[1, 5].proper_subset?(@set).should be_false + Set[nil].proper_subset?(@set).should be_false + Set["test"].proper_subset?(@set).should be_false + + @set.proper_subset?(@set).should be_false + Set[].proper_subset?(Set[]).should be_false + end + + it "raises an ArgumentError when passed a non-Set" do + lambda { Set[].proper_subset?([]) }.should raise_error(ArgumentError) + lambda { Set[].proper_subset?(1) }.should raise_error(ArgumentError) + lambda { Set[].proper_subset?("test") }.should raise_error(ArgumentError) + lambda { Set[].proper_subset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/proper_superset_spec.rb b/spec/rubyspec/library/set/proper_superset_spec.rb new file mode 100644 index 0000000000..61c7984a6b --- /dev/null +++ b/spec/rubyspec/library/set/proper_superset_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#proper_superset?" do + before :each do + @set = Set[1, 2, 3, 4] + end + + it "returns true if passed a Set that self is a proper superset of" do + @set.proper_superset?(Set[]).should be_true + Set[1, 2, 3].proper_superset?(Set[]).should be_true + Set["a", :b, ?c].proper_superset?(Set[]).should be_true + + @set.proper_superset?(Set[1, 2, 3]).should be_true + @set.proper_superset?(Set[1, 3]).should be_true + @set.proper_superset?(Set[1, 2]).should be_true + @set.proper_superset?(Set[1]).should be_true + + @set.proper_superset?(Set[5]).should be_false + @set.proper_superset?(Set[1, 5]).should be_false + @set.proper_superset?(Set[nil]).should be_false + @set.proper_superset?(Set["test"]).should be_false + + @set.proper_superset?(@set).should be_false + Set[].proper_superset?(Set[]).should be_false + end + + it "raises an ArgumentError when passed a non-Set" do + lambda { Set[].proper_superset?([]) }.should raise_error(ArgumentError) + lambda { Set[].proper_superset?(1) }.should raise_error(ArgumentError) + lambda { Set[].proper_superset?("test") }.should raise_error(ArgumentError) + lambda { Set[].proper_superset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/reject_spec.rb b/spec/rubyspec/library/set/reject_spec.rb new file mode 100644 index 0000000000..32d3a92801 --- /dev/null +++ b/spec/rubyspec/library/set/reject_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#reject!" do + before :each do + @set = Set["one", "two", "three"] + end + + it "yields every element of self" do + ret = [] + @set.reject! { |x| ret << x } + ret.sort.should == ["one", "two", "three"].sort + end + + it "deletes every element from self for which the passed block returns true" do + @set.reject! { |x| x.size == 3 } + @set.size.should eql(1) + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end + + it "returns self when self was modified" do + @set.reject! { |x| true }.should equal(@set) + end + + it "returns nil when self was not modified" do + @set.reject! { |x| false }.should be_nil + end + + it "returns an Enumerator when passed no block" do + enum = @set.reject! + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size == 3 } + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end +end diff --git a/spec/rubyspec/library/set/replace_spec.rb b/spec/rubyspec/library/set/replace_spec.rb new file mode 100644 index 0000000000..6f0c45b7ed --- /dev/null +++ b/spec/rubyspec/library/set/replace_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#replace" do + before :each do + @set = Set[:a, :b, :c] + end + + it "replaces the contents with other and returns self" do + @set.replace(Set[1, 2, 3]).should == @set + @set.should == Set[1, 2, 3] + end + + it "accepts any enumerable as other" do + @set.replace([1, 2, 3]).should == Set[1, 2, 3] + end +end diff --git a/spec/rubyspec/library/set/select_spec.rb b/spec/rubyspec/library/set/select_spec.rb new file mode 100644 index 0000000000..34274a7c46 --- /dev/null +++ b/spec/rubyspec/library/set/select_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#select!" do + before :each do + @set = Set["one", "two", "three"] + end + + it "yields every element of self" do + ret = [] + @set.select! { |x| ret << x } + ret.sort.should == ["one", "two", "three"].sort + end + + it "keeps every element from self for which the passed block returns true" do + @set.select! { |x| x.size != 3 } + @set.size.should eql(1) + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end + + it "returns self when self was modified" do + @set.select! { false }.should equal(@set) + end + + it "returns nil when self was not modified" do + @set.select! { true }.should be_nil + end + + it "returns an Enumerator when passed no block" do + enum = @set.select! + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size != 3 } + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end +end diff --git a/spec/rubyspec/library/set/shared/add.rb b/spec/rubyspec/library/set/shared/add.rb new file mode 100644 index 0000000000..9e797f5df9 --- /dev/null +++ b/spec/rubyspec/library/set/shared/add.rb @@ -0,0 +1,14 @@ +describe :set_add, shared: true do + before :each do + @set = Set.new + end + + it "adds the passed Object to self" do + @set.send(@method, "dog") + @set.should include("dog") + end + + it "returns self" do + @set.send(@method, "dog").should equal(@set) + end +end diff --git a/spec/rubyspec/library/set/shared/collect.rb b/spec/rubyspec/library/set/shared/collect.rb new file mode 100644 index 0000000000..bc58c231be --- /dev/null +++ b/spec/rubyspec/library/set/shared/collect.rb @@ -0,0 +1,20 @@ +describe :set_collect_bang, shared: true do + before :each do + @set = Set[1, 2, 3, 4, 5] + end + + it "yields each Object in self" do + res = [] + @set.send(@method) { |x| res << x } + res.sort.should == [1, 2, 3, 4, 5].sort + end + + it "returns self" do + @set.send(@method) { |x| x }.should equal(@set) + end + + it "replaces self with the return values of the block" do + @set.send(@method) { |x| x * 2 } + @set.should == Set[2, 4, 6, 8, 10] + end +end diff --git a/spec/rubyspec/library/set/shared/difference.rb b/spec/rubyspec/library/set/shared/difference.rb new file mode 100644 index 0000000000..9439715da7 --- /dev/null +++ b/spec/rubyspec/library/set/shared/difference.rb @@ -0,0 +1,15 @@ +describe :set_difference, shared: true do + before :each do + @set = Set[:a, :b, :c] + end + + it "returns a new Set containting self's elements excluding the elements in the passed Enumerable" do + @set.send(@method, Set[:a, :b]).should == Set[:c] + @set.send(@method, [:b, :c]).should == Set[:a] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set.send(@method, 1) }.should raise_error(ArgumentError) + lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/shared/include.rb b/spec/rubyspec/library/set/shared/include.rb new file mode 100644 index 0000000000..d41f8f4102 --- /dev/null +++ b/spec/rubyspec/library/set/shared/include.rb @@ -0,0 +1,7 @@ +describe :set_include, shared: true do + it "returns true when self contains the passed Object" do + set = Set[:a, :b, :c] + set.send(@method, :a).should be_true + set.send(@method, :e).should be_false + end +end diff --git a/spec/rubyspec/library/set/shared/intersection.rb b/spec/rubyspec/library/set/shared/intersection.rb new file mode 100644 index 0000000000..ed0db7457d --- /dev/null +++ b/spec/rubyspec/library/set/shared/intersection.rb @@ -0,0 +1,15 @@ +describe :set_intersection, shared: true do + before :each do + @set = Set[:a, :b, :c] + end + + it "returns a new Set containing only elements shared by self and the passed Enumerable" do + @set.send(@method, Set[:b, :c, :d, :e]).should == Set[:b, :c] + @set.send(@method, [:b, :c, :d]).should == Set[:b, :c] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set.send(@method, 1) }.should raise_error(ArgumentError) + lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/shared/length.rb b/spec/rubyspec/library/set/shared/length.rb new file mode 100644 index 0000000000..a8fcee9f39 --- /dev/null +++ b/spec/rubyspec/library/set/shared/length.rb @@ -0,0 +1,6 @@ +describe :set_length, shared: true do + it "returns the number of elements in the set" do + set = Set[:a, :b, :c] + set.send(@method).should == 3 + end +end diff --git a/spec/rubyspec/library/set/shared/union.rb b/spec/rubyspec/library/set/shared/union.rb new file mode 100644 index 0000000000..81920f5687 --- /dev/null +++ b/spec/rubyspec/library/set/shared/union.rb @@ -0,0 +1,15 @@ +describe :set_union, shared: true do + before :each do + @set = Set[:a, :b, :c] + end + + it "returns a new Set containing all elements of self and the passed Enumerable" do + @set.send(@method, Set[:b, :d, :e]).should == Set[:a, :b, :c, :d, :e] + @set.send(@method, [:b, :e]).should == Set[:a, :b, :c, :e] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set.send(@method, 1) }.should raise_error(ArgumentError) + lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/size_spec.rb b/spec/rubyspec/library/set/size_spec.rb new file mode 100644 index 0000000000..f33740c5cd --- /dev/null +++ b/spec/rubyspec/library/set/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'set' + +describe "Set#size" do + it_behaves_like :set_length, :size +end diff --git a/spec/rubyspec/library/set/sortedset/add_spec.rb b/spec/rubyspec/library/set/sortedset/add_spec.rb new file mode 100644 index 0000000000..bdc5c077d8 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/add_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/add', __FILE__) + +describe "SortedSet#add" do + it_behaves_like :sorted_set_add, :add + + it "takes only values which responds <=>" do + obj = mock('no_comparison_operator') + obj.should_receive(:respond_to?).with(:<=>).and_return(false) + lambda { SortedSet["hello"].add(obj) }.should raise_error(ArgumentError) + end +end + +describe "SortedSet#add?" do + before :each do + @set = SortedSet.new + end + + it "adds the passed Object to self" do + @set.add?("cat") + @set.should include("cat") + end + + it "returns self when the Object has not yet been added to self" do + @set.add?("cat").should equal(@set) + end + + it "returns nil when the Object has already been added to self" do + @set.add?("cat") + @set.add?("cat").should be_nil + end +end diff --git a/spec/rubyspec/library/set/sortedset/append_spec.rb b/spec/rubyspec/library/set/sortedset/append_spec.rb new file mode 100644 index 0000000000..62933f3e42 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/add', __FILE__) + +describe "SortedSet#<<" do + it_behaves_like :sorted_set_add, :<< +end diff --git a/spec/rubyspec/library/set/sortedset/classify_spec.rb b/spec/rubyspec/library/set/sortedset/classify_spec.rb new file mode 100644 index 0000000000..1e8c814699 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/classify_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#classify" do + before :each do + @set = SortedSet["one", "two", "three", "four"] + end + + it "yields each Object in self in sorted order" do + res = [] + @set.classify { |x| res << x } + res.should == ["one", "two", "three", "four"].sort + end + + it "returns an Enumerator when passed no block" do + enum = @set.classify + enum.should be_an_instance_of(Enumerator) + + classified = enum.each { |x| x.length } + classified.should == { 3 => SortedSet["one", "two"], 4 => SortedSet["four"], 5 => SortedSet["three"] } + end + + it "classifies the Objects in self based on the block's return value" do + classified = @set.classify { |x| x.length } + classified.should == { 3 => SortedSet["one", "two"], 4 => SortedSet["four"], 5 => SortedSet["three"] } + end +end diff --git a/spec/rubyspec/library/set/sortedset/clear_spec.rb b/spec/rubyspec/library/set/sortedset/clear_spec.rb new file mode 100644 index 0000000000..3a3277dd8a --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/clear_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#clear" do + before :each do + @set = SortedSet["one", "two", "three", "four"] + end + + it "removes all elements from self" do + @set.clear + @set.should be_empty + end + + it "returns self" do + @set.clear.should equal(@set) + end +end diff --git a/spec/rubyspec/library/set/sortedset/collect_spec.rb b/spec/rubyspec/library/set/sortedset/collect_spec.rb new file mode 100644 index 0000000000..18274e6353 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/collect_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/collect', __FILE__) + +describe "SortedSet#collect!" do + it_behaves_like :sorted_set_collect_bang, :collect! +end diff --git a/spec/rubyspec/library/set/sortedset/constructor_spec.rb b/spec/rubyspec/library/set/sortedset/constructor_spec.rb new file mode 100644 index 0000000000..45b6749e27 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/constructor_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet[]" do + it "returns a new SortedSet populated with the passed Objects" do + set = SortedSet[1, 2, 3] + + set.instance_of?(SortedSet).should be_true + set.size.should eql(3) + + set.should include(1) + set.should include(2) + set.should include(3) + end +end diff --git a/spec/rubyspec/library/set/sortedset/delete_if_spec.rb b/spec/rubyspec/library/set/sortedset/delete_if_spec.rb new file mode 100644 index 0000000000..c809ff75f0 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/delete_if_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#delete_if" do + before :each do + @set = SortedSet["one", "two", "three"] + end + + it "yields each Object in self in sorted order" do + ret = [] + @set.delete_if { |x| ret << x } + ret.should == ["one", "two", "three"].sort + end + + it "deletes every element from self for which the passed block returns true" do + @set.delete_if { |x| x.size == 3 } + @set.size.should eql(1) + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end + + it "returns self" do + @set.delete_if { |x| x }.should equal(@set) + end + + it "returns an Enumerator when passed no block" do + enum = @set.delete_if + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size == 3 } + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end +end diff --git a/spec/rubyspec/library/set/sortedset/delete_spec.rb b/spec/rubyspec/library/set/sortedset/delete_spec.rb new file mode 100644 index 0000000000..7123f79bcf --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/delete_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#delete" do + before :each do + @set = SortedSet["a", "b", "c"] + end + + it "deletes the passed Object from self" do + @set.delete("a") + @set.should_not include("a") + end + + it "returns self" do + @set.delete("a").should equal(@set) + @set.delete("x").should equal(@set) + end +end + +describe "SortedSet#delete?" do + before :each do + @set = SortedSet["a", "b", "c"] + end + + it "deletes the passed Object from self" do + @set.delete?("a") + @set.should_not include("a") + end + + it "returns self when the passed Object is in self" do + @set.delete?("a").should equal(@set) + end + + it "returns nil when the passed Object is not in self" do + @set.delete?("x").should be_nil + end +end diff --git a/spec/rubyspec/library/set/sortedset/difference_spec.rb b/spec/rubyspec/library/set/sortedset/difference_spec.rb new file mode 100644 index 0000000000..bc3650c55c --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/difference_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/difference', __FILE__) + +describe "SortedSet#difference" do + it_behaves_like :sorted_set_difference, :difference +end diff --git a/spec/rubyspec/library/set/sortedset/divide_spec.rb b/spec/rubyspec/library/set/sortedset/divide_spec.rb new file mode 100644 index 0000000000..adb152b7e6 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/divide_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#divide" do + it "divides self into a set of subsets based on the blocks return values" do + set = SortedSet["one", "two", "three", "four", "five"].divide { |x| x.length } + set.map { |x| x.to_a }.to_a.sort.should == [["five", "four"], ["one", "two"], ["three"]] + end + + it "yields each Object in self in sorted order" do + ret = [] + SortedSet["one", "two", "three", "four", "five"].divide { |x| ret << x } + ret.should == ["one", "two", "three", "four", "five"].sort + end + + # BUG: Does not raise a LocalJumpError, but a NoMethodError + # + # it "raises a LocalJumpError when not passed a block" do + # lambda { SortedSet[1].divide }.should raise_error(LocalJumpError) + # end +end + +describe "SortedSet#divide when passed a block with an arity of 2" do + it "divides self into a set of subsets based on the blocks return values" do + set = SortedSet[1, 3, 4, 6, 9, 10, 11].divide { |x, y| (x - y).abs == 1 } + set.map { |x| x.to_a }.to_a.sort.should == [[1], [3, 4], [6], [9, 10, 11]] + end + + it "yields each two Objects to the block" do + ret = [] + SortedSet[1, 2].divide { |x, y| ret << [x, y] } + ret.should == [[1, 1], [1, 2], [2, 1], [2, 2]] + end +end diff --git a/spec/rubyspec/library/set/sortedset/each_spec.rb b/spec/rubyspec/library/set/sortedset/each_spec.rb new file mode 100644 index 0000000000..c715c403b2 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/each_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#each" do + before :each do + @set = SortedSet[1, 2, 3] + end + + it "yields each Object in self in sorted order" do + ret = [] + SortedSet["one", "two", "three"].each { |x| ret << x } + ret.should == ["one", "two", "three"].sort + end + + it "returns self" do + @set.each { |x| x }.should equal(@set) + end + + it "returns an Enumerator when not passed a block" do + enum = @set.each + + ret = [] + enum.each { |x| ret << x } + ret.sort.should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/library/set/sortedset/empty_spec.rb b/spec/rubyspec/library/set/sortedset/empty_spec.rb new file mode 100644 index 0000000000..50d046e4c0 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/empty_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#empty?" do + it "returns true if self is empty" do + SortedSet[].empty?.should be_true + SortedSet[1].empty?.should be_false + SortedSet[1,2,3].empty?.should be_false + end +end diff --git a/spec/rubyspec/library/set/sortedset/eql_spec.rb b/spec/rubyspec/library/set/sortedset/eql_spec.rb new file mode 100644 index 0000000000..e7b3e7b624 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/eql_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#eql?" do + it "returns true when the passed argument is a SortedSet and contains the same elements" do + SortedSet[].should eql(SortedSet[]) + SortedSet[1, 2, 3].should eql(SortedSet[1, 2, 3]) + SortedSet[1, 2, 3].should eql(SortedSet[3, 2, 1]) + +# SortedSet["a", :b, ?c].should eql(SortedSet[?c, :b, "a"]) + + SortedSet[1, 2, 3].should_not eql(SortedSet[1.0, 2, 3]) + SortedSet[1, 2, 3].should_not eql(SortedSet[2, 3]) + SortedSet[1, 2, 3].should_not eql(SortedSet[]) + end +end diff --git a/spec/rubyspec/library/set/sortedset/equal_value_spec.rb b/spec/rubyspec/library/set/sortedset/equal_value_spec.rb new file mode 100644 index 0000000000..e358372aa4 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/equal_value_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#==" do + it "returns true when the passed Object is a SortedSet and self and the Object contain the same elements" do + SortedSet[].should == SortedSet[] + SortedSet[1, 2, 3].should == SortedSet[1, 2, 3] + SortedSet["1", "2", "3"].should == SortedSet["1", "2", "3"] + + SortedSet[1, 2, 3].should_not == SortedSet[1.0, 2, 3] + SortedSet[1, 2, 3].should_not == [1, 2, 3] + end +end diff --git a/spec/rubyspec/library/set/sortedset/exclusion_spec.rb b/spec/rubyspec/library/set/sortedset/exclusion_spec.rb new file mode 100644 index 0000000000..193dbb7725 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/exclusion_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#^" do + before :each do + @set = SortedSet[1, 2, 3, 4] + end + + it "returns a new SortedSet containing elements that are not in both self and the passed Enumberable" do + (@set ^ SortedSet[3, 4, 5]).should == SortedSet[1, 2, 5] + (@set ^ [3, 4, 5]).should == SortedSet[1, 2, 5] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set ^ 3 }.should raise_error(ArgumentError) + lambda { @set ^ Object.new }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/flatten_merge_spec.rb b/spec/rubyspec/library/set/sortedset/flatten_merge_spec.rb new file mode 100644 index 0000000000..2a2505a58b --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/flatten_merge_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#flatten_merge" do + it "is protected" do + SortedSet.should have_protected_instance_method("flatten_merge") + end +end diff --git a/spec/rubyspec/library/set/sortedset/flatten_spec.rb b/spec/rubyspec/library/set/sortedset/flatten_spec.rb new file mode 100644 index 0000000000..80d064b846 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/flatten_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +# Note: Flatten make little sens on sorted sets, because SortedSets are not (by default) +# comparable. For a SortedSet to be both valid and nested, we need to define a comparison operator: +module SortedSet_FlattenSpecs + class ComparableSortedSet < SortedSet + def <=>(other) + return puts "#{other} vs #{self}" unless other.is_a?(ComparableSortedSet) + to_a <=> other.to_a + end + end +end + +describe "SortedSet#flatten" do + it "returns a copy of self with each included SortedSet flattened" do + klass = SortedSet_FlattenSpecs::ComparableSortedSet + set = klass[klass[1,2], klass[3,4], klass[5,6,7], klass[8]] + flattened_set = set.flatten + + flattened_set.should_not equal(set) + flattened_set.should == klass[1, 2, 3, 4, 5, 6, 7, 8] + end +end + +describe "SortedSet#flatten!" do + it "flattens self" do + klass = SortedSet_FlattenSpecs::ComparableSortedSet + set = klass[klass[1,2], klass[3,4], klass[5,6,7], klass[8]] + set.flatten! + set.should == klass[1, 2, 3, 4, 5, 6, 7, 8] + end + + it "returns self when self was modified" do + klass = SortedSet_FlattenSpecs::ComparableSortedSet + set = klass[klass[1,2], klass[3,4]] + set.flatten!.should equal(set) + end + + it "returns nil when self was not modified" do + set = SortedSet[1, 2, 3, 4] + set.flatten!.should be_nil + end +end diff --git a/spec/rubyspec/library/set/sortedset/hash_spec.rb b/spec/rubyspec/library/set/sortedset/hash_spec.rb new file mode 100644 index 0000000000..176cb7e8dc --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/hash_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#hash" do + it "is static" do + SortedSet[].hash.should == SortedSet[].hash + SortedSet[1, 2, 3].hash.should == SortedSet[1, 2, 3].hash + SortedSet["a", "b", "c"].hash.should == SortedSet["c", "b", "a"].hash + + SortedSet[].hash.should_not == SortedSet[1, 2, 3].hash + SortedSet[1, 2, 3].hash.should_not == SortedSet["a", "b", "c"].hash + end +end diff --git a/spec/rubyspec/library/set/sortedset/include_spec.rb b/spec/rubyspec/library/set/sortedset/include_spec.rb new file mode 100644 index 0000000000..bb8ceda708 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/include_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/include', __FILE__) +require 'set' + +describe "SortedSet#include?" do + it_behaves_like :sorted_set_include, :include? +end diff --git a/spec/rubyspec/library/set/sortedset/initialize_spec.rb b/spec/rubyspec/library/set/sortedset/initialize_spec.rb new file mode 100644 index 0000000000..04ad908667 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/initialize_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#initialize" do + it "is private" do + SortedSet.should have_private_instance_method("initialize") + end + + it "adds all elements of the passed Enumerable to self" do + s = SortedSet.new([1, 2, 3]) + s.size.should eql(3) + s.should include(1) + s.should include(2) + s.should include(3) + end + + it "preprocesses all elements by a passed block before adding to self" do + s = SortedSet.new([1, 2, 3]) { |x| x * x } + s.size.should eql(3) + s.should include(1) + s.should include(4) + s.should include(9) + end +end diff --git a/spec/rubyspec/library/set/sortedset/inspect_spec.rb b/spec/rubyspec/library/set/sortedset/inspect_spec.rb new file mode 100644 index 0000000000..64b3f3d882 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/inspect_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#inspect" do + it "returns a String representation of self" do + SortedSet[].inspect.should be_kind_of(String) + SortedSet[1, 2, 3].inspect.should be_kind_of(String) + SortedSet["1", "2", "3"].inspect.should be_kind_of(String) + end +end diff --git a/spec/rubyspec/library/set/sortedset/intersection_spec.rb b/spec/rubyspec/library/set/sortedset/intersection_spec.rb new file mode 100644 index 0000000000..d3f5b49ceb --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/intersection_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/intersection', __FILE__) +require 'set' + +describe "SortedSet#intersection" do + it_behaves_like :sorted_set_intersection, :intersection +end + +describe "SortedSet#&" do + it_behaves_like :sorted_set_intersection, :& +end diff --git a/spec/rubyspec/library/set/sortedset/keep_if_spec.rb b/spec/rubyspec/library/set/sortedset/keep_if_spec.rb new file mode 100644 index 0000000000..7a117fbc87 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/keep_if_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#keep_if" do + before :each do + @set = SortedSet["one", "two", "three"] + end + + it "yields each Object in self in sorted order" do + ret = [] + @set.keep_if { |x| ret << x } + ret.should == ["one", "two", "three"].sort + end + + it "keeps every element from self for which the passed block returns true" do + @set.keep_if { |x| x.size != 3 } + @set.to_a.should == ["three"] + end + + it "returns self" do + @set.keep_if {}.should equal(@set) + end + + it "returns an Enumerator when passed no block" do + enum = @set.keep_if + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size != 3 } + @set.to_a.should == ["three"] + end +end diff --git a/spec/rubyspec/library/set/sortedset/length_spec.rb b/spec/rubyspec/library/set/sortedset/length_spec.rb new file mode 100644 index 0000000000..d829b3e08e --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'set' + +describe "SortedSet#length" do + it_behaves_like :sorted_set_length, :length +end diff --git a/spec/rubyspec/library/set/sortedset/map_spec.rb b/spec/rubyspec/library/set/sortedset/map_spec.rb new file mode 100644 index 0000000000..1f0828f347 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/map_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/collect', __FILE__) + +describe "SortedSet#map!" do + it_behaves_like :sorted_set_collect_bang, :map! +end diff --git a/spec/rubyspec/library/set/sortedset/member_spec.rb b/spec/rubyspec/library/set/sortedset/member_spec.rb new file mode 100644 index 0000000000..d64e318b83 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/member_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/include', __FILE__) +require 'set' + +describe "SortedSet#member?" do + it_behaves_like :sorted_set_include, :member? +end diff --git a/spec/rubyspec/library/set/sortedset/merge_spec.rb b/spec/rubyspec/library/set/sortedset/merge_spec.rb new file mode 100644 index 0000000000..c422fe9513 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/merge_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#merge" do + it "adds the elements of the passed Enumerable to self" do + SortedSet["a", "b"].merge(SortedSet["b", "c", "d"]).should == SortedSet["a", "b", "c", "d"] + SortedSet[1, 2].merge([3, 4]).should == SortedSet[1, 2, 3, 4] + end + + it "returns self" do + set = SortedSet[1, 2] + set.merge([3, 4]).should equal(set) + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { SortedSet[1, 2].merge(1) }.should raise_error(ArgumentError) + lambda { SortedSet[1, 2].merge(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/minus_spec.rb b/spec/rubyspec/library/set/sortedset/minus_spec.rb new file mode 100644 index 0000000000..1f56d57037 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/minus_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' +require File.expand_path('../shared/difference', __FILE__) + +describe "SortedSet#-" do + it_behaves_like :sorted_set_difference, :- +end diff --git a/spec/rubyspec/library/set/sortedset/plus_spec.rb b/spec/rubyspec/library/set/sortedset/plus_spec.rb new file mode 100644 index 0000000000..af9bdf82fa --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/plus_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/union', __FILE__) +require 'set' + +describe "SortedSet#+" do + it_behaves_like :sorted_set_union, :+ +end diff --git a/spec/rubyspec/library/set/sortedset/pretty_print_cycle_spec.rb b/spec/rubyspec/library/set/sortedset/pretty_print_cycle_spec.rb new file mode 100644 index 0000000000..6e79245e18 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/pretty_print_cycle_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#pretty_print_cycle" do + it "passes the 'pretty print' representation of a self-referencing SortedSet to the pretty print writer" do + pp = mock("PrettyPrint") + pp.should_receive(:text).with("#") + SortedSet[1, 2, 3].pretty_print_cycle(pp) + end +end diff --git a/spec/rubyspec/library/set/sortedset/pretty_print_spec.rb b/spec/rubyspec/library/set/sortedset/pretty_print_spec.rb new file mode 100644 index 0000000000..5317357b8f --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/pretty_print_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#pretty_print" do + it "passes the 'pretty print' representation of self to the pretty print writer" do + pp = mock("PrettyPrint") + set = SortedSet[1, 2, 3] + + pp.should_receive(:text).with("#") + + pp.should_receive(:nest).with(1).and_yield + pp.should_receive(:seplist).with(set) + + set.pretty_print(pp) + end +end diff --git a/spec/rubyspec/library/set/sortedset/proper_subset_spec.rb b/spec/rubyspec/library/set/sortedset/proper_subset_spec.rb new file mode 100644 index 0000000000..7e94774c1f --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/proper_subset_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#proper_subset?" do + before :each do + @set = SortedSet[1, 2, 3, 4] + end + + it "returns true if passed a SortedSet that self is a proper subset of" do + SortedSet[].proper_subset?(@set).should be_true + SortedSet[].proper_subset?(SortedSet[1, 2, 3]).should be_true + SortedSet[].proper_subset?(SortedSet["a", "b", "c"]).should be_true + + SortedSet[1, 2, 3].proper_subset?(@set).should be_true + SortedSet[1, 3].proper_subset?(@set).should be_true + SortedSet[1, 2].proper_subset?(@set).should be_true + SortedSet[1].proper_subset?(@set).should be_true + + SortedSet[5].proper_subset?(@set).should be_false + SortedSet[1, 5].proper_subset?(@set).should be_false + SortedSet["test"].proper_subset?(@set).should be_false + + @set.proper_subset?(@set).should be_false + SortedSet[].proper_subset?(SortedSet[]).should be_false + end + + it "raises an ArgumentError when passed a non-SortedSet" do + lambda { SortedSet[].proper_subset?([]) }.should raise_error(ArgumentError) + lambda { SortedSet[].proper_subset?(1) }.should raise_error(ArgumentError) + lambda { SortedSet[].proper_subset?("test") }.should raise_error(ArgumentError) + lambda { SortedSet[].proper_subset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/proper_superset_spec.rb b/spec/rubyspec/library/set/sortedset/proper_superset_spec.rb new file mode 100644 index 0000000000..ccfa37988d --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/proper_superset_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#proper_superset?" do + before :each do + @set = SortedSet[1, 2, 3, 4] + end + + it "returns true if passed a SortedSet that self is a proper superset of" do + @set.proper_superset?(SortedSet[]).should be_true + SortedSet[1, 2, 3].proper_superset?(SortedSet[]).should be_true + SortedSet["a", "b", "c"].proper_superset?(SortedSet[]).should be_true + + @set.proper_superset?(SortedSet[1, 2, 3]).should be_true + @set.proper_superset?(SortedSet[1, 3]).should be_true + @set.proper_superset?(SortedSet[1, 2]).should be_true + @set.proper_superset?(SortedSet[1]).should be_true + + @set.proper_superset?(SortedSet[5]).should be_false + @set.proper_superset?(SortedSet[1, 5]).should be_false + @set.proper_superset?(SortedSet["test"]).should be_false + + @set.proper_superset?(@set).should be_false + SortedSet[].proper_superset?(SortedSet[]).should be_false + end + + it "raises an ArgumentError when passed a non-SortedSet" do + lambda { SortedSet[].proper_superset?([]) }.should raise_error(ArgumentError) + lambda { SortedSet[].proper_superset?(1) }.should raise_error(ArgumentError) + lambda { SortedSet[].proper_superset?("test") }.should raise_error(ArgumentError) + lambda { SortedSet[].proper_superset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/reject_spec.rb b/spec/rubyspec/library/set/sortedset/reject_spec.rb new file mode 100644 index 0000000000..e357d55052 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/reject_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#reject!" do + before :each do + @set = SortedSet["one", "two", "three"] + end + + it "yields each Object in self in sorted order" do + res = [] + @set.reject! { |x| res << x } + res.should == ["one", "two", "three"].sort + end + + it "deletes every element from self for which the passed block returns true" do + @set.reject! { |x| x.size == 3 } + @set.size.should eql(1) + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end + + it "returns self when self was modified" do + @set.reject! { |x| true }.should equal(@set) + end + + it "returns nil when self was not modified" do + @set.reject! { |x| false }.should be_nil + end + + it "returns an Enumerator when passed no block" do + enum = @set.reject! + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size == 3 } + + @set.should_not include("one") + @set.should_not include("two") + @set.should include("three") + end +end diff --git a/spec/rubyspec/library/set/sortedset/replace_spec.rb b/spec/rubyspec/library/set/sortedset/replace_spec.rb new file mode 100644 index 0000000000..a5bf333e87 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/replace_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#replace" do + before :each do + @set = SortedSet["a", "b", "c"] + end + + it "replaces the contents with other and returns self" do + @set.replace(SortedSet[1, 2, 3]).should == @set + @set.should == SortedSet[1, 2, 3] + end + + it "accepts any enumerable as other" do + @set.replace([1, 2, 3]).should == SortedSet[1, 2, 3] + end +end diff --git a/spec/rubyspec/library/set/sortedset/select_spec.rb b/spec/rubyspec/library/set/sortedset/select_spec.rb new file mode 100644 index 0000000000..3ca748350a --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/select_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#select!" do + before :each do + @set = SortedSet["one", "two", "three"] + end + + it "yields each Object in self in sorted order" do + res = [] + @set.select! { |x| res << x } + res.should == ["one", "two", "three"].sort + end + + it "keeps every element from self for which the passed block returns true" do + @set.select! { |x| x.size != 3 } + @set.to_a.should == ["three"] + end + + it "returns self when self was modified" do + @set.select! { false }.should equal(@set) + end + + it "returns nil when self was not modified" do + @set.select! { true }.should be_nil + end + + it "returns an Enumerator when passed no block" do + enum = @set.select! + enum.should be_an_instance_of(Enumerator) + + enum.each { |x| x.size != 3 } + @set.to_a.should == ["three"] + end +end diff --git a/spec/rubyspec/library/set/sortedset/shared/add.rb b/spec/rubyspec/library/set/sortedset/shared/add.rb new file mode 100644 index 0000000000..95ef1b090e --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/shared/add.rb @@ -0,0 +1,14 @@ +describe :sorted_set_add, shared: true do + before :each do + @set = SortedSet.new + end + + it "adds the passed Object to self" do + @set.send(@method, "dog") + @set.should include("dog") + end + + it "returns self" do + @set.send(@method, "dog").should equal(@set) + end +end diff --git a/spec/rubyspec/library/set/sortedset/shared/collect.rb b/spec/rubyspec/library/set/sortedset/shared/collect.rb new file mode 100644 index 0000000000..e53304d427 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/shared/collect.rb @@ -0,0 +1,20 @@ +describe :sorted_set_collect_bang, shared: true do + before :each do + @set = SortedSet[1, 2, 3, 4, 5] + end + + it "yields each Object in self in sorted order" do + res = [] + SortedSet["one", "two", "three"].send(@method) { |x| res << x; x } + res.should == ["one", "two", "three"].sort + end + + it "returns self" do + @set.send(@method) { |x| x }.should equal(@set) + end + + it "replaces self with the return values of the block" do + @set.send(@method) { |x| x * 2 } + @set.should == SortedSet[2, 4, 6, 8, 10] + end +end diff --git a/spec/rubyspec/library/set/sortedset/shared/difference.rb b/spec/rubyspec/library/set/sortedset/shared/difference.rb new file mode 100644 index 0000000000..ec57015ac2 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/shared/difference.rb @@ -0,0 +1,15 @@ +describe :sorted_set_difference, shared: true do + before :each do + @set = SortedSet["a", "b", "c"] + end + + it "returns a new SortedSet containting self's elements excluding the elements in the passed Enumerable" do + @set.send(@method, SortedSet["a", "b"]).should == SortedSet["c"] + @set.send(@method, ["b", "c"]).should == SortedSet["a"] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set.send(@method, 1) }.should raise_error(ArgumentError) + lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/shared/include.rb b/spec/rubyspec/library/set/sortedset/shared/include.rb new file mode 100644 index 0000000000..cd1758819d --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/shared/include.rb @@ -0,0 +1,7 @@ +describe :sorted_set_include, shared: true do + it "returns true when self contains the passed Object" do + set = SortedSet["a", "b", "c"] + set.send(@method, "a").should be_true + set.send(@method, "e").should be_false + end +end diff --git a/spec/rubyspec/library/set/sortedset/shared/intersection.rb b/spec/rubyspec/library/set/sortedset/shared/intersection.rb new file mode 100644 index 0000000000..d3cfa96656 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/shared/intersection.rb @@ -0,0 +1,15 @@ +describe :sorted_set_intersection, shared: true do + before :each do + @set = SortedSet["a", "b", "c"] + end + + it "returns a new SortedSet containing only elements shared by self and the passed Enumerable" do + @set.send(@method, SortedSet["b", "c", "d", "e"]).should == SortedSet["b", "c"] + @set.send(@method, ["b", "c", "d"]).should == SortedSet["b", "c"] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set.send(@method, 1) }.should raise_error(ArgumentError) + lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/shared/length.rb b/spec/rubyspec/library/set/sortedset/shared/length.rb new file mode 100644 index 0000000000..d1dfee1cff --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/shared/length.rb @@ -0,0 +1,6 @@ +describe :sorted_set_length, shared: true do + it "returns the number of elements in the set" do + set = SortedSet["a", "b", "c"] + set.send(@method).should == 3 + end +end diff --git a/spec/rubyspec/library/set/sortedset/shared/union.rb b/spec/rubyspec/library/set/sortedset/shared/union.rb new file mode 100644 index 0000000000..4ff07ef5cc --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/shared/union.rb @@ -0,0 +1,15 @@ +describe :sorted_set_union, shared: true do + before :each do + @set = SortedSet["a", "b", "c"] + end + + it "returns a new SortedSet containing all elements of self and the passed Enumerable" do + @set.send(@method, SortedSet["b", "d", "e"]).should == SortedSet["a", "b", "c", "d", "e"] + @set.send(@method, ["b", "e"]).should == SortedSet["a", "b", "c", "e"] + end + + it "raises an ArgumentError when passed a non-Enumerable" do + lambda { @set.send(@method, 1) }.should raise_error(ArgumentError) + lambda { @set.send(@method, Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/size_spec.rb b/spec/rubyspec/library/set/sortedset/size_spec.rb new file mode 100644 index 0000000000..dbcdc3ded3 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'set' + +describe "SortedSet#size" do + it_behaves_like :sorted_set_length, :size +end diff --git a/spec/rubyspec/library/set/sortedset/subset_spec.rb b/spec/rubyspec/library/set/sortedset/subset_spec.rb new file mode 100644 index 0000000000..81f938317c --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/subset_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#subset?" do + before :each do + @set = SortedSet[1, 2, 3, 4] + end + + it "returns true if passed a SortedSet that is equal to self or self is a subset of" do + @set.subset?(@set).should be_true + SortedSet[].subset?(SortedSet[]).should be_true + + SortedSet[].subset?(@set).should be_true + SortedSet[].subset?(SortedSet[1, 2, 3]).should be_true + SortedSet[].subset?(SortedSet["a", "b", "c"]).should be_true + + SortedSet[1, 2, 3].subset?(@set).should be_true + SortedSet[1, 3].subset?(@set).should be_true + SortedSet[1, 2].subset?(@set).should be_true + SortedSet[1].subset?(@set).should be_true + + SortedSet[5].subset?(@set).should be_false + SortedSet[1, 5].subset?(@set).should be_false + SortedSet["test"].subset?(@set).should be_false + end + + it "raises an ArgumentError when passed a non-SortedSet" do + lambda { SortedSet[].subset?([]) }.should raise_error(ArgumentError) + lambda { SortedSet[].subset?(1) }.should raise_error(ArgumentError) + lambda { SortedSet[].subset?("test") }.should raise_error(ArgumentError) + lambda { SortedSet[].subset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/subtract_spec.rb b/spec/rubyspec/library/set/sortedset/subtract_spec.rb new file mode 100644 index 0000000000..207748cfb9 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/subtract_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#subtract" do + before :each do + @set = SortedSet["a", "b", "c"] + end + + it "deletes any elements contained in other and returns self" do + @set.subtract(SortedSet["b", "c"]).should == @set + @set.should == SortedSet["a"] + end + + it "accepts any enumerable as other" do + @set.subtract(["c"]).should == SortedSet["a", "b"] + end +end diff --git a/spec/rubyspec/library/set/sortedset/superset_spec.rb b/spec/rubyspec/library/set/sortedset/superset_spec.rb new file mode 100644 index 0000000000..fc54e618a2 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/superset_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#superset?" do + before :each do + @set = SortedSet[1, 2, 3, 4] + end + + it "returns true if passed a SortedSet that equals self or self is a proper superset of" do + @set.superset?(@set).should be_true + SortedSet[].superset?(SortedSet[]).should be_true + + @set.superset?(SortedSet[]).should be_true + SortedSet[1, 2, 3].superset?(SortedSet[]).should be_true + SortedSet["a", "b", "c"].superset?(SortedSet[]).should be_true + + @set.superset?(SortedSet[1, 2, 3]).should be_true + @set.superset?(SortedSet[1, 3]).should be_true + @set.superset?(SortedSet[1, 2]).should be_true + @set.superset?(SortedSet[1]).should be_true + + @set.superset?(SortedSet[5]).should be_false + @set.superset?(SortedSet[1, 5]).should be_false + @set.superset?(SortedSet["test"]).should be_false + end + + it "raises an ArgumentError when passed a non-SortedSet" do + lambda { SortedSet[].superset?([]) }.should raise_error(ArgumentError) + lambda { SortedSet[].superset?(1) }.should raise_error(ArgumentError) + lambda { SortedSet[].superset?("test") }.should raise_error(ArgumentError) + lambda { SortedSet[].superset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/sortedset/to_a_spec.rb b/spec/rubyspec/library/set/sortedset/to_a_spec.rb new file mode 100644 index 0000000000..f288cfb9d2 --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/to_a_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'set' + +describe "SortedSet#to_a" do + it "returns an array containing elements of self" do + SortedSet[1, 2, 3].to_a.sort.should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/library/set/sortedset/union_spec.rb b/spec/rubyspec/library/set/sortedset/union_spec.rb new file mode 100644 index 0000000000..c7255c3d2f --- /dev/null +++ b/spec/rubyspec/library/set/sortedset/union_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/union', __FILE__) +require 'set' + +describe "SortedSet#union" do + it_behaves_like :sorted_set_union, :union +end + +describe "SortedSet#|" do + it_behaves_like :sorted_set_union, :| +end diff --git a/spec/rubyspec/library/set/subset_spec.rb b/spec/rubyspec/library/set/subset_spec.rb new file mode 100644 index 0000000000..6503a7539f --- /dev/null +++ b/spec/rubyspec/library/set/subset_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#subset?" do + before :each do + @set = Set[1, 2, 3, 4] + end + + it "returns true if passed a Set that is equal to self or self is a subset of" do + @set.subset?(@set).should be_true + Set[].subset?(Set[]).should be_true + + Set[].subset?(@set).should be_true + Set[].subset?(Set[1, 2, 3]).should be_true + Set[].subset?(Set["a", :b, ?c]).should be_true + + Set[1, 2, 3].subset?(@set).should be_true + Set[1, 3].subset?(@set).should be_true + Set[1, 2].subset?(@set).should be_true + Set[1].subset?(@set).should be_true + + Set[5].subset?(@set).should be_false + Set[1, 5].subset?(@set).should be_false + Set[nil].subset?(@set).should be_false + Set["test"].subset?(@set).should be_false + end + + it "raises an ArgumentError when passed a non-Set" do + lambda { Set[].subset?([]) }.should raise_error(ArgumentError) + lambda { Set[].subset?(1) }.should raise_error(ArgumentError) + lambda { Set[].subset?("test") }.should raise_error(ArgumentError) + lambda { Set[].subset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/subtract_spec.rb b/spec/rubyspec/library/set/subtract_spec.rb new file mode 100644 index 0000000000..b0889bb675 --- /dev/null +++ b/spec/rubyspec/library/set/subtract_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#subtract" do + before :each do + @set = Set[:a, :b, :c] + end + + it "deletes any elements contained in other and returns self" do + @set.subtract(Set[:b, :c]).should == @set + @set.should == Set[:a] + end + + it "accepts any enumerable as other" do + @set.subtract([:c]).should == Set[:a, :b] + end +end diff --git a/spec/rubyspec/library/set/superset_spec.rb b/spec/rubyspec/library/set/superset_spec.rb new file mode 100644 index 0000000000..b7364f529e --- /dev/null +++ b/spec/rubyspec/library/set/superset_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#superset?" do + before :each do + @set = Set[1, 2, 3, 4] + end + + it "returns true if passed a Set that equals self or self is a proper superset of" do + @set.superset?(@set).should be_true + Set[].superset?(Set[]).should be_true + + @set.superset?(Set[]).should be_true + Set[1, 2, 3].superset?(Set[]).should be_true + Set["a", :b, ?c].superset?(Set[]).should be_true + + @set.superset?(Set[1, 2, 3]).should be_true + @set.superset?(Set[1, 3]).should be_true + @set.superset?(Set[1, 2]).should be_true + @set.superset?(Set[1]).should be_true + + @set.superset?(Set[5]).should be_false + @set.superset?(Set[1, 5]).should be_false + @set.superset?(Set[nil]).should be_false + @set.superset?(Set["test"]).should be_false + end + + it "raises an ArgumentError when passed a non-Set" do + lambda { Set[].superset?([]) }.should raise_error(ArgumentError) + lambda { Set[].superset?(1) }.should raise_error(ArgumentError) + lambda { Set[].superset?("test") }.should raise_error(ArgumentError) + lambda { Set[].superset?(Object.new) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/set/to_a_spec.rb b/spec/rubyspec/library/set/to_a_spec.rb new file mode 100644 index 0000000000..daee014e90 --- /dev/null +++ b/spec/rubyspec/library/set/to_a_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'set' + +describe "Set#to_a" do + it "returns an array containing elements of self" do + Set[1, 2, 3].to_a.sort.should == [1, 2, 3] + end +end diff --git a/spec/rubyspec/library/set/union_spec.rb b/spec/rubyspec/library/set/union_spec.rb new file mode 100644 index 0000000000..c705497928 --- /dev/null +++ b/spec/rubyspec/library/set/union_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/union', __FILE__) +require 'set' + +describe "Set#union" do + it_behaves_like :set_union, :union +end + +describe "Set#|" do + it_behaves_like :set_union, :| +end diff --git a/spec/rubyspec/library/shellwords/shellwords_spec.rb b/spec/rubyspec/library/shellwords/shellwords_spec.rb new file mode 100644 index 0000000000..aa35c1a5c3 --- /dev/null +++ b/spec/rubyspec/library/shellwords/shellwords_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'shellwords' +include Shellwords + +describe "Shellwords#shellwords" do + it "honors quoted strings" do + shellwords('a "b b" a').should == ['a', 'b b', 'a'] + end + + it "honors escaped double quotes" do + shellwords('a "\"b\" c" d').should == ['a', '"b" c', 'd'] + end + + it "honors escaped single quotes" do + shellwords("a \"'b' c\" d").should == ['a', "'b' c", 'd'] + end + + it "honors escaped spaces" do + shellwords('a b\ c d').should == ['a', 'b c', 'd'] + end + + it "raises ArgumentError when double quoted strings are misquoted" do + lambda { shellwords('a "b c d e') }.should raise_error(ArgumentError) + end + + it "raises ArgumentError when single quoted strings are misquoted" do + lambda { shellwords("a 'b c d e") }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/singleton/allocate_spec.rb b/spec/rubyspec/library/singleton/allocate_spec.rb new file mode 100644 index 0000000000..ce6a501db7 --- /dev/null +++ b/spec/rubyspec/library/singleton/allocate_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Singleton.allocate" do + it "is a private method" do + lambda { SingletonSpecs::MyClass.allocate }.should raise_error(NoMethodError) + end +end diff --git a/spec/rubyspec/library/singleton/clone_spec.rb b/spec/rubyspec/library/singleton/clone_spec.rb new file mode 100644 index 0000000000..964a57bdee --- /dev/null +++ b/spec/rubyspec/library/singleton/clone_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Singleton#clone" do + it "is prevented" do + lambda { SingletonSpecs::MyClass.instance.clone }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/singleton/dump_spec.rb b/spec/rubyspec/library/singleton/dump_spec.rb new file mode 100644 index 0000000000..03f94cffd2 --- /dev/null +++ b/spec/rubyspec/library/singleton/dump_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Singleton#_dump" do + + it "returns an empty string" do + SingletonSpecs::MyClass.instance.send(:_dump).should == "" + end + + it "returns an empty string from a singleton subclass" do + SingletonSpecs::MyClassChild.instance.send(:_dump).should == "" + end + +end diff --git a/spec/rubyspec/library/singleton/dup_spec.rb b/spec/rubyspec/library/singleton/dup_spec.rb new file mode 100644 index 0000000000..16fe147f26 --- /dev/null +++ b/spec/rubyspec/library/singleton/dup_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Singleton#dup" do + it "is prevented" do + lambda { SingletonSpecs::MyClass.instance.dup }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/singleton/fixtures/classes.rb b/spec/rubyspec/library/singleton/fixtures/classes.rb new file mode 100644 index 0000000000..c718ebaec8 --- /dev/null +++ b/spec/rubyspec/library/singleton/fixtures/classes.rb @@ -0,0 +1,18 @@ +require 'singleton' + +module SingletonSpecs + class MyClass + attr_accessor :data + include Singleton + end + + class NewSpec + include Singleton + end + + class MyClassChild < MyClass + end + + class NotInstantiated < MyClass + end +end diff --git a/spec/rubyspec/library/singleton/instance_spec.rb b/spec/rubyspec/library/singleton/instance_spec.rb new file mode 100644 index 0000000000..84cc081510 --- /dev/null +++ b/spec/rubyspec/library/singleton/instance_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Singleton.instance" do + it "returns an instance of the singleton class" do + SingletonSpecs::MyClass.instance.should be_kind_of(SingletonSpecs::MyClass) + end + + it "returns the same instance for multiple calls to instance" do + SingletonSpecs::MyClass.instance.should equal(SingletonSpecs::MyClass.instance) + end + + it "returns an instance of the singleton's subclasses" do + SingletonSpecs::MyClassChild.instance.should be_kind_of(SingletonSpecs::MyClassChild) + end + + it "returns the same instance for multiple class to instance on subclasses" do + SingletonSpecs::MyClassChild.instance.should equal(SingletonSpecs::MyClassChild.instance) + end + + it "returns an instance of the singleton's clone" do + klone = SingletonSpecs::MyClassChild.clone + klone.instance.should be_kind_of(klone) + end + + it "returns the same instance for multiple class to instance on clones" do + klone = SingletonSpecs::MyClassChild.clone + klone.instance.should equal(klone.instance) + end +end diff --git a/spec/rubyspec/library/singleton/load_spec.rb b/spec/rubyspec/library/singleton/load_spec.rb new file mode 100644 index 0000000000..fa5868693b --- /dev/null +++ b/spec/rubyspec/library/singleton/load_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# TODO: change to a.should be_equal(b) +# TODO: write spec for cloning classes and calling private methods +# TODO: write spec for private_methods not showing up via extended +describe "Singleton._load" do + it "returns the singleton instance for anything passed in" do + klass = SingletonSpecs::MyClass + klass._load("").should equal(klass.instance) + klass._load("42").should equal(klass.instance) + klass._load(42).should equal(klass.instance) + end + + it "returns the singleton instance for anything passed in to subclass" do + subklass = SingletonSpecs::MyClassChild + subklass._load("").should equal(subklass.instance) + subklass._load("42").should equal(subklass.instance) + subklass._load(42).should equal(subklass.instance) + end +end diff --git a/spec/rubyspec/library/singleton/new_spec.rb b/spec/rubyspec/library/singleton/new_spec.rb new file mode 100644 index 0000000000..cd20bebc2b --- /dev/null +++ b/spec/rubyspec/library/singleton/new_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Singleton.new" do + it "is a private method" do + lambda { SingletonSpecs::NewSpec.new }.should raise_error(NoMethodError) + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/afamily_spec.rb b/spec/rubyspec/library/socket/addrinfo/afamily_spec.rb new file mode 100644 index 0000000000..1845ab5e04 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/afamily_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#afamily" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns Socket::AF_INET" do + @addrinfo.afamily.should == Socket::AF_INET + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns Socket::AF_INET6" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns Socket::AF_UNIX" do + @addrinfo.afamily.should == Socket::AF_UNIX + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/bind_spec.rb b/spec/rubyspec/library/socket/addrinfo/bind_spec.rb new file mode 100644 index 0000000000..4afa3a0211 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/bind_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +describe "Addrinfo#bind" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", SocketSpecs.port) + end + + after :each do + @socket.close unless @socket.closed? + end + + it "returns a bound socket when no block is given" do + @socket = @addrinfo.bind + @socket.should be_kind_of(Socket) + @socket.closed?.should be_false + end + + it "yields the socket if a block is given" do + @addrinfo.bind do |sock| + @socket = sock + sock.should be_kind_of(Socket) + end + @socket.closed?.should be_true + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/canonname_spec.rb b/spec/rubyspec/library/socket/addrinfo/canonname_spec.rb new file mode 100644 index 0000000000..15dfe86467 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/canonname_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +describe "Addrinfo#canonname" do + + before :each do + @addrinfos = Addrinfo.getaddrinfo("localhost", 80, :INET, :STREAM, nil, Socket::AI_CANONNAME) + end + + it "returns the canonical name for a host" do + canonname = @addrinfos.map { |a| a.canonname }.find { |name| name and name.include?("localhost") } + if canonname + canonname.should include("localhost") + else + canonname.should == nil + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/initialize_spec.rb b/spec/rubyspec/library/socket/addrinfo/initialize_spec.rb new file mode 100644 index 0000000000..254539f95e --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/initialize_spec.rb @@ -0,0 +1,253 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#initialize" do + + describe "with a sockaddr string" do + + describe "without a family" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1")) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_UNSPEC + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family given" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET6 + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family and socket type" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET6 + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family, socket type and protocol" do + before :each do + @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "2001:db8::1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 25 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET6 + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET6 + end + + it "returns the specified socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the specified protocol" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + + end + + describe "with a sockaddr array" do + + describe "without a family" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"]) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family given" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == 0 + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family and socket type" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the 0 protocol" do + @addrinfo.protocol.should == 0 + end + end + + describe "with a family, socket type and protocol" do + before :each do + @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP) + end + + it "stores the ip address from the sockaddr" do + @addrinfo.ip_address.should == "127.0.0.1" + end + + it "stores the port number from the sockaddr" do + @addrinfo.ip_port.should == 46102 + end + + it "returns the Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 afamily" do + @addrinfo.afamily.should == Socket::AF_INET + end + + it "returns the 0 socket type" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + it "returns the specified protocol" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/inspect_sockaddr_spec.rb b/spec/rubyspec/library/socket/addrinfo/inspect_sockaddr_spec.rb new file mode 100644 index 0000000000..c7d69db760 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/inspect_sockaddr_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +require 'socket' + +describe 'Addrinfo#inspect_sockaddr' do + it 'IPv4' do + Addrinfo.tcp('127.0.0.1', 80).inspect_sockaddr.should == '127.0.0.1:80' + Addrinfo.tcp('127.0.0.1', 0).inspect_sockaddr.should == '127.0.0.1' + end + + it 'IPv6' do + Addrinfo.tcp('::1', 80).inspect_sockaddr.should == '[::1]:80' + Addrinfo.tcp('::1', 0).inspect_sockaddr.should == '::1' + ip = '2001:0db8:85a3:0000:0000:8a2e:0370:7334' + Addrinfo.tcp(ip, 80).inspect_sockaddr.should == '[2001:db8:85a3::8a2e:370:7334]:80' + Addrinfo.tcp(ip, 0).inspect_sockaddr.should == '2001:db8:85a3::8a2e:370:7334' + end + + platform_is_not :windows do + it 'UNIX' do + Addrinfo.unix('/tmp/sock').inspect_sockaddr.should == '/tmp/sock' + Addrinfo.unix('rel').inspect_sockaddr.should == 'UNIX rel' + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_address_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_address_spec.rb new file mode 100644 index 0000000000..f82cef0812 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_address_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ip_address" do + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns the ip address" do + @addrinfo.ip_address.should == "127.0.0.1" + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns the ip address" do + @addrinfo.ip_address.should == "::1" + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "raises an exception" do + lambda { @addrinfo.ip_address }.should raise_error(SocketError) + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_port_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_port_spec.rb new file mode 100644 index 0000000000..e437b88ca1 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_port_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ip_port" do + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns the port" do + @addrinfo.ip_port.should == 80 + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns the port" do + @addrinfo.ip_port.should == 80 + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "raises an exception" do + lambda { @addrinfo.ip_port }.should raise_error(SocketError) + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_spec.rb new file mode 100644 index 0000000000..2e4b613ae5 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ip?" do + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns true" do + @addrinfo.ip?.should be_true + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns true" do + @addrinfo.ip?.should be_true + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns Socket::AF_INET6" do + @addrinfo.ip?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_unpack_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_unpack_spec.rb new file mode 100644 index 0000000000..2b4a9372cc --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_unpack_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ip_unpack" do + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns the ip address and port pair" do + @addrinfo.ip_unpack.should == ["127.0.0.1", 80] + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns the ip address and port pair" do + @addrinfo.ip_unpack.should == ["::1", 80] + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "raises an exception" do + lambda { @addrinfo.ip_unpack }.should raise_error(SocketError) + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_loopback_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_loopback_spec.rb new file mode 100644 index 0000000000..457bd7cebf --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_loopback_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv4_loopback?" do + describe "for an ipv4 socket" do + before :each do + @loopback = Addrinfo.tcp("127.0.0.1", 80) + @other = Addrinfo.tcp("0.0.0.0", 80) + end + + it "returns true for the loopback address" do + @loopback.ipv4_loopback?.should be_true + end + + it "returns false for another address" do + @other.ipv4_loopback?.should be_false + end + end + + describe "for an ipv6 socket" do + before :each do + @loopback = Addrinfo.tcp("::1", 80) + @other = Addrinfo.tcp("::", 80) + end + + it "returns false for the loopback address" do + @loopback.ipv4_loopback?.should be_false + end + + it "returns false for another address" do + @other.ipv4_loopback?.should be_false + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns false" do + @addrinfo.ipv4_loopback?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_multicast_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_multicast_spec.rb new file mode 100644 index 0000000000..01f6a6ebf7 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_multicast_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv4_multicast?" do + describe "for an ipv4 socket" do + before :each do + @multicast = Addrinfo.tcp("224.0.0.1", 80) + @other = Addrinfo.tcp("0.0.0.0", 80) + end + + it "returns true for the loopback address" do + @multicast.ipv4_multicast?.should be_true + end + + it "returns false for another address" do + @other.ipv4_multicast?.should be_false + end + end + + describe "for an ipv6 socket" do + before :each do + @multicast = Addrinfo.tcp("ff02::1", 80) + @other = Addrinfo.tcp("::", 80) + end + + it "returns false for the loopback address" do + @multicast.ipv4_multicast?.should be_false + end + + it "returns false for another address" do + @other.ipv4_multicast?.should be_false + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns false" do + @addrinfo.ipv4_multicast?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_private_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_private_spec.rb new file mode 100644 index 0000000000..cf8bd8c1aa --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_private_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv4_private?" do + describe "for an ipv4 socket" do + before :each do + @private = Addrinfo.tcp("10.0.0.1", 80) + @other = Addrinfo.tcp("0.0.0.0", 80) + end + + it "returns true for a private address" do + @private.ipv4_private?.should be_true + end + + it "returns false for a public address" do + @other.ipv4_private?.should be_false + end + end + + describe "for an ipv6 socket" do + before :each do + @other = Addrinfo.tcp("::", 80) + end + + it "returns false" do + @other.ipv4_private?.should be_false + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns false" do + @addrinfo.ipv4_private?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb new file mode 100644 index 0000000000..3d4560532e --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv4?" do + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("10.0.0.1", 80) + end + + it "returns true" do + @addrinfo.ipv4?.should be_true + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns false" do + @addrinfo.ipv4?.should be_false + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns false" do + @addrinfo.ipv4?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv6_loopback_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv6_loopback_spec.rb new file mode 100644 index 0000000000..b0060378e6 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv6_loopback_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv6_loopback?" do + describe "for an ipv4 socket" do + before :each do + @loopback = Addrinfo.tcp("127.0.0.1", 80) + @other = Addrinfo.tcp("0.0.0.0", 80) + end + + it "returns true for the loopback address" do + @loopback.ipv6_loopback?.should be_false + end + + it "returns false for another address" do + @other.ipv6_loopback?.should be_false + end + end + + describe "for an ipv6 socket" do + before :each do + @loopback = Addrinfo.tcp("::1", 80) + @other = Addrinfo.tcp("::", 80) + end + + it "returns false for the loopback address" do + @loopback.ipv6_loopback?.should be_true + end + + it "returns false for another address" do + @other.ipv6_loopback?.should be_false + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns false" do + @addrinfo.ipv6_loopback?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv6_multicast_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv6_multicast_spec.rb new file mode 100644 index 0000000000..d8b3a96ebb --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv6_multicast_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv6_multicast?" do + describe "for an ipv4 socket" do + before :each do + @multicast = Addrinfo.tcp("224.0.0.1", 80) + @other = Addrinfo.tcp("0.0.0.0", 80) + end + + it "returns true for the loopback address" do + @multicast.ipv6_multicast?.should be_false + end + + it "returns false for another address" do + @other.ipv6_multicast?.should be_false + end + end + + describe "for an ipv6 socket" do + before :each do + @multicast = Addrinfo.tcp("ff02::1", 80) + @other = Addrinfo.tcp("::", 80) + end + + it "returns false for the loopback address" do + @multicast.ipv6_multicast?.should be_true + end + + it "returns false for another address" do + @other.ipv6_multicast?.should be_false + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns false" do + @addrinfo.ipv6_multicast?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb new file mode 100644 index 0000000000..b66bc0d70b --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv6?" do + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("10.0.0.1", 80) + end + + it "returns true" do + @addrinfo.ipv6?.should be_false + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns false" do + @addrinfo.ipv6?.should be_true + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns false" do + @addrinfo.ipv6?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/pfamily_spec.rb b/spec/rubyspec/library/socket/addrinfo/pfamily_spec.rb new file mode 100644 index 0000000000..d37ed73e1e --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/pfamily_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#pfamily" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns Socket::PF_INET" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns Socket::PF_INET6" do + @addrinfo.pfamily.should == Socket::PF_INET6 + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns Socket::PF_UNIX" do + @addrinfo.pfamily.should == Socket::PF_UNIX + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/protocol_spec.rb b/spec/rubyspec/library/socket/addrinfo/protocol_spec.rb new file mode 100644 index 0000000000..4ff11dc017 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/protocol_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#protocol" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns Socket::IPPROTO_TCP" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns Socket::IPPROTO_TCP" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns 0" do + @addrinfo.protocol.should == 0 + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/shared/to_sockaddr.rb b/spec/rubyspec/library/socket/addrinfo/shared/to_sockaddr.rb new file mode 100644 index 0000000000..86819a31b0 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/shared/to_sockaddr.rb @@ -0,0 +1,35 @@ +describe :socket_addrinfo_to_sockaddr, :shared => true do + + describe "for an ipv4 socket" do + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns a sockaddr packed structure" do + @addrinfo.send(@method).should be_kind_of(String) + end + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns a sockaddr packed structure" do + @addrinfo.send(@method).should be_kind_of(String) + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns a sockaddr packed structure" do + @addrinfo.send(@method).should be_kind_of(String) + end + end + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/socktype_spec.rb b/spec/rubyspec/library/socket/addrinfo/socktype_spec.rb new file mode 100644 index 0000000000..e1c8c0f3f5 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/socktype_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#socktype" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns Socket::SOCK_STREAM" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns Socket::SOCK_STREAM" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns Socket::SOCK_STREAM" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/tcp_spec.rb b/spec/rubyspec/library/socket/addrinfo/tcp_spec.rb new file mode 100644 index 0000000000..b5c18cefea --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/tcp_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo.tcp" do + + before :each do + @addrinfo = Addrinfo.tcp("localhost", "smtp") + end + + it "creates a addrinfo for a tcp socket" do + ["::1", "127.0.0.1"].should include(@addrinfo.ip_address) + [Socket::PF_INET, Socket::PF_INET6].should include(@addrinfo.pfamily) + @addrinfo.ip_port.should == 25 + @addrinfo.socktype.should == Socket::SOCK_STREAM + platform_is_not :solaris do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/to_s_spec.rb b/spec/rubyspec/library/socket/addrinfo/to_s_spec.rb new file mode 100644 index 0000000000..7205bdc823 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_sockaddr', __FILE__) +require 'socket' + +describe "Addrinfo#to_s" do + it_behaves_like(:socket_addrinfo_to_sockaddr, :to_s) +end diff --git a/spec/rubyspec/library/socket/addrinfo/to_sockaddr_spec.rb b/spec/rubyspec/library/socket/addrinfo/to_sockaddr_spec.rb new file mode 100644 index 0000000000..f3f926c2b6 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/to_sockaddr_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_sockaddr', __FILE__) +require 'socket' + +describe "Addrinfo#to_sockaddr" do + it_behaves_like(:socket_addrinfo_to_sockaddr, :to_sockaddr) +end diff --git a/spec/rubyspec/library/socket/addrinfo/udp_spec.rb b/spec/rubyspec/library/socket/addrinfo/udp_spec.rb new file mode 100644 index 0000000000..712d730e05 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/udp_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo.udp" do + + before :each do + @addrinfo = Addrinfo.udp("localhost", "daytime") + end + + it "creates a addrinfo for a tcp socket" do + ["::1", "127.0.0.1"].should include(@addrinfo.ip_address) + [Socket::PF_INET, Socket::PF_INET6].should include(@addrinfo.pfamily) + @addrinfo.ip_port.should == 13 + @addrinfo.socktype.should == Socket::SOCK_DGRAM + platform_is_not :solaris do + @addrinfo.protocol.should == Socket::IPPROTO_UDP + end + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/unix_path_spec.rb b/spec/rubyspec/library/socket/addrinfo/unix_path_spec.rb new file mode 100644 index 0000000000..3f7e03dd7b --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/unix_path_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +platform_is_not :windows do + describe "Addrinfo#unix_path" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "raises an exception" do + lambda { @addrinfo.unix_path }.should raise_error(SocketError) + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "raises an exception" do + lambda { @addrinfo.unix_path }.should raise_error(SocketError) + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns the socket path" do + @addrinfo.unix_path.should == "/tmp/sock" + end + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/unix_spec.rb b/spec/rubyspec/library/socket/addrinfo/unix_spec.rb new file mode 100644 index 0000000000..00eedc96e7 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/unix_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo.unix" do + + platform_is_not :windows do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "creates a addrinfo for a unix socket" do + @addrinfo.pfamily.should == Socket::PF_UNIX + @addrinfo.socktype.should == Socket::SOCK_STREAM + @addrinfo.protocol.should == 0 + @addrinfo.unix_path.should == "/tmp/sock" + end + end +end + +describe "Addrinfo#unix?" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns false" do + @addrinfo.unix?.should be_false + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns false" do + @addrinfo.unix?.should be_false + end + end + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns true" do + @addrinfo.unix?.should be_true + end + end + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/close_read_spec.rb b/spec/rubyspec/library/socket/basicsocket/close_read_spec.rb new file mode 100644 index 0000000000..d0b73c88ad --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/close_read_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#close_read" do + before :each do + @server = TCPServer.new(SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it "closes the reading end of the socket" do + @server.close_read + lambda { @server.read }.should raise_error(IOError) + end + + it "it works on sockets with closed ends" do + @server.close_read + lambda { @server.close_read }.should_not raise_error(Exception) + lambda { @server.read }.should raise_error(IOError) + end + + it "does not close the socket" do + @server.close_read + @server.closed?.should be_false + end + + it "fully closes the socket if it was already closed for writing" do + @server.close_write + @server.close_read + @server.closed?.should be_true + end + + it "raises IOError on closed socket" do + @server.close + lambda { @server.close_read }.should raise_error(IOError) + end + + it "returns nil" do + @server.close_read.should be_nil + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/close_write_spec.rb b/spec/rubyspec/library/socket/basicsocket/close_write_spec.rb new file mode 100644 index 0000000000..7cba1caa8f --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/close_write_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#close_write" do + before :each do + @server = TCPServer.new(SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it "closes the writing end of the socket" do + @server.close_write + lambda { @server.write("foo") }.should raise_error(IOError) + end + + it "works on sockets with closed write ends" do + @server.close_write + lambda { @server.close_write }.should_not raise_error(Exception) + lambda { @server.write("foo") }.should raise_error(IOError) + end + + it "does not close the socket" do + @server.close_write + @server.closed?.should be_false + end + + it "does not prevent reading" do + @server.close_write + @server.read(0).should == "" + end + + it "fully closes the socket if it was already closed for reading" do + @server.close_read + @server.close_write + @server.closed?.should be_true + end + + it "raises IOError on closed socket" do + @server.close + lambda { @server.close_write }.should raise_error(IOError) + end + + it "returns nil" do + @server.close_write.should be_nil + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/do_not_reverse_lookup_spec.rb b/spec/rubyspec/library/socket/basicsocket/do_not_reverse_lookup_spec.rb new file mode 100644 index 0000000000..0875c1eca1 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/do_not_reverse_lookup_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket.do_not_reverse_lookup" do + before :each do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + @socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + @socket.close unless @socket.closed? + BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup + end + + it "defaults to true" do + BasicSocket.do_not_reverse_lookup.should be_true + end + + it "causes 'peeraddr' to avoid name lookups" do + @socket.do_not_reverse_lookup = true + BasicSocket.do_not_reverse_lookup = true + @socket.peeraddr.should == ["AF_INET", SocketSpecs.port, "127.0.0.1", "127.0.0.1"] + end + + it "looks for hostnames when set to false" do + @socket.do_not_reverse_lookup = false + BasicSocket.do_not_reverse_lookup = false + @socket.peeraddr[2].should == SocketSpecs.hostname + end + + it "looks for numeric addresses when set to true" do + @socket.do_not_reverse_lookup = true + BasicSocket.do_not_reverse_lookup = true + @socket.peeraddr[2].should == "127.0.0.1" + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/for_fd_spec.rb b/spec/rubyspec/library/socket/basicsocket/for_fd_spec.rb new file mode 100644 index 0000000000..5ad1ea84d0 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/for_fd_spec.rb @@ -0,0 +1,21 @@ + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#for_fd" do + before :each do + @server = TCPServer.new(SocketSpecs.port) + @s2 = nil + end + + after :each do + @server.close if @server + end + + it "return a Socket instance wrapped around the descriptor" do + @s2 = TCPServer.for_fd(@server.fileno) + @s2.autoclose = false + @s2.should be_kind_of(TCPServer) + @s2.fileno.should == @server.fileno + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/getpeername_spec.rb b/spec/rubyspec/library/socket/basicsocket/getpeername_spec.rb new file mode 100644 index 0000000000..1edfbcd3ec --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/getpeername_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#getpeername" do + + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.new("127.0.0.1", SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + @client.close unless @client.closed? + end + + it "returns the sockaddr of the other end of the connection" do + server_sockaddr = Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1") + @client.getpeername.should == server_sockaddr + end + + # Catch general exceptions to prevent NotImplementedError + it "raises an error if socket's not connected" do + lambda { @server.getpeername }.should raise_error(Exception) + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/getsockname_spec.rb b/spec/rubyspec/library/socket/basicsocket/getsockname_spec.rb new file mode 100644 index 0000000000..6d30286733 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/getsockname_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#getsockname" do + after :each do + @socket.closed?.should be_false + @socket.close + end + + it "returns the sockaddr associacted with the socket" do + @socket = TCPServer.new("127.0.0.1", SocketSpecs.port) + sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname) + sockaddr.should == [SocketSpecs.port, "127.0.0.1"] + end + + it "works on sockets listening in ipaddr_any" do + @socket = TCPServer.new(SocketSpecs.port) + sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname) + ["::", "0.0.0.0", "::ffff:0.0.0.0"].include?(sockaddr[1]).should be_true + sockaddr[0].should == SocketSpecs.port + end + + it "returns empty sockaddr for unbinded sockets" do + @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname) + sockaddr.should == [0, "0.0.0.0"] + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/getsockopt_spec.rb b/spec/rubyspec/library/socket/basicsocket/getsockopt_spec.rb new file mode 100644 index 0000000000..dc4fffa5c1 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/getsockopt_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#getsockopt" do + before :each do + @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + end + + after :each do + @sock.closed?.should be_false + @sock.close + end + + platform_is_not :aix do + # A known bug in AIX. getsockopt(2) does not properly set + # the fifth argument for SO_TYPE, SO_OOBINLINE, SO_BROADCAST, etc. + + it "gets a socket option Socket::SO_TYPE" do + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).to_s + n.should == [Socket::SOCK_STREAM].pack("i") + end + + it "gets a socket option Socket::SO_OOBINLINE" do + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should == [0].pack("i") + end + end + + it "gets a socket option Socket::SO_LINGER" do + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s + if (n.size == 8) # linger struct on some platforms, not just a value + n.should == [0, 0].pack("ii") + else + n.should == [0].pack("i") + end + end + + it "gets a socket option Socket::SO_SNDBUF" do + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + n.unpack('i')[0].should > 0 + end + + it "raises a SystemCallError with an invalid socket option" do + lambda { @sock.getsockopt Socket::SOL_SOCKET, -1 }.should raise_error(Errno::ENOPROTOOPT) + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/ioctl_spec.rb b/spec/rubyspec/library/socket/basicsocket/ioctl_spec.rb new file mode 100644 index 0000000000..9a7f535317 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/ioctl_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Socket::BasicSocket#ioctl" do + platform_is :linux do + it "passes data from and to a String correctly" do + s = Socket.new Socket::AF_INET, Socket::SOCK_DGRAM, 0 + # /usr/include/net/if.h, structure ifreq + # The structure is 32 bytes on x86, 40 bytes on x86_64 + if_name = ['lo'].pack('a16') + buffer = if_name + 'z' * 24 + # SIOCGIFADDR in /usr/include/bits/ioctls.h + s.ioctl 0x8915, buffer + s.close + + # Interface name should remain unchanged. + buffer[0, 16].should == if_name + # lo should have an IPv4 address of 127.0.0.1 + buffer[16, 2].unpack('S!').first.should == Socket::AF_INET + buffer[20, 4].should == "\x7f\0\0\x01" + end + end + + platform_is :freebsd do + it "passes data from and to a String correctly" do + s = Socket.new Socket::AF_INET, Socket::SOCK_DGRAM, 0 + # /usr/include/net/if.h, structure ifreq + # The structure is 32 bytes on x86, 40 bytes on x86_64 + if_name = ['lo0'].pack('a16') + buffer = if_name + 'z' * 24 + # SIOCGIFADDR in /usr/include/bits/ioctls.h + s.ioctl 0xc0206921, buffer + s.close + + # Interface name should remain unchanged. + buffer[0, 16].should == if_name + # lo should have an IPv4 address of 127.0.0.1 + buffer[16, 1].unpack('C').first.should == 16 + buffer[17, 1].unpack('C').first.should == Socket::AF_INET + buffer[20, 4].should == "\x7f\0\0\x01" + end + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/recv_nonblock_spec.rb b/spec/rubyspec/library/socket/basicsocket/recv_nonblock_spec.rb new file mode 100644 index 0000000000..2c948eaa2f --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/recv_nonblock_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../shared/recv_nonblock', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#recv_nonblock" do + it_behaves_like :socket_recv_nonblock, :recv_nonblock +end diff --git a/spec/rubyspec/library/socket/basicsocket/recv_spec.rb b/spec/rubyspec/library/socket/basicsocket/recv_spec.rb new file mode 100644 index 0000000000..9f3227dc65 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/recv_spec.rb @@ -0,0 +1,93 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#recv" do + + before :each do + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + end + + after :each do + @server.closed?.should be_false + @server.close + ScratchPad.clear + end + + it "receives a specified number of bytes of a message from another socket" do + t = Thread.new do + client = @server.accept + ScratchPad.record client.recv(10) + client.recv(1) # this recv is important + client.close + end + Thread.pass while t.status and t.status != "sleep" + t.status.should_not be_nil + + socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + socket.send('hello', 0) + socket.close + + t.join + ScratchPad.recorded.should == 'hello' + end + + platform_is_not :solaris do + it "accepts flags to specify unusual receiving behaviour" do + t = Thread.new do + client = @server.accept + + # in-band data (TCP), doesn't receive the flag. + ScratchPad.record client.recv(10) + + # this recv is important (TODO: explain) + client.recv(10) + client.close + end + Thread.pass while t.status and t.status != "sleep" + t.status.should_not be_nil + + socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + socket.send('helloU', Socket::MSG_OOB) + socket.shutdown(1) + t.join + socket.close + ScratchPad.recorded.should == 'hello' + end + end + + it "gets lines delimited with a custom separator" do + t = Thread.new do + client = @server.accept + ScratchPad.record client.gets("\377") + + # this call is important (TODO: explain) + client.gets(nil) + client.close + end + Thread.pass while t.status and t.status != "sleep" + t.status.should_not be_nil + + socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + socket.write("firstline\377secondline\377") + socket.close + + t.join + ScratchPad.recorded.should == "firstline\377" + end + + ruby_version_is "2.3" do + it "allows an output buffer as third argument" do + socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + socket.write("data") + + client = @server.accept + buf = "foo" + client.recv(4, 0, buf) + client.close + buf.should == "data" + + socket.close + end + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/send_spec.rb b/spec/rubyspec/library/socket/basicsocket/send_spec.rb new file mode 100644 index 0000000000..7822f4696b --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/send_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#send" do + before :each do + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + @socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + end + + after :each do + @server.closed?.should be_false + @socket.closed?.should be_false + + @server.close + @socket.close + end + + it "sends a message to another socket and returns the number of bytes sent" do + data = "" + t = Thread.new do + client = @server.accept + loop do + got = client.recv(5) + break if got.empty? + data << got + end + client.close + end + Thread.pass while t.status and t.status != "sleep" + t.status.should_not be_nil + + @socket.send('hello', 0).should == 5 + @socket.shutdown(1) # indicate, that we are done sending + @socket.recv(10) + + t.join + data.should == 'hello' + end + + platform_is_not :solaris, :windows do + it "accepts flags to specify unusual sending behaviour" do + data = nil + peek_data = nil + t = Thread.new do + client = @server.accept + peek_data = client.recv(6, Socket::MSG_PEEK) + data = client.recv(6) + client.recv(10) # this recv is important + client.close + end + Thread.pass while t.status and t.status != "sleep" + t.status.should_not be_nil + + @socket.send('helloU', Socket::MSG_PEEK | Socket::MSG_OOB).should == 6 + @socket.shutdown # indicate, that we are done sending + + t.join + peek_data.should == "hello" + data.should == 'hello' + end + end + + it "accepts a sockaddr as recipient address" do + data = "" + t = Thread.new do + client = @server.accept + loop do + got = client.recv(5) + break if got.empty? + data << got + end + client.close + end + Thread.pass while t.status and t.status != "sleep" + t.status.should_not be_nil + + sockaddr = Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1") + @socket.send('hello', 0, sockaddr).should == 5 + @socket.shutdown # indicate, that we are done sending + + t.join + data.should == 'hello' + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/setsockopt_spec.rb b/spec/rubyspec/library/socket/basicsocket/setsockopt_spec.rb new file mode 100644 index 0000000000..523a22d957 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/setsockopt_spec.rb @@ -0,0 +1,213 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#setsockopt" do + + before :each do + @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + end + + after :each do + @sock.close unless @sock.closed? + end + + it "sets the socket linger to 0" do + linger = [0, 0].pack("ii") + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s + + if (n.size == 8) # linger struct on some platforms, not just a value + n.should == [0, 0].pack("ii") + else + n.should == [0].pack("i") + end + end + + it "sets the socket linger to some positive value" do + linger = [64, 64].pack("ii") + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s + if (n.size == 8) # linger struct on some platforms, not just a value + a = n.unpack('ii') + a[0].should_not == 0 + a[1].should == 64 + else + n.should == [64].pack("i") + end + end + + platform_is_not :windows do + it "raises EINVAL if passed wrong linger value" do + lambda do + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, 0) + end.should raise_error(Errno::EINVAL) + end + end + + platform_is_not :aix do + # A known bug in AIX. getsockopt(2) does not properly set + # the fifth argument for SO_TYPE, SO_OOBINLINE, SO_BROADCAST, etc. + + it "sets the socket option Socket::SO_OOBINLINE" do + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, true).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should_not == [0].pack("i") + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, false).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should == [0].pack("i") + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should_not == [0].pack("i") + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 0).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should == [0].pack("i") + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 2).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should_not == [0].pack("i") + + platform_is_not :windows do + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "") + }.should raise_error(SystemCallError) + end + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "blah").should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should_not == [0].pack("i") + + platform_is_not :windows do + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "0") + }.should raise_error(SystemCallError) + end + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00\x00").should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should == [0].pack("i") + + platform_is_not :windows do + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "1") + }.should raise_error(SystemCallError) + end + + platform_is_not :windows do + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00") + }.should raise_error(SystemCallError) + end + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1].pack('i')).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should_not == [0].pack("i") + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [0].pack('i')).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should == [0].pack("i") + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1000].pack('i')).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s + n.should_not == [0].pack("i") + end + end + + it "sets the socket option Socket::SO_SNDBUF" do + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 4000).should == 0 + sndbuf = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + # might not always be possible to set to exact size + sndbuf.unpack('i')[0].should >= 4000 + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, true).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + n.unpack('i')[0].should >= 1 + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, nil).should == 0 + }.should raise_error(TypeError) + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 1).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + n.unpack('i')[0].should >= 1 + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 2).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + n.unpack('i')[0].should >= 2 + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "") + }.should raise_error(SystemCallError) + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "bla") + }.should raise_error(SystemCallError) + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "0") + }.should raise_error(SystemCallError) + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "1") + }.should raise_error(SystemCallError) + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "\x00\x00\x00") + }.should raise_error(SystemCallError) + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "\x00\x00\x01\x00").should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + n.unpack('i')[0].should >= "\x00\x00\x01\x00".unpack('i')[0] + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, [4000].pack('i')).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + n.unpack('i')[0].should >= 4000 + + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, [1000].pack('i')).should == 0 + n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s + n.unpack('i')[0].should >= 1000 + end + + platform_is_not :aix do + describe 'accepts Socket::Option as argument' do + it 'boolean' do + option = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true) + @sock.setsockopt(option).should == 0 + @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).bool.should == true + end + + it 'int' do + option = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) + @sock.setsockopt(option).should == 0 + @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).bool.should == true + end + end + end + + platform_is :aix do + describe 'accepts Socket::Option as argument' do + it 'boolean' do + option = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true) + @sock.setsockopt(option).should == 0 + end + + it 'int' do + option = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) + @sock.setsockopt(option).should == 0 + end + end + end + + describe 'accepts Socket::Option as argument' do + it 'linger' do + option = Socket::Option.linger(true, 10) + @sock.setsockopt(option).should == 0 + onoff, seconds = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).linger + seconds.should == 10 + # Both results can be produced depending on the OS and value of Socket::SO_LINGER + [true, Socket::SO_LINGER].should include(onoff) + end + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/shutdown_spec.rb b/spec/rubyspec/library/socket/basicsocket/shutdown_spec.rb new file mode 100644 index 0000000000..c874f08697 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/shutdown_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#shutdown" do + +end diff --git a/spec/rubyspec/library/socket/constants/constants_spec.rb b/spec/rubyspec/library/socket/constants/constants_spec.rb new file mode 100644 index 0000000000..9b8a0e55b3 --- /dev/null +++ b/spec/rubyspec/library/socket/constants/constants_spec.rb @@ -0,0 +1,90 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +include Socket::Constants + +describe "Socket::Constants" do + it "defines socket types" do + consts = ["SOCK_DGRAM", "SOCK_RAW", "SOCK_RDM", "SOCK_SEQPACKET", "SOCK_STREAM"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines protocol families" do + consts = ["PF_INET6", "PF_INET", "PF_UNIX", "PF_UNSPEC"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + platform_is_not :aix do + it "defines PF_IPX protocol" do + Socket::Constants.should have_constant("PF_IPX") + end + end + + it "defines address families" do + consts = ["AF_INET6", "AF_INET", "AF_UNIX", "AF_UNSPEC"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + platform_is_not :aix do + it "defines AF_IPX address" do + Socket::Constants.should have_constant("AF_IPX") + end + end + + it "defines send/receive options" do + consts = ["MSG_DONTROUTE", "MSG_OOB", "MSG_PEEK"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines socket level options" do + consts = ["SOL_SOCKET"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines socket options" do + consts = ["SO_BROADCAST", "SO_DEBUG", "SO_DONTROUTE", "SO_ERROR", "SO_KEEPALIVE", "SO_LINGER", + "SO_OOBINLINE", "SO_RCVBUF", "SO_REUSEADDR", "SO_SNDBUF", "SO_TYPE"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + it "defines multicast options" do + consts = ["IP_ADD_MEMBERSHIP", + "IP_MULTICAST_LOOP", "IP_MULTICAST_TTL"] + platform_is_not :windows do + consts += ["IP_DEFAULT_MULTICAST_LOOP", "IP_DEFAULT_MULTICAST_TTL"] + end + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + + platform_is_not :solaris, :windows, :aix do + it "defines multicast options" do + consts = ["IP_MAX_MEMBERSHIPS"] + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end + end + + it "defines TCP options" do + consts = ["TCP_NODELAY"] + platform_is_not :windows do + consts << "TCP_MAXSEG" + end + consts.each do |c| + Socket::Constants.should have_constant(c) + end + end +end diff --git a/spec/rubyspec/library/socket/fixtures/classes.rb b/spec/rubyspec/library/socket/fixtures/classes.rb new file mode 100644 index 0000000000..ddb2396731 --- /dev/null +++ b/spec/rubyspec/library/socket/fixtures/classes.rb @@ -0,0 +1,106 @@ +require 'socket' + +module SocketSpecs + # helper to get the hostname associated to 127.0.0.1 + def self.hostname + # Calculate each time, without caching, since the result might + # depend on things like do_not_reverse_lookup mode, which is + # changing from test to test + Socket.getaddrinfo("127.0.0.1", nil)[0][2] + end + + def self.hostnamev6 + Socket.getaddrinfo("::1", nil)[0][2] + end + + def self.addr(which=:ipv4) + case which + when :ipv4 + host = "127.0.0.1" + when :ipv6 + host = "::1" + end + Socket.getaddrinfo(host, nil)[0][3] + end + + def self.find_available_port + begin + s = TCPServer.open(0) + port = s.addr[1] + s.close + port + rescue + 43191 + end + end + + def self.port + @@_port ||= find_available_port + end + + def self.str_port + port.to_s + end + + def self.local_port + find_available_port + end + + def self.sockaddr_in(port, host) + Socket::SockAddr_In.new(Socket.sockaddr_in(port, host)) + end + + def self.socket_path + tmp("unix_server_spec.socket", false) + end + + # TCPServer echo server accepting one connection + class SpecTCPServer + attr_accessor :hostname, :port, :logger + + def initialize(host=nil, port=nil, logger=nil) + @hostname = host || SocketSpecs.hostname + @port = port || SocketSpecs.port + @logger = logger + + start + end + + def start + log "SpecTCPServer starting on #{@hostname}:#{@port}" + @server = TCPServer.new @hostname, @port + + @thread = Thread.new do + socket = @server.accept + log "SpecTCPServer accepted connection: #{socket}" + service socket + end + self + end + + def service(socket) + begin + data = socket.recv(1024) + + return if data.empty? + log "SpecTCPServer received: #{data.inspect}" + + return if data == "QUIT" + + socket.send data, 0 + ensure + socket.close + end + end + + def shutdown + log "SpecTCPServer shutting down" + @thread.join + @server.close + end + + def log(message) + @logger.puts message if @logger + end + end +end diff --git a/spec/rubyspec/library/socket/fixtures/send_io.txt b/spec/rubyspec/library/socket/fixtures/send_io.txt new file mode 100644 index 0000000000..eaaa1eb3ec --- /dev/null +++ b/spec/rubyspec/library/socket/fixtures/send_io.txt @@ -0,0 +1 @@ +This data is magic. diff --git a/spec/rubyspec/library/socket/ipsocket/addr_spec.rb b/spec/rubyspec/library/socket/ipsocket/addr_spec.rb new file mode 100644 index 0000000000..2ac4c3d413 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/addr_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::IPSocket#addr" do + before :each do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + @socket = TCPServer.new("127.0.0.1", SocketSpecs.port) + end + + after :each do + @socket.close unless @socket.closed? + BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup + end + + it "returns an array with the socket's information" do + @socket.do_not_reverse_lookup = false + BasicSocket.do_not_reverse_lookup = false + addrinfo = @socket.addr + addrinfo[0].should == "AF_INET" + addrinfo[1].should == SocketSpecs.port + addrinfo[2].should == SocketSpecs.hostname + addrinfo[3].should == "127.0.0.1" + end + + it "returns an address in the array if do_not_reverse_lookup is true" do + @socket.do_not_reverse_lookup = true + BasicSocket.do_not_reverse_lookup = true + addrinfo = @socket.addr + addrinfo[0].should == "AF_INET" + addrinfo[1].should == SocketSpecs.port + addrinfo[2].should == "127.0.0.1" + addrinfo[3].should == "127.0.0.1" + end + + it "returns an address in the array if passed false" do + addrinfo = @socket.addr(false) + addrinfo[0].should == "AF_INET" + addrinfo[1].should == SocketSpecs.port + addrinfo[2].should == "127.0.0.1" + addrinfo[3].should == "127.0.0.1" + end +end diff --git a/spec/rubyspec/library/socket/ipsocket/getaddress_spec.rb b/spec/rubyspec/library/socket/ipsocket/getaddress_spec.rb new file mode 100644 index 0000000000..c574c7d267 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/getaddress_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::IPSocket#getaddress" do + + it "returns the IP address of hostname" do + addr_local = IPSocket.getaddress(SocketSpecs.hostname) + ["127.0.0.1", "::1"].include?(addr_local).should == true + end + + it "returns the IP address when passed an IP" do + IPSocket.getaddress("127.0.0.1").should == "127.0.0.1" + IPSocket.getaddress("0.0.0.0").should == "0.0.0.0" + end + + # There is no way to make this fail-proof on all machines, because + # DNS servers like opendns return A records for ANY host, including + # traditionally invalidly named ones. + quarantine! do + it "raises an error on unknown hostnames" do + lambda { + IPSocket.getaddress("rubyspecdoesntexist.fallingsnow.net") + }.should raise_error(SocketError) + end + end + +end diff --git a/spec/rubyspec/library/socket/ipsocket/peeraddr_spec.rb b/spec/rubyspec/library/socket/ipsocket/peeraddr_spec.rb new file mode 100644 index 0000000000..01b697bd27 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/peeraddr_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::IPSocket#peeraddr" do + before :each do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.new("127.0.0.1", SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + @client.close unless @client.closed? + BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup + end + + it "raises error if socket is not connected" do + lambda { @server.peeraddr }.should raise_error + end + + it "returns an array of information on the peer" do + @client.do_not_reverse_lookup = false + BasicSocket.do_not_reverse_lookup = false + addrinfo = @client.peeraddr + addrinfo[0].should == "AF_INET" + addrinfo[1].should == SocketSpecs.port + addrinfo[2].should == SocketSpecs.hostname + addrinfo[3].should == "127.0.0.1" + end + + it "returns an IP instead of hostname if do_not_reverse_lookup is true" do + @client.do_not_reverse_lookup = true + BasicSocket.do_not_reverse_lookup = true + addrinfo = @client.peeraddr + addrinfo[0].should == "AF_INET" + addrinfo[1].should == SocketSpecs.port + addrinfo[2].should == "127.0.0.1" + addrinfo[3].should == "127.0.0.1" + end + + it "returns an IP instead of hostname if passed false" do + addrinfo = @client.peeraddr(false) + addrinfo[0].should == "AF_INET" + addrinfo[1].should == SocketSpecs.port + addrinfo[2].should == "127.0.0.1" + addrinfo[3].should == "127.0.0.1" + end +end diff --git a/spec/rubyspec/library/socket/ipsocket/recvfrom_spec.rb b/spec/rubyspec/library/socket/ipsocket/recvfrom_spec.rb new file mode 100644 index 0000000000..565a1795e9 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/recvfrom_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::IPSocket#recvfrom" do + + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.new("127.0.0.1", SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + @client.close unless @client.closed? + end + + it "reads data from the connection" do + data = nil + t = Thread.new do + client = @server.accept + data = client.recvfrom(6) + client.close + end + + @client.send('hello', 0) + @client.shutdown rescue nil + # shutdown may raise Errno::ENOTCONN when sent data is pending. + t.join + + data.first.should == 'hello' + end + + it "reads up to len bytes" do + data = nil + t = Thread.new do + client = @server.accept + data = client.recvfrom(3) + client.close + end + + @client.send('hello', 0) + @client.shutdown rescue nil + t.join + + data.first.should == 'hel' + end + + it "returns an array with the data and connection info" do + data = nil + t = Thread.new do + client = @server.accept + data = client.recvfrom(3) + client.close + end + + @client.send('hello', 0) + @client.shutdown rescue nil + t.join + + data.size.should == 2 + data.first.should == "hel" + # This does not apply to every platform, dependant on recvfrom(2) + # data.last.should == nil + end + +end diff --git a/spec/rubyspec/library/socket/option/bool_spec.rb b/spec/rubyspec/library/socket/option/bool_spec.rb new file mode 100644 index 0000000000..74c832a0ad --- /dev/null +++ b/spec/rubyspec/library/socket/option/bool_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::Option.bool" do + it "creates a new Socket::Option" do + so = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true) + so.should be_an_instance_of(Socket::Option) + so.family.should == Socket::AF_INET + so.level.should == Socket::SOL_SOCKET + so.optname.should == Socket::SO_KEEPALIVE + so.data.should == [1].pack('i') + end +end + +describe "Socket::Option#bool" do + it "returns boolean value" do + Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true).bool.should == true + Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false).bool.should == false + end + + it "raises TypeError if option has not good size" do + so = Socket::Option.new(:UNSPEC, :SOCKET, :SO_LINGER, [0, 0].pack('i*')) + lambda { so.bool }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/socket/option/inspect_spec.rb b/spec/rubyspec/library/socket/option/inspect_spec.rb new file mode 100644 index 0000000000..df72f227a9 --- /dev/null +++ b/spec/rubyspec/library/socket/option/inspect_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe 'Socket::Option#inspect' do + it 'correctly returns SO_LINGER value' do + value = Socket::Option.linger(nil, 0).inspect + value.should == '#' + + value = Socket::Option.linger(false, 30).inspect + value.should == '#' + + value = Socket::Option.linger(true, 0).inspect + value.should == '#' + + value = Socket::Option.linger(true, 30).inspect + value.should == '#' + end +end diff --git a/spec/rubyspec/library/socket/option/int_spec.rb b/spec/rubyspec/library/socket/option/int_spec.rb new file mode 100644 index 0000000000..f926ff7968 --- /dev/null +++ b/spec/rubyspec/library/socket/option/int_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::Option.int" do + it "creates a new Socket::Option" do + so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 5) + so.should be_an_instance_of(Socket::Option) + so.family.should == Socket::Constants::AF_INET + so.level.should == Socket::Constants::SOL_SOCKET + so.optname.should == Socket::Constants::SO_KEEPALIVE + so.data.should == [5].pack('i') + end +end + +describe "Socket::Option#int" do + it "returns int value" do + so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 17) + so.int.should == 17 + + so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 32765) + so.int.should == 32765 + end + + it "raises TypeError if option has not good size" do + so = Socket::Option.new(:UNSPEC, :SOCKET, :SO_LINGER, [0, 0].pack('i*')) + lambda { so.int }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/socket/option/linger_spec.rb b/spec/rubyspec/library/socket/option/linger_spec.rb new file mode 100644 index 0000000000..687d421af3 --- /dev/null +++ b/spec/rubyspec/library/socket/option/linger_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +option_pack = 'i*' +platform_is :windows do + option_pack = 's*' +end + +describe "Socket::Option.linger" do + it "creates a new Socket::Option for SO_LINGER" do + so = Socket::Option.linger(1, 10) + so.should be_an_instance_of(Socket::Option) + so.family.should == Socket::Constants::AF_UNSPEC + so.level.should == Socket::Constants::SOL_SOCKET + so.optname.should == Socket::Constants::SO_LINGER + so.data.should == [1, 10].pack(option_pack) + end + + it "accepts boolean as onoff argument" do + so = Socket::Option.linger(false, 0) + so.data.should == [0, 0].pack(option_pack) + + so = Socket::Option.linger(true, 1) + so.data.should == [1, 1].pack(option_pack) + end +end + +describe "Socket::Option#linger" do + it "returns linger option" do + so = Socket::Option.linger(0, 5) + ary = so.linger + ary[0].should be_false + ary[1].should == 5 + + so = Socket::Option.linger(false, 4) + ary = so.linger + ary[0].should be_false + ary[1].should == 4 + + so = Socket::Option.linger(1, 10) + ary = so.linger + ary[0].should be_true + ary[1].should == 10 + + so = Socket::Option.linger(true, 9) + ary = so.linger + ary[0].should be_true + ary[1].should == 9 + end + + it "raises TypeError if not a SO_LINGER" do + so = Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :KEEPALIVE, 1) + lambda { so.linger }.should raise_error(TypeError) + end + + platform_is_not :windows do + it "raises TypeError if option has not good size" do + so = Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :LINGER, 1) + lambda { so.linger }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/library/socket/option/new_spec.rb b/spec/rubyspec/library/socket/option/new_spec.rb new file mode 100644 index 0000000000..4f2d0c5386 --- /dev/null +++ b/spec/rubyspec/library/socket/option/new_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::Option.new" do + it "should accept integers" do + so = Socket::Option.new(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, [0].pack('i')) + so.family.should == Socket::AF_INET + so.level.should == Socket::SOL_SOCKET + so.optname.should == Socket::SO_KEEPALIVE + end + + it "should accept symbols" do + so = Socket::Option.new(:AF_INET, :SOL_SOCKET, :SO_KEEPALIVE, [0].pack('i')) + so.family.should == Socket::AF_INET + so.level.should == Socket::SOL_SOCKET + so.optname.should == Socket::SO_KEEPALIVE + + so = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [0].pack('i')) + so.family.should == Socket::AF_INET + so.level.should == Socket::SOL_SOCKET + so.optname.should == Socket::SO_KEEPALIVE + end + + it "should raise error on unknown family" do + lambda { Socket::Option.new(:INET4, :SOCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError) + end + + it "should raise error on unknown level" do + lambda { Socket::Option.new(:INET, :ROCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError) + end + + it "should raise error on unknown option name" do + lambda { Socket::Option.new(:INET, :SOCKET, :ALIVE, [0].pack('i')) }.should raise_error(SocketError) + end +end diff --git a/spec/rubyspec/library/socket/shared/pack_sockaddr.rb b/spec/rubyspec/library/socket/shared/pack_sockaddr.rb new file mode 100644 index 0000000000..4ffa02a8d8 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/pack_sockaddr.rb @@ -0,0 +1,50 @@ +# coding: utf-8 +describe :socket_pack_sockaddr_in, shared: true do + it "packs and unpacks" do + sockaddr_in = Socket.public_send(@method, 0, nil) + port, addr = Socket.unpack_sockaddr_in(sockaddr_in) + ["127.0.0.1", "::1"].include?(addr).should == true + port.should == 0 + + sockaddr_in = Socket.public_send(@method, 0, '') + Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '0.0.0.0'] + + sockaddr_in = Socket.public_send(@method, 80, '127.0.0.1') + Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] + + sockaddr_in = Socket.public_send(@method, '80', '127.0.0.1') + Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1'] + + sockaddr_in = Socket.public_send(@method, nil, '127.0.0.1') + Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1'] + end +end + +describe :socket_pack_sockaddr_un, shared: true do + platform_is_not :windows do + it 'should be idempotent' do + bytes = Socket.public_send(@method, '/tmp/foo').bytes + bytes[2..9].should == [47, 116, 109, 112, 47, 102, 111, 111] + bytes[10..-1].all?(&:zero?).should == true + end + + it "packs and unpacks" do + sockaddr_un = Socket.public_send(@method, '/tmp/s') + Socket.unpack_sockaddr_un(sockaddr_un).should == '/tmp/s' + end + + it "handles correctly paths with multibyte chars" do + sockaddr_un = Socket.public_send(@method, '/home/вася/sock') + path = Socket.unpack_sockaddr_un(sockaddr_un).encode('UTF-8', 'UTF-8') + path.should == '/home/вася/sock' + end + end + + platform_is_not :windows, :aix do + it "raises if path length exceeds max size" do + # AIX doesn't raise error + long_path = Array.new(512, 0).join + lambda { Socket.public_send(@method, long_path) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/library/socket/shared/partially_closable_sockets.rb b/spec/rubyspec/library/socket/shared/partially_closable_sockets.rb new file mode 100644 index 0000000000..1309f15f85 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/partially_closable_sockets.rb @@ -0,0 +1,13 @@ +describe "partially closable sockets", shared: true do + specify "if the write end is closed then the other side can read past EOF without blocking" do + @s1.write("foo") + @s1.close_write + @s2.read("foo".size + 1).should == "foo" + end + + specify "closing the write end ensures that the other side can read until EOF" do + @s1.write("hello world") + @s1.close_write + @s2.read.should == "hello world" + end +end diff --git a/spec/rubyspec/library/socket/shared/recv_nonblock.rb b/spec/rubyspec/library/socket/shared/recv_nonblock.rb new file mode 100644 index 0000000000..4a63b16024 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/recv_nonblock.rb @@ -0,0 +1,54 @@ +describe :socket_recv_nonblock, shared: true do + before :each do + @s1 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) + @s2 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) + end + + after :each do + @s1.close unless @s1.closed? + @s2.close unless @s2.closed? + end + + it "raises an exception extending IO::WaitReadable if there's no data available" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + lambda { + @s1.recv_nonblock(5) + }.should raise_error(IO::WaitReadable) { |e| + platform_is_not :windows do + e.should be_kind_of(Errno::EAGAIN) + end + platform_is :windows do + e.should be_kind_of(Errno::EWOULDBLOCK) + end + } + end + + it "receives data after it's ready" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + @s2.send("aaa", 0, @s1.getsockname) + IO.select([@s1], nil, nil, 2) + @s1.recv_nonblock(5).should == "aaa" + end + + ruby_version_is "2.3" do + it "allows an output buffer as third argument" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + @s2.send("data", 0, @s1.getsockname) + IO.select([@s1], nil, nil, 2) + + buf = "foo" + @s1.recv_nonblock(5, 0, buf) + buf.should == "data" + end + end + + it "does not block if there's no data available" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + @s2.send("a", 0, @s1.getsockname) + IO.select([@s1], nil, nil, 2) + @s1.recv_nonblock(1).should == "a" + lambda { + @s1.recv_nonblock(5) + }.should raise_error(IO::WaitReadable) + end +end diff --git a/spec/rubyspec/library/socket/shared/socketpair.rb b/spec/rubyspec/library/socket/shared/socketpair.rb new file mode 100644 index 0000000000..03ee0e1a52 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/socketpair.rb @@ -0,0 +1,23 @@ +describe :socket_socketpair, shared: true do + platform_is_not :windows do + it "ensures the returned sockets are connected" do + s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, 1, 0) + s1.puts("test") + s2.gets.should == "test\n" + s1.close + s2.close + end + + it "responses with array of two sockets" do + begin + s1, s2 = Socket.public_send(@method, :UNIX, :STREAM) + + s1.should be_an_instance_of(Socket) + s2.should be_an_instance_of(Socket) + ensure + s1.close + s2.close + end + end + end +end diff --git a/spec/rubyspec/library/socket/socket/accept_nonblock_spec.rb b/spec/rubyspec/library/socket/socket/accept_nonblock_spec.rb new file mode 100644 index 0000000000..be0bbf5f03 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/accept_nonblock_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "Socket#accept_nonblock" do + before :each do + @hostname = "127.0.0.1" + @addr = Socket.sockaddr_in(0, @hostname) + @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + @socket.bind(@addr) + @socket.listen(1) + end + + after :each do + @socket.close + end + + it "raises IO::WaitReadable if the connection is not accepted yet" do + lambda { + @socket.accept_nonblock + }.should raise_error(IO::WaitReadable) { |e| + platform_is_not :windows do + e.should be_kind_of(Errno::EAGAIN) + end + platform_is :windows do + e.should be_kind_of(Errno::EWOULDBLOCK) + end + } + end + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @socket.accept_nonblock(exception: false).should == :wait_readable + end + end +end diff --git a/spec/rubyspec/library/socket/socket/accept_spec.rb b/spec/rubyspec/library/socket/socket/accept_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/accept_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/bind_spec.rb b/spec/rubyspec/library/socket/socket/bind_spec.rb new file mode 100644 index 0000000000..57dff3bd6d --- /dev/null +++ b/spec/rubyspec/library/socket/socket/bind_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +include Socket::Constants + +describe "Socket#bind on SOCK_DGRAM socket" do + before :each do + @sock = Socket.new(AF_INET, SOCK_DGRAM, 0); + @sockaddr = Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1"); + end + + after :each do + @sock.closed?.should be_false + @sock.close + end + + it "binds to a port" do + lambda { @sock.bind(@sockaddr) }.should_not raise_error + end + + it "returns 0 if successful" do + @sock.bind(@sockaddr).should == 0 + end + + it "raises Errno::EINVAL when binding to an already bound port" do + @sock.bind(@sockaddr); + + lambda { @sock.bind(@sockaddr); }.should raise_error(Errno::EINVAL); + end + + it "raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available from the local machine" do + sockaddr1 = Socket.pack_sockaddr_in(SocketSpecs.port, "4.3.2.1"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EADDRNOTAVAIL) + end + + platform_is_not :windows, :cygwin do + it "raises Errno::EACCES when the current user does not have permission to bind" do + sockaddr1 = Socket.pack_sockaddr_in(1, "127.0.0.1"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EACCES) + end + end +end + +describe "Socket#bind on SOCK_STREAM socket" do + before :each do + @sock = Socket.new(AF_INET, SOCK_STREAM, 0); + @sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, true) + @sockaddr = Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1"); + end + + after :each do + @sock.closed?.should be_false + @sock.close + end + + it "binds to a port" do + lambda { @sock.bind(@sockaddr) }.should_not raise_error + end + + it "returns 0 if successful" do + @sock.bind(@sockaddr).should == 0 + end + + it "raises Errno::EINVAL when binding to an already bound port" do + @sock.bind(@sockaddr); + + lambda { @sock.bind(@sockaddr); }.should raise_error(Errno::EINVAL); + end + + it "raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available from the local machine" do + sockaddr1 = Socket.pack_sockaddr_in(SocketSpecs.port, "4.3.2.1"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EADDRNOTAVAIL) + end + + platform_is_not :windows, :cygwin do + it "raises Errno::EACCES when the current user does not have permission to bind" do + sockaddr1 = Socket.pack_sockaddr_in(1, "127.0.0.1"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EACCES) + end + end +end diff --git a/spec/rubyspec/library/socket/socket/connect_nonblock_spec.rb b/spec/rubyspec/library/socket/socket/connect_nonblock_spec.rb new file mode 100644 index 0000000000..77c2340688 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/connect_nonblock_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "Socket#connect_nonblock" do + before :each do + @hostname = "127.0.0.1" + @addr = Socket.sockaddr_in(SocketSpecs.port, @hostname) + @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + @thread = nil + end + + after :each do + @socket.close + @thread.join if @thread + end + + it "connects the socket to the remote side" do + ready = false + @thread = Thread.new do + server = TCPServer.new(@hostname, SocketSpecs.port) + ready = true + conn = server.accept + conn << "hello!" + conn.close + server.close + end + + Thread.pass while (@thread.status and @thread.status != 'sleep') or !ready + + begin + @socket.connect_nonblock(@addr) + rescue Errno::EINPROGRESS + end + + IO.select nil, [@socket] + + begin + @socket.connect_nonblock(@addr) + rescue Errno::EISCONN + # Not all OS's use this errno, so we trap and ignore it + end + + @socket.read(6).should == "hello!" + end + + platform_is_not :freebsd, :solaris, :aix do + it "raises Errno::EINPROGRESS when the connect would block" do + lambda do + @socket.connect_nonblock(@addr) + end.should raise_error(Errno::EINPROGRESS) + end + + it "raises Errno::EINPROGRESS with IO::WaitWritable mixed in when the connect would block" do + lambda do + @socket.connect_nonblock(@addr) + end.should raise_error(IO::WaitWritable) + end + + ruby_version_is "2.3" do + it "returns :wait_writable in exceptionless mode when the connect would block" do + @socket.connect_nonblock(@addr, exception: false).should == :wait_writable + end + end + end +end diff --git a/spec/rubyspec/library/socket/socket/connect_spec.rb b/spec/rubyspec/library/socket/socket/connect_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/connect_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/for_fd_spec.rb b/spec/rubyspec/library/socket/socket/for_fd_spec.rb new file mode 100644 index 0000000000..7f3cfcceb4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/for_fd_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +describe "Socket.for_fd" do + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.open("127.0.0.1", SocketSpecs.port) + end + + after :each do + @socket.close + @client.close + @host.close + @server.close + end + + it "creates a new Socket that aliases the existing Socket's file descriptor" do + @socket = Socket.for_fd(@client.fileno) + @socket.autoclose = false + @socket.fileno.should == @client.fileno + + @socket.send("foo", 0) + @client.send("bar", 0) + + @host = @server.accept + @host.read(3).should == "foo" + @host.read(3).should == "bar" + end +end diff --git a/spec/rubyspec/library/socket/socket/getaddrinfo_spec.rb b/spec/rubyspec/library/socket/socket/getaddrinfo_spec.rb new file mode 100644 index 0000000000..fa8112c010 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/getaddrinfo_spec.rb @@ -0,0 +1,112 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "Socket#getaddrinfo" do + before :each do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + BasicSocket.do_not_reverse_lookup = true + end + + after :each do + BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup + end + + platform_is_not :solaris, :windows do + it "gets the address information" do + expected = [] + # The check for AP_INET6's class is needed because ipaddr.rb adds + # fake AP_INET6 even in case when IPv6 is not really supported. + # Without such check, this test might fail when ipaddr was required + # by some other specs. + if (Socket.constants.include? 'AF_INET6') && + (Socket::AF_INET6.class != Object) then + expected.concat [ + ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6, + Socket::SOCK_DGRAM, Socket::IPPROTO_UDP], + ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6, + Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6, + Socket::SOCK_DGRAM, Socket::IPPROTO_UDP], + ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6, + Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ] + end + + expected.concat [ + ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET, + Socket::SOCK_DGRAM, Socket::IPPROTO_UDP], + ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET, + Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ] + + addrinfo = Socket.getaddrinfo SocketSpecs.hostname, 'discard' + addrinfo.each do |a| + case a.last + when Socket::IPPROTO_UDP, Socket::IPPROTO_TCP + expected.should include(a) + else + # don't check this. It's some weird protocol we don't know about + # so we can't spec it. + end + end + end + + # #getaddrinfo will return a INADDR_ANY address (0.0.0.0 + # or "::") if it's a passive socket. In the case of non-passive + # sockets (AI_PASSIVE not set) it should return the loopback + # address (127.0.0.1 or "::1". + + it "accepts empty addresses for IPv4 passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + Socket::AI_PASSIVE) + + expected = [["AF_INET", 9, "0.0.0.0", "0.0.0.0", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]] + res.should == expected + end + + it "accepts empty addresses for IPv4 non-passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + 0) + + expected = [["AF_INET", 9, "127.0.0.1", "127.0.0.1", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]] + res.should == expected + end + + + it "accepts empty addresses for IPv6 passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET6, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + Socket::AI_PASSIVE) + + expected = [ + ["AF_INET6", 9, "::", "::", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ["AF_INET6", 9, "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:0", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP] + ] + res.each { |a| expected.should include (a) } + end + + it "accepts empty addresses for IPv6 non-passive sockets" do + res = Socket.getaddrinfo(nil, "discard", + Socket::AF_INET6, + Socket::SOCK_STREAM, + Socket::IPPROTO_TCP, + 0) + + expected = [ + ["AF_INET6", 9, "::1", "::1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP], + ["AF_INET6", 9, "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP] + ] + res.each { |a| expected.should include(a) } + end + end +end diff --git a/spec/rubyspec/library/socket/socket/gethostbyaddr_spec.rb b/spec/rubyspec/library/socket/socket/gethostbyaddr_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/gethostbyaddr_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/gethostbyname_spec.rb b/spec/rubyspec/library/socket/socket/gethostbyname_spec.rb new file mode 100644 index 0000000000..a93c9ffb98 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/gethostbyname_spec.rb @@ -0,0 +1,17 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "Socket#gethostbyname" do + it "returns broadcast address info for ''" do + addr = Socket.gethostbyname(''); + addr.should == ["255.255.255.255", [], 2, "\377\377\377\377"] + end + + it "returns broadcast address info for ''" do + addr = Socket.gethostbyname(''); + addr.should == ["0.0.0.0", [], 2, "\000\000\000\000"] + end +end diff --git a/spec/rubyspec/library/socket/socket/gethostname_spec.rb b/spec/rubyspec/library/socket/socket/gethostname_spec.rb new file mode 100644 index 0000000000..c61e6b3eb4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/gethostname_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket.gethostname" do + it "returns the host name" do + Socket.gethostname.should == `hostname`.strip + end +end diff --git a/spec/rubyspec/library/socket/socket/getnameinfo_spec.rb b/spec/rubyspec/library/socket/socket/getnameinfo_spec.rb new file mode 100644 index 0000000000..2b0ea4a723 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/getnameinfo_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "Socket.getnameinfo" do + before :each do + @reverse_lookup = BasicSocket.do_not_reverse_lookup + BasicSocket.do_not_reverse_lookup = true + end + + after :each do + BasicSocket.do_not_reverse_lookup = @reverse_lookup + end + + it "gets the name information and don't resolve it" do + sockaddr = Socket.sockaddr_in SocketSpecs.port, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "#{SocketSpecs.port}"] + end + + def should_be_valid_dns_name(name) + # http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address + # ftp://ftp.rfc-editor.org/in-notes/rfc3696.txt + # http://domainkeys.sourceforge.net/underscore.html + valid_dns = /^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\-_]*[a-zA-Z0-9_])\.)*([A-Za-z_]|[A-Za-z_][A-Za-z0-9\-_]*[A-Za-z0-9_])\.?$/ + name.should =~ valid_dns + end + + it "gets the name information and resolve the host" do + sockaddr = Socket.sockaddr_in SocketSpecs.port, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICSERV) + should_be_valid_dns_name(name_info[0]) + name_info[1].should == SocketSpecs.port.to_s + end + + it "gets the name information and resolves the service" do + sockaddr = Socket.sockaddr_in 9, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr) + name_info.size.should == 2 + should_be_valid_dns_name(name_info[0]) + # see http://www.iana.org/assignments/port-numbers + name_info[1].should == 'discard' + end + + it "gets a 3-element array and doesn't resolve hostname" do + name_info = Socket.getnameinfo(["AF_INET", SocketSpecs.port, '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "#{SocketSpecs.port}"] + end + + it "gets a 3-element array and resolves the service" do + name_info = Socket.getnameinfo ["AF_INET", 9, '127.0.0.1'] + name_info[1].should == 'discard' + end + + it "gets a 4-element array and doesn't resolve hostname" do + name_info = Socket.getnameinfo(["AF_INET", SocketSpecs.port, 'foo', '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "#{SocketSpecs.port}"] + end + + it "gets a 4-element array and resolves the service" do + name_info = Socket.getnameinfo ["AF_INET", 9, 'foo', '127.0.0.1'] + name_info[1].should == 'discard' + end + +end diff --git a/spec/rubyspec/library/socket/socket/getservbyname_spec.rb b/spec/rubyspec/library/socket/socket/getservbyname_spec.rb new file mode 100644 index 0000000000..a48b5753b4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/getservbyname_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket#getservbyname" do + it "returns the port for service 'discard'" do + Socket.getservbyname('discard').should == 9 + end + + it "returns the port for service 'discard' with protocol 'tcp'" do + Socket.getservbyname('discard', 'tcp').should == 9 + end + + it "returns the port for service 'domain' with protocol 'udp'" do + Socket.getservbyname('domain', 'udp').should == 53 + end + + it "returns the port for service 'daytime'" do + Socket.getservbyname('daytime').should == 13 + end + + it "raises a SocketError when the service or port is invalid" do + lambda { Socket.getservbyname('invalid') }.should raise_error(SocketError) + end +end diff --git a/spec/rubyspec/library/socket/socket/listen_spec.rb b/spec/rubyspec/library/socket/socket/listen_spec.rb new file mode 100644 index 0000000000..ebc97954fb --- /dev/null +++ b/spec/rubyspec/library/socket/socket/listen_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +include Socket::Constants + +describe "Socket#listen" do + before :each do + @socket = Socket.new(AF_INET, SOCK_STREAM, 0) + end + + after :each do + @socket.closed?.should be_false + @socket.close + end + + it "verifies we can listen for incoming connections" do + sockaddr = Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1") + @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) + @socket.bind(sockaddr) + @socket.listen(1).should == 0 + end +end diff --git a/spec/rubyspec/library/socket/socket/new_spec.rb b/spec/rubyspec/library/socket/socket/new_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/new_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/rubyspec/library/socket/socket/pack_sockaddr_in_spec.rb new file mode 100644 index 0000000000..8c95b948dc --- /dev/null +++ b/spec/rubyspec/library/socket/socket/pack_sockaddr_in_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#pack_sockaddr_in" do + it_behaves_like :socket_pack_sockaddr_in, :pack_sockaddr_in +end diff --git a/spec/rubyspec/library/socket/socket/pack_sockaddr_un_spec.rb b/spec/rubyspec/library/socket/socket/pack_sockaddr_un_spec.rb new file mode 100644 index 0000000000..aacb6d54dc --- /dev/null +++ b/spec/rubyspec/library/socket/socket/pack_sockaddr_un_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#pack_sockaddr_un" do + it_behaves_like :socket_pack_sockaddr_un, :pack_sockaddr_un +end diff --git a/spec/rubyspec/library/socket/socket/pair_spec.rb b/spec/rubyspec/library/socket/socket/pair_spec.rb new file mode 100644 index 0000000000..663ca3f183 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/pair_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/socketpair', __FILE__) + +describe "Socket#pair" do + it_behaves_like :socket_socketpair, :pair +end diff --git a/spec/rubyspec/library/socket/socket/recvfrom_nonblock_spec.rb b/spec/rubyspec/library/socket/socket/recvfrom_nonblock_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/recvfrom_nonblock_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/recvfrom_spec.rb b/spec/rubyspec/library/socket/socket/recvfrom_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/recvfrom_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/sockaddr_in_spec.rb b/spec/rubyspec/library/socket/socket/sockaddr_in_spec.rb new file mode 100644 index 0000000000..59e0318fda --- /dev/null +++ b/spec/rubyspec/library/socket/socket/sockaddr_in_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#sockaddr_in" do + it_behaves_like :socket_pack_sockaddr_in, :sockaddr_in +end diff --git a/spec/rubyspec/library/socket/socket/sockaddr_un_spec.rb b/spec/rubyspec/library/socket/socket/sockaddr_un_spec.rb new file mode 100644 index 0000000000..fa233587d9 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/sockaddr_un_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#sockaddr_un" do + it_behaves_like :socket_pack_sockaddr_un, :sockaddr_un +end diff --git a/spec/rubyspec/library/socket/socket/socket_spec.rb b/spec/rubyspec/library/socket/socket/socket_spec.rb new file mode 100644 index 0000000000..dbaed17af4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/socket_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket" do + it "inherits from BasicSocket and IO" do + Socket.superclass.should == BasicSocket + BasicSocket.superclass.should == IO + end +end + +describe "The socket class hierarchy" do + it "has an IPSocket in parallel to Socket" do + Socket.ancestors.include?(IPSocket).should == false + IPSocket.ancestors.include?(Socket).should == false + IPSocket.superclass.should == BasicSocket + end + + it "has TCPSocket and UDPSocket subclasses of IPSocket" do + TCPSocket.superclass.should == IPSocket + UDPSocket.superclass.should == IPSocket + end + + platform_is_not :windows do + it "has a UNIXSocket in parallel to Socket" do + Socket.ancestors.include?(UNIXSocket).should == false + UNIXSocket.ancestors.include?(Socket).should == false + UNIXSocket.superclass.should == BasicSocket + end + end +end + +platform_is_not :windows do + describe "Server class hierarchy" do + it "contains UNIXServer" do + UNIXServer.superclass.should == UNIXSocket + end + end +end diff --git a/spec/rubyspec/library/socket/socket/socketpair_spec.rb b/spec/rubyspec/library/socket/socket/socketpair_spec.rb new file mode 100644 index 0000000000..80b07170a6 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/socketpair_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/socketpair', __FILE__) + +describe "Socket#socketpair" do + it_behaves_like :socket_socketpair, :socketpair +end diff --git a/spec/rubyspec/library/socket/socket/sysaccept_spec.rb b/spec/rubyspec/library/socket/socket/sysaccept_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/sysaccept_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/unpack_sockaddr_in_spec.rb b/spec/rubyspec/library/socket/socket/unpack_sockaddr_in_spec.rb new file mode 100644 index 0000000000..2df3b69a6d --- /dev/null +++ b/spec/rubyspec/library/socket/socket/unpack_sockaddr_in_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +describe "Socket.unpack_sockaddr_in" do + + it "decodes the host name and port number of a packed sockaddr_in" do + sockaddr = Socket.sockaddr_in SocketSpecs.port, '127.0.0.1' + Socket.unpack_sockaddr_in(sockaddr).should == [SocketSpecs.port, '127.0.0.1'] + end + + it "gets the hostname and port number from a passed Addrinfo" do + addrinfo = Addrinfo.tcp('127.0.0.1', SocketSpecs.port) + Socket.unpack_sockaddr_in(addrinfo).should == [SocketSpecs.port, '127.0.0.1'] + end + + platform_is_not :windows do + it "raises an ArgumentError when the sin_family is not AF_INET" do + sockaddr = Socket.sockaddr_un '/tmp/x' + lambda { Socket.unpack_sockaddr_in sockaddr }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when passed addrinfo is not AF_INET/AF_INET6" do + addrinfo = Addrinfo.unix('/tmp/sock') + lambda { Socket.unpack_sockaddr_in(addrinfo) }.should raise_error(ArgumentError) + end + end + +end diff --git a/spec/rubyspec/library/socket/socket/unpack_sockaddr_un_spec.rb b/spec/rubyspec/library/socket/socket/unpack_sockaddr_un_spec.rb new file mode 100644 index 0000000000..4d558c89c4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/unpack_sockaddr_un_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe 'Socket.unpack_sockaddr_un' do + platform_is_not :windows do + it 'decodes sockaddr to unix path' do + sockaddr = Socket.sockaddr_un('/tmp/sock') + Socket.unpack_sockaddr_un(sockaddr).should == '/tmp/sock' + end + + it 'returns unix path from a passed Addrinfo' do + addrinfo = Addrinfo.unix('/tmp/sock') + Socket.unpack_sockaddr_un(addrinfo).should == '/tmp/sock' + end + + it 'raises an ArgumentError when the sin_family is not AF_UNIX' do + sockaddr = Socket.sockaddr_in(SocketSpecs.port, '127.0.0.1') + lambda { Socket.unpack_sockaddr_un(sockaddr) }.should raise_error(ArgumentError) + end + + it 'raises an ArgumentError when passed addrinfo is not AF_UNIX' do + addrinfo = Addrinfo.tcp('127.0.0.1', SocketSpecs.port) + lambda { Socket.unpack_sockaddr_un(addrinfo) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/accept_nonblock_spec.rb b/spec/rubyspec/library/socket/tcpserver/accept_nonblock_spec.rb new file mode 100644 index 0000000000..d0f2673af3 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/accept_nonblock_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::TCPServer.accept_nonblock" do + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it "accepts non blocking connections" do + @server.listen(5) + lambda { + @server.accept_nonblock + }.should raise_error(IO::WaitReadable) + + c = TCPSocket.new("127.0.0.1", SocketSpecs.port) + sleep 0.1 + s = @server.accept_nonblock + + port, address = Socket.unpack_sockaddr_in(s.getsockname) + + port.should == SocketSpecs.port + address.should == "127.0.0.1" + s.should be_kind_of(TCPSocket) + + c.close + s.close + end + + it "raises an IOError if the socket is closed" do + @server.close + lambda { @server.accept }.should raise_error(IOError) + end + + describe 'without a connected client' do + it 'raises error' do + lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable) + end + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @server.accept_nonblock(exception: false).should == :wait_readable + end + end + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/accept_spec.rb b/spec/rubyspec/library/socket/tcpserver/accept_spec.rb new file mode 100644 index 0000000000..cf1fbbd873 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/accept_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + + +describe "TCPServer#accept" do + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it "accepts a connection and returns a TCPSocket" do + data = nil + t = Thread.new do + client = @server.accept + client.should be_kind_of(TCPSocket) + data = client.read(5) + client << "goodbye" + client.close + end + Thread.pass while t.status and t.status != "sleep" + + socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + socket.write('hello') + socket.shutdown(1) # we are done with sending + socket.read.should == 'goodbye' + t.join + data.should == 'hello' + socket.close + end + + it "can be interrupted by Thread#kill" do + t = Thread.new { @server.accept } + + Thread.pass while t.status and t.status != "sleep" + + # kill thread, ensure it dies in a reasonable amount of time + t.kill + a = 1 + while a < 2000 + break unless t.alive? + Thread.pass + sleep 0.2 + a += 1 + end + a.should < 2000 + end + + it "can be interrupted by Thread#raise" do + t = Thread.new { @server.accept } + + Thread.pass while t.status and t.status != "sleep" + + # raise in thread, ensure the raise happens + ex = Exception.new + t.raise ex + lambda { t.join }.should raise_error(Exception) + end + + it "raises an IOError if the socket is closed" do + @server.close + lambda { @server.accept }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/gets_spec.rb b/spec/rubyspec/library/socket/tcpserver/gets_spec.rb new file mode 100644 index 0000000000..2f8e699a53 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/gets_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPServer#gets" do + before :each do + @server = TCPServer.new(SocketSpecs.hostname, SocketSpecs.port) + end + + after :each do + @server.close + end + + it "raises Errno::ENOTCONN on gets" do + lambda { @server.gets }.should raise_error(Errno::ENOTCONN) + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/listen_spec.rb b/spec/rubyspec/library/socket/tcpserver/listen_spec.rb new file mode 100644 index 0000000000..5e9b9c1090 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/listen_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe 'TCPServer#listen' do + before :each do + @server = TCPServer.new(SocketSpecs.hostname, SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it 'returns 0' do + @server.listen(10).should == 0 + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/new_spec.rb b/spec/rubyspec/library/socket/tcpserver/new_spec.rb new file mode 100644 index 0000000000..4b7f53a397 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/new_spec.rb @@ -0,0 +1,97 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPServer.new" do + after :each do + @server.close if @server && !@server.closed? + end + + it "binds to a host and a port" do + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + addr = @server.addr + addr[0].should == 'AF_INET' + addr[1].should be_kind_of(Fixnum) + # on some platforms (Mac), MRI + # returns comma at the end. + addr[2].should =~ /^#{SocketSpecs.hostname}\b/ + addr[3].should == '127.0.0.1' + end + + it "binds to localhost and a port with either IPv4 or IPv6" do + @server = TCPServer.new(SocketSpecs.hostname, SocketSpecs.port) + addr = @server.addr + if addr[0] == 'AF_INET' + addr[1].should == SocketSpecs.port + addr[2].should =~ /^#{SocketSpecs.hostname}\b/ + addr[3].should == '127.0.0.1' + else + addr[1].should == SocketSpecs.port + addr[2].should =~ /^#{SocketSpecs.hostnamev6}\b/ + addr[3].should == '::1' + end + end + + it "binds to INADDR_ANY if the hostname is empty" do + @server = TCPServer.new('', SocketSpecs.port) + addr = @server.addr + addr[0].should == 'AF_INET' + addr[1].should == SocketSpecs.port + addr[2].should == '0.0.0.0' + addr[3].should == '0.0.0.0' + end + + it "binds to INADDR_ANY if the hostname is empty and the port is a string" do + @server = TCPServer.new('', SocketSpecs.port.to_s) + addr = @server.addr + addr[0].should == 'AF_INET' + addr[1].should == SocketSpecs.port + addr[2].should == '0.0.0.0' + addr[3].should == '0.0.0.0' + end + + it "coerces port to string, then determines port from that number or service name" do + t = Object.new + lambda { TCPServer.new(SocketSpecs.hostname, t) }.should raise_error(TypeError) + + def t.to_str; SocketSpecs.port.to_s; end + + @server = TCPServer.new(SocketSpecs.hostname, t) + addr = @server.addr + addr[1].should == SocketSpecs.port + + # TODO: This should also accept strings like 'https', but I don't know how to + # pick such a service port that will be able to reliably bind... + end + + it "raises Errno::EADDRNOTAVAIL when the adress is unknown" do + lambda { TCPServer.new("1.2.3.4", 4000) }.should raise_error(Errno::EADDRNOTAVAIL) + end + + # There is no way to make this fail-proof on all machines, because + # DNS servers like opendns return A records for ANY host, including + # traditionally invalidly named ones. + quarantine! do + it "raises a SocketError when the host is unknown" do + lambda { + TCPServer.new("--notavalidname", 4000) + }.should raise_error(SocketError) + end + end + + it "raises Errno::EADDRINUSE when address is already in use" do + lambda { + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + }.should raise_error(Errno::EADDRINUSE) + end + + platform_is_not :windows, :aix do + # A known bug in AIX. getsockopt(2) does not properly set + # the fifth argument for SO_REUSEADDR. + it "sets SO_REUSEADDR on the resulting server" do + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + @server.getsockopt(:SOCKET, :REUSEADDR).data.should_not == "\x00\x00\x00\x00" + @server.getsockopt(:SOCKET, :REUSEADDR).int.should_not == 0 + end + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/output_spec.rb b/spec/rubyspec/library/socket/tcpserver/output_spec.rb new file mode 100644 index 0000000000..b193bb83ea --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/output_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPServer#<<" do + after :each do + @server.close if @server + @socket.close if @socket + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/readpartial_spec.rb b/spec/rubyspec/library/socket/tcpserver/readpartial_spec.rb new file mode 100644 index 0000000000..35d07a0309 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/readpartial_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPServer#readpartial" do + after :each do + @server.close if @server + @socket.close if @socket + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/sysaccept_spec.rb b/spec/rubyspec/library/socket/tcpserver/sysaccept_spec.rb new file mode 100644 index 0000000000..1cd1c1c719 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/sysaccept_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "TCPServer#sysaccept" do + before :each do + @server = TCPServer.new(SocketSpecs.hostname, SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it 'blocks if no connections' do + lambda { @server.sysaccept }.should block_caller + end + + it 'returns file descriptor of an accepted connection' do + begin + sock = TCPSocket.new(SocketSpecs.hostname, SocketSpecs.port) + + fd = @server.sysaccept + + fd.should be_an_instance_of(Fixnum) + ensure + sock.close if sock && !sock.closed? + IO.for_fd(fd).close if fd + end + end +end diff --git a/spec/rubyspec/library/socket/tcpsocket/gethostbyname_spec.rb b/spec/rubyspec/library/socket/tcpsocket/gethostbyname_spec.rb new file mode 100644 index 0000000000..11838aca27 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/gethostbyname_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +# TODO: verify these for windows +describe "TCPSocket#gethostbyname" do + before :each do + @host_info = TCPSocket.gethostbyname(SocketSpecs.hostname) + end + + it "returns an array elements of information on the hostname" do + @host_info.should be_kind_of(Array) + end + + platform_is_not :windows do + it "returns the canonical name as first value" do + @host_info[0].should == SocketSpecs.hostname + end + + it "returns the address type as the third value" do + address_type = @host_info[2] + [Socket::AF_INET, Socket::AF_INET6].include?(address_type).should be_true + end + + it "returns the IP address as the fourth value" do + ip = @host_info[3] + ["127.0.0.1", "::1"].include?(ip).should be_true + end + end + + platform_is :windows do + quarantine! do # name lookup seems not working on Windows CI + it "returns the canonical name as first value" do + host = "#{ENV['COMPUTERNAME'].downcase}" + host << ".#{ENV['USERDNSDOMAIN'].downcase}" if ENV['USERDNSDOMAIN'] + @host_info[0].should == host + end + end + + it "returns the address type as the third value" do + @host_info[2].should == Socket::AF_INET + end + + it "returns the IP address as the fourth value" do + @host_info[3].should == "127.0.0.1" + end + end + + it "returns any aliases to the address as second value" do + @host_info[1].should be_kind_of(Array) + end +end diff --git a/spec/rubyspec/library/socket/tcpsocket/new_spec.rb b/spec/rubyspec/library/socket/tcpsocket/new_spec.rb new file mode 100644 index 0000000000..279576272b --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/new_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/new', __FILE__) + +describe "TCPSocket.new" do + it_behaves_like :tcpsocket_new, :new +end diff --git a/spec/rubyspec/library/socket/tcpsocket/open_spec.rb b/spec/rubyspec/library/socket/tcpsocket/open_spec.rb new file mode 100644 index 0000000000..fb4cc4629a --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/open_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/new', __FILE__) + +describe "TCPSocket.open" do + it_behaves_like :tcpsocket_new, :open +end diff --git a/spec/rubyspec/library/socket/tcpsocket/partially_closable_spec.rb b/spec/rubyspec/library/socket/tcpsocket/partially_closable_spec.rb new file mode 100644 index 0000000000..f38b251090 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/partially_closable_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/partially_closable_sockets', __FILE__) + +describe "TCPSocket partial closability" do + + before :each do + port = SocketSpecs.find_available_port + @server = TCPServer.new("127.0.0.1", port) + @s1 = TCPSocket.new("127.0.0.1", port) + @s2 = @server.accept + end + + after :each do + @server.close + @s1.close + @s2.close + end + + it_should_behave_like "partially closable sockets" + +end diff --git a/spec/rubyspec/library/socket/tcpsocket/recv_nonblock_spec.rb b/spec/rubyspec/library/socket/tcpsocket/recv_nonblock_spec.rb new file mode 100644 index 0000000000..1d89aa866c --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/recv_nonblock_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPSocket#recv_nonblock" do + before :each do + @server = SocketSpecs::SpecTCPServer.new + @hostname = @server.hostname + end + + after :each do + if @socket + @socket.write "QUIT" + @socket.close + end + @server.shutdown + end + + it "returns a String read from the socket" do + @socket = TCPSocket.new @hostname, SocketSpecs.port + @socket.write "TCPSocket#recv_nonblock" + + # Wait for the server to echo. This spec is testing the return + # value, not the non-blocking behavior. + # + # TODO: Figure out a good way to test non-blocking. + IO.select([@socket]) + @socket.recv_nonblock(50).should == "TCPSocket#recv_nonblock" + end + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @socket = TCPSocket.new @hostname, SocketSpecs.port + @socket.recv_nonblock(50, exception: false).should == :wait_readable + end + end +end diff --git a/spec/rubyspec/library/socket/tcpsocket/setsockopt_spec.rb b/spec/rubyspec/library/socket/tcpsocket/setsockopt_spec.rb new file mode 100644 index 0000000000..e4d3da10d1 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/setsockopt_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPSocket#setsockopt" do + before :each do + @server = SocketSpecs::SpecTCPServer.new + @hostname = @server.hostname + end + + before :each do + @sock = TCPSocket.new @hostname, SocketSpecs.port + end + + after :each do + @sock.close unless @sock.closed? + @server.shutdown + end + + describe "using constants" do + it "sets the TCP nodelay to 1" do + @sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1).should == 0 + end + end + + describe "using symbols" do + it "sets the TCP nodelay to 1" do + @sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, 1).should == 0 + end + + context "without prefix" do + it "sets the TCP nodelay to 1" do + @sock.setsockopt(:TCP, :NODELAY, 1).should == 0 + end + end + end + + describe "using strings" do + it "sets the TCP nodelay to 1" do + @sock.setsockopt('IPPROTO_TCP', 'TCP_NODELAY', 1).should == 0 + end + + context "without prefix" do + it "sets the TCP nodelay to 1" do + @sock.setsockopt('TCP', 'NODELAY', 1).should == 0 + end + end + end +end \ No newline at end of file diff --git a/spec/rubyspec/library/socket/tcpsocket/shared/new.rb b/spec/rubyspec/library/socket/tcpsocket/shared/new.rb new file mode 100644 index 0000000000..b6f557fc18 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/shared/new.rb @@ -0,0 +1,71 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/classes', __FILE__) + +describe :tcpsocket_new, shared: true do + it "requires a hostname and a port as arguments" do + lambda { TCPSocket.send(@method) }.should raise_error(ArgumentError) + end + + it "refuses the connection when there is no server to connect to" do + lambda do + TCPSocket.send(@method, SocketSpecs.hostname, SocketSpecs.local_port) + end.should raise_error(Errno::ECONNREFUSED) + end + + describe "with a running server" do + before :each do + @server = SocketSpecs::SpecTCPServer.new + @hostname = @server.hostname + end + + after :each do + if @socket + @socket.write "QUIT" + @socket.close + end + @server.shutdown + end + + it "silently ignores 'nil' as the third parameter" do + @socket = TCPSocket.send(@method, @hostname, SocketSpecs.port, nil) + @socket.should be_an_instance_of(TCPSocket) + end + + it "connects to a listening server with host and port" do + @socket = TCPSocket.send(@method, @hostname, SocketSpecs.port) + @socket.should be_an_instance_of(TCPSocket) + end + + it "connects to a server when passed local_host argument" do + @socket = TCPSocket.send(@method, @hostname, SocketSpecs.port, @hostname) + @socket.should be_an_instance_of(TCPSocket) + end + + it "connects to a server when passed local_host and local_port arguments" do + @socket = TCPSocket.send(@method, @hostname, SocketSpecs.port, + @hostname, SocketSpecs.local_port) + @socket.should be_an_instance_of(TCPSocket) + end + + it "has an address once it has connected to a listening server" do + @socket = TCPSocket.send(@method, @hostname, SocketSpecs.port) + @socket.should be_an_instance_of(TCPSocket) + + # TODO: Figure out how to abstract this. You can get AF_INET + # from 'Socket.getaddrinfo(hostname, nil)[0][3]' but socket.addr + # will return AF_INET6. At least this check will weed out clearly + # erroneous values. + @socket.addr[0].should =~ /^AF_INET6?/ + + case @socket.addr[0] + when 'AF_INET' + @socket.addr[3].should == SocketSpecs.addr(:ipv4) + when 'AF_INET6' + @socket.addr[3].should == SocketSpecs.addr(:ipv6) + end + + @socket.addr[1].should be_kind_of(Fixnum) + @socket.addr[2].should =~ /^#{@hostname}/ + end + end +end diff --git a/spec/rubyspec/library/socket/udpsocket/bind_spec.rb b/spec/rubyspec/library/socket/udpsocket/bind_spec.rb new file mode 100644 index 0000000000..067baa2472 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/bind_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UDPSocket.bind" do + + before :each do + @socket = UDPSocket.new + end + + after :each do + @socket.close unless @socket.closed? + end + + it "binds the socket to a port" do + @socket.bind(SocketSpecs.hostname, SocketSpecs.port) + + lambda { @socket.bind(SocketSpecs.hostname, SocketSpecs.port) }.should raise_error + end + + it "receives a hostname and a port" do + @socket.bind(SocketSpecs.hostname, SocketSpecs.port) + + port, host = Socket.unpack_sockaddr_in(@socket.getsockname) + + host.should == "127.0.0.1" + port.should == SocketSpecs.port + end + + it "binds to INADDR_ANY if the hostname is empty" do + @socket.bind("", SocketSpecs.port) + port, host = Socket.unpack_sockaddr_in(@socket.getsockname) + host.should == "0.0.0.0" + port.should == SocketSpecs.port + end +end diff --git a/spec/rubyspec/library/socket/udpsocket/connect_spec.rb b/spec/rubyspec/library/socket/udpsocket/connect_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/connect_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/udpsocket/new_spec.rb b/spec/rubyspec/library/socket/udpsocket/new_spec.rb new file mode 100644 index 0000000000..5a2e4e1f31 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/new_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe 'UDPSocket.new' do + after :each do + @socket.close if @socket && !@socket.closed? + end + + it 'without arguments' do + @socket = UDPSocket.new + @socket.should be_an_instance_of(UDPSocket) + end + + it 'using Fixnum argument' do + @socket = UDPSocket.new(Socket::AF_INET) + @socket.should be_an_instance_of(UDPSocket) + end + + it 'using Symbol argument' do + @socket = UDPSocket.new(:INET) + @socket.should be_an_instance_of(UDPSocket) + end + + it 'using String argument' do + @socket = UDPSocket.new('INET') + @socket.should be_an_instance_of(UDPSocket) + end + + it 'raises Errno::EAFNOSUPPORT if unsupported family passed' do + lambda { UDPSocket.new(-1) }.should raise_error(Errno::EAFNOSUPPORT) + end +end diff --git a/spec/rubyspec/library/socket/udpsocket/open_spec.rb b/spec/rubyspec/library/socket/udpsocket/open_spec.rb new file mode 100644 index 0000000000..188f879ed1 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/open_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UDPSocket.open" do + after :each do + @socket.close if @socket && !@socket.closed? + end + + it "allows calls to open without arguments" do + @socket = UDPSocket.open + @socket.should be_kind_of(UDPSocket) + end +end diff --git a/spec/rubyspec/library/socket/udpsocket/recvfrom_nonblock_spec.rb b/spec/rubyspec/library/socket/udpsocket/recvfrom_nonblock_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/recvfrom_nonblock_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/udpsocket/send_spec.rb b/spec/rubyspec/library/socket/udpsocket/send_spec.rb new file mode 100644 index 0000000000..ad0e6a7f2f --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/send_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UDPSocket.send" do + before :each do + @ready = false + @server_thread = Thread.new do + @server = UDPSocket.open + @server.bind(nil, SocketSpecs.port) + @ready = true + begin + @msg = @server.recvfrom_nonblock(64) + rescue IO::WaitReadable + IO.select([@server]) + retry + end + @server.close + end + Thread.pass while @server_thread.status and !@ready + end + + it "sends data in ad hoc mode" do + @socket = UDPSocket.open + @socket.send("ad hoc", 0, SocketSpecs.hostname,SocketSpecs.port) + @socket.close + @server_thread.join + + @msg[0].should == "ad hoc" + @msg[1][0].should == "AF_INET" + @msg[1][1].should be_kind_of(Fixnum) + @msg[1][3].should == "127.0.0.1" + end + + it "sends data in ad hoc mode (with port given as a String)" do + @socket = UDPSocket.open + @socket.send("ad hoc", 0, SocketSpecs.hostname,SocketSpecs.str_port) + @socket.close + @server_thread.join + + @msg[0].should == "ad hoc" + @msg[1][0].should == "AF_INET" + @msg[1][1].should be_kind_of(Fixnum) + @msg[1][3].should == "127.0.0.1" + end + + it "sends data in connection mode" do + @socket = UDPSocket.open + @socket.connect(SocketSpecs.hostname,SocketSpecs.port) + @socket.send("connection-based", 0) + @socket.close + @server_thread.join + + @msg[0].should == "connection-based" + @msg[1][0].should == "AF_INET" + @msg[1][1].should be_kind_of(Fixnum) + @msg[1][3].should == "127.0.0.1" + end +end diff --git a/spec/rubyspec/library/socket/unixserver/accept_nonblock_spec.rb b/spec/rubyspec/library/socket/unixserver/accept_nonblock_spec.rb new file mode 100644 index 0000000000..bad9139eea --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/accept_nonblock_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXServer#accept_nonblock" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + + @socket = @server.accept_nonblock + @client.send("foobar", 0) + end + + after :each do + @socket.close + @client.close + @server.close + rm_r @path + end + + it "accepts a connection in a non-blocking way" do + data = @socket.recvfrom(6).first + data.should == "foobar" + end + + it "returns a UNIXSocket" do + @socket.should be_kind_of(UNIXSocket) + end + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @server.accept_nonblock(exception: false).should == :wait_readable + end + end + end +end diff --git a/spec/rubyspec/library/socket/unixserver/accept_spec.rb b/spec/rubyspec/library/socket/unixserver/accept_spec.rb new file mode 100644 index 0000000000..6ce1f2c6db --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/accept_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is_not :windows do + describe "UNIXServer#accept" do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + end + + after :each do + rm_r @path + end + + it "accepts what is written by the client" do + server = UNIXServer.open(SocketSpecs.socket_path) + client = UNIXSocket.open(SocketSpecs.socket_path) + + client.send('hello', 0) + + sock = server.accept + data, info = sock.recvfrom(5) + + data.should == 'hello' + info.should_not be_empty + + server.close + client.close + sock.close + end + + it "can be interrupted by Thread#kill" do + server = UNIXServer.new(@path) + t = Thread.new { + server.accept + } + Thread.pass while t.status and t.status != "sleep" + + # kill thread, ensure it dies in a reasonable amount of time + t.kill + a = 1 + while a < 2000 + break unless t.alive? + Thread.pass + sleep 0.2 + a += 1 + end + a.should < 2000 + server.close + end + + it "can be interrupted by Thread#raise" do + server = UNIXServer.new(@path) + t = Thread.new { + server.accept + } + Thread.pass while t.status and t.status != "sleep" + + # raise in thread, ensure the raise happens + ex = Exception.new + t.raise ex + lambda { t.join }.should raise_error(Exception) + server.close + end + end +end diff --git a/spec/rubyspec/library/socket/unixserver/for_fd_spec.rb b/spec/rubyspec/library/socket/unixserver/for_fd_spec.rb new file mode 100644 index 0000000000..bf8aa41d40 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/for_fd_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is_not :windows do + describe "UNIXServer#for_fd" do + before :each do + @unix_path = tmp("unix_socket") + @unix = UNIXServer.new(@unix_path) + end + + after :each do + @unix.close if @unix + rm_r @unix_path + end + + it "can calculate the path" do + b = UNIXServer.for_fd(@unix.fileno) + b.autoclose = false + + b.path.should == @unix_path + end + end +end diff --git a/spec/rubyspec/library/socket/unixserver/new_spec.rb b/spec/rubyspec/library/socket/unixserver/new_spec.rb new file mode 100644 index 0000000000..d34aa0ca03 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/new_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXServer.new" do + it_behaves_like :unixserver_new, :new +end diff --git a/spec/rubyspec/library/socket/unixserver/open_spec.rb b/spec/rubyspec/library/socket/unixserver/open_spec.rb new file mode 100644 index 0000000000..4401d9dda8 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/open_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXServer.open" do + it_behaves_like :unixserver_new, :open + + platform_is_not :windows do + before :each do + @path = tmp("unixserver_spec") + rm_r @path + end + + after :each do + @server.close if @server + @server = nil + rm_r @path + end + + it "yields the new UNIXServer object to the block, if given" do + UNIXServer.open(@path) do |unix| + unix.path.should == @path + unix.addr.should == ["AF_UNIX", @path] + end + end + end +end diff --git a/spec/rubyspec/library/socket/unixserver/shared/new.rb b/spec/rubyspec/library/socket/unixserver/shared/new.rb new file mode 100644 index 0000000000..9b0798b828 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/shared/new.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/classes', __FILE__) +require 'tempfile' + +describe :unixserver_new, shared: true do + platform_is_not :windows do + before :each do + @path = tmp("unixserver_spec") + rm_r @path + end + + after :each do + @server.close if @server + @server = nil + rm_r @path + end + + it "creates a new UNIXServer" do + @server = UNIXServer.send(@method, @path) + @server.path.should == @path + @server.addr.should == ["AF_UNIX", @path] + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/addr_spec.rb b/spec/rubyspec/library/socket/unixsocket/addr_spec.rb new file mode 100644 index 0000000000..893a910e92 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/addr_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#addr" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @path + end + + it "returns the address family of this socket in an array" do + @client.addr[0].should == "AF_UNIX" + end + + it "returns the path of the socket in an array if it's a server" do + @server.addr[1].should == @path + end + + it "returns an empty string for path if it's a client" do + @client.addr[1].should == "" + end + + it "returns an array" do + @client.addr.should be_kind_of(Array) + end + end + +end diff --git a/spec/rubyspec/library/socket/unixsocket/inspect_spec.rb b/spec/rubyspec/library/socket/unixsocket/inspect_spec.rb new file mode 100644 index 0000000000..8ea25ec1e9 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/inspect_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#inspect" do + platform_is_not :windows do + it "returns sockets fd for unnamed sockets" do + begin + s1, s2 = UNIXSocket.socketpair + s1.inspect.should == "#" + s2.inspect.should == "#" + ensure + s1.close + s2.close + end + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/new_spec.rb b/spec/rubyspec/library/socket/unixsocket/new_spec.rb new file mode 100644 index 0000000000..7db8613b96 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/new_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXSocket.new" do + it_behaves_like :unixsocket_new, :new +end diff --git a/spec/rubyspec/library/socket/unixsocket/open_spec.rb b/spec/rubyspec/library/socket/unixsocket/open_spec.rb new file mode 100644 index 0000000000..eb8ffbaf22 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/open_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXSocket.open" do + it_behaves_like :unixsocket_new, :open +end + +describe "UNIXSocket.open" do + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + @server = UNIXServer.open(@path) + end + + after :each do + @server.close + rm_r @path + end + + it "opens a unix socket on the specified file and yields it to the block" do + UNIXSocket.send(@method, @path) do |client| + client.addr[0].should == "AF_UNIX" + client.closed?.should == false + end + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/pair_spec.rb b/spec/rubyspec/library/socket/unixsocket/pair_spec.rb new file mode 100644 index 0000000000..5cd75e2906 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/pair_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/partially_closable_sockets', __FILE__) + +describe "UNIXSocket#pair" do + platform_is_not :windows do + + it_should_behave_like "partially closable sockets" + + before :each do + @s1, @s2 = UNIXSocket.pair + end + + after :each do + @s1.close + @s2.close + end + + it "returns a pair of connected sockets" do + @s1.puts "foo" + @s2.gets.should == "foo\n" + end + + it "returns sockets with no name" do + @s1.path.should == @s2.path + @s1.path.should == "" + end + + it "returns sockets with no address" do + @s1.addr.should == ["AF_UNIX", ""] + @s2.addr.should == ["AF_UNIX", ""] + end + + it "returns sockets with no peeraddr" do + @s1.peeraddr.should == ["AF_UNIX", ""] + @s2.peeraddr.should == ["AF_UNIX", ""] + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/partially_closable_spec.rb b/spec/rubyspec/library/socket/unixsocket/partially_closable_spec.rb new file mode 100644 index 0000000000..1123a23541 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/partially_closable_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/partially_closable_sockets', __FILE__) + +platform_is_not :windows do + describe "UNIXSocket partial closability" do + + before :each do + @path = SocketSpecs.socket_path + rm_r @path + @server = UNIXServer.open(@path) + @s1 = UNIXSocket.new(@path) + @s2 = @server.accept + end + + after :each do + @server.close + @s1.close + @s2.close + rm_r @path + end + + it_should_behave_like "partially closable sockets" + + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/path_spec.rb b/spec/rubyspec/library/socket/unixsocket/path_spec.rb new file mode 100644 index 0000000000..a9186854da --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/path_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#path" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @path + end + + it "returns the path of the socket if it's a server" do + @server.path.should == @path + end + + it "returns an empty string for path if it's a client" do + @client.path.should == "" + end + end + +end diff --git a/spec/rubyspec/library/socket/unixsocket/peeraddr_spec.rb b/spec/rubyspec/library/socket/unixsocket/peeraddr_spec.rb new file mode 100644 index 0000000000..cd224540ef --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/peeraddr_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#peeraddr" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @path + end + + it "returns the address familly and path of the server end of the connection" do + @client.peeraddr.should == ["AF_UNIX", @path] + end + + it "raises an error in server sockets" do + lambda { @server.peeraddr }.should raise_error + end + end + +end diff --git a/spec/rubyspec/library/socket/unixsocket/recv_io_spec.rb b/spec/rubyspec/library/socket/unixsocket/recv_io_spec.rb new file mode 100644 index 0000000000..9fc4470572 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/recv_io_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#recv_io" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + + @send_io_path = File.expand_path('../../fixtures/send_io.txt', __FILE__) + @file = File.open(@send_io_path) + end + + after :each do + @io.close if @io + @socket.close if @socket + + @file.close + @client.close + @server.close + rm_r @path + end + + it "reads an IO object across the socket" do + @client.send_io(@file) + + @socket = @server.accept + @io = @socket.recv_io + + @io.read.should == File.read(@send_io_path) + end + + it "takes an optional class to use" do + @client.send_io(@file) + + @socket = @server.accept + @io = @socket.recv_io(File) + + @io.should be_kind_of(File) + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/recvfrom_spec.rb b/spec/rubyspec/library/socket/unixsocket/recvfrom_spec.rb new file mode 100644 index 0000000000..7ac002607c --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/recvfrom_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#recvfrom" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @path + end + + it "receives len bytes from sock" do + @client.send("foobar", 0) + sock = @server.accept + sock.recvfrom(6).first.should == "foobar" + sock.close + end + + it "returns an array with data and information on the sender" do + @client.send("foobar", 0) + sock = @server.accept + data = sock.recvfrom(6) + data.first.should == "foobar" + data.last.should == ["AF_UNIX", ""] + sock.close + end + + it "uses different message options" do + @client.send("foobar", Socket::MSG_PEEK) + sock = @server.accept + peek_data = sock.recvfrom(6, Socket::MSG_PEEK) # Does not retrieve the message + real_data = sock.recvfrom(6) + + real_data.should == peek_data + peek_data.should == ["foobar", ["AF_UNIX", ""]] + sock.close + end + end + +end diff --git a/spec/rubyspec/library/socket/unixsocket/send_io_spec.rb b/spec/rubyspec/library/socket/unixsocket/send_io_spec.rb new file mode 100644 index 0000000000..fb6ce9ba17 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/send_io_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#send_io" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + + @send_io_path = File.expand_path('../../fixtures/send_io.txt', __FILE__) + @file = File.open(@send_io_path) + end + + after :each do + @io.close if @io + @socket.close if @socket + + @file.close + @client.close + @server.close + rm_r @path + end + + it "sends the fd for an IO object across the socket" do + @client.send_io(@file) + + @socket = @server.accept + @io = @socket.recv_io + + @io.read.should == File.read(@send_io_path) + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/shared/new.rb b/spec/rubyspec/library/socket/unixsocket/shared/new.rb new file mode 100644 index 0000000000..9d8fb809d2 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/shared/new.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/classes', __FILE__) + +describe :unixsocket_new, shared: true do + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + @server = UNIXServer.open(@path) + end + + after :each do + @client.close if @client + @server.close + rm_r @path + end + + it "opens a unix socket on the specified file" do + @client = UNIXSocket.send(@method, @path) + + @client.addr[0].should == "AF_UNIX" + @client.closed?.should == false + end + end +end diff --git a/spec/rubyspec/library/stringio/append_spec.rb b/spec/rubyspec/library/stringio/append_spec.rb new file mode 100644 index 0000000000..ff0dc233cd --- /dev/null +++ b/spec/rubyspec/library/stringio/append_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#<< when passed [Object]" do + before :each do + @io = StringIO.new("example") + end + + it "returns self" do + (@io << "just testing").should equal(@io) + end + + it "writes the passed argument onto self" do + (@io << "just testing") + @io.string.should == "just testing" + (@io << " and more testing") + @io.string.should == "just testing and more testing" + end + + it "writes the passed argument at the current position" do + @io.pos = 5 + @io << "" + @io.string.should == "examp" + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 15 + @io << "just testing" + @io.string.should == "example\000\000\000\000\000\000\000\000just testing" + end + + it "taints self's String when the passed argument is tainted" do + (@io << "test".taint) + @io.string.tainted?.should be_true + end + + it "does not taint self when the passed argument is tainted" do + (@io << "test".taint) + @io.tainted?.should be_false + end + + it "updates self's position" do + @io << "test" + @io.pos.should eql(4) + end + + it "tries to convert the passed argument to a String using #to_s" do + obj = mock("to_s") + obj.should_receive(:to_s).and_return("Test") + + (@io << obj).string.should == "Testple" + end +end + +describe "StringIO#<< when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + lambda { io << "test" }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + lambda { io << "test" }.should raise_error(IOError) + end +end + +describe "StringIO#<< when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self, ignoring current position" do + (@io << ", just testing") + @io.string.should == "example, just testing" + + @io.pos = 3 + (@io << " and more testing") + @io.string.should == "example, just testing and more testing" + end + + it "correctly updates self's position" do + @io << ", testing" + @io.pos.should eql(16) + end +end diff --git a/spec/rubyspec/library/stringio/binmode_spec.rb b/spec/rubyspec/library/stringio/binmode_spec.rb new file mode 100644 index 0000000000..11fbbaadeb --- /dev/null +++ b/spec/rubyspec/library/stringio/binmode_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#binmode" do + it "returns self" do + io = StringIO.new("example") + io.binmode.should equal(io) + end +end diff --git a/spec/rubyspec/library/stringio/bytes_spec.rb b/spec/rubyspec/library/stringio/bytes_spec.rb new file mode 100644 index 0000000000..22d990dff7 --- /dev/null +++ b/spec/rubyspec/library/stringio/bytes_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/each_byte', __FILE__) + +describe "StringIO#bytes" do + it_behaves_like :stringio_each_byte, :bytes +end + +describe "StringIO#bytes when self is not readable" do + it_behaves_like :stringio_each_byte_not_readable, :bytes +end diff --git a/spec/rubyspec/library/stringio/chars_spec.rb b/spec/rubyspec/library/stringio/chars_spec.rb new file mode 100644 index 0000000000..c5ffde23db --- /dev/null +++ b/spec/rubyspec/library/stringio/chars_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/each_char', __FILE__) + +describe "StringIO#chars" do + it_behaves_like :stringio_each_char, :chars +end + +describe "StringIO#chars when self is not readable" do + it_behaves_like :stringio_each_char_not_readable, :chars +end diff --git a/spec/rubyspec/library/stringio/close_read_spec.rb b/spec/rubyspec/library/stringio/close_read_spec.rb new file mode 100644 index 0000000000..59e16385d2 --- /dev/null +++ b/spec/rubyspec/library/stringio/close_read_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#close_read" do + before :each do + @io = StringIO.new("example") + end + + it "returns nil" do + @io.close_read.should be_nil + end + + it "prevents further reading" do + @io.close_read + lambda { @io.read(1) }.should raise_error(IOError) + end + + it "allows further writing" do + @io.close_read + @io.write("x").should == 1 + end + + it "raises an IOError when in write-only mode" do + io = StringIO.new("example", "w") + lambda { io.close_read }.should raise_error(IOError) + + io = StringIO.new("example") + io.close_read + ruby_version_is ''...'2.3' do + lambda { io.close_read }.should raise_error(IOError) + end + ruby_version_is '2.3' do + io.close_read.should == nil + end + end +end diff --git a/spec/rubyspec/library/stringio/close_spec.rb b/spec/rubyspec/library/stringio/close_spec.rb new file mode 100644 index 0000000000..423aad4bd1 --- /dev/null +++ b/spec/rubyspec/library/stringio/close_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#close" do + before :each do + @io = StringIOSpecs.build + end + + it "returns nil" do + @io.close.should be_nil + end + + it "prevents further reading and/or writing" do + @io.close + lambda { @io.read(1) }.should raise_error(IOError) + lambda { @io.write('x') }.should raise_error(IOError) + end + + ruby_version_is ''...'2.3' do + it "raises an IOError when self was already closed" do + @io.close + lambda { @io.close }.should raise_error(IOError) + end + end + + ruby_version_is "2.3" do + it "does not raise anything when self was already closed" do + @io.close + lambda { @io.close }.should_not raise_error(IOError) + end + end +end diff --git a/spec/rubyspec/library/stringio/close_write_spec.rb b/spec/rubyspec/library/stringio/close_write_spec.rb new file mode 100644 index 0000000000..6637fe6043 --- /dev/null +++ b/spec/rubyspec/library/stringio/close_write_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#close_write" do + before :each do + @io = StringIO.new("example") + end + + it "returns nil" do + @io.close_write.should be_nil + end + + it "prevents further writing" do + @io.close_write + lambda { @io.write('x') }.should raise_error(IOError) + end + + it "allows further reading" do + @io.close_write + @io.read(1).should == 'e' + end + + it "raises an IOError when in read-only mode" do + io = StringIO.new("example", "r") + lambda { io.close_write }.should raise_error(IOError) + + io = StringIO.new("example") + io.close_write + ruby_version_is ''...'2.3' do + lambda { io.close_write }.should raise_error(IOError) + end + ruby_version_is '2.3' do + io.close_write.should == nil + end + end +end diff --git a/spec/rubyspec/library/stringio/closed_read_spec.rb b/spec/rubyspec/library/stringio/closed_read_spec.rb new file mode 100644 index 0000000000..971eb5f71c --- /dev/null +++ b/spec/rubyspec/library/stringio/closed_read_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#closed_read?" do + it "returns true if self is not readable" do + io = StringIO.new("example", "r+") + io.close_write + io.closed_read?.should be_false + io.close_read + io.closed_read?.should be_true + end +end diff --git a/spec/rubyspec/library/stringio/closed_spec.rb b/spec/rubyspec/library/stringio/closed_spec.rb new file mode 100644 index 0000000000..4026d78775 --- /dev/null +++ b/spec/rubyspec/library/stringio/closed_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#closed?" do + it "returns true if self is completely closed" do + io = StringIO.new("example", "r+") + io.close_read + io.closed?.should be_false + io.close_write + io.closed?.should be_true + + io = StringIO.new("example", "r+") + io.close + io.closed?.should be_true + end +end diff --git a/spec/rubyspec/library/stringio/closed_write_spec.rb b/spec/rubyspec/library/stringio/closed_write_spec.rb new file mode 100644 index 0000000000..52707aa240 --- /dev/null +++ b/spec/rubyspec/library/stringio/closed_write_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#closed_write?" do + it "returns true if self is not writable" do + io = StringIO.new("example", "r+") + io.close_read + io.closed_write?.should be_false + io.close_write + io.closed_write?.should be_true + end +end diff --git a/spec/rubyspec/library/stringio/codepoints_spec.rb b/spec/rubyspec/library/stringio/codepoints_spec.rb new file mode 100644 index 0000000000..098bd3c4c3 --- /dev/null +++ b/spec/rubyspec/library/stringio/codepoints_spec.rb @@ -0,0 +1,9 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/codepoints', __FILE__) + +# See redmine #1667 +describe "StringIO#codepoints" do + it_behaves_like(:stringio_codepoints, :codepoints) +end diff --git a/spec/rubyspec/library/stringio/each_byte_spec.rb b/spec/rubyspec/library/stringio/each_byte_spec.rb new file mode 100644 index 0000000000..8e88997bc0 --- /dev/null +++ b/spec/rubyspec/library/stringio/each_byte_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/each_byte', __FILE__) + +describe "StringIO#each_byte" do + it_behaves_like :stringio_each_byte, :each_byte +end + +describe "StringIO#each_byte when self is not readable" do + it_behaves_like :stringio_each_byte_not_readable, :each_byte +end diff --git a/spec/rubyspec/library/stringio/each_char_spec.rb b/spec/rubyspec/library/stringio/each_char_spec.rb new file mode 100644 index 0000000000..a141ae03fe --- /dev/null +++ b/spec/rubyspec/library/stringio/each_char_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/each_char', __FILE__) + +describe "StringIO#each_char" do + it_behaves_like :stringio_each_char, :each_char +end + +describe "StringIO#each_char when self is not readable" do + it_behaves_like :stringio_each_char_not_readable, :chars +end diff --git a/spec/rubyspec/library/stringio/each_codepoint_spec.rb b/spec/rubyspec/library/stringio/each_codepoint_spec.rb new file mode 100644 index 0000000000..25a0bf4c81 --- /dev/null +++ b/spec/rubyspec/library/stringio/each_codepoint_spec.rb @@ -0,0 +1,10 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/codepoints', __FILE__) + +# See redmine #1667 +describe "StringIO#each_codepoint" do + it_behaves_like(:stringio_codepoints, :codepoints) +end + diff --git a/spec/rubyspec/library/stringio/each_line_spec.rb b/spec/rubyspec/library/stringio/each_line_spec.rb new file mode 100644 index 0000000000..e4deb9b3e9 --- /dev/null +++ b/spec/rubyspec/library/stringio/each_line_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each', __FILE__) + +describe "StringIO#each_line when passed a separator" do + it_behaves_like :stringio_each_separator, :each_line +end + +describe "StringIO#each_line when passed no arguments" do + it_behaves_like :stringio_each_no_arguments, :each_line +end + +describe "StringIO#each_line when self is not readable" do + it_behaves_like :stringio_each_not_readable, :each_line +end diff --git a/spec/rubyspec/library/stringio/each_spec.rb b/spec/rubyspec/library/stringio/each_spec.rb new file mode 100644 index 0000000000..07ad070192 --- /dev/null +++ b/spec/rubyspec/library/stringio/each_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each', __FILE__) + +describe "StringIO#each when passed a separator" do + it_behaves_like :stringio_each_separator, :each +end + +describe "StringIO#each when passed no arguments" do + it_behaves_like :stringio_each_no_arguments, :each +end + +describe "StringIO#each when self is not readable" do + it_behaves_like :stringio_each_not_readable, :each +end diff --git a/spec/rubyspec/library/stringio/eof_spec.rb b/spec/rubyspec/library/stringio/eof_spec.rb new file mode 100644 index 0000000000..a5bb3dbe3d --- /dev/null +++ b/spec/rubyspec/library/stringio/eof_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/eof', __FILE__) + +describe "StringIO#eof?" do + it_behaves_like :stringio_eof, :eof? +end + +describe "StringIO#eof" do + it_behaves_like :stringio_eof, :eof +end diff --git a/spec/rubyspec/library/stringio/external_encoding_spec.rb b/spec/rubyspec/library/stringio/external_encoding_spec.rb new file mode 100644 index 0000000000..9bf2d8ee8c --- /dev/null +++ b/spec/rubyspec/library/stringio/external_encoding_spec.rb @@ -0,0 +1,21 @@ +require 'stringio' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "StringIO#external_encoding" do + it "gets the encoding of the underlying String" do + io = StringIO.new + io.set_encoding Encoding::EUC_JP + io.external_encoding.should == Encoding::EUC_JP + end + + ruby_version_is "2.3" do + it "does not set the encoding of its buffer string if the string is frozen" do + str = "foo".freeze + enc = str.encoding + io = StringIO.new(str) + io.set_encoding Encoding::EUC_JP + io.external_encoding.should == Encoding::EUC_JP + str.encoding.should == enc + end + end +end diff --git a/spec/rubyspec/library/stringio/fcntl_spec.rb b/spec/rubyspec/library/stringio/fcntl_spec.rb new file mode 100644 index 0000000000..5250359a79 --- /dev/null +++ b/spec/rubyspec/library/stringio/fcntl_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#fcntl" do + it "raises a NotImplementedError" do + lambda { StringIO.new("boom").fcntl }.should raise_error(NotImplementedError) + end +end diff --git a/spec/rubyspec/library/stringio/fileno_spec.rb b/spec/rubyspec/library/stringio/fileno_spec.rb new file mode 100644 index 0000000000..a5ae820830 --- /dev/null +++ b/spec/rubyspec/library/stringio/fileno_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/each', __FILE__) + +describe "StringIO#fileno" do + it "returns nil" do + StringIO.new("nuffin").fileno.should be_nil + end +end diff --git a/spec/rubyspec/library/stringio/fixtures/classes.rb b/spec/rubyspec/library/stringio/fixtures/classes.rb new file mode 100644 index 0000000000..bb8dc354cc --- /dev/null +++ b/spec/rubyspec/library/stringio/fixtures/classes.rb @@ -0,0 +1,15 @@ +require 'stringio' + +class StringSubclass < String; end + +module StringIOSpecs + def self.build + str = <<-EOS + each + peach + pear + plum + EOS + StringIO.new(str) + end +end diff --git a/spec/rubyspec/library/stringio/flush_spec.rb b/spec/rubyspec/library/stringio/flush_spec.rb new file mode 100644 index 0000000000..75a92db85a --- /dev/null +++ b/spec/rubyspec/library/stringio/flush_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#flush" do + it "returns self" do + io = StringIO.new("flush") + io.flush.should equal(io) + end +end diff --git a/spec/rubyspec/library/stringio/fsync_spec.rb b/spec/rubyspec/library/stringio/fsync_spec.rb new file mode 100644 index 0000000000..509f4a972f --- /dev/null +++ b/spec/rubyspec/library/stringio/fsync_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#fsync" do + it "returns zero" do + io = StringIO.new("fsync") + io.fsync.should eql(0) + end +end diff --git a/spec/rubyspec/library/stringio/getbyte_spec.rb b/spec/rubyspec/library/stringio/getbyte_spec.rb new file mode 100644 index 0000000000..163cb9d0c6 --- /dev/null +++ b/spec/rubyspec/library/stringio/getbyte_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/getc', __FILE__) + +describe "StringIO#getbyte" do + it_behaves_like :stringio_getc, :getbyte + + it "returns the 8-bit byte at the current position" do + io = StringIO.new("example") + + io.send(@method).should == 101 + io.send(@method).should == 120 + io.send(@method).should == 97 + end +end + +describe "StringIO#getbyte when self is not readable" do + it_behaves_like :stringio_getc_not_readable, :getbyte +end diff --git a/spec/rubyspec/library/stringio/getc_spec.rb b/spec/rubyspec/library/stringio/getc_spec.rb new file mode 100644 index 0000000000..f7e98d2a33 --- /dev/null +++ b/spec/rubyspec/library/stringio/getc_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/getc', __FILE__) + +describe "StringIO#getc" do + it_behaves_like :stringio_getc, :getc + + it "returns the charactor at the current position" do + io = StringIO.new("example") + + io.send(@method).should == ?e + io.send(@method).should == ?x + io.send(@method).should == ?a + end +end + +describe "StringIO#getc when self is not readable" do + it_behaves_like :stringio_getc_not_readable, :getc +end diff --git a/spec/rubyspec/library/stringio/getch_spec.rb b/spec/rubyspec/library/stringio/getch_spec.rb new file mode 100644 index 0000000000..c7fdfe9080 --- /dev/null +++ b/spec/rubyspec/library/stringio/getch_spec.rb @@ -0,0 +1,46 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/getc', __FILE__) + +# This method is added by io/console on require. +describe "StringIO#getch" do + require 'io/console' + + it_behaves_like :stringio_getc, :getch + + it "returns the charactor at the current position" do + io = StringIO.new("example") + + io.send(@method).should == ?e + io.send(@method).should == ?x + io.send(@method).should == ?a + end + + with_feature :encoding do + it "increments #pos by the byte size of the character in multibyte strings" do + io = StringIO.new("föóbar") + + io.send(@method); io.pos.should == 1 # "f" has byte size 1 + io.send(@method); io.pos.should == 3 # "ö" has byte size 2 + io.send(@method); io.pos.should == 5 # "ó" has byte size 2 + io.send(@method); io.pos.should == 6 # "b" has byte size 1 + end + end + + it "returns nil at the end of the string" do + # empty string case + io = StringIO.new("") + io.send(@method).should == nil + io.send(@method).should == nil + + # non-empty string case + io = StringIO.new("a") + io.send(@method) # skip one + io.send(@method).should == nil + end + + describe "StringIO#getch when self is not readable" do + it_behaves_like :stringio_getc_not_readable, :getch + end +end diff --git a/spec/rubyspec/library/stringio/gets_spec.rb b/spec/rubyspec/library/stringio/gets_spec.rb new file mode 100644 index 0000000000..307f564a6e --- /dev/null +++ b/spec/rubyspec/library/stringio/gets_spec.rb @@ -0,0 +1,238 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "stringio" + +describe "StringIO#gets when passed [separator]" do + before :each do + @io = StringIO.new("this>is>an>example") + end + + it "returns the data read till the next occurence of the passed separator" do + @io.gets(">").should == "this>" + @io.gets(">").should == "is>" + @io.gets(">").should == "an>" + @io.gets(">").should == "example" + end + + it "sets $_ to the read content" do + @io.gets(">") + $_.should == "this>" + @io.gets(">") + $_.should == "is>" + @io.gets(">") + $_.should == "an>" + @io.gets(">") + $_.should == "example" + @io.gets(">") + $_.should be_nil + end + + it "accepts string as separator" do + @io.gets("is>") + $_.should == "this>" + @io.gets("an>") + $_.should == "is>an>" + @io.gets("example") + $_.should == "example" + @io.gets("ple") + $_.should be_nil + end + + it "updates self's lineno by one" do + @io.gets(">") + @io.lineno.should eql(1) + + @io.gets(">") + @io.lineno.should eql(2) + + @io.gets(">") + @io.lineno.should eql(3) + end + + it "returns the next paragraph when the passed separator is an empty String" do + io = StringIO.new("this is\n\nan example") + io.gets("").should == "this is\n\n" + io.gets("").should == "an example" + end + + it "returns the remaining content starting at the current position when passed nil" do + io = StringIO.new("this is\n\nan example") + io.pos = 5 + io.gets(nil).should == "is\n\nan example" + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return(">") + @io.gets(obj).should == "this>" + end +end + +describe "StringIO#gets when passed no argument" do + before :each do + @io = StringIO.new("this is\nan example\nfor StringIO#gets") + end + + it "returns the data read till the next occurence of $/ or till eof" do + @io.gets.should == "this is\n" + + begin + old_sep, $/ = $/, " " + @io.gets.should == "an " + @io.gets.should == "example\nfor " + @io.gets.should == "StringIO#gets" + ensure + $/ = old_sep + end + end + + it "sets $_ to the read content" do + @io.gets + $_.should == "this is\n" + @io.gets + $_.should == "an example\n" + @io.gets + $_.should == "for StringIO#gets" + @io.gets + $_.should be_nil + end + + it "updates self's position" do + @io.gets + @io.pos.should eql(8) + + @io.gets + @io.pos.should eql(19) + + @io.gets + @io.pos.should eql(36) + end + + it "updates self's lineno" do + @io.gets + @io.lineno.should eql(1) + + @io.gets + @io.lineno.should eql(2) + + @io.gets + @io.lineno.should eql(3) + end + + it "returns nil if self is at the end" do + @io.pos = 36 + @io.gets.should be_nil + @io.gets.should be_nil + end +end + +describe "StringIO#gets when passed [limit]" do + before :each do + @io = StringIO.new("this>is>an>example") + end + + it "returns the data read until the limit is met" do + @io.gets(4).should == "this" + @io.gets(3).should == ">is" + @io.gets(5).should == ">an>e" + @io.gets(6).should == "xample" + end + + it "sets $_ to the read content" do + @io.gets(4) + $_.should == "this" + @io.gets(3) + $_.should == ">is" + @io.gets(5) + $_.should == ">an>e" + @io.gets(6) + $_.should == "xample" + @io.gets(3) + $_.should be_nil + end + + it "updates self's lineno by one" do + @io.gets(3) + @io.lineno.should eql(1) + + @io.gets(3) + @io.lineno.should eql(2) + + @io.gets(3) + @io.lineno.should eql(3) + end + + it "tries to convert the passed limit to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(4) + @io.gets(obj).should == "this" + end + + it "returns a blank string when passed a limit of 0" do + @io.gets(0).should == "" + end +end + +describe "StringIO#gets when passed [separator] and [limit]" do + before :each do + @io = StringIO.new("this>is>an>example") + end + + it "returns the data read until the limit is consumed or the separator is met" do + @io.gets('>', 8).should == "this>" + @io.gets('>', 2).should == "is" + @io.gets('>', 10).should == ">" + @io.gets('>', 6).should == "an>" + @io.gets('>', 5).should == "examp" + end + + it "sets $_ to the read content" do + @io.gets('>', 8) + $_.should == "this>" + @io.gets('>', 2) + $_.should == "is" + @io.gets('>', 10) + $_.should == ">" + @io.gets('>', 6) + $_.should == "an>" + @io.gets('>', 5) + $_.should == "examp" + end + + it "updates self's lineno by one" do + @io.gets('>', 3) + @io.lineno.should eql(1) + + @io.gets('>', 3) + @io.lineno.should eql(2) + + @io.gets('>', 3) + @io.lineno.should eql(3) + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return('>') + @io.gets(obj, 5).should == "this>" + end + + it "does not raise TypeError if passed separator is nil" do + @io.gets(nil, 5).should == "this>" + end + + it "tries to convert the passed limit to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(5) + @io.gets('>', obj).should == "this>" + end +end + +describe "StringIO#gets when in write-only mode" do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + lambda { io.gets }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + lambda { io.gets }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/initialize_spec.rb b/spec/rubyspec/library/stringio/initialize_spec.rb new file mode 100644 index 0000000000..8b661a3790 --- /dev/null +++ b/spec/rubyspec/library/stringio/initialize_spec.rb @@ -0,0 +1,185 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' + +describe "StringIO#initialize when passed [Object, mode]" do + before :each do + @io = StringIO.allocate + end + + it "uses the passed Object as the StringIO backend" do + @io.send(:initialize, str = "example", "r") + @io.string.should equal(str) + end + + it "sets the mode based on the passed mode" do + io = StringIO.allocate + io.send(:initialize, "example", "r") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.allocate + io.send(:initialize, "example", "rb") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.allocate + io.send(:initialize, "example", "r+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "rb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "w") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "wb") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "w+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "wb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "a") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "ab") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "a+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", "ab+") + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "allows passing the mode as an Integer" do + io = StringIO.allocate + io.send(:initialize, "example", IO::RDONLY) + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.allocate + io.send(:initialize, "example", IO::RDWR) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::WRONLY) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::WRONLY | IO::TRUNC) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::RDWR | IO::TRUNC) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::WRONLY | IO::APPEND) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.allocate + io.send(:initialize, "example", IO::RDWR | IO::APPEND) + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "raises a RuntimeError when passed a frozen String in truncate mode as StringIO backend" do + io = StringIO.allocate + lambda { io.send(:initialize, "example".freeze, IO::TRUNC) }.should raise_error(RuntimeError) + end + + it "tries to convert the passed mode to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("r") + @io.send(:initialize, "example", obj) + + @io.closed_read?.should be_false + @io.closed_write?.should be_true + end + + it "raises an Errno::EACCES error when passed a frozen string with a write-mode" do + (str = "example").freeze + lambda { @io.send(:initialize, str, "r+") }.should raise_error(Errno::EACCES) + lambda { @io.send(:initialize, str, "w") }.should raise_error(Errno::EACCES) + lambda { @io.send(:initialize, str, "a") }.should raise_error(Errno::EACCES) + end +end + +describe "StringIO#initialize when passed [Object]" do + before :each do + @io = StringIO.allocate + end + + it "uses the passed Object as the StringIO backend" do + @io.send(:initialize, str = "example") + @io.string.should equal(str) + end + + it "sets the mode to read-write" do + @io.send(:initialize, "example") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("example") + @io.send(:initialize, obj) + @io.string.should == "example" + end + + it "automatically sets the mode to read-only when passed a frozen string" do + (str = "example").freeze + @io.send(:initialize, str) + @io.closed_read?.should be_false + @io.closed_write?.should be_true + end +end + +describe "StringIO#initialize when passed no arguments" do + before :each do + @io = StringIO.allocate + end + + it "is private" do + StringIO.should have_private_instance_method(:initialize) + end + + it "sets the mode to read-write" do + @io.send(:initialize, "example") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + end + + it "uses an empty String as the StringIO backend" do + @io.send(:initialize) + @io.string.should == "" + end +end diff --git a/spec/rubyspec/library/stringio/internal_encoding_spec.rb b/spec/rubyspec/library/stringio/internal_encoding_spec.rb new file mode 100644 index 0000000000..f8ecb35989 --- /dev/null +++ b/spec/rubyspec/library/stringio/internal_encoding_spec.rb @@ -0,0 +1,10 @@ +require 'stringio' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "StringIO#internal_encoding" do + it "returns nil" do + io = StringIO.new + io.set_encoding Encoding::UTF_8 + io.internal_encoding.should == nil + end +end diff --git a/spec/rubyspec/library/stringio/isatty_spec.rb b/spec/rubyspec/library/stringio/isatty_spec.rb new file mode 100644 index 0000000000..c07dc2f6cc --- /dev/null +++ b/spec/rubyspec/library/stringio/isatty_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/isatty', __FILE__) + +describe "StringIO#isatty" do + it_behaves_like :stringio_isatty, :isatty +end diff --git a/spec/rubyspec/library/stringio/length_spec.rb b/spec/rubyspec/library/stringio/length_spec.rb new file mode 100644 index 0000000000..ae8eb15502 --- /dev/null +++ b/spec/rubyspec/library/stringio/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "StringIO#length" do + it_behaves_like :stringio_length, :length +end diff --git a/spec/rubyspec/library/stringio/lineno_spec.rb b/spec/rubyspec/library/stringio/lineno_spec.rb new file mode 100644 index 0000000000..b7ef83c7e1 --- /dev/null +++ b/spec/rubyspec/library/stringio/lineno_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "stringio" + +describe "StringIO#lineno" do + before :each do + @io = StringIO.new("this\nis\nan\nexample") + end + + it "returns the number of lines read" do + @io.gets + @io.gets + @io.gets + @io.lineno.should eql(3) + end +end + +describe "StringIO#lineno=" do + before :each do + @io = StringIO.new("this\nis\nan\nexample") + end + + it "sets the current line number, but has no impact on the position" do + @io.lineno = 3 + @io.pos.should eql(0) + + @io.gets.should == "this\n" + @io.lineno.should eql(4) + @io.pos.should eql(5) + end +end diff --git a/spec/rubyspec/library/stringio/lines_spec.rb b/spec/rubyspec/library/stringio/lines_spec.rb new file mode 100644 index 0000000000..550b25549e --- /dev/null +++ b/spec/rubyspec/library/stringio/lines_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/each', __FILE__) + +describe "StringIO#lines when passed a separator" do + it_behaves_like :stringio_each_separator, :lines +end + +describe "StringIO#lines when passed no arguments" do + it_behaves_like :stringio_each_no_arguments, :lines +end + +describe "StringIO#lines when self is not readable" do + it_behaves_like :stringio_each_not_readable, :lines +end diff --git a/spec/rubyspec/library/stringio/open_spec.rb b/spec/rubyspec/library/stringio/open_spec.rb new file mode 100644 index 0000000000..136ff5f972 --- /dev/null +++ b/spec/rubyspec/library/stringio/open_spec.rb @@ -0,0 +1,208 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' + +describe "StringIO.open when passed [Object, mode]" do + it "uses the passed Object as the StringIO backend" do + io = StringIO.open(str = "example", "r") + io.string.should equal(str) + end + + it "returns the blocks return value when yielding" do + ret = StringIO.open("example", "r") { :test } + ret.should equal(:test) + end + + it "yields self to the passed block" do + io = nil + StringIO.open("example", "r") { |strio| io = strio } + io.should be_kind_of(StringIO) + end + + it "closes self after yielding" do + io = nil + StringIO.open("example", "r") { |strio| io = strio } + io.closed?.should be_true + end + + it "even closes self when an exception is raised while yielding" do + io = nil + begin + StringIO.open("example", "r") do |strio| + io = strio + raise "Error" + end + rescue + end + io.closed?.should be_true + end + + it "sets self's string to nil after yielding" do + io = nil + StringIO.open("example", "r") { |strio| io = strio } + io.string.should be_nil + end + + it "even sets self's string to nil when an exception is raised while yielding" do + io = nil + begin + StringIO.open("example", "r") do |strio| + io = strio + raise "Error" + end + rescue + end + io.string.should be_nil + end + + it "sets the mode based on the passed mode" do + io = StringIO.open("example", "r") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.open("example", "rb") + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.open("example", "r+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "rb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "w") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "wb") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "w+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "wb+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "a") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "ab") + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", "a+") + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", "ab+") + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "allows passing the mode as an Integer" do + io = StringIO.open("example", IO::RDONLY) + io.closed_read?.should be_false + io.closed_write?.should be_true + + io = StringIO.open("example", IO::RDWR) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", IO::WRONLY) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", IO::WRONLY | IO::TRUNC) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", IO::RDWR | IO::TRUNC) + io.closed_read?.should be_false + io.closed_write?.should be_false + + io = StringIO.open("example", IO::WRONLY | IO::APPEND) + io.closed_read?.should be_true + io.closed_write?.should be_false + + io = StringIO.open("example", IO::RDWR | IO::APPEND) + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "raises a RuntimeError when passed a frozen String in truncate mode as StringIO backend" do + lambda { StringIO.open("example".freeze, IO::TRUNC) }.should raise_error(RuntimeError) + end + + it "tries to convert the passed mode to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("r") + io = StringIO.open("example", obj) + + io.closed_read?.should be_false + io.closed_write?.should be_true + end + + it "raises an Errno::EACCES error when passed a frozen string with a write-mode" do + (str = "example").freeze + lambda { StringIO.open(str, "r+") }.should raise_error(Errno::EACCES) + lambda { StringIO.open(str, "w") }.should raise_error(Errno::EACCES) + lambda { StringIO.open(str, "a") }.should raise_error(Errno::EACCES) + end +end + +describe "StringIO.open when passed [Object]" do + it "uses the passed Object as the StringIO backend" do + io = StringIO.open(str = "example") + io.string.should equal(str) + end + + it "yields self to the passed block" do + io = nil + ret = StringIO.open("example") { |strio| io = strio } + io.should equal(ret) + end + + it "sets the mode to read-write" do + io = StringIO.open("example") + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return("example") + io = StringIO.open(obj) + io.string.should == "example" + end + + it "automatically sets the mode to read-only when passed a frozen string" do + (str = "example").freeze + io = StringIO.open(str) + io.closed_read?.should be_false + io.closed_write?.should be_true + end +end + +describe "StringIO.open when passed no arguments" do + it "yields self to the passed block" do + io = nil + ret = StringIO.open { |strio| io = strio } + io.should equal(ret) + end + + it "sets the mode to read-write" do + io = StringIO.open + io.closed_read?.should be_false + io.closed_write?.should be_false + end + + it "uses an empty String as the StringIO backend" do + StringIO.open.string.should == "" + end +end + diff --git a/spec/rubyspec/library/stringio/path_spec.rb b/spec/rubyspec/library/stringio/path_spec.rb new file mode 100644 index 0000000000..6165cf97c4 --- /dev/null +++ b/spec/rubyspec/library/stringio/path_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#path" do + it "is not defined" do + lambda { StringIO.new("path").path }.should raise_error(NoMethodError) + end +end diff --git a/spec/rubyspec/library/stringio/pid_spec.rb b/spec/rubyspec/library/stringio/pid_spec.rb new file mode 100644 index 0000000000..41e60880cc --- /dev/null +++ b/spec/rubyspec/library/stringio/pid_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#pid" do + it "returns nil" do + StringIO.new("pid").pid.should be_nil + end +end diff --git a/spec/rubyspec/library/stringio/pos_spec.rb b/spec/rubyspec/library/stringio/pos_spec.rb new file mode 100644 index 0000000000..59d0a43f12 --- /dev/null +++ b/spec/rubyspec/library/stringio/pos_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/tell', __FILE__) + +describe "StringIO#pos" do + it_behaves_like :stringio_tell, :pos +end + +describe "StringIO#pos=" do + before :each do + @io = StringIOSpecs.build + end + + it "updates the current byte offset" do + @io.pos = 26 + @io.read(1).should == "r" + end + + it "raises an EINVAL if given a negative argument" do + lambda { @io.pos = -10 }.should raise_error(Errno::EINVAL) + end + + it "updates the current byte offset after reaching EOF" do + @io.read + @io.pos = 26 + @io.read(1).should == "r" + end +end diff --git a/spec/rubyspec/library/stringio/print_spec.rb b/spec/rubyspec/library/stringio/print_spec.rb new file mode 100644 index 0000000000..0f67aeb921 --- /dev/null +++ b/spec/rubyspec/library/stringio/print_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#print" do + before :each do + @io = StringIO.new('example') + end + + it "prints $_ when passed no arguments" do + $_ = nil + @io.print + @io.string.should == "example" + + $_ = "blah" + @io.print + @io.string.should == "blahple" + end + + it "prints the passed arguments to self" do + @io.print(5, 6, 7, 8) + @io.string.should == "5678ple" + end + + it "tries to convert the passed Object to a String using #to_s" do + obj = mock("to_s") + obj.should_receive(:to_s).and_return("to_s") + @io.print(obj) + @io.string.should == "to_sple" + end + + it "returns nil" do + @io.print(1, 2, 3).should be_nil + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 10 + @io.print(1, 2, 3) + @io.string.should == "example\000\000\000123" + end + + it "honors the output record separator global" do + old_rs, $\ = $\, 'x' + + begin + @io.print(5, 6, 7, 8) + @io.string.should == '5678xle' + ensure + $\ = old_rs + end + end + + it "updates the current position" do + @io.print(1, 2, 3) + @io.pos.should eql(3) + + @io.print(1, 2, 3) + @io.pos.should eql(6) + end + + it "correctly updates the current position when honoring the output record separator global" do + old_rs, $\ = $\, 'x' + + begin + @io.print(5, 6, 7, 8) + @io.pos.should eql(5) + ensure + $\ = old_rs + end + end +end + +describe "StringIO#print when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.print(", just testing") + @io.string.should == "example, just testing" + + @io.print(" and more testing") + @io.string.should == "example, just testing and more testing" + end + + it "correctly updates self's position" do + @io.print(", testing") + @io.pos.should eql(16) + end +end + +describe "StringIO#print when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + lambda { io.print("test") }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + lambda { io.print("test") }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/printf_spec.rb b/spec/rubyspec/library/stringio/printf_spec.rb new file mode 100644 index 0000000000..0309480a9a --- /dev/null +++ b/spec/rubyspec/library/stringio/printf_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#printf" do + before :each do + @io = StringIO.new('example') + end + + it "returns nil" do + @io.printf("%d %04x", 123, 123).should be_nil + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 10 + @io.printf("%d", 123) + @io.string.should == "example\000\000\000123" + end + + it "performs format conversion" do + @io.printf("%d %04x", 123, 123) + @io.string.should == "123 007b" + end + + it "updates the current position" do + @io.printf("%d %04x", 123, 123) + @io.pos.should eql(8) + + @io.printf("%d %04x", 123, 123) + @io.pos.should eql(16) + end +end + +describe "StringIO#printf when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.printf("%d %04x", 123, 123) + @io.string.should == "example123 007b" + + @io.printf("%d %04x", 123, 123) + @io.string.should == "example123 007b123 007b" + end + + it "correctly updates self's position" do + @io.printf("%d %04x", 123, 123) + @io.pos.should eql(15) + end +end + +describe "StringIO#printf when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + lambda { io.printf("test") }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + lambda { io.printf("test") }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/putc_spec.rb b/spec/rubyspec/library/stringio/putc_spec.rb new file mode 100644 index 0000000000..783e5a405b --- /dev/null +++ b/spec/rubyspec/library/stringio/putc_spec.rb @@ -0,0 +1,88 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#putc when passed [String]" do + before :each do + @io = StringIO.new('example') + end + + it "overwrites the character at the current position" do + @io.putc("t") + @io.string.should == "txample" + + @io.pos = 3 + @io.putc("t") + @io.string.should == "txatple" + end + + it "only writes the first character from the passed String" do + @io.putc("test") + @io.string.should == "txample" + end + + it "returns the passed String" do + str = "test" + @io.putc(str).should equal(str) + end + + it "correctly updates the current position" do + @io.putc("t") + @io.pos.should == 1 + + @io.putc("test") + @io.pos.should == 2 + + @io.putc("t") + @io.pos.should == 3 + end +end + +describe "StringIO#putc when passed [Object]" do + before :each do + @io = StringIO.new('example') + end + + it "it writes the passed Integer % 256 to self" do + @io.putc(333) # 333 % 256 == ?M + @io.string.should == "Mxample" + + @io.putc(-450) # -450 % 256 == ?> + @io.string.should == "M>ample" + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 10 + @io.putc(?A) + @io.string.should == "example\000\000\000A" + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(116) + @io.putc(obj) + @io.string.should == "txample" + end + + it "raises a TypeError when the passed argument can't be coerced to Integer" do + lambda { @io.putc(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#putc when in append mode" do + it "appends to the end of self" do + io = StringIO.new("test", "a") + io.putc(?t) + io.string.should == "testt" + end +end + +describe "StringIO#putc when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + lambda { io.putc(?a) }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + lambda { io.putc("t") }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/puts_spec.rb b/spec/rubyspec/library/stringio/puts_spec.rb new file mode 100644 index 0000000000..93a676aa15 --- /dev/null +++ b/spec/rubyspec/library/stringio/puts_spec.rb @@ -0,0 +1,159 @@ +# -*- encoding: utf-8 -*- + +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#puts when passed an Array" do + before :each do + @io = StringIO.new + end + + it "writes each element of the passed Array to self, separated by a newline" do + @io.puts([1, 2, 3, 4]) + @io.string.should == "1\n2\n3\n4\n" + + @io.puts([1, 2], [3, 4]) + @io.string.should == "1\n2\n3\n4\n1\n2\n3\n4\n" + end + + it "flattens nested Arrays" do + @io.puts([1, [2, [3, [4]]]]) + @io.string.should == "1\n2\n3\n4\n" + end + + it "handles self-recursive arrays correctly" do + (ary = [5]) + ary << ary + @io.puts(ary) + @io.string.should == "5\n[...]\n" + end + + it "does not honor the global output record separator $\\" do + begin + old_rs, $\ = $\, "test" + @io.puts([1, 2, 3, 4]) + @io.string.should == "1\n2\n3\n4\n" + ensure + $\ = old_rs + end + end + + it "first tries to convert each Array element to an Array using #to_ary" do + obj = mock("Object") + obj.should_receive(:to_ary).and_return(["to_ary"]) + @io.puts([obj]) + @io.string.should == "to_ary\n" + end + + it "then tries to convert each Array element to a String using #to_s" do + obj = mock("Object") + obj.should_receive(:to_s).and_return("to_s") + @io.puts([obj]) + @io.string.should == "to_s\n" + end +end + +describe "StringIO#puts when passed 1 or more objects" do + before :each do + @io = StringIO.new + end + + it "does not honor the global output record separator $\\" do + begin + old_rs, $\ = $\, "test" + @io.puts(1, 2, 3, 4) + @io.string.should == "1\n2\n3\n4\n" + ensure + $\ = old_rs + end + end + + it "does not put a \\n after each Objects that end in a newline" do + @io.puts("1\n", "2\n", "3\n") + @io.string.should == "1\n2\n3\n" + end + + it "first tries to convert each Object to an Array using #to_ary" do + obj = mock("Object") + obj.should_receive(:to_ary).and_return(["to_ary"]) + @io.puts(obj) + @io.string.should == "to_ary\n" + end + + it "then tries to convert each Object to a String using #to_s" do + obj = mock("Object") + obj.should_receive(:to_s).and_return("to_s") + @io.puts(obj) + @io.string.should == "to_s\n" + end + + it "prints a newline when passed an empty string" do + @io.puts '' + @io.string.should == "\n" + end +end + +describe "StringIO#puts when passed no arguments" do + before :each do + @io = StringIO.new + end + + it "returns nil" do + @io.puts.should be_nil + end + + it "prints a newline" do + @io.puts + @io.string.should == "\n" + end + + it "does not honor the global output record separator $\\" do + begin + old_rs, $\ = $\, "test" + @io.puts + @io.string.should == "\n" + ensure + $\ = old_rs + end + end +end + +describe "StringIO#puts when in append mode" do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.puts(", just testing") + @io.string.should == "example, just testing\n" + + @io.puts(" and more testing") + @io.string.should == "example, just testing\n and more testing\n" + end + + it "correctly updates self's position" do + @io.puts(", testing") + @io.pos.should eql(17) + end +end + +describe "StringIO#puts when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + lambda { io.puts }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + lambda { io.puts }.should raise_error(IOError) + end +end + +describe "StringIO#puts when passed an encoded string" do + it "stores the bytes unmodified" do + io = StringIO.new("") + io.puts "\x00\x01\x02" + io.puts "æåø" + + io.string.should == "\x00\x01\x02\næåø\n" + end +end diff --git a/spec/rubyspec/library/stringio/read_nonblock_spec.rb b/spec/rubyspec/library/stringio/read_nonblock_spec.rb new file mode 100644 index 0000000000..84aefe8de7 --- /dev/null +++ b/spec/rubyspec/library/stringio/read_nonblock_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "stringio" +require File.expand_path('../shared/read', __FILE__) +require File.expand_path('../shared/sysread', __FILE__) + +describe "StringIO#read_nonblock when passed length, buffer" do + it_behaves_like :stringio_read, :read_nonblock +end + +describe "StringIO#read_nonblock when passed length" do + it_behaves_like :stringio_read_length, :read_nonblock +end + +describe "StringIO#read_nonblock when passed nil" do + it_behaves_like :stringio_read_nil, :read_nonblock +end + +describe "StringIO#read_nonblock when passed length" do + it_behaves_like :stringio_sysread_length, :read_nonblock +end diff --git a/spec/rubyspec/library/stringio/read_spec.rb b/spec/rubyspec/library/stringio/read_spec.rb new file mode 100644 index 0000000000..2b9c6e10b2 --- /dev/null +++ b/spec/rubyspec/library/stringio/read_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "stringio" +require File.expand_path('../shared/read', __FILE__) + +describe "StringIO#read when passed length, buffer" do + it_behaves_like :stringio_read, :read +end + +describe "StringIO#read when passed [length]" do + it_behaves_like :stringio_read_length, :read +end + +describe "StringIO#read when passed no arguments" do + it_behaves_like :stringio_read_no_arguments, :read + + it "returns an empty string if at EOF" do + @io.read.should == "example" + @io.read.should == "" + end +end + +describe "StringIO#read when passed nil" do + it_behaves_like :stringio_read_nil, :read + + it "returns an empty string if at EOF" do + @io.read(nil).should == "example" + @io.read(nil).should == "" + end +end + +describe "StringIO#read when self is not readable" do + it_behaves_like :stringio_read_not_readable, :read +end + +describe "StringIO#read when passed [length]" do + before :each do + @io = StringIO.new("example") + end + + it "returns nil when self's position is at the end" do + @io.pos = 7 + @io.read(10).should be_nil + end + + it "returns an empty String when length is 0" do + @io.read(0).should == "" + end +end + +describe "StringIO#read when passed length and a buffer" do + before :each do + @io = StringIO.new("abcdefghijklmnopqrstuvwxyz") + end + + it "reads [length] characters into the buffer" do + buf = "foo" + result = @io.read(10, buf) + + buf.should == "abcdefghij" + result.should equal(buf) + end +end diff --git a/spec/rubyspec/library/stringio/readbyte_spec.rb b/spec/rubyspec/library/stringio/readbyte_spec.rb new file mode 100644 index 0000000000..0fbc49a4dd --- /dev/null +++ b/spec/rubyspec/library/stringio/readbyte_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/readchar', __FILE__) + +describe "StringIO#readbyte" do + it_behaves_like :stringio_readchar, :readbyte + + it "reads the next 8-bit byte from self's current position" do + io = StringIO.new("example") + + io.send(@method).should == 101 + + io.pos = 4 + io.send(@method).should == 112 + end +end + +describe "StringIO#readbyte when self is not readable" do + it_behaves_like :stringio_readchar_not_readable, :readbyte +end diff --git a/spec/rubyspec/library/stringio/readchar_spec.rb b/spec/rubyspec/library/stringio/readchar_spec.rb new file mode 100644 index 0000000000..af673c871a --- /dev/null +++ b/spec/rubyspec/library/stringio/readchar_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' +require File.expand_path('../shared/readchar', __FILE__) + +describe "StringIO#readchar" do + it_behaves_like :stringio_readchar, :readchar + + it "reads the next 8-bit byte from self's current position" do + io = StringIO.new("example") + + io.send(@method).should == ?e + + io.pos = 4 + io.send(@method).should == ?p + end +end + +describe "StringIO#readchar when self is not readable" do + it_behaves_like :stringio_readchar_not_readable, :readchar +end diff --git a/spec/rubyspec/library/stringio/readline_spec.rb b/spec/rubyspec/library/stringio/readline_spec.rb new file mode 100644 index 0000000000..90890e3ad1 --- /dev/null +++ b/spec/rubyspec/library/stringio/readline_spec.rb @@ -0,0 +1,122 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + + +describe "StringIO#readline when passed [separator]" do + before :each do + @io = StringIO.new("this>is>an>example") + end + + it "returns the data read till the next occurence of the passed separator" do + @io.readline(">").should == "this>" + @io.readline(">").should == "is>" + @io.readline(">").should == "an>" + @io.readline(">").should == "example" + end + + it "sets $_ to the read content" do + @io.readline(">") + $_.should == "this>" + @io.readline(">") + $_.should == "is>" + @io.readline(">") + $_.should == "an>" + @io.readline(">") + $_.should == "example" + end + + it "updates self's lineno by one" do + @io.readline(">") + @io.lineno.should eql(1) + + @io.readline(">") + @io.lineno.should eql(2) + + @io.readline(">") + @io.lineno.should eql(3) + end + + it "returns the next paragraph when the passed separator is an empty String" do + io = StringIO.new("this is\n\nan example") + io.readline("").should == "this is\n\n" + io.readline("").should == "an example" + end + + it "returns the remaining content starting at the current position when passed nil" do + io = StringIO.new("this is\n\nan example") + io.pos = 5 + io.readline(nil).should == "is\n\nan example" + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock('to_str') + obj.should_receive(:to_str).and_return(">") + @io.readline(obj).should == "this>" + end +end + +describe "StringIO#readline when passed no argument" do + before :each do + @io = StringIO.new("this is\nan example\nfor StringIO#readline") + end + + it "returns the data read till the next occurence of $/ or till eof" do + @io.readline.should == "this is\n" + + begin + old_sep, $/ = $/, " " + @io.readline.should == "an " + @io.readline.should == "example\nfor " + @io.readline.should == "StringIO#readline" + ensure + $/ = old_sep + end + end + + it "sets $_ to the read content" do + @io.readline + $_.should == "this is\n" + @io.readline + $_.should == "an example\n" + @io.readline + $_.should == "for StringIO#readline" + end + + it "updates self's position" do + @io.readline + @io.pos.should eql(8) + + @io.readline + @io.pos.should eql(19) + + @io.readline + @io.pos.should eql(40) + end + + it "updates self's lineno" do + @io.readline + @io.lineno.should eql(1) + + @io.readline + @io.lineno.should eql(2) + + @io.readline + @io.lineno.should eql(3) + end + + it "raises an IOError if self is at the end" do + @io.pos = 40 + lambda { @io.readline }.should raise_error(IOError) + end +end + +describe "StringIO#readline when in write-only mode" do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + lambda { io.readline }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + lambda { io.readline }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/readlines_spec.rb b/spec/rubyspec/library/stringio/readlines_spec.rb new file mode 100644 index 0000000000..215a6cbb2a --- /dev/null +++ b/spec/rubyspec/library/stringio/readlines_spec.rb @@ -0,0 +1,92 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#readlines when passed [separator]" do + before :each do + @io = StringIO.new("this>is>an>example") + end + + it "returns an Array containing lines based on the passed separator" do + @io.readlines(">").should == ["this>", "is>", "an>", "example"] + end + + it "updates self's position based on the number of read bytes" do + @io.readlines(">") + @io.pos.should eql(18) + end + + it "updates self's lineno based on the number of read lines" do + @io.readlines(">") + @io.lineno.should eql(4) + end + + it "does not change $_" do + $_ = "test" + @io.readlines(">") + $_.should == "test" + end + + it "returns an Array containing all paragraphs when the passed separator is an empty String" do + io = StringIO.new("this is\n\nan example") + io.readlines("").should == ["this is\n\n", "an example"] + end + + it "returns the remaining content as one line starting at the current position when passed nil" do + io = StringIO.new("this is\n\nan example") + io.pos = 5 + io.readlines(nil).should == ["is\n\nan example"] + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock('to_str') + obj.stub!(:to_str).and_return(">") + @io.readlines(obj).should == ["this>", "is>", "an>", "example"] + end +end + +describe "StringIO#readlines when passed no argument" do + before :each do + @io = StringIO.new("this is\nan example\nfor StringIO#readlines") + end + + it "returns an Array containing lines based on $/" do + begin + old_sep, $/ = $/, " " + @io.readlines.should == ["this ", "is\nan ", "example\nfor ", "StringIO#readlines"] + ensure + $/ = old_sep + end + end + + it "updates self's position based on the number of read bytes" do + @io.readlines + @io.pos.should eql(41) + end + + it "updates self's lineno based on the number of read lines" do + @io.readlines + @io.lineno.should eql(3) + end + + it "does not change $_" do + $_ = "test" + @io.readlines(">") + $_.should == "test" + end + + it "returns an empty Array when self is at the end" do + @io.pos = 41 + @io.readlines.should == [] + end +end + +describe "StringIO#readlines when in write-only mode" do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + lambda { io.readlines }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + lambda { io.readlines }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/readpartial_spec.rb b/spec/rubyspec/library/stringio/readpartial_spec.rb new file mode 100644 index 0000000000..e65e50fa41 --- /dev/null +++ b/spec/rubyspec/library/stringio/readpartial_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#readpartial" do + before :each do + @string = StringIO.new('Stop, look, listen') + end + + after :each do + @string.close unless @string.closed? + end + + it "raises IOError on closed stream" do + @string.close + lambda { @string.readpartial(10) }.should raise_error(IOError) + end + + it "reads at most the specified number of bytes" do + + # buffered read + @string.read(1).should == 'S' + # return only specified number, not the whole buffer + @string.readpartial(1).should == "t" + end + + it "reads after ungetc with data in the buffer" do + c = @string.getc + @string.ungetc(c) + @string.readpartial(4).should == "Stop" + @string.readpartial(3).should == ", l" + end + + it "reads after ungetc without data in the buffer" do + @string = StringIO.new + @string.write("f").should == 1 + @string.rewind + c = @string.getc + c.should == 'f' + @string.ungetc(c).should == nil + + @string.readpartial(2).should == "f" + @string.rewind + # now, also check that the ungot char is cleared and + # not returned again + @string.write("b").should == 1 + @string.rewind + @string.readpartial(2).should == "b" + end + + it "discards the existing buffer content upon successful read" do + buffer = "existing" + @string.readpartial(11, buffer) + buffer.should == "Stop, look," + end + + it "raises EOFError on EOF" do + @string.readpartial(18).should == 'Stop, look, listen' + lambda { @string.readpartial(10) }.should raise_error(EOFError) + end + + it "discards the existing buffer content upon error" do + buffer = 'hello' + @string.readpartial(100) + lambda { @string.readpartial(1, buffer) }.should raise_error(EOFError) + buffer.should be_empty + end + + it "raises IOError if the stream is closed" do + @string.close + lambda { @string.readpartial(1) }.should raise_error(IOError) + end + + it "raises ArgumentError if the negative argument is provided" do + lambda { @string.readpartial(-1) }.should raise_error(ArgumentError) + end + + it "immediately returns an empty string if the length argument is 0" do + @string.readpartial(0).should == "" + end +end diff --git a/spec/rubyspec/library/stringio/reopen_spec.rb b/spec/rubyspec/library/stringio/reopen_spec.rb new file mode 100644 index 0000000000..abdecdf9de --- /dev/null +++ b/spec/rubyspec/library/stringio/reopen_spec.rb @@ -0,0 +1,288 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#reopen when passed [Object, Integer]" do + before :each do + @io = StringIO.new("example") + end + + it "reopens self with the passed Object in the passed mode" do + @io.reopen("reopened", IO::RDONLY) + @io.closed_read?.should be_false + @io.closed_write?.should be_true + @io.string.should == "reopened" + + @io.reopen("reopened, twice", IO::WRONLY) + @io.closed_read?.should be_true + @io.closed_write?.should be_false + @io.string.should == "reopened, twice" + + @io.reopen("reopened, another time", IO::RDWR) + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "reopened, another time" + end + + # NOTE: WEIRD! + it "does not taint self when the passed Object was tainted" do + @io.reopen("reopened".taint, IO::RDONLY) + @io.tainted?.should be_false + + @io.reopen("reopened".taint, IO::WRONLY) + @io.tainted?.should be_false + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("to_str") + @io.reopen(obj, IO::RDWR) + @io.string.should == "to_str" + end + + it "raises a TypeError when the passed Object can't be converted to a String" do + lambda { @io.reopen(Object.new, IO::RDWR) }.should raise_error(TypeError) + end + + it "raises an Errno::EACCES when trying to reopen self with a frozen String in write-mode" do + lambda { @io.reopen("burn".freeze, IO::WRONLY) }.should raise_error(Errno::EACCES) + lambda { @io.reopen("burn".freeze, IO::WRONLY | IO::APPEND) }.should raise_error(Errno::EACCES) + end + + it "raises a RuntimeError when trying to reopen self with a frozen String in truncate-mode" do + lambda { @io.reopen("burn".freeze, IO::RDONLY | IO::TRUNC) }.should raise_error(RuntimeError) + end + + it "does not raise IOError when passed a frozen String in read-mode" do + @io.reopen("burn".freeze, IO::RDONLY) + @io.string.should == "burn" + end +end + +describe "StringIO#reopen when passed [Object, Object]" do + before :each do + @io = StringIO.new("example") + end + + it "reopens self with the passed Object in the passed mode" do + @io.reopen("reopened", "r") + @io.closed_read?.should be_false + @io.closed_write?.should be_true + @io.string.should == "reopened" + + @io.reopen("reopened, twice", "r+") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "reopened, twice" + + @io.reopen("reopened, another", "w+") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "" + + @io.reopen("reopened, another time", "r+") + @io.closed_read?.should be_false + @io.closed_write?.should be_false + @io.string.should == "reopened, another time" + end + + it "truncates the passed String when opened in truncate mode" do + @io.reopen(str = "reopened", "w") + str.should == "" + end + + # NOTE: WEIRD! + it "does not taint self when the passed Object was tainted" do + @io.reopen("reopened".taint, "r") + @io.tainted?.should be_false + + @io.reopen("reopened".taint, "w") + @io.tainted?.should be_false + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("to_str") + @io.reopen(obj, "r") + @io.string.should == "to_str" + end + + it "raises a TypeError when the passed Object can't be converted to a String using #to_str" do + lambda { @io.reopen(Object.new, "r") }.should raise_error(TypeError) + end + + it "resets self's position to 0" do + @io.read(5) + @io.reopen("reopened") + @io.pos.should eql(0) + end + + it "resets self's line number to 0" do + @io.gets + @io.reopen("reopened") + @io.lineno.should eql(0) + end + + it "tries to convert the passed mode Object to an Integer using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("r") + @io.reopen("reopened", obj) + @io.closed_read?.should be_false + @io.closed_write?.should be_true + @io.string.should == "reopened" + end + + it "raises an Errno::EACCES error when trying to reopen self with a frozen String in write-mode" do + lambda { @io.reopen("burn".freeze, 'w') }.should raise_error(Errno::EACCES) + lambda { @io.reopen("burn".freeze, 'w+') }.should raise_error(Errno::EACCES) + lambda { @io.reopen("burn".freeze, 'a') }.should raise_error(Errno::EACCES) + lambda { @io.reopen("burn".freeze, "r+") }.should raise_error(Errno::EACCES) + end + + it "does not raise IOError if a frozen string is passed in read mode" do + @io.reopen("burn".freeze, "r") + @io.string.should == "burn" + end +end + +describe "StringIO#reopen when passed [String]" do + before :each do + @io = StringIO.new("example") + end + + it "reopens self with the passed String in read-write mode" do + @io.close + + @io.reopen("reopened") + + @io.closed_write?.should be_false + @io.closed_read?.should be_false + + @io.string.should == "reopened" + end + + # NOTE: WEIRD! + it "does not taint self when the passed Object was tainted" do + @io.reopen("reopened".taint) + @io.tainted?.should be_false + end + + it "resets self's position to 0" do + @io.read(5) + @io.reopen("reopened") + @io.pos.should eql(0) + end + + it "resets self's line number to 0" do + @io.gets + @io.reopen("reopened") + @io.lineno.should eql(0) + end +end + +describe "StringIO#reopen when passed [Object]" do + before :each do + @io = StringIO.new("example") + end + + it "raises a TypeError when passed an Object that can't be converted to a StringIO" do + lambda { @io.reopen(Object.new) }.should raise_error(TypeError) + end + + it "does not try to convert the passed Object to a String using #to_str" do + obj = mock("not to_str") + obj.should_not_receive(:to_str) + lambda { @io.reopen(obj) }.should raise_error(TypeError) + end + + it "tries to convert the passed Object to a StringIO using #to_strio" do + obj = mock("to_strio") + obj.should_receive(:to_strio).and_return(StringIO.new("to_strio")) + @io.reopen(obj) + @io.string.should == "to_strio" + end + + # NOTE: WEIRD! + it "taints self when the passed Object was tainted" do + @io.reopen(StringIO.new("reopened").taint) + @io.tainted?.should be_true + end +end + +describe "StringIO#reopen when passed no arguments" do + before :each do + @io = StringIO.new("example\nsecond line") + end + + it "resets self's mode to read-write" do + @io.close + @io.reopen + @io.closed_read?.should be_false + @io.closed_write?.should be_false + end + + it "resets self's position to 0" do + @io.read(5) + @io.reopen + @io.pos.should eql(0) + end + + it "resets self's line number to 0" do + @io.gets + @io.reopen + @io.lineno.should eql(0) + end +end + +# NOTE: Some reopen specs disabled due to MRI bugs. See: +# http://rubyforge.org/tracker/index.php?func=detail&aid=13919&group_id=426&atid=1698 +# for details. +describe "StringIO#reopen" do + before :each do + @io = StringIO.new('hello','a') + end + + # TODO: find out if this is really a bug + it "reopens a stream when given a String argument" do + @io.reopen('goodbye').should == @io + @io.string.should == 'goodbye' + @io << 'x' + @io.string.should == 'xoodbye' + end + + it "reopens a stream in append mode when flagged as such" do + @io.reopen('goodbye', 'a').should == @io + @io.string.should == 'goodbye' + @io << 'x' + @io.string.should == 'goodbyex' + end + + it "reopens and truncate when reopened in write mode" do + @io.reopen('goodbye', 'wb').should == @io + @io.string.should == '' + @io << 'x' + @io.string.should == 'x' + end + + it "truncates the given string, not a copy" do + str = 'goodbye' + @io.reopen(str, 'w') + @io.string.should == '' + str.should == '' + end + + it "taints self if the provided StringIO argument is tainted" do + new_io = StringIO.new("tainted") + new_io.taint + @io.reopen(new_io) + @io.tainted?.should == true + end + + it "does not truncate the content even when the StringIO argument is in the truncate mode" do + orig_io = StringIO.new("Original StringIO", IO::RDWR|IO::TRUNC) + orig_io.write("BLAH") # make sure the content is not empty + + @io.reopen(orig_io) + @io.string.should == "BLAH" + end + +end diff --git a/spec/rubyspec/library/stringio/rewind_spec.rb b/spec/rubyspec/library/stringio/rewind_spec.rb new file mode 100644 index 0000000000..ef144a9f5f --- /dev/null +++ b/spec/rubyspec/library/stringio/rewind_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#rewind" do + before :each do + @io = StringIO.new("hello\nworld") + @io.pos = 3 + @io.lineno = 1 + end + + it "returns 0" do + @io.rewind.should eql(0) + end + + it "resets the position" do + @io.rewind + @io.pos.should == 0 + end + + it "resets the line number" do + @io.rewind + @io.lineno.should == 0 + end +end diff --git a/spec/rubyspec/library/stringio/seek_spec.rb b/spec/rubyspec/library/stringio/seek_spec.rb new file mode 100644 index 0000000000..ec79122039 --- /dev/null +++ b/spec/rubyspec/library/stringio/seek_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#seek" do + before :each do + @io = StringIO.new("12345678") + end + + it "seeks from the current position when whence is IO::SEEK_CUR" do + @io.pos = 1 + @io.seek(1, IO::SEEK_CUR) + @io.pos.should eql(2) + + @io.seek(-1, IO::SEEK_CUR) + @io.pos.should eql(1) + end + + it "seeks from the end of self when whence is IO::SEEK_END" do + @io.seek(3, IO::SEEK_END) + @io.pos.should eql(11) # Outside of the StringIO's content + + @io.seek(-2, IO::SEEK_END) + @io.pos.should eql(6) + end + + it "seeks to an absolute position when whence is IO::SEEK_SET" do + @io.seek(5, IO::SEEK_SET) + @io.pos.should == 5 + + @io.pos = 3 + @io.seek(5, IO::SEEK_SET) + @io.pos.should == 5 + end + + it "raises an Errno::EINVAL error on negative amounts when whence is IO::SEEK_SET" do + lambda { @io.seek(-5, IO::SEEK_SET) }.should raise_error(Errno::EINVAL) + end + + it "raises an Errno::EINVAL error on incorrect whence argument" do + lambda { @io.seek(0, 3) }.should raise_error(Errno::EINVAL) + lambda { @io.seek(0, -1) }.should raise_error(Errno::EINVAL) + lambda { @io.seek(0, 2**16) }.should raise_error(Errno::EINVAL) + lambda { @io.seek(0, -2**16) }.should raise_error(Errno::EINVAL) + end + + it "tries to convert the passed Object to a String using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(2) + @io.seek(obj) + @io.pos.should eql(2) + end + + it "raises a TypeError when the passed Object can't be converted to an Integer" do + lambda { @io.seek(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#seek when self is closed" do + before :each do + @io = StringIO.new("example") + @io.close + end + + it "raises an IOError" do + lambda { @io.seek(5) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/set_encoding_spec.rb b/spec/rubyspec/library/stringio/set_encoding_spec.rb new file mode 100644 index 0000000000..c66c70ab04 --- /dev/null +++ b/spec/rubyspec/library/stringio/set_encoding_spec.rb @@ -0,0 +1,10 @@ +require 'stringio' +require File.expand_path('../../../spec_helper', __FILE__) + +describe "StringIO#set_encoding" do + it "sets the encoding of the underlying String" do + io = StringIO.new + io.set_encoding Encoding::UTF_8 + io.string.encoding.should == Encoding::UTF_8 + end +end diff --git a/spec/rubyspec/library/stringio/shared/codepoints.rb b/spec/rubyspec/library/stringio/shared/codepoints.rb new file mode 100644 index 0000000000..c8ca03329f --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/codepoints.rb @@ -0,0 +1,45 @@ +# -*- encoding: utf-8 -*- +describe :stringio_codepoints, shared: true do + before :each do + @io = StringIO.new("∂φ/∂x = gaîté") + @enum = @io.send(@method) + end + + it "returns an Enumerator" do + @enum.should be_an_instance_of(Enumerator) + end + + it "yields each codepoint code in turn" do + @enum.to_a.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] + end + + it "yields each codepoint starting from the current position" do + @io.pos = 15 + @enum.to_a.should == [238, 116, 233] + end + + it "raises an error if reading invalid sequence" do + @io.pos = 1 # inside of a multibyte sequence + lambda { @enum.first }.should raise_error(ArgumentError) + end + + it "raises an IOError if not readable" do + @io.close_read + lambda { @enum.to_a }.should raise_error(IOError) + + io = StringIO.new("xyz", "w") + lambda { io.send(@method).to_a }.should raise_error(IOError) + end + + + it "calls the given block" do + r = [] + @io.send(@method){|c| r << c } + r.should == [8706, 966, 47, 8706, 120, 32, 61, 32, 103, 97, 238, 116, 233] + end + + it "returns self" do + @io.send(@method) {|l| l }.should equal(@io) + end + +end diff --git a/spec/rubyspec/library/stringio/shared/each.rb b/spec/rubyspec/library/stringio/shared/each.rb new file mode 100644 index 0000000000..0fde23634e --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/each.rb @@ -0,0 +1,105 @@ +describe :stringio_each_separator, shared: true do + before :each do + @io = StringIO.new("a b c d e\n1 2 3 4 5") + end + + it "uses the passed argument as the line separator" do + seen = [] + @io.send(@method, " ") {|s| seen << s} + seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] + end + + it "does not change $_" do + $_ = "test" + @io.send(@method, " ") { |s| s} + $_.should == "test" + end + + it "returns self" do + @io.send(@method) {|l| l }.should equal(@io) + end + + it "tries to convert the passed separator to a String using #to_str" do + obj = mock("to_str") + obj.stub!(:to_str).and_return(" ") + + seen = [] + @io.send(@method, obj) { |l| seen << l } + seen.should == ["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"] + end + + it "yields self's content starting from the current position when the passed separator is nil" do + seen = [] + io = StringIO.new("1 2 1 2 1 2") + io.pos = 2 + io.send(@method, nil) {|s| seen << s} + seen.should == ["2 1 2 1 2"] + end + + it "yields each paragraph when passed an empty String as separator" do + seen = [] + io = StringIO.new("para1\n\npara2\n\n\npara3") + io.send(@method, "") {|s| seen << s} + seen.should == ["para1\n\n", "para2\n\n", "para3"] + end +end + +describe :stringio_each_no_arguments, shared: true do + before :each do + @io = StringIO.new("a b c d e\n1 2 3 4 5") + end + + it "yields each line to the passed block" do + seen = [] + @io.send(@method) {|s| seen << s } + seen.should == ["a b c d e\n", "1 2 3 4 5"] + end + + it "yields each line starting from the current position" do + seen = [] + @io.pos = 4 + @io.send(@method) {|s| seen << s } + seen.should == ["c d e\n", "1 2 3 4 5"] + end + + it "does not change $_" do + $_ = "test" + @io.send(@method) { |s| s} + $_.should == "test" + end + + it "uses $/ as the default line separator" do + seen = [] + begin + old_rs, $/ = $/, " " + @io.send(@method) {|s| seen << s } + seen.should eql(["a ", "b ", "c ", "d ", "e\n1 ", "2 ", "3 ", "4 ", "5"]) + ensure + $/ = old_rs + end + end + + it "returns self" do + @io.send(@method) {|l| l }.should equal(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.send(@method) + enum.instance_of?(Enumerator).should be_true + + seen = [] + enum.each { |b| seen << b } + seen.should == ["a b c d e\n", "1 2 3 4 5"] + end +end + +describe :stringio_each_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("a b c d e", "w") + lambda { io.send(@method) { |b| b } }.should raise_error(IOError) + + io = StringIO.new("a b c d e") + io.close_read + lambda { io.send(@method) { |b| b } }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/shared/each_byte.rb b/spec/rubyspec/library/stringio/shared/each_byte.rb new file mode 100644 index 0000000000..1dc48ee437 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/each_byte.rb @@ -0,0 +1,48 @@ +describe :stringio_each_byte, shared: true do + before :each do + @io = StringIO.new("xyz") + end + + it "yields each character code in turn" do + seen = [] + @io.send(@method) { |b| seen << b } + seen.should == [120, 121, 122] + end + + it "updates the position before each yield" do + seen = [] + @io.send(@method) { |b| seen << @io.pos } + seen.should == [1, 2, 3] + end + + it "does not yield if the current position is out of bounds" do + @io.pos = 1000 + seen = nil + @io.send(@method) { |b| seen = b } + seen.should be_nil + end + + it "returns self" do + @io.send(@method) {}.should equal(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.send(@method) + enum.instance_of?(Enumerator).should be_true + + seen = [] + enum.each { |b| seen << b } + seen.should == [120, 121, 122] + end +end + +describe :stringio_each_byte_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + lambda { io.send(@method) { |b| b } }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + lambda { io.send(@method) { |b| b } }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/shared/each_char.rb b/spec/rubyspec/library/stringio/shared/each_char.rb new file mode 100644 index 0000000000..35efdcb749 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/each_char.rb @@ -0,0 +1,36 @@ +# -*- encoding: utf-8 -*- +describe :stringio_each_char, shared: true do + before :each do + @io = StringIO.new("xyz äöü") + end + + it "yields each character code in turn" do + seen = [] + @io.send(@method) { |c| seen << c } + seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] + end + + it "returns self" do + @io.send(@method) {}.should equal(@io) + end + + it "returns an Enumerator when passed no block" do + enum = @io.send(@method) + enum.instance_of?(Enumerator).should be_true + + seen = [] + enum.each { |c| seen << c } + seen.should == ["x", "y", "z", " ", "ä", "ö", "ü"] + end +end + +describe :stringio_each_char_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + lambda { io.send(@method) { |b| b } }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + lambda { io.send(@method) { |b| b } }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/shared/eof.rb b/spec/rubyspec/library/stringio/shared/eof.rb new file mode 100644 index 0000000000..e0368a2892 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/eof.rb @@ -0,0 +1,24 @@ +describe :stringio_eof, shared: true do + before :each do + @io = StringIO.new("eof") + end + + it "returns true when self's position is greater than or equal to self's size" do + @io.pos = 3 + @io.send(@method).should be_true + + @io.pos = 6 + @io.send(@method).should be_true + end + + it "returns false when self's position is less than self's size" do + @io.pos = 0 + @io.send(@method).should be_false + + @io.pos = 1 + @io.send(@method).should be_false + + @io.pos = 2 + @io.send(@method).should be_false + end +end diff --git a/spec/rubyspec/library/stringio/shared/getc.rb b/spec/rubyspec/library/stringio/shared/getc.rb new file mode 100644 index 0000000000..3e064f9c1e --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/getc.rb @@ -0,0 +1,43 @@ +describe :stringio_getc, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "increases self's position by one" do + @io.send(@method) + @io.pos.should eql(1) + + @io.send(@method) + @io.pos.should eql(2) + + @io.send(@method) + @io.pos.should eql(3) + end + + it "returns nil when called at the end of self" do + @io.pos = 7 + @io.send(@method).should be_nil + @io.send(@method).should be_nil + @io.send(@method).should be_nil + end + + it "does not increase self's position when called at the end of file" do + @io.pos = 7 + @io.send(@method) + @io.pos.should eql(7) + + @io.send(@method) + @io.pos.should eql(7) + end +end + +describe :stringio_getc_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("xyz", "w") + lambda { io.send(@method) }.should raise_error(IOError) + + io = StringIO.new("xyz") + io.close_read + lambda { io.send(@method) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/shared/isatty.rb b/spec/rubyspec/library/stringio/shared/isatty.rb new file mode 100644 index 0000000000..3da5999953 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/isatty.rb @@ -0,0 +1,5 @@ +describe :stringio_isatty, shared: true do + it "returns false" do + StringIO.new('tty').send(@method).should be_false + end +end diff --git a/spec/rubyspec/library/stringio/shared/length.rb b/spec/rubyspec/library/stringio/shared/length.rb new file mode 100644 index 0000000000..60a4eb1bdd --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/length.rb @@ -0,0 +1,5 @@ +describe :stringio_length, shared: true do + it "returns the length of the wrapped string" do + StringIO.new("example").send(@method).should == 7 + end +end diff --git a/spec/rubyspec/library/stringio/shared/read.rb b/spec/rubyspec/library/stringio/shared/read.rb new file mode 100644 index 0000000000..025829a2b1 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/read.rb @@ -0,0 +1,121 @@ +describe :stringio_read, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "returns the passed buffer String" do + # Note: Rubinius bug: + # @io.send(@method, 7, buffer = "").should equal(buffer) + ret = @io.send(@method, 7, buffer = "") + ret.should equal(buffer) + end + + it "reads length bytes and writes them to the buffer String" do + @io.send(@method, 7, buffer = "") + buffer.should == "example" + end + + it "tries to convert the passed buffer Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(buffer = "") + + @io.send(@method, 7, obj) + buffer.should == "example" + end + + it "raises a TypeError when the passed buffer Object can't be converted to a String" do + lambda { @io.send(@method, 7, Object.new) }.should raise_error(TypeError) + end + + it "raises an error when passed a frozen String as buffer" do + lambda { @io.send(@method, 7, "".freeze) }.should raise_error(RuntimeError) + end +end + +describe :stringio_read_length, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "reads length bytes from the current position and returns them" do + @io.pos = 3 + @io.send(@method, 4).should == "mple" + end + + it "reads at most the whole content" do + @io.send(@method, 999).should == "example" + end + + it "correctly updates the position" do + @io.send(@method, 3) + @io.pos.should eql(3) + + @io.send(@method, 999) + @io.pos.should eql(7) + end + + it "tries to convert the passed length to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(7) + @io.send(@method, obj).should == "example" + end + + it "raises a TypeError when the passed length can't be converted to an Integer" do + lambda { @io.send(@method, Object.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when the passed length is negative" do + lambda { @io.send(@method, -2) }.should raise_error(ArgumentError) + end + + it "returns a binary String" do + @io.send(@method, 4).encoding.should == Encoding::ASCII_8BIT + end +end + +describe :stringio_read_no_arguments, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "reads the whole content starting from the current position" do + @io.send(@method).should == "example" + + @io.pos = 3 + @io.send(@method).should == "mple" + end + + it "correctly updates the current position" do + @io.send(@method) + @io.pos.should eql(7) + end +end + +describe :stringio_read_nil, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "returns the remaining content from the current position" do + @io.send(@method, nil).should == "example" + + @io.pos = 4 + @io.send(@method, nil).should == "ple" + end + + it "updates the current position" do + @io.send(@method, nil) + @io.pos.should eql(7) + end +end + +describe :stringio_read_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("test", "w") + lambda { io.send(@method) }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_read + lambda { io.send(@method) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/shared/readchar.rb b/spec/rubyspec/library/stringio/shared/readchar.rb new file mode 100644 index 0000000000..19194f0680 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/readchar.rb @@ -0,0 +1,29 @@ +describe :stringio_readchar, shared: true do + before :each do + @io = StringIO.new("example") + end + + it "correctly updates the current position" do + @io.send(@method) + @io.pos.should == 1 + + @io.send(@method) + @io.pos.should == 2 + end + + it "raises an EOFError when self is at the end" do + @io.pos = 7 + lambda { @io.send(@method) }.should raise_error(EOFError) + end +end + +describe :stringio_readchar_not_readable, shared: true do + it "raises an IOError" do + io = StringIO.new("a b c d e", "w") + lambda { io.send(@method) }.should raise_error(IOError) + + io = StringIO.new("a b c d e") + io.close_read + lambda { io.send(@method) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/shared/sysread.rb b/spec/rubyspec/library/stringio/shared/sysread.rb new file mode 100644 index 0000000000..9800b2339b --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/sysread.rb @@ -0,0 +1,15 @@ +describe :stringio_sysread_length, :shared => true do + before :each do + @io = StringIO.new("example") + end + + it "returns an empty String when passed 0 and no data remains" do + @io.send(@method, 8).should == "example" + @io.send(@method, 0).should == "" + end + + it "raises an EOFError when passed length > 0 and no data remains" do + @io.read.should == "example" + lambda { @io.sysread(1) }.should raise_error(EOFError) + end +end diff --git a/spec/rubyspec/library/stringio/shared/tell.rb b/spec/rubyspec/library/stringio/shared/tell.rb new file mode 100644 index 0000000000..852c51c192 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/tell.rb @@ -0,0 +1,12 @@ +describe :stringio_tell, shared: true do + before :each do + @io = StringIOSpecs.build + end + + it "returns the current byte offset" do + @io.getc + @io.send(@method).should == 1 + @io.read(7) + @io.send(@method).should == 8 + end +end diff --git a/spec/rubyspec/library/stringio/shared/write.rb b/spec/rubyspec/library/stringio/shared/write.rb new file mode 100644 index 0000000000..bcb548bbd0 --- /dev/null +++ b/spec/rubyspec/library/stringio/shared/write.rb @@ -0,0 +1,87 @@ +describe :stringio_write, shared: true do + before :each do + @io = StringIO.new('12345') + end + + it "tries to convert the passed Object to a String using #to_s" do + obj = mock("to_s") + obj.should_receive(:to_s).and_return("to_s") + @io.send(@method, obj) + @io.string.should == "to_s5" + end +end + +describe :stringio_write_string, shared: true do + before :each do + @io = StringIO.new('12345') + end + + # TODO: RDoc says that #write appends at the current position. + it "writes the passed String at the current buffer position" do + @io.pos = 2 + @io.send(@method, 'x').should == 1 + @io.string.should == '12x45' + @io.send(@method, 7).should == 1 + @io.string.should == '12x75' + end + + it "pads self with \\000 when the current position is after the end" do + @io.pos = 8 + @io.send(@method, 'x') + @io.string.should == "12345\000\000\000x" + @io.send(@method, 9) + @io.string.should == "12345\000\000\000x9" + end + + it "returns the number of bytes written" do + @io.send(@method, '').should == 0 + @io.send(@method, nil).should == 0 + str = "1" * 100 + @io.send(@method, str).should == 100 + end + + it "updates self's position" do + @io.send(@method, 'test') + @io.pos.should eql(4) + end + + it "taints self's String when the passed argument is tainted" do + @io.send(@method, "test".taint) + @io.string.tainted?.should be_true + end + + it "does not taint self when the passed argument is tainted" do + @io.send(@method, "test".taint) + @io.tainted?.should be_false + end +end + +describe :stringio_write_not_writable, shared: true do + it "raises an IOError" do + io = StringIO.new("test", "r") + lambda { io.send(@method, "test") }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + lambda { io.send(@method, "test") }.should raise_error(IOError) + end +end + +describe :stringio_write_append, shared: true do + before :each do + @io = StringIO.new("example", "a") + end + + it "appends the passed argument to the end of self" do + @io.send(@method, ", just testing") + @io.string.should == "example, just testing" + + @io.send(@method, " and more testing") + @io.string.should == "example, just testing and more testing" + end + + it "correctly updates self's position" do + @io.send(@method, ", testing") + @io.pos.should eql(16) + end +end diff --git a/spec/rubyspec/library/stringio/size_spec.rb b/spec/rubyspec/library/stringio/size_spec.rb new file mode 100644 index 0000000000..1d6a7fc15e --- /dev/null +++ b/spec/rubyspec/library/stringio/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/length', __FILE__) + +describe "StringIO#size" do + it_behaves_like :stringio_length, :size +end diff --git a/spec/rubyspec/library/stringio/string_spec.rb b/spec/rubyspec/library/stringio/string_spec.rb new file mode 100644 index 0000000000..7c4181b6de --- /dev/null +++ b/spec/rubyspec/library/stringio/string_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#string" do + it "returns the underlying string" do + io = StringIO.new(str = "hello") + io.string.should equal(str) + end +end + +describe "StringIO#string=" do + before :each do + @io = StringIO.new("example\nstring") + end + + it "returns the passed String" do + str = "test" + (@io.string = str).should equal(str) + end + + it "changes the underlying string" do + str = "hello" + @io.string = str + @io.string.should equal(str) + end + + it "resets the position" do + @io.pos = 1 + @io.string = "other" + @io.pos.should eql(0) + end + + it "resets the line number" do + @io.lineno = 1 + @io.string = "other" + @io.lineno.should eql(0) + end + + it "tries to convert the passed Object to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("to_str") + + @io.string = obj + @io.string.should == "to_str" + end + + it "raises a TypeError when the passed Object can't be converted to an Integer" do + lambda { @io.seek(Object.new) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/stringio/stringio_spec.rb b/spec/rubyspec/library/stringio/stringio_spec.rb new file mode 100644 index 0000000000..9e2cb9cf90 --- /dev/null +++ b/spec/rubyspec/library/stringio/stringio_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "stringio" + +describe "StringIO" do + it "includes the Enumerable module" do + StringIO.should include(Enumerable) + end +end + diff --git a/spec/rubyspec/library/stringio/sync_spec.rb b/spec/rubyspec/library/stringio/sync_spec.rb new file mode 100644 index 0000000000..b662d7b7cc --- /dev/null +++ b/spec/rubyspec/library/stringio/sync_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#sync" do + it "returns true" do + StringIO.new('').sync.should be_true + end +end + +describe "StringIO#sync=" do + before :each do + @io = StringIO.new('') + end + + it "does not change 'sync' status" do + @io.sync = false + @io.sync.should be_true + end +end diff --git a/spec/rubyspec/library/stringio/sysread_spec.rb b/spec/rubyspec/library/stringio/sysread_spec.rb new file mode 100644 index 0000000000..cce8d8c88a --- /dev/null +++ b/spec/rubyspec/library/stringio/sysread_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "stringio" +require File.expand_path('../shared/read', __FILE__) + +describe "StringIO#sysread when passed length, buffer" do + it_behaves_like :stringio_read, :sysread +end + +describe "StringIO#sysread when passed [length]" do + it_behaves_like :stringio_read_length, :sysread +end + +describe "StringIO#sysread when passed no arguments" do + it_behaves_like :stringio_read_no_arguments, :sysread + + it "returns an empty String if at EOF" do + @io.sysread.should == "example" + @io.sysread.should == "" + end +end + +describe "StringIO#sysread when self is not readable" do + it_behaves_like :stringio_read_not_readable, :sysread +end + +describe "StringIO#sysread when passed nil" do + it_behaves_like :stringio_read_nil, :sysread + + it "returns an empty String if at EOF" do + @io.sysread(nil).should == "example" + @io.sysread(nil).should == "" + end +end + +describe "StringIO#sysread when passed [length]" do + before :each do + @io = StringIO.new("example") + end + + it "raises an EOFError when self's position is at the end" do + @io.pos = 7 + lambda { @io.sysread(10) }.should raise_error(EOFError) + end + + it "returns an empty String when length is 0" do + @io.sysread(0).should == "" + end +end diff --git a/spec/rubyspec/library/stringio/syswrite_spec.rb b/spec/rubyspec/library/stringio/syswrite_spec.rb new file mode 100644 index 0000000000..8b65e81a05 --- /dev/null +++ b/spec/rubyspec/library/stringio/syswrite_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/write', __FILE__) + +describe "StringIO#syswrite when passed [Object]" do + it_behaves_like :stringio_write, :syswrite +end + +describe "StringIO#syswrite when passed [String]" do + it_behaves_like :stringio_write_string, :syswrite +end + +describe "StringIO#syswrite when self is not writable" do + it_behaves_like :stringio_write_not_writable, :syswrite +end + +describe "StringIO#syswrite when in append mode" do + it_behaves_like :stringio_write_append, :syswrite +end diff --git a/spec/rubyspec/library/stringio/tell_spec.rb b/spec/rubyspec/library/stringio/tell_spec.rb new file mode 100644 index 0000000000..af6a01497e --- /dev/null +++ b/spec/rubyspec/library/stringio/tell_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/tell', __FILE__) + +describe "StringIO#tell" do + it_behaves_like :stringio_tell, :tell +end diff --git a/spec/rubyspec/library/stringio/truncate_spec.rb b/spec/rubyspec/library/stringio/truncate_spec.rb new file mode 100644 index 0000000000..1023b3d13c --- /dev/null +++ b/spec/rubyspec/library/stringio/truncate_spec.rb @@ -0,0 +1,70 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "stringio" + +describe "StringIO#truncate when passed [length]" do + before :each do + @io = StringIO.new('123456789') + end + + # TODO: Report to Ruby-Core: The RDoc says it always returns 0 + it "returns the passed length" do + @io.truncate(4).should eql(4) + @io.truncate(10).should eql(10) + end + + it "truncated the underlying string down to the passed length" do + @io.truncate(4) + @io.string.should == "1234" + end + + it "does not create a copy of the underlying string" do + io = StringIO.new(str = "123456789") + io.truncate(4) + io.string.should equal(str) + end + + it "does not change the position" do + @io.pos = 7 + @io.truncate(4) + @io.pos.should eql(7) + end + + it "can grow a string to a larger size, padding it with \\000" do + @io.truncate(12) + @io.string.should == "123456789\000\000\000" + end + + it "raises an Errno::EINVAL when the passed length is negative" do + lambda { @io.truncate(-1) }.should raise_error(Errno::EINVAL) + lambda { @io.truncate(-10) }.should raise_error(Errno::EINVAL) + end + + it "tries to convert the passed length to an Integer using #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(4) + + @io.truncate(obj) + @io.string.should == "1234" + end + + it "returns the passed length Object, NOT the result of #to_int" do + obj = mock("to_int") + obj.should_receive(:to_int).and_return(4) + @io.truncate(obj).should equal(obj) + end + + it "raises a TypeError when the passed length can't be converted to an Integer" do + lambda { @io.truncate(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#truncate when self is not writable" do + it "raises an IOError" do + io = StringIO.new("test", "r") + lambda { io.truncate(2) }.should raise_error(IOError) + + io = StringIO.new("test") + io.close_write + lambda { io.truncate(2) }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/stringio/tty_spec.rb b/spec/rubyspec/library/stringio/tty_spec.rb new file mode 100644 index 0000000000..7540e68a2b --- /dev/null +++ b/spec/rubyspec/library/stringio/tty_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/isatty', __FILE__) + +describe "StringIO#tty?" do + it_behaves_like :stringio_isatty, :tty? +end diff --git a/spec/rubyspec/library/stringio/ungetbyte_spec.rb b/spec/rubyspec/library/stringio/ungetbyte_spec.rb new file mode 100644 index 0000000000..a7d9b28024 --- /dev/null +++ b/spec/rubyspec/library/stringio/ungetbyte_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'stringio' + +describe "StringIO#ungetbyte" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/stringio/ungetc_spec.rb b/spec/rubyspec/library/stringio/ungetc_spec.rb new file mode 100644 index 0000000000..613d49cfbd --- /dev/null +++ b/spec/rubyspec/library/stringio/ungetc_spec.rb @@ -0,0 +1,72 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "StringIO#ungetc when passed [char]" do + before :each do + @io = StringIO.new('1234') + end + + it "writes the passed char before the current position" do + @io.pos = 1 + @io.ungetc(?A) + @io.string.should == 'A234' + end + + it "returns nil" do + @io.pos = 1 + @io.ungetc(?A).should be_nil + end + + it "decreases the current position by one" do + @io.pos = 2 + @io.ungetc(?A) + @io.pos.should eql(1) + end + + it "pads with \\000 when the current position is after the end" do + @io.pos = 15 + @io.ungetc(?A) + @io.string.should == "1234\000\000\000\000\000\000\000\000\000\000A" + end + + it "tries to convert the passed argument to an String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(?A) + + @io.pos = 1 + @io.ungetc(obj) + @io.string.should == "A234" + end + + it "raises a TypeError when the passed length can't be converted to an Integer or String" do + lambda { @io.ungetc(Object.new) }.should raise_error(TypeError) + end +end + +describe "StringIO#ungetc when self is not readable" do + it "raises an IOError" do + io = StringIO.new("test", "w") + io.pos = 1 + lambda { io.ungetc(?A) }.should raise_error(IOError) + + io = StringIO.new("test") + io.pos = 1 + io.close_read + lambda { io.ungetc(?A) }.should raise_error(IOError) + end +end + +# Note: This is incorrect. +# +# describe "StringIO#ungetc when self is not writable" do +# it "raises an IOError" do +# io = StringIO.new("test", "r") +# io.pos = 1 +# lambda { io.ungetc(?A) }.should raise_error(IOError) +# +# io = StringIO.new("test") +# io.pos = 1 +# io.close_write +# lambda { io.ungetc(?A) }.should raise_error(IOError) +# end +# end diff --git a/spec/rubyspec/library/stringio/write_nonblock_spec.rb b/spec/rubyspec/library/stringio/write_nonblock_spec.rb new file mode 100644 index 0000000000..d4d5ab5a85 --- /dev/null +++ b/spec/rubyspec/library/stringio/write_nonblock_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/write', __FILE__) + +describe "StringIO#write_nonblock when passed [Object]" do + it_behaves_like :stringio_write, :write_nonblock +end + +describe "StringIO#write_nonblock when passed [String]" do + it_behaves_like :stringio_write_string, :write_nonblock +end + +describe "StringIO#write_nonblock when self is not writable" do + it_behaves_like :stringio_write_not_writable, :write_nonblock +end + +describe "StringIO#write_nonblock when in append mode" do + it_behaves_like :stringio_write_append, :write_nonblock +end diff --git a/spec/rubyspec/library/stringio/write_spec.rb b/spec/rubyspec/library/stringio/write_spec.rb new file mode 100644 index 0000000000..706234da7e --- /dev/null +++ b/spec/rubyspec/library/stringio/write_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/write', __FILE__) + +describe "StringIO#write when passed [Object]" do + it_behaves_like :stringio_write, :write +end + +describe "StringIO#write when passed [String]" do + it_behaves_like :stringio_write_string, :write +end + +describe "StringIO#write when self is not writable" do + it_behaves_like :stringio_write_not_writable, :write +end + +describe "StringIO#write when in append mode" do + it_behaves_like :stringio_write_append, :write +end diff --git a/spec/rubyspec/library/stringscanner/append_spec.rb b/spec/rubyspec/library/stringscanner/append_spec.rb new file mode 100644 index 0000000000..f75b3cc715 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/append_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/concat.rb', __FILE__) +require 'strscan' + +describe "StringScanner#<<" do + it_behaves_like :strscan_concat, :<< +end + +describe "StringScanner#<< when passed a Fixnum" do + it_behaves_like :strscan_concat_fixnum, :<< +end diff --git a/spec/rubyspec/library/stringscanner/beginning_of_line_spec.rb b/spec/rubyspec/library/stringscanner/beginning_of_line_spec.rb new file mode 100644 index 0000000000..192a83f2c9 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/beginning_of_line_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/bol.rb', __FILE__) +require 'strscan' + +describe "StringScanner#beginning_of_line?" do + it_behaves_like(:strscan_bol, :beginning_of_line?) +end diff --git a/spec/rubyspec/library/stringscanner/bol_spec.rb b/spec/rubyspec/library/stringscanner/bol_spec.rb new file mode 100644 index 0000000000..ee5257529a --- /dev/null +++ b/spec/rubyspec/library/stringscanner/bol_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/bol.rb', __FILE__) +require 'strscan' + +describe "StringScanner#bol?" do + it_behaves_like(:strscan_bol, :bol?) +end diff --git a/spec/rubyspec/library/stringscanner/check_spec.rb b/spec/rubyspec/library/stringscanner/check_spec.rb new file mode 100644 index 0000000000..db98197f81 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/check_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#check" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the value that scan would return, without advancing the scan pointer" do + @s.check(/This/).should == "This" + @s.matched.should == "This" + @s.pos.should == 0 + @s.check(/is/).should == nil + @s.matched.should == nil + end +end diff --git a/spec/rubyspec/library/stringscanner/check_until_spec.rb b/spec/rubyspec/library/stringscanner/check_until_spec.rb new file mode 100644 index 0000000000..e397b2e4c1 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/check_until_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#check_until" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the same value of scan_until, but don't advances the scan pointer" do + @s.check_until(/a/).should == "This is a" + @s.pos.should == 0 + @s.matched.should == "a" + @s.check_until(/test/).should == "This is a test" + end +end diff --git a/spec/rubyspec/library/stringscanner/clear_spec.rb b/spec/rubyspec/library/stringscanner/clear_spec.rb new file mode 100644 index 0000000000..81b2e68897 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/clear_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/terminate.rb', __FILE__) +require 'strscan' + +describe "StringScanner#clear" do + it_behaves_like(:strscan_terminate, :clear) + + it "warns in verbose mode that the method is obsolete" do + s = StringScanner.new("abc") + lambda { + $VERBOSE = true + s.clear + }.should complain(/clear.*obsolete.*terminate/) + + lambda { + $VERBOSE = false + s.clear + }.should_not complain + end +end diff --git a/spec/rubyspec/library/stringscanner/concat_spec.rb b/spec/rubyspec/library/stringscanner/concat_spec.rb new file mode 100644 index 0000000000..dccc7d0efd --- /dev/null +++ b/spec/rubyspec/library/stringscanner/concat_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/concat.rb', __FILE__) +require 'strscan' + +describe "StringScanner#concat" do + it_behaves_like(:strscan_concat, :concat) +end + +describe "StringScanner#concat when passed a Fixnum" do + it_behaves_like(:strscan_concat_fixnum, :concat) +end diff --git a/spec/rubyspec/library/stringscanner/dup_spec.rb b/spec/rubyspec/library/stringscanner/dup_spec.rb new file mode 100644 index 0000000000..2f0feff071 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/dup_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#dup" do + before :each do + @string = "this is a test" + @orig_s = StringScanner.new(@string) + end + + it "copies the passed StringScanner's content to self" do + s = @orig_s.dup + s.string.should == @string + end + + it "copies the passed StringSCanner's position to self" do + @orig_s.pos = 5 + s = @orig_s.dup + s.pos.should eql(5) + end + + it "copies previous match state" do + @orig_s.scan(/\w+/) + @orig_s.scan(/\s/) + + @orig_s.pre_match.should == "this" + + s = @orig_s.dup + s.pre_match.should == "this" + + s.unscan + s.scan(/\s/).should == " " + end + + it "copies the passed StringScanner scan pointer to self" do + @orig_s.terminate + s = @orig_s.dup + s.eos?.should be_true + end +end diff --git a/spec/rubyspec/library/stringscanner/element_reference_spec.rb b/spec/rubyspec/library/stringscanner/element_reference_spec.rb new file mode 100644 index 0000000000..c9acb1c7e7 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/element_reference_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#[]" do + before :each do + @s = StringScanner.new("Fri Jun 13 2008 22:43") + end + + it "returns nil if there is no current match" do + @s[0].should be_nil + end + + it "returns the n-th subgroup in the most recent match" do + @s.scan(/(\w+) (\w+) (\d+) /) + @s[0].should == "Fri Jun 13 " + @s[1].should == "Fri" + @s[2].should == "Jun" + @s[3].should == "13" + @s[-3].should == "Fri" + @s[-2].should == "Jun" + @s[-1].should == "13" + end + + it "returns nil if index is outside of self" do + @s.scan(/(\w+) (\w+) (\d+) /) + @s[5].should == nil + @s[-5].should == nil + end + + it "calls to_int on the given index" do + @s.scan(/(\w+) (\w+) (\d+) /) + @s[0.5].should == "Fri Jun 13 " + end + + it "raises a TypeError if the given index is nil" do + @s.scan(/(\w+) (\w+) (\d+) /) + lambda { @s[nil]}.should raise_error(TypeError) + end + + it "raises a TypeError when a Range is as argument" do + @s.scan(/(\w+) (\w+) (\d+) /) + lambda { @s[0..2]}.should raise_error(TypeError) + end + + it "raises a IndexError when there's no named capture" do + @s.scan(/(\w+) (\w+) (\d+) /) + lambda { @s["wday"]}.should raise_error(IndexError) + lambda { @s[:wday]}.should raise_error(IndexError) + end + + it "returns named capture" do + @s.scan(/(?\w+) (?\w+) (?\d+) /) + @s["wday"].should == "Fri" + @s["month"].should == "Jun" + @s["day"].should == "13" + @s[:wday].should == "Fri" + @s[:month].should == "Jun" + @s[:day].should == "13" + end +end + diff --git a/spec/rubyspec/library/stringscanner/empty_spec.rb b/spec/rubyspec/library/stringscanner/empty_spec.rb new file mode 100644 index 0000000000..ebbc2c2703 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/empty_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eos.rb', __FILE__) +require 'strscan' + +describe "StringScanner#empty?" do + it_behaves_like(:strscan_eos, :empty?) + + it "warns in verbose mode that the method is obsolete" do + s = StringScanner.new("abc") + lambda { + $VERBOSE = true + s.empty? + }.should complain(/empty?.*obsolete.*eos?/) + + lambda { + $VERBOSE = false + s.empty? + }.should_not complain + end +end diff --git a/spec/rubyspec/library/stringscanner/eos_spec.rb b/spec/rubyspec/library/stringscanner/eos_spec.rb new file mode 100644 index 0000000000..3fdeceb25d --- /dev/null +++ b/spec/rubyspec/library/stringscanner/eos_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/eos.rb', __FILE__) +require 'strscan' + +describe "StringScanner#eos?" do + it_behaves_like(:strscan_eos, :eos?) +end diff --git a/spec/rubyspec/library/stringscanner/exist_spec.rb b/spec/rubyspec/library/stringscanner/exist_spec.rb new file mode 100644 index 0000000000..ff13de89df --- /dev/null +++ b/spec/rubyspec/library/stringscanner/exist_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#exist?" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the index of the first occurrence of the given pattern" do + @s.exist?(/s/).should == 4 + @s.scan(/This is/) + @s.exist?(/s/).should == 6 + end + + it "returns 0 if the pattern is empty" do + @s.exist?(//).should == 0 + end + + it "returns nil if the pattern isn't found in the string" do + @s.exist?(/S/).should == nil + @s.scan(/This is/) + @s.exist?(/i/).should == nil + end +end diff --git a/spec/rubyspec/library/stringscanner/get_byte_spec.rb b/spec/rubyspec/library/stringscanner/get_byte_spec.rb new file mode 100644 index 0000000000..786b068042 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/get_byte_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/get_byte', __FILE__) +require 'strscan' + +describe "StringScanner#get_byte" do + it_behaves_like :strscan_get_byte, :get_byte +end diff --git a/spec/rubyspec/library/stringscanner/getbyte_spec.rb b/spec/rubyspec/library/stringscanner/getbyte_spec.rb new file mode 100644 index 0000000000..6d3b8c357d --- /dev/null +++ b/spec/rubyspec/library/stringscanner/getbyte_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/get_byte', __FILE__) +require File.expand_path('../shared/extract_range', __FILE__) +require 'strscan' + +describe "StringScanner#getbyte" do + it_behaves_like :strscan_get_byte, :getbyte + + it "warns in verbose mode that the method is obsolete" do + s = StringScanner.new("abc") + lambda { + $VERBOSE = true + s.getbyte + }.should complain(/getbyte.*obsolete.*get_byte/) + + lambda { + $VERBOSE = false + s.getbyte + }.should_not complain + end + + it_behaves_like :extract_range, :getbyte +end diff --git a/spec/rubyspec/library/stringscanner/getch_spec.rb b/spec/rubyspec/library/stringscanner/getch_spec.rb new file mode 100644 index 0000000000..ae2907d817 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/getch_spec.rb @@ -0,0 +1,35 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/extract_range', __FILE__) +require 'strscan' + +describe "StringScanner#getch" do + it "scans one character and returns it" do + s = StringScanner.new('abc') + s.getch.should == "a" + s.getch.should == "b" + s.getch.should == "c" + end + + it "is multi-byte character sensitive" do + # Japanese hiragana "A" in EUC-JP + src = "\244\242".force_encoding("euc-jp") + + s = StringScanner.new(src) + s.getch.should == src + end + + it "returns nil at the end of the string" do + # empty string case + s = StringScanner.new('') + s.getch.should == nil + s.getch.should == nil + + # non-empty string case + s = StringScanner.new('a') + s.getch # skip one + s.getch.should == nil + end + + it_behaves_like :extract_range, :getch +end diff --git a/spec/rubyspec/library/stringscanner/initialize_spec.rb b/spec/rubyspec/library/stringscanner/initialize_spec.rb new file mode 100644 index 0000000000..ab0ea23408 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/initialize_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#initialize" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "is a private method" do + StringScanner.should have_private_instance_method(:initialize) + end + + it "returns an instance of StringScanner" do + @s.should be_kind_of(StringScanner) + @s.tainted?.should be_false + @s.eos?.should be_false + end + + it "converts the argument into a string using #to_str" do + m = mock(:str) + + s = "test" + m.should_receive(:to_str).and_return(s) + + scan = StringScanner.new(m) + scan.string.should == s + end +end diff --git a/spec/rubyspec/library/stringscanner/inspect_spec.rb b/spec/rubyspec/library/stringscanner/inspect_spec.rb new file mode 100644 index 0000000000..2f22a29a2e --- /dev/null +++ b/spec/rubyspec/library/stringscanner/inspect_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#inspect" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns a String object" do + @s.inspect.should be_kind_of(String) + end + + it "returns a string that represents the StringScanner object" do + @s.inspect.should == "#" + @s.scan_until(/is/) + @s.inspect.should == "#" + @s.terminate + @s.inspect.should == "#" + end +end diff --git a/spec/rubyspec/library/stringscanner/match_spec.rb b/spec/rubyspec/library/stringscanner/match_spec.rb new file mode 100644 index 0000000000..227be6c3a5 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/match_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#match?" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the length of the match and the scan pointer is not advanced" do + @s.match?(/\w+/).should == 4 + @s.match?(/\w+/).should == 4 + @s.pos.should == 0 + end + + it "returns nil if there's no match" do + @s.match?(/\d+/).should == nil + @s.match?(/\s+/).should == nil + end + + it "effects pre_match" do + @s.scan(/\w+/) + @s.scan(/\s/) + + @s.pre_match.should == "This" + @s.match?(/\w+/) + @s.pre_match.should == "This " + end +end diff --git a/spec/rubyspec/library/stringscanner/matched_size_spec.rb b/spec/rubyspec/library/stringscanner/matched_size_spec.rb new file mode 100644 index 0000000000..7aac71878a --- /dev/null +++ b/spec/rubyspec/library/stringscanner/matched_size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/matched_size.rb', __FILE__) +require 'strscan' + +describe "StringScanner#matched_size" do + it_behaves_like(:strscan_matched_size, :matched_size) +end diff --git a/spec/rubyspec/library/stringscanner/matched_spec.rb b/spec/rubyspec/library/stringscanner/matched_spec.rb new file mode 100644 index 0000000000..1d781bd3ef --- /dev/null +++ b/spec/rubyspec/library/stringscanner/matched_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/extract_range_matched', __FILE__) +require 'strscan' + +describe "StringScanner#matched" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the last matched string" do + @s.match?(/\w+/) + @s.matched.should == "This" + @s.getch + @s.matched.should == "T" + @s.get_byte + @s.matched.should == "h" + end + + it "returns nil if there's no match" do + @s.match?(/\d+/) + @s.matched.should == nil + end + + it_behaves_like :extract_range_matched, :matched +end + +describe "StringScanner#matched?" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns true if the last match was successful" do + @s.match?(/\w+/) + @s.matched?.should be_true + end + + it "returns false if there's no match" do + @s.match?(/\d+/) + @s.matched?.should be_false + end +end diff --git a/spec/rubyspec/library/stringscanner/must_C_version_spec.rb b/spec/rubyspec/library/stringscanner/must_C_version_spec.rb new file mode 100644 index 0000000000..c7d24ed2f8 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/must_C_version_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner.must_C_version" do + it "returns self" do + StringScanner.must_C_version.should == StringScanner + end +end diff --git a/spec/rubyspec/library/stringscanner/peek_spec.rb b/spec/rubyspec/library/stringscanner/peek_spec.rb new file mode 100644 index 0000000000..49490ec3da --- /dev/null +++ b/spec/rubyspec/library/stringscanner/peek_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/peek.rb', __FILE__) +require 'strscan' + +describe "StringScanner#peek" do + it_behaves_like(:strscan_peek, :peek) +end + diff --git a/spec/rubyspec/library/stringscanner/peep_spec.rb b/spec/rubyspec/library/stringscanner/peep_spec.rb new file mode 100644 index 0000000000..677c6f8a21 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/peep_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/peek.rb', __FILE__) +require 'strscan' + +describe "StringScanner#peep" do + it_behaves_like(:strscan_peek, :peep) + + it "warns in verbose mode that the method is obsolete" do + s = StringScanner.new("abc") + lambda { + $VERBOSE = true + s.peep(1) + }.should complain(/peep.*obsolete.*peek/) + + lambda { + $VERBOSE = false + s.peep(1) + }.should_not complain + end +end diff --git a/spec/rubyspec/library/stringscanner/pointer_spec.rb b/spec/rubyspec/library/stringscanner/pointer_spec.rb new file mode 100644 index 0000000000..8c993c5a71 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/pointer_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/pos.rb', __FILE__) +require 'strscan' + +describe "StringScanner#pointer" do + it_behaves_like(:strscan_pos, :pointer) +end + +describe "StringScanner#pointer=" do + it_behaves_like(:strscan_pos_set, :pointer=) +end diff --git a/spec/rubyspec/library/stringscanner/pos_spec.rb b/spec/rubyspec/library/stringscanner/pos_spec.rb new file mode 100644 index 0000000000..059e6ff846 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/pos_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/pos.rb', __FILE__) +require 'strscan' + +describe "StringScanner#pos" do + it_behaves_like(:strscan_pos, :pos) +end + +describe "StringScanner#pos=" do + it_behaves_like(:strscan_pos_set, :pos=) +end diff --git a/spec/rubyspec/library/stringscanner/post_match_spec.rb b/spec/rubyspec/library/stringscanner/post_match_spec.rb new file mode 100644 index 0000000000..94064bb368 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/post_match_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/extract_range_matched', __FILE__) +require 'strscan' + +describe "StringScanner#post_match" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the post-match (in the regular expression sense) of the last scan" do + @s.post_match.should == nil + @s.scan(/\w+\s/) + @s.post_match.should == "is a test" + @s.getch + @s.post_match.should == "s a test" + @s.get_byte + @s.post_match.should == " a test" + @s.get_byte + @s.post_match.should == "a test" + end + + it "returns nil if there's no match" do + @s.scan(/\s+/) + @s.post_match.should == nil + end + + it_behaves_like :extract_range_matched, :post_match +end diff --git a/spec/rubyspec/library/stringscanner/pre_match_spec.rb b/spec/rubyspec/library/stringscanner/pre_match_spec.rb new file mode 100644 index 0000000000..0d4759dc5c --- /dev/null +++ b/spec/rubyspec/library/stringscanner/pre_match_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/extract_range_matched', __FILE__) +require 'strscan' + +describe "StringScanner#pre_match" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the pre-match (in the regular expression sense) of the last scan" do + @s.pre_match.should == nil + @s.scan(/\w+\s/) + @s.pre_match.should == "" + @s.getch + @s.pre_match.should == "This " + @s.get_byte + @s.pre_match.should == "This i" + @s.get_byte + @s.pre_match.should == "This is" + end + + it "returns nil if there's no match" do + @s.scan(/\s+/) + @s.pre_match.should == nil + end + + it "is more than just the data from the last match" do + @s.scan(/\w+/) + @s.scan_until(/a te/) + @s.pre_match.should == "This is " + end + + it "is not changed when the scanner's position changes" do + @s.scan_until(/\s+/) + @s.pre_match.should == "This" + @s.pos -= 1 + @s.pre_match.should == "This" + end + + it_behaves_like :extract_range_matched, :pre_match +end diff --git a/spec/rubyspec/library/stringscanner/reset_spec.rb b/spec/rubyspec/library/stringscanner/reset_spec.rb new file mode 100644 index 0000000000..9631d0bb4a --- /dev/null +++ b/spec/rubyspec/library/stringscanner/reset_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#reset" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "reset the scan pointer and clear matching data" do + @s.scan(/This/) + @s.reset + @s.pos.should == 0 + @s.matched.should == nil + end +end diff --git a/spec/rubyspec/library/stringscanner/rest_size_spec.rb b/spec/rubyspec/library/stringscanner/rest_size_spec.rb new file mode 100644 index 0000000000..ad019f2a6b --- /dev/null +++ b/spec/rubyspec/library/stringscanner/rest_size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rest_size.rb', __FILE__) +require 'strscan' + +describe "StringScanner#rest_size" do + it_behaves_like(:strscan_rest_size, :rest_size) +end diff --git a/spec/rubyspec/library/stringscanner/rest_spec.rb b/spec/rubyspec/library/stringscanner/rest_spec.rb new file mode 100644 index 0000000000..87f2d056bb --- /dev/null +++ b/spec/rubyspec/library/stringscanner/rest_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/extract_range_matched', __FILE__) +require 'strscan' + +describe "StringScanner#rest" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the rest of the string" do + @s.scan(/This\s+/) + @s.rest.should == "is a test" + end + + it "returns self in the reset position" do + @s.reset + @s.rest.should == @s.string + end + + it "returns an empty string in the terminate position" do + @s.terminate + @s.rest.should == "" + end + + it_behaves_like :extract_range_matched, :rest + +end + +describe "StringScanner#rest?" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns true if there is more data in the string" do + @s.rest?.should be_true + @s.scan(/This/) + @s.rest?.should be_true + end + + it "returns false if there is no more data in the string" do + @s.terminate + @s.rest?.should be_false + end + + it "is the opposite of eos?" do + @s.rest?.should_not == @s.eos? + end +end diff --git a/spec/rubyspec/library/stringscanner/restsize_spec.rb b/spec/rubyspec/library/stringscanner/restsize_spec.rb new file mode 100644 index 0000000000..9822d69d84 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/restsize_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rest_size.rb', __FILE__) +require 'strscan' + +describe "StringScanner#restsize" do + it_behaves_like(:strscan_rest_size, :restsize) + + it "warns in verbose mode that the method is obsolete" do + s = StringScanner.new("abc") + lambda { + $VERBOSE = true + s.restsize + }.should complain(/restsize.*obsolete.*rest_size/) + + lambda { + $VERBOSE = false + s.restsize + }.should_not complain + end +end diff --git a/spec/rubyspec/library/stringscanner/scan_full_spec.rb b/spec/rubyspec/library/stringscanner/scan_full_spec.rb new file mode 100644 index 0000000000..ada9578558 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/scan_full_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#scan_full" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the number of bytes advanced" do + orig_pos = @s.pos + @s.scan_full(/This/, false, false).should == 4 + @s.pos.should == orig_pos + end + + it "returns the number of bytes advanced and advances the scan pointer if the second argument is true" do + @s.scan_full(/This/, true, false).should == 4 + @s.pos.should == 4 + end + + it "returns the matched string if the third argument is true" do + orig_pos = @s.pos + @s.scan_full(/This/, false, true).should == "This" + @s.pos.should == orig_pos + end + + it "returns the matched string if the third argument is true and advances the scan pointer if the second argument is true" do + @s.scan_full(/This/, true, true).should == "This" + @s.pos.should == 4 + end +end diff --git a/spec/rubyspec/library/stringscanner/scan_spec.rb b/spec/rubyspec/library/stringscanner/scan_spec.rb new file mode 100644 index 0000000000..84a0f27f08 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/scan_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#scan" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the matched string" do + @s.scan(/\w+/).should == "This" + @s.scan(/.../).should == " is" + @s.scan(//).should == "" + @s.scan(/\s+/).should == " " + end + + it "treats ^ as matching from the beginning of the current position" do + @s.scan(/\w+/).should == "This" + @s.scan(/^\d/).should be_nil + @s.scan(/^\s/).should == " " + end + + it "returns nil if there's no match" do + @s.scan(/\d/).should == nil + end + + it "returns nil when there is no more to scan" do + @s.scan(/[\w\s]+/).should == "This is a test" + @s.scan(/\w+/).should be_nil + end + + it "returns an empty string when the pattern matches empty" do + @s.scan(/.*/).should == "This is a test" + @s.scan(/.*/).should == "" + @s.scan(/./).should be_nil + end + + it "raises a TypeError if pattern isn't a Regexp" do + lambda { @s.scan("aoeu") }.should raise_error(TypeError) + lambda { @s.scan(5) }.should raise_error(TypeError) + lambda { @s.scan(:test) }.should raise_error(TypeError) + lambda { @s.scan(mock('x')) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/stringscanner/scan_until_spec.rb b/spec/rubyspec/library/stringscanner/scan_until_spec.rb new file mode 100644 index 0000000000..702283ed22 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/scan_until_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#scan_until" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the substring up to and including the end of the match" do + @s.scan_until(/a/).should == "This is a" + @s.pre_match.should == "This is " + @s.post_match.should == " test" + end + + it "returns nil if there's no match" do + @s.scan_until(/\d/).should == nil + end + + it "can match anchors properly" do + @s.scan(/T/) + @s.scan_until(/^h/).should == "h" + end +end diff --git a/spec/rubyspec/library/stringscanner/search_full_spec.rb b/spec/rubyspec/library/stringscanner/search_full_spec.rb new file mode 100644 index 0000000000..43ff840003 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/search_full_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#search_full" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the number of bytes advanced" do + orig_pos = @s.pos + @s.search_full(/This/, false, false).should == 4 + @s.pos.should == orig_pos + end + + it "returns the number of bytes advanced and advances the scan pointer if the second argument is true" do + @s.search_full(/This/, true, false).should == 4 + @s.pos.should == 4 + end + + it "returns the matched string if the third argument is true" do + orig_pos = @s.pos + @s.search_full(/This/, false, true).should == "This" + @s.pos.should == orig_pos + end + + it "returns the matched string if the third argument is true and advances the scan pointer if the second argument is true" do + @s.search_full(/This/, true, true).should == "This" + @s.pos.should == 4 + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/bol.rb b/spec/rubyspec/library/stringscanner/shared/bol.rb new file mode 100644 index 0000000000..ebcdd7938f --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/bol.rb @@ -0,0 +1,25 @@ +describe :strscan_bol, shared: true do + it "returns true if the scan pointer is at the beginning of the line, false otherwise" do + s = StringScanner.new("This is a test") + s.send(@method).should be_true + s.scan(/This/) + s.send(@method).should be_false + s.terminate + s.send(@method).should be_false + + s = StringScanner.new("hello\nworld") + s.bol?.should be_true + s.scan(/\w+/) + s.bol?.should be_false + s.scan(/\n/) + s.bol?.should be_true + s.unscan + s.bol?.should be_false + end + + it "returns true if the scan pointer is at the end of the line of an empty string." do + s = StringScanner.new('') + s.terminate + s.send(@method).should be_true + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/concat.rb b/spec/rubyspec/library/stringscanner/shared/concat.rb new file mode 100644 index 0000000000..28788d3ff1 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/concat.rb @@ -0,0 +1,30 @@ +describe :strscan_concat, shared: true do + it "concatenates the given argument to self and returns self" do + s = StringScanner.new("hello ") + s.send(@method, 'world').should == s + s.string.should == "hello world" + s.eos?.should be_false + end + + it "raises a TypeError if the given argument can't be converted to a String" do + lambda { StringScanner.new('hello').send(@method, :world) }.should raise_error(TypeError) + lambda { StringScanner.new('hello').send(@method, mock('x')) }.should raise_error(TypeError) + end +end + +describe :strscan_concat_fixnum, shared: true do + it "raises a TypeError" do + a = StringScanner.new("hello world") + lambda { a.send(@method, 333) }.should raise_error(TypeError) + b = StringScanner.new("") + lambda { b.send(@method, (256 * 3 + 64)) }.should raise_error(TypeError) + lambda { b.send(@method, -200) }.should raise_error(TypeError) + end + + it "doesn't call to_int on the argument" do + x = mock('x') + x.should_not_receive(:to_int) + + lambda { "".send(@method, x) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/eos.rb b/spec/rubyspec/library/stringscanner/shared/eos.rb new file mode 100644 index 0000000000..ea04c764a2 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/eos.rb @@ -0,0 +1,17 @@ +describe :strscan_eos, shared: true do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns true if the scan pointer is at the end of the string" do + @s.terminate + @s.send(@method).should be_true + + s = StringScanner.new('') + s.send(@method).should be_true + end + + it "returns false if the scan pointer is not at the end of the string" do + @s.send(@method).should be_false + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/extract_range.rb b/spec/rubyspec/library/stringscanner/shared/extract_range.rb new file mode 100644 index 0000000000..7e98540b1a --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/extract_range.rb @@ -0,0 +1,22 @@ +describe :extract_range, shared: true do + it "returns an instance of String when passed a String subclass" do + cls = Class.new(String) + sub = cls.new("abc") + + s = StringScanner.new(sub) + ch = s.send(@method) + ch.should_not be_kind_of(cls) + ch.should be_an_instance_of(String) + end + + it "taints the returned String if the input was tainted" do + str = 'abc' + str.taint + + s = StringScanner.new(str) + + s.send(@method).tainted?.should be_true + s.send(@method).tainted?.should be_true + s.send(@method).tainted?.should be_true + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/extract_range_matched.rb b/spec/rubyspec/library/stringscanner/shared/extract_range_matched.rb new file mode 100644 index 0000000000..fe695e8ac1 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/extract_range_matched.rb @@ -0,0 +1,22 @@ +describe :extract_range_matched, shared: true do + it "returns an instance of String when passed a String subclass" do + cls = Class.new(String) + sub = cls.new("abc") + + s = StringScanner.new(sub) + s.scan(/\w{1}/) + + ch = s.send(@method) + ch.should_not be_kind_of(cls) + ch.should be_an_instance_of(String) + end + + it "taints the returned String if the input was tainted" do + str = 'abc' + str.taint + + s = StringScanner.new(str) + s.scan(/\w{1}/) + s.send(@method).tainted?.should be_true + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/get_byte.rb b/spec/rubyspec/library/stringscanner/shared/get_byte.rb new file mode 100644 index 0000000000..763ab6f4a4 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/get_byte.rb @@ -0,0 +1,29 @@ +# -*- encoding: binary -*- +describe :strscan_get_byte, shared: true do + it "scans one byte and returns it" do + s = StringScanner.new('abc5.') + s.send(@method).should == 'a' + s.send(@method).should == 'b' + s.send(@method).should == 'c' + s.send(@method).should == '5' + s.send(@method).should == '.' + end + + it "is not multi-byte character sensitive" do + s = StringScanner.new("\244\242") + s.send(@method).should == "\244" + s.send(@method).should == "\242" + end + + it "returns nil at the end of the string" do + # empty string case + s = StringScanner.new('') + s.send(@method).should == nil + s.send(@method).should == nil + + # non-empty string case + s = StringScanner.new('a') + s.send(@method) # skip one + s.send(@method).should == nil + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/matched_size.rb b/spec/rubyspec/library/stringscanner/shared/matched_size.rb new file mode 100644 index 0000000000..0d783dc92b --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/matched_size.rb @@ -0,0 +1,21 @@ +describe :strscan_matched_size, shared: true do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the size of the most recent match" do + @s.check(/This/) + @s.send(@method).should == 4 + @s.send(@method).should == 4 + @s.scan(//) + @s.send(@method).should == 0 + end + + it "returns nil if there was no recent match" do + @s.send(@method).should == nil + @s.check(/\d+/) + @s.send(@method).should == nil + @s.terminate + @s.send(@method).should == nil + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/peek.rb b/spec/rubyspec/library/stringscanner/shared/peek.rb new file mode 100644 index 0000000000..f414cc3b40 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/peek.rb @@ -0,0 +1,44 @@ +describe :strscan_peek, shared: true do + before :each do + @s = StringScanner.new('This is a test') + end + + it "returns at most the specified number of characters from the current position" do + @s.send(@method, 4).should == "This" + @s.pos.should == 0 + @s.pos = 5 + @s.send(@method, 2).should == "is" + @s.send(@method, 1000).should == "is a test" + end + + it "returns an empty string when the passed argument is zero" do + @s.send(@method, 0).should == "" + end + + it "raises a ArgumentError when the passed argument is negative" do + lambda { @s.send(@method, -2) }.should raise_error(ArgumentError) + end + + it "raises a RangeError when the passed argument is a Bignum" do + lambda { @s.send(@method, bignum_value) }.should raise_error(RangeError) + end + + it "returns an instance of String when passed a String subclass" do + cls = Class.new(String) + sub = cls.new("abc") + + s = StringScanner.new(sub) + + ch = s.send(@method, 1) + ch.should_not be_kind_of(cls) + ch.should be_an_instance_of(String) + end + + it "taints the returned String if the input was tainted" do + str = 'abc' + str.taint + + s = StringScanner.new(str) + s.send(@method, 1).tainted?.should be_true + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/pos.rb b/spec/rubyspec/library/stringscanner/shared/pos.rb new file mode 100644 index 0000000000..80ded17b0f --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/pos.rb @@ -0,0 +1,52 @@ +describe :strscan_pos, shared: true do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the position of the scan pointer" do + @s.send(@method).should == 0 + @s.scan_until(/This is/) + @s.send(@method).should == 7 + @s.get_byte + @s.send(@method).should == 8 + @s.terminate + @s.send(@method).should == 14 + end + + it "returns 0 in the reset position" do + @s.reset + @s.send(@method).should == 0 + end + + it "returns the length of the string in the terminate position" do + @s.terminate + @s.send(@method).should == @s.string.length + end +end + +describe :strscan_pos_set, shared: true do + before :each do + @s = StringScanner.new("This is a test") + end + + it "modify the scan pointer" do + @s.send(@method, 5) + @s.rest.should == "is a test" + end + + it "positions from the end if the argument is negative" do + @s.send(@method, -2) + @s.rest.should == "st" + @s.pos.should == 12 + end + + it "raises a RangeError if position too far backward" do + lambda { + @s.send(@method, -20) + }.should raise_error(RangeError) + end + + it "raises a RangeError when the passed argument is out of range" do + lambda { @s.send(@method, 20) }.should raise_error(RangeError) + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/rest_size.rb b/spec/rubyspec/library/stringscanner/shared/rest_size.rb new file mode 100644 index 0000000000..4c4f49e45c --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/rest_size.rb @@ -0,0 +1,18 @@ +describe :strscan_rest_size, shared: true do + before :each do + @s = StringScanner.new('This is a test') + end + + it "returns the length of the rest of the string" do + @s.send(@method).should == 14 + @s.scan(/This/) + @s.send(@method).should == 10 + @s.terminate + @s.send(@method).should == 0 + end + + it "is equivalent to rest.size" do + @s.scan(/This/) + @s.send(@method).should == @s.rest.size + end +end diff --git a/spec/rubyspec/library/stringscanner/shared/terminate.rb b/spec/rubyspec/library/stringscanner/shared/terminate.rb new file mode 100644 index 0000000000..bf41d097e2 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/shared/terminate.rb @@ -0,0 +1,8 @@ +describe :strscan_terminate, shared: true do + it "set the scan pointer to the end of the string and clear matching data." do + s = StringScanner.new('This is a test') + s.send(@method) + s.bol?.should be_false + s.eos?.should be_true + end +end diff --git a/spec/rubyspec/library/stringscanner/skip_spec.rb b/spec/rubyspec/library/stringscanner/skip_spec.rb new file mode 100644 index 0000000000..7426267b6e --- /dev/null +++ b/spec/rubyspec/library/stringscanner/skip_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#skip" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns length of the match" do + @s.skip(/\w+/).should == 4 + @s.skip(/\s+\w+/).should == 3 + end + + it "returns nil if there's no match" do + @s.skip(/\s+/).should == nil + @s.skip(/\d+/).should == nil + end +end diff --git a/spec/rubyspec/library/stringscanner/skip_until_spec.rb b/spec/rubyspec/library/stringscanner/skip_until_spec.rb new file mode 100644 index 0000000000..c6e17900d1 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/skip_until_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#skip_until" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "returns the number of bytes advanced and advances the scan pointer until pattern is matched and consumed" do + @s.skip_until(/a/).should == 9 + @s.pos.should == 9 + @s.matched.should == "a" + end + + it "returns nil if no match was found" do + @s.skip_until(/d+/).should == nil + end +end diff --git a/spec/rubyspec/library/stringscanner/string_spec.rb b/spec/rubyspec/library/stringscanner/string_spec.rb new file mode 100644 index 0000000000..080c0dfcf1 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/string_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#string" do + before :each do + @string = "This is a test" + @s = StringScanner.new(@string) + end + + it "returns the string being scanned" do + @s.string.should == "This is a test" + @s << " case" + @s.string.should == "This is a test case" + end + + it "returns the identical object passed in" do + @s.string.equal?(@string).should be_true + end +end + +describe "StringScanner#string=" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "changes the string being scanned to the argument and resets the scanner" do + @s.string = "Hello world" + @s.string.should == "Hello world" + end + + it "converts the argument into a string using #to_str" do + m = mock(:str) + + s = "test" + m.should_receive(:to_str).and_return(s) + + @s.string = m + @s.string.should == s + end +end diff --git a/spec/rubyspec/library/stringscanner/terminate_spec.rb b/spec/rubyspec/library/stringscanner/terminate_spec.rb new file mode 100644 index 0000000000..01cea3dbdf --- /dev/null +++ b/spec/rubyspec/library/stringscanner/terminate_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/terminate.rb', __FILE__) +require 'strscan' + +describe "StringScanner#terminate" do + it_behaves_like(:strscan_terminate, :terminate) +end diff --git a/spec/rubyspec/library/stringscanner/unscan_spec.rb b/spec/rubyspec/library/stringscanner/unscan_spec.rb new file mode 100644 index 0000000000..7663f20d61 --- /dev/null +++ b/spec/rubyspec/library/stringscanner/unscan_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'strscan' + +describe "StringScanner#unscan" do + before :each do + @s = StringScanner.new("This is a test") + end + + it "set the scan pointer to the previous position" do + @s.scan(/This/) + @s.unscan + @s.matched.should == nil + @s.pos.should == 0 + end + + it "remember only one previous position" do + @s.scan(/This/) + pos = @s.pos + @s.scan(/ is/) + @s.unscan + @s.pos.should == pos + end + + it "raises a ScanError when the previous match had failed" do + lambda { @s.unscan }.should raise_error(ScanError) + lambda { @s.scan(/\d/); @s.unscan }.should raise_error(ScanError) + end +end diff --git a/spec/rubyspec/library/syslog/alert_spec.rb b/spec/rubyspec/library/syslog/alert_spec.rb new file mode 100644 index 0000000000..4653fa8636 --- /dev/null +++ b/spec/rubyspec/library/syslog/alert_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.alert" do + it_behaves_like :syslog_log, :alert + end +end diff --git a/spec/rubyspec/library/syslog/close_spec.rb b/spec/rubyspec/library/syslog/close_spec.rb new file mode 100644 index 0000000000..f7bc4ac6e9 --- /dev/null +++ b/spec/rubyspec/library/syslog/close_spec.rb @@ -0,0 +1,57 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.close" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "closes the log" do + Syslog.opened?.should be_false + Syslog.open + Syslog.opened?.should be_true + Syslog.close + Syslog.opened?.should be_false + end + + it "raises a RuntimeError if the log's already closed" do + lambda { Syslog.close }.should raise_error(RuntimeError) + end + + it "it does not work inside blocks" do + lambda { + Syslog.open { |s| s.close } + }.should raise_error(RuntimeError) + Syslog.opened?.should == false + end + + it "sets the identity to nil" do + Syslog.open("rubyspec") + Syslog.ident.should == "rubyspec" + Syslog.close + Syslog.ident.should be_nil + end + + it "sets the options to nil" do + Syslog.open("rubyspec", Syslog::LOG_PID) + Syslog.options.should == Syslog::LOG_PID + Syslog.close + Syslog.options.should == nil + end + + it "sets the facility to nil" do + Syslog.open + Syslog.facility.should == 8 + Syslog.close + Syslog.facility.should == nil + end + end + end +end diff --git a/spec/rubyspec/library/syslog/constants_spec.rb b/spec/rubyspec/library/syslog/constants_spec.rb new file mode 100644 index 0000000000..c335ff46e6 --- /dev/null +++ b/spec/rubyspec/library/syslog/constants_spec.rb @@ -0,0 +1,40 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog::Constants" do + platform_is_not :windows, :solaris, :aix do + before :all do + @constants = %w(LOG_AUTHPRIV LOG_USER LOG_LOCAL2 LOG_NOTICE LOG_NDELAY + LOG_SYSLOG LOG_ALERT LOG_FTP LOG_LOCAL5 LOG_ERR LOG_AUTH + LOG_LOCAL1 LOG_ODELAY LOG_NEWS LOG_DAEMON LOG_LOCAL4 + LOG_CRIT LOG_INFO LOG_PERROR LOG_LOCAL0 LOG_CONS LOG_LPR + LOG_LOCAL7 LOG_WARNING LOG_CRON LOG_LOCAL3 LOG_EMERG + LOG_NOWAIT LOG_UUCP LOG_PID LOG_KERN LOG_MAIL LOG_LOCAL6 + LOG_DEBUG) + end + + it "includes the Syslog constants" do + @constants.each do |c| + Syslog::Constants.should have_constant(c) + end + end + end + + # The masks are defined in + + describe "Syslog::Constants.LOG_MASK" do + it "returns the mask value for a priority" do + Syslog::Constants.LOG_MASK(Syslog::LOG_DEBUG).should == 128 + Syslog::Constants.LOG_MASK(Syslog::LOG_WARNING).should == 16 + end + end + + describe "Syslog::Constants.LOG_UPTO" do + it "returns a mask for the priorities up to a given argument" do + Syslog::Constants.LOG_UPTO(Syslog::LOG_ALERT).should == 3 + Syslog::Constants.LOG_UPTO(Syslog::LOG_DEBUG).should == 255 + end + end + end +end diff --git a/spec/rubyspec/library/syslog/crit_spec.rb b/spec/rubyspec/library/syslog/crit_spec.rb new file mode 100644 index 0000000000..28e3af67cc --- /dev/null +++ b/spec/rubyspec/library/syslog/crit_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.crit" do + it_behaves_like :syslog_log, :crit + end +end diff --git a/spec/rubyspec/library/syslog/debug_spec.rb b/spec/rubyspec/library/syslog/debug_spec.rb new file mode 100644 index 0000000000..ff9fc5d97e --- /dev/null +++ b/spec/rubyspec/library/syslog/debug_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.debug" do + it_behaves_like :syslog_log, :debug + end +end diff --git a/spec/rubyspec/library/syslog/emerg_spec.rb b/spec/rubyspec/library/syslog/emerg_spec.rb new file mode 100644 index 0000000000..15cc49be32 --- /dev/null +++ b/spec/rubyspec/library/syslog/emerg_spec.rb @@ -0,0 +1,15 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.emerg" do + # Some way needs do be found to prevent this spec + # from causing output on all open terminals. If this + # is not possible, this spec may need a special guard + # that only runs when requested. + quarantine! do + it_behaves_like :syslog_log, :emerg + end + end +end diff --git a/spec/rubyspec/library/syslog/err_spec.rb b/spec/rubyspec/library/syslog/err_spec.rb new file mode 100644 index 0000000000..fdadd49e09 --- /dev/null +++ b/spec/rubyspec/library/syslog/err_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.err" do + it_behaves_like :syslog_log, :err + end +end diff --git a/spec/rubyspec/library/syslog/facility_spec.rb b/spec/rubyspec/library/syslog/facility_spec.rb new file mode 100644 index 0000000000..02ea754b6b --- /dev/null +++ b/spec/rubyspec/library/syslog/facility_spec.rb @@ -0,0 +1,47 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.facility" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "returns the logging facility" do + Syslog.open("rubyspec", 3, Syslog::LOG_MAIL) + Syslog.facility.should == Syslog::LOG_MAIL + Syslog.close + end + + it "returns nil if the log is closed" do + Syslog.opened?.should be_false + Syslog.facility.should == nil + end + + it "defaults to LOG_USER" do + Syslog.open + Syslog.facility.should == Syslog::LOG_USER + Syslog.close + end + + it "resets after each open call" do + Syslog.open + Syslog.facility.should == Syslog::LOG_USER + + Syslog.open!("rubyspec", 3, Syslog::LOG_MAIL) + Syslog.facility.should == Syslog::LOG_MAIL + Syslog.close + + Syslog.open + Syslog.facility.should == Syslog::LOG_USER + Syslog.close + end + end + end +end diff --git a/spec/rubyspec/library/syslog/ident_spec.rb b/spec/rubyspec/library/syslog/ident_spec.rb new file mode 100644 index 0000000000..a685dfa927 --- /dev/null +++ b/spec/rubyspec/library/syslog/ident_spec.rb @@ -0,0 +1,34 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.ident" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "returns the logging identity" do + Syslog.open("rubyspec") + Syslog.ident.should == "rubyspec" + Syslog.close + end + + it "returns nil if the log is closed" do + Syslog.opened?.should == false + Syslog.ident.should == nil + end + + it "defaults to $0" do + Syslog.open + Syslog.ident.should == $0 + Syslog.close + end + end + end +end diff --git a/spec/rubyspec/library/syslog/info_spec.rb b/spec/rubyspec/library/syslog/info_spec.rb new file mode 100644 index 0000000000..88ff81994d --- /dev/null +++ b/spec/rubyspec/library/syslog/info_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.info" do + it_behaves_like :syslog_log, :info + end +end diff --git a/spec/rubyspec/library/syslog/inspect_spec.rb b/spec/rubyspec/library/syslog/inspect_spec.rb new file mode 100644 index 0000000000..5fd7793c22 --- /dev/null +++ b/spec/rubyspec/library/syslog/inspect_spec.rb @@ -0,0 +1,38 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.inspect" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "returns a string a closed log" do + Syslog.inspect.should =~ /opened=false/ + end + + it "returns a string for an opened log" do + Syslog.open + Syslog.inspect.should =~ /opened=true.*/ + Syslog.close + end + + it "includes the ident, options, facility and mask" do + Syslog.open("rubyspec", Syslog::LOG_PID, Syslog::LOG_USER) + inspect_str = Syslog.inspect.split ", " + inspect_str[0].should =~ /opened=true/ + inspect_str[1].should == "ident=\"rubyspec\"" + inspect_str[2].should == "options=#{Syslog::LOG_PID}" + inspect_str[3].should == "facility=#{Syslog::LOG_USER}" + inspect_str[4].should == "mask=255>" + Syslog.close + end + end + end +end diff --git a/spec/rubyspec/library/syslog/instance_spec.rb b/spec/rubyspec/library/syslog/instance_spec.rb new file mode 100644 index 0000000000..60f43dbdea --- /dev/null +++ b/spec/rubyspec/library/syslog/instance_spec.rb @@ -0,0 +1,12 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.instance" do + platform_is_not :windows do + it "returns the module" do + Syslog.instance.should == Syslog + end + end + end +end diff --git a/spec/rubyspec/library/syslog/log_spec.rb b/spec/rubyspec/library/syslog/log_spec.rb new file mode 100644 index 0000000000..2403943cfc --- /dev/null +++ b/spec/rubyspec/library/syslog/log_spec.rb @@ -0,0 +1,55 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.log" do + platform_is_not :windows, :darwin, :solaris, :aix do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "receives a priority as first argument" do + lambda { + Syslog.open("rubyspec", Syslog::LOG_PERROR) do |s| + s.log(Syslog::LOG_ALERT, "Hello") + s.log(Syslog::LOG_CRIT, "World") + end + }.should output_to_fd("rubyspec: Hello\nrubyspec: World\n", $stderr) + end + + it "accepts undefined priorites" do + lambda { + Syslog.open("rubyspec", Syslog::LOG_PERROR) do |s| + s.log(1337, "Hello") + end + # use a regex since it'll output unknown facility/priority messages + }.should output_to_fd(/rubyspec: Hello/, $stderr) + end + + it "fails with TypeError on nil log messages" do + Syslog.open do |s| + lambda { s.log(1, nil) }.should raise_error(TypeError) + end + end + + it "fails if the log is closed" do + lambda { + Syslog.log(Syslog::LOG_ALERT, "test") + }.should raise_error(RuntimeError) + end + + it "accepts printf parameters" do + lambda { + Syslog.open("rubyspec", Syslog::LOG_PERROR) do |s| + s.log(Syslog::LOG_ALERT, "%s x %d", "chunky bacon", 2) + end + }.should output_to_fd("rubyspec: chunky bacon x 2\n", $stderr) + end + end + end +end diff --git a/spec/rubyspec/library/syslog/mask_spec.rb b/spec/rubyspec/library/syslog/mask_spec.rb new file mode 100644 index 0000000000..49be56d304 --- /dev/null +++ b/spec/rubyspec/library/syslog/mask_spec.rb @@ -0,0 +1,112 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.mask" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + # make sure we return the mask to the default value + Syslog.open { |s| s.mask = 255 } + end + + it "returns the log priority mask" do + Syslog.open("rubyspec") do + Syslog.mask.should == 255 + Syslog.mask = 3 + Syslog.mask.should == 3 + Syslog.mask = 255 + end + end + + it "defaults to 255" do + Syslog.open do |s| + s.mask.should == 255 + end + end + + it "returns nil if the log is closed" do + Syslog.opened?.should == false + Syslog.mask.should == nil + end + + platform_is :darwin do + it "resets if the log is reopened" do + Syslog.open + Syslog.mask.should == 255 + Syslog.mask = 64 + + Syslog.reopen("rubyspec") do + Syslog.mask.should == 255 + end + + Syslog.open do + Syslog.mask.should == 255 + end + end + end + + platform_is_not :darwin do + it "persists if the log is reopened" do + Syslog.open + Syslog.mask.should == 255 + Syslog.mask = 64 + + Syslog.reopen("rubyspec") do + Syslog.mask.should == 64 + end + + Syslog.open do + Syslog.mask.should == 64 + end + end + end + end + end + + describe "Syslog.mask=" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + # make sure we return the mask to the default value + Syslog.open { |s| s.mask = 255 } + end + + it "sets the log priority mask" do + Syslog.open do + Syslog.mask = 64 + Syslog.mask.should == 64 + end + end + + it "raises an error if the log is closed" do + lambda { Syslog.mask = 1337 }.should raise_error(RuntimeError) + end + + it "only accepts numbers" do + Syslog.open do + + Syslog.mask = 1337 + Syslog.mask.should == 1337 + + Syslog.mask = 3.1416 + Syslog.mask.should == 3 + + lambda { Syslog.mask = "oh hai" }.should raise_error(TypeError) + lambda { Syslog.mask = "43" }.should raise_error(TypeError) + + end + end + end + end +end diff --git a/spec/rubyspec/library/syslog/notice_spec.rb b/spec/rubyspec/library/syslog/notice_spec.rb new file mode 100644 index 0000000000..7a27e23729 --- /dev/null +++ b/spec/rubyspec/library/syslog/notice_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.notice" do + it_behaves_like :syslog_log, :notice + end +end diff --git a/spec/rubyspec/library/syslog/open_spec.rb b/spec/rubyspec/library/syslog/open_spec.rb new file mode 100644 index 0000000000..18e7f0c80e --- /dev/null +++ b/spec/rubyspec/library/syslog/open_spec.rb @@ -0,0 +1,86 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/reopen', __FILE__) + require 'syslog' + + describe "Syslog.open" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "returns the module" do + Syslog.open.should == Syslog + Syslog.close + Syslog.open("Test", 5, 9).should == Syslog + Syslog.close + end + + it "receives an identity as first argument" do + Syslog.open("rubyspec") + Syslog.ident.should == "rubyspec" + Syslog.close + end + + it "defaults the identity to $0" do + Syslog.open + Syslog.ident.should == $0 + Syslog.close + end + + it "receives the logging options as second argument" do + Syslog.open("rubyspec", Syslog::LOG_PID) + Syslog.options.should == Syslog::LOG_PID + Syslog.close + end + + it "defaults the logging options to LOG_PID | LOG_CONS" do + Syslog.open + Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS + Syslog.close + end + + it "receives a facility as third argument" do + Syslog.open("rubyspec", Syslog::LOG_PID, 0) + Syslog.facility.should == 0 + Syslog.close + end + + it "defaults the facility to LOG_USER" do + Syslog.open + Syslog.facility.should == Syslog::LOG_USER + Syslog.close + end + + it "receives a block and calls it with the module" do + Syslog.open("rubyspec", 3, 8) do |s| + s.should == Syslog + s.ident.should == "rubyspec" + s.options.should == 3 + s.facility.should == Syslog::LOG_USER + end + end + + it "closes the log if after it receives a block" do + Syslog.open{ } + Syslog.opened?.should be_false + end + + it "raises an error if the log is opened" do + Syslog.open + lambda { Syslog.open}.should raise_error + lambda { Syslog.close; Syslog.open }.should_not raise_error + Syslog.close + end + end + end + + describe "Syslog.open!" do + it_behaves_like :syslog_reopen, :open! + end +end diff --git a/spec/rubyspec/library/syslog/opened_spec.rb b/spec/rubyspec/library/syslog/opened_spec.rb new file mode 100644 index 0000000000..82b8ba45f7 --- /dev/null +++ b/spec/rubyspec/library/syslog/opened_spec.rb @@ -0,0 +1,38 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.opened?" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "returns true if the log is opened" do + Syslog.open + Syslog.opened?.should be_true + Syslog.close + end + + it "returns false otherwise" do + Syslog.opened?.should be_false + Syslog.open + Syslog.close + Syslog.opened?.should be_false + end + + it "works inside a block" do + Syslog.open do |s| + s.opened?.should be_true + Syslog.opened?.should be_true + end + Syslog.opened?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/syslog/options_spec.rb b/spec/rubyspec/library/syslog/options_spec.rb new file mode 100644 index 0000000000..4a3914531a --- /dev/null +++ b/spec/rubyspec/library/syslog/options_spec.rb @@ -0,0 +1,47 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require 'syslog' + + describe "Syslog.options" do + platform_is_not :windows do + + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "returns the logging options" do + Syslog.open("rubyspec", Syslog::LOG_PID) + Syslog.options.should == Syslog::LOG_PID + Syslog.close + end + + it "returns nil when the log is closed" do + Syslog.opened?.should be_false + Syslog.options.should == nil + end + + it "defaults to LOG_PID | LOG_CONS" do + Syslog.open + Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS + Syslog.close + end + + it "resets after each open call" do + Syslog.open + Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS + + Syslog.open!("rubyspec", Syslog::LOG_PID) + Syslog.options.should == Syslog::LOG_PID + Syslog.close + + Syslog.open + Syslog.options.should == Syslog::LOG_PID | Syslog::LOG_CONS + Syslog.close + end + end + end +end diff --git a/spec/rubyspec/library/syslog/reopen_spec.rb b/spec/rubyspec/library/syslog/reopen_spec.rb new file mode 100644 index 0000000000..de1ce2c255 --- /dev/null +++ b/spec/rubyspec/library/syslog/reopen_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/reopen', __FILE__) + require 'syslog' + + describe "Syslog.reopen" do + it_behaves_like :syslog_reopen, :reopen + end +end diff --git a/spec/rubyspec/library/syslog/shared/log.rb b/spec/rubyspec/library/syslog/shared/log.rb new file mode 100644 index 0000000000..6d0d3a3c23 --- /dev/null +++ b/spec/rubyspec/library/syslog/shared/log.rb @@ -0,0 +1,40 @@ +describe :syslog_log, shared: true do + platform_is_not :windows, :darwin, :solaris, :aix do + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "logs a message" do + lambda { + Syslog.open("rubyspec", Syslog::LOG_PERROR) do + Syslog.send(@method, "Hello") + end + }.should output_to_fd("rubyspec: Hello\n", $stderr) + end + + it "accepts sprintf arguments" do + lambda { + Syslog.open("rubyspec", Syslog::LOG_PERROR) do + Syslog.send(@method, "Hello %s", "world") + Syslog.send(@method, "%d dogs", 2) + end + }.should output_to_fd("rubyspec: Hello world\nrubyspec: 2 dogs\n", $stderr) + end + + it "works as an alias for Syslog.log" do + level = Syslog.const_get "LOG_#{@method.to_s.upcase}" + response = "rubyspec: Hello\n" + lambda { + Syslog.open("rubyspec", Syslog::LOG_PERROR) do + Syslog.send(@method, "Hello") + Syslog.log(level, "Hello") + end + # make sure the same thing is written to $stderr. + }.should output_to_fd(response * 2, $stderr) + end + end +end diff --git a/spec/rubyspec/library/syslog/shared/reopen.rb b/spec/rubyspec/library/syslog/shared/reopen.rb new file mode 100644 index 0000000000..fd30ab824a --- /dev/null +++ b/spec/rubyspec/library/syslog/shared/reopen.rb @@ -0,0 +1,40 @@ +describe :syslog_reopen, shared: true do + platform_is_not :windows do + before :each do + Syslog.opened?.should be_false + end + + after :each do + Syslog.opened?.should be_false + end + + it "reopens the log" do + Syslog.open + lambda { Syslog.send(@method)}.should_not raise_error + Syslog.opened?.should be_true + Syslog.close + end + + it "fails with RuntimeError if the log is closed" do + lambda { Syslog.send(@method)}.should raise_error(RuntimeError) + end + + it "receives the same parameters as Syslog.open" do + Syslog.open + Syslog.send(@method, "rubyspec", 3, 8) do |s| + s.should == Syslog + s.ident.should == "rubyspec" + s.options.should == 3 + s.facility.should == Syslog::LOG_USER + s.opened?.should be_true + end + Syslog.opened?.should be_false + end + + it "returns the module" do + Syslog.open + Syslog.send(@method).should == Syslog + Syslog.close + end + end +end diff --git a/spec/rubyspec/library/syslog/warning_spec.rb b/spec/rubyspec/library/syslog/warning_spec.rb new file mode 100644 index 0000000000..85fcb3a355 --- /dev/null +++ b/spec/rubyspec/library/syslog/warning_spec.rb @@ -0,0 +1,9 @@ +platform_is_not :windows do + require File.expand_path('../../../spec_helper', __FILE__) + require File.expand_path('../shared/log', __FILE__) + require 'syslog' + + describe "Syslog.warning" do + it_behaves_like :syslog_log, :warning + end +end diff --git a/spec/rubyspec/library/tempfile/_close_spec.rb b/spec/rubyspec/library/tempfile/_close_spec.rb new file mode 100644 index 0000000000..d91a3e1adc --- /dev/null +++ b/spec/rubyspec/library/tempfile/_close_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'tempfile' + +describe "Tempfile#_close" do + before :each do + @tempfile = Tempfile.new("specs") + end + + after :each do + @tempfile.close! + end + + it "is protected" do + Tempfile.should have_protected_instance_method(:_close) + end + + it "closes self" do + @tempfile.send(:_close) + @tempfile.closed?.should be_true + end +end diff --git a/spec/rubyspec/library/tempfile/callback_spec.rb b/spec/rubyspec/library/tempfile/callback_spec.rb new file mode 100644 index 0000000000..045252fec3 --- /dev/null +++ b/spec/rubyspec/library/tempfile/callback_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'tempfile' + +describe "Tempfile.callback" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/tempfile/close_spec.rb b/spec/rubyspec/library/tempfile/close_spec.rb new file mode 100644 index 0000000000..aa776b840c --- /dev/null +++ b/spec/rubyspec/library/tempfile/close_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'tempfile' + +describe "Tempfile#close when passed no argument or [false]" do + before :each do + @tempfile = Tempfile.new("specs", tmp("")) + end + + after :each do + @tempfile.close! + end + + it "closes self" do + @tempfile.close + @tempfile.closed?.should be_true + end + + it "does not unlink self" do + path = @tempfile.path + @tempfile.close + File.exist?(path).should be_true + end +end + +describe "Tempfile#close when passed [true]" do + before :each do + @tempfile = Tempfile.new("specs", tmp("")) + end + + it "closes self" do + @tempfile.close(true) + @tempfile.closed?.should be_true + end + + it "unlinks self" do + path = @tempfile.path + @tempfile.close(true) + File.exist?(path).should be_false + end +end + +describe "Tempfile#close!" do + before :each do + @tempfile = Tempfile.new("specs", tmp("")) + end + + it "closes self" do + @tempfile.close! + @tempfile.closed?.should be_true + end + + it "unlinks self" do + path = @tempfile.path + @tempfile.close! + File.exist?(path).should be_false + end +end diff --git a/spec/rubyspec/library/tempfile/delete_spec.rb b/spec/rubyspec/library/tempfile/delete_spec.rb new file mode 100644 index 0000000000..8e92631e62 --- /dev/null +++ b/spec/rubyspec/library/tempfile/delete_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/unlink', __FILE__) +require 'tempfile' + +describe "Tempfile#delete" do + it_behaves_like :tempfile_unlink, :delete +end diff --git a/spec/rubyspec/library/tempfile/initialize_spec.rb b/spec/rubyspec/library/tempfile/initialize_spec.rb new file mode 100644 index 0000000000..79f33e3e98 --- /dev/null +++ b/spec/rubyspec/library/tempfile/initialize_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'tempfile' + +describe "Tempfile#initialize" do + before :each do + @tempfile = Tempfile.allocate + end + + after :each do + @tempfile.close! + end + + it "opens a new tempfile with the passed name in the passed directory" do + @tempfile.send(:initialize, "basename", tmp("")) + File.exist?(@tempfile.path).should be_true + + tmpdir = tmp("") + path = @tempfile.path + + platform_is :windows do + # on Windows, both types of slashes are OK, + # but the tmp helper always uses '/' + path.gsub!('\\', '/') + end + + path[0, tmpdir.length].should == tmpdir + path.should include("basename") + end + + platform_is_not :windows do + it "sets the permisssions on the tempfile to 0600" do + @tempfile.send(:initialize, "basename", tmp("")) + File.stat(@tempfile.path).mode.should == 0100600 + end + end + + it "accepts encoding options" do + @tempfile.send(:initialize, ['shiftjis', 'yml'], encoding: 'SHIFT_JIS') + @tempfile.external_encoding.should == Encoding::Shift_JIS + end +end diff --git a/spec/rubyspec/library/tempfile/length_spec.rb b/spec/rubyspec/library/tempfile/length_spec.rb new file mode 100644 index 0000000000..b4f0a3b1d4 --- /dev/null +++ b/spec/rubyspec/library/tempfile/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'tempfile' + +describe "Tempfile#length" do + it_behaves_like :tempfile_length, :length +end diff --git a/spec/rubyspec/library/tempfile/open_spec.rb b/spec/rubyspec/library/tempfile/open_spec.rb new file mode 100644 index 0000000000..3ceaeec502 --- /dev/null +++ b/spec/rubyspec/library/tempfile/open_spec.rb @@ -0,0 +1,82 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'tempfile' + +describe "Tempfile#open" do + before :each do + @tempfile = Tempfile.new("specs") + @tempfile.puts("Test!") + end + + after :each do + @tempfile.close! + end + + it "reopens self" do + @tempfile.close + @tempfile.open + @tempfile.closed?.should be_false + end + + it "reopens self in read and write mode and does not truncate" do + @tempfile.open + @tempfile.puts("Another Test!") + + @tempfile.open + @tempfile.readline.should == "Another Test!\n" + end +end + +describe "Tempfile.open" do + after :each do + @tempfile.close! if @tempfile + end + + it "returns a new, open Tempfile instance" do + @tempfile = Tempfile.open("specs") + # Delegation messes up .should be_an_instance_of(Tempfile) + @tempfile.instance_of?(Tempfile).should be_true + end + + it "is passed an array [base, suffix] as first argument" do + Tempfile.open(["specs", ".tt"]) { |tempfile| @tempfile = tempfile } + @tempfile.path.should =~ /specs.*\.tt$/ + end +end + +describe "Tempfile.open when passed a block" do + before :each do + ScratchPad.clear + end + + after :each do + # Tempfile.open with block does not unlink + @tempfile.close! if @tempfile + end + + it "yields a new, open Tempfile instance to the block" do + Tempfile.open("specs") do |tempfile| + @tempfile = tempfile + ScratchPad.record :yielded + + # Delegation messes up .should be_an_instance_of(Tempfile) + tempfile.instance_of?(Tempfile).should be_true + tempfile.closed?.should be_false + end + + ScratchPad.recorded.should == :yielded + end + + it "returns the value of the block" do + value = Tempfile.open("specs") do |tempfile| + @tempfile = tempfile + "return" + end + value.should == "return" + end + + it "closes the yielded Tempfile after the block" do + Tempfile.open("specs") { |tempfile| @tempfile = tempfile } + @tempfile.closed?.should be_true + end +end + diff --git a/spec/rubyspec/library/tempfile/path_spec.rb b/spec/rubyspec/library/tempfile/path_spec.rb new file mode 100644 index 0000000000..f25e902872 --- /dev/null +++ b/spec/rubyspec/library/tempfile/path_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'tempfile' + +describe "Tempfile#path" do + before :each do + @tempfile = Tempfile.new("specs", tmp("")) + end + + after :each do + @tempfile.close! + end + + it "returns the path to the tempfile" do + tmpdir = tmp("") + path = @tempfile.path + + platform_is :windows do + # on Windows, both types of slashes are OK, + # but the tmp helper always uses '/' + path.gsub!('\\', '/') + end + + path[0, tmpdir.length].should == tmpdir + path.should include("specs") + end +end diff --git a/spec/rubyspec/library/tempfile/shared/length.rb b/spec/rubyspec/library/tempfile/shared/length.rb new file mode 100644 index 0000000000..4d18d1f385 --- /dev/null +++ b/spec/rubyspec/library/tempfile/shared/length.rb @@ -0,0 +1,21 @@ +describe :tempfile_length, shared: true do + before :each do + @tempfile = Tempfile.new("specs") + end + + after :each do + @tempfile.close! + end + + it "returns the size of self" do + @tempfile.send(@method).should eql(0) + @tempfile.print("Test!") + @tempfile.send(@method).should eql(5) + end + + it "returns the size of self even if self is closed" do + @tempfile.print("Test!") + @tempfile.close + @tempfile.send(@method).should eql(5) + end +end diff --git a/spec/rubyspec/library/tempfile/shared/unlink.rb b/spec/rubyspec/library/tempfile/shared/unlink.rb new file mode 100644 index 0000000000..2b575fd391 --- /dev/null +++ b/spec/rubyspec/library/tempfile/shared/unlink.rb @@ -0,0 +1,12 @@ +describe :tempfile_unlink, shared: true do + before :each do + @tempfile = Tempfile.new("specs") + end + + it "unlinks self" do + @tempfile.close + path = @tempfile.path + @tempfile.send(@method) + File.exist?(path).should be_false + end +end diff --git a/spec/rubyspec/library/tempfile/size_spec.rb b/spec/rubyspec/library/tempfile/size_spec.rb new file mode 100644 index 0000000000..ac66d35906 --- /dev/null +++ b/spec/rubyspec/library/tempfile/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/length', __FILE__) +require 'tempfile' + +describe "Tempfile#size" do + it_behaves_like :tempfile_length, :size +end diff --git a/spec/rubyspec/library/tempfile/unlink_spec.rb b/spec/rubyspec/library/tempfile/unlink_spec.rb new file mode 100644 index 0000000000..d4ef343c8d --- /dev/null +++ b/spec/rubyspec/library/tempfile/unlink_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/unlink', __FILE__) +require 'tempfile' + +describe "Tempfile#unlink" do + it_behaves_like :tempfile_unlink, :unlink +end diff --git a/spec/rubyspec/library/thread/exclusive_spec.rb b/spec/rubyspec/library/thread/exclusive_spec.rb new file mode 100644 index 0000000000..9d30188976 --- /dev/null +++ b/spec/rubyspec/library/thread/exclusive_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'thread' + +describe "Thread.exclusive" do + before :each do + ScratchPad.clear + end + + it "returns the result of yielding" do + Thread.exclusive { :result }.should == :result + end +end diff --git a/spec/rubyspec/library/thread/queue/append_spec.rb b/spec/rubyspec/library/thread/queue/append_spec.rb new file mode 100644 index 0000000000..8a9e6a21ec --- /dev/null +++ b/spec/rubyspec/library/thread/queue/append_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/enque', __FILE__) + +describe "Thread::Queue#<<" do + it_behaves_like :queue_enq, :<<, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/clear_spec.rb b/spec/rubyspec/library/thread/queue/clear_spec.rb new file mode 100644 index 0000000000..3272a5f3e0 --- /dev/null +++ b/spec/rubyspec/library/thread/queue/clear_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/clear', __FILE__) + +describe "Thread::Queue#clear" do + it_behaves_like :queue_clear, :clear, -> { Queue.new } + + # TODO: test for atomicity of Queue#clear +end diff --git a/spec/rubyspec/library/thread/queue/close_spec.rb b/spec/rubyspec/library/thread/queue/close_spec.rb new file mode 100644 index 0000000000..728fff1bfa --- /dev/null +++ b/spec/rubyspec/library/thread/queue/close_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/close', __FILE__) + +ruby_version_is "2.3" do + describe "Queue#close" do + it_behaves_like :queue_close, :close, -> { Queue.new } + end +end diff --git a/spec/rubyspec/library/thread/queue/closed_spec.rb b/spec/rubyspec/library/thread/queue/closed_spec.rb new file mode 100644 index 0000000000..98ce9c70e3 --- /dev/null +++ b/spec/rubyspec/library/thread/queue/closed_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/closed', __FILE__) + +ruby_version_is "2.3" do + describe "Queue#closed?" do + it_behaves_like :queue_closed?, :closed?, -> { Queue.new } + end +end diff --git a/spec/rubyspec/library/thread/queue/deq_spec.rb b/spec/rubyspec/library/thread/queue/deq_spec.rb new file mode 100644 index 0000000000..7b629bafce --- /dev/null +++ b/spec/rubyspec/library/thread/queue/deq_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/deque', __FILE__) + +describe "Thread::Queue#deq" do + it_behaves_like :queue_deq, :deq, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/empty_spec.rb b/spec/rubyspec/library/thread/queue/empty_spec.rb new file mode 100644 index 0000000000..dea7eb658e --- /dev/null +++ b/spec/rubyspec/library/thread/queue/empty_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/empty', __FILE__) + +describe "Thread::Queue#empty?" do + it_behaves_like :queue_empty?, :empty?, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/enq_spec.rb b/spec/rubyspec/library/thread/queue/enq_spec.rb new file mode 100644 index 0000000000..dc2508589e --- /dev/null +++ b/spec/rubyspec/library/thread/queue/enq_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/enque', __FILE__) + +describe "Thread::Queue#enq" do + it_behaves_like :queue_enq, :enq, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/length_spec.rb b/spec/rubyspec/library/thread/queue/length_spec.rb new file mode 100644 index 0000000000..f33e51a971 --- /dev/null +++ b/spec/rubyspec/library/thread/queue/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/length', __FILE__) + +describe "Thread::Queue#length" do + it_behaves_like :queue_length, :length, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/num_waiting_spec.rb b/spec/rubyspec/library/thread/queue/num_waiting_spec.rb new file mode 100644 index 0000000000..253ef8a476 --- /dev/null +++ b/spec/rubyspec/library/thread/queue/num_waiting_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/num_waiting', __FILE__) + +describe "Thread::Queue#num_waiting" do + it_behaves_like :queue_num_waiting, :num_waiting, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/pop_spec.rb b/spec/rubyspec/library/thread/queue/pop_spec.rb new file mode 100644 index 0000000000..e812e9442e --- /dev/null +++ b/spec/rubyspec/library/thread/queue/pop_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/deque', __FILE__) + +describe "Thread::Queue#pop" do + it_behaves_like :queue_deq, :pop, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/push_spec.rb b/spec/rubyspec/library/thread/queue/push_spec.rb new file mode 100644 index 0000000000..2f1a31315d --- /dev/null +++ b/spec/rubyspec/library/thread/queue/push_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/enque', __FILE__) + +describe "Thread::Queue#push" do + it_behaves_like :queue_enq, :push, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/shift_spec.rb b/spec/rubyspec/library/thread/queue/shift_spec.rb new file mode 100644 index 0000000000..16164a72ec --- /dev/null +++ b/spec/rubyspec/library/thread/queue/shift_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/deque', __FILE__) + +describe "Thread::Queue#shift" do + it_behaves_like :queue_deq, :shift, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/queue/size_spec.rb b/spec/rubyspec/library/thread/queue/size_spec.rb new file mode 100644 index 0000000000..4b148aecd3 --- /dev/null +++ b/spec/rubyspec/library/thread/queue/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/length', __FILE__) + +describe "Thread::Queue#size" do + it_behaves_like :queue_length, :size, -> { Queue.new } +end diff --git a/spec/rubyspec/library/thread/shared/queue/clear.rb b/spec/rubyspec/library/thread/shared/queue/clear.rb new file mode 100644 index 0000000000..59ea37d615 --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/clear.rb @@ -0,0 +1,10 @@ +describe :queue_clear, shared: true do + it "removes all objects from the queue" do + queue = @object.call + queue << Object.new + queue << 1 + queue.empty?.should be_false + queue.clear + queue.empty?.should be_true + end +end diff --git a/spec/rubyspec/library/thread/shared/queue/close.rb b/spec/rubyspec/library/thread/shared/queue/close.rb new file mode 100644 index 0000000000..4457f3ae8b --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/close.rb @@ -0,0 +1,26 @@ +describe :queue_close, shared: true do + it "closes the queue and returns nil for further #pop" do + q = @object.call + q << 1 + q.close + q.pop.should == 1 + q.pop.should == nil + q.pop.should == nil + end + + it "prevents further #push" do + q = @object.call + q.close + lambda { + q << 1 + }.should raise_error(ClosedQueueError) + end + + it "may be called multiple times" do + q = @object.call + q.close + q.closed?.should be_true + q.close # no effect + q.closed?.should be_true + end +end diff --git a/spec/rubyspec/library/thread/shared/queue/closed.rb b/spec/rubyspec/library/thread/shared/queue/closed.rb new file mode 100644 index 0000000000..b3cea0c524 --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/closed.rb @@ -0,0 +1,12 @@ +describe :queue_closed?, shared: true do + it "returns false initially" do + queue = @object.call + queue.closed?.should be_false + end + + it "returns true when the queue is closed" do + queue = @object.call + queue.close + queue.closed?.should be_true + end +end diff --git a/spec/rubyspec/library/thread/shared/queue/deque.rb b/spec/rubyspec/library/thread/shared/queue/deque.rb new file mode 100644 index 0000000000..1b06dffa2c --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/deque.rb @@ -0,0 +1,37 @@ +describe :queue_deq, shared: true do + it "removes an item from the Queue" do + q = @object.call + q << Object.new + q.size.should == 1 + q.send(@method) + q.size.should == 0 + end + + it "returns items in the order they were added" do + q = @object.call + q << 1 + q << 2 + q.send(@method).should == 1 + q.send(@method).should == 2 + end + + it "blocks the thread until there are items in the queue" do + q = @object.call + v = 0 + + th = Thread.new do + q.send(@method) + v = 1 + end + + v.should == 0 + q << Object.new + th.join + v.should == 1 + end + + it "raises a ThreadError if Queue is empty" do + q = @object.call + lambda { q.send(@method,true) }.should raise_error(ThreadError) + end +end diff --git a/spec/rubyspec/library/thread/shared/queue/empty.rb b/spec/rubyspec/library/thread/shared/queue/empty.rb new file mode 100644 index 0000000000..4acd831d48 --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/empty.rb @@ -0,0 +1,12 @@ +describe :queue_empty?, shared: true do + it "returns true on an empty Queue" do + queue = @object.call + queue.empty?.should be_true + end + + it "returns false when Queue is not empty" do + queue = @object.call + queue << Object.new + queue.empty?.should be_false + end +end diff --git a/spec/rubyspec/library/thread/shared/queue/enque.rb b/spec/rubyspec/library/thread/shared/queue/enque.rb new file mode 100644 index 0000000000..36b98d3a07 --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/enque.rb @@ -0,0 +1,10 @@ +describe :queue_enq, shared: true do + it "adds an element to the Queue" do + q = @object.call + q.size.should == 0 + q.send(@method, Object.new) + q.size.should == 1 + q.send(@method, Object.new) + q.size.should == 2 + end +end diff --git a/spec/rubyspec/library/thread/shared/queue/length.rb b/spec/rubyspec/library/thread/shared/queue/length.rb new file mode 100644 index 0000000000..a0143a4e19 --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/length.rb @@ -0,0 +1,9 @@ +describe :queue_length, shared: true do + it "returns the number of elements" do + q = @object.call + q.send(@method).should == 0 + q << Object.new + q << Object.new + q.send(@method).should == 2 + end +end diff --git a/spec/rubyspec/library/thread/shared/queue/num_waiting.rb b/spec/rubyspec/library/thread/shared/queue/num_waiting.rb new file mode 100644 index 0000000000..b054951e45 --- /dev/null +++ b/spec/rubyspec/library/thread/shared/queue/num_waiting.rb @@ -0,0 +1,16 @@ +describe :queue_num_waiting, shared: true do + it "reports the number of threads waiting on the queue" do + q = @object.call + threads = [] + + 5.times do |i| + q.num_waiting.should == i + t = Thread.new { q.deq } + Thread.pass until q.num_waiting == i+1 + threads << t + end + + threads.each { q.enq Object.new } + threads.each {|t| t.join } + end +end diff --git a/spec/rubyspec/library/thread/sizedqueue/append_spec.rb b/spec/rubyspec/library/thread/sizedqueue/append_spec.rb new file mode 100644 index 0000000000..2799472767 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/append_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/enque', __FILE__) +require File.expand_path('../shared/enque', __FILE__) + +describe "Thread::SizedQueue#<<" do + it_behaves_like :queue_enq, :<<, -> { SizedQueue.new(10) } +end + +describe "Thread::SizedQueue#<<" do + it_behaves_like :sizedqueue_enq, :<< +end diff --git a/spec/rubyspec/library/thread/sizedqueue/clear_spec.rb b/spec/rubyspec/library/thread/sizedqueue/clear_spec.rb new file mode 100644 index 0000000000..7dc328803a --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/clear_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/clear', __FILE__) + +describe "Thread::SizedQueue#clear" do + it_behaves_like :queue_clear, :clear, -> { SizedQueue.new(10) } + + # TODO: test for atomicity of Queue#clear +end diff --git a/spec/rubyspec/library/thread/sizedqueue/close_spec.rb b/spec/rubyspec/library/thread/sizedqueue/close_spec.rb new file mode 100644 index 0000000000..465b9ea091 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/close_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/close', __FILE__) + +ruby_version_is "2.3" do + describe "SizedQueue#close" do + it_behaves_like :queue_close, :close, -> { SizedQueue.new(10) } + end +end diff --git a/spec/rubyspec/library/thread/sizedqueue/closed_spec.rb b/spec/rubyspec/library/thread/sizedqueue/closed_spec.rb new file mode 100644 index 0000000000..9ec72c1aa7 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/closed_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/closed', __FILE__) + +ruby_version_is "2.3" do + describe "SizedQueue#closed?" do + it_behaves_like :queue_closed?, :closed?, -> { SizedQueue.new(10) } + end +end diff --git a/spec/rubyspec/library/thread/sizedqueue/deq_spec.rb b/spec/rubyspec/library/thread/sizedqueue/deq_spec.rb new file mode 100644 index 0000000000..cc11319d22 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/deq_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/deque', __FILE__) + +describe "Thread::SizedQueue#deq" do + it_behaves_like :queue_deq, :deq, -> { SizedQueue.new(10) } +end diff --git a/spec/rubyspec/library/thread/sizedqueue/empty_spec.rb b/spec/rubyspec/library/thread/sizedqueue/empty_spec.rb new file mode 100644 index 0000000000..368bb00d5f --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/empty_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/empty', __FILE__) + +describe "Thread::SizedQueue#empty?" do + it_behaves_like :queue_empty?, :empty?, -> { SizedQueue.new(10) } +end diff --git a/spec/rubyspec/library/thread/sizedqueue/enq_spec.rb b/spec/rubyspec/library/thread/sizedqueue/enq_spec.rb new file mode 100644 index 0000000000..a0be7bbf05 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/enq_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/enque', __FILE__) +require File.expand_path('../shared/enque', __FILE__) + +describe "Thread::SizedQueue#enq" do + it_behaves_like :queue_enq, :enq, -> { SizedQueue.new(10) } +end + +describe "Thread::SizedQueue#enq" do + it_behaves_like :sizedqueue_enq, :enq +end diff --git a/spec/rubyspec/library/thread/sizedqueue/length_spec.rb b/spec/rubyspec/library/thread/sizedqueue/length_spec.rb new file mode 100644 index 0000000000..c292883a8e --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/length_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/length', __FILE__) + +describe "Thread::SizedQueue#length" do + it_behaves_like :queue_length, :length, -> { SizedQueue.new(10) } +end diff --git a/spec/rubyspec/library/thread/sizedqueue/max_spec.rb b/spec/rubyspec/library/thread/sizedqueue/max_spec.rb new file mode 100644 index 0000000000..337404d821 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/max_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' + +describe "Thread::SizedQueue#max" do + before :each do + @sized_queue = SizedQueue.new(5) + end + + it "returns the size of the queue" do + @sized_queue.max.should == 5 + end +end + +describe "Thread::SizedQueue#max=" do + before :each do + @sized_queue = SizedQueue.new(5) + end + + it "sets the size of the queue" do + @sized_queue.max.should == 5 + @sized_queue.max = 10 + @sized_queue.max.should == 10 + end + + it "does not remove items already in the queue beyond the maximum" do + @sized_queue.enq 1 + @sized_queue.enq 2 + @sized_queue.enq 3 + @sized_queue.max = 2 + (@sized_queue.size > @sized_queue.max).should be_true + @sized_queue.deq.should == 1 + @sized_queue.deq.should == 2 + @sized_queue.deq.should == 3 + end + + it "raises a TypeError when given a non-numeric value" do + lambda { @sized_queue.max = "foo" }.should raise_error(TypeError) + lambda { @sized_queue.max = Object.new }.should raise_error(TypeError) + end + + it "raises an argument error when set to zero" do + @sized_queue.max.should == 5 + lambda { @sized_queue.max = 0 }.should raise_error(ArgumentError) + @sized_queue.max.should == 5 + end + + it "raises an argument error when set to a negative number" do + @sized_queue.max.should == 5 + lambda { @sized_queue.max = -1 }.should raise_error(ArgumentError) + @sized_queue.max.should == 5 + end +end diff --git a/spec/rubyspec/library/thread/sizedqueue/new_spec.rb b/spec/rubyspec/library/thread/sizedqueue/new_spec.rb new file mode 100644 index 0000000000..a36aa5ed18 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/new_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' + +describe "Thread::SizedQueue#new" do + it "returns a new SizedQueue" do + SizedQueue.new(1).should be_kind_of(SizedQueue) + end + + it "raises a TypeError when the given argument is not Numeric" do + lambda { SizedQueue.new("foo") }.should raise_error(TypeError) + lambda { SizedQueue.new(Object.new) }.should raise_error(TypeError) + end + + it "raises an argument error when no argument is given" do + lambda { SizedQueue.new }.should raise_error(ArgumentError) + end + + it "raises an argument error when the given argument is zero" do + lambda { SizedQueue.new(0) }.should raise_error(ArgumentError) + end + + it "raises an argument error when the given argument is negative" do + lambda { SizedQueue.new(-1) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/thread/sizedqueue/num_waiting_spec.rb b/spec/rubyspec/library/thread/sizedqueue/num_waiting_spec.rb new file mode 100644 index 0000000000..2ce53875cc --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/num_waiting_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/num_waiting', __FILE__) + +describe "Thread::SizedQueue#num_waiting" do + it_behaves_like :queue_num_waiting, :num_waiting, -> { SizedQueue.new(10) } + + it "reports the number of threads waiting to push" do + q = SizedQueue.new(1) + q.push(1) + t = Thread.new { q.push(2) } + sleep 0.05 until t.stop? + q.num_waiting.should == 1 + + q.pop + t.join + end +end diff --git a/spec/rubyspec/library/thread/sizedqueue/pop_spec.rb b/spec/rubyspec/library/thread/sizedqueue/pop_spec.rb new file mode 100644 index 0000000000..a83ea6406a --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/pop_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/deque', __FILE__) + +describe "Thread::SizedQueue#pop" do + it_behaves_like :queue_deq, :pop, -> { SizedQueue.new(10) } +end diff --git a/spec/rubyspec/library/thread/sizedqueue/push_spec.rb b/spec/rubyspec/library/thread/sizedqueue/push_spec.rb new file mode 100644 index 0000000000..6021a043c9 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/push_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/enque', __FILE__) +require File.expand_path('../shared/enque', __FILE__) + +describe "Thread::SizedQueue#push" do + it_behaves_like :queue_enq, :push, -> { SizedQueue.new(10) } +end + +describe "Thread::SizedQueue#push" do + it_behaves_like :sizedqueue_enq, :push +end diff --git a/spec/rubyspec/library/thread/sizedqueue/shared/enque.rb b/spec/rubyspec/library/thread/sizedqueue/shared/enque.rb new file mode 100644 index 0000000000..627f8bd745 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/shared/enque.rb @@ -0,0 +1,34 @@ +describe :sizedqueue_enq, shared: true do + it "blocks if queued elements exceed size" do + q = SizedQueue.new(1) + + q.size.should == 0 + q.send(@method, :first_element) + q.size.should == 1 + + blocked_thread = Thread.new { q.send(@method, :second_element) } + sleep 0.01 until blocked_thread.stop? + + q.size.should == 1 + q.pop.should == :first_element + + blocked_thread.join + q.size.should == 1 + q.pop.should == :second_element + q.size.should == 0 + end + + it "raises a ThreadError if queued elements exceed size when not blocking" do + q = SizedQueue.new(2) + + non_blocking = true + add_to_queue = lambda { q.send(@method, Object.new, non_blocking) } + + q.size.should == 0 + add_to_queue.call + q.size.should == 1 + add_to_queue.call + q.size.should == 2 + add_to_queue.should raise_error(ThreadError) + end +end diff --git a/spec/rubyspec/library/thread/sizedqueue/shift_spec.rb b/spec/rubyspec/library/thread/sizedqueue/shift_spec.rb new file mode 100644 index 0000000000..89345718df --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/shift_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/deque', __FILE__) + +describe "Thread::SizedQueue#shift" do + it_behaves_like :queue_deq, :shift, -> { SizedQueue.new(10) } +end diff --git a/spec/rubyspec/library/thread/sizedqueue/size_spec.rb b/spec/rubyspec/library/thread/sizedqueue/size_spec.rb new file mode 100644 index 0000000000..7ab72d91b1 --- /dev/null +++ b/spec/rubyspec/library/thread/sizedqueue/size_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'thread' +require File.expand_path('../../shared/queue/length', __FILE__) + +describe "Thread::SizedQueue#size" do + it_behaves_like :queue_length, :size, -> { SizedQueue.new(10) } +end diff --git a/spec/rubyspec/library/time/httpdate_spec.rb b/spec/rubyspec/library/time/httpdate_spec.rb new file mode 100644 index 0000000000..af3fd83608 --- /dev/null +++ b/spec/rubyspec/library/time/httpdate_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'time' + +describe "Time.httpdate" do + it "parses RFC-2616 strings" do + t = Time.utc(1994, 11, 6, 8, 49, 37) + t.should == Time.httpdate("Sun, 06 Nov 1994 08:49:37 GMT") + + # relies on Time.parse (not yet implemented) + # t.should == Time.httpdate("Sunday, 06-Nov-94 08:49:37 GMT") + + t.should == Time.httpdate("Sun Nov 6 08:49:37 1994") + Time.utc(1995, 11, 15, 6, 25, 24).should == Time.httpdate("Wed, 15 Nov 1995 06:25:24 GMT") + Time.utc(1995, 11, 15, 4, 58, 8).should == Time.httpdate("Wed, 15 Nov 1995 04:58:08 GMT") + Time.utc(1994, 11, 15, 8, 12, 31).should == Time.httpdate("Tue, 15 Nov 1994 08:12:31 GMT") + Time.utc(1994, 12, 1, 16, 0, 0).should == Time.httpdate("Thu, 01 Dec 1994 16:00:00 GMT") + Time.utc(1994, 10, 29, 19, 43, 31).should == Time.httpdate("Sat, 29 Oct 1994 19:43:31 GMT") + Time.utc(1994, 11, 15, 12, 45, 26).should == Time.httpdate("Tue, 15 Nov 1994 12:45:26 GMT") + Time.utc(1999, 12, 31, 23, 59, 59).should == Time.httpdate("Fri, 31 Dec 1999 23:59:59 GMT") + end +end diff --git a/spec/rubyspec/library/time/iso8601_spec.rb b/spec/rubyspec/library/time/iso8601_spec.rb new file mode 100644 index 0000000000..5f324e5ac6 --- /dev/null +++ b/spec/rubyspec/library/time/iso8601_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/xmlschema', __FILE__) +require 'time' + +describe "Time.xmlschema" do + it_behaves_like :time_xmlschema, :iso8601 +end diff --git a/spec/rubyspec/library/time/rfc2822_spec.rb b/spec/rubyspec/library/time/rfc2822_spec.rb new file mode 100644 index 0000000000..ac3950440f --- /dev/null +++ b/spec/rubyspec/library/time/rfc2822_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rfc2822', __FILE__) +require 'time' + +describe "Time.rfc2822" do + it_behaves_like :time_rfc2822, :rfc2822 +end diff --git a/spec/rubyspec/library/time/rfc822_spec.rb b/spec/rubyspec/library/time/rfc822_spec.rb new file mode 100644 index 0000000000..969050d5ac --- /dev/null +++ b/spec/rubyspec/library/time/rfc822_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/rfc2822', __FILE__) +require 'time' + +describe "Time.rfc822" do + it_behaves_like :time_rfc2822, :rfc822 +end diff --git a/spec/rubyspec/library/time/shared/rfc2822.rb b/spec/rubyspec/library/time/shared/rfc2822.rb new file mode 100644 index 0000000000..b7bf0fb5f5 --- /dev/null +++ b/spec/rubyspec/library/time/shared/rfc2822.rb @@ -0,0 +1,65 @@ +describe :time_rfc2822, shared: true do + it "parses RFC-822 strings" do + t1 = (Time.utc(1976, 8, 26, 14, 30) + 4 * 3600) + t2 = Time.rfc2822("26 Aug 76 14:30 EDT") + t1.should == t2 + + t3 = Time.utc(1976, 8, 27, 9, 32) + 7 * 3600 + t4 = Time.rfc2822("27 Aug 76 09:32 PDT") + t3.should == t4 + end + + it "parses RFC-2822 strings" do + t1 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 + t2 = Time.rfc2822("Fri, 21 Nov 1997 09:55:06 -0600") + t1.should == t2 + + t3 = Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600 + t4 = Time.rfc2822("Tue, 1 Jul 2003 10:52:37 +0200") + t3.should == t4 + + t5 = Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600 + t6 = Time.rfc2822("Fri, 21 Nov 1997 10:01:10 -0600") + t5.should == t6 + + t7 = Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600 + t8 = Time.rfc2822("Fri, 21 Nov 1997 11:00:00 -0600") + t7.should == t8 + + t9 = Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600 + t10 = Time.rfc2822("Mon, 24 Nov 1997 14:22:01 -0800") + t9.should == t10 + + begin + Time.at(-1) + rescue ArgumentError + # ignore + else + t11 = Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60 + t12 = Time.rfc2822("Thu, 13 Feb 1969 23:32:54 -0330") + t11.should == t12 + + t13 = Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60 + t14 = Time.rfc2822(" Thu, + 13 + Feb + 1969 + 23:32 + -0330 (Newfoundland Time)") + t13.should == t14 + end + + t15 = Time.utc(1997, 11, 21, 9, 55, 6) + t16 = Time.rfc2822("21 Nov 97 09:55:06 GMT") + t15.should == t16 + + t17 = Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600 + t18 = Time.rfc2822("Fri, 21 Nov 1997 09 : 55 : 06 -0600") + t17.should == t18 + + lambda { + # inner comment is not supported. + Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600") + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/time/shared/xmlschema.rb b/spec/rubyspec/library/time/shared/xmlschema.rb new file mode 100644 index 0000000000..44d33cda7e --- /dev/null +++ b/spec/rubyspec/library/time/shared/xmlschema.rb @@ -0,0 +1,53 @@ +describe :time_xmlschema, shared: true do + it "parses ISO-8601 strings" do + t = Time.utc(1985, 4, 12, 23, 20, 50, 520000) + s = "1985-04-12T23:20:50.52Z" + t.should == Time.xmlschema(s) + #s.should == t.xmlschema(2) + + t = Time.utc(1996, 12, 20, 0, 39, 57) + s = "1996-12-19T16:39:57-08:00" + t.should == Time.xmlschema(s) + # There is no way to generate time string with arbitrary timezone. + s = "1996-12-20T00:39:57Z" + t.should == Time.xmlschema(s) + #assert_equal(s, t.xmlschema) + + t = Time.utc(1990, 12, 31, 23, 59, 60) + s = "1990-12-31T23:59:60Z" + t.should == Time.xmlschema(s) + # leap second is representable only if timezone file has it. + s = "1990-12-31T15:59:60-08:00" + t.should == Time.xmlschema(s) + + begin + Time.at(-1) + rescue ArgumentError + # ignore + else + t = Time.utc(1937, 1, 1, 11, 40, 27, 870000) + s = "1937-01-01T12:00:27.87+00:20" + t.should == Time.xmlschema(s) + end + + # more + + # (Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600).should == Time.xmlschema("1999-05-31T13:20:00-05:00") + # (Time.local(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00") + # (Time.utc(2000, 1, 20, 12, 0, 0)).should == Time.xmlschema("2000-01-20T12:00:00Z") + # (Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600).should == Time.xmlschema("2000-01-20T12:00:00+12:00") + # (Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600).should == Time.xmlschema("2000-01-20T12:00:00-13:00") + # (Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600).should == Time.xmlschema("2000-03-04T23:00:00+03:00") + # (Time.utc(2000, 3, 4, 20, 0, 0)).should == Time.xmlschema("2000-03-04T20:00:00Z") + # (Time.local(2000, 1, 15, 0, 0, 0)).should == Time.xmlschema("2000-01-15T00:00:00") + # (Time.local(2000, 2, 15, 0, 0, 0)).should == Time.xmlschema("2000-02-15T00:00:00") + # (Time.local(2000, 1, 15, 12, 0, 0)).should == Time.xmlschema("2000-01-15T12:00:00") + # (Time.utc(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00Z") + # (Time.local(2000, 1, 1, 12, 0, 0)).should == Time.xmlschema("2000-01-01T12:00:00") + # (Time.utc(1999, 12, 31, 23, 0, 0)).should == Time.xmlschema("1999-12-31T23:00:00Z") + # (Time.local(2000, 1, 16, 12, 0, 0)).should == Time.xmlschema("2000-01-16T12:00:00") + # (Time.local(2000, 1, 16, 0, 0, 0)).should == Time.xmlschema("2000-01-16T00:00:00") + # (Time.utc(2000, 1, 12, 12, 13, 14)).should == Time.xmlschema("2000-01-12T12:13:14Z") + # (Time.utc(2001, 4, 17, 19, 23, 17, 300000)).should == Time.xmlschema("2001-04-17T19:23:17.3Z") + end +end diff --git a/spec/rubyspec/library/time/to_date_spec.rb b/spec/rubyspec/library/time/to_date_spec.rb new file mode 100644 index 0000000000..855dc796cf --- /dev/null +++ b/spec/rubyspec/library/time/to_date_spec.rb @@ -0,0 +1,42 @@ + +require File.expand_path('../../../spec_helper', __FILE__) +require 'time' + +describe "Time#to_date" do + it "yields accurate julian date for ambiguous pre-Gregorian reform value" do + Time.utc(1582, 10, 4).to_date.jd.should == Date::ITALY - 11 # 2299150j + end + + it "yields accurate julian date for Julian-Gregorian gap value" do + Time.utc(1582, 10, 14).to_date.jd.should == Date::ITALY - 1 # 2299160j + end + + it "yields accurate julian date for post-Gregorian reform value" do + Time.utc(1582, 10, 15).to_date.jd.should == Date::ITALY # 2299161j + end + + it "yields same julian day regardless of UTC time value" do + Time.utc(1582, 10, 15, 00, 00, 00).to_date.jd.should == Date::ITALY + Time.utc(1582, 10, 15, 23, 59, 59).to_date.jd.should == Date::ITALY + end + + it "yields same julian day regardless of local time or zone" do + + with_timezone("Pacific/Pago_Pago", -11) do + Time.local(1582, 10, 15, 00, 00, 00).to_date.jd.should == Date::ITALY + Time.local(1582, 10, 15, 23, 59, 59).to_date.jd.should == Date::ITALY + end + + with_timezone("Asia/Kamchatka", +12) do + Time.local(1582, 10, 15, 00, 00, 00).to_date.jd.should == Date::ITALY + Time.local(1582, 10, 15, 23, 59, 59).to_date.jd.should == Date::ITALY + end + + end + + it "yields date with default Calendar reform day" do + Time.utc(1582, 10, 4).to_date.start.should == Date::ITALY + Time.utc(1582, 10, 14).to_date.start.should == Date::ITALY + Time.utc(1582, 10, 15).to_date.start.should == Date::ITALY + end +end diff --git a/spec/rubyspec/library/time/xmlschema_spec.rb b/spec/rubyspec/library/time/xmlschema_spec.rb new file mode 100644 index 0000000000..14ea081d6e --- /dev/null +++ b/spec/rubyspec/library/time/xmlschema_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/xmlschema', __FILE__) +require 'time' + +describe "Time.xmlschema" do + it_behaves_like :time_xmlschema, :xmlschema +end diff --git a/spec/rubyspec/library/timeout/error_spec.rb b/spec/rubyspec/library/timeout/error_spec.rb new file mode 100644 index 0000000000..37462ecd49 --- /dev/null +++ b/spec/rubyspec/library/timeout/error_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'timeout' + +describe "Timeout::Error" do + it "is a subclass of RuntimeError" do + RuntimeError.should be_ancestor_of(Timeout::Error) + end +end diff --git a/spec/rubyspec/library/timeout/timeout_spec.rb b/spec/rubyspec/library/timeout/timeout_spec.rb new file mode 100644 index 0000000000..2eccd02097 --- /dev/null +++ b/spec/rubyspec/library/timeout/timeout_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'timeout' + +describe "Timeout.timeout" do + it "raises Timeout::Error when it times out with no specified error type" do + lambda { + Timeout.timeout(1) do + sleep 3 + end + }.should raise_error(Timeout::Error) + end + + it "raises specified error type when it times out" do + lambda do + Timeout.timeout(1, StandardError) do + sleep 3 + end + end.should raise_error(StandardError) + end + + it "does not wait too long" do + before_time = Time.now + lambda do + Timeout.timeout(1, StandardError) do + sleep 3 + end + end.should raise_error(StandardError) + + (Time.now - before_time).should be_close(1.0, 0.5) + end + + it "returns back the last value in the block" do + Timeout.timeout(1) do + 42 + end.should == 42 + end +end diff --git a/spec/rubyspec/library/tmpdir/dir/mktmpdir_spec.rb b/spec/rubyspec/library/tmpdir/dir/mktmpdir_spec.rb new file mode 100644 index 0000000000..3459a47fe6 --- /dev/null +++ b/spec/rubyspec/library/tmpdir/dir/mktmpdir_spec.rb @@ -0,0 +1,117 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require "tmpdir" + +describe "Dir.mktmpdir when passed no arguments" do + after :each do + Dir.rmdir @tmpdir if File.directory? @tmpdir + end + + it "returns the path to the created tmp-dir" do + Dir.stub!(:mkdir) + Dir.should_receive(:tmpdir).and_return("/tmp") + @tmpdir = Dir.mktmpdir + @tmpdir.should =~ /^\/tmp\// + end + + it "creates a new writable directory in the path provided by Dir.tmpdir" do + Dir.should_receive(:tmpdir).and_return(tmp("")) + @tmpdir = Dir.mktmpdir + File.directory?(@tmpdir).should be_true + File.writable?(@tmpdir).should be_true + end +end + +describe "Dir.mktmpdir when passed a block" do + before :each do + @real_tmp_root = tmp('') + Dir.stub!(:tmpdir).and_return(@real_tmp_root) + FileUtils.stub!(:remove_entry) + FileUtils.stub!(:remove_entry_secure) + end + + after :each do + Dir.rmdir @tmpdir if File.directory? @tmpdir + end + + it "yields the path to the passed block" do + Dir.stub!(:mkdir) + called = nil + Dir.mktmpdir do |path| + @tmpdir = path + called = true + path.start_with?(@real_tmp_root).should be_true + end + called.should be_true + end + + it "creates the tmp-dir before yielding" do + Dir.should_receive(:tmpdir).and_return(tmp("")) + Dir.mktmpdir do |path| + @tmpdir = path + File.directory?(path).should be_true + File.writable?(path).should be_true + end + end + + it "removes the tmp-dir after executing the block" do + Dir.stub!(:mkdir) + Dir.mktmpdir do |path| + @tmpdir = path + FileUtils.should_receive(:remove_entry).with(path) + end + end + + it "returns the blocks return value" do + Dir.stub!(:mkdir) + result = Dir.mktmpdir do |path| + @tmpdir = path + :test + end + result.should equal(:test) + end +end + +describe "Dir.mktmpdir when passed [String]" do + before :each do + Dir.stub!(:mkdir) + Dir.stub!(:tmpdir).and_return("/tmp") + end + + after :each do + Dir.rmdir @tmpdir if File.directory? @tmpdir + end + + it "uses the passed String as a prefix to the tmp-directory" do + prefix = "before" + @tmpdir = Dir.mktmpdir(prefix) + @tmpdir.should =~ /^\/tmp\/#{prefix}/ + end +end + +describe "Dir.mktmpdir when passed [Array]" do + before :each do + Dir.stub!(:mkdir) + Dir.stub!(:tmpdir).and_return("/tmp") + FileUtils.stub!(:remove_entry_secure) + end + + after :each do + Dir.rmdir @tmpdir if File.directory? @tmpdir + end + + it "uses the first element of the passed Array as a prefix and the scond element as a suffix to the tmp-directory" do + prefix = "before" + suffix = "after" + + @tmpdir = Dir.mktmpdir([prefix, suffix]) + @tmpdir.should =~ /#{suffix}$/ + end +end + +describe "Dir.mktmpdir when passed [Object]" do + it "raises an ArgumentError" do + lambda { Dir.mktmpdir(Object.new) }.should raise_error(ArgumentError) + lambda { Dir.mktmpdir(:symbol) }.should raise_error(ArgumentError) + lambda { Dir.mktmpdir(10) }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/tmpdir/dir/tmpdir_spec.rb b/spec/rubyspec/library/tmpdir/dir/tmpdir_spec.rb new file mode 100644 index 0000000000..19d54e03b3 --- /dev/null +++ b/spec/rubyspec/library/tmpdir/dir/tmpdir_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require "tmpdir" + +describe "Dir.tmpdir" do + it "returns the path to a writable and readable directory" do + dir = Dir.tmpdir + File.directory?(dir).should be_true + File.writable?(dir).should be_true + end +end diff --git a/spec/rubyspec/library/uri/decode_www_form_component_spec.rb b/spec/rubyspec/library/uri/decode_www_form_component_spec.rb new file mode 100644 index 0000000000..04cc634947 --- /dev/null +++ b/spec/rubyspec/library/uri/decode_www_form_component_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI.decode_www_form_component" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/decode_www_form_spec.rb b/spec/rubyspec/library/uri/decode_www_form_spec.rb new file mode 100644 index 0000000000..d4854a6ece --- /dev/null +++ b/spec/rubyspec/library/uri/decode_www_form_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI.decode_www_form" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/encode_www_form_component_spec.rb b/spec/rubyspec/library/uri/encode_www_form_component_spec.rb new file mode 100644 index 0000000000..753b6d50d0 --- /dev/null +++ b/spec/rubyspec/library/uri/encode_www_form_component_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI.encode_www_form_component" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/encode_www_form_spec.rb b/spec/rubyspec/library/uri/encode_www_form_spec.rb new file mode 100644 index 0000000000..b72b928344 --- /dev/null +++ b/spec/rubyspec/library/uri/encode_www_form_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI.encode_www_form" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/eql_spec.rb b/spec/rubyspec/library/uri/eql_spec.rb new file mode 100644 index 0000000000..2bbc5291e9 --- /dev/null +++ b/spec/rubyspec/library/uri/eql_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/normalization', __FILE__) +require File.expand_path('../shared/eql', __FILE__) +require 'uri' + +describe "URI#eql?" do + it_behaves_like :uri_eql, :eql? + + it_behaves_like :uri_eql_against_other_types, :eql? +end diff --git a/spec/rubyspec/library/uri/equality_spec.rb b/spec/rubyspec/library/uri/equality_spec.rb new file mode 100644 index 0000000000..07d48a9583 --- /dev/null +++ b/spec/rubyspec/library/uri/equality_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/normalization', __FILE__) +require File.expand_path('../shared/eql', __FILE__) +require 'uri' + +describe "URI#==" do + it "ignores capitalization of host names" do + URI("http://exAMPLE.cOm").should == URI("http://example.com") + end + + it "ignores capitalization of scheme" do + URI("hTTp://example.com").should == URI("http://example.com") + end + + it "treats a blank path and a path of '/' as the same" do + URI("http://example.com").should == URI("http://example.com/") + end + + it "is case sensitive in all components of the URI but the host and scheme" do + URI("http://example.com/paTH").should_not == URI("http://example.com/path") + URI("http://uSer@example.com").should_not == URI("http://user@example.com") + URI("http://example.com/path?quERy").should_not == URI("http://example.com/path?query") + URI("http://example.com/#fragMENT").should_not == URI("http://example.com/#fragment") + end + + it "differentiates based on port number" do + URI("http://example.com:8080").should_not == URI("http://example.com") + end + + # Note: The previous tests will be included in following ones + + it_behaves_like :uri_eql, :== + + it_behaves_like :uri_eql_against_other_types, :== + + quarantine! do # Quarantined until redmine:2542 is accepted + it "returns true only if the normalized forms are equivalent" do + URISpec::NORMALIZED_FORMS.each do |form| + normal_uri = URI(form[:normalized]) + form[:equivalent].each do |same| + URI(same).should == normal_uri + end + end + end + end +end diff --git a/spec/rubyspec/library/uri/escape/decode_spec.rb b/spec/rubyspec/library/uri/escape/decode_spec.rb new file mode 100644 index 0000000000..34d3e787c4 --- /dev/null +++ b/spec/rubyspec/library/uri/escape/decode_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Escape#decode" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/escape/encode_spec.rb b/spec/rubyspec/library/uri/escape/encode_spec.rb new file mode 100644 index 0000000000..edde60b3cd --- /dev/null +++ b/spec/rubyspec/library/uri/escape/encode_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Escape#encode" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/escape/escape_spec.rb b/spec/rubyspec/library/uri/escape/escape_spec.rb new file mode 100644 index 0000000000..3c6b957b18 --- /dev/null +++ b/spec/rubyspec/library/uri/escape/escape_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Escape#escape" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/escape/unescape_spec.rb b/spec/rubyspec/library/uri/escape/unescape_spec.rb new file mode 100644 index 0000000000..6a7165a0d3 --- /dev/null +++ b/spec/rubyspec/library/uri/escape/unescape_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Escape#unescape" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/extract_spec.rb b/spec/rubyspec/library/uri/extract_spec.rb new file mode 100644 index 0000000000..7b660851b6 --- /dev/null +++ b/spec/rubyspec/library/uri/extract_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI.extract" do + it "behaves according to its documentation" do + URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.").should == ["http://foo.example.org/bla", "mailto:test@example.com"] + end + + it "treats contiguous URIs as a single URI" do + URI.extract('http://example.jphttp://example.jp').should == ['http://example.jphttp://example.jp'] + end + + it "treats pretty much anything with a colon as a URI" do + URI.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').should == ['From:', 'mailto:xxx@xxx.xxx.xxx]'] + end + + it "wraps a URI string in an array" do + URI.extract("http://github.com/brixen/rubyspec/tree/master").should == ["http://github.com/brixen/rubyspec/tree/master"] + end + + it "pulls a variety of protocol URIs from a string" do + URI.extract("this is a string, it has http://rubini.us/ in it").should == ["http://rubini.us/"] + URI.extract("mailto:spambait@example.com").should == ["mailto:spambait@example.com"] + URI.extract("ftp://ruby-lang.org/").should == ["ftp://ruby-lang.org/"] + URI.extract("https://mail.google.com").should == ["https://mail.google.com"] + URI.extract("anything://example.com/").should == ["anything://example.com/"] + end + + it "pulls all URIs within a string in order into an array when a block is not given" do + URI.extract("1.3. Example URI + + The following examples illustrate URI that are in common use. + + ftp://ftp.is.co.za/rfc/rfc1808.txt + -- ftp scheme for File Transfer Protocol services + + gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles + -- gopher scheme for Gopher and Gopher+ Protocol services + + http://www.math.uio.no/faq/compression-faq/part1.html + -- http scheme for Hypertext Transfer Protocol services + + mailto:mduerst@ifi.unizh.ch + -- mailto scheme for electronic mail addresses + + news:comp.infosystems.www.servers.unix + -- news scheme for USENET news groups and articles + + telnet://melvyl.ucop.edu/ + -- telnet scheme for interactive services via the TELNET Protocol + ").should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch","news:comp.infosystems.www.servers.unix","telnet://melvyl.ucop.edu/"] + end + + it "yields each URI in the given string in order to a block, if given, and returns nil" do + results = ["http://foo.example.org/bla", "mailto:test@example.com"] + URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") {|uri| + uri.should == results.shift + }.should == nil + results.should == [] + end + + it "allows the user to specify a list of acceptable protocols of URIs to scan for" do + URI.extract("1.3. Example URI + + The following examples illustrate URI that are in common use. + + ftp://ftp.is.co.za/rfc/rfc1808.txt + -- ftp scheme for File Transfer Protocol services + + gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles + -- gopher scheme for Gopher and Gopher+ Protocol services + + http://www.math.uio.no/faq/compression-faq/part1.html + -- http scheme for Hypertext Transfer Protocol services + + mailto:mduerst@ifi.unizh.ch + -- mailto scheme for electronic mail addresses + + news:comp.infosystems.www.servers.unix + -- news scheme for USENET news groups and articles + + telnet://melvyl.ucop.edu/ + -- telnet scheme for interactive services via the TELNET Protocol + ", ["http","ftp","mailto"]).should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch"] + end +end diff --git a/spec/rubyspec/library/uri/fixtures/classes.rb b/spec/rubyspec/library/uri/fixtures/classes.rb new file mode 100644 index 0000000000..e1179307cc --- /dev/null +++ b/spec/rubyspec/library/uri/fixtures/classes.rb @@ -0,0 +1,11 @@ +require 'uri' + +module URISpec + def self.components(uri) + result = {} + uri.component.each do |component| + result[component] = uri.send(component) + end + result + end +end diff --git a/spec/rubyspec/library/uri/fixtures/normalization.rb b/spec/rubyspec/library/uri/fixtures/normalization.rb new file mode 100644 index 0000000000..cbc26c9b48 --- /dev/null +++ b/spec/rubyspec/library/uri/fixtures/normalization.rb @@ -0,0 +1,54 @@ +module URISpec + # Not an exhaustive list. Refer to rfc3986 + NORMALIZED_FORMS = [ + { normalized: "http://example.com/", + equivalent: %w{ hTTp://example.com/ + http://exaMple.com/ + http://exa%4dple.com/ + http://exa%4Dple.com/ + http://exa%6dple.com/ + http://exa%6Dple.com/ + http://@example.com/ + http://example.com:/ + http://example.com:80/ + http://example.com + }, + different: %w{ http://example.com/# + http://example.com/? + http://example.com:8888/ + http:///example.com + http:example.com + https://example.com/ + }, + }, + { normalized: "http://example.com/index.html", + equivalent: %w{ http://example.com/index.ht%6dl + http://example.com/index.ht%6Dl + }, + different: %w{ http://example.com/index.hTMl + http://example.com/index.ht%4dl + http://example.com/index + http://example.com/ + http://example.com/ + }, + }, + { normalized: "http://example.com/x?y#z", + equivalent: %w{ http://example.com/x?y#%7a + http://example.com/x?y#%7A + http://example.com/x?%79#z + }, + different: %w{ http://example.com/x?Y#z + http://example.com/x?y#Z + http://example.com/x?y=#z + http://example.com/x?y + http://example.com/x#z + }, + }, + { normalized: "http://example.com/x?q=a%20b", + equivalent: %w{ + }, + different: %w{ http://example.com/x?q=a+b + }, + }, + ] +end diff --git a/spec/rubyspec/library/uri/ftp/build_spec.rb b/spec/rubyspec/library/uri/ftp/build_spec.rb new file mode 100644 index 0000000000..c7765e2868 --- /dev/null +++ b/spec/rubyspec/library/uri/ftp/build_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::FTP.build" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ftp/merge_spec.rb b/spec/rubyspec/library/uri/ftp/merge_spec.rb new file mode 100644 index 0000000000..b766e992ca --- /dev/null +++ b/spec/rubyspec/library/uri/ftp/merge_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::FTP#merge" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ftp/new2_spec.rb b/spec/rubyspec/library/uri/ftp/new2_spec.rb new file mode 100644 index 0000000000..a43916af6a --- /dev/null +++ b/spec/rubyspec/library/uri/ftp/new2_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::FTP.new2" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ftp/path_spec.rb b/spec/rubyspec/library/uri/ftp/path_spec.rb new file mode 100644 index 0000000000..9e1a00602f --- /dev/null +++ b/spec/rubyspec/library/uri/ftp/path_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::FTP#path=" do + before :each do + @url = URI.parse('ftp://example.com') + end + + it "does not require a leading /" do + @url.path = 'foo' + @url.path.should == 'foo' + end + + it "does not strip the leading /" do + @url.path = '/foo' + @url.path.should == '/foo' + end +end + +describe "URI::FTP#path" do + it "unescapes the leading /" do + url = URI.parse('ftp://example.com/%2Ffoo') + + url.path.should == '/foo' + end +end diff --git a/spec/rubyspec/library/uri/ftp/set_typecode_spec.rb b/spec/rubyspec/library/uri/ftp/set_typecode_spec.rb new file mode 100644 index 0000000000..b815bc8740 --- /dev/null +++ b/spec/rubyspec/library/uri/ftp/set_typecode_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::FTP#set_typecode" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ftp/to_s_spec.rb b/spec/rubyspec/library/uri/ftp/to_s_spec.rb new file mode 100644 index 0000000000..e4e2832e86 --- /dev/null +++ b/spec/rubyspec/library/uri/ftp/to_s_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + + +describe "URI::FTP#to_s" do + before :each do + @url = URI.parse('ftp://example.com') + end + + it "escapes the leading /" do + @url.path = '/foo' + + @url.to_s.should == 'ftp://example.com/%2Ffoo' + end +end diff --git a/spec/rubyspec/library/uri/ftp/typecode_spec.rb b/spec/rubyspec/library/uri/ftp/typecode_spec.rb new file mode 100644 index 0000000000..b298c2ae98 --- /dev/null +++ b/spec/rubyspec/library/uri/ftp/typecode_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::FTP#typecode" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::FTP#typecode=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/absolute_spec.rb b/spec/rubyspec/library/uri/generic/absolute_spec.rb new file mode 100644 index 0000000000..4f6526b827 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/absolute_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#absolute" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#absolute?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/build2_spec.rb b/spec/rubyspec/library/uri/generic/build2_spec.rb new file mode 100644 index 0000000000..0b9a6788f6 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/build2_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic.build2" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/build_spec.rb b/spec/rubyspec/library/uri/generic/build_spec.rb new file mode 100644 index 0000000000..6fa5d6ac55 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/build_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic.build" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/coerce_spec.rb b/spec/rubyspec/library/uri/generic/coerce_spec.rb new file mode 100644 index 0000000000..1b1a040f63 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/coerce_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#coerce" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/component_ary_spec.rb b/spec/rubyspec/library/uri/generic/component_ary_spec.rb new file mode 100644 index 0000000000..3244073e0e --- /dev/null +++ b/spec/rubyspec/library/uri/generic/component_ary_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#component_ary" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/component_spec.rb b/spec/rubyspec/library/uri/generic/component_spec.rb new file mode 100644 index 0000000000..6fb83d7796 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/component_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#component" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic.component" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/default_port_spec.rb b/spec/rubyspec/library/uri/generic/default_port_spec.rb new file mode 100644 index 0000000000..d1e0ce2d3f --- /dev/null +++ b/spec/rubyspec/library/uri/generic/default_port_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#default_port" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic.default_port" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/eql_spec.rb b/spec/rubyspec/library/uri/generic/eql_spec.rb new file mode 100644 index 0000000000..65f9204a19 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/eql_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#eql?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/equal_value_spec.rb b/spec/rubyspec/library/uri/generic/equal_value_spec.rb new file mode 100644 index 0000000000..f41b202498 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/equal_value_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#==" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/fragment_spec.rb b/spec/rubyspec/library/uri/generic/fragment_spec.rb new file mode 100644 index 0000000000..de6f4e078d --- /dev/null +++ b/spec/rubyspec/library/uri/generic/fragment_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#fragment" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#fragment=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/hash_spec.rb b/spec/rubyspec/library/uri/generic/hash_spec.rb new file mode 100644 index 0000000000..3410558067 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/hash_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#hash" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/hierarchical_spec.rb b/spec/rubyspec/library/uri/generic/hierarchical_spec.rb new file mode 100644 index 0000000000..1c90dc4f8f --- /dev/null +++ b/spec/rubyspec/library/uri/generic/hierarchical_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#hierarchical?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/host_spec.rb b/spec/rubyspec/library/uri/generic/host_spec.rb new file mode 100644 index 0000000000..6fb6c1c36a --- /dev/null +++ b/spec/rubyspec/library/uri/generic/host_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#host" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#host=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/inspect_spec.rb b/spec/rubyspec/library/uri/generic/inspect_spec.rb new file mode 100644 index 0000000000..696c3308d4 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/inspect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#inspect" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/merge_spec.rb b/spec/rubyspec/library/uri/generic/merge_spec.rb new file mode 100644 index 0000000000..63642197e1 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/merge_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#merge" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#merge!" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/minus_spec.rb b/spec/rubyspec/library/uri/generic/minus_spec.rb new file mode 100644 index 0000000000..3426a6068b --- /dev/null +++ b/spec/rubyspec/library/uri/generic/minus_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#-" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/normalize_spec.rb b/spec/rubyspec/library/uri/generic/normalize_spec.rb new file mode 100644 index 0000000000..ac02b644d0 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/normalize_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#normalize" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#normalize!" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/opaque_spec.rb b/spec/rubyspec/library/uri/generic/opaque_spec.rb new file mode 100644 index 0000000000..f418c220f2 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/opaque_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#opaque" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#opaque=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/password_spec.rb b/spec/rubyspec/library/uri/generic/password_spec.rb new file mode 100644 index 0000000000..087db60fb9 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/password_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#password" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#password=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/path_spec.rb b/spec/rubyspec/library/uri/generic/path_spec.rb new file mode 100644 index 0000000000..5ea60b5418 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/path_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#path" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#path=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/plus_spec.rb b/spec/rubyspec/library/uri/generic/plus_spec.rb new file mode 100644 index 0000000000..3d1c031022 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/plus_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#+" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/port_spec.rb b/spec/rubyspec/library/uri/generic/port_spec.rb new file mode 100644 index 0000000000..148e84ff14 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/port_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#port" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#port=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/query_spec.rb b/spec/rubyspec/library/uri/generic/query_spec.rb new file mode 100644 index 0000000000..945fdc06a3 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/query_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#query" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#query=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/registry_spec.rb b/spec/rubyspec/library/uri/generic/registry_spec.rb new file mode 100644 index 0000000000..6a48b25465 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/registry_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#registry" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#registry=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/relative_spec.rb b/spec/rubyspec/library/uri/generic/relative_spec.rb new file mode 100644 index 0000000000..09730fa4eb --- /dev/null +++ b/spec/rubyspec/library/uri/generic/relative_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#relative?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/route_from_spec.rb b/spec/rubyspec/library/uri/generic/route_from_spec.rb new file mode 100644 index 0000000000..03321cbf0d --- /dev/null +++ b/spec/rubyspec/library/uri/generic/route_from_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#route_from" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/route_to_spec.rb b/spec/rubyspec/library/uri/generic/route_to_spec.rb new file mode 100644 index 0000000000..a12e1f7649 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/route_to_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#route_to" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/scheme_spec.rb b/spec/rubyspec/library/uri/generic/scheme_spec.rb new file mode 100644 index 0000000000..fa3dfcb8aa --- /dev/null +++ b/spec/rubyspec/library/uri/generic/scheme_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#scheme" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#scheme=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/select_spec.rb b/spec/rubyspec/library/uri/generic/select_spec.rb new file mode 100644 index 0000000000..5cc104f5dd --- /dev/null +++ b/spec/rubyspec/library/uri/generic/select_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#select" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_fragment_spec.rb b/spec/rubyspec/library/uri/generic/set_fragment_spec.rb new file mode 100644 index 0000000000..cebad46585 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_fragment_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_fragment" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_host_spec.rb b/spec/rubyspec/library/uri/generic/set_host_spec.rb new file mode 100644 index 0000000000..357b7a6889 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_host_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_host" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_opaque_spec.rb b/spec/rubyspec/library/uri/generic/set_opaque_spec.rb new file mode 100644 index 0000000000..afd6597675 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_opaque_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_opaque" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_password_spec.rb b/spec/rubyspec/library/uri/generic/set_password_spec.rb new file mode 100644 index 0000000000..15b4fdc37d --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_password_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_password" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_path_spec.rb b/spec/rubyspec/library/uri/generic/set_path_spec.rb new file mode 100644 index 0000000000..b4366d789c --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_path_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_path" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_port_spec.rb b/spec/rubyspec/library/uri/generic/set_port_spec.rb new file mode 100644 index 0000000000..aa65bb96e3 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_port_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_port" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_query_spec.rb b/spec/rubyspec/library/uri/generic/set_query_spec.rb new file mode 100644 index 0000000000..b1c25e56ca --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_query_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_query" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_registry_spec.rb b/spec/rubyspec/library/uri/generic/set_registry_spec.rb new file mode 100644 index 0000000000..602f868ac1 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_registry_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_registry" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_scheme_spec.rb b/spec/rubyspec/library/uri/generic/set_scheme_spec.rb new file mode 100644 index 0000000000..e1a94c5b1a --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_scheme_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_scheme" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_user_spec.rb b/spec/rubyspec/library/uri/generic/set_user_spec.rb new file mode 100644 index 0000000000..36a6ac9e85 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_user_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_user" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/set_userinfo_spec.rb b/spec/rubyspec/library/uri/generic/set_userinfo_spec.rb new file mode 100644 index 0000000000..cbe80d9809 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/set_userinfo_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#set_userinfo" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/to_s_spec.rb b/spec/rubyspec/library/uri/generic/to_s_spec.rb new file mode 100644 index 0000000000..c0a0f803ef --- /dev/null +++ b/spec/rubyspec/library/uri/generic/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#to_s" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/use_registry_spec.rb b/spec/rubyspec/library/uri/generic/use_registry_spec.rb new file mode 100644 index 0000000000..4e7ae6a5cf --- /dev/null +++ b/spec/rubyspec/library/uri/generic/use_registry_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic.use_registry" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/user_spec.rb b/spec/rubyspec/library/uri/generic/user_spec.rb new file mode 100644 index 0000000000..b785ef6879 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/user_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#user" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#user=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/generic/userinfo_spec.rb b/spec/rubyspec/library/uri/generic/userinfo_spec.rb new file mode 100644 index 0000000000..5d0fc50b65 --- /dev/null +++ b/spec/rubyspec/library/uri/generic/userinfo_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Generic#userinfo" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::Generic#userinfo=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/http/build_spec.rb b/spec/rubyspec/library/uri/http/build_spec.rb new file mode 100644 index 0000000000..85103167e0 --- /dev/null +++ b/spec/rubyspec/library/uri/http/build_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::HTTP.build" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/http/request_uri_spec.rb b/spec/rubyspec/library/uri/http/request_uri_spec.rb new file mode 100644 index 0000000000..85d89aba77 --- /dev/null +++ b/spec/rubyspec/library/uri/http/request_uri_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::HTTP.request_uri" do + it "returns a string of the path + query" do + URI("http://reddit.com/r/ruby/").request_uri.should == "/r/ruby/" + URI("http://reddit.com/r/ruby/search?q=rubinius").request_uri.should == "/r/ruby/search?q=rubinius" + end + + it "returns '/' if the path of the URI is blank" do + URI("http://ruby.reddit.com").request_uri.should == "/" + end +end +describe "URI::HTTP#request_uri" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/join_spec.rb b/spec/rubyspec/library/uri/join_spec.rb new file mode 100644 index 0000000000..681ba45cc7 --- /dev/null +++ b/spec/rubyspec/library/uri/join_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI.join" do + it "returns a URI object of the concatenation of a protocol and domain, and a path" do + URI.join("http://localhost/","main.rbx").should == URI.parse("http://localhost/main.rbx") + end + + it "accepts URI objects" do + URI.join(URI("http://localhost/"),"main.rbx").should == URI.parse("http://localhost/main.rbx") + URI.join("http://localhost/",URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") + URI.join(URI("http://localhost/"),URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") + end + + it "accepts string-like arguments with to_str" do + str = mock('string-like') + str.should_receive(:to_str).and_return("http://ruby-lang.org") + str2 = mock('string-like also') + str2.should_receive(:to_str).and_return("foo/bar") + URI.join(str, str2).should == URI.parse("http://ruby-lang.org/foo/bar") + end + + it "raises an error if given no argument" do + lambda{ URI.join }.should raise_error + end + + it "doesn't create redundant '/'s" do + URI.join("http://localhost/", "/main.rbx").should == URI.parse("http://localhost/main.rbx") + end + + it "discards arguments given before an absolute uri" do + URI.join("http://localhost/a/b/c/d", "http://ruby-lang.com/foo", "bar").should == URI.parse("http://ruby-lang.com/bar") + end + + it "resolves .. in paths" do + URI.join("http://localhost/a/b/c/d", "../../e/f", "g/h/../i").to_s.should == "http://localhost/a/e/g/i" + end +end + + +# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar')) +# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar')) +# assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/')) +# +# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz')) +# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz')) +# assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/')) +# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge')) +# +# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz')) +# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) +# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) +# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) +# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) diff --git a/spec/rubyspec/library/uri/ldap/attributes_spec.rb b/spec/rubyspec/library/uri/ldap/attributes_spec.rb new file mode 100644 index 0000000000..2309de7c62 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/attributes_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#attributes" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::LDAP#attributes=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/build_spec.rb b/spec/rubyspec/library/uri/ldap/build_spec.rb new file mode 100644 index 0000000000..99e2611b1f --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/build_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP.build" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/dn_spec.rb b/spec/rubyspec/library/uri/ldap/dn_spec.rb new file mode 100644 index 0000000000..b1371611d3 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/dn_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#dn" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::LDAP#dn=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/extensions_spec.rb b/spec/rubyspec/library/uri/ldap/extensions_spec.rb new file mode 100644 index 0000000000..2d9b09e6a7 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/extensions_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#extensions" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::LDAP#extensions=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/filter_spec.rb b/spec/rubyspec/library/uri/ldap/filter_spec.rb new file mode 100644 index 0000000000..1f996339db --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/filter_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#filter" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::LDAP#filter=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/hierarchical_spec.rb b/spec/rubyspec/library/uri/ldap/hierarchical_spec.rb new file mode 100644 index 0000000000..97c23a7f0c --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/hierarchical_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#hierarchical?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/scope_spec.rb b/spec/rubyspec/library/uri/ldap/scope_spec.rb new file mode 100644 index 0000000000..d4a02e08f0 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/scope_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#scope" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::LDAP#scope=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/set_attributes_spec.rb b/spec/rubyspec/library/uri/ldap/set_attributes_spec.rb new file mode 100644 index 0000000000..1bbcb34837 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/set_attributes_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#set_attributes" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/set_dn_spec.rb b/spec/rubyspec/library/uri/ldap/set_dn_spec.rb new file mode 100644 index 0000000000..abb640b585 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/set_dn_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#set_dn" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/set_extensions_spec.rb b/spec/rubyspec/library/uri/ldap/set_extensions_spec.rb new file mode 100644 index 0000000000..5cd2077aab --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/set_extensions_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#set_extensions" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/set_filter_spec.rb b/spec/rubyspec/library/uri/ldap/set_filter_spec.rb new file mode 100644 index 0000000000..f1b8e5e595 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/set_filter_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#set_filter" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/ldap/set_scope_spec.rb b/spec/rubyspec/library/uri/ldap/set_scope_spec.rb new file mode 100644 index 0000000000..5a0841cfd8 --- /dev/null +++ b/spec/rubyspec/library/uri/ldap/set_scope_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::LDAP#set_scope" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/build_spec.rb b/spec/rubyspec/library/uri/mailto/build_spec.rb new file mode 100644 index 0000000000..cb57f0c794 --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/build_spec.rb @@ -0,0 +1,98 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Mailto.build" do + it "conforms to the MatzRuby tests" do + ok = [] + bad = [] + + # RFC2368, 6. Examples + # mailto:chris@example.com + ok << ["mailto:chris@example.com"] + ok[-1] << ["chris@example.com", nil] + ok[-1] << {to: "chris@example.com"} + + # mailto:infobot@example.com?subject=current-issue + ok << ["mailto:infobot@example.com?subject=current-issue"] + ok[-1] << ["infobot@example.com", ["subject=current-issue"]] + ok[-1] << {to: "infobot@example.com", + headers: ["subject=current-issue"]} + + # mailto:infobot@example.com?body=send%20current-issue + ok << ["mailto:infobot@example.com?body=send%20current-issue"] + ok[-1] << ["infobot@example.com", ["body=send%20current-issue"]] + ok[-1] << {to: "infobot@example.com", + headers: ["body=send%20current-issue"]} + + # mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index + ok << ["mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index"] + ok[-1] << ["infobot@example.com", + ["body=send%20current-issue%0D%0Asend%20index"]] + ok[-1] << {to: "infobot@example.com", + headers: ["body=send%20current-issue%0D%0Asend%20index"]} + + # mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com + ok << ["mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com"] + ok[-1] << ["foobar@example.com", + ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]] + ok[-1] << {to: "foobar@example.com", + headers: ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]} + + # mailto:majordomo@example.com?body=subscribe%20bamboo-l + ok << ["mailto:majordomo@example.com?body=subscribe%20bamboo-l"] + ok[-1] << ["majordomo@example.com", ["body=subscribe%20bamboo-l"]] + ok[-1] << {to: "majordomo@example.com", + headers: ["body=subscribe%20bamboo-l"]} + + # mailto:joe@example.com?cc=bob@example.com&body=hello + ok << ["mailto:joe@example.com?cc=bob@example.com&body=hello"] + ok[-1] << ["joe@example.com", ["cc=bob@example.com", "body=hello"]] + ok[-1] << {to: "joe@example.com", + headers: ["cc=bob@example.com", "body=hello"]} + + # mailto:?to=joe@example.com&cc=bob@example.com&body=hello + ok << ["mailto:?to=joe@example.com&cc=bob@example.com&body=hello"] + ok[-1] << [nil, + ["to=joe@example.com", "cc=bob@example.com", "body=hello"]] + ok[-1] << {headers: ["to=joe@example.com", "cc=bob@example.com", "body=hello"]} + + # mailto:gorby%25kremvax@example.com + ok << ["mailto:gorby%25kremvax@example.com"] + ok[-1] << ["gorby%25kremvax@example.com", nil] + ok[-1] << {to: "gorby%25kremvax@example.com"} + + # mailto:unlikely%3Faddress@example.com?blat=foop + ok << ["mailto:unlikely%3Faddress@example.com?blat=foop"] + ok[-1] << ["unlikely%3Faddress@example.com", ["blat=foop"]] + ok[-1] << {to: "unlikely%3Faddress@example.com", + headers: ["blat=foop"]} + + ok_all = ok.flatten.join("\0") + + # mailto:joe@example.com?cc=bob@example.com?body=hello ; WRONG! + bad << ["joe@example.com", ["cc=bob@example.com?body=hello"]] + + # mailto:javascript:alert() + bad << ["javascript:alert()", []] + + # '=' which is in hname or hvalue is wrong. + bad << ["foo@example.jp?subject=1+1=2", []] + + ok.each do |x| + URI::MailTo.build(x[1]).to_s.should == x[0] + URI::MailTo.build(x[2]).to_s.should == x[0] + end + + bad.each do |x| + lambda { URI::MailTo.build(x) }.should raise_error(URI::InvalidComponentError) + end + + ok.flatten.join("\0").should == ok_all + end +end + + + +describe "URI::MailTo.build" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/headers_spec.rb b/spec/rubyspec/library/uri/mailto/headers_spec.rb new file mode 100644 index 0000000000..844fdee714 --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/headers_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::MailTo#headers" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::MailTo#headers=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/set_headers_spec.rb b/spec/rubyspec/library/uri/mailto/set_headers_spec.rb new file mode 100644 index 0000000000..c1384d5dca --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/set_headers_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::MailTo#set_headers" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/set_to_spec.rb b/spec/rubyspec/library/uri/mailto/set_to_spec.rb new file mode 100644 index 0000000000..a8351a2092 --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/set_to_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::MailTo#set_to" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/to_mailtext_spec.rb b/spec/rubyspec/library/uri/mailto/to_mailtext_spec.rb new file mode 100644 index 0000000000..4c7a48874f --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/to_mailtext_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::MailTo#to_mailtext" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/to_rfc822text_spec.rb b/spec/rubyspec/library/uri/mailto/to_rfc822text_spec.rb new file mode 100644 index 0000000000..e769f62deb --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/to_rfc822text_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::MailTo#to_rfc822text" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/to_s_spec.rb b/spec/rubyspec/library/uri/mailto/to_s_spec.rb new file mode 100644 index 0000000000..2709d19d27 --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/to_s_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::MailTo#to_s" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/mailto/to_spec.rb b/spec/rubyspec/library/uri/mailto/to_spec.rb new file mode 100644 index 0000000000..f30d23dd53 --- /dev/null +++ b/spec/rubyspec/library/uri/mailto/to_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::MailTo#to" do + it "needs to be reviewed for spec completeness" +end + +describe "URI::MailTo#to=" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/merge_spec.rb b/spec/rubyspec/library/uri/merge_spec.rb new file mode 100644 index 0000000000..c62e80d6b2 --- /dev/null +++ b/spec/rubyspec/library/uri/merge_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI#merge" do + it "returns the receiver and the argument, joined as per URI.join" do + URI("http://localhost/").merge("main.rbx").should == URI.parse("http://localhost/main.rbx") + URI("http://localhost/a/b/c/d").merge("http://ruby-lang.com/foo").should == URI.parse("http://ruby-lang.com/foo") + URI("http://localhost/a/b/c/d").merge("../../e/f").to_s.should == "http://localhost/a/e/f" + end + + it "accepts URI objects as argument" do + URI("http://localhost/").merge(URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") + end + + it "accepts a string-like argument" do + str = mock('string-like') + str.should_receive(:to_str).and_return("foo/bar") + URI("http://localhost/").merge(str).should == URI.parse("http://localhost/foo/bar") + end +end diff --git a/spec/rubyspec/library/uri/normalize_spec.rb b/spec/rubyspec/library/uri/normalize_spec.rb new file mode 100644 index 0000000000..079a9ad61d --- /dev/null +++ b/spec/rubyspec/library/uri/normalize_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/normalization', __FILE__) +require 'uri' + +describe "URI#normalize" do + it "adds a / onto the end of the URI if the path is blank" do + no_path = URI("http://example.com") + no_path.to_s.should_not == "http://example.com/" + no_path.normalize.to_s.should == "http://example.com/" + end + + it "downcases the host of the URI" do + uri = URI("http://exAMPLE.cOm/") + uri.to_s.should_not == "http://example.com/" + uri.normalize.to_s.should == "http://example.com/" + end + + # The previous tests are included by the one below + + quarantine! do # Quarantined until redmine:2542 is accepted + it "respects RFC 3986" do + URISpec::NORMALIZED_FORMS.each do |form| + normal_uri = URI(form[:normalized]) + normalized = normal_uri.normalize.to_s + normal_uri.to_s.should == normalized + form[:equivalent].each do |same| + URI(same).normalize.to_s.should == normalized + end + form[:different].each do |other| + URI(other).normalize.to_s.should_not == normalized + end + end + end + end +end diff --git a/spec/rubyspec/library/uri/parse_spec.rb b/spec/rubyspec/library/uri/parse_spec.rb new file mode 100644 index 0000000000..4aa84ae2ee --- /dev/null +++ b/spec/rubyspec/library/uri/parse_spec.rb @@ -0,0 +1,203 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "URI.parse" do + + it "returns a URI::HTTP object when parsing an HTTP URI" do + URI.parse("http://www.example.com/").should be_kind_of(URI::HTTP) + end + + it "populates the components of a parsed URI::HTTP, setting the port to 80 by default" do + # general case + URISpec.components(URI.parse("http://user:pass@example.com/path/?query=val&q2=val2#fragment")).should == { + scheme: "http", + userinfo: "user:pass", + host: "example.com", + port: 80, + path: "/path/", + query: "query=val&q2=val2", + fragment: "fragment" + } + + # multiple paths + URISpec.components(URI.parse("http://a/b/c/d;p?q")).should == { + scheme: "http", + userinfo: nil, + host: "a", + port: 80, + path: "/b/c/d;p", + query: "q", + fragment: nil + } + + # multi-level domain + URISpec.components(URI.parse('http://www.math.uio.no/faq/compression-faq/part1.html')).should == { + scheme: "http", + userinfo: nil, + host: "www.math.uio.no", + port: 80, + path: "/faq/compression-faq/part1.html", + query: nil, + fragment: nil + } + end + + it "parses out the port number of a URI, when given" do + URI.parse("http://example.com:8080/").port.should == 8080 + end + + it "returns a URI::HTTPS object when parsing an HTTPS URI" do + URI.parse("https://important-intern-net.net").should be_kind_of(URI::HTTPS) + end + + it "sets the port of a parsed https URI to 443 by default" do + URI.parse("https://example.com/").port.should == 443 + end + + it "populates the components of a parsed URI::FTP object" do + # generic, empty password. + url = URI.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i") + url.should be_kind_of(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: "anonymous", + host: "ruby-lang.org", + port: 21, + path: "pub/ruby/1.8/ruby-1.8.6.tar.bz2", + typecode: "i" + } + + # multidomain, no user or password + url = URI.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt') + url.should be_kind_of(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: nil, + host: "ftp.is.co.za", + port: 21, + path: "rfc/rfc1808.txt", + typecode: nil + } + + # empty user + url = URI.parse('ftp://:pass@localhost/') + url.should be_kind_of(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: ":pass", + host: "localhost", + port: 21, + path: "", + typecode: nil + } + url.password.should == "pass" + end + + it "returns a URI::LDAP object when parsing an LDAP URI" do + #taken from http://www.faqs.org/rfcs/rfc2255.html 'cause I don't really know what an LDAP url looks like + ldap_uris = %w{ ldap:///o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen) ldap://ldap.itd.umich.edu/c=GB?objectClass?one ldap://ldap.question.com/o=Question%3f,c=US?mail ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04) ldap:///??sub??bindname=cn=Manager%2co=Foo ldap:///??sub??!bindname=cn=Manager%2co=Foo } + ldap_uris.each do |ldap_uri| + URI.parse(ldap_uri).should be_kind_of(URI::LDAP) + end + end + + it "populates the components of a parsed URI::LDAP object" do + URISpec.components(URI.parse("ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress?scope?filter?extensions")).should == { + scheme: "ldap", + host: "ldap.itd.umich.edu", + port: 389, + dn: "o=University%20of%20Michigan,c=US", + attributes: "postalAddress", + scope: "scope", + filter: "filter", + extensions: "extensions" + } + end + + it "returns a URI::MailTo object when passed a mailto URI" do + URI.parse("mailto:spam@mailinator.com").should be_kind_of(URI::MailTo) + end + + it "populates the components of a parsed URI::MailTo object" do + URISpec.components(URI.parse("mailto:spam@mailinator.com?subject=Discounts%20On%20Imported%20methods!!!&body=Exciting%20offer")).should == { + scheme: "mailto", + to: "spam@mailinator.com", + headers: [["subject","Discounts%20On%20Imported%20methods!!!"], + ["body", "Exciting%20offer"]] + } + end + + # TODO + # Test registry + it "does its best to extract components from URI::Generic objects" do + # generic + URISpec.components(URI("scheme://userinfo@host/path?query#fragment")).should == { + scheme: "scheme", + userinfo: "userinfo", + host: "host", + port: nil, + path: "/path", + query: "query", + fragment: "fragment", + registry: nil, + opaque: nil + } + + # gopher + gopher = URI.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles') + gopher.should be_kind_of(URI::Generic) + + URISpec.components(gopher).should == { + scheme: "gopher", + userinfo: nil, + host: "spinaltap.micro.umn.edu", + port: nil, + path: "/00/Weather/California/Los%20Angeles", + query: nil, + fragment: nil, + registry: nil, + opaque: nil + } + + # news + news = URI.parse('news:comp.infosystems.www.servers.unix') + news.should be_kind_of(URI::Generic) + URISpec.components(news).should == { + scheme: "news", + userinfo: nil, + host: nil, + port: nil, + path: nil, + query: nil, + fragment: nil, + registry: nil, + opaque: "comp.infosystems.www.servers.unix" + } + + # telnet + telnet = URI.parse('telnet://melvyl.ucop.edu/') + telnet.should be_kind_of(URI::Generic) + URISpec.components(telnet).should == { + scheme: "telnet", + userinfo: nil, + host: "melvyl.ucop.edu", + port: nil, + path: "/", + query: nil, + fragment: nil, + registry: nil, + opaque: nil + } + + # files + file_l = URI.parse('file:///foo/bar.txt') + file_l.should be_kind_of(URI::Generic) + file = URI.parse('file:/foo/bar.txt') + file.should be_kind_of(URI::Generic) + end + + it "doesn't raise errors on URIs which has underscore in reg_name" do + URI.parse('http://a_b:80/').host.should == "a_b" + URI.parse('http://a_b/').host.should == "a_b" + end +end diff --git a/spec/rubyspec/library/uri/parser/escape_spec.rb b/spec/rubyspec/library/uri/parser/escape_spec.rb new file mode 100644 index 0000000000..8682e0ebce --- /dev/null +++ b/spec/rubyspec/library/uri/parser/escape_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Parser#escape" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/parser/extract_spec.rb b/spec/rubyspec/library/uri/parser/extract_spec.rb new file mode 100644 index 0000000000..5dac947060 --- /dev/null +++ b/spec/rubyspec/library/uri/parser/extract_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../shared/extract', __FILE__) +require 'uri' + +describe "URI::Parser#extract" do + it_behaves_like :uri_extract, :extract, URI::Parser.new +end diff --git a/spec/rubyspec/library/uri/parser/inspect_spec.rb b/spec/rubyspec/library/uri/parser/inspect_spec.rb new file mode 100644 index 0000000000..2eaeeeafee --- /dev/null +++ b/spec/rubyspec/library/uri/parser/inspect_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Parser#split" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/parser/join_spec.rb b/spec/rubyspec/library/uri/parser/join_spec.rb new file mode 100644 index 0000000000..1800a16236 --- /dev/null +++ b/spec/rubyspec/library/uri/parser/join_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../shared/join', __FILE__) +require 'uri' + +describe "URI::Parser#join" do + it_behaves_like :uri_join, :join, URI::Parser.new +end diff --git a/spec/rubyspec/library/uri/parser/make_regexp_spec.rb b/spec/rubyspec/library/uri/parser/make_regexp_spec.rb new file mode 100644 index 0000000000..e27f0d14db --- /dev/null +++ b/spec/rubyspec/library/uri/parser/make_regexp_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Parser#make_regexp" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/parser/parse_spec.rb b/spec/rubyspec/library/uri/parser/parse_spec.rb new file mode 100644 index 0000000000..76c1970645 --- /dev/null +++ b/spec/rubyspec/library/uri/parser/parse_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/parse', __FILE__) + +describe "URI::Parser#parse" do + it_behaves_like :uri_parse, :parse, URI::Parser.new +end diff --git a/spec/rubyspec/library/uri/parser/split_spec.rb b/spec/rubyspec/library/uri/parser/split_spec.rb new file mode 100644 index 0000000000..2eaeeeafee --- /dev/null +++ b/spec/rubyspec/library/uri/parser/split_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Parser#split" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/parser/unescape_spec.rb b/spec/rubyspec/library/uri/parser/unescape_spec.rb new file mode 100644 index 0000000000..8ffc534226 --- /dev/null +++ b/spec/rubyspec/library/uri/parser/unescape_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Parser#unescape" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/plus_spec.rb b/spec/rubyspec/library/uri/plus_spec.rb new file mode 100644 index 0000000000..45c1aa5e57 --- /dev/null +++ b/spec/rubyspec/library/uri/plus_spec.rb @@ -0,0 +1,459 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +#an alias of URI#merge +describe "URI#+" do + it "replaces the end of the path of the URI when added to a string that looks like a relative path" do + (URI('http://foo') + 'bar').should == URI("http://foo/bar") + (URI('http://foo/baz') + 'bar').should == URI("http://foo/bar") + (URI('http://foo/baz/') + 'bar').should == URI("http://foo/baz/bar") + (URI('mailto:foo@example.com') + "#bar").should == URI("mailto:foo@example.com#bar") + end + + it "replaces the entire path of the URI when added to a string that begins with a /" do + (URI('http://foo/baz/') + '/bar').should == URI("http://foo/bar") + end + + it "replaces the entire url when added to a string that looks like a full url" do + (URI.parse('http://a/b') + 'http://x/y').should == URI("http://x/y") + (URI.parse('telnet:example.com') + 'http://x/y').should == URI("http://x/y") + end + + it "canonicalizes the URI's path, removing ../'s" do + (URI.parse('http://a/b/c/../') + "./").should == URI("http://a/b/") + (URI.parse('http://a/b/c/../') + ".").should == URI("http://a/b/") + (URI.parse('http://a/b/c/') + "../").should == URI("http://a/b/") + (URI.parse('http://a/b/c/../../') + "./").should == URI("http://a/") + (URI.parse('http://a/b/c/') + "../e/").should == URI("http://a/b/e/") + (URI.parse('http://a/b/c/') + "../e/../").should == URI("http://a/b/") + (URI.parse('http://a/b/../c/') + ".").should == URI("http://a/c/") + + (URI.parse('http://a/b/c/../../../') + ".").should == URI("http://a/") + end + + it "doesn't conconicalize the path when adding to the empty string" do + (URI.parse('http://a/b/c/../') + "").should == URI("http://a/b/c/../") + end + + it "raises a URI::BadURIError when adding two relative URIs" do + lambda {URI.parse('a/b/c') + "d"}.should raise_error(URI::BadURIError) + end + + #Todo: make more BDD? + it "conforms to the merge specifications from rfc 2396" do + @url = 'http://a/b/c/d;p?q' + @base_url = URI.parse(@url) + +# http://a/b/c/d;p?q +# g:h = g:h + url = @base_url.merge('g:h') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g:h' + url = @base_url.route_to('g:h') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g:h' + +# http://a/b/c/d;p?q +# g = http://a/b/c/g + url = @base_url.merge('g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g' + url = @base_url.route_to('http://a/b/c/g') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g' + +# http://a/b/c/d;p?q +# ./g = http://a/b/c/g + url = @base_url.merge('./g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g' + url = @base_url.route_to('http://a/b/c/g') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == './g' # ok + url.to_s.should == 'g' + +# http://a/b/c/d;p?q +# g/ = http://a/b/c/g/ + url = @base_url.merge('g/') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g/' + url = @base_url.route_to('http://a/b/c/g/') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g/' + +# http://a/b/c/d;p?q +# /g = http://a/g + url = @base_url.merge('/g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/g' + url = @base_url.route_to('http://a/g') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == '/g' # ok + url.to_s.should == '../../g' + +# http://a/b/c/d;p?q +# //g = http://g + url = @base_url.merge('//g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://g' + url = @base_url.route_to('http://g') + url.should be_kind_of(URI::Generic) + url.to_s.should == '//g' + +# http://a/b/c/d;p?q +# ?y = http://a/b/c/?y + url = @base_url.merge('?y') + url.should be_kind_of(URI::HTTP) + + url.to_s.should == 'http://a/b/c/d;p?y' + + url = @base_url.route_to('http://a/b/c/?y') + url.should be_kind_of(URI::Generic) + url.to_s.should == '?y' + +# http://a/b/c/d;p?q +# g?y = http://a/b/c/g?y + url = @base_url.merge('g?y') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g?y' + url = @base_url.route_to('http://a/b/c/g?y') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g?y' + +# http://a/b/c/d;p?q +# #s = (current document)#s + url = @base_url.merge('#s') + url.should be_kind_of(URI::HTTP) + url.to_s.should == @base_url.to_s + '#s' + url = @base_url.route_to(@base_url.to_s + '#s') + url.should be_kind_of(URI::Generic) + url.to_s.should == '#s' + +# http://a/b/c/d;p?q +# g#s = http://a/b/c/g#s + url = @base_url.merge('g#s') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g#s' + url = @base_url.route_to('http://a/b/c/g#s') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g#s' + +# http://a/b/c/d;p?q +# g?y#s = http://a/b/c/g?y#s + url = @base_url.merge('g?y#s') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g?y#s' + url = @base_url.route_to('http://a/b/c/g?y#s') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g?y#s' + +# http://a/b/c/d;p?q +# ;x = http://a/b/c/;x + url = @base_url.merge(';x') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/;x' + url = @base_url.route_to('http://a/b/c/;x') + url.should be_kind_of(URI::Generic) + url.to_s.should == ';x' + +# http://a/b/c/d;p?q +# g;x = http://a/b/c/g;x + url = @base_url.merge('g;x') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g;x' + url = @base_url.route_to('http://a/b/c/g;x') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g;x' + +# http://a/b/c/d;p?q +# g;x?y#s = http://a/b/c/g;x?y#s + url = @base_url.merge('g;x?y#s') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g;x?y#s' + url = @base_url.route_to('http://a/b/c/g;x?y#s') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g;x?y#s' + +# http://a/b/c/d;p?q +# . = http://a/b/c/ + url = @base_url.merge('.') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/' + url = @base_url.route_to('http://a/b/c/') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == '.' # ok + url.to_s.should == './' + +# http://a/b/c/d;p?q +# ./ = http://a/b/c/ + url = @base_url.merge('./') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/' + url = @base_url.route_to('http://a/b/c/') + url.should be_kind_of(URI::Generic) + url.to_s.should == './' + +# http://a/b/c/d;p?q +# .. = http://a/b/ + url = @base_url.merge('..') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/' + url = @base_url.route_to('http://a/b/') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == '..' # ok + url.to_s.should == '../' + +# http://a/b/c/d;p?q +# ../ = http://a/b/ + url = @base_url.merge('../') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/' + url = @base_url.route_to('http://a/b/') + url.should be_kind_of(URI::Generic) + url.to_s.should == '../' + +# http://a/b/c/d;p?q +# ../g = http://a/b/g + url = @base_url.merge('../g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/g' + url = @base_url.route_to('http://a/b/g') + url.should be_kind_of(URI::Generic) + url.to_s.should == '../g' + +# http://a/b/c/d;p?q +# ../.. = http://a/ + url = @base_url.merge('../..') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/' + url = @base_url.route_to('http://a/') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == '../..' # ok + url.to_s.should == '../../' + +# http://a/b/c/d;p?q +# ../../ = http://a/ + url = @base_url.merge('../../') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/' + url = @base_url.route_to('http://a/') + url.should be_kind_of(URI::Generic) + url.to_s.should == '../../' + +# http://a/b/c/d;p?q +# ../../g = http://a/g + url = @base_url.merge('../../g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/g' + url = @base_url.route_to('http://a/g') + url.should be_kind_of(URI::Generic) + url.to_s.should == '../../g' + +# http://a/b/c/d;p?q +# <> = (current document) + url = @base_url.merge('') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/d;p?q' + url = @base_url.route_to('http://a/b/c/d;p?q') + url.should be_kind_of(URI::Generic) + url.to_s.should == '' + +# http://a/b/c/d;p?q +# /./g = http://a/./g + url = @base_url.merge('/./g') + url.should be_kind_of(URI::HTTP) + + url.to_s.should == 'http://a/g' + + url = @base_url.route_to('http://a/./g') + url.should be_kind_of(URI::Generic) + url.to_s.should == '/./g' + +# http://a/b/c/d;p?q +# /../g = http://a/../g + url = @base_url.merge('/../g') + url.should be_kind_of(URI::HTTP) + + url.to_s.should == 'http://a/g' + + url = @base_url.route_to('http://a/../g') + url.should be_kind_of(URI::Generic) + url.to_s.should == '/../g' + +# http://a/b/c/d;p?q +# g. = http://a/b/c/g. + url = @base_url.merge('g.') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g.' + url = @base_url.route_to('http://a/b/c/g.') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g.' + +# http://a/b/c/d;p?q +# .g = http://a/b/c/.g + url = @base_url.merge('.g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/.g' + url = @base_url.route_to('http://a/b/c/.g') + url.should be_kind_of(URI::Generic) + url.to_s.should == '.g' + +# http://a/b/c/d;p?q +# g.. = http://a/b/c/g.. + url = @base_url.merge('g..') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g..' + url = @base_url.route_to('http://a/b/c/g..') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g..' + +# http://a/b/c/d;p?q +# ..g = http://a/b/c/..g + url = @base_url.merge('..g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/..g' + url = @base_url.route_to('http://a/b/c/..g') + url.should be_kind_of(URI::Generic) + url.to_s.should == '..g' + +# http://a/b/c/d;p?q +# ../../../g = http://a/../g + url = @base_url.merge('../../../g') + url.should be_kind_of(URI::HTTP) + + url.to_s.should == 'http://a/g' + + url = @base_url.route_to('http://a/../g') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == '../../../g' # ok? yes, it confuses you + url.to_s.should == '/../g' # and it is clearly + +# http://a/b/c/d;p?q +# ../../../../g = http://a/../../g + url = @base_url.merge('../../../../g') + url.should be_kind_of(URI::HTTP) + + url.to_s.should == 'http://a/g' + + url = @base_url.route_to('http://a/../../g') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == '../../../../g' # ok? yes, it confuses you + url.to_s.should == '/../../g' # and it is clearly + +# http://a/b/c/d;p?q +# ./../g = http://a/b/g + url = @base_url.merge('./../g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/g' + url = @base_url.route_to('http://a/b/g') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == './../g' # ok + url.to_s.should == '../g' + +# http://a/b/c/d;p?q +# ./g/. = http://a/b/c/g/ + url = @base_url.merge('./g/.') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g/' + url = @base_url.route_to('http://a/b/c/g/') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == './g/.' # ok + url.to_s.should == 'g/' + +# http://a/b/c/d;p?q +# g/./h = http://a/b/c/g/h + url = @base_url.merge('g/./h') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g/h' + url = @base_url.route_to('http://a/b/c/g/h') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == 'g/./h' # ok + url.to_s.should == 'g/h' + +# http://a/b/c/d;p?q +# g/../h = http://a/b/c/h + url = @base_url.merge('g/../h') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/h' + url = @base_url.route_to('http://a/b/c/h') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == 'g/../h' # ok + url.to_s.should == 'h' + +# http://a/b/c/d;p?q +# g;x=1/./y = http://a/b/c/g;x=1/y + url = @base_url.merge('g;x=1/./y') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g;x=1/y' + url = @base_url.route_to('http://a/b/c/g;x=1/y') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == 'g;x=1/./y' # ok + url.to_s.should == 'g;x=1/y' + +# http://a/b/c/d;p?q +# g;x=1/../y = http://a/b/c/y + url = @base_url.merge('g;x=1/../y') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/y' + url = @base_url.route_to('http://a/b/c/y') + url.should be_kind_of(URI::Generic) + url.to_s.should_not == 'g;x=1/../y' # ok + url.to_s.should == 'y' + +# http://a/b/c/d;p?q +# g?y/./x = http://a/b/c/g?y/./x + url = @base_url.merge('g?y/./x') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g?y/./x' + url = @base_url.route_to('http://a/b/c/g?y/./x') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g?y/./x' + +# http://a/b/c/d;p?q +# g?y/../x = http://a/b/c/g?y/../x + url = @base_url.merge('g?y/../x') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g?y/../x' + url = @base_url.route_to('http://a/b/c/g?y/../x') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g?y/../x' + +# http://a/b/c/d;p?q +# g#s/./x = http://a/b/c/g#s/./x + url = @base_url.merge('g#s/./x') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g#s/./x' + url = @base_url.route_to('http://a/b/c/g#s/./x') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g#s/./x' + +# http://a/b/c/d;p?q +# g#s/../x = http://a/b/c/g#s/../x + url = @base_url.merge('g#s/../x') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http://a/b/c/g#s/../x' + url = @base_url.route_to('http://a/b/c/g#s/../x') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'g#s/../x' + +# http://a/b/c/d;p?q +# http:g = http:g ; for validating parsers +# | http://a/b/c/g ; for backwards compatibility + url = @base_url.merge('http:g') + url.should be_kind_of(URI::HTTP) + url.to_s.should == 'http:g' + url = @base_url.route_to('http:g') + url.should be_kind_of(URI::Generic) + url.to_s.should == 'http:g' + end +end + +#TODO: incorporate these tests: +# +# u = URI.parse('http://foo/bar/baz') +# assert_equal(nil, u.merge!("")) +# assert_equal(nil, u.merge!(u)) +# assert(nil != u.merge!(".")) +# assert_equal('http://foo/bar/', u.to_s) +# assert(nil != u.merge!("../baz")) +# assert_equal('http://foo/baz', u.to_s) diff --git a/spec/rubyspec/library/uri/regexp_spec.rb b/spec/rubyspec/library/uri/regexp_spec.rb new file mode 100644 index 0000000000..cf63507013 --- /dev/null +++ b/spec/rubyspec/library/uri/regexp_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +#I'm more or less ok with these limited tests, as the more extensive extract tests +#use URI.regexp +describe "URI.regexp" do + it "behaves according to the MatzRuby tests" do + URI.regexp.should == URI.regexp + 'x http:// x'.slice(URI.regexp).should == 'http://' + 'x http:// x'.slice(URI.regexp(['http'])).should == 'http://' + 'x http:// x ftp://'.slice(URI.regexp(['http'])).should == 'http://' + 'http://'.slice(URI.regexp([])).should == nil + ''.slice(URI.regexp).should == nil + 'xxxx'.slice(URI.regexp).should == nil + ':'.slice(URI.regexp).should == nil + 'From:'.slice(URI.regexp).should == 'From:' + end +end diff --git a/spec/rubyspec/library/uri/route_from_spec.rb b/spec/rubyspec/library/uri/route_from_spec.rb new file mode 100644 index 0000000000..11a2c44f90 --- /dev/null +++ b/spec/rubyspec/library/uri/route_from_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI#route_from" do + + #this could be split out a good bit better + it "gives the minimal difference between the current URI and the target" do + URI("http://example.com/a.html").route_from('http://example.com/a.html').to_s.should == "" + URI("http://example.com/a.html").route_from('http://example.com/b.html').to_s.should == "a.html" + URI("http://example.com/a/").route_from('http://example.com/b/').to_s.should == "../a/" + URI("http://example.com/b/").route_from('http://example.com/a/c').to_s.should == "../b/" + URI("http://example.com/b/").route_from('http://example.com/a/b/').to_s.should == "../../b/" + URI("http://example.com/b/").route_from('http://EXAMPLE.cOm/a/b/').to_s.should == "../../b/" + URI("http://example.net/b/").route_from('http://example.com/a/b/').to_s.should == "//example.net/b/" + URI("mailto:foo@example.com#bar").route_from('mailto:foo@example.com').to_s.should == "#bar" + end + + it "accepts a string-like argument" do + str = mock('string-like') + str.should_receive(:to_str).and_return("http://example.com/b.html") + URI("http://example.com/a.html").route_from(str).to_s.should == "a.html" + end +end diff --git a/spec/rubyspec/library/uri/route_to_spec.rb b/spec/rubyspec/library/uri/route_to_spec.rb new file mode 100644 index 0000000000..2eb68afdfd --- /dev/null +++ b/spec/rubyspec/library/uri/route_to_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI#route_to" do + + #this could be split out a good bit better + it "gives the minimal difference between the current URI and the target" do + URI("http://example.com/a.html").route_to('http://example.com/a.html').to_s.should == "" + URI("http://example.com/a.html").route_to('http://example.com/b.html').to_s.should == "b.html" + URI("http://example.com/a/").route_to('http://example.com/b/').to_s.should == "../b/" + URI("http://example.com/a/c").route_to('http://example.com/b/').to_s.should == "../b/" + URI("http://example.com/a/b/").route_to('http://example.com/b/').to_s.should == "../../b/" + URI("http://example.com/a/b/").route_to('http://EXAMPLE.cOm/b/').to_s.should == "../../b/" + URI("http://example.com/a/b/").route_to('http://example.net/b/').to_s.should == "//example.net/b/" + URI("mailto:foo@example.com").route_to('mailto:foo@example.com#bar').to_s.should == "#bar" + + #this was a little surprising to me + URI("mailto:foo@example.com#bar").route_to('mailto:foo@example.com').to_s.should == "" + end + + it "accepts a string-like argument" do + str = mock('string-like') + str.should_receive(:to_str).and_return("http://example.com/b.html") + URI("http://example.com/a.html").route_to(str).to_s.should == "b.html" + end +end diff --git a/spec/rubyspec/library/uri/select_spec.rb b/spec/rubyspec/library/uri/select_spec.rb new file mode 100644 index 0000000000..46474757cc --- /dev/null +++ b/spec/rubyspec/library/uri/select_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI#select" do + it "takes any number of component names as symbols, and returns an array of those components" do + URI("http://host:8080/path/").select.should == [] + URI("http://host:8080/path/").select(:scheme,:host,:port,:path).should == [ + "http","host",8080,"/path/"] + end + + it "returns nil for any valid component that isn't set and doesn't have a default" do + uri = URI("http://host") + uri.select(:userinfo, :query, :fragment).should == [nil] * 3 + uri.select(:port, :path).should == [80, ''] + end + + it "raises an ArgumentError if a component is requested that isn't valid under the given scheme" do + [ + lambda {URI("mailto:spam@mailinator.com").select(:path)}, + lambda {URI("http://blog.blag.web").select(:typecode)}, + ].each do |select_lambda| + select_lambda.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if given strings rather than symbols" do + lambda { + URI("http://host:8080/path/").select("scheme","host","port",'path') + }.should raise_error(ArgumentError) + end +end diff --git a/spec/rubyspec/library/uri/set_component_spec.rb b/spec/rubyspec/library/uri/set_component_spec.rb new file mode 100644 index 0000000000..9b8372108a --- /dev/null +++ b/spec/rubyspec/library/uri/set_component_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +#TODO: make this more BDD +describe "URI#select" do + it "conforms to the MatzRuby tests" do + uri = URI.parse('http://foo:bar@baz') + (uri.user = 'oof').should == 'oof' + uri.to_s.should == 'http://oof:bar@baz' + (uri.password = 'rab').should == 'rab' + uri.to_s.should == 'http://oof:rab@baz' + (uri.userinfo = 'foo').should == 'foo' + uri.to_s.should == 'http://foo:rab@baz' + (uri.userinfo = ['foo', 'bar']).should == ['foo', 'bar'] + uri.to_s.should == 'http://foo:bar@baz' + (uri.userinfo = ['foo']).should == ['foo'] + uri.to_s.should == 'http://foo:bar@baz' + (uri.host = 'zab').should == 'zab' + uri.to_s.should == 'http://foo:bar@zab' + (uri.port = 8080).should == 8080 + uri.to_s.should == 'http://foo:bar@zab:8080' + (uri.path = '/').should == '/' + uri.to_s.should == 'http://foo:bar@zab:8080/' + (uri.query = 'a=1').should == 'a=1' + uri.to_s.should == 'http://foo:bar@zab:8080/?a=1' + (uri.fragment = 'b123').should == 'b123' + uri.to_s.should == 'http://foo:bar@zab:8080/?a=1#b123' + + uri = URI.parse('http://example.com') + lambda { uri.password = 'bar' }.should raise_error(URI::InvalidURIError) + uri.userinfo = 'foo:bar' + uri.to_s.should == 'http://foo:bar@example.com' + lambda { uri.registry = 'bar' }.should raise_error(URI::InvalidURIError) + lambda { uri.opaque = 'bar' }.should raise_error(URI::InvalidURIError) + + uri = URI.parse('mailto:foo@example.com') + lambda { uri.user = 'bar' }.should raise_error(URI::InvalidURIError) + lambda { uri.password = 'bar' }.should raise_error(URI::InvalidURIError) + lambda { uri.userinfo = ['bar', 'baz'] }.should raise_error(URI::InvalidURIError) + lambda { uri.host = 'bar' }.should raise_error(URI::InvalidURIError) + lambda { uri.port = 'bar' }.should raise_error(URI::InvalidURIError) + lambda { uri.path = 'bar' }.should raise_error(URI::InvalidURIError) + lambda { uri.query = 'bar' }.should raise_error(URI::InvalidURIError) + end +end + + diff --git a/spec/rubyspec/library/uri/shared/eql.rb b/spec/rubyspec/library/uri/shared/eql.rb new file mode 100644 index 0000000000..2cc960d39a --- /dev/null +++ b/spec/rubyspec/library/uri/shared/eql.rb @@ -0,0 +1,17 @@ +describe :uri_eql, shared: true do + it "returns false if the normalized forms are different" do + URISpec::NORMALIZED_FORMS.each do |form| + normal_uri = URI(form[:normalized]) + form[:different].each do |other| + URI(other).send(@method, normal_uri).should be_false + end + end + end +end + +describe :uri_eql_against_other_types, shared: true do + it "returns false for when compared to non-uri objects" do + URI("http://example.com/").send(@method, "http://example.com/").should be_false + URI("http://example.com/").send(@method, nil).should be_false + end +end diff --git a/spec/rubyspec/library/uri/shared/extract.rb b/spec/rubyspec/library/uri/shared/extract.rb new file mode 100644 index 0000000000..efe60ae4b9 --- /dev/null +++ b/spec/rubyspec/library/uri/shared/extract.rb @@ -0,0 +1,83 @@ +describe :uri_extract, shared: true do + it "behaves according to its documentation" do + @object.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.").should == ["http://foo.example.org/bla", "mailto:test@example.com"] + end + + it "treats contiguous URIs as a single URI" do + @object.extract('http://example.jphttp://example.jp').should == ['http://example.jphttp://example.jp'] + end + + it "treats pretty much anything with a colon as a URI" do + @object.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').should == ['From:', 'mailto:xxx@xxx.xxx.xxx]'] + end + + it "wraps a URI string in an array" do + @object.extract("http://github.com/brixen/rubyspec/tree/master").should == ["http://github.com/brixen/rubyspec/tree/master"] + end + + it "pulls a variety of protocol URIs from a string" do + @object.extract("this is a string, it has http://rubini.us/ in it").should == ["http://rubini.us/"] + @object.extract("mailto:spambait@example.com").should == ["mailto:spambait@example.com"] + @object.extract("ftp://ruby-lang.org/").should == ["ftp://ruby-lang.org/"] + @object.extract("https://mail.google.com").should == ["https://mail.google.com"] + @object.extract("anything://example.com/").should == ["anything://example.com/"] + end + + it "pulls all URIs within a string in order into an array when a block is not given" do + @object.extract("1.3. Example URI + + The following examples illustrate URI that are in common use. + + ftp://ftp.is.co.za/rfc/rfc1808.txt + -- ftp scheme for File Transfer Protocol services + + gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles + -- gopher scheme for Gopher and Gopher+ Protocol services + + http://www.math.uio.no/faq/compression-faq/part1.html + -- http scheme for Hypertext Transfer Protocol services + + mailto:mduerst@ifi.unizh.ch + -- mailto scheme for electronic mail addresses + + news:comp.infosystems.www.servers.unix + -- news scheme for USENET news groups and articles + + telnet://melvyl.ucop.edu/ + -- telnet scheme for interactive services via the TELNET Protocol + ").should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch","news:comp.infosystems.www.servers.unix","telnet://melvyl.ucop.edu/"] + end + + it "yields each URI in the given string in order to a block, if given, and returns nil" do + results = ["http://foo.example.org/bla", "mailto:test@example.com"] + @object.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") {|uri| + uri.should == results.shift + }.should == nil + results.should == [] + end + + it "allows the user to specify a list of acceptable protocols of URIs to scan for" do + @object.extract("1.3. Example URI + + The following examples illustrate URI that are in common use. + + ftp://ftp.is.co.za/rfc/rfc1808.txt + -- ftp scheme for File Transfer Protocol services + + gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles + -- gopher scheme for Gopher and Gopher+ Protocol services + + http://www.math.uio.no/faq/compression-faq/part1.html + -- http scheme for Hypertext Transfer Protocol services + + mailto:mduerst@ifi.unizh.ch + -- mailto scheme for electronic mail addresses + + news:comp.infosystems.www.servers.unix + -- news scheme for USENET news groups and articles + + telnet://melvyl.ucop.edu/ + -- telnet scheme for interactive services via the TELNET Protocol + ", ["http","ftp","mailto"]).should == ["ftp://ftp.is.co.za/rfc/rfc1808.txt","http://www.math.uio.no/faq/compression-faq/part1.html","mailto:mduerst@ifi.unizh.ch"] + end +end diff --git a/spec/rubyspec/library/uri/shared/join.rb b/spec/rubyspec/library/uri/shared/join.rb new file mode 100644 index 0000000000..dfe44e9be2 --- /dev/null +++ b/spec/rubyspec/library/uri/shared/join.rb @@ -0,0 +1,54 @@ +describe :uri_join, shared: true do + it "returns a URI object of the concatenation of a protocol and domain, and a path" do + @object.join("http://localhost/","main.rbx").should == URI.parse("http://localhost/main.rbx") + end + + it "accepts URI objects" do + @object.join(URI("http://localhost/"),"main.rbx").should == URI.parse("http://localhost/main.rbx") + @object.join("http://localhost/",URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") + @object.join(URI("http://localhost/"),URI("main.rbx")).should == URI.parse("http://localhost/main.rbx") + end + + it "accepts string-like arguments with to_str" do + str = mock('string-like') + str.should_receive(:to_str).and_return("http://ruby-lang.org") + str2 = mock('string-like also') + str2.should_receive(:to_str).and_return("foo/bar") + @object.join(str, str2).should == URI.parse("http://ruby-lang.org/foo/bar") + end + + it "raises an error if given no argument" do + lambda{ @object.join }.should raise_error + end + + it "doesn't create redundant '/'s" do + @object.join("http://localhost/", "/main.rbx").should == URI.parse("http://localhost/main.rbx") + end + + it "discards arguments given before an absolute uri" do + @object.join("http://localhost/a/b/c/d", "http://ruby-lang.com/foo", "bar").should == URI.parse("http://ruby-lang.com/bar") + end + + it "resolves .. in paths" do + @object.join("http://localhost/a/b/c/d", "../../e/f", "g/h/../i").to_s.should == "http://localhost/a/e/g/i" + end +end + + +# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar')) +# assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar')) +# assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/')) +# +# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz')) +# assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz')) +# assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/')) +# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge')) +# +# assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz')) +# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) +# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) +# assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) +# assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) +# assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) diff --git a/spec/rubyspec/library/uri/shared/parse.rb b/spec/rubyspec/library/uri/shared/parse.rb new file mode 100644 index 0000000000..5ecbffcaf2 --- /dev/null +++ b/spec/rubyspec/library/uri/shared/parse.rb @@ -0,0 +1,199 @@ +describe :uri_parse, shared: true do + it "returns a URI::HTTP object when parsing an HTTP URI" do + @object.parse("http://www.example.com/").should be_kind_of(URI::HTTP) + end + + it "populates the components of a parsed URI::HTTP, setting the port to 80 by default" do + # general case + URISpec.components(@object.parse("http://user:pass@example.com/path/?query=val&q2=val2#fragment")).should == { + scheme: "http", + userinfo: "user:pass", + host: "example.com", + port: 80, + path: "/path/", + query: "query=val&q2=val2", + fragment: "fragment" + } + + # multiple paths + URISpec.components(@object.parse("http://a/b/c/d;p?q")).should == { + scheme: "http", + userinfo: nil, + host: "a", + port: 80, + path: "/b/c/d;p", + query: "q", + fragment: nil + } + + # multi-level domain + URISpec.components(@object.parse('http://www.math.uio.no/faq/compression-faq/part1.html')).should == { + scheme: "http", + userinfo: nil, + host: "www.math.uio.no", + port: 80, + path: "/faq/compression-faq/part1.html", + query: nil, + fragment: nil + } + end + + it "parses out the port number of a URI, when given" do + @object.parse("http://example.com:8080/").port.should == 8080 + end + + it "returns a URI::HTTPS object when parsing an HTTPS URI" do + @object.parse("https://important-intern-net.net").should be_kind_of(URI::HTTPS) + end + + it "sets the port of a parsed https URI to 443 by default" do + @object.parse("https://example.com/").port.should == 443 + end + + it "populates the components of a parsed URI::FTP object" do + # generic, empty password. + url = @object.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i") + url.should be_kind_of(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: "anonymous", + host: "ruby-lang.org", + port: 21, + path: "pub/ruby/1.8/ruby-1.8.6.tar.bz2", + typecode: "i" + } + + # multidomain, no user or password + url = @object.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt') + url.should be_kind_of(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: nil, + host: "ftp.is.co.za", + port: 21, + path: "rfc/rfc1808.txt", + typecode: nil + } + + # empty user + url = @object.parse('ftp://:pass@localhost/') + url.should be_kind_of(URI::FTP) + URISpec.components(url).should == { + scheme: "ftp", + userinfo: ":pass", + host: "localhost", + port: 21, + path: "", + typecode: nil + } + url.password.should == "pass" + end + + it "returns a URI::LDAP object when parsing an LDAP URI" do + #taken from http://www.faqs.org/rfcs/rfc2255.html 'cause I don't really know what an LDAP url looks like + ldap_uris = %w{ ldap:///o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress ldap://host.com:6666/o=University%20of%20Michigan,c=US??sub?(cn=Babs%20Jensen) ldap://ldap.itd.umich.edu/c=GB?objectClass?one ldap://ldap.question.com/o=Question%3f,c=US?mail ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04) ldap:///??sub??bindname=cn=Manager%2co=Foo ldap:///??sub??!bindname=cn=Manager%2co=Foo } + ldap_uris.each do |ldap_uri| + @object.parse(ldap_uri).should be_kind_of(URI::LDAP) + end + end + + it "populates the components of a parsed URI::LDAP object" do + URISpec.components(@object.parse("ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US?postalAddress?scope?filter?extensions")).should == { + scheme: "ldap", + host: "ldap.itd.umich.edu", + port: 389, + dn: "o=University%20of%20Michigan,c=US", + attributes: "postalAddress", + scope: "scope", + filter: "filter", + extensions: "extensions" + } + end + + it "returns a URI::MailTo object when passed a mailto URI" do + @object.parse("mailto:spam@mailinator.com").should be_kind_of(URI::MailTo) + end + + it "populates the components of a parsed URI::MailTo object" do + URISpec.components(@object.parse("mailto:spam@mailinator.com?subject=Discounts%20On%20Imported%20methods!!!&body=Exciting%20offer")).should == { + scheme: "mailto", + to: "spam@mailinator.com", + headers: [["subject","Discounts%20On%20Imported%20methods!!!"], + ["body", "Exciting%20offer"]] + } + end + + # TODO + # Test registry + it "does its best to extract components from URI::Generic objects" do + # generic + URISpec.components(URI("scheme://userinfo@host/path?query#fragment")).should == { + scheme: "scheme", + userinfo: "userinfo", + host: "host", + port: nil, + path: "/path", + query: "query", + fragment: "fragment", + registry: nil, + opaque: nil + } + + # gopher + gopher = @object.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles') + gopher.should be_kind_of(URI::Generic) + + URISpec.components(gopher).should == { + scheme: "gopher", + userinfo: nil, + host: "spinaltap.micro.umn.edu", + port: nil, + path: "/00/Weather/California/Los%20Angeles", + query: nil, + fragment: nil, + registry: nil, + opaque: nil + } + + # news + news = @object.parse('news:comp.infosystems.www.servers.unix') + news.should be_kind_of(URI::Generic) + URISpec.components(news).should == { + scheme: "news", + userinfo: nil, + host: nil, + port: nil, + path: nil, + query: nil, + fragment: nil, + registry: nil, + opaque: "comp.infosystems.www.servers.unix" + } + + # telnet + telnet = @object.parse('telnet://melvyl.ucop.edu/') + telnet.should be_kind_of(URI::Generic) + URISpec.components(telnet).should == { + scheme: "telnet", + userinfo: nil, + host: "melvyl.ucop.edu", + port: nil, + path: "/", + query: nil, + fragment: nil, + registry: nil, + opaque: nil + } + + # files + file_l = @object.parse('file:///foo/bar.txt') + file_l.should be_kind_of(URI::Generic) + file = @object.parse('file:/foo/bar.txt') + file.should be_kind_of(URI::Generic) + end + + it "raises errors on malformed URIs" do + lambda { @object.parse('http://a_b:80/') }.should raise_error(URI::InvalidURIError) + lambda { @object.parse('http://a_b/') }.should raise_error(URI::InvalidURIError) + end +end diff --git a/spec/rubyspec/library/uri/split_spec.rb b/spec/rubyspec/library/uri/split_spec.rb new file mode 100644 index 0000000000..f0ab6ff35c --- /dev/null +++ b/spec/rubyspec/library/uri/split_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +describe "URI.split" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/uri/uri_spec.rb b/spec/rubyspec/library/uri/uri_spec.rb new file mode 100644 index 0000000000..90936a770f --- /dev/null +++ b/spec/rubyspec/library/uri/uri_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'uri' + +#the testing is light here as this is an alias for URI.parse + +#we're just testing that the method ends up in the right place +describe "the URI method" do + it "parses a given URI, returning a URI object" do + result = URI.parse("http://ruby-lang.org") + URI("http://ruby-lang.org").should == result + Kernel::URI("http://ruby-lang.org").should == result + end + + it "converts its argument with to_str" do + str = mock('string-like') + str.should_receive(:to_str).and_return("http://ruby-lang.org") + URI(str).should == URI.parse("http://ruby-lang.org") + end + + it "returns the argument if it is a URI object" do + result = URI.parse("http://ruby-lang.org") + URI(result).should equal(result) + end + + #apparently this was a concern? imported from MRI tests + it "does not add a URI method to Object instances" do + lambda {Object.new.URI("http://ruby-lang.org/")}.should raise_error(NoMethodError) + end +end diff --git a/spec/rubyspec/library/uri/util/make_components_hash_spec.rb b/spec/rubyspec/library/uri/util/make_components_hash_spec.rb new file mode 100644 index 0000000000..0f491112e8 --- /dev/null +++ b/spec/rubyspec/library/uri/util/make_components_hash_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'uri' + +describe "URI::Util.make_components_hash" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/weakref/__getobj___spec.rb b/spec/rubyspec/library/weakref/__getobj___spec.rb new file mode 100644 index 0000000000..e75b8f4704 --- /dev/null +++ b/spec/rubyspec/library/weakref/__getobj___spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "WeakRef#__getobj__" do + it "returns the object if it is reachable" do + obj = Object.new + ref = WeakRef.new(obj) + ref.__getobj__.should equal(obj) + end + + it "raises WeakRef::RefError if the object is no longer reachable" do + ref = WeakRefSpec.make_dead_weakref + lambda { + ref.__getobj__ + }.should raise_error(WeakRef::RefError) + end +end diff --git a/spec/rubyspec/library/weakref/fixtures/classes.rb b/spec/rubyspec/library/weakref/fixtures/classes.rb new file mode 100644 index 0000000000..560c58b041 --- /dev/null +++ b/spec/rubyspec/library/weakref/fixtures/classes.rb @@ -0,0 +1,24 @@ +require 'weakref' + +# From MRI test_weakref.rb +class WeakRefSpec + def self.make_weakref(level = 10) + if level > 0 + make_weakref(level - 1) + else + WeakRef.new(Object.new) + end + end + + def self.make_dead_weakref + weaks = [] + weak = nil + 10_000.times do + weaks << make_weakref + GC.start + GC.start + break if weak = weaks.find { |w| !w.weakref_alive? } + end + weak + end +end diff --git a/spec/rubyspec/library/weakref/send_spec.rb b/spec/rubyspec/library/weakref/send_spec.rb new file mode 100644 index 0000000000..173e1055dd --- /dev/null +++ b/spec/rubyspec/library/weakref/send_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'weakref' + +describe "WeakRef#__send__" do + module WeakRefSpecs + class << self + def delegated_method + :result + end + + def protected_method + :result + end + protected :protected_method + + def private_method + :result + end + private :private_method + end + end + + it "delegates to public methods of the weakly-referenced object" do + wr = WeakRef.new(WeakRefSpecs) + wr.delegated_method.should == :result + end + + it "delegates to protected methods of the weakly-referenced object" do + wr = WeakRef.new(WeakRefSpecs) + lambda { wr.protected_method }.should raise_error(NameError) + end + + it "does not delegate to private methods of the weakly-referenced object" do + wr = WeakRef.new(WeakRefSpecs) + lambda { wr.private_method }.should raise_error(NameError) + end +end diff --git a/spec/rubyspec/library/weakref/weakref_alive_spec.rb b/spec/rubyspec/library/weakref/weakref_alive_spec.rb new file mode 100644 index 0000000000..b3c2eab620 --- /dev/null +++ b/spec/rubyspec/library/weakref/weakref_alive_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "WeakRef#weakref_alive?" do + it "returns true if the object is reachable" do + obj = Object.new + ref = WeakRef.new(obj) + ref.weakref_alive?.should == true + end + + it "returns a falsey value if the object is no longer reachable" do + ref = WeakRefSpec.make_dead_weakref + [false, nil].should include(ref.weakref_alive?) + end +end diff --git a/spec/rubyspec/library/win32ole/fixtures/classes.rb b/spec/rubyspec/library/win32ole/fixtures/classes.rb new file mode 100644 index 0000000000..830b1be0b5 --- /dev/null +++ b/spec/rubyspec/library/win32ole/fixtures/classes.rb @@ -0,0 +1,14 @@ +module WIN32OLESpecs + def self.new_ole(name) + retried = false + begin + WIN32OLE.new(name) + rescue WIN32OLERuntimeError => e + unless retried + retried = true + retry + end + raise e + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/_getproperty_spec.rb b/spec/rubyspec/library/win32ole/win32ole/_getproperty_spec.rb new file mode 100644 index 0000000000..650a641f5b --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/_getproperty_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#_getproperty" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "gets name" do + @ie._getproperty(0, [], []).should =~ /explorer/i + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/_invoke_spec.rb b/spec/rubyspec/library/win32ole/win32ole/_invoke_spec.rb new file mode 100644 index 0000000000..7a6a771014 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/_invoke_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#_invoke" do + before :each do + @shell = WIN32OLESpecs.new_ole 'Shell.application' + end + + it "raises ArgumentError if insufficient number of arguments are given" do + lambda { @shell._invoke() }.should raise_error ArgumentError + lambda { @shell._invoke(0) }.should raise_error ArgumentError + lambda { @shell._invoke(0, []) }.should raise_error ArgumentError + end + + it "dispatches the method bound to a specific ID" do + @shell._invoke(0x60020002, [0], [WIN32OLE::VARIANT::VT_VARIANT]).title.should =~ /Desktop/i + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/codepage_spec.rb b/spec/rubyspec/library/win32ole/win32ole/codepage_spec.rb new file mode 100644 index 0000000000..c84593871d --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/codepage_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE.codepage=" do + it "sets codepage" do + cp = WIN32OLE.codepage + WIN32OLE.codepage = WIN32OLE::CP_UTF8 + WIN32OLE.codepage.should == WIN32OLE::CP_UTF8 + WIN32OLE.codepage = cp + end + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/connect_spec.rb b/spec/rubyspec/library/win32ole/win32ole/connect_spec.rb new file mode 100644 index 0000000000..ee480a727c --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/connect_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE.connect" do + it "creates WIN32OLE object given valid argument" do + obj = WIN32OLE.connect("winmgmts:") + obj.should be_kind_of WIN32OLE + end + + it "raises TypeError when given invalid argument" do + lambda { WIN32OLE.connect 1 }.should raise_error TypeError + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/const_load_spec.rb b/spec/rubyspec/library/win32ole/win32ole/const_load_spec.rb new file mode 100644 index 0000000000..a3c50f215e --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/const_load_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE.const_load when passed Shell.Application OLE object" do + before :each do + @win32ole = WIN32OLESpecs.new_ole 'Shell.Application' + end + + it "loads constant SsfWINDOWS into WIN32OLE namespace" do + WIN32OLE.const_defined?(:SsfWINDOWS).should be_false + WIN32OLE.const_load @win32ole + WIN32OLE.const_defined?(:SsfWINDOWS).should be_true + end + end + + describe "WIN32OLE.const_load when namespace is specified" do + before :each do + module WIN32OLE_RUBYSPEC; end + @win32ole = WIN32OLESpecs.new_ole 'Shell.Application' + end + + it "loads constants into given namespace" do + module WIN32OLE_RUBYSPEC; end + + WIN32OLE_RUBYSPEC.const_defined?(:SsfWINDOWS).should be_false + WIN32OLE.const_load @win32ole, WIN32OLE_RUBYSPEC + WIN32OLE_RUBYSPEC.const_defined?(:SsfWINDOWS).should be_true + + end + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/constants_spec.rb b/spec/rubyspec/library/win32ole/win32ole/constants_spec.rb new file mode 100644 index 0000000000..2a04511305 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/constants_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE class" do + it "defines constant CP_ACP" do + WIN32OLE::CP_ACP.should == 0 + end + + it "defines constant CP_OEMCP" do + WIN32OLE::CP_OEMCP.should == 1 + end + + it "defines constant CP_MACCP" do + WIN32OLE::CP_MACCP.should == 2 + end + + it "defines constant CP_THREAD_ACP" do + WIN32OLE::CP_THREAD_ACP.should == 3 + end + + it "defines constant CP_SYMBOL" do + WIN32OLE::CP_SYMBOL.should == 42 + end + + it "defines constant CP_UTF7" do + WIN32OLE::CP_UTF7.should == 65000 + end + + it "defines constant CP_UTF8" do + WIN32OLE::CP_UTF8.should == 65001 + end + + it "defines constant LOCALE_SYSTEM_DEFAULT" do + WIN32OLE::LOCALE_SYSTEM_DEFAULT.should == 0x0800 + end + + it "defines constant LOCALE_USER_DEFAULT" do + WIN32OLE::LOCALE_USER_DEFAULT.should == 0x0400 + end + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/create_guid_spec.rb b/spec/rubyspec/library/win32ole/win32ole/create_guid_spec.rb new file mode 100644 index 0000000000..17a7df10be --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/create_guid_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE.create_guid" do + it "generates guid with valid format" do + WIN32OLE.create_guid.should =~ /^\{[A-Z0-9]{8}\-[A-Z0-9]{4}\-[A-Z0-9]{4}\-[A-Z0-9]{4}\-[A-Z0-9]{12}/ + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/invoke_spec.rb b/spec/rubyspec/library/win32ole/win32ole/invoke_spec.rb new file mode 100644 index 0000000000..41752de359 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/invoke_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#invoke" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "get name by invoking 'Name' OLE method" do + @ie.invoke('Name').should =~ /explorer/i + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/locale_spec.rb b/spec/rubyspec/library/win32ole/win32ole/locale_spec.rb new file mode 100644 index 0000000000..7cf4d9bc98 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/locale_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE.locale" do + it "gets locale" do + WIN32OLE.locale.should == WIN32OLE::LOCALE_SYSTEM_DEFAULT + end + end + + describe "WIN32OLE.locale=" do + it "sets locale to Japanese, if available" do + begin + begin + WIN32OLE.locale = 1041 + rescue WIN32OLERuntimeError + STDERR.puts("\n#{__FILE__}:#{__LINE__}:#{self.class.name}.test_s_locale_set is skipped(Japanese locale is not installed)") + return + end + + WIN32OLE.locale.should == 1041 + WIN32OLE.locale = WIN32OLE::LOCALE_SYSTEM_DEFAULT + lambda { WIN32OLE.locale = 111 }.should raise_error WIN32OLERuntimeError + WIN32OLE.locale.should == WIN32OLE::LOCALE_SYSTEM_DEFAULT + ensure + WIN32OLE.locale.should == WIN32OLE::LOCALE_SYSTEM_DEFAULT + end + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/new_spec.rb b/spec/rubyspec/library/win32ole/win32ole/new_spec.rb new file mode 100644 index 0000000000..78f141b608 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/new_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLESpecs.new_ole" do + it "creates a WIN32OLE object from OLE server name" do + shell = WIN32OLESpecs.new_ole 'Shell.Application' + shell.should be_kind_of WIN32OLE + end + + it "creates a WIN32OLE object from valid CLSID" do + shell = WIN32OLESpecs.new_ole("{13709620-C279-11CE-A49E-444553540000}") + shell.should be_kind_of WIN32OLE + end + + it "raises TypeError if argument cannot be converted to String" do + lambda { WIN32OLESpecs.new_ole(42) }.should raise_error( TypeError ) + end + + it "raises WIN32OLERuntimeError if invalid string is given" do + lambda { WIN32OLESpecs.new_ole('foo') }.should raise_error( WIN32OLERuntimeError ) + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/ole_func_methods_spec.rb b/spec/rubyspec/library/win32ole/win32ole/ole_func_methods_spec.rb new file mode 100644 index 0000000000..82b5eb2680 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/ole_func_methods_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#ole_func_methods" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "raises ArgumentError if argument is given" do + lambda { @ie.ole_func_methods(1) }.should raise_error ArgumentError + end + + it "returns an array of WIN32OLE_METHODs" do + @ie.ole_func_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true + end + + it "contains a 'AddRef' method for Internet Explorer" do + @ie.ole_func_methods.map { |m| m.name }.include?('AddRef').should be_true + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/ole_get_methods_spec.rb b/spec/rubyspec/library/win32ole/win32ole/ole_get_methods_spec.rb new file mode 100644 index 0000000000..2510d60512 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/ole_get_methods_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + + describe "WIN32OLE#ole_get_methods" do + + before :each do + @win32ole = WIN32OLESpecs.new_ole('Shell.Application') + end + + it "returns an array of WIN32OLE_METHOD objects" do + @win32ole.ole_get_methods.all? {|m| m.kind_of? WIN32OLE_METHOD}.should be_true + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/ole_method_help_spec.rb b/spec/rubyspec/library/win32ole/win32ole/ole_method_help_spec.rb new file mode 100644 index 0000000000..45b8e93eac --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/ole_method_help_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/ole_method', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#ole_method_help" do + it_behaves_like :win32ole_ole_method, :ole_method_help + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/ole_method_spec.rb b/spec/rubyspec/library/win32ole/win32ole/ole_method_spec.rb new file mode 100644 index 0000000000..cb1d1d172b --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/ole_method_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/ole_method', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#ole_method" do + it_behaves_like :win32ole_ole_method, :ole_method + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/ole_methods_spec.rb b/spec/rubyspec/library/win32ole/win32ole/ole_methods_spec.rb new file mode 100644 index 0000000000..bba2aa73ae --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/ole_methods_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#ole_methods" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "raises ArgumentError if argument is given" do + lambda { @ie.ole_methods(1) }.should raise_error ArgumentError + end + + it "returns an array of WIN32OLE_METHODs" do + @ie.ole_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true + end + + it "contains a 'AddRef' method for Internet Explorer" do + @ie.ole_methods.map { |m| m.name }.include?('AddRef').should be_true + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/ole_obj_help_spec.rb b/spec/rubyspec/library/win32ole/win32ole/ole_obj_help_spec.rb new file mode 100644 index 0000000000..eede93bb87 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/ole_obj_help_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#ole_obj_help" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "raises ArgumentError if argument is given" do + lambda { @ie.ole_obj_help(1) }.should raise_error ArgumentError + end + + it "returns an instance of WIN32OLE_TYPE" do + @ie.ole_obj_help.kind_of?(WIN32OLE_TYPE).should be_true + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/ole_put_methods_spec.rb b/spec/rubyspec/library/win32ole/win32ole/ole_put_methods_spec.rb new file mode 100644 index 0000000000..5334e7a47d --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/ole_put_methods_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#ole_put_methods" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "raises ArgumentError if argument is given" do + lambda { @ie.ole_put_methods(1) }.should raise_error ArgumentError + end + + it "returns an array of WIN32OLE_METHODs" do + @ie.ole_put_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true + end + + it "contains a 'Height' method for Internet Explorer" do + @ie.ole_put_methods.map { |m| m.name }.include?('Height').should be_true + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/setproperty_spec.rb b/spec/rubyspec/library/win32ole/win32ole/setproperty_spec.rb new file mode 100644 index 0000000000..ae08e63f78 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/setproperty_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../shared/setproperty', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE#setproperty" do + it_behaves_like :win32ole_setproperty, :setproperty + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole/shared/ole_method.rb b/spec/rubyspec/library/win32ole/win32ole/shared/ole_method.rb new file mode 100644 index 0000000000..668000c1fc --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/shared/ole_method.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe :win32ole_ole_method, shared: true do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "raises ArgumentError if no argument is given" do + lambda { @ie.send(@method) }.should raise_error ArgumentError + end + + it "returns the WIN32OLE_METHOD 'Quit' if given 'Quit'" do + result = @ie.send(@method, "Quit") + result.kind_of?(WIN32OLE_METHOD).should be_true + result.name.should == 'Quit' + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole/shared/setproperty.rb b/spec/rubyspec/library/win32ole/win32ole/shared/setproperty.rb new file mode 100644 index 0000000000..9703e402bf --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole/shared/setproperty.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe :win32ole_setproperty, shared: true do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit + end + + it "raises ArgumentError if no argument is given" do + lambda { @ie.send(@method) }.should raise_error ArgumentError + end + + it "sets height to 500 and returns nil" do + height = 500 + result = @ie.send(@method, 'Height', height) + result.should == nil + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_event/new_spec.rb b/spec/rubyspec/library/win32ole/win32ole_event/new_spec.rb new file mode 100644 index 0000000000..5fffac535e --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_event/new_spec.rb @@ -0,0 +1,33 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_EVENT.new" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + end + + after :each do + @ie.Quit if @ie + end + + it "raises TypeError given invalid argument" do + lambda { WIN32OLE_EVENT.new "A" }.should raise_error TypeError + end + + it "raises RuntimeError if event does not exist" do + lambda { WIN32OLE_EVENT.new(@ie, 'A') }.should raise_error RuntimeError + end + + it "raises RuntimeError if OLE object has no events" do + dict = WIN32OLESpecs.new_ole('Scripting.Dictionary') + lambda { WIN32OLE_EVENT.new(dict) }.should raise_error RuntimeError + end + + it "creates WIN32OLE_EVENT object" do + ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') + ev.should be_kind_of WIN32OLE_EVENT + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_event/on_event_spec.rb b/spec/rubyspec/library/win32ole/win32ole_event/on_event_spec.rb new file mode 100644 index 0000000000..6bf8d2e1da --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_event/on_event_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is :windows do + require 'win32ole' + + def default_handler(event, *args) + @event += event + end + + def alternate_handler(event, *args) + @event2 = "alternate" + end + + def handler3(event, *args) + @event3 += event + end + + + describe "WIN32OLE_EVENT#on_event with no argument" do + before :each do + @ie = WIN32OLESpecs.new_ole('InternetExplorer.Application') + @ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') + @event = '' + @event2 = '' + @event3 = '' + @ie.StatusBar = true + end + + after :each do + @ie.Quit + end + + it "sets event handler properly, and the handler is invoked by event loop" do + @ev.on_event { |*args| default_handler(*args) } + @ie.StatusText='hello' + WIN32OLE_EVENT.message_loop + @event.should =~ /StatusTextChange/ + end + + it "accepts a String argument, sets event handler properly, and the handler is invoked by event loop" do + @ev.on_event("StatusTextChange") { |*args| @event = 'foo' } + @ie.StatusText='hello' + WIN32OLE_EVENT.message_loop + @event.should =~ /foo/ + end + + it "registers multiple event handlers for the same event" do + @ev.on_event("StatusTextChange") { |*args| default_handler(*args) } + @ev.on_event("StatusTextChange") { |*args| alternate_handler(*args) } + @ie.StatusText= 'hello' + WIN32OLE_EVENT.message_loop + @event2.should == 'alternate' + end + + it "accepts a Symbol argument, sets event handler properly, and the handler is invoked by event loop" do + @ev.on_event(:StatusTextChange) { |*args| @event = 'foo' } + @ie.StatusText='hello' + WIN32OLE_EVENT.message_loop + @event.should =~ /foo/ + end + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/dispid_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/dispid_spec.rb new file mode 100644 index 0000000000..840cdf72b8 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/dispid_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#dispid" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @m = WIN32OLE_METHOD.new(ole_type, "namespace") + end + + it "raises ArgumentError if argument is given" do + lambda { @m.dispid(0) }.should raise_error ArgumentError + end + + it "returns expected dispatch ID for Shell's 'namespace' method" do + @m.dispid.should == 1610743810 # value found in MRI's test + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/event_interface_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/event_interface_spec.rb new file mode 100644 index 0000000000..9d3ca2b8e4 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/event_interface_spec.rb @@ -0,0 +1,26 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#event_interface" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Internet Controls", "WebBrowser") + @navigate_method = WIN32OLE_METHOD.new(ole_type, "NavigateComplete") + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @namespace_method = WIN32OLE_METHOD.new(ole_type, "namespace") + end + + it "raises ArgumentError if argument is given" do + lambda { @navigate_method.event_interface(1) }.should raise_error ArgumentError + end + + it "returns expected string for browser's 'NavigateComplete' method" do + @navigate_method.event_interface.should == "DWebBrowserEvents" + end + + it "returns nil if method has no event interface" do + @namespace_method.event_interface.should be_nil + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/event_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/event_spec.rb new file mode 100644 index 0000000000..25435438aa --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/event_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#event?" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Internet Controls", "WebBrowser") + @navigate_method = WIN32OLE_METHOD.new(ole_type, "NavigateComplete") + end + + it "raises ArgumentError if argument is given" do + lambda { @navigate_method.event?(1) }.should raise_error ArgumentError + end + + it "returns true for browser's 'NavigateComplete' method" do + @navigate_method.event?.should be_true + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/helpcontext_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/helpcontext_spec.rb new file mode 100644 index 0000000000..c0e66a7a18 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/helpcontext_spec.rb @@ -0,0 +1,26 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#helpcontext" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Internet Controls", "WebBrowser") + @navigate_method = WIN32OLE_METHOD.new(ole_type, "NavigateComplete") + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @navigate_method.helpcontext(1) }.should raise_error ArgumentError + end + + it "returns expected value for browser's 'NavigateComplete' method" do + @navigate_method.helpcontext.should == 0 + end + + it "returns expected value for Scripting Runtime's 'name' method" do + @m_file_name.helpcontext.should == 2181996 # value indicated in MRI's test + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/helpfile_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/helpfile_spec.rb new file mode 100644 index 0000000000..72cc4da16b --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/helpfile_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#helpfile" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.helpfile(1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'File' method" do + @m_file_name.helpfile.should =~ /VBENLR.*\.CHM$/i + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/helpstring_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/helpstring_spec.rb new file mode 100644 index 0000000000..60105d0aa2 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/helpstring_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#helpstring" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.helpstring(1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'File' method" do + @m_file_name.helpstring.should == "Get name of file" # value indicated in MRI's test + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/invkind_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/invkind_spec.rb new file mode 100644 index 0000000000..cf0a74bbce --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/invkind_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#invkind" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.invkind(1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'name' method" do + @m_file_name.invkind.should == 2 + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/invoke_kind_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/invoke_kind_spec.rb new file mode 100644 index 0000000000..4d2af8fb0c --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/invoke_kind_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#invoke_kind" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.invoke_kind(1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'name' method" do + @m_file_name.invoke_kind.should =~ /^(UNKNOWN|PROPERTY|PROPERTYGET|PROPERTYPUT|PROPERTYPUTREF|FUNC)$/ + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/name_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/name_spec.rb new file mode 100644 index 0000000000..0c2b3eeba0 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/name_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#name" do + it_behaves_like :win32ole_method_name, :name + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/new_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/new_spec.rb new file mode 100644 index 0000000000..fc8d1d7691 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/new_spec.rb @@ -0,0 +1,33 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD.new" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + it "raises TypeError when given non-strings" do + lambda { WIN32OLE_METHOD.new(1, 2) }.should raise_error TypeError + end + + it "raises ArgumentError if only 1 arugment is given" do + lambda { WIN32OLE_METHOD.new("hello") }.should raise_error ArgumentError + lambda { WIN32OLE_METHOD.new(@ole_type) }.should raise_error ArgumentError + end + + it "returns a valid WIN32OLE_METHOD object" do + WIN32OLE_METHOD.new(@ole_type, "Open").should be_kind_of WIN32OLE_METHOD + WIN32OLE_METHOD.new(@ole_type, "open").should be_kind_of WIN32OLE_METHOD + end + + it "raises WIN32OLERuntimeError if the method does not exist" do + lambda { WIN32OLE_METHOD.new(@ole_type, "NonexistentMethod") }.should raise_error WIN32OLERuntimeError + end + + it "raises TypeError if second argument is not a String" do + lambda { WIN32OLE_METHOD.new(@ole_type, 5) }.should raise_error TypeError + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/offset_vtbl_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/offset_vtbl_spec.rb new file mode 100644 index 0000000000..e436441409 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/offset_vtbl_spec.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#offset_vtbl" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.offset_vtbl(1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'name' method" do + pointer_size = RUBY_PLATFORM =~ /\bx64\b/ ? 64 : 1.size * 8 + @m_file_name.offset_vtbl.should == pointer_size + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/params_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/params_spec.rb new file mode 100644 index 0000000000..08c7f04bdd --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/params_spec.rb @@ -0,0 +1,28 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#params" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.params(1) }.should raise_error ArgumentError + end + + it "returns empty array for Scripting Runtime's 'name' method" do + @m_file_name.params.should be_kind_of Array + @m_file_name.params.should be_empty + end + + it "returns 4-element array of WIN32OLE_PARAM for Shell's 'BrowseForFolder' method" do + @m_browse_for_folder.params.all? { |p| p.kind_of? WIN32OLE_PARAM }.should be_true + @m_browse_for_folder.params.size == 4 + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/return_type_detail_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/return_type_detail_spec.rb new file mode 100644 index 0000000000..b8f2bbe084 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/return_type_detail_spec.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#return_type_detail" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_browse_for_folder.return_type_detail(1) }.should raise_error ArgumentError + end + + it "returns expected value for Shell Control's 'BrowseForFolder' method" do + @m_browse_for_folder.return_type_detail.should be_kind_of Array + @m_browse_for_folder.return_type_detail.should == ['PTR', 'USERDEFINED', 'Folder'] + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/return_type_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/return_type_spec.rb new file mode 100644 index 0000000000..b68481fe59 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/return_type_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#return_type" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.return_type(1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'name' method" do + @m_file_name.return_type.should == 'BSTR' + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/return_vtype_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/return_vtype_spec.rb new file mode 100644 index 0000000000..f236de01f9 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/return_vtype_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#return_vtype" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_browse_for_folder.return_vtype(1) }.should raise_error ArgumentError + end + + it "returns expected value for Shell Control's 'BrowseForFolder' method" do + @m_browse_for_folder.return_vtype.should == 26 + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/shared/name.rb b/spec/rubyspec/library/win32ole/win32ole_method/shared/name.rb new file mode 100644 index 0000000000..2be6478a6e --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/shared/name.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe :win32ole_method_name, shared: true do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "File") + @m_file_name = WIN32OLE_METHOD.new(ole_type, "name") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_file_name.send(@method, 1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'name' method" do + @m_file_name.send(@method).should == 'Name' # note the capitalization + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/size_opt_params_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/size_opt_params_spec.rb new file mode 100644 index 0000000000..a63c50474c --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/size_opt_params_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#size_opt_params" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_browse_for_folder.size_opt_params(1) }.should raise_error ArgumentError + end + + it "returns expected value for Shell Control's 'BrowseForFolder' method" do + @m_browse_for_folder.size_opt_params.should == 1 + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/size_params_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/size_params_spec.rb new file mode 100644 index 0000000000..fe93d5bc66 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/size_params_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#size_params" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_browse_for_folder.size_params(1) }.should raise_error ArgumentError + end + + it "returns expected value for Shell Control's 'BrowseForFolder' method" do + @m_browse_for_folder.size_params.should == 4 + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/to_s_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/to_s_spec.rb new file mode 100644 index 0000000000..95fd2fcdb5 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/to_s_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#name" do + it_behaves_like :win32ole_method_name, :to_s + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_method/visible_spec.rb b/spec/rubyspec/library/win32ole/win32ole_method/visible_spec.rb new file mode 100644 index 0000000000..b49fac6066 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_method/visible_spec.rb @@ -0,0 +1,20 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_METHOD#visible?" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + @m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder") + end + + it "raises ArgumentError if argument is given" do + lambda { @m_browse_for_folder.visible?(1) }.should raise_error ArgumentError + end + + it "returns true for Shell Control's 'BrowseForFolder' method" do + @m_browse_for_folder.visible?.should be_true + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/default_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/default_spec.rb new file mode 100644 index 0000000000..7a1337ec7c --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/default_spec.rb @@ -0,0 +1,31 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#default" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + m_browse_for_folder = WIN32OLE_METHOD.new(ole_type, "BrowseForFolder") + @params = m_browse_for_folder.params + + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject") + m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile") + @param_overwritefiles = m_copyfile.params[2] + end + + it "raises ArgumentError if argument is given" do + lambda { @params[0].default(1) }.should raise_error ArgumentError + end + + it "returns nil for each of WIN32OLE_PARAM for Shell's 'BrowseForFolder' method" do + @params.each do |p| + p.default.should be_nil + end + end + + it "returns true for 3rd parameter of FileSystemObject's 'CopyFile' method" do + @param_overwritefiles.default.should == true # not be_true + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/input_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/input_spec.rb new file mode 100644 index 0000000000..bdf4bccc79 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/input_spec.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#input?" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject") + m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile") + @param_overwritefiles = m_copyfile.params[2] + end + + it "raises ArgumentError if argument is given" do + lambda { @param_overwritefiles.input?(1) }.should raise_error ArgumentError + end + + it "returns true for 3rd parameter of FileSystemObject's 'CopyFile' method" do + @param_overwritefiles.input?.should == true + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/name_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/name_spec.rb new file mode 100644 index 0000000000..b3c947d6fb --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/name_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#name" do + it_behaves_like :win32ole_param_name, :name + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/ole_type_detail_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/ole_type_detail_spec.rb new file mode 100644 index 0000000000..3ba51c02f1 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/ole_type_detail_spec.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#ole_type_detail" do + before :each do + ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject") + m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile") + @param_overwritefiles = m_copyfile.params[2] + end + + it "raises ArgumentError if argument is given" do + lambda { @param_overwritefiles.ole_type_detail(1) }.should raise_error ArgumentError + end + + it "returns ['BOOL'] for 3rd parameter of FileSystemObject's 'CopyFile' method" do + @param_overwritefiles.ole_type_detail.should == ['BOOL'] + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/ole_type_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/ole_type_spec.rb new file mode 100644 index 0000000000..52bee2c9b8 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/ole_type_spec.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#ole_type" do + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject") + m_copyfile = WIN32OLE_METHOD.new(ole_type, "CopyFile") + @param_overwritefiles = m_copyfile.params[2] + end + + it "raises ArgumentError if argument is given" do + lambda { @param_overwritefiles.ole_type(1) }.should raise_error ArgumentError + end + + it "returns 'BOOL' for 3rd parameter of FileSystemObject's 'CopyFile' method" do + @param_overwritefiles.ole_type.should == 'BOOL' + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/optional_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/optional_spec.rb new file mode 100644 index 0000000000..2476df8641 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/optional_spec.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#optional?" do + before :each do + ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject") + m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile") + @param_overwritefiles = m_copyfile.params[2] + end + + it "raises ArgumentError if argument is given" do + lambda { @param_overwritefiles.optional?(1) }.should raise_error ArgumentError + end + + it "returns true for 3rd parameter of FileSystemObject's 'CopyFile' method" do + @param_overwritefiles.optional?.should be_true + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/retval_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/retval_spec.rb new file mode 100644 index 0000000000..90946c0774 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/retval_spec.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#retval?" do + before :each do + ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject") + m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile") + @param_overwritefiles = m_copyfile.params[2] + end + + it "raises ArgumentError if argument is given" do + lambda { @param_overwritefiles.retval?(1) }.should raise_error ArgumentError + end + + it "returns false for 3rd parameter of FileSystemObject's 'CopyFile' method" do + @param_overwritefiles.retval?.should be_false + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/shared/name.rb b/spec/rubyspec/library/win32ole/win32ole_param/shared/name.rb new file mode 100644 index 0000000000..b7892d92fb --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/shared/name.rb @@ -0,0 +1,21 @@ +platform_is :windows do + require 'win32ole' + + describe :win32ole_param_name, shared: true do + before :each do + ole_type_detail = WIN32OLE_TYPE.new("Microsoft Scripting Runtime", "FileSystemObject") + m_copyfile = WIN32OLE_METHOD.new(ole_type_detail, "CopyFile") + @param_overwritefiles = m_copyfile.params[2] + end + + it "raises ArgumentError if argument is given" do + lambda { @param_overwritefiles.send(@method, 1) }.should raise_error ArgumentError + end + + it "returns expected value for Scripting Runtime's 'name' method" do + @param_overwritefiles.send(@method).should == 'OverWriteFiles' # note the capitalization + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_param/to_s_spec.rb b/spec/rubyspec/library/win32ole/win32ole_param/to_s_spec.rb new file mode 100644 index 0000000000..7852bb0494 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_param/to_s_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_PARAM#to_s" do + it_behaves_like :win32ole_param_name, :to_s + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/guid_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/guid_spec.rb new file mode 100644 index 0000000000..25907c8e32 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/guid_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#guid for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns String with expected format" do + @ole_type.guid.should =~ /\A\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}\z/ + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/helpcontext_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/helpcontext_spec.rb new file mode 100644 index 0000000000..d436835188 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/helpcontext_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#helpcontext for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns an Integer" do + @ole_type.helpcontext.should be_kind_of Integer + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/helpfile_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/helpfile_spec.rb new file mode 100644 index 0000000000..01e6945138 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/helpfile_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#helpfile for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns an empty string" do + @ole_type.helpfile.should be_empty + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/helpstring_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/helpstring_spec.rb new file mode 100644 index 0000000000..3bd2cbe5dd --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/helpstring_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#helpstring for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns expected string" do + @ole_type.helpstring.should == "Shell Object Type Information" + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/major_version_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/major_version_spec.rb new file mode 100644 index 0000000000..7dae16617d --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/major_version_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#major_version for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns an Integer" do + @ole_type.major_version.should be_kind_of Integer + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/minor_version_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/minor_version_spec.rb new file mode 100644 index 0000000000..ff412dd100 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/minor_version_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#minor_version for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns an Integer" do + @ole_type.minor_version.should be_kind_of Integer + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/name_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/name_spec.rb new file mode 100644 index 0000000000..b395cf05d5 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/name_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#name" do + it_behaves_like :win32ole_type_name, :name + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/new_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/new_spec.rb new file mode 100644 index 0000000000..9443a64c75 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/new_spec.rb @@ -0,0 +1,37 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE.new" do + it "raises ArgumentError with no argument" do + lambda { WIN32OLE_TYPE.new }.should raise_error ArgumentError + end + + it "raises ArgumentError with invalid string" do + lambda { WIN32OLE_TYPE.new("foo") }.should raise_error ArgumentError + end + + it "raises TypeError if second argument is not a String" do + lambda { WIN32OLE_TYPE.new(1,2) }.should raise_error TypeError + lambda { WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation',2) }. + should raise_error TypeError + end + + it "raise WIN32OLERuntimeError if OLE object specified is not found" do + lambda { WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation','foo') }. + should raise_error WIN32OLERuntimeError + lambda { WIN32OLE_TYPE.new('Microsoft Shell Controls And Automation','Application') }. + should raise_error WIN32OLERuntimeError + end + + it "creates WIN32OLE_TYPE object from name and valid type" do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + ole_type.should be_kind_of WIN32OLE_TYPE + end + + it "creates WIN32OLE_TYPE object from CLSID and valid type" do + ole_type2 = WIN32OLE_TYPE.new("{13709620-C279-11CE-A49E-444553540000}", "Shell") + ole_type2.should be_kind_of WIN32OLE_TYPE + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/ole_classes_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/ole_classes_spec.rb new file mode 100644 index 0000000000..0ce0fc98a4 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/ole_classes_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE.ole_classes for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns array of WIN32OLE_TYPEs" do + WIN32OLE_TYPE.ole_classes("Microsoft Shell Controls And Automation").all? {|e| e.kind_of? WIN32OLE_TYPE }.should be_true + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/ole_methods_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/ole_methods_spec.rb new file mode 100644 index 0000000000..9265549d20 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/ole_methods_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#ole_methods for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns an Integer" do + @ole_type.ole_methods.all? { |m| m.kind_of? WIN32OLE_METHOD }.should be_true + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/ole_type_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/ole_type_spec.rb new file mode 100644 index 0000000000..2bc19aa85e --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/ole_type_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#ole_type for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns string 'Class'" do + @ole_type.ole_type.should == "Class" + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/progid_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/progid_spec.rb new file mode 100644 index 0000000000..f0d80ba39e --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/progid_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#progid for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns expected string" do + @ole_type.progid.should == "Shell.Application.1" + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/progids_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/progids_spec.rb new file mode 100644 index 0000000000..0cdd3514e3 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/progids_spec.rb @@ -0,0 +1,14 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE.progids" do + it "raises ArgumentError if an argument is given" do + lambda { WIN32OLE_TYPE.progids(1) }.should raise_error ArgumentError + end + + it "returns an array containing 'Shell.Explorer'" do + WIN32OLE_TYPE.progids().include?('Shell.Explorer').should be_true + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/shared/name.rb b/spec/rubyspec/library/win32ole/win32ole_type/shared/name.rb new file mode 100644 index 0000000000..6484ef0ef8 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/shared/name.rb @@ -0,0 +1,19 @@ +platform_is :windows do + require 'win32ole' + + describe :win32ole_type_name, shared: true do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + end + + it "raises ArgumentError if argument is given" do + lambda { @ole_type.send(@method, 1) }.should raise_error ArgumentError + end + + it "returns a String" do + @ole_type.send(@method).should == 'ShellSpecialFolderConstants' + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/src_type_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/src_type_spec.rb new file mode 100644 index 0000000000..71e304d80a --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/src_type_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#src_type for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns nil" do + @ole_type.src_type.should be_nil + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/to_s_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/to_s_spec.rb new file mode 100644 index 0000000000..5a50bd11de --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/to_s_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#to_s" do + it_behaves_like :win32ole_type_name, :to_s + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/typekind_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/typekind_spec.rb new file mode 100644 index 0000000000..35f3562721 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/typekind_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#typekind for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns an Integer" do + @ole_type.typekind.should be_kind_of Integer + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/typelibs_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/typelibs_spec.rb new file mode 100644 index 0000000000..3a28c0496c --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/typelibs_spec.rb @@ -0,0 +1,22 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE.typelibs for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "raises ArgumentError if any argument is give" do + lambda { WIN32OLE_TYPE.typelibs(1) }.should raise_error ArgumentError + end + + it "returns array of type libraries" do + WIN32OLE_TYPE.typelibs().include?("Microsoft Shell Controls And Automation").should be_true + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/variables_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/variables_spec.rb new file mode 100644 index 0000000000..fbf3dd0341 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/variables_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#variables for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns an empty array" do + @ole_type.variables.should == [] + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_type/visible_spec.rb b/spec/rubyspec/library/win32ole/win32ole_type/visible_spec.rb new file mode 100644 index 0000000000..403b2b843b --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_type/visible_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_TYPE#visible? for Shell Controls" do + before :each do + @ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "Shell") + end + + after :each do + @ole_type = nil + end + + it "returns true" do + @ole_type.visible?.should be_true + end + + end +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/name_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/name_spec.rb new file mode 100644 index 0000000000..724fd5c70a --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/name_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#name" do + it_behaves_like :win32ole_variable_new, :name + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/ole_type_detail_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/ole_type_detail_spec.rb new file mode 100644 index 0000000000..dab4edabaa --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/ole_type_detail_spec.rb @@ -0,0 +1,19 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#ole_type_detail" do + # not sure how WIN32OLE_VARIABLE objects are supposed to be generated + # WIN32OLE_VARIABLE.new even seg faults in some cases + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + @var = ole_type.variables[0] + end + + it "returns a nonempty Array" do + @var.ole_type_detail.should be_kind_of Array + @var.ole_type_detail.should_not be_empty + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/ole_type_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/ole_type_spec.rb new file mode 100644 index 0000000000..d08acc9bde --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/ole_type_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#ole_type" do + # not sure how WIN32OLE_VARIABLE objects are supposed to be generated + # WIN32OLE_VARIABLE.new even seg faults in some cases + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + @var = ole_type.variables[0] + end + + it "returns a String" do + @var.ole_type.should be_kind_of String + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/shared/name.rb b/spec/rubyspec/library/win32ole/win32ole_variable/shared/name.rb new file mode 100644 index 0000000000..033e830fac --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/shared/name.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe :win32ole_variable_new, shared: true do + # not sure how WIN32OLE_VARIABLE objects are supposed to be generated + # WIN32OLE_VARIABLE.new even seg faults in some cases + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + @var = ole_type.variables[0] + end + + it "returns a String" do + @var.send(@method).should be_kind_of String + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/to_s_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/to_s_spec.rb new file mode 100644 index 0000000000..9853f91801 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/to_s_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../shared/name', __FILE__) + +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#to_s" do + it_behaves_like :win32ole_variable_new, :to_s + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/value_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/value_spec.rb new file mode 100644 index 0000000000..c15f64c2c5 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/value_spec.rb @@ -0,0 +1,19 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#value" do + # not sure how WIN32OLE_VARIABLE objects are supposed to be generated + # WIN32OLE_VARIABLE.new even seg faults in some cases + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + @var = ole_type.variables[0] + end + + it "returns a Integer" do + # according to doc, this could return nil + @var.value.should be_kind_of Integer + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/variable_kind_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/variable_kind_spec.rb new file mode 100644 index 0000000000..4cca7f8874 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/variable_kind_spec.rb @@ -0,0 +1,19 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#variable_kind" do + # not sure how WIN32OLE_VARIABLE objects are supposed to be generated + # WIN32OLE_VARIABLE.new even seg faults in some cases + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + @var = ole_type.variables[0] + end + + it "returns a String" do + @var.variable_kind.should be_kind_of String + @var.variable_kind.should == 'CONSTANT' + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/varkind_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/varkind_spec.rb new file mode 100644 index 0000000000..56cd1c337a --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/varkind_spec.rb @@ -0,0 +1,19 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#varkind" do + # TODO review + # not sure how WIN32OLE_VARIABLE objects are supposed to be generated + # WIN32OLE_VARIABLE.new even seg faults in some cases + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + @var = ole_type.variables[0] + end + + it "returns an Integer" do + @var.varkind.should be_kind_of Integer + end + + end + +end diff --git a/spec/rubyspec/library/win32ole/win32ole_variable/visible_spec.rb b/spec/rubyspec/library/win32ole/win32ole_variable/visible_spec.rb new file mode 100644 index 0000000000..7f7a557b57 --- /dev/null +++ b/spec/rubyspec/library/win32ole/win32ole_variable/visible_spec.rb @@ -0,0 +1,18 @@ +platform_is :windows do + require 'win32ole' + + describe "WIN32OLE_VARIABLE#visible?" do + # not sure how WIN32OLE_VARIABLE objects are supposed to be generated + # WIN32OLE_VARIABLE.new even seg faults in some cases + before :each do + ole_type = WIN32OLE_TYPE.new("Microsoft Shell Controls And Automation", "ShellSpecialFolderConstants") + @var = ole_type.variables[0] + end + + it "returns a String" do + @var.visible?.should be_true + end + + end + +end diff --git a/spec/rubyspec/library/yaml/add_builtin_type_spec.rb b/spec/rubyspec/library/yaml/add_builtin_type_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/add_builtin_type_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/add_domain_type_spec.rb b/spec/rubyspec/library/yaml/add_domain_type_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/add_domain_type_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/add_private_type_spec.rb b/spec/rubyspec/library/yaml/add_private_type_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/add_private_type_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/add_ruby_type_spec.rb b/spec/rubyspec/library/yaml/add_ruby_type_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/add_ruby_type_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/detect_implicit_spec.rb b/spec/rubyspec/library/yaml/detect_implicit_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/detect_implicit_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/dump_spec.rb b/spec/rubyspec/library/yaml/dump_spec.rb new file mode 100644 index 0000000000..10b29ced2b --- /dev/null +++ b/spec/rubyspec/library/yaml/dump_spec.rb @@ -0,0 +1,47 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +# TODO: WTF is this using a global? +describe "YAML.dump" do + after :each do + rm_r $test_file + end + + it "converts an object to YAML and write result to io when io provided" do + File.open($test_file, 'w' ) do |io| + YAML.dump( ['badger', 'elephant', 'tiger'], io ) + end + YAML.load_file($test_file).should == ['badger', 'elephant', 'tiger'] + end + + it "returns a string containing dumped YAML when no io provided" do + YAML.dump( :locked ).should match_yaml("--- :locked\n") + end + + it "returns the same string that #to_yaml on objects" do + ["a", "b", "c"].to_yaml.should == YAML.dump(["a", "b", "c"]) + end + + it "dumps strings into YAML strings" do + YAML.dump("str").should match_yaml("--- str\n") + end + + it "dumps hashes into YAML key-values" do + YAML.dump({ "a" => "b" }).should match_yaml("--- \na: b\n") + end + + it "dumps Arrays into YAML collection" do + YAML.dump(["a", "b", "c"]).should match_yaml("--- \n- a\n- b\n- c\n") + end + + it "dumps an OpenStruct" do + require "ostruct" + os = OpenStruct.new("age" => 20, "name" => "John") + YAML.dump(os).should match_yaml("--- !ruby/object:OpenStruct\ntable:\n :age: 20\n :name: John\n") + end + + it "dumps a File without any state" do + file = File.new(__FILE__) + YAML.dump(file).should match_yaml("--- !ruby/object:File {}\n") + end +end diff --git a/spec/rubyspec/library/yaml/dump_stream_spec.rb b/spec/rubyspec/library/yaml/dump_stream_spec.rb new file mode 100644 index 0000000000..918b62607f --- /dev/null +++ b/spec/rubyspec/library/yaml/dump_stream_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "YAML.dump_stream" do + it "returns a YAML stream containing the objects passed" do + YAML.dump_stream('foo', 20, [], {}).should match_yaml("--- foo\n--- 20\n--- []\n\n--- {}\n\n") + end +end diff --git a/spec/rubyspec/library/yaml/each_node_spec.rb b/spec/rubyspec/library/yaml/each_node_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/each_node_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/emitter_spec.rb b/spec/rubyspec/library/yaml/emitter_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/emitter_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/fixtures/common.rb b/spec/rubyspec/library/yaml/fixtures/common.rb new file mode 100644 index 0000000000..1d868806f1 --- /dev/null +++ b/spec/rubyspec/library/yaml/fixtures/common.rb @@ -0,0 +1,10 @@ +begin + require 'syck' +rescue LoadError + # do nothing +end + +require 'yaml' + +$test_file = tmp("yaml_test_file") +$test_parse_file = File.dirname(__FILE__) + "/test_yaml.yml" diff --git a/spec/rubyspec/library/yaml/fixtures/example_class.rb b/spec/rubyspec/library/yaml/fixtures/example_class.rb new file mode 100644 index 0000000000..751435a305 --- /dev/null +++ b/spec/rubyspec/library/yaml/fixtures/example_class.rb @@ -0,0 +1,5 @@ +class FooBar + def initialize(name) + @name = name + end +end diff --git a/spec/rubyspec/library/yaml/fixtures/strings.rb b/spec/rubyspec/library/yaml/fixtures/strings.rb new file mode 100644 index 0000000000..6f66dc3659 --- /dev/null +++ b/spec/rubyspec/library/yaml/fixtures/strings.rb @@ -0,0 +1,36 @@ +$complex_key_1 = <2, "car"=>1}, io ) } + YAML.load_file($test_file).should == {"bar"=>2, "car"=>1} + end +end diff --git a/spec/rubyspec/library/yaml/load_spec.rb b/spec/rubyspec/library/yaml/load_spec.rb new file mode 100644 index 0000000000..7c5a33d52f --- /dev/null +++ b/spec/rubyspec/library/yaml/load_spec.rb @@ -0,0 +1,116 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../fixtures/strings', __FILE__) + +describe "YAML.load" do + after :each do + rm_r $test_file + end + + it "returns a document from current io stream when io provided" do + File.open($test_file, 'w') do |io| + YAML.dump( ['badger', 'elephant', 'tiger'], io ) + end + File.open($test_file) { |yf| YAML.load( yf ) }.should == ['badger', 'elephant', 'tiger'] + end + + it "loads strings" do + strings = ["str", + " str", + "'str'", + "str", + " str", + "'str'", + "\"str\"", + "\n str", + "--- str", + "---\nstr", + "--- \nstr", + "--- \n str", + "--- 'str'" + ] + strings.each do |str| + YAML.load(str).should == "str" + end + end + + it "fails on invalid keys" do + if YAML.to_s == "Psych" + error = Psych::SyntaxError + else + error = ArgumentError + end + lambda { YAML.load("key1: value\ninvalid_key") }.should raise_error(error) + end + + it "accepts symbols" do + YAML.load( "--- :locked" ).should == :locked + end + + it "accepts numbers" do + YAML.load("47").should == 47 + YAML.load("-1").should == -1 + end + + it "accepts collections" do + expected = ["a", "b", "c"] + YAML.load("--- \n- a\n- b\n- c\n").should == expected + YAML.load("--- [a, b, c]").should == expected + YAML.load("[a, b, c]").should == expected + end + + it "parses start markers" do + YAML.load("---\n").should == nil + YAML.load("--- ---\n").should == "---" + YAML.load("--- abc").should == "abc" + end + + it "works with block sequence shortcuts" do + block_seq = "- - - one\n - two\n - three" + YAML.load(block_seq).should == [[["one", "two", "three"]]] + end + + it "works on complex keys" do + require 'date' + expected = { + [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ], + [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), + Date.new( 2001, 8, 12 ), + Date.new( 2001, 8, 14 ) ] + } + YAML.load($complex_key_1).should == expected + end + + it "loads a symbol key that contains spaces" do + string = ":user name: This is the user name." + expected = { :"user name" => "This is the user name."} + YAML.load(string).should == expected + end + + describe "with iso8601 timestamp" do + it "computes the microseconds" do + [ [YAML.load("2011-03-22t23:32:11.2233+01:00"), 223300], + [YAML.load("2011-03-22t23:32:11.0099+01:00"), 9900], + [YAML.load("2011-03-22t23:32:11.000076+01:00"), 76] + ].should be_computed_by(:usec) + end + + it "rounds values smaller than 1 usec to 0 " do + YAML.load("2011-03-22t23:32:11.000000342222+01:00").usec.should == 0 + end + end + + it "loads an OpenStruct" do + require "ostruct" + os = OpenStruct.new("age" => 20, "name" => "John") + loaded = YAML.load("--- !ruby/object:OpenStruct\ntable:\n :age: 20\n :name: John\n") + loaded.should == os + end + + it "loads a File but raise an error when used as it is uninitialized" do + loaded = YAML.load("--- !ruby/object:File {}\n") + lambda { + loaded.read(1) + }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/yaml/load_stream_spec.rb b/spec/rubyspec/library/yaml/load_stream_spec.rb new file mode 100644 index 0000000000..f134f4642f --- /dev/null +++ b/spec/rubyspec/library/yaml/load_stream_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../fixtures/strings', __FILE__) +require File.expand_path('../shared/each_document', __FILE__) + +describe "YAML.load_stream" do + it_behaves_like :yaml_each_document, :load_stream +end diff --git a/spec/rubyspec/library/yaml/object_maker_spec.rb b/spec/rubyspec/library/yaml/object_maker_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/object_maker_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/parse_documents_spec.rb b/spec/rubyspec/library/yaml/parse_documents_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/parse_documents_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/parse_file_spec.rb b/spec/rubyspec/library/yaml/parse_file_spec.rb new file mode 100644 index 0000000000..d8980135f5 --- /dev/null +++ b/spec/rubyspec/library/yaml/parse_file_spec.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "YAML#parse_file" do + quarantine! do + it "returns a YAML::Syck::Map object after parsing a YAML file" do + YAML.parse_file($test_parse_file).should be_kind_of(YAML::Syck::Map) + end + end +end diff --git a/spec/rubyspec/library/yaml/parse_spec.rb b/spec/rubyspec/library/yaml/parse_spec.rb new file mode 100644 index 0000000000..137fc23cf8 --- /dev/null +++ b/spec/rubyspec/library/yaml/parse_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +describe "YAML#parse with an empty string" do + it "returns false" do + YAML.parse('').should be_false + end +end + +describe "YAML#parse" do + before :each do + @string_yaml = "foo".to_yaml + end + + it "returns the value from the object" do + if YAML.to_s == "Psych" + YAML.parse(@string_yaml).to_ruby.should == "foo" + else + YAML.parse(@string_yaml).value.should == "foo" + end + end +end diff --git a/spec/rubyspec/library/yaml/parser_spec.rb b/spec/rubyspec/library/yaml/parser_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/parser_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/quick_emit_spec.rb b/spec/rubyspec/library/yaml/quick_emit_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/quick_emit_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/read_type_class_spec.rb b/spec/rubyspec/library/yaml/read_type_class_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/read_type_class_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/shared/each_document.rb b/spec/rubyspec/library/yaml/shared/each_document.rb new file mode 100644 index 0000000000..5d9d240005 --- /dev/null +++ b/spec/rubyspec/library/yaml/shared/each_document.rb @@ -0,0 +1,18 @@ +describe :yaml_each_document, shared: true do + it "calls the block on each succesive document" do + documents = [] + YAML.send(@method, $multidocument) do |doc| + documents << doc + end + documents.should == [["Mark McGwire", "Sammy Sosa", "Ken Griffey"], + ["Chicago Cubs", "St Louis Cardinals"]] + end + + it "works on files" do + File.open($test_parse_file, "r") do |file| + YAML.send(@method, file) do |doc| + doc.should == {"project"=>{"name"=>"RubySpec"}} + end + end + end +end diff --git a/spec/rubyspec/library/yaml/tagurize_spec.rb b/spec/rubyspec/library/yaml/tagurize_spec.rb new file mode 100644 index 0000000000..f6025cbea3 --- /dev/null +++ b/spec/rubyspec/library/yaml/tagurize_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) + +ruby_version_is ''...'2.5' do + describe "YAML.tagurize" do + it "converts a type_id to a taguri" do + YAML.tagurize('wtf').should == "tag:yaml.org,2002:wtf" + YAML.tagurize(1).should == 1 + end + end +end diff --git a/spec/rubyspec/library/yaml/to_yaml_spec.rb b/spec/rubyspec/library/yaml/to_yaml_spec.rb new file mode 100644 index 0000000000..d129fe2d79 --- /dev/null +++ b/spec/rubyspec/library/yaml/to_yaml_spec.rb @@ -0,0 +1,99 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) +require File.expand_path('../fixtures/example_class', __FILE__) + +describe "Object#to_yaml" do + + it "returns the YAML representation of an Array object" do + %w( 30 ruby maz irb 99 ).to_yaml.gsub("'", '"').should match_yaml("--- \n- \"30\"\n- ruby\n- maz\n- irb\n- \"99\"\n") + end + + it "returns the YAML representation of a Hash object" do + { "a" => "b"}.to_yaml.should match_yaml("--- \na: b\n") + end + + it "returns the YAML representation of a Class object" do + FooBar.new("baz").to_yaml.should match_yaml("--- !ruby/object:FooBar\nname: baz\n") + + end + + it "returns the YAML representation of a Date object" do + require 'date' + Date.parse('1997/12/30').to_yaml.should match_yaml("--- 1997-12-30\n") + end + + it "returns the YAML representation of a FalseClass" do + false_klass = false + false_klass.should be_kind_of(FalseClass) + false_klass.to_yaml.should match_yaml("--- false\n") + end + + it "returns the YAML representation of a Float object" do + float = 1.2 + float.should be_kind_of(Float) + float.to_yaml.should match_yaml("--- 1.2\n") + end + + it "returns the YAML representation of an Integer object" do + int = 20 + int.should be_kind_of(Integer) + int.to_yaml.should match_yaml("--- 20\n") + end + + it "returns the YAML representation of a NilClass object" do + nil_klass = nil + nil_klass.should be_kind_of(NilClass) + nil_klass.to_yaml.should match_yaml("--- \n") + end + + it "returns the YAML represenation of a RegExp object" do + Regexp.new('^a-z+:\\s+\w+').to_yaml.should match_yaml("--- !ruby/regexp /^a-z+:\\s+\\w+/\n") + end + + it "returns the YAML representation of a String object" do + "I love Ruby".to_yaml.should match_yaml("--- I love Ruby\n") + end + + it "returns the YAML representation of a Struct object" do + Person = Struct.new(:name, :gender) + Person.new("Jane", "female").to_yaml.should match_yaml("--- !ruby/struct:Person\nname: Jane\ngender: female\n") + end + + it "returns the YAML representation of a Symbol object" do + :symbol.to_yaml.should match_yaml("--- :symbol\n") + end + + it "returns the YAML representation of a Time object" do + Time.utc(2000,"jan",1,20,15,1).to_yaml.sub(/\.0+/, "").should match_yaml("--- 2000-01-01 20:15:01 Z\n") + end + + it "returns the YAML representation of a TrueClass" do + true_klass = true + true_klass.should be_kind_of(TrueClass) + true_klass.to_yaml.should match_yaml("--- true\n") + end + + it "returns the YAML representation of a Error object" do + StandardError.new("foobar").to_yaml.should match_yaml("--- !ruby/exception:StandardError\nmessage: foobar\n") + end + + it "returns the YAML representation for Range objects" do + yaml = Range.new(1,3).to_yaml + yaml.include?("!ruby/range").should be_true + yaml.include?("begin: 1").should be_true + yaml.include?("end: 3").should be_true + yaml.include?("excl: false").should be_true + end + + it "returns the YAML representation of numeric constants" do + nan_value.to_yaml.downcase.should match_yaml("--- .nan\n") + infinity_value.to_yaml.downcase.should match_yaml("--- .inf\n") + (-infinity_value).to_yaml.downcase.should match_yaml("--- -.inf\n") + (0.0).to_yaml.should match_yaml("--- 0.0\n") + end + + it "returns the YAML representation of an array of hashes" do + players = [{"a" => "b"}, {"b" => "c"}] + players.to_yaml.should match_yaml("--- \n- a: b\n- b: c\n") + end +end diff --git a/spec/rubyspec/library/yaml/transfer_spec.rb b/spec/rubyspec/library/yaml/transfer_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/transfer_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/yaml/try_implicit_spec.rb b/spec/rubyspec/library/yaml/try_implicit_spec.rb new file mode 100644 index 0000000000..a8af231b47 --- /dev/null +++ b/spec/rubyspec/library/yaml/try_implicit_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/common', __FILE__) diff --git a/spec/rubyspec/library/zlib/adler32_spec.rb b/spec/rubyspec/library/zlib/adler32_spec.rb new file mode 100644 index 0000000000..1767dc2011 --- /dev/null +++ b/spec/rubyspec/library/zlib/adler32_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib.adler32" do + it "calculates Adler checksum for string" do + Zlib.adler32("").should == 1 + Zlib.adler32(" ").should == 2162721 + Zlib.adler32("123456789").should == 152961502 + Zlib.adler32("!@#\{$\}%^&**()").should == 365495023 + Zlib.adler32("to be or not to be" * 22).should == 3979904837 + Zlib.adler32("0").should == 3211313 + Zlib.adler32((2**32).to_s).should == 193331739 + Zlib.adler32((2**64).to_s).should == 723452953 + end + + it "calculates Adler checksum for string and initial Adler value" do + test_string = "This is a test string! How exciting!%?" + Zlib.adler32(test_string, 0).should == 63900955 + Zlib.adler32(test_string, 1).should == 66391324 + Zlib.adler32(test_string, 2**8).should == 701435419 + Zlib.adler32(test_string, 2**16).should == 63966491 + lambda { Zlib.adler32(test_string, 2**128) }.should raise_error(RangeError) + end + + it "calculates the Adler checksum for string and initial Adler value for Bignums" do + test_string = "This is a test string! How exciting!%?" + Zlib.adler32(test_string, 2**30).should == 1137642779 + end + + it "assumes that the initial value is given to adler, if adler is omitted" do + orig_crc = Zlib.adler32 + Zlib.adler32("").should == Zlib.adler32("", orig_crc) + Zlib.adler32(" ").should == Zlib.adler32(" ", orig_crc) + Zlib.adler32("123456789").should == Zlib.adler32("123456789", orig_crc) + Zlib.adler32("!@#\{$\}%^&**()").should == Zlib.adler32("!@#\{$\}%^&**()", orig_crc) + Zlib.adler32("to be or not to be" * 22).should == Zlib.adler32("to be or not to be" * 22, orig_crc) + Zlib.adler32("0").should == Zlib.adler32("0", orig_crc) + Zlib.adler32((2**32).to_s).should == Zlib.adler32((2**32).to_s, orig_crc) + Zlib.adler32((2**64).to_s).should == Zlib.adler32((2**64).to_s, orig_crc) + end + + it "it returns the CRC initial value, if string is omitted" do + Zlib.adler32.should == 1 + end + +end diff --git a/spec/rubyspec/library/zlib/crc32_spec.rb b/spec/rubyspec/library/zlib/crc32_spec.rb new file mode 100644 index 0000000000..22d1dac28b --- /dev/null +++ b/spec/rubyspec/library/zlib/crc32_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib.crc32" do + it "calculates CRC checksum for string" do + Zlib.crc32("").should == 0 + Zlib.crc32(" ").should == 3916222277 + Zlib.crc32("123456789").should == 3421780262 + Zlib.crc32("!@#\{$\}%^&**()").should == 2824518887 + Zlib.crc32("to be or not to be" * 22).should == 1832379978 + Zlib.crc32("0").should == 4108050209 + Zlib.crc32((2**32).to_s).should == 3267533297 + Zlib.crc32((2**64).to_s).should == 653721760 + end + + it "calculates CRC checksum for string and initial CRC value" do + test_string = "This is a test string! How exciting!%?" + # Zlib.crc32(test_string, -2**28).should == 3230195786 + # Zlib.crc32(test_string, -2**20).should == 2770207303 + # Zlib.crc32(test_string, -2**16).should == 2299432960 + # Zlib.crc32(test_string, -2**8).should == 861809849 + # Zlib.crc32(test_string, -1).should == 2170124077 + Zlib.crc32(test_string, 0).should == 3864990561 + Zlib.crc32(test_string, 1).should == 1809313411 + Zlib.crc32(test_string, 2**8).should == 1722745982 + Zlib.crc32(test_string, 2**16).should == 1932511220 + lambda { Zlib.crc32(test_string, 2**128) }.should raise_error(RangeError) + end + + it "calculates the CRC checksum for string and initial CRC value for Bignums" do + test_string = "This is a test string! How exciting!%?" + # Zlib.crc32(test_string, -2**30).should == 277228695 + Zlib.crc32(test_string, 2**30).should == 46597132 + end + + it "assumes that the initial value is given to crc, if crc is omitted" do + orig_crc = Zlib.crc32 + Zlib.crc32("").should == Zlib.crc32("", orig_crc) + Zlib.crc32(" ").should == Zlib.crc32(" ", orig_crc) + Zlib.crc32("123456789").should == Zlib.crc32("123456789", orig_crc) + Zlib.crc32("!@#\{$\}%^&**()").should == Zlib.crc32("!@#\{$\}%^&**()", orig_crc) + Zlib.crc32("to be or not to be" * 22).should == Zlib.crc32("to be or not to be" * 22, orig_crc) + Zlib.crc32("0").should == Zlib.crc32("0", orig_crc) + Zlib.crc32((2**32).to_s).should == Zlib.crc32((2**32).to_s, orig_crc) + Zlib.crc32((2**64).to_s).should == Zlib.crc32((2**64).to_s, orig_crc) + end + + it "it returns the CRC initial value, if string is omitted" do + Zlib.crc32.should == 0 + end + +end diff --git a/spec/rubyspec/library/zlib/crc_table_spec.rb b/spec/rubyspec/library/zlib/crc_table_spec.rb new file mode 100644 index 0000000000..de40232d38 --- /dev/null +++ b/spec/rubyspec/library/zlib/crc_table_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require "zlib" + +describe "Zlib.crc_table" do + + it "returns the same value as zlib's get_crc_table()" do + Zlib.crc_table.should == + [0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035, 249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049, 498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639, 325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317, 997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443, 901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665, 651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303, 671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565, 1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059, 2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297, 1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223, 1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405, 1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995, 1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649, 1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015, 1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989, 3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, 3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377, 4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879, 4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637, 3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859, 3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161, 3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815, 3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221, 2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371, 2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881, 2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567, 2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701, 2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035, 2932959818, 3654703836, 1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897, 3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431, 3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117] + end + +end diff --git a/spec/rubyspec/library/zlib/deflate/append_spec.rb b/spec/rubyspec/library/zlib/deflate/append_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/deflate/append_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/deflate/deflate_spec.rb b/spec/rubyspec/library/zlib/deflate/deflate_spec.rb new file mode 100644 index 0000000000..44b3389701 --- /dev/null +++ b/spec/rubyspec/library/zlib/deflate/deflate_spec.rb @@ -0,0 +1,128 @@ +require 'zlib' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Zlib::Deflate.deflate" do + it "deflates some data" do + data = Array.new(10,0).pack('C*') + + zipped = Zlib::Deflate.deflate data + + zipped.should == [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*') + end + + it "deflates lots of data" do + data = "\000" * 32 * 1024 + + zipped = Zlib::Deflate.deflate data + + zipped.should == ([120, 156, 237, 193, 1, 1, 0, 0] + + [0, 128, 144, 254, 175, 238, 8, 10] + + Array.new(31, 0) + + [24, 128, 0, 0, 1]).pack('C*') + end + + it "deflates chunked data" do + random_generator = Random.new(0) + deflated = '' + + Zlib.deflate(random_generator.bytes(20000)) do |chunk| + deflated << chunk + end + + deflated.length.should == 20016 + end +end + +describe "Zlib::Deflate#deflate" do + before :each do + @deflator = Zlib::Deflate.new + end + + it "deflates some data" do + data = "\000" * 10 + + zipped = @deflator.deflate data, Zlib::FINISH + @deflator.finish + + zipped.should == [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*') + end + + it "deflates lots of data" do + data = "\000" * 32 * 1024 + + zipped = @deflator.deflate data, Zlib::FINISH + @deflator.finish + + zipped.should == ([120, 156, 237, 193, 1, 1, 0, 0] + + [0, 128, 144, 254, 175, 238, 8, 10] + + Array.new(31, 0) + + [24, 128, 0, 0, 1]).pack('C*') + end +end + +describe "Zlib::Deflate#deflate" do + + before :each do + @deflator = Zlib::Deflate.new + @random_generator = Random.new(0) + @original = '' + @chunks = [] + end + + describe "without break" do + + before do + 2.times do + @input = @random_generator.bytes(20000) + @original << @input + @deflator.deflate(@input) do |chunk| + @chunks << chunk + end + end + end + + it "deflates chunked data" do + @deflator.finish + @chunks.map { |chunk| chunk.length }.should == [16384, 16384] + end + + it "deflates chunked data with final chunk" do + final = @deflator.finish + final.length.should == 7253 + end + + it "deflates chunked data without errors" do + final = @deflator.finish + @chunks << final + @original.should == Zlib.inflate(@chunks.join) + end + + end + + describe "with break" do + before :each do + @input = @random_generator.bytes(20000) + @deflator.deflate(@input) do |chunk| + @chunks << chunk + break + end + end + + it "deflates only first chunk" do + @deflator.finish + @chunks.map { |chunk| chunk.length }.should == [16384] + end + + it "deflates chunked data with final chunk" do + final = @deflator.finish + final.length.should == 3632 + end + + it "deflates chunked data without errors" do + final = @deflator.finish + @chunks << final + @input.should == Zlib.inflate(@chunks.join) + end + + end +end diff --git a/spec/rubyspec/library/zlib/deflate/flush_spec.rb b/spec/rubyspec/library/zlib/deflate/flush_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/deflate/flush_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/deflate/new_spec.rb b/spec/rubyspec/library/zlib/deflate/new_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/deflate/new_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/deflate/params_spec.rb b/spec/rubyspec/library/zlib/deflate/params_spec.rb new file mode 100644 index 0000000000..bb2fd3ee7c --- /dev/null +++ b/spec/rubyspec/library/zlib/deflate/params_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::Deflate#params" do +it "changes the deflate parameters" do + data = 'abcdefghijklm' + + d = Zlib::Deflate.new Zlib::NO_COMPRESSION, Zlib::MAX_WBITS, + Zlib::DEF_MEM_LEVEL, Zlib::DEFAULT_STRATEGY + + d << data.slice!(0..10) + d.params Zlib::BEST_COMPRESSION, Zlib::DEFAULT_STRATEGY + d << data + + Zlib::Inflate.inflate(d.finish).should == 'abcdefghijklm' + end +end + diff --git a/spec/rubyspec/library/zlib/deflate/set_dictionary_spec.rb b/spec/rubyspec/library/zlib/deflate/set_dictionary_spec.rb new file mode 100644 index 0000000000..c5c62d9529 --- /dev/null +++ b/spec/rubyspec/library/zlib/deflate/set_dictionary_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::Deflate#set_dictionary" do + it "sets the dictionary" do + d = Zlib::Deflate.new + d.set_dictionary 'aaaaaaaaaa' + d << 'abcdefghij' + + d.finish.should == [120, 187, 20, 225, 3, 203, 75, 76, + 74, 78, 73, 77, 75, 207, 200, 204, + 2, 0, 21, 134, 3, 248].pack('C*') + end +end + diff --git a/spec/rubyspec/library/zlib/gzipfile/close_spec.rb b/spec/rubyspec/library/zlib/gzipfile/close_spec.rb new file mode 100644 index 0000000000..9486d6b9ec --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/close_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "Zlib::GzipFile#close" do + it "finishes the stream and closes the io" do + io = StringIO.new "".b + Zlib::GzipWriter.wrap io do |gzio| + gzio.close + + gzio.closed?.should == true + + lambda { gzio.orig_name }.should \ + raise_error(Zlib::GzipFile::Error, 'closed gzip stream') + lambda { gzio.comment }.should \ + raise_error(Zlib::GzipFile::Error, 'closed gzip stream') + end + + io.string[10..-1].should == ([3] + Array.new(9,0)).pack('C*') + end +end + diff --git a/spec/rubyspec/library/zlib/gzipfile/closed_spec.rb b/spec/rubyspec/library/zlib/gzipfile/closed_spec.rb new file mode 100644 index 0000000000..69785bc41c --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/closed_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "Zlib::GzipFile#closed?" do + it "returns the closed status" do + io = StringIO.new + Zlib::GzipWriter.wrap io do |gzio| + gzio.closed?.should == false + + gzio.close + + gzio.closed?.should == true + end + end +end + diff --git a/spec/rubyspec/library/zlib/gzipfile/comment_spec.rb b/spec/rubyspec/library/zlib/gzipfile/comment_spec.rb new file mode 100644 index 0000000000..638e85a4a7 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/comment_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "Zlib::GzipFile#comment" do + before :each do + @io = StringIO.new + end + + it "returns the name" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.comment = 'name' + + gzio.comment.should == 'name' + end + end + + it "raises an error on a closed stream" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.close + + lambda { gzio.comment }.should \ + raise_error(Zlib::GzipFile::Error, 'closed gzip stream') + end + end +end + diff --git a/spec/rubyspec/library/zlib/gzipfile/crc_spec.rb b/spec/rubyspec/library/zlib/gzipfile/crc_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/crc_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipfile/finish_spec.rb b/spec/rubyspec/library/zlib/gzipfile/finish_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/finish_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipfile/level_spec.rb b/spec/rubyspec/library/zlib/gzipfile/level_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/level_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipfile/mtime_spec.rb b/spec/rubyspec/library/zlib/gzipfile/mtime_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/mtime_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipfile/orig_name_spec.rb b/spec/rubyspec/library/zlib/gzipfile/orig_name_spec.rb new file mode 100644 index 0000000000..42a3b2f376 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/orig_name_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "Zlib::GzipFile#orig_name" do + before :each do + @io = StringIO.new + end + + it "returns the name" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.orig_name = 'name' + + gzio.orig_name.should == 'name' + end + end + + it "raises an error on a closed stream" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.close + + lambda { gzio.orig_name }.should \ + raise_error(Zlib::GzipFile::Error, 'closed gzip stream') + end + end +end + diff --git a/spec/rubyspec/library/zlib/gzipfile/os_code_spec.rb b/spec/rubyspec/library/zlib/gzipfile/os_code_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/os_code_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipfile/sync_spec.rb b/spec/rubyspec/library/zlib/gzipfile/sync_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/sync_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipfile/to_io_spec.rb b/spec/rubyspec/library/zlib/gzipfile/to_io_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/to_io_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipfile/wrap_spec.rb b/spec/rubyspec/library/zlib/gzipfile/wrap_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipfile/wrap_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/each_byte_spec.rb b/spec/rubyspec/library/zlib/gzipreader/each_byte_spec.rb new file mode 100644 index 0000000000..6da9ac8323 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/each_byte_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "GzipReader#each_byte" do + + before :each do + @data = '12345abcde' + @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77, + 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*') + + @io = StringIO.new @zip + ScratchPad.clear + end + + it "calls the given block for each byte in the stream, passing the byte as an argument" do + gz = Zlib::GzipReader.new @io + + ScratchPad.record [] + gz.each_byte { |b| ScratchPad << b } + + ScratchPad.recorded.should == [49, 50, 51, 52, 53, 97, 98, 99, 100, 101] + end + + it "returns an enumerator, which yields each byte in the stream, when no block is passed" do + gz = Zlib::GzipReader.new @io + enum = gz.each_byte + + ScratchPad.record [] + while true + begin + ScratchPad << enum.next + rescue StopIteration + break + end + end + + ScratchPad.recorded.should == [49, 50, 51, 52, 53, 97, 98, 99, 100, 101] + end + + it "increments position before calling the block" do + gz = Zlib::GzipReader.new @io + + i = 1 + gz.each_byte do |ignore| + gz.pos.should == i + i += 1 + end + end + +end diff --git a/spec/rubyspec/library/zlib/gzipreader/each_line_spec.rb b/spec/rubyspec/library/zlib/gzipreader/each_line_spec.rb new file mode 100644 index 0000000000..7ff116a258 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/each_line_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/each', __FILE__) + +describe "GzipReader#each_line" do + it_behaves_like :gzipreader_each, :each_line +end diff --git a/spec/rubyspec/library/zlib/gzipreader/each_spec.rb b/spec/rubyspec/library/zlib/gzipreader/each_spec.rb new file mode 100644 index 0000000000..dd780e4083 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/each_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/each', __FILE__) + +describe "GzipReader#each" do + it_behaves_like :gzipreader_each, :each +end diff --git a/spec/rubyspec/library/zlib/gzipreader/eof_spec.rb b/spec/rubyspec/library/zlib/gzipreader/eof_spec.rb new file mode 100644 index 0000000000..446cbfec37 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/eof_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "GzipReader#eof?" do + + before :each do + @data = '{"a":1234}' + @zip = [31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 171, 86, 74, 84, 178, 50, + 52, 50, 54, 169, 5, 0, 196, 20, 118, 213, 10, 0, 0, 0].pack('C*') + @io = StringIO.new @zip + end + + it "returns true when at EOF" do + gz = Zlib::GzipReader.new @io + gz.eof?.should be_false + gz.read + gz.eof?.should be_true + end + + it "returns true when at EOF with the exact length of uncompressed data" do + gz = Zlib::GzipReader.new @io + gz.eof?.should be_false + gz.read(10) + gz.eof?.should be_true + end + + it "returns true when at EOF with a length greater than the size of uncompressed data" do + gz = Zlib::GzipReader.new @io + gz.eof?.should be_false + gz.read(11) + gz.eof?.should be_true + end + + it "returns false when at EOF when there's data left in the buffer to read" do + gz = Zlib::GzipReader.new @io + gz.read(9) + gz.eof?.should be_false + gz.read + gz.eof?.should be_true + end + + # This is especially important for JRuby, since eof? there + # is more than just a simple accessor. + it "does not affect the reading data" do + gz = Zlib::GzipReader.new @io + 0.upto(9) do |i| + gz.eof?.should be_false + gz.read(1).should == @data[i, 1] + end + gz.eof?.should be_true + gz.read().should == "" + gz.eof?.should be_true + end + +end diff --git a/spec/rubyspec/library/zlib/gzipreader/getc_spec.rb b/spec/rubyspec/library/zlib/gzipreader/getc_spec.rb new file mode 100644 index 0000000000..a3c4aecf76 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/getc_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "GzipReader#getc" do + + before :each do + @data = '12345abcde' + @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77, + 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*') + @io = StringIO.new @zip + end + + it "returns the next character from the stream" do + gz = Zlib::GzipReader.new @io + gz.pos.should == 0 + + gz.getc.should == '1' + gz.getc.should == '2' + gz.getc.should == '3' + gz.getc.should == '4' + gz.getc.should == '5' + end + + it "increments position" do + gz = Zlib::GzipReader.new @io + (0..@data.size).each do |i| + gz.pos.should == i + gz.getc + end + end + + it "returns nil at the end of the stream" do + gz = Zlib::GzipReader.new @io + gz.read + pos = gz.pos + gz.getc.should be_nil + gz.pos.should == pos + end + +end diff --git a/spec/rubyspec/library/zlib/gzipreader/gets_spec.rb b/spec/rubyspec/library/zlib/gzipreader/gets_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/gets_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/lineno_spec.rb b/spec/rubyspec/library/zlib/gzipreader/lineno_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/lineno_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/new_spec.rb b/spec/rubyspec/library/zlib/gzipreader/new_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/new_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/open_spec.rb b/spec/rubyspec/library/zlib/gzipreader/open_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/open_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/pos_spec.rb b/spec/rubyspec/library/zlib/gzipreader/pos_spec.rb new file mode 100644 index 0000000000..66fbf388d8 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/pos_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "GzipReader#pos" do + + before :each do + @data = '12345abcde' + @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77, + 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*') + @io = StringIO.new @zip + end + + it "returns the position" do + gz = Zlib::GzipReader.new @io + + gz.pos.should == 0 + + gz.read 5 + gz.pos.should == 5 + + gz.read + gz.pos.should == @data.length + end + +end + diff --git a/spec/rubyspec/library/zlib/gzipreader/read_spec.rb b/spec/rubyspec/library/zlib/gzipreader/read_spec.rb new file mode 100644 index 0000000000..337f507502 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/read_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "GzipReader#read" do + + before :each do + @data = '12345abcde' + @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77, + 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*') + @io = StringIO.new @zip + end + + it "with no arguments reads the entire content of a gzip file" do + gz = Zlib::GzipReader.new @io + gz.read.should == @data + end + + it "with nil length argument reads the entire content of a gzip file" do + gz = Zlib::GzipReader.new @io + gz.read(nil).should == @data + end + + it "reads the contents up to a certain size" do + gz = Zlib::GzipReader.new @io + gz.read(5).should == @data[0...5] + gz.read(5).should == @data[5...10] + end + + it "does not accept a negative length to read" do + gz = Zlib::GzipReader.new @io + lambda { + gz.read(-1) + }.should raise_error(ArgumentError) + end + + it "returns an empty string if a 0 length is given" do + gz = Zlib::GzipReader.new @io + gz.read(0).should == "" + end + + it "respects :external_encoding option" do + gz = Zlib::GzipReader.new(@io, external_encoding: 'UTF-8') + gz.read.encoding.should == Encoding::UTF_8 + + @io.rewind + gz = Zlib::GzipReader.new(@io, external_encoding: 'UTF-16LE') + gz.read.encoding.should == Encoding::UTF_16LE + end + + describe "at the end of data" do + it "returns empty string if length prameter is not specified or 0" do + gz = Zlib::GzipReader.new @io + gz.read # read till the end + gz.read(0).should == "" + gz.read().should == "" + gz.read(nil).should == "" + end + + it "returns nil if length prameter is positive" do + gz = Zlib::GzipReader.new @io + gz.read # read till the end + gz.read(1).should be_nil + gz.read(2**16).should be_nil + end + end + +end diff --git a/spec/rubyspec/library/zlib/gzipreader/readchar_spec.rb b/spec/rubyspec/library/zlib/gzipreader/readchar_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/readchar_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/readline_spec.rb b/spec/rubyspec/library/zlib/gzipreader/readline_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/readline_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/readlines_spec.rb b/spec/rubyspec/library/zlib/gzipreader/readlines_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/readlines_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/rewind_spec.rb b/spec/rubyspec/library/zlib/gzipreader/rewind_spec.rb new file mode 100644 index 0000000000..70bee3372d --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/rewind_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "GzipReader#rewind" do + + before :each do + @data = '12345abcde' + @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77, + 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*') + @io = StringIO.new @zip + ScratchPad.clear + end + + it "resets the position of the stream pointer" do + gz = Zlib::GzipReader.new @io + gz.read + gz.pos.should == @data.length + + gz.rewind + gz.pos.should == 0 + gz.lineno.should == 0 + end + + it "resets the position of the stream pointer to data previously read" do + gz = Zlib::GzipReader.new @io + first_read = gz.read + gz.rewind + first_read.should == gz.read + end + + it "invokes seek method on the associated IO object" do + # first, prepare the mock object: + (obj = mock("io")).should_receive(:get_io).any_number_of_times.and_return(@io) + def obj.read(args); get_io.read(args); end + def obj.seek(pos, whence = 0) + ScratchPad.record :seek + get_io.seek(pos, whence) + end + + gz = Zlib::GzipReader.new(obj) + gz.rewind() + + ScratchPad.recorded.should == :seek + gz.pos.should == 0 + gz.read.should == "12345abcde" + end +end diff --git a/spec/rubyspec/library/zlib/gzipreader/shared/each.rb b/spec/rubyspec/library/zlib/gzipreader/shared/each.rb new file mode 100644 index 0000000000..47cd284b6a --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/shared/each.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe :gzipreader_each, shared: true do + + before :each do + @data = "firstline\nsecondline\n\nforthline" + @zip = [31, 139, 8, 0, 244, 125, 128, 88, 2, 255, 75, 203, 44, 42, 46, 201, + 201, 204, 75, 229, 42, 78, 77, 206, 207, 75, 1, 51, 185, 210,242, + 139, 74, 50, 64, 76, 0, 180, 54, 61, 111, 31, 0, 0, 0].pack('C*') + + @io = StringIO.new @zip + @gzreader = Zlib::GzipReader.new @io + end + + after :each do + ScratchPad.clear + end + + it "calls the given block for each line in the stream, passing the line as an argument" do + ScratchPad.record [] + @gzreader.send(@method) { |b| ScratchPad << b } + + ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"] + end + + it "returns an enumerator, which yields each byte in the stream, when no block is passed" do + enum = @gzreader.send(@method) + + ScratchPad.record [] + while true + begin + ScratchPad << enum.next + rescue StopIteration + break + end + end + + ScratchPad.recorded.should == ["firstline\n", "secondline\n", "\n", "forthline"] + end + + it "increments position before calling the block" do + i = 0 + @gzreader.send(@method) do |line| + i += line.length + @gzreader.pos.should == i + end + end + +end diff --git a/spec/rubyspec/library/zlib/gzipreader/tell_spec.rb b/spec/rubyspec/library/zlib/gzipreader/tell_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/tell_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/ungetc_spec.rb b/spec/rubyspec/library/zlib/gzipreader/ungetc_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/ungetc_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipreader/unused_spec.rb b/spec/rubyspec/library/zlib/gzipreader/unused_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipreader/unused_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/append_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/append_spec.rb new file mode 100644 index 0000000000..922e921840 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/append_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "Zlib::GzipWriter#<<" do + before :each do + @io = StringIO.new + end + + it "returns self" do + Zlib::GzipWriter.wrap @io do |gzio| + (gzio << "test").should equal(gzio) + end + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/library/zlib/gzipwriter/comment_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/comment_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/comment_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/flush_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/flush_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/flush_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/mtime_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/mtime_spec.rb new file mode 100644 index 0000000000..f73d6e3226 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/mtime_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "Zlib::GzipWriter#mtime=" do + before :each do + @io = StringIO.new + end + + it "sets mtime using Integer" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.mtime = 1 + + gzio.mtime.should == Time.at(1) + end + + @io.string[4, 4].should == [1,0,0,0].pack('C*') + end + +it "sets mtime using Time" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.mtime = Time.at 1 + + gzio.mtime.should == Time.at(1) + end + + @io.string[4, 4].should == [1,0,0,0].pack('C*') + end + + it "raises if the header was written" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.write '' + + lambda { gzio.mtime = nil }.should \ + raise_error(Zlib::GzipFile::Error, 'header is already written') + end + end +end + diff --git a/spec/rubyspec/library/zlib/gzipwriter/new_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/new_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/new_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/open_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/open_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/open_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/orig_name_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/orig_name_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/orig_name_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/pos_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/pos_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/pos_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/print_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/print_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/print_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/printf_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/printf_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/printf_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/putc_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/putc_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/putc_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/puts_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/puts_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/puts_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/tell_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/tell_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/tell_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/gzipwriter/write_spec.rb b/spec/rubyspec/library/zlib/gzipwriter/write_spec.rb new file mode 100644 index 0000000000..5ff4241423 --- /dev/null +++ b/spec/rubyspec/library/zlib/gzipwriter/write_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'stringio' +require 'zlib' + +describe "GzipWriter#write" do + before :each do + @data = '12345abcde' + @zip = [31, 139, 8, 0, 44, 220, 209, 71, 0, 3, 51, 52, 50, 54, 49, 77, + 76, 74, 78, 73, 5, 0, 157, 5, 0, 36, 10, 0, 0, 0].pack('C*') + @io = StringIO.new "".b + end + + it "writes some compressed data" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.write @data + end + + # skip gzip header for now + @io.string.unpack('C*')[10..-1].should == @zip.unpack('C*')[10..-1] + end + + it "returns the number of bytes in the input" do + Zlib::GzipWriter.wrap @io do |gzio| + gzio.write(@data).should == @data.size + end + end + + it "handles inputs of 2^23 bytes" do + input = '.'.b * (2 ** 23) + + Zlib::GzipWriter.wrap @io do |gzio| + gzio.write input + end + @io.string.size.should == 8176 + end +end diff --git a/spec/rubyspec/library/zlib/inflate/append_spec.rb b/spec/rubyspec/library/zlib/inflate/append_spec.rb new file mode 100644 index 0000000000..a768a766a2 --- /dev/null +++ b/spec/rubyspec/library/zlib/inflate/append_spec.rb @@ -0,0 +1,60 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::Inflate#<<" do + before :all do + @foo_deflated = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*') + end + + before :each do + @z = Zlib::Inflate.new + end + + after :each do + @z.close unless @z.closed? + end + + it "appends data to the input stream" do + @z << @foo_deflated + @z.finish.should == 'foo' + end + + it "treats nil argument as the end of compressed data" do + @z = Zlib::Inflate.new + @z << @foo_deflated << nil + @z.finish.should == 'foo' + end + + it "just passes through the data after nil argument" do + @z = Zlib::Inflate.new + @z << @foo_deflated << nil + @z << "-after_nil_data" + @z.finish.should == 'foo-after_nil_data' + end + + it "properly handles data in chunks" do + # add bytes, one by one + @foo_deflated.each_byte { |d| @z << d.chr} + @z.finish.should == "foo" + end + + it "properly handles incomplete data" do + # add bytes, one by one + @foo_deflated[0, 5].each_byte { |d| @z << d.chr} + lambda { @z.finish }.should raise_error(Zlib::BufError) + end + + it "properly handles excessive data, byte-by-byte" do + # add bytes, one by one + data = @foo_deflated * 2 + data.each_byte { |d| @z << d.chr} + @z.finish.should == "foo" + @foo_deflated + end + + it "properly handles excessive data, in one go" do + # add bytes, one by one + data = @foo_deflated * 2 + @z << data + @z.finish.should == "foo" + @foo_deflated + end +end diff --git a/spec/rubyspec/library/zlib/inflate/finish_spec.rb b/spec/rubyspec/library/zlib/inflate/finish_spec.rb new file mode 100644 index 0000000000..f6e592fb6b --- /dev/null +++ b/spec/rubyspec/library/zlib/inflate/finish_spec.rb @@ -0,0 +1,28 @@ +require 'zlib' + +describe "Zlib::Inflate#finish" do + + before do + @zeros = Zlib::Deflate.deflate("0" * 100_000) + @inflator = Zlib::Inflate.new + @chunks = [] + + @inflator.inflate(@zeros) do |chunk| + @chunks << chunk + break + end + + @inflator.finish do |chunk| + @chunks << chunk + end + end + + it "inflates chunked data" do + @chunks.map { |chunk| chunk.length }.should == [16384, 16384, 16384, 16384, 16384, 16384, 1696] + end + + it "each chunk should have the same prefix" do + @chunks.all? { |chunk| chunk =~ /\A0+\z/ }.should be_true + end + +end diff --git a/spec/rubyspec/library/zlib/inflate/inflate_spec.rb b/spec/rubyspec/library/zlib/inflate/inflate_spec.rb new file mode 100644 index 0000000000..1fa16d9e98 --- /dev/null +++ b/spec/rubyspec/library/zlib/inflate/inflate_spec.rb @@ -0,0 +1,152 @@ +require 'zlib' +require File.expand_path('../../../../spec_helper', __FILE__) + +describe "Zlib::Inflate#inflate" do + + before :each do + @inflator = Zlib::Inflate.new + end + it "inflates some data" do + data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*') + unzipped = @inflator.inflate data + @inflator.finish + + unzipped.should == "\000" * 10 + end + + it "inflates lots of data" do + data = [120, 156, 237, 193, 1, 1, 0, 0] + + [0, 128, 144, 254, 175, 238, 8, 10] + + Array.new(31, 0) + + [24, 128, 0, 0, 1] + + unzipped = @inflator.inflate data.pack('C*') + @inflator.finish + + unzipped.should == "\000" * 32 * 1024 + end + + it "works in pass-through mode, once finished" do + data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1] + @inflator.inflate data.pack('C*') + @inflator.finish # this is a precondition + + out = @inflator.inflate('uncompressed_data') + out << @inflator.finish + out.should == 'uncompressed_data' + + @inflator << ('uncompressed_data') << nil + @inflator.finish.should == 'uncompressed_data' + end + +end + +describe "Zlib::Inflate.inflate" do + + it "inflates some data" do + data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1] + unzipped = Zlib::Inflate.inflate data.pack('C*') + + unzipped.should == "\000" * 10 + end + + it "inflates lots of data" do + data = [120, 156, 237, 193, 1, 1, 0, 0] + + [0, 128, 144, 254, 175, 238, 8, 10] + + Array.new(31,0) + + [24, 128, 0, 0, 1] + + zipped = Zlib::Inflate.inflate data.pack('C*') + + zipped.should == "\000" * 32 * 1024 + end + + it "properly handles data in chunks" do + data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*') + z = Zlib::Inflate.new + # add bytes, one by one + result = "" + data.each_byte { |d| result << z.inflate(d.chr)} + result << z.finish + result.should == "foo" + end + + it "properly handles incomplete data" do + data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*')[0,5] + z = Zlib::Inflate.new + # add bytes, one by one, but not all + result = "" + data.each_byte { |d| result << z.inflate(d.chr)} + lambda { result << z.finish }.should raise_error(Zlib::BufError) + end + + it "properly handles excessive data, byte-by-byte" do + main_data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*') + data = main_data * 2 + result = "" + + z = Zlib::Inflate.new + # add bytes, one by one + data.each_byte { |d| result << z.inflate(d.chr)} + result << z.finish + + # the first chunk is inflated to its completion, + # the second chunk is just passed through. + result.should == "foo" + main_data + end + + it "properly handles excessive data, in one go" do + main_data = [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*') + data = main_data * 2 + result = "" + + z = Zlib::Inflate.new + result << z.inflate(data) + result << z.finish + + # the first chunk is inflated to its completion, + # the second chunk is just passed through. + result.should == "foo" + main_data + end +end + +describe "Zlib::Inflate#inflate" do + + before do + @zeros = Zlib::Deflate.deflate("0" * 100_000) + @inflator = Zlib::Inflate.new + @chunks = [] + end + + describe "without break" do + + before do + @inflator.inflate(@zeros) do |chunk| + @chunks << chunk + end + end + + it "inflates chunked data" do + @chunks.map { |chunk| chunk.size }.should == [16384, 16384, 16384, 16384, 16384, 16384, 1696] + end + + it "properly handles chunked data" do + @chunks.all? { |chunk| chunk =~ /\A0+\z/ }.should be_true + end + end + + describe "with break" do + + before do + @inflator.inflate(@zeros) do |chunk| + @chunks << chunk + break + end + end + + it "inflates chunked break" do + output = @inflator.inflate nil + (100_000 - @chunks.first.length).should == output.length + end + end +end diff --git a/spec/rubyspec/library/zlib/inflate/new_spec.rb b/spec/rubyspec/library/zlib/inflate/new_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/inflate/new_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/inflate/set_dictionary_spec.rb b/spec/rubyspec/library/zlib/inflate/set_dictionary_spec.rb new file mode 100644 index 0000000000..890815b8e6 --- /dev/null +++ b/spec/rubyspec/library/zlib/inflate/set_dictionary_spec.rb @@ -0,0 +1,21 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::Inflate#set_dictionary" do + it "sets the inflate dictionary" do + deflated = "x\273\024\341\003\313KLJNIMK\317\310\314\002\000\025\206\003\370" + + i = Zlib::Inflate.new + + begin + i << deflated + flunk 'Zlib::NeedDict not raised' + rescue Zlib::NeedDict + i.set_dictionary 'aaaaaaaaaa' + end + + i.finish.should == 'abcdefghij' + end +end + diff --git a/spec/rubyspec/library/zlib/inflate/sync_point_spec.rb b/spec/rubyspec/library/zlib/inflate/sync_point_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/inflate/sync_point_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/inflate/sync_spec.rb b/spec/rubyspec/library/zlib/inflate/sync_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/inflate/sync_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zlib_version_spec.rb b/spec/rubyspec/library/zlib/zlib_version_spec.rb new file mode 100644 index 0000000000..14fb93ef07 --- /dev/null +++ b/spec/rubyspec/library/zlib/zlib_version_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/adler_spec.rb b/spec/rubyspec/library/zlib/zstream/adler_spec.rb new file mode 100644 index 0000000000..e562fd260e --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/adler_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::ZStream#adler" do + it "generates hash" do + z = Zlib::Deflate.new + z << "foo" + z.finish + z.adler.should == 0x02820145 + end +end diff --git a/spec/rubyspec/library/zlib/zstream/avail_in_spec.rb b/spec/rubyspec/library/zlib/zstream/avail_in_spec.rb new file mode 100644 index 0000000000..25d3219a5a --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/avail_in_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::ZStream#avail_in" do + it "returns bytes in the input buffer" do + z = Zlib::Deflate.new + z.avail_in.should == 0 + end +end diff --git a/spec/rubyspec/library/zlib/zstream/avail_out_spec.rb b/spec/rubyspec/library/zlib/zstream/avail_out_spec.rb new file mode 100644 index 0000000000..c747bbbc2f --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/avail_out_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::ZStream#avail_out" do + it "returns bytes in the output buffer" do + z = Zlib::Deflate.new + z.avail_out.should == 0 + end +end diff --git a/spec/rubyspec/library/zlib/zstream/close_spec.rb b/spec/rubyspec/library/zlib/zstream/close_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/close_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/closed_spec.rb b/spec/rubyspec/library/zlib/zstream/closed_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/closed_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/data_type_spec.rb b/spec/rubyspec/library/zlib/zstream/data_type_spec.rb new file mode 100644 index 0000000000..614f0d10a2 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/data_type_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::ZStream#data_type" do + it "returns the type of the data in the stream" do + z = Zlib::Deflate.new + [Zlib::ASCII, Zlib::BINARY, Zlib::UNKNOWN].include?(z.data_type).should == true + end +end diff --git a/spec/rubyspec/library/zlib/zstream/end_spec.rb b/spec/rubyspec/library/zlib/zstream/end_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/end_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/ended_spec.rb b/spec/rubyspec/library/zlib/zstream/ended_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/ended_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/finish_spec.rb b/spec/rubyspec/library/zlib/zstream/finish_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/finish_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/finished_spec.rb b/spec/rubyspec/library/zlib/zstream/finished_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/finished_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/flush_next_in_spec.rb b/spec/rubyspec/library/zlib/zstream/flush_next_in_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/flush_next_in_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/flush_next_out_spec.rb b/spec/rubyspec/library/zlib/zstream/flush_next_out_spec.rb new file mode 100644 index 0000000000..8ba8414cd1 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/flush_next_out_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'zlib' + +describe "Zlib::ZStream#flush_next_out" do + + it "flushes the stream and flushes the output buffer" do + zs = Zlib::Inflate.new + zs << [120, 156, 75, 203, 207, 7, 0, 2, 130, 1, 69].pack('C*') + + zs.flush_next_out.should == 'foo' + zs.finished?.should == true + zs.flush_next_out.should == '' + end +end + + diff --git a/spec/rubyspec/library/zlib/zstream/reset_spec.rb b/spec/rubyspec/library/zlib/zstream/reset_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/reset_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/stream_end_spec.rb b/spec/rubyspec/library/zlib/zstream/stream_end_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/stream_end_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/total_in_spec.rb b/spec/rubyspec/library/zlib/zstream/total_in_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/total_in_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/library/zlib/zstream/total_out_spec.rb b/spec/rubyspec/library/zlib/zstream/total_out_spec.rb new file mode 100644 index 0000000000..6a4c1dadb4 --- /dev/null +++ b/spec/rubyspec/library/zlib/zstream/total_out_spec.rb @@ -0,0 +1 @@ +require File.expand_path('../../../../spec_helper', __FILE__) diff --git a/spec/rubyspec/optional/capi/README b/spec/rubyspec/optional/capi/README new file mode 100644 index 0000000000..efbfb09dcb --- /dev/null +++ b/spec/rubyspec/optional/capi/README @@ -0,0 +1,16 @@ +C-API Specs + +These specs test the C-API from Ruby. The following are conventions for the +specs: + +1. Put specs for functions related to a Ruby class in a file named according + to the class. For example, for rb_ary_new function, put the specs in + optional/capi/array_spec.rb +2. Put the C file containing the C functions for array_spec.rb in + optional/capi/ext/array_spec.c +3. Add a '#define HAVE_RB_ARY_NEW 1' to rubyspec.h +4. Name the C extension class 'CApiArraySpecs'. +5. Name the C functions 'array_spec_rb_ary_new'. +6. Wrap the code in the optional/capi/ext/array_spec.c in + '#ifdef HAVE_RB_ARY_NEW' +6. Attach the C function to the class using the name 'rb_ary_new' diff --git a/spec/rubyspec/optional/capi/array_spec.rb b/spec/rubyspec/optional/capi/array_spec.rb new file mode 100644 index 0000000000..2fd898ad94 --- /dev/null +++ b/spec/rubyspec/optional/capi/array_spec.rb @@ -0,0 +1,463 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("array") + +describe :rb_ary_new2, shared: true do + it "returns an empty array" do + @s.send(@method, 5).should == [] + end + + it "raises an ArgumentError when the given argument is negative" do + lambda { @s.send(@method, -1) }.should raise_error(ArgumentError) + end +end + +describe "C-API Array function" do + before :each do + @s = CApiArraySpecs.new + end + + describe "rb_Array" do + it "returns obj if it is an array" do + arr = @s.rb_Array([1,2]) + arr.should == [1, 2] + end + + it "tries to convert obj to an array" do + arr = @s.rb_Array({"bar" => "foo"}) + arr.should == [["bar", "foo"]] + end + + it "returns obj wrapped in an array if it cannot be converted to an array" do + arr = @s.rb_Array("a") + arr.should == ["a"] + end + end + + describe "rb_ary_new" do + it "returns an empty array" do + @s.rb_ary_new.should == [] + end + end + + describe "rb_ary_new2" do + it_behaves_like :rb_ary_new2, :rb_ary_new2 + end + + describe "rb_ary_new_capa" do + it_behaves_like :rb_ary_new2, :rb_ary_new_capa + end + + describe "rb_ary_new3" do + it "returns an array with the passed cardinality and varargs" do + @s.rb_ary_new3(1,2,3).should == [1,2,3] + end + end + + describe "rb_ary_new_from_args" do + it "returns an array with the passed cardinality and varargs" do + @s.rb_ary_new_from_args(1,2,3).should == [1,2,3] + end + end + + describe "rb_ary_new4" do + it "returns an array with the passed values" do + @s.rb_ary_new4(1,2,3).should == [1,2,3] + end + end + + describe "rb_ary_new_from_values" do + it "returns an array with the passed values" do + @s.rb_ary_new_from_values(1,2,3).should == [1,2,3] + end + end + + describe "rb_ary_push" do + it "adds an element to the array" do + @s.rb_ary_push([], 4).should == [4] + end + end + + describe "rb_ary_cat" do + it "pushes the given objects onto the end of the array" do + @s.rb_ary_cat([1, 2], 3, 4).should == [1, 2, 3, 4] + end + + it "raises a RuntimeError if the array is frozen" do + lambda { @s.rb_ary_cat([].freeze, 1) }.should raise_error(RuntimeError) + end + end + + describe "rb_ary_pop" do + it "removes and returns the last element in the array" do + a = [1,2,3] + @s.rb_ary_pop(a).should == 3 + a.should == [1,2] + end + end + + describe "rb_ary_join" do + it "joins elements of an array with a string" do + a = [1,2,3] + b = "," + @s.rb_ary_join(a,b).should == "1,2,3" + end + end + + describe "rb_ary_to_s" do + it "creates an Array literal representation as a String" do + @s.rb_ary_to_s([1,2,3]).should == "[1, 2, 3]" + @s.rb_ary_to_s([]).should == "[]" + end + end + + describe "rb_ary_reverse" do + it "reverses the order of elements in the array" do + a = [1,2,3] + @s.rb_ary_reverse(a) + a.should == [3,2,1] + end + + it "returns the original array" do + a = [1,2,3] + @s.rb_ary_reverse(a).should equal(a) + end + end + + describe "rb_ary_rotate" do + it "rotates the array so that the element at the specified position comes first" do + @s.rb_ary_rotate([1, 2, 3, 4], 2).should == [3, 4, 1, 2] + @s.rb_ary_rotate([1, 2, 3, 4], -3).should == [2, 3, 4, 1] + end + + it "raises a RuntimeError if the array is frozen" do + lambda { @s.rb_ary_rotate([].freeze, 1) }.should raise_error(RuntimeError) + end + end + + describe "rb_ary_entry" do + it "returns nil when passed an empty array" do + @s.rb_ary_entry([], 0).should == nil + end + + it "returns elements from the end when passed a negative index" do + @s.rb_ary_entry([1, 2, 3], -1).should == 3 + @s.rb_ary_entry([1, 2, 3], -2).should == 2 + end + + it "returns nil if the index is out of range" do + @s.rb_ary_entry([1, 2, 3], 3).should == nil + @s.rb_ary_entry([1, 2, 3], -10).should == nil + end + end + + describe "rb_ary_clear" do + it "removes all elements from the array" do + @s.rb_ary_clear([]).should == [] + @s.rb_ary_clear([1, 2, 3]).should == [] + end + end + + describe "rb_ary_dup" do + it "duplicates the array" do + @s.rb_ary_dup([]).should == [] + + a = [1, 2, 3] + b = @s.rb_ary_dup(a) + + b.should == a + b.should_not equal(a) + end + end + + describe "rb_ary_unshift" do + it "prepends the element to the array" do + a = [1, 2, 3] + @s.rb_ary_unshift(a, "a").should == ["a", 1, 2, 3] + a.should == ['a', 1, 2, 3] + end + end + + describe "rb_ary_shift" do + it "removes and returns the first element" do + a = [5, 1, 1, 5, 4] + @s.rb_ary_shift(a).should == 5 + a.should == [1, 1, 5, 4] + end + + it "returns nil when the array is empty" do + @s.rb_ary_shift([]).should == nil + end + end + + describe "rb_ary_store" do + it "overwrites the element at the given position" do + a = [1, 2, 3] + @s.rb_ary_store(a, 1, 5) + a.should == [1, 5, 3] + end + + it "writes to elements offset from the end if passed a negative index" do + a = [1, 2, 3] + @s.rb_ary_store(a, -1, 5) + a.should == [1, 2, 5] + end + + it "raises an IndexError if the negative index is greater than the length" do + a = [1, 2, 3] + lambda { @s.rb_ary_store(a, -10, 5) }.should raise_error(IndexError) + end + + it "enlarges the array as needed" do + a = [] + @s.rb_ary_store(a, 2, 7) + a.should == [nil, nil, 7] + end + + it "raises a RuntimeError if the array is frozen" do + a = [1, 2, 3].freeze + lambda { @s.rb_ary_store(a, 1, 5) }.should raise_error(RuntimeError) + end + end + + describe "rb_ary_concat" do + it "concats two arrays" do + a = [5, 1, 1, 5, 4] + b = [2, 3] + @s.rb_ary_concat(a, b).should == [5, 1, 1, 5, 4, 2, 3] + end + end + + describe "rb_ary_plus" do + it "adds two arrays together" do + @s.rb_ary_plus([10], [20]).should == [10, 20] + end + end + + describe "RARRAY_PTR" do + it "returns a pointer to a C array of the array's elements" do + a = [1, 2, 3] + b = [] + @s.RARRAY_PTR_iterate(a) do |e| + b << e + end + a.should == b + end + + it "allows assigning to the elements of the C array" do + a = [1, 2, 3] + @s.RARRAY_PTR_assign(a, :set) + a.should == [:set, :set, :set] + end + end + + describe "RARRAY_LEN" do + it "returns the size of the array" do + @s.RARRAY_LEN([1, 2, 3]).should == 3 + end + end + + describe "RARRAY_AREF" do + # This macro does NOT do any bounds checking! + it "returns an element from the array" do + @s.RARRAY_AREF([1, 2, 3], 1).should == 2 + end + end + + describe "rb_assoc_new" do + it "returns an array containing the two elements" do + @s.rb_assoc_new(1, 2).should == [1, 2] + @s.rb_assoc_new(:h, [:a, :b]).should == [:h, [:a, :b]] + end + end + + describe "rb_ary_includes" do + it "returns true if the array includes the element" do + @s.rb_ary_includes([1, 2, 3], 2).should be_true + end + + it "returns false if the array does not include the element" do + @s.rb_ary_includes([1, 2, 3], 4).should be_false + end + end + + describe "rb_ary_aref" do + it "returns the element at the given index" do + @s.rb_ary_aref([:me, :you], 0).should == :me + @s.rb_ary_aref([:me, :you], 1).should == :you + end + + it "returns nil for an out of range index" do + @s.rb_ary_aref([1, 2, 3], 6).should be_nil + end + + it "returns a new array where the first argument is the index and the second is the length" do + @s.rb_ary_aref([1, 2, 3, 4], 0, 2).should == [1, 2] + @s.rb_ary_aref([1, 2, 3, 4], -4, 3).should == [1, 2, 3] + end + + it "accepts a range" do + @s.rb_ary_aref([1, 2, 3, 4], 0..-1).should == [1, 2, 3, 4] + end + + it "returns nil when the start of a range is out of bounds" do + @s.rb_ary_aref([1, 2, 3, 4], 6..10).should be_nil + end + + it "returns an empty array when the start of a range equals the last element" do + @s.rb_ary_aref([1, 2, 3, 4], 4..10).should == [] + end + end + + describe "rb_iterate" do + it "calls an callback function as a block passed to an method" do + s = [1,2,3,4] + s2 = @s.rb_iterate(s) + + s2.should == s + + # Make sure they're different objects + s2.equal?(s).should be_false + end + + it "calls a function with the other function available as a block" do + h = {a: 1, b: 2} + + @s.rb_iterate_each_pair(h).sort.should == [1,2] + end + + it "calls a function which can yield into the original block" do + s2 = [] + + o = Object.new + def o.each + yield 1 + yield 2 + yield 3 + yield 4 + end + + @s.rb_iterate_then_yield(o) { |x| s2 << x } + + s2.should == [1,2,3,4] + end + end + + describe "rb_ary_delete" do + it "removes an element from an array and returns it" do + ary = [1, 2, 3, 4] + @s.rb_ary_delete(ary, 3).should == 3 + ary.should == [1, 2, 4] + end + + it "returns nil if the element is not in the array" do + ary = [1, 2, 3, 4] + @s.rb_ary_delete(ary, 5).should be_nil + ary.should == [1, 2, 3, 4] + end + end + + describe "rb_mem_clear" do + it "sets elements of a C array to nil" do + @s.rb_mem_clear(1).should == nil + end + end + + describe "rb_ary_freeze" do + it "freezes the object exactly like Kernel#freeze" do + ary = [1,2] + @s.rb_ary_freeze(ary) + ary.frozen?.should be_true + end + end + + describe "rb_ary_delete_at" do + before :each do + @array = [1, 2, 3, 4] + end + + it "removes an element from an array at a positive index" do + @s.rb_ary_delete_at(@array, 2).should == 3 + @array.should == [1, 2, 4] + end + + it "removes an element from an array at a negative index" do + @s.rb_ary_delete_at(@array, -3).should == 2 + @array.should == [1, 3, 4] + end + + it "returns nil if the index is out of bounds" do + @s.rb_ary_delete_at(@array, 4).should be_nil + @array.should == [1, 2, 3, 4] + end + + it "returns nil if the negative index is out of bounds" do + @s.rb_ary_delete_at(@array, -5).should be_nil + @array.should == [1, 2, 3, 4] + end + end + + describe "rb_ary_to_ary" do + + describe "with an array" do + + it "returns the given array" do + array = [1, 2, 3] + @s.rb_ary_to_ary(array).should equal(array) + end + + end + + describe "with an object that responds to to_ary" do + + it "calls to_ary on the object" do + obj = mock('to_ary') + obj.stub!(:to_ary).and_return([1, 2, 3]) + @s.rb_ary_to_ary(obj).should == [1, 2, 3] + end + + end + + describe "with an object that responds to to_a" do + + it "returns the original object in an array" do + obj = mock('to_a') + obj.stub!(:to_a).and_return([1, 2, 3]) + @s.rb_ary_to_ary(obj).should == [obj] + end + + end + + describe "with an object that doesn't respond to to_ary" do + + it "returns the original object in an array" do + obj = mock('no_to_ary') + @s.rb_ary_to_ary(obj).should == [obj] + end + + end + + end + + describe "rb_ary_subseq" do + it "returns a subsequence of the given array" do + @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, 3).should == [2, 3, 4] + end + + it "returns an empty array for a subsequence of 0 elements" do + @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, 0).should == [] + end + + it "returns nil if the begin index is out of bound" do + @s.rb_ary_subseq([1, 2, 3, 4, 5], 6, 3).should be_nil + end + + it "returns the existing subsequence of the length is out of bounds" do + @s.rb_ary_subseq([1, 2, 3, 4, 5], 4, 3).should == [5] + end + + it "returns nil if the size is negative" do + @s.rb_ary_subseq([1, 2, 3, 4, 5], 1, -1).should be_nil + end + end +end diff --git a/spec/rubyspec/optional/capi/bignum_spec.rb b/spec/rubyspec/optional/capi/bignum_spec.rb new file mode 100644 index 0000000000..84b5de95c2 --- /dev/null +++ b/spec/rubyspec/optional/capi/bignum_spec.rb @@ -0,0 +1,219 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("bignum") + +def ensure_bignum(n) + raise "Bignum#coerce returned Fixnum" if fixnum_min <= n && n <= fixnum_max + n +end + +full_range_longs = (fixnum_max == 2**(0.size * 8 - 1) - 1) + +describe "CApiBignumSpecs" do + before :each do + @s = CApiBignumSpecs.new + + if full_range_longs + @max_long = 2**(0.size * 8 - 1) - 1 + @min_long = -@max_long - 1 + @max_ulong = ensure_bignum(2**(0.size * 8) - 1) + else + @max_long = ensure_bignum(2**(0.size * 8 - 1) - 1) + @min_long = ensure_bignum(-@max_long - 1) + @max_ulong = ensure_bignum(2**(0.size * 8) - 1) + end +end + + describe "rb_big2long" do + unless full_range_longs + it "converts a Bignum" do + @s.rb_big2long(@max_long).should == @max_long + @s.rb_big2long(@min_long).should == @min_long + end + end + + it "raises RangeError if passed Bignum overflow long" do + lambda { @s.rb_big2long(ensure_bignum(@max_long + 1)) }.should raise_error(RangeError) + lambda { @s.rb_big2long(ensure_bignum(@min_long - 1)) }.should raise_error(RangeError) + end + end + + describe "rb_big2ll" do + unless full_range_longs + it "converts a Bignum" do + @s.rb_big2ll(@max_long).should == @max_long + @s.rb_big2ll(@min_long).should == @min_long + end + end + + it "raises RangeError if passed Bignum overflow long" do + lambda { @s.rb_big2ll(ensure_bignum(@max_long << 40)) }.should raise_error(RangeError) + lambda { @s.rb_big2ll(ensure_bignum(@min_long << 40)) }.should raise_error(RangeError) + end + end + + describe "rb_big2ulong" do + it "converts a Bignum" do + @s.rb_big2ulong(@max_ulong).should == @max_ulong + end + + unless full_range_longs + it "wraps around if passed a negative bignum" do + @s.rb_big2ulong(ensure_bignum(@min_long + 1)).should == -(@min_long - 1) + end + end + + it "raises RangeError if passed Bignum overflow long" do + lambda { @s.rb_big2ulong(ensure_bignum(@max_ulong + 1)) }.should raise_error(RangeError) + lambda { @s.rb_big2ulong(ensure_bignum(@min_long - 1)) }.should raise_error(RangeError) + end + + unless full_range_longs + it "wraps around if passed a negative bignum" do + @s.rb_big2ulong(ensure_bignum(@min_long)).should == -(@min_long) + end + end + end + + describe "rb_big2dbl" do + it "converts a Bignum to a double value" do + @s.rb_big2dbl(ensure_bignum(Float::MAX.to_i)).eql?(Float::MAX).should == true + end + + it "returns Infinity if the number is too big for a double" do + huge_bignum = ensure_bignum(Float::MAX.to_i * 2) + @s.rb_big2dbl(huge_bignum).should == infinity_value + end + + it "returns -Infinity if the number is negative and too big for a double" do + huge_bignum = -ensure_bignum(Float::MAX.to_i * 2) + @s.rb_big2dbl(huge_bignum).should == -infinity_value + end + end + + describe "rb_big2str" do + + it "converts a Bignum to a string with base 10" do + @s.rb_big2str(ensure_bignum(2**70), 10).eql?("1180591620717411303424").should == true + end + + it "converts a Bignum to a string with a different base" do + @s.rb_big2str(ensure_bignum(2**70), 16).eql?("400000000000000000").should == true + end + end + + describe "rb_big_cmp" do + it "compares a Bignum with a Bignum" do + @s.rb_big_cmp(bignum_value, bignum_value(1)).should == -1 + end + + it "compares a Bignum with a Fixnum" do + @s.rb_big_cmp(bignum_value, 5).should == 1 + end + end + + describe "rb_big_pack" do + it "packs a Bignum into an unsigned long" do + val = @s.rb_big_pack(@max_ulong) + val.should == @max_ulong + end + + platform_is wordsize: 64 do + it "packs max_ulong into 2 ulongs to allow sign bit" do + val = @s.rb_big_pack_length(@max_ulong) + val.should == 2 + val = @s.rb_big_pack_array(@max_ulong, 2) + val[0].should == @max_ulong + val[1].should == 0 + end + + it "packs a 72-bit positive Bignum into 2 unsigned longs" do + num = 2 ** 71 + val = @s.rb_big_pack_length(num) + val.should == 2 + end + + it "packs a 72-bit positive Bignum into correct 2 longs" do + num = 2 ** 71 + 1 + val = @s.rb_big_pack_array(num, 2) + val[0].should == 1; + val[1].should == 0x80; + end + + it "packs a 72-bit negative Bignum into correct 2 longs" do + num = -(2 ** 71 + 1) + val = @s.rb_big_pack_array(num, @s.rb_big_pack_length(num)) + val[0].should == @max_ulong; + val[1].should == @max_ulong - 0x80; + end + + it "packs lower order bytes into least significant bytes of longs for positive bignum" do + num = 0 + 32.times { |i| num += i << (i * 8) } + val = @s.rb_big_pack_array(num, @s.rb_big_pack_length(num)) + val.size.should == 4 + 32.times do |i| + a_long = val[i/8] + a_byte = (a_long >> ((i % 8) * 8)) & 0xff + a_byte.should == i + end + end + + it "packs lower order bytes into least significant bytes of longs for negative bignum" do + num = 0 + 32.times { |i| num += i << (i * 8) } + num = -num + val = @s.rb_big_pack_array(num, @s.rb_big_pack_length(num)) + val.size.should == 4 + expected_bytes = [0x00, 0xff, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0 ] + 32.times do |i| + a_long = val[i/8] + a_byte = (a_long >> ((i % 8) * 8)) & 0xff + a_byte.should == expected_bytes[i] + end + end + end + end + + describe "rb_dbl2big" do + it "returns a Fixnum for a Fixnum input value" do + val = @s.rb_dbl2big(2) + + val.kind_of?(Fixnum).should == true + val.should == 2 + end + + it "returns a Fixnum for a Float input value" do + val = @s.rb_dbl2big(2.5) + + val.kind_of?(Fixnum).should == true + val.should == 2 + end + + it "returns a Bignum for a large enough Float input value" do + input = 219238102380912830988.5 # chosen by fair dice roll + val = @s.rb_dbl2big(input) + + val.kind_of?(Bignum).should == true + + # This value is based on the output of a simple C extension that uses + # rb_dbl2big() to convert the above input value to a Bignum. + val.should == 219238102380912836608 + end + + it "raises FloatDomainError for Infinity values" do + inf = 1.0 / 0 + + lambda { @s.rb_dbl2big(inf) }.should raise_error(FloatDomainError) + end + + it "raises FloatDomainError for NaN values" do + nan = 0.0 / 0 + + lambda { @s.rb_dbl2big(nan) }.should raise_error(FloatDomainError) + end + end +end diff --git a/spec/rubyspec/optional/capi/class_spec.rb b/spec/rubyspec/optional/capi/class_spec.rb new file mode 100644 index 0000000000..d94c1ab902 --- /dev/null +++ b/spec/rubyspec/optional/capi/class_spec.rb @@ -0,0 +1,388 @@ +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/class', __FILE__) + +load_extension("class") + +autoload :ClassUnderAutoload, "#{object_path}/class_under_autoload_spec" +autoload :ClassIdUnderAutoload, "#{object_path}/class_id_under_autoload_spec" + +describe :rb_path_to_class, shared: true do + it "returns a class or module from a scoped String" do + @s.send(@method, "CApiClassSpecs::A::B").should equal(CApiClassSpecs::A::B) + end + + it "resolves autoload constants" do + @s.send(@method, "CApiClassSpecs::A::D").name.should == "CApiClassSpecs::A::D" + end + + it "raises an ArgumentError if a constant in the path does not exist" do + lambda { @s.send(@method, "CApiClassSpecs::NotDefined::B") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the final constant does not exist" do + lambda { @s.send(@method, "CApiClassSpecs::NotDefined") }.should raise_error(ArgumentError) + end + + it "raises a TypeError if the constant is not a class or module" do + lambda { @s.send(@method, "CApiClassSpecs::A::C") }.should raise_error(TypeError) + end + + it "raises an ArgumentError even if a constant in the path exists on toplevel" do + lambda { @s.send(@method, "CApiClassSpecs::Object") }.should raise_error(ArgumentError) + end +end + +describe "C-API Class function" do + before :each do + @s = CApiClassSpecs.new + end + + describe "rb_class_new_instance" do + it "allocates and initializes a new object" do + o = @s.rb_class_new_instance(0, nil, CApiClassSpecs::Alloc) + o.class.should == CApiClassSpecs::Alloc + o.initialized.should be_true + end + + it "passes arguments to the #initialize method" do + o = @s.rb_class_new_instance(2, [:one, :two], CApiClassSpecs::Alloc) + o.arguments.should == [:one, :two] + end + end + + describe "rb_include_module" do + it "includes a module into a class" do + c = Class.new + o = c.new + lambda { o.included? }.should raise_error(NameError) + @s.rb_include_module(c, CApiClassSpecs::M) + o.included?.should be_true + end + end + + describe "rb_define_attr" do + before :each do + @a = CApiClassSpecs::Attr.new + end + + it "defines an attr_reader when passed true, false" do + @s.rb_define_attr(CApiClassSpecs::Attr, :foo, true, false) + @a.foo.should == 1 + lambda { @a.foo = 5 }.should raise_error(NameError) + end + + it "defines an attr_writer when passed false, true" do + @s.rb_define_attr(CApiClassSpecs::Attr, :bar, false, true) + lambda { @a.bar }.should raise_error(NameError) + @a.bar = 5 + @a.instance_variable_get(:@bar).should == 5 + end + + it "defines an attr_accessor when passed true, true" do + @s.rb_define_attr(CApiClassSpecs::Attr, :baz, true, true) + @a.baz.should == 3 + @a.baz = 6 + @a.baz.should == 6 + end + end + + describe "rb_call_super" do + it "calls the method in the superclass" do + @s.define_call_super_method CApiClassSpecs::Sub, "call_super_method" + obj = CApiClassSpecs::Sub.new + obj.call_super_method.should == :super_method + end + + it "calls the method in the superclass through two native levels" do + @s.define_call_super_method CApiClassSpecs::Sub, "call_super_method" + @s.define_call_super_method CApiClassSpecs::SubSub, "call_super_method" + obj = CApiClassSpecs::SubSub.new + obj.call_super_method.should == :super_method + end + end + + describe "rb_class2name" do + it "returns the class name" do + @s.rb_class2name(CApiClassSpecs).should == "CApiClassSpecs" + end + + it "returns a string for an anonymous class" do + @s.rb_class2name(Class.new).should be_kind_of(String) + end + end + + describe "rb_class_path" do + it "returns a String of a class path with no scope modifiers" do + @s.rb_class_path(Array).should == "Array" + end + + it "returns a String of a class path with scope modifiers" do + @s.rb_class_path(File::Stat).should == "File::Stat" + end + end + + describe "rb_class_name" do + it "returns the class name" do + @s.rb_class_name(CApiClassSpecs).should == "CApiClassSpecs" + end + + it "returns a string for an anonymous class" do + @s.rb_class_name(Class.new).should be_kind_of(String) + end + end + + describe "rb_path2class" do + it_behaves_like :rb_path_to_class, :rb_path2class + end + + describe "rb_path_to_class" do + it_behaves_like :rb_path_to_class, :rb_path_to_class + end + + describe "rb_cvar_defined" do + it "returns false when the class variable is not defined" do + @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@nocvar").should be_false + end + + it "returns true when the class variable is defined" do + @s.rb_cvar_defined(CApiClassSpecs::CVars, "@@cvar").should be_true + end + + it "returns true if the class instance variable is defined" do + @s.rb_cvar_defined(CApiClassSpecs::CVars, "@c_ivar").should be_true + end + end + + describe "rb_cv_set" do + it "sets a class variable" do + o = CApiClassSpecs::CVars.new + o.new_cv.should be_nil + @s.rb_cv_set(CApiClassSpecs::CVars, "@@new_cv", 1) + o.new_cv.should == 1 + CApiClassSpecs::CVars.remove_class_variable :@@new_cv + end + end + + describe "rb_cv_get" do + it "returns the value of the class variable" do + @s.rb_cvar_get(CApiClassSpecs::CVars, "@@cvar").should == :cvar + end + + it "raises a NameError if the class variable is not defined" do + lambda { + @s.rb_cv_get(CApiClassSpecs::CVars, "@@no_cvar") + }.should raise_error(NameError, /class variable @@no_cvar/) + end + end + + describe "rb_cvar_set" do + it "sets a class variable" do + o = CApiClassSpecs::CVars.new + o.new_cvar.should be_nil + @s.rb_cvar_set(CApiClassSpecs::CVars, "@@new_cvar", 1) + o.new_cvar.should == 1 + CApiClassSpecs::CVars.remove_class_variable :@@new_cvar + end + + end + + describe "rb_define_class" do + before :each do + @cls = @s.rb_define_class("ClassSpecDefineClass", CApiClassSpecs::Super) + end + + it "creates a subclass of the superclass" do + @cls.should be_kind_of(Class) + ClassSpecDefineClass.should equal(@cls) + @cls.superclass.should == CApiClassSpecs::Super + end + + it "sets the class name" do + @cls.name.should == "ClassSpecDefineClass" + end + + it "calls #inherited on the superclass" do + CApiClassSpecs::Super.should_receive(:inherited) + @s.rb_define_class("ClassSpecDefineClass2", CApiClassSpecs::Super) + Object.send(:remove_const, :ClassSpecDefineClass2) + end + + it "raises a TypeError when given a non class object to superclass" do + lambda { + @s.rb_define_class("ClassSpecDefineClass3", Module.new) + }.should raise_error(TypeError) + end + + it "raises a TypeError when given a mismatched class to superclass" do + lambda { + @s.rb_define_class("ClassSpecDefineClass", Object) + }.should raise_error(TypeError) + end + + ruby_version_is "2.4" do + it "raises a ArgumentError when given NULL as superclass" do + lambda { + @s.rb_define_class("ClassSpecDefineClass4", nil) + }.should raise_error(ArgumentError) + end + end + end + + describe "rb_define_class_under" do + it "creates a subclass of the superclass contained in a module" do + cls = @s.rb_define_class_under(CApiClassSpecs, + "ClassUnder1", + CApiClassSpecs::Super) + cls.should be_kind_of(Class) + CApiClassSpecs::Super.should be_ancestor_of(CApiClassSpecs::ClassUnder1) + end + + it "sets the class name" do + cls = @s.rb_define_class_under(CApiClassSpecs, "ClassUnder3", Object) + cls.name.should == "CApiClassSpecs::ClassUnder3" + end + + it "calls #inherited on the superclass" do + CApiClassSpecs::Super.should_receive(:inherited) + @s.rb_define_class_under(CApiClassSpecs, "ClassUnder4", CApiClassSpecs::Super) + CApiClassSpecs.send(:remove_const, :ClassUnder4) + end + + it "raises a TypeError when given a non class object to superclass" do + lambda { @s.rb_define_class_under(CApiClassSpecs, + "ClassUnder5", + Module.new) + }.should raise_error(TypeError) + end + + ruby_version_is "2.3" do + it "raises a TypeError when given a mismatched class to superclass" do + CApiClassSpecs::ClassUnder6 = Class.new(CApiClassSpecs::Super) + lambda { @s.rb_define_class_under(CApiClassSpecs, + "ClassUnder6", + Class.new) + }.should raise_error(TypeError) + end + end + + ruby_version_is ""..."2.3" do + it "raises a NameError when given a mismatched class to superclass" do + CApiClassSpecs::ClassUnder6 = Class.new(CApiClassSpecs::Super) + lambda { @s.rb_define_class_under(CApiClassSpecs, + "ClassUnder6", + Class.new) + }.should raise_error(NameError) + end + end + + it "defines a class for an existing Autoload" do + compile_extension("class_under_autoload") + + ClassUnderAutoload.name.should == "ClassUnderAutoload" + end + + ruby_version_is "2.3" do + it "raises a TypeError if class is defined and its superclass mismatches the given one" do + lambda { @s.rb_define_class_under(CApiClassSpecs, "Sub", Object) }.should raise_error(TypeError) + end + end + end + + describe "rb_define_class_id_under" do + it "creates a subclass of the superclass contained in a module" do + cls = @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder1, CApiClassSpecs::Super) + cls.should be_kind_of(Class) + CApiClassSpecs::Super.should be_ancestor_of(CApiClassSpecs::ClassIdUnder1) + end + + it "sets the class name" do + cls = @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder3, Object) + cls.name.should == "CApiClassSpecs::ClassIdUnder3" + end + + it "calls #inherited on the superclass" do + CApiClassSpecs::Super.should_receive(:inherited) + @s.rb_define_class_id_under(CApiClassSpecs, :ClassIdUnder4, CApiClassSpecs::Super) + CApiClassSpecs.send(:remove_const, :ClassIdUnder4) + end + + it "defines a class for an existing Autoload" do + compile_extension("class_id_under_autoload") + + ClassIdUnderAutoload.name.should == "ClassIdUnderAutoload" + end + + ruby_version_is "2.3" do + it "raises a TypeError if class is defined and its superclass mismatches the given one" do + lambda { @s.rb_define_class_id_under(CApiClassSpecs, :Sub, Object) }.should raise_error(TypeError) + end + end + end + + describe "rb_define_class_variable" do + it "sets a class variable" do + o = CApiClassSpecs::CVars.new + o.rbdcv_cvar.should be_nil + @s.rb_define_class_variable(CApiClassSpecs::CVars, "@@rbdcv_cvar", 1) + o.rbdcv_cvar.should == 1 + CApiClassSpecs::CVars.remove_class_variable :@@rbdcv_cvar + end + end + + describe "rb_cvar_get" do + it "returns the value of the class variable" do + @s.rb_cvar_get(CApiClassSpecs::CVars, "@@cvar").should == :cvar + end + + it "raises a NameError if the class variable is not defined" do + lambda { + @s.rb_cvar_get(CApiClassSpecs::CVars, "@@no_cvar") + }.should raise_error(NameError, /class variable @@no_cvar/) + end + end + + describe "rb_class_new" do + it "returns an new subclass of the superclass" do + subclass = @s.rb_class_new(CApiClassSpecs::NewClass) + CApiClassSpecs::NewClass.should be_ancestor_of(subclass) + end + + it "raises a TypeError if passed Class as the superclass" do + lambda { @s.rb_class_new(Class) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a singleton class as the superclass" do + metaclass = Object.new.singleton_class + lambda { @s.rb_class_new(metaclass) }.should raise_error(TypeError) + end + end + + describe "rb_class_superclass" do + it "returns the superclass of a class" do + cls = @s.rb_class_superclass(CApiClassSpecs::Sub) + cls.should == CApiClassSpecs::Super + end + + it "returns nil if the class has no superclass" do + @s.rb_class_superclass(BasicObject).should be_nil + end + end + + describe "rb_class_real" do + it "returns the class of an object ignoring the singleton class" do + obj = CApiClassSpecs::Sub.new + def obj.some_method() end + + @s.rb_class_real(obj).should == CApiClassSpecs::Sub + end + + it "returns the class of an object ignoring included modules" do + obj = CApiClassSpecs::SubM.new + @s.rb_class_real(obj).should == CApiClassSpecs::SubM + end + + it "returns 0 if passed 0" do + @s.rb_class_real(0).should == 0 + end + end +end diff --git a/spec/rubyspec/optional/capi/complex_spec.rb b/spec/rubyspec/optional/capi/complex_spec.rb new file mode 100644 index 0000000000..ad84e47ced --- /dev/null +++ b/spec/rubyspec/optional/capi/complex_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("complex") + +describe :rb_Complex, shared: true do + it "creates a new Complex with numerator and denominator" do + @r.send(@method, 1, 2).should == Complex(1, 2) + end +end + +describe :rb_complex_new, shared: true do + it "creates a normalized Complex" do + r = @r.send(@method, 10, 4) + r.real.should == 10 + r.imag.should == 4 + end +end + +describe "CApiComplexSpecs" do + before :each do + @r = CApiComplexSpecs.new + end + + describe "rb_Complex" do + it_behaves_like :rb_Complex, :rb_Complex + end + + describe "rb_Complex2" do + it_behaves_like :rb_Complex, :rb_Complex2 + end + + describe "rb_Complex1" do + it "creates a new Complex with real and imaginary of 0" do + @r.rb_Complex1(5).should == Complex(5, 0) + end + end + + describe "rb_complex_new" do + it_behaves_like :rb_complex_new, :rb_complex_new + end + + describe "rb_complex_new2" do + it_behaves_like :rb_complex_new, :rb_complex_new2 + end +end diff --git a/spec/rubyspec/optional/capi/constants_spec.rb b/spec/rubyspec/optional/capi/constants_spec.rb new file mode 100644 index 0000000000..d663ecda30 --- /dev/null +++ b/spec/rubyspec/optional/capi/constants_spec.rb @@ -0,0 +1,268 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("constants") + +describe "C-API constant" do + before :each do + @s = CApiConstantsSpecs.new + end + + specify "rb_cArray references the Array class" do + @s.rb_cArray.should == Array + end + + ruby_version_is ""..."2.4" do + specify "rb_cBignum references the Bignum class" do + @s.rb_cBignum.should == Bignum + end + end + + specify "rb_cClass references the Class class" do + @s.rb_cClass.should == Class + end + + specify "rb_mComparable references the Comparable module" do + @s.rb_mComparable.should == Comparable + end + + specify "rb_cData references the Data class" do + @s.rb_cData.should == Data + end + + specify "rb_mEnumerable references the Enumerable module" do + @s.rb_mEnumerable.should == Enumerable + end + + specify "rb_cFalseClass references the FalseClass class" do + @s.rb_cFalseClass.should == FalseClass + end + + specify "rb_cFile references the File class" do + @s.rb_cFile.should == File + end + + ruby_version_is ""..."2.4" do + specify "rb_cFixnum references the Fixnum class" do + @s.rb_cFixnum.should == Fixnum + end + end + + specify "rb_cFloat references the Float class" do + @s.rb_cFloat.should == Float + end + + specify "rb_cHash references the Hash class" do + @s.rb_cHash.should == Hash + end + + specify "rb_cInteger references the Integer class" do + @s.rb_cInteger.should == Integer + end + + specify "rb_cIO references the IO class" do + @s.rb_cIO.should == IO + end + + specify "rb_mKernel references the Kernel module" do + @s.rb_mKernel.should == Kernel + end + + specify "rb_cMatch references the MatchData class" do + @s.rb_cMatch.should == MatchData + end + + specify "rb_cModule references the Module class" do + @s.rb_cModule.should == Module + end + + specify "rb_cNilClass references the NilClass class" do + @s.rb_cNilClass.should == NilClass + end + + specify "rb_cNumeric references the Numeric class" do + @s.rb_cNumeric.should == Numeric + end + + specify "rb_cObject references the Object class" do + @s.rb_cObject.should == Object + end + + specify "rb_cRange references the Range class" do + @s.rb_cRange.should == Range + end + + specify "rb_cRegexp references the Regexp class" do + @s.rb_cRegexp.should == Regexp + end + + specify "rb_cString references the String class" do + @s.rb_cString.should == String + end + + specify "rb_cStruct references the Struct class" do + @s.rb_cStruct.should == Struct + end + + specify "rb_cSymbol references the Symbol class" do + @s.rb_cSymbol.should == Symbol + end + + specify "rb_cTime references the Time class" do + @s.rb_cTime.should == Time + end + + specify "rb_cThread references the Thread class" do + @s.rb_cThread.should == Thread + end + + specify "rb_cTrueClass references the TrueClass class" do + @s.rb_cTrueClass.should == TrueClass + end + + specify "rb_cProc references the Proc class" do + @s.rb_cProc.should == Proc + end + + specify "rb_cMethod references the Method class" do + @s.rb_cMethod.should == Method + end + + specify "rb_cDir references the Dir class" do + @s.rb_cDir.should == Dir + end + +end + +describe "C-API exception constant" do + before :each do + @s = CApiConstantsSpecs.new + end + + specify "rb_eArgError references the ArgumentError class" do + @s.rb_eArgError.should == ArgumentError + end + + specify "rb_eEOFError references the EOFError class" do + @s.rb_eEOFError.should == EOFError + end + + specify "rb_eErrno references the Errno module" do + @s.rb_mErrno.should == Errno + end + + specify "rb_eException references the Exception class" do + @s.rb_eException.should == Exception + end + + specify "rb_eFloatDomainError references the FloatDomainError class" do + @s.rb_eFloatDomainError.should == FloatDomainError + end + + specify "rb_eIndexError references the IndexError class" do + @s.rb_eIndexError.should == IndexError + end + + specify "rb_eInterrupt references the Interrupt class" do + @s.rb_eInterrupt.should == Interrupt + end + + specify "rb_eIOError references the IOError class" do + @s.rb_eIOError.should == IOError + end + + specify "rb_eLoadError references the LoadError class" do + @s.rb_eLoadError.should == LoadError + end + + specify "rb_eLocalJumpError references the LocalJumpError class" do + @s.rb_eLocalJumpError.should == LocalJumpError + end + + specify "rb_eMathDomainError references the Math::DomainError class" do + @s.rb_eMathDomainError.should == Math::DomainError + end + + specify "rb_eEncCompatError references the Encoding::CompatibilityError" do + @s.rb_eEncCompatError.should == Encoding::CompatibilityError + end + + specify "rb_eNameError references the NameError class" do + @s.rb_eNameError.should == NameError + end + + specify "rb_eNoMemError references the NoMemoryError class" do + @s.rb_eNoMemError.should == NoMemoryError + end + + specify "rb_eNoMethodError references the NoMethodError class" do + @s.rb_eNoMethodError.should == NoMethodError + end + + specify "rb_eNotImpError references the NotImplementedError class" do + @s.rb_eNotImpError.should == NotImplementedError + end + + specify "rb_eRangeError references the RangeError class" do + @s.rb_eRangeError.should == RangeError + end + + specify "rb_eRegexpError references the RegexpError class" do + @s.rb_eRegexpError.should == RegexpError + end + + specify "rb_eRuntimeError references the RuntimeError class" do + @s.rb_eRuntimeError.should == RuntimeError + end + + specify "rb_eScriptError references the ScriptError class" do + @s.rb_eScriptError.should == ScriptError + end + + specify "rb_eSecurityError references the SecurityError class" do + @s.rb_eSecurityError.should == SecurityError + end + + specify "rb_eSignal references the SignalException class" do + @s.rb_eSignal.should == SignalException + end + + specify "rb_eStandardError references the StandardError class" do + @s.rb_eStandardError.should == StandardError + end + + specify "rb_eSyntaxError references the SyntaxError class" do + @s.rb_eSyntaxError.should == SyntaxError + end + + specify "rb_eSystemCallError references the SystemCallError class" do + @s.rb_eSystemCallError.should == SystemCallError + end + + specify "rb_eSystemExit references the SystemExit class" do + @s.rb_eSystemExit.should == SystemExit + end + + specify "rb_eSysStackError references the SystemStackError class" do + @s.rb_eSysStackError.should == SystemStackError + end + + specify "rb_eTypeError references the TypeError class" do + @s.rb_eTypeError.should == TypeError + end + + specify "rb_eThreadError references the ThreadError class" do + @s.rb_eThreadError.should == ThreadError + end + + specify "rb_mWaitReadable references the IO::WaitReadable module" do + @s.rb_mWaitReadable.should == IO::WaitReadable + end + + specify "rb_mWaitWritable references the IO::WaitWritable module" do + @s.rb_mWaitWritable.should == IO::WaitWritable + end + + specify "rb_eZeroDivError references the ZeroDivisionError class" do + @s.rb_eZeroDivError.should == ZeroDivisionError + end +end diff --git a/spec/rubyspec/optional/capi/data_spec.rb b/spec/rubyspec/optional/capi/data_spec.rb new file mode 100644 index 0000000000..ae1f57b091 --- /dev/null +++ b/spec/rubyspec/optional/capi/data_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("data") + +describe "CApiAllocSpecs (a class with an alloc func defined)" do + it "calls the alloc func" do + @s = CApiAllocSpecs.new + @s.wrapped_data.should == 42 # not defined in initialize + end +end + +describe "CApiWrappedStruct" do + before :each do + @s = CApiWrappedStructSpecs.new + end + + it "wraps with Data_Wrap_Struct and Data_Get_Struct returns data" do + a = @s.wrap_struct(1024) + @s.get_struct(a).should == 1024 + end + + it "allows for using NULL as the klass for Data_Wrap_Struct" do + a = @s.wrap_struct_null(1024) + @s.get_struct(a).should == 1024 + end + + describe "RDATA()" do + it "returns the struct data" do + a = @s.wrap_struct(1024) + @s.get_struct_rdata(a).should == 1024 + end + + it "allows changing the wrapped struct" do + a = @s.wrap_struct(1024) + @s.change_struct(a, 100) + @s.get_struct(a).should == 100 + end + end + + describe "DATA_PTR" do + it "returns the struct data" do + a = @s.wrap_struct(1024) + @s.get_struct_data_ptr(a).should == 1024 + end + end +end diff --git a/spec/rubyspec/optional/capi/encoding_spec.rb b/spec/rubyspec/optional/capi/encoding_spec.rb new file mode 100644 index 0000000000..9408c0c6c5 --- /dev/null +++ b/spec/rubyspec/optional/capi/encoding_spec.rb @@ -0,0 +1,485 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/encoding', __FILE__) + +load_extension('encoding') + +describe :rb_enc_get_index, shared: true do + it "returns the index of the encoding of a String" do + @s.send(@method, "string").should >= 0 + end + + it "returns the index of the encoding of a Regexp" do + @s.send(@method, /regexp/).should >= 0 + end + + it "returns the index of the encoding of an Object" do + obj = mock("rb_enc_get_index string") + @s.rb_enc_set_index(obj, 1) + @s.send(@method, obj).should == 1 + end + + it "returns the index of the dummy encoding of an Object" do + obj = mock("rb_enc_get_index string") + index = Encoding.list.index(Encoding::UTF_16) + @s.rb_enc_set_index(obj, index) + @s.send(@method, obj).should == index + end + + it "returns 0 for an object without an encoding" do + obj = mock("rb_enc_get_index string") + @s.send(@method, obj).should == 0 + end +end + +describe :rb_enc_set_index, shared: true do + it "sets the object's encoding to the Encoding specified by the index" do + obj = "abc" + result = @s.send(@method, obj, 2) + + # This is used because indexes should be considered implementation + # dependent. So a pair is returned: + # [rb_enc_find_index()->name, rb_enc_get(obj)->name] + result.first.should == result.last + end + + it "associates an encoding with a subclass of String" do + str = CApiEncodingSpecs::S.new "abc" + result = @s.send(@method, str, 1) + result.first.should == result.last + end + + it "associates an encoding with an object" do + obj = mock("rb_enc_set_index string") + result = @s.send(@method, obj, 1) + result.first.should == result.last + end +end + +describe "C-API Encoding function" do + before :each do + @s = CApiEncodingSpecs.new + end + + describe "rb_encdb_alias" do + it "creates an alias for an existing Encoding" do + @s.rb_encdb_alias("ZOMGWTFBBQ", "UTF-8").should >= 0 + Encoding.find("ZOMGWTFBBQ").name.should == "UTF-8" + end + end + + describe "rb_enc_find" do + it "returns the encoding of an Encoding" do + @s.rb_enc_find("UTF-8").should == "UTF-8" + end + + it "returns the encoding of an Encoding specified with lower case" do + @s.rb_enc_find("utf-8").should == "UTF-8" + end + end + + describe "rb_enc_find_index" do + it "returns the index of an Encoding" do + @s.rb_enc_find_index("UTF-8").should >= 0 + end + + it "returns the index of an Encoding specified with lower case" do + @s.rb_enc_find_index("utf-8").should >= 0 + end + + it "returns -1 for an non existing encoding" do + @s.rb_enc_find_index("non-existant-encoding").should == -1 + end + end + + describe "rb_enc_from_index" do + it "returns an Encoding" do + @s.rb_enc_from_index(0).should be_an_instance_of(String) + end + end + + describe "rb_usascii_encoding" do + it "returns the encoding for Encoding::US_ASCII" do + @s.rb_usascii_encoding.should == "US-ASCII" + end + end + + describe "rb_ascii8bit_encoding" do + it "returns the encoding for Encoding::ASCII_8BIT" do + @s.rb_ascii8bit_encoding.should == "ASCII-8BIT" + end + end + + describe "rb_utf8_encoding" do + it "returns the encoding for Encoding::UTF_8" do + @s.rb_utf8_encoding.should == "UTF-8" + end + end + + describe "rb_enc_from_encoding" do + it "returns an Encoding instance from an encoding data structure" do + @s.rb_enc_from_encoding("UTF-8").should == Encoding::UTF_8 + end + end + + describe "rb_locale_encoding" do + it "returns the encoding for the current locale" do + @s.rb_locale_encoding.should == Encoding.find('locale').name + end + end + + describe "rb_filesystem_encoding" do + it "returns the encoding for the current filesystem" do + @s.rb_filesystem_encoding.should == Encoding.find('filesystem').name + end + end + + describe "rb_enc_get" do + it "returns the encoding ossociated with an object" do + str = "abc".encode Encoding::ASCII_8BIT + @s.rb_enc_get(str).should == "ASCII-8BIT" + end + end + + describe "rb_obj_encoding" do + it "returns the encoding ossociated with an object" do + str = "abc".encode Encoding::ASCII_8BIT + @s.rb_obj_encoding(str).should == Encoding::ASCII_8BIT + end + end + + describe "rb_enc_get_index" do + it_behaves_like :rb_enc_get_index, :rb_enc_get_index + + it "returns the index of the encoding of a Symbol" do + @s.send(@method, :symbol).should >= 0 + end + + it "returns -1 as the index of nil" do + @s.send(@method, nil).should == -1 + end + + it "returns -1 as the index for immediates" do + @s.send(@method, 1).should == -1 + end + end + + describe "rb_enc_set_index" do + it_behaves_like :rb_enc_set_index, :rb_enc_set_index + end + + describe "rb_enc_str_new" do + it "returns a String in US-ASCII encoding when high bits are set" do + xEE = [0xEE].pack('C').force_encoding('utf-8') + result = @s.rb_enc_str_new(xEE, 1, Encoding::US_ASCII) + result.encoding.should equal(Encoding::US_ASCII) + end + end + + describe "rb_enc_str_coderange" do + describe "when the encoding is ASCII-8BIT" do + it "returns ENC_CODERANGE_7BIT if there are no high bits set" do + result = @s.rb_enc_str_coderange("abc".force_encoding("ascii-8bit")) + result.should == :coderange_7bit + end + + it "returns ENC_CODERANGE_VALID if there are high bits set" do + xEE = [0xEE].pack('C').force_encoding('utf-8') + result = @s.rb_enc_str_coderange(xEE.force_encoding("ascii-8bit")) + result.should == :coderange_valid + end + end + + describe "when the encoding is UTF-8" do + it "returns ENC_CODERANGE_7BIT if there are no high bits set" do + result = @s.rb_enc_str_coderange("abc".force_encoding("utf-8")) + result.should == :coderange_7bit + end + + it "returns ENC_CODERANGE_VALID if there are high bits set in a valid string" do + result = @s.rb_enc_str_coderange("\xE3\x81\x82".force_encoding("utf-8")) + result.should == :coderange_valid + end + + it "returns ENC_CODERANGE_BROKEN if there are high bits set in an invalid string" do + result = @s.rb_enc_str_coderange([0xEE].pack('C').force_encoding("utf-8")) + result.should == :coderange_broken + end + end + + describe "when the encoding is US-ASCII" do + it "returns ENC_CODERANGE_7BIT if there are no high bits set" do + result = @s.rb_enc_str_coderange("abc".force_encoding("us-ascii")) + result.should == :coderange_7bit + end + + it "returns ENC_CODERANGE_BROKEN if there are high bits set" do + result = @s.rb_enc_str_coderange([0xEE].pack('C').force_encoding("us-ascii")) + result.should == :coderange_broken + end + end + end + + describe "ENCODING_GET" do + it_behaves_like :rb_enc_get_index, :ENCODING_GET + end + + describe "ENCODING_SET" do + it_behaves_like :rb_enc_set_index, :ENCODING_SET + end + + describe "ENC_CODERANGE_ASCIIONLY" do + it "returns true if the object encoding is only ASCII" do + str = "abc".force_encoding("us-ascii") + str.valid_encoding? # make sure to set the coderange + @s.ENC_CODERANGE_ASCIIONLY(str).should be_true + end + + it "returns false if the object encoding is not ASCII only" do + str = "ありがとう".force_encoding("utf-8") + @s.ENC_CODERANGE_ASCIIONLY(str).should be_false + end + end + + describe "rb_to_encoding" do + it "returns the encoding for the Encoding instance passed" do + @s.rb_to_encoding(Encoding::BINARY).should == "ASCII-8BIT" + end + + it "returns the correct encoding for a replicated encoding" do + @s.rb_to_encoding(Encoding::IBM857).should == "IBM857" + end + + it "returns the encoding when passed a String" do + @s.rb_to_encoding("ASCII").should == "US-ASCII" + end + + it "calls #to_str to convert the argument to a String" do + obj = mock("rb_to_encoding Encoding name") + obj.should_receive(:to_str).and_return("utf-8") + + @s.rb_to_encoding(obj).should == "UTF-8" + end + end + + describe "rb_to_encoding_index" do + it "returns the index of the encoding for the Encoding instance passed" do + @s.rb_to_encoding_index(Encoding::BINARY).should >= 0 + end + + it "returns the index of the encoding when passed a String" do + @s.rb_to_encoding_index("ASCII").should >= 0 + end + + it "returns the index of the dummy encoding of an Object" do + index = Encoding.list.index(Encoding::UTF_16) + @s.rb_to_encoding_index(Encoding::UTF_16.name).should == index + end + + it "calls #to_str to convert the argument to a String" do + obj = mock("rb_to_encoding Encoding name") + obj.should_receive(:to_str).and_return("utf-8") + + @s.rb_to_encoding_index(obj).should >= 0 + end + end + + describe "rb_enc_compatible" do + it "returns 0 if the encodings of the Strings are not compatible" do + a = [0xff].pack('C').force_encoding "ascii-8bit" + b = "\u3042".encode("utf-8") + @s.rb_enc_compatible(a, b).should == 0 + end + + # The coverage of this sucks, but there is not a simple way (yet?) to + # easily share the specs between rb_enc_compatible and + # Encoding.compatible? + it "returns the same value as Encoding.compatible? if the Strings have a compatible encoding" do + a = "abc".force_encoding("us-ascii") + b = "\u3042".encode("utf-8") + @s.rb_enc_compatible(a, b).should == Encoding.compatible?(a, b) + end + end + + describe "rb_enc_copy" do + before :each do + @obj = "rb_enc_copy".encode(Encoding::US_ASCII) + end + + it "sets the encoding of a String to that of the second argument" do + @s.rb_enc_copy("string", @obj).encoding.should == Encoding::US_ASCII + end + + it "raises a RuntimeError if the second argument is a Symbol" do + lambda { @s.rb_enc_copy(:symbol, @obj) }.should raise_error(RuntimeError) + end + + it "sets the encoding of a Regexp to that of the second argument" do + @s.rb_enc_copy(/regexp/, @obj).encoding.should == Encoding::US_ASCII + end + end + + describe "rb_default_internal_encoding" do + before :each do + @default = Encoding.default_internal + end + + after :each do + Encoding.default_internal = @default + end + + it "returns 0 if Encoding.default_internal is nil" do + Encoding.default_internal = nil + @s.rb_default_internal_encoding.should be_nil + end + + it "returns the encoding for Encoding.default_internal" do + Encoding.default_internal = "US-ASCII" + @s.rb_default_internal_encoding.should == "US-ASCII" + Encoding.default_internal = "UTF-8" + @s.rb_default_internal_encoding.should == "UTF-8" + end + end + + describe "rb_default_external_encoding" do + before :each do + @default = Encoding.default_external + end + + after :each do + Encoding.default_external = @default + end + + it "returns the encoding for Encoding.default_external" do + Encoding.default_external = "BINARY" + @s.rb_default_external_encoding.should == "ASCII-8BIT" + end + end + + describe "rb_enc_associate" do + it "sets the encoding of a String to the encoding" do + @s.rb_enc_associate("string", "ASCII-8BIT").encoding.should == Encoding::ASCII_8BIT + end + + it "raises a RuntimeError if the argument is Symbol" do + lambda { @s.rb_enc_associate(:symbol, "US-ASCII") }.should raise_error(RuntimeError) + end + + it "sets the encoding of a Regexp to the encoding" do + @s.rb_enc_associate(/regexp/, "ASCII-8BIT").encoding.should == Encoding::ASCII_8BIT + end + + it "sets the encoding of a String to a default when the encoding is NULL" do + @s.rb_enc_associate("string", nil).encoding.should == Encoding::ASCII_8BIT + end + end + + describe "rb_enc_associate_index" do + it "sets the encoding of a String to the encoding" do + index = @s.rb_enc_find_index("ASCII-8BIT") + enc = @s.rb_enc_associate_index("string", index).encoding + enc.should == Encoding::ASCII_8BIT + end + + it "sets the encoding of a Regexp to the encoding" do + index = @s.rb_enc_find_index("UTF-8") + enc = @s.rb_enc_associate_index(/regexp/, index).encoding + enc.should == Encoding::UTF_8 + end + + it "sets the encoding of a Symbol to the encoding" do + index = @s.rb_enc_find_index("UTF-8") + lambda { @s.rb_enc_associate_index(:symbol, index) }.should raise_error(RuntimeError) + end + end + + describe "rb_ascii8bit_encindex" do + it "returns an index for the ASCII-8BIT encoding" do + @s.rb_ascii8bit_encindex().should >= 0 + end + end + + describe "rb_utf8_encindex" do + it "returns an index for the UTF-8 encoding" do + @s.rb_utf8_encindex().should >= 0 + end + end + + describe "rb_usascii_encindex" do + it "returns an index for the US-ASCII encoding" do + @s.rb_usascii_encindex().should >= 0 + end + end + + describe "rb_locale_encindex" do + it "returns an index for the locale encoding" do + @s.rb_locale_encindex().should >= 0 + end + end + + describe "rb_filesystem_encindex" do + it "returns an index for the filesystem encoding" do + @s.rb_filesystem_encindex().should >= 0 + end + end + + describe "rb_enc_to_index" do + it "returns an index for the encoding" do + @s.rb_enc_to_index("UTF-8").should >= 0 + end + + it "returns a non-negative int if the encoding is not defined" do + # Encoding indexes are an implementation detail and not guaranteed + # across implementations. + @s.rb_enc_to_index("FTU-81").should >= 0 + end + end + + describe "rb_enc_nth" do + it "returns the byte index of the given character index" do + @s.rb_enc_nth("hüllo", 3).should == 4 + end + end + + describe "rb_enc_codepoint_len" do + it "raises ArgumentError if an empty string is given" do + lambda do + @s.rb_enc_codepoint_len("") + end.should raise_error(ArgumentError) + end + + it "raises ArgumentError if an invalid byte sequence is given" do + lambda do + @s.rb_enc_codepoint_len([0xa0, 0xa1].pack('CC').force_encoding('utf-8')) # Invalid sequence identifier + end.should raise_error(ArgumentError) + end + + it "returns codepoint 0x24 and length 1 for character '$'" do + codepoint, length = @s.rb_enc_codepoint_len("$") + + codepoint.should == 0x24 + length.should == 1 + end + + it "returns codepoint 0xA2 and length 2 for character '¢'" do + codepoint, length = @s.rb_enc_codepoint_len("¢") + + codepoint.should == 0xA2 + length.should == 2 + end + + it "returns codepoint 0x20AC and length 3 for character '€'" do + codepoint, length = @s.rb_enc_codepoint_len("€") + + codepoint.should == 0x20AC + length.should == 3 + end + + it "returns codepoint 0x24B62 and length 4 for character '𤭢'" do + codepoint, length = @s.rb_enc_codepoint_len("𤭢") + + codepoint.should == 0x24B62 + length.should == 4 + end + end +end diff --git a/spec/rubyspec/optional/capi/enumerator_spec.rb b/spec/rubyspec/optional/capi/enumerator_spec.rb new file mode 100644 index 0000000000..44634f73a1 --- /dev/null +++ b/spec/rubyspec/optional/capi/enumerator_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("enumerator") + +describe "C-API Enumerator function" do + before :each do + @s = CApiEnumeratorSpecs.new + end + + describe "rb_enumeratorize" do + before do + @enumerable = [1, 2, 3] + end + + it "constructs a new Enumerator for the given object, method and arguments" do + enumerator = @s.rb_enumeratorize(@enumerable, :each, :arg1, :arg2) + enumerator.class.should == Enumerator + end + + it "enumerates the given object" do + enumerator = @s.rb_enumeratorize(@enumerable, :each) + enumerated = [] + enumerator.each { |i| enumerated << i } + enumerated.should == @enumerable + end + + it "uses the given method for enumeration" do + enumerator = @s.rb_enumeratorize(@enumerable, :awesome_each) + @enumerable.should_receive(:awesome_each) + enumerator.each {} + end + + it "passes the given arguments to the enumeration method" do + enumerator = @s.rb_enumeratorize(@enumerable, :each, :arg1, :arg2) + @enumerable.should_receive(:each).with(:arg1, :arg2) + enumerator.each {} + end + end +end diff --git a/spec/rubyspec/optional/capi/exception_spec.rb b/spec/rubyspec/optional/capi/exception_spec.rb new file mode 100644 index 0000000000..4800e6ffc0 --- /dev/null +++ b/spec/rubyspec/optional/capi/exception_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("exception") + +describe "C-API Exception function" do + before :each do + @s = CApiExceptionSpecs.new + end + + describe "rb_exc_new" do + it "creates an exception from a C string and length" do + @s.rb_exc_new('foo').to_s.should == 'foo' + end + end + + describe "rb_exc_new2" do + it "creates an exception from a C string" do + @s.rb_exc_new2('foo').to_s.should == 'foo' + end + end + + describe "rb_exc_new3" do + it "creates an exception from a Ruby string" do + @s.rb_exc_new3('foo').to_s.should == 'foo' + end + end + + describe "rb_exc_raise" do + it "raises passed exception" do + runtime_error = RuntimeError.new '42' + lambda { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42') + end + + it "raises an exception with an empty backtrace" do + runtime_error = RuntimeError.new '42' + runtime_error.set_backtrace [] + lambda { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42') + end + end + + describe "rb_set_errinfo" do + after :each do + @s.rb_set_errinfo(nil) + end + + it "accepts nil" do + @s.rb_set_errinfo(nil).should be_nil + end + + it "accepts an Exception instance" do + @s.rb_set_errinfo(Exception.new).should be_nil + end + + it "raises a TypeError if the object is not nil or an Exception instance" do + lambda { @s.rb_set_errinfo("error") }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/optional/capi/ext/.gitignore b/spec/rubyspec/optional/capi/ext/.gitignore new file mode 100644 index 0000000000..577d117bb1 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/.gitignore @@ -0,0 +1,9 @@ +# signature of implementation that +# last compiled an extension +*.sig + +# build artifacts +*.o +*.so +*.bundle +*.dll diff --git a/spec/rubyspec/optional/capi/ext/array_spec.c b/spec/rubyspec/optional/capi/ext/array_spec.c new file mode 100644 index 0000000000..8bc144195c --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/array_spec.c @@ -0,0 +1,452 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_ARRAY +static VALUE array_spec_rb_Array(VALUE self, VALUE object) { + return rb_Array(object); +} +#endif + +#if defined(HAVE_RARRAY_LEN) && defined(HAVE_RARRAY_PTR) +static VALUE array_spec_RARRAY_PTR_iterate(VALUE self, VALUE array) { + int i; + VALUE* ptr; + + ptr = RARRAY_PTR(array); + for(i = 0; i < RARRAY_LEN(array); i++) { + rb_yield(ptr[i]); + } + return Qnil; +} + +static VALUE array_spec_RARRAY_PTR_assign(VALUE self, VALUE array, VALUE value) { + int i; + VALUE* ptr; + + ptr = RARRAY_PTR(array); + for(i = 0; i < RARRAY_LEN(array); i++) { + ptr[i] = value; + } + return Qnil; +} +#endif + +#ifdef HAVE_RARRAY_LEN +static VALUE array_spec_RARRAY_LEN(VALUE self, VALUE array) { + return INT2FIX(RARRAY_LEN(array)); +} +#endif + +#ifdef HAVE_RARRAY_AREF +static VALUE array_spec_RARRAY_AREF(VALUE self, VALUE array, VALUE index) { + return RARRAY_AREF(array, FIX2INT(index)); +} +#endif + +#ifdef HAVE_RB_ARY_AREF +static VALUE array_spec_rb_ary_aref(int argc, VALUE *argv, VALUE self) { + VALUE ary, args; + rb_scan_args(argc, argv, "1*", &ary, &args); + return rb_ary_aref((int)RARRAY_LEN(args), RARRAY_PTR(args), ary); +} +#endif + +#ifdef HAVE_RB_ARY_CLEAR +static VALUE array_spec_rb_ary_clear(VALUE self, VALUE array) { + return rb_ary_clear(array); +} +#endif + +#ifdef HAVE_RB_ARY_DELETE +static VALUE array_spec_rb_ary_delete(VALUE self, VALUE array, VALUE item) { + return rb_ary_delete(array, item); +} +#endif + +#ifdef HAVE_RB_ARY_DELETE_AT +static VALUE array_spec_rb_ary_delete_at(VALUE self, VALUE array, VALUE index) { + return rb_ary_delete_at(array, NUM2LONG(index)); +} +#endif + +#ifdef HAVE_RB_ARY_DUP +static VALUE array_spec_rb_ary_dup(VALUE self, VALUE array) { + return rb_ary_dup(array); +} +#endif + +#ifdef HAVE_RB_ARY_ENTRY +static VALUE array_spec_rb_ary_entry(VALUE self, VALUE array, VALUE offset) { + return rb_ary_entry(array, FIX2INT(offset)); +} +#endif + +#ifdef HAVE_RB_ARY_INCLUDES +static VALUE array_spec_rb_ary_includes(VALUE self, VALUE ary, VALUE item) { + return rb_ary_includes(ary, item); +} +#endif + +#ifdef HAVE_RB_ARY_JOIN +static VALUE array_spec_rb_ary_join(VALUE self, VALUE array1, VALUE array2) { + return rb_ary_join(array1, array2); +} +#endif + +#ifdef HAVE_RB_ARY_TO_S +static VALUE array_spec_rb_ary_to_s(VALUE self, VALUE array) { + return rb_ary_to_s(array); +} +#endif + +#ifdef HAVE_RB_ARY_NEW +static VALUE array_spec_rb_ary_new(VALUE self) { + VALUE ret; + ret = rb_ary_new(); + return ret; +} +#endif + +#ifdef HAVE_RB_ARY_NEW2 +static VALUE array_spec_rb_ary_new2(VALUE self, VALUE length) { + return rb_ary_new2(NUM2LONG(length)); +} +#endif + +#ifdef HAVE_RB_ARY_NEW_CAPA +static VALUE array_spec_rb_ary_new_capa(VALUE self, VALUE length) { + return rb_ary_new_capa(NUM2LONG(length)); +} +#endif + +#ifdef HAVE_RB_ARY_NEW3 +static VALUE array_spec_rb_ary_new3(VALUE self, VALUE first, VALUE second, VALUE third) { + return rb_ary_new3(3, first, second, third); +} +#endif + +#ifdef HAVE_RB_ARY_NEW_FROM_ARGS +static VALUE array_spec_rb_ary_new_from_args(VALUE self, VALUE first, VALUE second, VALUE third) { + return rb_ary_new_from_args(3, first, second, third); +} +#endif + +#ifdef HAVE_RB_ARY_NEW4 +static VALUE array_spec_rb_ary_new4(VALUE self, VALUE first, VALUE second, VALUE third) { + VALUE values[3]; + values[0] = first; + values[1] = second; + values[2] = third; + return rb_ary_new4(3, values); +} +#endif + +#ifdef HAVE_RB_ARY_NEW_FROM_VALUES +static VALUE array_spec_rb_ary_new_from_values(VALUE self, VALUE first, VALUE second, VALUE third) { + VALUE values[3]; + values[0] = first; + values[1] = second; + values[2] = third; + return rb_ary_new_from_values(3, values); +} +#endif + +#ifdef HAVE_RB_ARY_POP +static VALUE array_spec_rb_ary_pop(VALUE self, VALUE array) { + return rb_ary_pop(array); +} +#endif + +#ifdef HAVE_RB_ARY_PUSH +static VALUE array_spec_rb_ary_push(VALUE self, VALUE array, VALUE item) { + rb_ary_push(array, item); + return array; +} +#endif + +#ifdef HAVE_RB_ARY_CAT +static VALUE array_spec_rb_ary_cat(int argc, VALUE *argv, VALUE self) { + VALUE ary, args; + rb_scan_args(argc, argv, "1*", &ary, &args); + return rb_ary_cat(ary, RARRAY_PTR(args), RARRAY_LEN(args)); +} +#endif + +#ifdef HAVE_RB_ARY_REVERSE +static VALUE array_spec_rb_ary_reverse(VALUE self, VALUE array) { + return rb_ary_reverse(array); +} +#endif + +#ifdef HAVE_RB_ARY_ROTATE +static VALUE array_spec_rb_ary_rotate(VALUE self, VALUE array, VALUE count) { + return rb_ary_rotate(array, NUM2LONG(count)); +} +#endif + +#ifdef HAVE_RB_ARY_SHIFT +static VALUE array_spec_rb_ary_shift(VALUE self, VALUE array) { + return rb_ary_shift(array); +} +#endif + +#ifdef HAVE_RB_ARY_STORE +static VALUE array_spec_rb_ary_store(VALUE self, VALUE array, VALUE offset, VALUE value) { + rb_ary_store(array, FIX2INT(offset), value); + + return Qnil; +} +#endif + +#ifdef HAVE_RB_ARY_CONCAT +static VALUE array_spec_rb_ary_concat(VALUE self, VALUE array1, VALUE array2) { + return rb_ary_concat(array1, array2); +} +#endif + +#ifdef HAVE_RB_ARY_PLUS +static VALUE array_spec_rb_ary_plus(VALUE self, VALUE array1, VALUE array2) { + return rb_ary_plus(array1, array2); +} +#endif + +#ifdef HAVE_RB_ARY_UNSHIFT +static VALUE array_spec_rb_ary_unshift(VALUE self, VALUE array, VALUE val) { + return rb_ary_unshift(array, val); +} +#endif + +#ifdef HAVE_RB_ASSOC_NEW +static VALUE array_spec_rb_assoc_new(VALUE self, VALUE first, VALUE second) { + return rb_assoc_new(first, second); +} +#endif + +#if defined(HAVE_RB_ITERATE) && defined(HAVE_RB_EACH) +static VALUE copy_ary(VALUE el, VALUE new_ary) { + return rb_ary_push(new_ary, el); +} + +static VALUE array_spec_rb_iterate(VALUE self, VALUE ary) { + VALUE new_ary = rb_ary_new(); + + rb_iterate(rb_each, ary, copy_ary, new_ary); + + return new_ary; +} + +static VALUE sub_pair(VALUE el, VALUE holder) { + return rb_ary_push(holder, rb_ary_entry(el, 1)); +} + +static VALUE each_pair(VALUE obj) { + return rb_funcall(obj, rb_intern("each_pair"), 0); +} + +static VALUE array_spec_rb_iterate_each_pair(VALUE self, VALUE obj) { + VALUE new_ary = rb_ary_new(); + + rb_iterate(each_pair, obj, sub_pair, new_ary); + + return new_ary; +} + +static VALUE iter_yield(VALUE el, VALUE ary) { + rb_yield(el); + return Qnil; +} + +static VALUE array_spec_rb_iterate_then_yield(VALUE self, VALUE obj) { + rb_iterate(rb_each, obj, iter_yield, obj); + return Qnil; +} +#endif + +#if defined(HAVE_RB_MEM_CLEAR) +static VALUE array_spec_rb_mem_clear(VALUE self, VALUE obj) { + VALUE ary[1]; + ary[0] = obj; + rb_mem_clear(ary, 1); + return ary[0]; +} +#endif + +#ifdef HAVE_RB_ARY_FREEZE +static VALUE array_spec_rb_ary_freeze(VALUE self, VALUE ary) { + return rb_ary_freeze(ary); +} +#endif + +#ifdef HAVE_RB_ARY_TO_ARY +static VALUE array_spec_rb_ary_to_ary(VALUE self, VALUE ary) { + return rb_ary_to_ary(ary); +} +#endif + +#ifdef HAVE_RB_ARY_SUBSEQ +static VALUE array_spec_rb_ary_subseq(VALUE self, VALUE ary, VALUE begin, VALUE len) { + return rb_ary_subseq(ary, FIX2LONG(begin), FIX2LONG(len)); +} +#endif + +void Init_array_spec(void) { + VALUE cls; + cls = rb_define_class("CApiArraySpecs", rb_cObject); + +#ifdef HAVE_RB_ARRAY + rb_define_method(cls, "rb_Array", array_spec_rb_Array, 1); +#endif + +#ifdef HAVE_RARRAY_LEN + rb_define_method(cls, "RARRAY_LEN", array_spec_RARRAY_LEN, 1); +#endif + +#if defined(HAVE_RARRAY_LEN) && defined(HAVE_RARRAY_PTR) + rb_define_method(cls, "RARRAY_PTR_iterate", array_spec_RARRAY_PTR_iterate, 1); + rb_define_method(cls, "RARRAY_PTR_assign", array_spec_RARRAY_PTR_assign, 2); +#endif + +#ifdef HAVE_RARRAY_AREF + rb_define_method(cls, "RARRAY_AREF", array_spec_RARRAY_AREF, 2); +#endif + +#ifdef HAVE_RB_ARY_AREF + rb_define_method(cls, "rb_ary_aref", array_spec_rb_ary_aref, -1); +#endif + +#ifdef HAVE_RB_ARY_CLEAR + rb_define_method(cls, "rb_ary_clear", array_spec_rb_ary_clear, 1); +#endif + +#ifdef HAVE_RB_ARY_DELETE + rb_define_method(cls, "rb_ary_delete", array_spec_rb_ary_delete, 2); +#endif + +#ifdef HAVE_RB_ARY_DELETE_AT + rb_define_method(cls, "rb_ary_delete_at", array_spec_rb_ary_delete_at, 2); +#endif + +#ifdef HAVE_RB_ARY_DUP + rb_define_method(cls, "rb_ary_dup", array_spec_rb_ary_dup, 1); +#endif + +#ifdef HAVE_RB_ARY_ENTRY + rb_define_method(cls, "rb_ary_entry", array_spec_rb_ary_entry, 2); +#endif + +#ifdef HAVE_RB_ARY_INCLUDES + rb_define_method(cls, "rb_ary_includes", array_spec_rb_ary_includes, 2); +#endif + +#ifdef HAVE_RB_ARY_JOIN + rb_define_method(cls, "rb_ary_join", array_spec_rb_ary_join, 2); +#endif + +#ifdef HAVE_RB_ARY_TO_S + rb_define_method(cls, "rb_ary_to_s", array_spec_rb_ary_to_s, 1); +#endif + +#ifdef HAVE_RB_ARY_NEW + rb_define_method(cls, "rb_ary_new", array_spec_rb_ary_new, 0); +#endif + +#ifdef HAVE_RB_ARY_NEW2 + rb_define_method(cls, "rb_ary_new2", array_spec_rb_ary_new2, 1); +#endif + +#ifdef HAVE_RB_ARY_NEW_CAPA + rb_define_method(cls, "rb_ary_new_capa", array_spec_rb_ary_new_capa, 1); +#endif + +#ifdef HAVE_RB_ARY_NEW3 + rb_define_method(cls, "rb_ary_new3", array_spec_rb_ary_new3, 3); +#endif + +#ifdef HAVE_RB_ARY_NEW_FROM_ARGS + rb_define_method(cls, "rb_ary_new_from_args", array_spec_rb_ary_new_from_args, 3); +#endif + +#ifdef HAVE_RB_ARY_NEW4 + rb_define_method(cls, "rb_ary_new4", array_spec_rb_ary_new4, 3); +#endif + +#ifdef HAVE_RB_ARY_NEW_FROM_VALUES + rb_define_method(cls, "rb_ary_new_from_values", array_spec_rb_ary_new_from_values, 3); +#endif + +#ifdef HAVE_RB_ARY_POP + rb_define_method(cls, "rb_ary_pop", array_spec_rb_ary_pop, 1); +#endif + +#ifdef HAVE_RB_ARY_PUSH + rb_define_method(cls, "rb_ary_push", array_spec_rb_ary_push, 2); +#endif + +#ifdef HAVE_RB_ARY_CAT + rb_define_method(cls, "rb_ary_cat", array_spec_rb_ary_cat, -1); +#endif + +#ifdef HAVE_RB_ARY_REVERSE + rb_define_method(cls, "rb_ary_reverse", array_spec_rb_ary_reverse, 1); +#endif + +#ifdef HAVE_RB_ARY_ROTATE + rb_define_method(cls, "rb_ary_rotate", array_spec_rb_ary_rotate, 2); +#endif + +#ifdef HAVE_RB_ARY_SHIFT + rb_define_method(cls, "rb_ary_shift", array_spec_rb_ary_shift, 1); +#endif + +#ifdef HAVE_RB_ARY_STORE + rb_define_method(cls, "rb_ary_store", array_spec_rb_ary_store, 3); +#endif + +#ifdef HAVE_RB_ARY_CONCAT + rb_define_method(cls, "rb_ary_concat", array_spec_rb_ary_concat, 2); +#endif + +#ifdef HAVE_RB_ARY_PLUS + rb_define_method(cls, "rb_ary_plus", array_spec_rb_ary_plus, 2); +#endif + +#ifdef HAVE_RB_ARY_UNSHIFT + rb_define_method(cls, "rb_ary_unshift", array_spec_rb_ary_unshift, 2); +#endif + +#ifdef HAVE_RB_ASSOC_NEW + rb_define_method(cls, "rb_assoc_new", array_spec_rb_assoc_new, 2); +#endif + +#if defined(HAVE_RB_ITERATE) && defined(HAVE_RB_EACH) + rb_define_method(cls, "rb_iterate", array_spec_rb_iterate, 1); + rb_define_method(cls, "rb_iterate_each_pair", array_spec_rb_iterate_each_pair, 1); + rb_define_method(cls, "rb_iterate_then_yield", array_spec_rb_iterate_then_yield, 1); +#endif + +#if defined(HAVE_RB_MEM_CLEAR) + rb_define_method(cls, "rb_mem_clear", array_spec_rb_mem_clear, 1); +#endif + +#ifdef HAVE_RB_ARY_FREEZE + rb_define_method(cls, "rb_ary_freeze", array_spec_rb_ary_freeze, 1); +#endif + +#ifdef HAVE_RB_ARY_TO_ARY + rb_define_method(cls, "rb_ary_to_ary", array_spec_rb_ary_to_ary, 1); +#endif + +#ifdef HAVE_RB_ARY_SUBSEQ + rb_define_method(cls, "rb_ary_subseq", array_spec_rb_ary_subseq, 3); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/bignum_spec.c b/spec/rubyspec/optional/capi/ext/bignum_spec.c new file mode 100644 index 0000000000..ab3b36eadc --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/bignum_spec.c @@ -0,0 +1,149 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_BIG2DBL +static VALUE bignum_spec_rb_big2dbl(VALUE self, VALUE num) { + return rb_float_new(rb_big2dbl(num)); +} +#endif + +#ifdef HAVE_RB_DBL2BIG +static VALUE bignum_spec_rb_dbl2big(VALUE self, VALUE num) { + double dnum = NUM2DBL(num); + + return rb_dbl2big(dnum); +} +#endif + +#ifdef HAVE_RB_BIG2LL +static VALUE bignum_spec_rb_big2ll(VALUE self, VALUE num) { + return rb_ll2inum(rb_big2ll(num)); +} +#endif + +#ifdef HAVE_RB_BIG2LONG +static VALUE bignum_spec_rb_big2long(VALUE self, VALUE num) { + return LONG2NUM(rb_big2long(num)); +} +#endif + +#ifdef HAVE_RB_BIG2STR +static VALUE bignum_spec_rb_big2str(VALUE self, VALUE num, VALUE base) { + return rb_big2str(num, FIX2INT(base)); +} +#endif + +#ifdef HAVE_RB_BIG2ULONG +static VALUE bignum_spec_rb_big2ulong(VALUE self, VALUE num) { + return ULONG2NUM(rb_big2ulong(num)); +} +#endif + +#ifdef HAVE_RB_BIG_CMP +static VALUE bignum_spec_rb_big_cmp(VALUE self, VALUE x, VALUE y) { + return rb_big_cmp(x, y); +} +#endif + +#ifdef HAVE_RB_BIG_PACK +static VALUE bignum_spec_rb_big_pack(VALUE self, VALUE val) { + unsigned long buff; + + rb_big_pack(val, &buff, 1); + + return ULONG2NUM(buff); +} +#endif + +#if HAVE_ABSINT_SIZE +static VALUE bignum_spec_rb_big_pack_length(VALUE self, VALUE val) { + long long_len; + int leading_bits = 0; + int divisor = SIZEOF_LONG; + size_t len = rb_absint_size(val, &leading_bits); + if (leading_bits == 0) { + len += 1; + } + + long_len = len / divisor + ((len % divisor == 0) ? 0 : 1); + return LONG2NUM(long_len); +} +#endif + +#ifdef HAVE_RB_BIG_PACK +static VALUE bignum_spec_rb_big_pack_array(VALUE self, VALUE val, VALUE len) { + int i; + long long_len = NUM2LONG(len); + + VALUE ary = rb_ary_new_capa(long_len); + unsigned long *buf = malloc(long_len * SIZEOF_LONG); + + /* The array should be filled with recognisable junk so we can check + it is all cleared properly. */ + + for (i = 0; i < long_len; i++) { +#if SIZEOF_LONG == 8 + buf[i] = 0xfedcba9876543210L; +#else + buf[i] = 0xfedcba98L; +#endif + } + + rb_big_pack(val, buf, long_len); + for (i = 0; i < long_len; i++) { + rb_ary_store(ary, i, ULONG2NUM(buf[i])); + } + free(buf); + return ary; +} +#endif + +void Init_bignum_spec(void) { + VALUE cls; + cls = rb_define_class("CApiBignumSpecs", rb_cObject); + +#ifdef HAVE_RB_BIG2DBL + rb_define_method(cls, "rb_big2dbl", bignum_spec_rb_big2dbl, 1); +#endif + +#ifdef HAVE_RB_DBL2BIG + rb_define_method(cls, "rb_dbl2big", bignum_spec_rb_dbl2big, 1); +#endif + +#ifdef HAVE_RB_BIG2LL + rb_define_method(cls, "rb_big2ll", bignum_spec_rb_big2ll, 1); +#endif + +#ifdef HAVE_RB_BIG2LONG + rb_define_method(cls, "rb_big2long", bignum_spec_rb_big2long, 1); +#endif + +#ifdef HAVE_RB_BIG2STR + rb_define_method(cls, "rb_big2str", bignum_spec_rb_big2str, 2); +#endif + +#ifdef HAVE_RB_BIG2ULONG + rb_define_method(cls, "rb_big2ulong", bignum_spec_rb_big2ulong, 1); +#endif + +#ifdef HAVE_RB_BIG_CMP + rb_define_method(cls, "rb_big_cmp", bignum_spec_rb_big_cmp, 2); +#endif + +#ifdef HAVE_RB_BIG_PACK + rb_define_method(cls, "rb_big_pack", bignum_spec_rb_big_pack, 1); + rb_define_method(cls, "rb_big_pack_array", bignum_spec_rb_big_pack_array, 2); +#endif + +#ifdef HAVE_ABSINT_SIZE + rb_define_method(cls, "rb_big_pack_length", bignum_spec_rb_big_pack_length, 1); +#endif +} + +#ifdef __cplusplus +extern "C" { +#endif diff --git a/spec/rubyspec/optional/capi/ext/boolean_spec.c b/spec/rubyspec/optional/capi/ext/boolean_spec.c new file mode 100644 index 0000000000..94d79c0fc0 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/boolean_spec.c @@ -0,0 +1,34 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static VALUE boolean_spec_is_true(VALUE self, VALUE boolean) { + if (boolean) { + return INT2NUM(1); + } else { + return INT2NUM(2); + } +} + +static VALUE boolean_spec_q_true(VALUE self) { + return Qtrue; +} + +static VALUE boolean_spec_q_false(VALUE self) { + return Qfalse; +} + +void Init_boolean_spec(void) { + VALUE cls; + cls = rb_define_class("CApiBooleanSpecs", rb_cObject); + rb_define_method(cls, "is_true", boolean_spec_is_true, 1); + rb_define_method(cls, "q_true", boolean_spec_q_true, 0); + rb_define_method(cls, "q_false", boolean_spec_q_false, 0); +} + +#ifdef __cplusplus +extern "C" { +#endif diff --git a/spec/rubyspec/optional/capi/ext/class_id_under_autoload_spec.c b/spec/rubyspec/optional/capi/ext/class_id_under_autoload_spec.c new file mode 100644 index 0000000000..64393a9397 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/class_id_under_autoload_spec.c @@ -0,0 +1,5 @@ +#include "ruby.h" + +void Init_class_id_under_autoload_spec(void) { + rb_define_class_id_under(rb_cObject, rb_intern("ClassIdUnderAutoload"), rb_cObject); +} diff --git a/spec/rubyspec/optional/capi/ext/class_spec.c b/spec/rubyspec/optional/capi/ext/class_spec.c new file mode 100644 index 0000000000..e3860df1da --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/class_spec.c @@ -0,0 +1,261 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_CALL_SUPER +static VALUE class_spec_call_super_method(VALUE self) { + return rb_call_super(0, 0); +} + +static VALUE class_spec_define_call_super_method(VALUE self, VALUE obj, VALUE str_name) { + rb_define_method(obj, RSTRING_PTR(str_name), class_spec_call_super_method, 0); + return Qnil; +} +#endif + +#ifdef HAVE_RB_CLASS_PATH +static VALUE class_spec_rb_class_path(VALUE self, VALUE klass) { + return rb_class_path(klass); +} +#endif + +#ifdef HAVE_RB_CLASS_NAME +static VALUE class_spec_rb_class_name(VALUE self, VALUE klass) { + return rb_class_name(klass); +} +#endif + +#ifdef HAVE_RB_CLASS2NAME +static VALUE class_spec_rb_class2name(VALUE self, VALUE klass) { + return rb_str_new2( rb_class2name(klass) ); +} +#endif + +#ifdef HAVE_RB_PATH2CLASS +static VALUE class_spec_rb_path2class(VALUE self, VALUE path) { + return rb_path2class(RSTRING_PTR(path)); +} +#endif + +#ifdef HAVE_RB_PATH_TO_CLASS +static VALUE class_spec_rb_path_to_class(VALUE self, VALUE path) { + return rb_path_to_class(path); +} +#endif + +#ifdef HAVE_RB_CLASS_NEW +static VALUE class_spec_rb_class_new(VALUE self, VALUE super) { + return rb_class_new(super); +} +#endif + +#ifdef HAVE_RB_CLASS_NEW_INSTANCE +static VALUE class_spec_rb_class_new_instance(VALUE self, + VALUE nargs, VALUE args, + VALUE klass) { + int c_nargs = FIX2INT(nargs); + VALUE *c_args = alloca(sizeof(VALUE) * c_nargs); + int i; + + for (i = 0; i < c_nargs; i++) + c_args[i] = rb_ary_entry(args, i); + + return rb_class_new_instance(c_nargs, c_args, klass); +} +#endif + +#ifdef HAVE_RB_CLASS_REAL +static VALUE class_spec_rb_class_real(VALUE self, VALUE object) { + if(rb_type_p(object, T_FIXNUM)) { + return INT2FIX(rb_class_real(FIX2INT(object))); + } else { + return rb_class_real(CLASS_OF(object)); + } +} +#endif + +#ifdef HAVE_RB_CLASS_SUPERCLASS +static VALUE class_spec_rb_class_superclass(VALUE self, VALUE klass) { + return rb_class_superclass(klass); +} +#endif + +#ifdef HAVE_RB_CVAR_DEFINED +static VALUE class_spec_cvar_defined(VALUE self, VALUE klass, VALUE id) { + ID as_id = rb_intern(StringValuePtr(id)); + return rb_cvar_defined(klass, as_id); +} +#endif + +#ifdef HAVE_RB_CVAR_GET +static VALUE class_spec_cvar_get(VALUE self, VALUE klass, VALUE name) { + return rb_cvar_get(klass, rb_intern(StringValuePtr(name))); +} +#endif + +#ifdef HAVE_RB_CVAR_SET +static VALUE class_spec_cvar_set(VALUE self, VALUE klass, VALUE name, VALUE val) { + rb_cvar_set(klass, rb_intern(StringValuePtr(name)), val); + return Qnil; +} +#endif + +#ifdef HAVE_RB_CV_GET +static VALUE class_spec_cv_get(VALUE self, VALUE klass, VALUE name) { + return rb_cv_get(klass, StringValuePtr(name)); +} +#endif + +#ifdef HAVE_RB_CV_SET +static VALUE class_spec_cv_set(VALUE self, VALUE klass, VALUE name, VALUE val) { + rb_cv_set(klass, StringValuePtr(name), val); + + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_ATTR +VALUE class_spec_define_attr(VALUE self, VALUE klass, VALUE sym, VALUE read, VALUE write) { + int int_read, int_write; + int_read = read == Qtrue ? 1 : 0; + int_write = write == Qtrue ? 1 : 0; + rb_define_attr(klass, rb_id2name(SYM2ID(sym)), int_read, int_write); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_CLASS +static VALUE class_spec_rb_define_class(VALUE self, VALUE name, VALUE super) { + if(NIL_P(super)) super = 0; + return rb_define_class(RSTRING_PTR(name), super); +} +#endif + +#ifdef HAVE_RB_DEFINE_CLASS_UNDER +static VALUE class_spec_rb_define_class_under(VALUE self, VALUE outer, + VALUE name, VALUE super) { + if(NIL_P(super)) super = 0; + return rb_define_class_under(outer, RSTRING_PTR(name), super); +} +#endif + +#ifdef HAVE_RB_DEFINE_CLASS_ID_UNDER +static VALUE class_spec_rb_define_class_id_under(VALUE self, VALUE outer, + VALUE name, VALUE super) { + if(NIL_P(super)) super = 0; + return rb_define_class_id_under(outer, SYM2ID(name), super); +} +#endif + +#ifdef HAVE_RB_DEFINE_CLASS_VARIABLE +static VALUE class_spec_define_class_variable(VALUE self, VALUE klass, VALUE name, VALUE val) { + rb_define_class_variable(klass, StringValuePtr(name), val); + return Qnil; +} +#endif + +#ifdef HAVE_RB_INCLUDE_MODULE +static VALUE class_spec_include_module(VALUE self, VALUE klass, VALUE module) { + rb_include_module(klass, module); + return klass; +} +#endif + +void Init_class_spec(void) { + VALUE cls; + cls = rb_define_class("CApiClassSpecs", rb_cObject); + +#ifdef HAVE_RB_CALL_SUPER + rb_define_method(cls, "define_call_super_method", class_spec_define_call_super_method, 2); +#endif + +#ifdef HAVE_RB_CLASS_PATH + rb_define_method(cls, "rb_class_path", class_spec_rb_class_path, 1); +#endif + +#ifdef HAVE_RB_CLASS_NAME + rb_define_method(cls, "rb_class_name", class_spec_rb_class_name, 1); +#endif + +#ifdef HAVE_RB_CLASS2NAME + rb_define_method(cls, "rb_class2name", class_spec_rb_class2name, 1); +#endif + +#ifdef HAVE_RB_PATH2CLASS + rb_define_method(cls, "rb_path2class", class_spec_rb_path2class, 1); +#endif + +#ifdef HAVE_RB_PATH_TO_CLASS + rb_define_method(cls, "rb_path_to_class", class_spec_rb_path_to_class, 1); +#endif + +#ifdef HAVE_RB_CLASS_NEW + rb_define_method(cls, "rb_class_new", class_spec_rb_class_new, 1); +#endif + +#ifdef HAVE_RB_CLASS_NEW_INSTANCE + rb_define_method(cls, "rb_class_new_instance", class_spec_rb_class_new_instance, 3); +#endif + +#ifdef HAVE_RB_CLASS_REAL + rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1); +#endif + +#ifdef HAVE_RB_CLASS_SUPERCLASS + rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1); +#endif + +#ifdef HAVE_RB_CVAR_DEFINED + rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2); +#endif + +#ifdef HAVE_RB_CVAR_GET + rb_define_method(cls, "rb_cvar_get", class_spec_cvar_get, 2); +#endif + +#ifdef HAVE_RB_CVAR_SET + rb_define_method(cls, "rb_cvar_set", class_spec_cvar_set, 3); +#endif + +#ifdef HAVE_RB_CV_GET + rb_define_method(cls, "rb_cv_get", class_spec_cv_get, 2); +#endif + +#ifdef HAVE_RB_CV_SET + rb_define_method(cls, "rb_cv_set", class_spec_cv_set, 3); +#endif + +#ifdef HAVE_RB_DEFINE_ATTR + rb_define_method(cls, "rb_define_attr", class_spec_define_attr, 4); +#endif + +#ifdef HAVE_RB_DEFINE_CLASS + rb_define_method(cls, "rb_define_class", class_spec_rb_define_class, 2); +#endif + +#ifdef HAVE_RB_DEFINE_CLASS_UNDER + rb_define_method(cls, "rb_define_class_under", class_spec_rb_define_class_under, 3); +#endif + +#ifdef HAVE_RB_DEFINE_CLASS_ID_UNDER + rb_define_method(cls, "rb_define_class_id_under", class_spec_rb_define_class_id_under, 3); +#endif + +#ifdef HAVE_RB_DEFINE_CLASS_VARIABLE + rb_define_method(cls, "rb_define_class_variable", class_spec_define_class_variable, 3); +#endif + +#ifdef HAVE_RB_INCLUDE_MODULE + rb_define_method(cls, "rb_include_module", class_spec_include_module, 2); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/class_under_autoload_spec.c b/spec/rubyspec/optional/capi/ext/class_under_autoload_spec.c new file mode 100644 index 0000000000..120dec7327 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/class_under_autoload_spec.c @@ -0,0 +1,5 @@ +#include "ruby.h" + +void Init_class_under_autoload_spec(void) { + rb_define_class_under(rb_cObject, "ClassUnderAutoload", rb_cObject); +} diff --git a/spec/rubyspec/optional/capi/ext/complex_spec.c b/spec/rubyspec/optional/capi/ext/complex_spec.c new file mode 100644 index 0000000000..476bbce31c --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/complex_spec.c @@ -0,0 +1,76 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_COMPLEX +static VALUE complex_spec_rb_Complex(VALUE self, VALUE num, VALUE den) { + return rb_Complex(num, den); +} +#endif + +#ifdef HAVE_RB_COMPLEX1 +static VALUE complex_spec_rb_Complex1(VALUE self, VALUE num) { + return rb_Complex1(num); +} +#endif + +#ifdef HAVE_RB_COMPLEX2 +static VALUE complex_spec_rb_Complex2(VALUE self, VALUE num, VALUE den) { + return rb_Complex2(num, den); +} +#endif + +#ifdef HAVE_RB_COMPLEX_NEW +static VALUE complex_spec_rb_complex_new(VALUE self, VALUE num, VALUE den) { + return rb_complex_new(num, den); +} +#endif + +#ifdef HAVE_RB_COMPLEX_NEW1 +static VALUE complex_spec_rb_complex_new1(VALUE self, VALUE num) { + return rb_complex_new1(num); +} +#endif + +#ifdef HAVE_RB_COMPLEX_NEW2 +static VALUE complex_spec_rb_complex_new2(VALUE self, VALUE num, VALUE den) { + return rb_complex_new2(num, den); +} +#endif + +void Init_complex_spec(void) { + VALUE cls; + cls = rb_define_class("CApiComplexSpecs", rb_cObject); + +#ifdef HAVE_RB_COMPLEX + rb_define_method(cls, "rb_Complex", complex_spec_rb_Complex, 2); +#endif + +#ifdef HAVE_RB_COMPLEX1 + rb_define_method(cls, "rb_Complex1", complex_spec_rb_Complex1, 1); +#endif + +#ifdef HAVE_RB_COMPLEX2 + rb_define_method(cls, "rb_Complex2", complex_spec_rb_Complex2, 2); +#endif + +#ifdef HAVE_RB_COMPLEX_NEW + rb_define_method(cls, "rb_complex_new", complex_spec_rb_complex_new, 2); +#endif + +#ifdef HAVE_RB_COMPLEX_NEW1 + rb_define_method(cls, "rb_complex_new1", complex_spec_rb_complex_new1, 1); +#endif + +#ifdef HAVE_RB_COMPLEX_NEW2 + rb_define_method(cls, "rb_complex_new2", complex_spec_rb_complex_new2, 2); +#endif +} + +#ifdef __cplusplus +} +#endif + diff --git a/spec/rubyspec/optional/capi/ext/constants_spec.c b/spec/rubyspec/optional/capi/ext/constants_spec.c new file mode 100644 index 0000000000..7751b12224 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/constants_spec.c @@ -0,0 +1,646 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_CARRAY +static VALUE constants_spec_rb_cArray(VALUE self) { + return rb_cArray; +} +#endif + +#ifdef HAVE_RB_CBIGNUM +static VALUE constants_spec_rb_cBignum(VALUE self) { + return rb_cBignum; +} +#endif + +#ifdef HAVE_RB_CCLASS +static VALUE constants_spec_rb_cClass(VALUE self) { + return rb_cClass; +} +#endif + +#ifdef HAVE_RB_CDATA +static VALUE constants_spec_rb_cData(VALUE self) { + return rb_cData; +} +#endif + +#ifdef HAVE_RB_CFALSECLASS +static VALUE constants_spec_rb_cFalseClass(VALUE self) { + return rb_cFalseClass; +} +#endif + +#ifdef HAVE_RB_CFILE +static VALUE constants_spec_rb_cFile(VALUE self) { + return rb_cFile; +} +#endif + +#ifdef HAVE_RB_CFIXNUM +static VALUE constants_spec_rb_cFixnum(VALUE self) { + return rb_cFixnum; +} +#endif + +#ifdef HAVE_RB_CFLOAT +static VALUE constants_spec_rb_cFloat(VALUE self) { + return rb_cFloat; +} +#endif + +#ifdef HAVE_RB_CHASH +static VALUE constants_spec_rb_cHash(VALUE self) { + return rb_cHash; +} +#endif + +#ifdef HAVE_RB_CINTEGER +static VALUE constants_spec_rb_cInteger(VALUE self) { + return rb_cInteger; +} +#endif + +#ifdef HAVE_RB_CIO +static VALUE constants_spec_rb_cIO(VALUE self) { + return rb_cIO; +} +#endif + +#ifdef HAVE_RB_CMODULE +static VALUE constants_spec_rb_cModule(VALUE self) { + return rb_cModule; +} +#endif + +#ifdef HAVE_RB_CMATCH +static VALUE constants_spec_rb_cMatch(VALUE self) { + return rb_cMatch; +} +#endif + +#ifdef HAVE_RB_CNILCLASS +static VALUE constants_spec_rb_cNilClass(VALUE self) { + return rb_cNilClass; +} +#endif + +#ifdef HAVE_RB_CNUMERIC +static VALUE constants_spec_rb_cNumeric(VALUE self) { + return rb_cNumeric; +} +#endif + +#ifdef HAVE_RB_COBJECT +static VALUE constants_spec_rb_cObject(VALUE self) { + return rb_cObject; +} +#endif + +#ifdef HAVE_RB_CRANGE +static VALUE constants_spec_rb_cRange(VALUE self) { + return rb_cRange; +} +#endif + +#ifdef HAVE_RB_CREGEXP +static VALUE constants_spec_rb_cRegexp(VALUE self) { + return rb_cRegexp; +} +#endif + +#ifdef HAVE_RB_CSTRING +static VALUE constants_spec_rb_cString(VALUE self) { + return rb_cString; +} +#endif + +#ifdef HAVE_RB_CSTRUCT +static VALUE constants_spec_rb_cStruct(VALUE self) { + return rb_cStruct; +} +#endif + +#ifdef HAVE_RB_CSYMBOL +static VALUE constants_spec_rb_cSymbol(VALUE self) { + return rb_cSymbol; +} +#endif + +#ifdef HAVE_RB_CTIME +static VALUE constants_spec_rb_cTime(VALUE self) { + return rb_cTime; +} +#endif + +#ifdef HAVE_RB_CTHREAD +static VALUE constants_spec_rb_cThread(VALUE self) { + return rb_cThread; +} +#endif + +#ifdef HAVE_RB_CTRUECLASS +static VALUE constants_spec_rb_cTrueClass(VALUE self) { + return rb_cTrueClass; +} +#endif + +#ifdef HAVE_RB_CPROC +static VALUE constants_spec_rb_cProc(VALUE self) { + return rb_cProc; +} +#endif + +#ifdef HAVE_RB_CMETHOD +static VALUE constants_spec_rb_cMethod(VALUE self) { + return rb_cMethod; +} +#endif + +#ifdef HAVE_RB_CENUMERATOR +static VALUE constants_spec_rb_cEnumerator(VALUE self) { + return rb_cEnumerator; +} +#endif + +#ifdef HAVE_RB_MCOMPARABLE +static VALUE constants_spec_rb_mComparable(VALUE self) { + return rb_mComparable; +} +#endif + +#ifdef HAVE_RB_MENUMERABLE +static VALUE constants_spec_rb_mEnumerable(VALUE self) { + return rb_mEnumerable; +} +#endif + +#ifdef HAVE_RB_MKERNEL +static VALUE constants_spec_rb_mKernel(VALUE self) { + return rb_mKernel; +} +#endif + +#ifdef HAVE_RB_EARGERROR +static VALUE constants_spec_rb_eArgError(VALUE self) { + return rb_eArgError; +} +#endif + +#ifdef HAVE_RB_EEOFERROR +static VALUE constants_spec_rb_eEOFError(VALUE self) { + return rb_eEOFError; +} +#endif + +#ifdef HAVE_RB_MERRNO +static VALUE constants_spec_rb_mErrno(VALUE self) { + return rb_mErrno; +} +#endif + +#ifdef HAVE_RB_EEXCEPTION +static VALUE constants_spec_rb_eException(VALUE self) { + return rb_eException; +} +#endif + +#ifdef HAVE_RB_EFLOATDOMAINERROR +static VALUE constants_spec_rb_eFloatDomainError(VALUE self) { + return rb_eFloatDomainError; +} +#endif + +#ifdef HAVE_RB_EINDEXERROR +static VALUE constants_spec_rb_eIndexError(VALUE self) { + return rb_eIndexError; +} +#endif + +#ifdef HAVE_RB_EINTERRUPT +static VALUE constants_spec_rb_eInterrupt(VALUE self) { + return rb_eInterrupt; +} +#endif + +#ifdef HAVE_RB_EIOERROR +static VALUE constants_spec_rb_eIOError(VALUE self) { + return rb_eIOError; +} +#endif + +#ifdef HAVE_RB_ELOADERROR +static VALUE constants_spec_rb_eLoadError(VALUE self) { + return rb_eLoadError; +} +#endif + +#ifdef HAVE_RB_ELOCALJUMPERROR +static VALUE constants_spec_rb_eLocalJumpError(VALUE self) { + return rb_eLocalJumpError; +} +#endif + +#ifdef HAVE_RB_ENAMEERROR +static VALUE constants_spec_rb_eNameError(VALUE self) { + return rb_eNameError; +} +#endif + +#ifdef HAVE_RB_ENOMEMERROR +static VALUE constants_spec_rb_eNoMemError(VALUE self) { + return rb_eNoMemError; +} +#endif + +#ifdef HAVE_RB_ENOMETHODERROR +static VALUE constants_spec_rb_eNoMethodError(VALUE self) { + return rb_eNoMethodError; +} +#endif + +#ifdef HAVE_RB_ENOTIMPERROR +static VALUE constants_spec_rb_eNotImpError(VALUE self) { + return rb_eNotImpError; +} +#endif + +#ifdef HAVE_RB_ERANGEERROR +static VALUE constants_spec_rb_eRangeError(VALUE self) { + return rb_eRangeError; +} +#endif + +#ifdef HAVE_RB_EREGEXPERROR +static VALUE constants_spec_rb_eRegexpError(VALUE self) { + return rb_eRegexpError; +} +#endif + +#ifdef HAVE_RB_ERUNTIMEERROR +static VALUE constants_spec_rb_eRuntimeError(VALUE self) { + return rb_eRuntimeError; +} +#endif + +#ifdef HAVE_RB_ESCRIPTERROR +static VALUE constants_spec_rb_eScriptError(VALUE self) { + return rb_eScriptError; +} +#endif + +#ifdef HAVE_RB_ESECURITYERROR +static VALUE constants_spec_rb_eSecurityError(VALUE self) { + return rb_eSecurityError; +} +#endif + +#ifdef HAVE_RB_ESIGNAL +static VALUE constants_spec_rb_eSignal(VALUE self) { + return rb_eSignal; +} +#endif + +#ifdef HAVE_RB_ESTANDARDERROR +static VALUE constants_spec_rb_eStandardError(VALUE self) { + return rb_eStandardError; +} +#endif + +#ifdef HAVE_RB_ESYNTAXERROR +static VALUE constants_spec_rb_eSyntaxError(VALUE self) { + return rb_eSyntaxError; +} +#endif + +#ifdef HAVE_RB_ESYSTEMCALLERROR +static VALUE constants_spec_rb_eSystemCallError(VALUE self) { + return rb_eSystemCallError; +} +#endif + +#ifdef HAVE_RB_ESYSTEMEXIT +static VALUE constants_spec_rb_eSystemExit(VALUE self) { + return rb_eSystemExit; +} +#endif + +#ifdef HAVE_RB_ESYSSTACKERROR +static VALUE constants_spec_rb_eSysStackError(VALUE self) { + return rb_eSysStackError; +} +#endif + +#ifdef HAVE_RB_ETYPEERROR +static VALUE constants_spec_rb_eTypeError(VALUE self) { + return rb_eTypeError; +} +#endif + +#ifdef HAVE_RB_ETHREADERROR +static VALUE constants_spec_rb_eThreadError(VALUE self) { + return rb_eThreadError; +} +#endif + +#ifdef HAVE_RB_EZERODIVERROR +static VALUE constants_spec_rb_eZeroDivError(VALUE self) { + return rb_eZeroDivError; +} +#endif + +#ifdef HAVE_RB_EMATHDOMAINERROR +static VALUE constants_spec_rb_eMathDomainError(VALUE self) { + return rb_eMathDomainError; +} +#endif + +#ifdef HAVE_RB_EENCCOMPATERROR +static VALUE constants_spec_rb_eEncCompatError(VALUE self) { + return rb_eEncCompatError; +} +#endif + +#ifdef HAVE_RB_MWAITREADABLE +static VALUE constants_spec_rb_mWaitReadable(VALUE self) { + return rb_mWaitReadable; +} +#endif + +#ifdef HAVE_RB_MWAITWRITABLE +static VALUE constants_spec_rb_mWaitWritable(VALUE self) { + return rb_mWaitWritable; +} +#endif + +#ifdef HAVE_RB_CDIR +static VALUE constants_spec_rb_cDir(VALUE self) { + return rb_cDir; +} +#endif + +void Init_constants_spec(void) { + VALUE cls; + cls = rb_define_class("CApiConstantsSpecs", rb_cObject); + +#ifdef HAVE_RB_CARRAY + rb_define_method(cls, "rb_cArray", constants_spec_rb_cArray, 0); +#endif + +#ifdef HAVE_RB_CBIGNUM + rb_define_method(cls, "rb_cBignum", constants_spec_rb_cBignum, 0); +#endif + +#ifdef HAVE_RB_CCLASS + rb_define_method(cls, "rb_cClass", constants_spec_rb_cClass, 0); +#endif + +#ifdef HAVE_RB_CDATA + rb_define_method(cls, "rb_cData", constants_spec_rb_cData, 0); +#endif + +#ifdef HAVE_RB_CFALSECLASS + rb_define_method(cls, "rb_cFalseClass", constants_spec_rb_cFalseClass, 0); +#endif + +#ifdef HAVE_RB_CFILE + rb_define_method(cls, "rb_cFile", constants_spec_rb_cFile, 0); +#endif + +#ifdef HAVE_RB_CFIXNUM + rb_define_method(cls, "rb_cFixnum", constants_spec_rb_cFixnum, 0); +#endif + +#ifdef HAVE_RB_CFLOAT + rb_define_method(cls, "rb_cFloat", constants_spec_rb_cFloat, 0); +#endif + +#ifdef HAVE_RB_CHASH + rb_define_method(cls, "rb_cHash", constants_spec_rb_cHash, 0); +#endif + +#ifdef HAVE_RB_CINTEGER + rb_define_method(cls, "rb_cInteger", constants_spec_rb_cInteger, 0); +#endif + +#ifdef HAVE_RB_CIO + rb_define_method(cls, "rb_cIO", constants_spec_rb_cIO, 0); +#endif + +#ifdef HAVE_RB_CMATCH + rb_define_method(cls, "rb_cMatch", constants_spec_rb_cMatch, 0); +#endif + +#ifdef HAVE_RB_CMODULE + rb_define_method(cls, "rb_cModule", constants_spec_rb_cModule, 0); +#endif + +#ifdef HAVE_RB_CNILCLASS + rb_define_method(cls, "rb_cNilClass", constants_spec_rb_cNilClass, 0); +#endif + +#ifdef HAVE_RB_CNUMERIC + rb_define_method(cls, "rb_cNumeric", constants_spec_rb_cNumeric, 0); +#endif + +#ifdef HAVE_RB_COBJECT + rb_define_method(cls, "rb_cObject", constants_spec_rb_cObject, 0); +#endif + +#ifdef HAVE_RB_CRANGE + rb_define_method(cls, "rb_cRange", constants_spec_rb_cRange, 0); +#endif + +#ifdef HAVE_RB_CREGEXP + rb_define_method(cls, "rb_cRegexp", constants_spec_rb_cRegexp, 0); +#endif + +#ifdef HAVE_RB_CSTRING + rb_define_method(cls, "rb_cString", constants_spec_rb_cString, 0); +#endif + +#ifdef HAVE_RB_CSTRUCT + rb_define_method(cls, "rb_cStruct", constants_spec_rb_cStruct, 0); +#endif + +#ifdef HAVE_RB_CSYMBOL + rb_define_method(cls, "rb_cSymbol", constants_spec_rb_cSymbol, 0); +#endif + +#ifdef HAVE_RB_CTIME + rb_define_method(cls, "rb_cTime", constants_spec_rb_cTime, 0); +#endif + +#ifdef HAVE_RB_CTHREAD + rb_define_method(cls, "rb_cThread", constants_spec_rb_cThread, 0); +#endif + +#ifdef HAVE_RB_CTRUECLASS + rb_define_method(cls, "rb_cTrueClass", constants_spec_rb_cTrueClass, 0); +#endif + +#ifdef HAVE_RB_CPROC + rb_define_method(cls, "rb_cProc", constants_spec_rb_cProc, 0); +#endif + +#ifdef HAVE_RB_CMETHOD + rb_define_method(cls, "rb_cMethod", constants_spec_rb_cMethod, 0); +#endif + +#ifdef HAVE_RB_CENUMERATOR + rb_define_method(cls, "rb_cEnumerator", constants_spec_rb_cEnumerator, 0); +#endif + +#ifdef HAVE_RB_MCOMPARABLE + rb_define_method(cls, "rb_mComparable", constants_spec_rb_mComparable, 0); +#endif + +#ifdef HAVE_RB_MENUMERABLE + rb_define_method(cls, "rb_mEnumerable", constants_spec_rb_mEnumerable, 0); +#endif + +#ifdef HAVE_RB_MKERNEL + rb_define_method(cls, "rb_mKernel", constants_spec_rb_mKernel, 0); +#endif + +#ifdef HAVE_RB_EARGERROR + rb_define_method(cls, "rb_eArgError", constants_spec_rb_eArgError, 0); +#endif + +#ifdef HAVE_RB_EEOFERROR + rb_define_method(cls, "rb_eEOFError", constants_spec_rb_eEOFError, 0); +#endif + +#ifdef HAVE_RB_MERRNO + rb_define_method(cls, "rb_mErrno", constants_spec_rb_mErrno, 0); +#endif + +#ifdef HAVE_RB_EEXCEPTION + rb_define_method(cls, "rb_eException", constants_spec_rb_eException, 0); +#endif + +#ifdef HAVE_RB_EFLOATDOMAINERROR + rb_define_method(cls, "rb_eFloatDomainError", constants_spec_rb_eFloatDomainError, 0); +#endif + +#ifdef HAVE_RB_EINDEXERROR + rb_define_method(cls, "rb_eIndexError", constants_spec_rb_eIndexError, 0); +#endif + +#ifdef HAVE_RB_EINTERRUPT + rb_define_method(cls, "rb_eInterrupt", constants_spec_rb_eInterrupt, 0); +#endif + +#ifdef HAVE_RB_EIOERROR + rb_define_method(cls, "rb_eIOError", constants_spec_rb_eIOError, 0); +#endif + +#ifdef HAVE_RB_ELOADERROR + rb_define_method(cls, "rb_eLoadError", constants_spec_rb_eLoadError, 0); +#endif + +#ifdef HAVE_RB_ELOCALJUMPERROR + rb_define_method(cls, "rb_eLocalJumpError", constants_spec_rb_eLocalJumpError, 0); +#endif + +#ifdef HAVE_RB_ENAMEERROR + rb_define_method(cls, "rb_eNameError", constants_spec_rb_eNameError, 0); +#endif + +#ifdef HAVE_RB_ENOMEMERROR + rb_define_method(cls, "rb_eNoMemError", constants_spec_rb_eNoMemError, 0); +#endif + +#ifdef HAVE_RB_ENOMETHODERROR + rb_define_method(cls, "rb_eNoMethodError", constants_spec_rb_eNoMethodError, 0); +#endif + +#ifdef HAVE_RB_ENOTIMPERROR + rb_define_method(cls, "rb_eNotImpError", constants_spec_rb_eNotImpError, 0); +#endif + +#ifdef HAVE_RB_ERANGEERROR + rb_define_method(cls, "rb_eRangeError", constants_spec_rb_eRangeError, 0); +#endif + +#ifdef HAVE_RB_EREGEXPERROR + rb_define_method(cls, "rb_eRegexpError", constants_spec_rb_eRegexpError, 0); +#endif + +#ifdef HAVE_RB_ERUNTIMEERROR + rb_define_method(cls, "rb_eRuntimeError", constants_spec_rb_eRuntimeError, 0); +#endif + +#ifdef HAVE_RB_ESCRIPTERROR + rb_define_method(cls, "rb_eScriptError", constants_spec_rb_eScriptError, 0); +#endif + +#ifdef HAVE_RB_ESECURITYERROR + rb_define_method(cls, "rb_eSecurityError", constants_spec_rb_eSecurityError, 0); +#endif + +#ifdef HAVE_RB_ESIGNAL + rb_define_method(cls, "rb_eSignal", constants_spec_rb_eSignal, 0); +#endif + +#ifdef HAVE_RB_ESTANDARDERROR + rb_define_method(cls, "rb_eStandardError", constants_spec_rb_eStandardError, 0); +#endif + +#ifdef HAVE_RB_ESYNTAXERROR + rb_define_method(cls, "rb_eSyntaxError", constants_spec_rb_eSyntaxError, 0); +#endif + +#ifdef HAVE_RB_ESYSTEMCALLERROR + rb_define_method(cls, "rb_eSystemCallError", constants_spec_rb_eSystemCallError, 0); +#endif + +#ifdef HAVE_RB_ESYSTEMEXIT + rb_define_method(cls, "rb_eSystemExit", constants_spec_rb_eSystemExit, 0); +#endif + +#ifdef HAVE_RB_ESYSSTACKERROR + rb_define_method(cls, "rb_eSysStackError", constants_spec_rb_eSysStackError, 0); +#endif + +#ifdef HAVE_RB_ETYPEERROR + rb_define_method(cls, "rb_eTypeError", constants_spec_rb_eTypeError, 0); +#endif + +#ifdef HAVE_RB_ETHREADERROR + rb_define_method(cls, "rb_eThreadError", constants_spec_rb_eThreadError, 0); +#endif + +#ifdef HAVE_RB_EZERODIVERROR + rb_define_method(cls, "rb_eZeroDivError", constants_spec_rb_eZeroDivError, 0); +#endif + +#ifdef HAVE_RB_EMATHDOMAINERROR + rb_define_method(cls, "rb_eMathDomainError", constants_spec_rb_eMathDomainError, 0); +#endif + +#ifdef HAVE_RB_EENCCOMPATERROR + rb_define_method(cls, "rb_eEncCompatError", constants_spec_rb_eEncCompatError, 0); +#endif + +#ifdef HAVE_RB_MWAITREADABLE + rb_define_method(cls, "rb_mWaitReadable", constants_spec_rb_mWaitReadable, 0); +#endif + +#ifdef HAVE_RB_MWAITWRITABLE + rb_define_method(cls, "rb_mWaitWritable", constants_spec_rb_mWaitWritable, 0); +#endif + +#ifdef HAVE_RB_CDIR + rb_define_method(cls, "rb_cDir", constants_spec_rb_cDir, 0); +#endif + +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/data_spec.c b/spec/rubyspec/optional/capi/ext/data_spec.c new file mode 100644 index 0000000000..ed79497897 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/data_spec.c @@ -0,0 +1,97 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(HAVE_RDATA) && defined(HAVE_DATA_WRAP_STRUCT) +struct sample_wrapped_struct { + int foo; +}; + +void sample_wrapped_struct_free(void* st) { + free(st); +} + +void sample_wrapped_struct_mark(void* st) { +} + +VALUE sdaf_alloc_func(VALUE klass) { + struct sample_wrapped_struct* bar = (struct sample_wrapped_struct *)malloc(sizeof(struct sample_wrapped_struct)); + bar->foo = 42; + return Data_Wrap_Struct(klass, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar); +} + +VALUE sdaf_get_struct(VALUE self) { + struct sample_wrapped_struct* bar; + Data_Get_Struct(self, struct sample_wrapped_struct, bar); + + return INT2FIX((*bar).foo); +} + +VALUE sws_wrap_struct(VALUE self, VALUE val) { + struct sample_wrapped_struct* bar = (struct sample_wrapped_struct *)malloc(sizeof(struct sample_wrapped_struct)); + bar->foo = FIX2INT(val); + return Data_Wrap_Struct(rb_cObject, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar); +} + +VALUE sws_wrap_struct_null(VALUE self, VALUE val) { + struct sample_wrapped_struct* bar = (struct sample_wrapped_struct *)malloc(sizeof(struct sample_wrapped_struct)); + bar->foo = FIX2INT(val); + return Data_Wrap_Struct(0, &sample_wrapped_struct_mark, &sample_wrapped_struct_free, bar); +} + +VALUE sws_get_struct(VALUE self, VALUE obj) { + struct sample_wrapped_struct* bar; + Data_Get_Struct(obj, struct sample_wrapped_struct, bar); + + return INT2FIX((*bar).foo); +} + +VALUE sws_get_struct_rdata(VALUE self, VALUE obj) { + struct sample_wrapped_struct* bar; + bar = (struct sample_wrapped_struct*) RDATA(obj)->data; + return INT2FIX(bar->foo); +} + +VALUE sws_get_struct_data_ptr(VALUE self, VALUE obj) { + struct sample_wrapped_struct* bar; + bar = (struct sample_wrapped_struct*) DATA_PTR(obj); + return INT2FIX(bar->foo); +} + +VALUE sws_change_struct(VALUE self, VALUE obj, VALUE new_val) { + struct sample_wrapped_struct *old_struct, *new_struct; + new_struct = (struct sample_wrapped_struct *)malloc(sizeof(struct sample_wrapped_struct)); + new_struct->foo = FIX2INT(new_val); + old_struct = RDATA(obj)->data; + free(old_struct); + RDATA(obj)->data = new_struct; + return Qnil; +} +#endif + +void Init_data_spec(void) { + VALUE cls; + cls = rb_define_class("CApiAllocSpecs", rb_cObject); + +#if defined(HAVE_RDATA) && defined(HAVE_DATA_WRAP_STRUCT) + rb_define_alloc_func(cls, sdaf_alloc_func); + rb_define_method(cls, "wrapped_data", sdaf_get_struct, 0); + + cls = rb_define_class("CApiWrappedStructSpecs", rb_cObject); + rb_define_method(cls, "wrap_struct", sws_wrap_struct, 1); + rb_define_method(cls, "wrap_struct_null", sws_wrap_struct_null, 1); + rb_define_method(cls, "get_struct", sws_get_struct, 1); + rb_define_method(cls, "get_struct_rdata", sws_get_struct_rdata, 1); + rb_define_method(cls, "get_struct_data_ptr", sws_get_struct_data_ptr, 1); + rb_define_method(cls, "change_struct", sws_change_struct, 2); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/encoding_spec.c b/spec/rubyspec/optional/capi/ext/encoding_spec.c new file mode 100644 index 0000000000..2e555ebc77 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/encoding_spec.c @@ -0,0 +1,424 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include "ruby/encoding.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_ENC_CODERANGE_ASCIIONLY +static VALUE encoding_spec_ENC_CODERANGE_ASCIIONLY(VALUE self, VALUE obj) { + if(ENC_CODERANGE_ASCIIONLY(obj)) { + return Qtrue; + } else { + return Qfalse; + } +} +#endif + +#ifdef HAVE_RB_USASCII_ENCODING +static VALUE encoding_spec_rb_usascii_encoding(VALUE self) { + return rb_str_new2(rb_usascii_encoding()->name); +} +#endif + +#ifdef HAVE_RB_USASCII_ENCINDEX +static VALUE encoding_spec_rb_usascii_encindex(VALUE self) { + return INT2NUM(rb_usascii_encindex()); +} +#endif + +#ifdef HAVE_RB_ASCII8BIT_ENCODING +static VALUE encoding_spec_rb_ascii8bit_encoding(VALUE self) { + return rb_str_new2(rb_ascii8bit_encoding()->name); +} +#endif + +#ifdef HAVE_RB_ASCII8BIT_ENCINDEX +static VALUE encoding_spec_rb_ascii8bit_encindex(VALUE self) { + return INT2NUM(rb_ascii8bit_encindex()); +} +#endif + +#ifdef HAVE_RB_UTF8_ENCODING +static VALUE encoding_spec_rb_utf8_encoding(VALUE self) { + return rb_str_new2(rb_utf8_encoding()->name); +} +#endif + +#ifdef HAVE_RB_UTF8_ENCINDEX +static VALUE encoding_spec_rb_utf8_encindex(VALUE self) { + return INT2NUM(rb_utf8_encindex()); +} +#endif + +#ifdef HAVE_RB_LOCALE_ENCODING +static VALUE encoding_spec_rb_locale_encoding(VALUE self) { + return rb_str_new2(rb_locale_encoding()->name); +} +#endif + +#ifdef HAVE_RB_LOCALE_ENCINDEX +static VALUE encoding_spec_rb_locale_encindex(VALUE self) { + return INT2NUM(rb_locale_encindex()); +} +#endif + +#ifdef HAVE_RB_FILESYSTEM_ENCODING +static VALUE encoding_spec_rb_filesystem_encoding(VALUE self) { + return rb_str_new2(rb_filesystem_encoding()->name); +} +#endif + +#ifdef HAVE_RB_FILESYSTEM_ENCINDEX +static VALUE encoding_spec_rb_filesystem_encindex(VALUE self) { + return INT2NUM(rb_filesystem_encindex()); +} +#endif + +#ifdef HAVE_RB_DEFAULT_INTERNAL_ENCODING +static VALUE encoding_spec_rb_default_internal_encoding(VALUE self) { + rb_encoding* enc = rb_default_internal_encoding(); + if(enc == 0) return Qnil; + return rb_str_new2(enc->name); +} +#endif + +#ifdef HAVE_RB_DEFAULT_EXTERNAL_ENCODING +static VALUE encoding_spec_rb_default_external_encoding(VALUE self) { + rb_encoding* enc = rb_default_external_encoding(); + if(enc == 0) return Qnil; + return rb_str_new2(enc->name); +} +#endif + +#ifdef HAVE_RB_ENCDB_ALIAS +/* Not exposed by MRI C-API encoding.h but used in the pg gem. */ +extern int rb_encdb_alias(const char* alias, const char* orig); + +static VALUE encoding_spec_rb_encdb_alias(VALUE self, VALUE alias, VALUE orig) { + return INT2NUM(rb_encdb_alias(RSTRING_PTR(alias), RSTRING_PTR(orig))); +} +#endif + +#if defined(HAVE_RB_ENC_ASSOCIATE) && defined(HAVE_RB_ENC_FIND) +static VALUE encoding_spec_rb_enc_associate(VALUE self, VALUE obj, VALUE enc) { + return rb_enc_associate(obj, NIL_P(enc) ? NULL : rb_enc_find(RSTRING_PTR(enc))); +} +#endif + +#if defined(HAVE_RB_ENC_ASSOCIATE_INDEX) && defined(HAVE_RB_ENC_FIND_INDEX) +static VALUE encoding_spec_rb_enc_associate_index(VALUE self, VALUE obj, VALUE index) { + return rb_enc_associate_index(obj, FIX2INT(index)); +} +#endif + +#ifdef HAVE_RB_ENC_COMPATIBLE +static VALUE encoding_spec_rb_enc_compatible(VALUE self, VALUE a, VALUE b) { + rb_encoding* enc = rb_enc_compatible(a, b); + + if(!enc) return INT2FIX(0); + + return rb_enc_from_encoding(enc); +} +#endif + +#ifdef HAVE_RB_ENC_COPY +static VALUE encoding_spec_rb_enc_copy(VALUE self, VALUE dest, VALUE src) { + rb_enc_copy(dest, src); + return dest; +} +#endif + +#ifdef HAVE_RB_ENC_FIND +static VALUE encoding_spec_rb_enc_find(VALUE self, VALUE name) { + return rb_str_new2(rb_enc_find(RSTRING_PTR(name))->name); +} +#endif + +#ifdef HAVE_RB_ENC_FIND_INDEX +static VALUE encoding_spec_rb_enc_find_index(VALUE self, VALUE name) { + return INT2NUM(rb_enc_find_index(RSTRING_PTR(name))); +} +#endif + +#ifdef HAVE_RB_ENC_FROM_INDEX +static VALUE encoding_spec_rb_enc_from_index(VALUE self, VALUE index) { + return rb_str_new2(rb_enc_from_index(NUM2INT(index))->name); +} +#endif + +#ifdef HAVE_RB_ENC_FROM_ENCODING +static VALUE encoding_spec_rb_enc_from_encoding(VALUE self, VALUE name) { + return rb_enc_from_encoding(rb_enc_find(RSTRING_PTR(name))); +} +#endif + +#ifdef HAVE_RB_ENC_GET +static VALUE encoding_spec_rb_enc_get(VALUE self, VALUE obj) { + return rb_str_new2(rb_enc_get(obj)->name); +} +#endif + +#ifdef HAVE_RB_OBJ_ENCODING +static VALUE encoding_spec_rb_obj_encoding(VALUE self, VALUE obj) { + return rb_obj_encoding(obj); +} +#endif + +#ifdef HAVE_RB_ENC_GET_INDEX +static VALUE encoding_spec_rb_enc_get_index(VALUE self, VALUE obj) { + return INT2NUM(rb_enc_get_index(obj)); +} +#endif + +#if defined(HAVE_RB_ENC_SET_INDEX) \ + && defined(HAVE_RB_ENC_FIND_INDEX) \ + && defined(HAVE_RB_ENC_FIND_INDEX) +static VALUE encoding_spec_rb_enc_set_index(VALUE self, VALUE obj, VALUE index) { + int i = NUM2INT(index); + + rb_encoding* enc = rb_enc_from_index(i); + rb_enc_set_index(obj, i); + + return rb_ary_new3(2, rb_str_new2(rb_enc_name(enc)), + rb_str_new2(rb_enc_name(rb_enc_get(obj)))); +} +#endif + +#ifdef HAVE_RB_ENC_STR_CODERANGE +static VALUE encoding_spec_rb_enc_str_coderange(VALUE self, VALUE str) { + int coderange = rb_enc_str_coderange(str); + + switch(coderange) { + case ENC_CODERANGE_UNKNOWN: + return ID2SYM(rb_intern("coderange_unknown")); + case ENC_CODERANGE_7BIT: + return ID2SYM(rb_intern("coderange_7bit")); + case ENC_CODERANGE_VALID: + return ID2SYM(rb_intern("coderange_valid")); + case ENC_CODERANGE_BROKEN: + return ID2SYM(rb_intern("coderange_broken")); + default: + return ID2SYM(rb_intern("coderange_unrecognized")); + } +} +#endif + +#ifdef HAVE_RB_ENC_STR_NEW +static VALUE encoding_spec_rb_enc_str_new(VALUE self, VALUE str, VALUE len, VALUE enc) { + return rb_enc_str_new(RSTRING_PTR(str), FIX2INT(len), rb_to_encoding(enc)); +} +#endif + +#ifdef HAVE_ENCODING_GET +static VALUE encoding_spec_ENCODING_GET(VALUE self, VALUE obj) { + return INT2NUM(ENCODING_GET(obj)); +} +#endif + +#ifdef HAVE_ENCODING_SET +static VALUE encoding_spec_ENCODING_SET(VALUE self, VALUE obj, VALUE index) { + int i = NUM2INT(index); + + rb_encoding* enc = rb_enc_from_index(i); + ENCODING_SET(obj, i); + + return rb_ary_new3(2, rb_str_new2(rb_enc_name(enc)), + rb_str_new2(rb_enc_name(rb_enc_get(obj)))); +} +#endif + +#if defined(HAVE_RB_ENC_TO_INDEX) && defined(HAVE_RB_ENC_FIND) +static VALUE encoding_spec_rb_enc_to_index(VALUE self, VALUE name) { + return INT2NUM(rb_enc_to_index(NIL_P(name) ? NULL : rb_enc_find(RSTRING_PTR(name)))); +} +#endif + +#ifdef HAVE_RB_TO_ENCODING +static VALUE encoding_spec_rb_to_encoding(VALUE self, VALUE obj) { + return rb_str_new2(rb_to_encoding(obj)->name); +} +#endif + +#ifdef HAVE_RB_TO_ENCODING_INDEX +static VALUE encoding_spec_rb_to_encoding_index(VALUE self, VALUE obj) { + return INT2NUM(rb_to_encoding_index(obj)); +} +#endif + +#ifdef HAVE_RB_ENC_NTH +static VALUE encoding_spec_rb_enc_nth(VALUE self, VALUE str, VALUE index) { + char* start = RSTRING_PTR(str); + char* end = start + RSTRING_LEN(str); + char* ptr = rb_enc_nth(start, end, FIX2LONG(index), rb_enc_get(str)); + return LONG2NUM(ptr - start); +} +#endif + +#ifdef HAVE_RB_ENC_CODEPOINT_LEN +static VALUE encoding_spec_rb_enc_codepoint_len(VALUE self, VALUE str) { + char* start = RSTRING_PTR(str); + char* end = start + RSTRING_LEN(str); + + int len; + unsigned int codepoint = rb_enc_codepoint_len(start, end, &len, rb_enc_get(str)); + + return rb_ary_new3(2, LONG2NUM(codepoint), LONG2NUM(len)); +} +#endif + +void Init_encoding_spec(void) { + VALUE cls; + cls = rb_define_class("CApiEncodingSpecs", rb_cObject); + +#ifdef HAVE_ENC_CODERANGE_ASCIIONLY + rb_define_method(cls, "ENC_CODERANGE_ASCIIONLY", + encoding_spec_ENC_CODERANGE_ASCIIONLY, 1); +#endif + +#ifdef HAVE_RB_USASCII_ENCODING + rb_define_method(cls, "rb_usascii_encoding", encoding_spec_rb_usascii_encoding, 0); +#endif + +#ifdef HAVE_RB_USASCII_ENCINDEX + rb_define_method(cls, "rb_usascii_encindex", encoding_spec_rb_usascii_encindex, 0); +#endif + +#ifdef HAVE_RB_ASCII8BIT_ENCODING + rb_define_method(cls, "rb_ascii8bit_encoding", encoding_spec_rb_ascii8bit_encoding, 0); +#endif + +#ifdef HAVE_RB_ASCII8BIT_ENCINDEX + rb_define_method(cls, "rb_ascii8bit_encindex", encoding_spec_rb_ascii8bit_encindex, 0); +#endif + +#ifdef HAVE_RB_UTF8_ENCODING + rb_define_method(cls, "rb_utf8_encoding", encoding_spec_rb_utf8_encoding, 0); +#endif + +#ifdef HAVE_RB_UTF8_ENCINDEX + rb_define_method(cls, "rb_utf8_encindex", encoding_spec_rb_utf8_encindex, 0); +#endif + +#ifdef HAVE_RB_LOCALE_ENCODING + rb_define_method(cls, "rb_locale_encoding", encoding_spec_rb_locale_encoding, 0); +#endif + +#ifdef HAVE_RB_LOCALE_ENCINDEX + rb_define_method(cls, "rb_locale_encindex", encoding_spec_rb_locale_encindex, 0); +#endif + +#ifdef HAVE_RB_FILESYSTEM_ENCODING + rb_define_method(cls, "rb_filesystem_encoding", encoding_spec_rb_filesystem_encoding, 0); +#endif + +#ifdef HAVE_RB_FILESYSTEM_ENCINDEX + rb_define_method(cls, "rb_filesystem_encindex", encoding_spec_rb_filesystem_encindex, 0); +#endif + +#ifdef HAVE_RB_DEFAULT_INTERNAL_ENCODING + rb_define_method(cls, "rb_default_internal_encoding", + encoding_spec_rb_default_internal_encoding, 0); +#endif + +#ifdef HAVE_RB_DEFAULT_EXTERNAL_ENCODING + rb_define_method(cls, "rb_default_external_encoding", + encoding_spec_rb_default_external_encoding, 0); +#endif + +#ifdef HAVE_RB_ENCDB_ALIAS + rb_define_method(cls, "rb_encdb_alias", encoding_spec_rb_encdb_alias, 2); +#endif + +#ifdef HAVE_RB_ENC_ASSOCIATE + rb_define_method(cls, "rb_enc_associate", encoding_spec_rb_enc_associate, 2); +#endif + +#ifdef HAVE_RB_ENC_ASSOCIATE_INDEX + rb_define_method(cls, "rb_enc_associate_index", encoding_spec_rb_enc_associate_index, 2); +#endif + +#ifdef HAVE_RB_ENC_COMPATIBLE + rb_define_method(cls, "rb_enc_compatible", encoding_spec_rb_enc_compatible, 2); +#endif + +#ifdef HAVE_RB_ENC_COPY + rb_define_method(cls, "rb_enc_copy", encoding_spec_rb_enc_copy, 2); +#endif + +#ifdef HAVE_RB_ENC_FIND + rb_define_method(cls, "rb_enc_find", encoding_spec_rb_enc_find, 1); +#endif + +#ifdef HAVE_RB_ENC_FIND_INDEX + rb_define_method(cls, "rb_enc_find_index", encoding_spec_rb_enc_find_index, 1); +#endif + +#ifdef HAVE_RB_ENC_FROM_INDEX + rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1); +#endif + +#ifdef HAVE_RB_ENC_FROM_ENCODING + rb_define_method(cls, "rb_enc_from_encoding", encoding_spec_rb_enc_from_encoding, 1); +#endif + +#ifdef HAVE_RB_ENC_GET + rb_define_method(cls, "rb_enc_get", encoding_spec_rb_enc_get, 1); +#endif + +#ifdef HAVE_RB_OBJ_ENCODING + rb_define_method(cls, "rb_obj_encoding", encoding_spec_rb_obj_encoding, 1); +#endif + +#ifdef HAVE_RB_ENC_GET_INDEX + rb_define_method(cls, "rb_enc_get_index", encoding_spec_rb_enc_get_index, 1); +#endif + +#if defined(HAVE_RB_ENC_SET_INDEX) \ + && defined(HAVE_RB_ENC_FIND_INDEX) \ + && defined(HAVE_RB_ENC_FIND_INDEX) + rb_define_method(cls, "rb_enc_set_index", encoding_spec_rb_enc_set_index, 2); +#endif + +#ifdef HAVE_RB_ENC_STR_CODERANGE + rb_define_method(cls, "rb_enc_str_coderange", encoding_spec_rb_enc_str_coderange, 1); +#endif + +#ifdef HAVE_RB_ENC_STR_NEW + rb_define_method(cls, "rb_enc_str_new", encoding_spec_rb_enc_str_new, 3); +#endif + +#ifdef HAVE_ENCODING_GET + rb_define_method(cls, "ENCODING_GET", encoding_spec_ENCODING_GET, 1); +#endif + +#ifdef HAVE_ENCODING_SET + rb_define_method(cls, "ENCODING_SET", encoding_spec_ENCODING_SET, 2); +#endif + +#if defined(HAVE_RB_ENC_TO_INDEX) && defined(HAVE_RB_ENC_FIND) + rb_define_method(cls, "rb_enc_to_index", encoding_spec_rb_enc_to_index, 1); +#endif + +#ifdef HAVE_RB_TO_ENCODING + rb_define_method(cls, "rb_to_encoding", encoding_spec_rb_to_encoding, 1); +#endif + +#ifdef HAVE_RB_TO_ENCODING_INDEX + rb_define_method(cls, "rb_to_encoding_index", encoding_spec_rb_to_encoding_index, 1); +#endif + +#ifdef HAVE_RB_ENC_NTH + rb_define_method(cls, "rb_enc_nth", encoding_spec_rb_enc_nth, 2); +#endif + +#ifdef HAVE_RB_ENC_CODEPOINT_LEN + rb_define_method(cls, "rb_enc_codepoint_len", encoding_spec_rb_enc_codepoint_len, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/enumerator_spec.c b/spec/rubyspec/optional/capi/ext/enumerator_spec.c new file mode 100644 index 0000000000..6b08feab52 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/enumerator_spec.c @@ -0,0 +1,27 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_ENUMERATORIZE +VALUE enumerator_spec_rb_enumeratorize(int argc, VALUE *argv, VALUE self) { + VALUE obj, meth, args; + rb_scan_args(argc, argv, "2*", &obj, &meth, &args); + return rb_enumeratorize(obj, meth, (int)RARRAY_LEN(args), RARRAY_PTR(args)); +} +#endif + +void Init_enumerator_spec(void) { + VALUE cls; + cls = rb_define_class("CApiEnumeratorSpecs", rb_cObject); + +#ifdef HAVE_RB_ENUMERATORIZE + rb_define_method(cls, "rb_enumeratorize", enumerator_spec_rb_enumeratorize, -1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/exception_spec.c b/spec/rubyspec/optional/capi/ext/exception_spec.c new file mode 100644 index 0000000000..b37f74f03e --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/exception_spec.c @@ -0,0 +1,72 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_EXC_NEW +VALUE exception_spec_rb_exc_new(VALUE self, VALUE str) { + char *cstr = StringValuePtr(str); + return rb_exc_new(rb_eException, cstr, strlen(cstr)); +} +#endif + +#ifdef HAVE_RB_EXC_NEW2 +VALUE exception_spec_rb_exc_new2(VALUE self, VALUE str) { + char *cstr = StringValuePtr(str); + return rb_exc_new2(rb_eException, cstr); +} +#endif + +#ifdef HAVE_RB_EXC_NEW3 +VALUE exception_spec_rb_exc_new3(VALUE self, VALUE str) { + return rb_exc_new3(rb_eException, str); +} +#endif + +#ifdef HAVE_RB_EXC_RAISE +VALUE exception_spec_rb_exc_raise(VALUE self, VALUE exc) { + if (self != Qundef) rb_exc_raise(exc); + return Qnil; +} +#endif + +#ifdef HAVE_RB_SET_ERRINFO +VALUE exception_spec_rb_set_errinfo(VALUE self, VALUE exc) { + rb_set_errinfo(exc); + return Qnil; +} +#endif + +void Init_exception_spec(void) { + VALUE cls; + cls = rb_define_class("CApiExceptionSpecs", rb_cObject); + +#ifdef HAVE_RB_EXC_NEW + rb_define_method(cls, "rb_exc_new", exception_spec_rb_exc_new, 1); +#endif + +#ifdef HAVE_RB_EXC_NEW2 + rb_define_method(cls, "rb_exc_new2", exception_spec_rb_exc_new2, 1); +#endif + +#ifdef HAVE_RB_EXC_NEW3 + rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1); +#endif + +#ifdef HAVE_RB_EXC_RAISE + rb_define_method(cls, "rb_exc_raise", exception_spec_rb_exc_raise, 1); +#endif + +#ifdef HAVE_RB_SET_ERRINFO + rb_define_method(cls, "rb_set_errinfo", exception_spec_rb_set_errinfo, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/file_spec.c b/spec/rubyspec/optional/capi/ext/file_spec.c new file mode 100644 index 0000000000..98f8db1595 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/file_spec.c @@ -0,0 +1,44 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_FILE_OPEN +VALUE file_spec_rb_file_open(VALUE self, VALUE name, VALUE mode) { + return rb_file_open(RSTRING_PTR(name), RSTRING_PTR(mode)); +} +#endif + +#ifdef HAVE_RB_FILE_OPEN_STR +VALUE file_spec_rb_file_open_str(VALUE self, VALUE name, VALUE mode) { + return rb_file_open_str(name, RSTRING_PTR(mode)); +} +#endif + +#ifdef HAVE_FILEPATHVALUE +VALUE file_spec_FilePathValue(VALUE self, VALUE obj) { + return FilePathValue(obj); +} +#endif + +void Init_file_spec(void) { + VALUE cls = rb_define_class("CApiFileSpecs", rb_cObject); + +#ifdef HAVE_RB_FILE_OPEN + rb_define_method(cls, "rb_file_open", file_spec_rb_file_open, 2); +#endif + +#ifdef HAVE_RB_FILE_OPEN_STR + rb_define_method(cls, "rb_file_open_str", file_spec_rb_file_open_str, 2); +#endif + +#ifdef HAVE_FILEPATHVALUE + rb_define_method(cls, "FilePathValue", file_spec_FilePathValue, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/fixnum_spec.c b/spec/rubyspec/optional/capi/ext/fixnum_spec.c new file mode 100644 index 0000000000..4c0572186d --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/fixnum_spec.c @@ -0,0 +1,36 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_FIX2UINT +static VALUE fixnum_spec_rb_fix2uint(VALUE self, VALUE value) { + return INT2FIX(rb_fix2uint(value)); +} +#endif + +#ifdef HAVE_RB_FIX2INT +static VALUE fixnum_spec_rb_fix2int(VALUE self, VALUE value) { + return INT2FIX(rb_fix2int(value)); +} +#endif + + +void Init_fixnum_spec(void) { + VALUE cls; + cls = rb_define_class("CApiFixnumSpecs", rb_cObject); + +#ifdef HAVE_RB_FIX2UINT + rb_define_method(cls, "rb_fix2uint", fixnum_spec_rb_fix2uint, 1); +#endif + +#ifdef HAVE_RB_FIX2INT + rb_define_method(cls, "rb_fix2int", fixnum_spec_rb_fix2int, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/float_spec.c b/spec/rubyspec/optional/capi/ext/float_spec.c new file mode 100644 index 0000000000..15c74e62c9 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/float_spec.c @@ -0,0 +1,54 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_FLOAT_NEW +static VALUE float_spec_new_zero(VALUE self) { + double flt = 0; + return rb_float_new(flt); +} + +static VALUE float_spec_new_point_five(VALUE self) { + double flt = 0.555; + return rb_float_new(flt); +} +#endif + +#ifdef HAVE_RB_RFLOAT +static VALUE float_spec_rb_Float(VALUE self, VALUE float_str) { + return rb_Float(float_str); +} +#endif + +#ifdef HAVE_RFLOAT_VALUE +static VALUE float_spec_RFLOAT_VALUE(VALUE self, VALUE float_h) { + return rb_float_new(RFLOAT_VALUE(float_h)); +} +#endif + +void Init_float_spec(void) { + VALUE cls; + cls = rb_define_class("CApiFloatSpecs", rb_cObject); + +#ifdef HAVE_RB_FLOAT_NEW + rb_define_method(cls, "new_zero", float_spec_new_zero, 0); + rb_define_method(cls, "new_point_five", float_spec_new_point_five, 0); +#endif + +#ifdef HAVE_RB_RFLOAT + rb_define_method(cls, "rb_Float", float_spec_rb_Float, 1); +#endif + +#ifdef HAVE_RFLOAT_VALUE + rb_define_method(cls, "RFLOAT_VALUE", float_spec_RFLOAT_VALUE, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/gc_spec.c b/spec/rubyspec/optional/capi/ext/gc_spec.c new file mode 100644 index 0000000000..c5895eb0aa --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/gc_spec.c @@ -0,0 +1,61 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_GC_REGISTER_ADDRESS +VALUE registered_tagged_value; +VALUE registered_reference_value; + +static VALUE registered_tagged_address(VALUE self) { + return registered_tagged_value; +} + +static VALUE registered_reference_address(VALUE self) { + return registered_reference_value; +} +#endif + +#ifdef HAVE_RB_GC_ENABLE +static VALUE gc_spec_rb_gc_enable() { + return rb_gc_enable(); +} +#endif + +#ifdef HAVE_RB_GC_DISABLE +static VALUE gc_spec_rb_gc_disable() { + return rb_gc_disable(); +} +#endif + + +void Init_gc_spec(void) { + VALUE cls; + cls = rb_define_class("CApiGCSpecs", rb_cObject); + +#ifdef HAVE_RB_GC_REGISTER_ADDRESS + registered_tagged_value = INT2NUM(10); + registered_reference_value = rb_str_new2("Globally registered data"); + + rb_gc_register_address(®istered_tagged_value); + rb_gc_register_address(®istered_reference_value); + + rb_define_method(cls, "registered_tagged_address", registered_tagged_address, 0); + rb_define_method(cls, "registered_reference_address", registered_reference_address, 0); +#endif + +#ifdef HAVE_RB_GC_ENABLE + rb_define_method(cls, "rb_gc_enable", gc_spec_rb_gc_enable, 0); +#endif + +#ifdef HAVE_RB_GC_DISABLE + rb_define_method(cls, "rb_gc_disable", gc_spec_rb_gc_disable, 0); +#endif + +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/globals_spec.c b/spec/rubyspec/optional/capi/ext/globals_spec.c new file mode 100644 index 0000000000..0c28a7f8ab --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/globals_spec.c @@ -0,0 +1,199 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_DEFINE_HOOKED_VARIABLE +VALUE g_hooked_var; + +void var_2x_setter(VALUE val, ID id, VALUE *var) { + *var = INT2NUM(NUM2INT(val) * 2); +} + +static VALUE sb_define_hooked_variable(VALUE self, VALUE var_name) { + rb_define_hooked_variable(StringValuePtr(var_name), &g_hooked_var, 0, var_2x_setter); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_READONLY_VARIABLE +VALUE g_ro_var; + +static VALUE sb_define_readonly_variable(VALUE self, VALUE var_name, VALUE val) { + g_ro_var = val; + rb_define_readonly_variable(StringValuePtr(var_name), &g_ro_var); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_VARIABLE +VALUE g_var; + +static VALUE sb_get_global_value(VALUE self) { + return g_var; +} + +static VALUE sb_define_variable(VALUE self, VALUE var_name, VALUE val) { + g_var = val; + rb_define_variable(StringValuePtr(var_name), &g_var); + return Qnil; +} +#endif + +#ifdef HAVE_RB_F_GLOBAL_VARIABLES +static VALUE sb_f_global_variables(VALUE self) { + return rb_f_global_variables(); +} +#endif + +#ifdef HAVE_RB_GV_GET +static VALUE sb_gv_get(VALUE self, VALUE var) { + return rb_gv_get(StringValuePtr(var)); +} +#endif + +#ifdef HAVE_RB_GV_SET +static VALUE sb_gv_set(VALUE self, VALUE var, VALUE val) { + return rb_gv_set(StringValuePtr(var), val); +} +#endif + +#ifdef HAVE_RB_STDIN +static VALUE global_spec_rb_stdin(VALUE self) { + return rb_stdin; +} +#endif + +#ifdef HAVE_RB_STDOUT +static VALUE global_spec_rb_stdout(VALUE self) { + return rb_stdout; +} +#endif + +#ifdef HAVE_RB_STDERR +static VALUE global_spec_rb_stderr(VALUE self) { + return rb_stderr; +} +#endif + +#ifdef HAVE_RB_DEFOUT +static VALUE global_spec_rb_defout(VALUE self) { + return rb_defout; +} +#endif + +#ifdef HAVE_RB_RS +static VALUE global_spec_rb_rs(VALUE self) { + return rb_rs; +} +#endif + +#ifdef HAVE_RB_DEFAULT_RS +static VALUE global_spec_rb_default_rs(VALUE self) { + return rb_default_rs; +} +#endif + +#ifdef HAVE_RB_OUTPUT_RS +static VALUE global_spec_rb_output_rs(VALUE self) { + return rb_output_rs; +} +#endif + +#ifdef HAVE_RB_OUTPUT_FS +static VALUE global_spec_rb_output_fs(VALUE self) { + return rb_output_fs; +} +#endif + +#ifdef HAVE_RB_LASTLINE_SET +static VALUE global_spec_rb_lastline_set(VALUE self, VALUE line) { + rb_lastline_set(line); + return Qnil; +} +#endif + +#ifdef HAVE_RB_LASTLINE_GET +static VALUE global_spec_rb_lastline_get(VALUE self) { + return rb_lastline_get(); +} +#endif + +void Init_globals_spec(void) { + VALUE cls; + cls = rb_define_class("CApiGlobalSpecs", rb_cObject); + +#ifdef HAVE_RB_DEFINE_HOOKED_VARIABLE + g_hooked_var = Qnil; + rb_define_method(cls, "rb_define_hooked_variable_2x", sb_define_hooked_variable, 1); +#endif + +#ifdef HAVE_RB_DEFINE_READONLY_VARIABLE + g_ro_var = Qnil; + rb_define_method(cls, "rb_define_readonly_variable", sb_define_readonly_variable, 2); +#endif + +#ifdef HAVE_RB_DEFINE_VARIABLE + g_var = Qnil; + rb_define_method(cls, "rb_define_variable", sb_define_variable, 2); + rb_define_method(cls, "sb_get_global_value", sb_get_global_value, 0); +#endif + +#ifdef HAVE_RB_F_GLOBAL_VARIABLES + rb_define_method(cls, "rb_f_global_variables", sb_f_global_variables, 0); +#endif + +#ifdef HAVE_RB_GV_GET + rb_define_method(cls, "sb_gv_get", sb_gv_get, 1); +#endif + +#ifdef HAVE_RB_GV_SET + rb_define_method(cls, "sb_gv_set", sb_gv_set, 2); +#endif + +#ifdef HAVE_RB_STDIN + rb_define_method(cls, "rb_stdin", global_spec_rb_stdin, 0); +#endif + +#ifdef HAVE_RB_STDOUT + rb_define_method(cls, "rb_stdout", global_spec_rb_stdout, 0); +#endif + +#ifdef HAVE_RB_STDERR + rb_define_method(cls, "rb_stderr", global_spec_rb_stderr, 0); +#endif + +#ifdef HAVE_RB_DEFOUT + rb_define_method(cls, "rb_defout", global_spec_rb_defout, 0); +#endif + +#ifdef HAVE_RB_RS + rb_define_method(cls, "rb_rs", global_spec_rb_rs, 0); +#endif + +#ifdef HAVE_RB_DEFAULT_RS + rb_define_method(cls, "rb_default_rs", global_spec_rb_default_rs, 0); +#endif + +#ifdef HAVE_RB_OUTPUT_RS + rb_define_method(cls, "rb_output_rs", global_spec_rb_output_rs, 0); +#endif + +#ifdef HAVE_RB_OUTPUT_FS + rb_define_method(cls, "rb_output_fs", global_spec_rb_output_fs, 0); +#endif + +#ifdef HAVE_RB_LASTLINE_SET + rb_define_method(cls, "rb_lastline_set", global_spec_rb_lastline_set, 1); +#endif + +#ifdef HAVE_RB_LASTLINE_GET + rb_define_method(cls, "rb_lastline_get", global_spec_rb_lastline_get, 0); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/hash_spec.c b/spec/rubyspec/optional/capi/ext/hash_spec.c new file mode 100644 index 0000000000..ac45003844 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/hash_spec.c @@ -0,0 +1,208 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_HASH +VALUE hash_spec_rb_hash(VALUE self, VALUE hash) { + return rb_hash(hash); +} +#endif + +#ifdef HAVE_RB_HASH2 +VALUE hash_spec_rb_Hash(VALUE self, VALUE val) { + return rb_Hash(val); +} +#endif + +#ifdef HAVE_RB_HASH_DUP +VALUE hash_spec_rb_hash_dup(VALUE self, VALUE hash) { + return rb_hash_dup(hash); +} +#endif + +#ifdef HAVE_RB_HASH_FREEZE +VALUE hash_spec_rb_hash_freeze(VALUE self, VALUE hash) { + return rb_hash_freeze(hash); +} +#endif + +#ifdef HAVE_RB_HASH_AREF +VALUE hash_spec_rb_hash_aref(VALUE self, VALUE hash, VALUE key) { + return rb_hash_aref(hash, key); +} + +VALUE hash_spec_rb_hash_aref_nil(VALUE self, VALUE hash, VALUE key) { + VALUE ret = rb_hash_aref(hash, key); + return NIL_P(ret) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_HASH_ASET +VALUE hash_spec_rb_hash_aset(VALUE self, VALUE hash, VALUE key, VALUE val) { + return rb_hash_aset(hash, key, val); +} +#endif + +#ifdef HAVE_RB_HASH_CLEAR +VALUE hash_spec_rb_hash_clear(VALUE self, VALUE hash) { + return rb_hash_clear(hash); +} +#endif + +#ifdef HAVE_RB_HASH_DELETE +VALUE hash_spec_rb_hash_delete(VALUE self, VALUE hash, VALUE key) { + return rb_hash_delete(hash, key); +} +#endif + +#ifdef HAVE_RB_HASH_DELETE_IF +VALUE hash_spec_rb_hash_delete_if(VALUE self, VALUE hash) { + return rb_hash_delete_if(hash); +} +#endif + +#ifdef HAVE_RB_HASH_FOREACH +static int foreach_i(VALUE key, VALUE val, VALUE other) { + rb_hash_aset(other, key, val); + return 0; /* ST_CONTINUE; */ +} + +static int foreach_stop_i(VALUE key, VALUE val, VALUE other) { + rb_hash_aset(other, key, val); + return 1; /* ST_STOP; */ +} + +static int foreach_delete_i(VALUE key, VALUE val, VALUE other) { + rb_hash_aset(other, key, val); + return 2; /* ST_DELETE; */ +} + +VALUE hash_spec_rb_hash_foreach(VALUE self, VALUE hsh) { + VALUE other = rb_hash_new(); + rb_hash_foreach(hsh, foreach_i, other); + return other; +} + +VALUE hash_spec_rb_hash_foreach_stop(VALUE self, VALUE hsh) { + VALUE other = rb_hash_new(); + rb_hash_foreach(hsh, foreach_stop_i, other); + return other; +} + +VALUE hash_spec_rb_hash_foreach_delete(VALUE self, VALUE hsh) { + VALUE other = rb_hash_new(); + rb_hash_foreach(hsh, foreach_delete_i, other); + return other; +} +#endif + +#ifdef HAVE_RB_HASH_LOOKUP +VALUE hash_spec_rb_hash_lookup(VALUE self, VALUE hash, VALUE key) { + return rb_hash_lookup(hash, key); +} + +VALUE hash_spec_rb_hash_lookup_nil(VALUE self, VALUE hash, VALUE key) { + VALUE ret = rb_hash_lookup(hash, key); + return ret == Qnil ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_HASH_LOOKUP2 +VALUE hash_spec_rb_hash_lookup2(VALUE self, VALUE hash, VALUE key, VALUE def) { + return rb_hash_lookup2(hash, key, def); +} +#endif + +#ifdef HAVE_RB_HASH_NEW +VALUE hash_spec_rb_hash_new(VALUE self) { + return rb_hash_new(); +} +#endif + +#ifdef HAVE_RB_HASH_SIZE +VALUE hash_spec_rb_hash_size(VALUE self, VALUE hash) { + return rb_hash_size(hash); +} +#endif + +#ifdef HAVE_RB_HASH_SET_IFNONE +VALUE hash_spec_rb_hash_set_ifnone(VALUE self, VALUE hash, VALUE def) { + return rb_hash_set_ifnone(hash, def); +} +#endif + +void Init_hash_spec(void) { + VALUE cls; + cls = rb_define_class("CApiHashSpecs", rb_cObject); + +#ifdef HAVE_RB_HASH + rb_define_method(cls, "rb_hash", hash_spec_rb_hash, 1); +#endif + +#ifdef HAVE_RB_HASH2 + rb_define_method(cls, "rb_Hash", hash_spec_rb_Hash, 1); +#endif + +#ifdef HAVE_RB_HASH_DUP + rb_define_method(cls, "rb_hash_dup", hash_spec_rb_hash_dup, 1); +#endif + +#ifdef HAVE_RB_HASH_FREEZE + rb_define_method(cls, "rb_hash_freeze", hash_spec_rb_hash_freeze, 1); +#endif + +#ifdef HAVE_RB_HASH_AREF + rb_define_method(cls, "rb_hash_aref", hash_spec_rb_hash_aref, 2); + rb_define_method(cls, "rb_hash_aref_nil", hash_spec_rb_hash_aref_nil, 2); +#endif + +#ifdef HAVE_RB_HASH_ASET + rb_define_method(cls, "rb_hash_aset", hash_spec_rb_hash_aset, 3); +#endif + +#ifdef HAVE_RB_HASH_CLEAR + rb_define_method(cls, "rb_hash_clear", hash_spec_rb_hash_clear, 1); +#endif + +#ifdef HAVE_RB_HASH_DELETE + rb_define_method(cls, "rb_hash_delete", hash_spec_rb_hash_delete, 2); +#endif + +#ifdef HAVE_RB_HASH_DELETE_IF + rb_define_method(cls, "rb_hash_delete_if", hash_spec_rb_hash_delete_if, 1); +#endif + +#ifdef HAVE_RB_HASH_FOREACH + rb_define_method(cls, "rb_hash_foreach", hash_spec_rb_hash_foreach, 1); + rb_define_method(cls, "rb_hash_foreach_stop", hash_spec_rb_hash_foreach_stop, 1); + rb_define_method(cls, "rb_hash_foreach_delete", hash_spec_rb_hash_foreach_delete, 1); +#endif + +#ifdef HAVE_RB_HASH_LOOKUP + rb_define_method(cls, "rb_hash_lookup_nil", hash_spec_rb_hash_lookup_nil, 2); + rb_define_method(cls, "rb_hash_lookup", hash_spec_rb_hash_lookup, 2); +#endif + +#ifdef HAVE_RB_HASH_LOOKUP2 + rb_define_method(cls, "rb_hash_lookup2", hash_spec_rb_hash_lookup2, 3); +#endif + +#ifdef HAVE_RB_HASH_NEW + rb_define_method(cls, "rb_hash_new", hash_spec_rb_hash_new, 0); +#endif + +#ifdef HAVE_RB_HASH_SIZE + rb_define_method(cls, "rb_hash_size", hash_spec_rb_hash_size, 1); +#endif + +#ifdef HAVE_RB_HASH_SET_IFNONE + rb_define_method(cls, "rb_hash_set_ifnone", hash_spec_rb_hash_set_ifnone, 2); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/integer_spec.c b/spec/rubyspec/optional/capi/ext/integer_spec.c new file mode 100644 index 0000000000..821d8373c9 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/integer_spec.c @@ -0,0 +1,40 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_INTEGER_PACK +static VALUE integer_spec_rb_integer_pack(VALUE self, VALUE value, + VALUE words, VALUE numwords, VALUE wordsize, VALUE nails, VALUE flags) +{ + int result = rb_integer_pack(value, (void*)RSTRING_PTR(words), FIX2INT(numwords), + FIX2INT(wordsize), FIX2INT(nails), FIX2INT(flags)); + return INT2FIX(result); +} +#endif + +void Init_integer_spec(void) { +#ifdef HAVE_RB_INTEGER_PACK + VALUE cls; + cls = rb_define_class("CApiIntegerSpecs", rb_cObject); + + rb_define_const(cls, "MSWORD", INT2NUM(INTEGER_PACK_MSWORD_FIRST)); + rb_define_const(cls, "LSWORD", INT2NUM(INTEGER_PACK_LSWORD_FIRST)); + rb_define_const(cls, "MSBYTE", INT2NUM(INTEGER_PACK_MSBYTE_FIRST)); + rb_define_const(cls, "LSBYTE", INT2NUM(INTEGER_PACK_LSBYTE_FIRST)); + rb_define_const(cls, "NATIVE", INT2NUM(INTEGER_PACK_NATIVE_BYTE_ORDER)); + rb_define_const(cls, "PACK_2COMP", INT2NUM(INTEGER_PACK_2COMP)); + rb_define_const(cls, "LITTLE_ENDIAN", INT2NUM(INTEGER_PACK_LITTLE_ENDIAN)); + rb_define_const(cls, "BIG_ENDIAN", INT2NUM(INTEGER_PACK_BIG_ENDIAN)); + rb_define_const(cls, "FORCE_BIGNUM", INT2NUM(INTEGER_PACK_FORCE_BIGNUM)); + rb_define_const(cls, "NEGATIVE", INT2NUM(INTEGER_PACK_NEGATIVE)); + + rb_define_method(cls, "rb_integer_pack", integer_spec_rb_integer_pack, 6); +#endif +} + +#ifdef __cplusplus +extern "C" { +#endif diff --git a/spec/rubyspec/optional/capi/ext/io_spec.c b/spec/rubyspec/optional/capi/ext/io_spec.c new file mode 100644 index 0000000000..982284deab --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/io_spec.c @@ -0,0 +1,296 @@ +#include "ruby.h" +#include "rubyspec.h" +#include "ruby/io.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static int set_non_blocking(int fd) { + int flags; +#if defined(O_NONBLOCK) + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +#else + flags = 1; + return ioctl(fd, FIOBIO, &flags); +#endif +} + +#ifdef HAVE_GET_OPEN_FILE +static int io_spec_get_fd(VALUE io) { + rb_io_t* fp; + GetOpenFile(io, fp); + return fp->fd; +} + +VALUE io_spec_GetOpenFile_fd(VALUE self, VALUE io) { + return INT2NUM(io_spec_get_fd(io)); +} +#endif + +#ifdef HAVE_RB_IO_ADDSTR +VALUE io_spec_rb_io_addstr(VALUE self, VALUE io, VALUE str) { + return rb_io_addstr(io, str); +} +#endif + +#ifdef HAVE_RB_IO_PRINTF +VALUE io_spec_rb_io_printf(VALUE self, VALUE io, VALUE ary) { + long argc = RARRAY_LEN(ary); + VALUE *argv = alloca(sizeof(VALUE) * argc); + int i; + + for (i = 0; i < argc; i++) { + argv[i] = rb_ary_entry(ary, i); + } + + return rb_io_printf((int)argc, argv, io); +} +#endif + +#ifdef HAVE_RB_IO_PRINT +VALUE io_spec_rb_io_print(VALUE self, VALUE io, VALUE ary) { + long argc = RARRAY_LEN(ary); + VALUE *argv = alloca(sizeof(VALUE) * argc); + int i; + + for (i = 0; i < argc; i++) { + argv[i] = rb_ary_entry(ary, i); + } + + return rb_io_print((int)argc, argv, io); +} +#endif + +#ifdef HAVE_RB_IO_PUTS +VALUE io_spec_rb_io_puts(VALUE self, VALUE io, VALUE ary) { + long argc = RARRAY_LEN(ary); + VALUE *argv = alloca(sizeof(VALUE) * argc); + int i; + + for (i = 0; i < argc; i++) { + argv[i] = rb_ary_entry(ary, i); + } + + return rb_io_puts((int)argc, argv, io); +} +#endif + +#ifdef HAVE_RB_IO_WRITE +VALUE io_spec_rb_io_write(VALUE self, VALUE io, VALUE str) { + return rb_io_write(io, str); +} +#endif + +#ifdef HAVE_RB_IO_CHECK_IO +VALUE io_spec_rb_io_check_io(VALUE self, VALUE io) { + return rb_io_check_io(io); +} +#endif + +#ifdef HAVE_RB_IO_CHECK_READABLE +VALUE io_spec_rb_io_check_readable(VALUE self, VALUE io) { + rb_io_t* fp; + GetOpenFile(io, fp); + rb_io_check_readable(fp); + return Qnil; +} +#endif + +#ifdef HAVE_RB_IO_CHECK_WRITABLE +VALUE io_spec_rb_io_check_writable(VALUE self, VALUE io) { + rb_io_t* fp; + GetOpenFile(io, fp); + rb_io_check_writable(fp); + return Qnil; +} +#endif + +#ifdef HAVE_RB_IO_CHECK_CLOSED +VALUE io_spec_rb_io_check_closed(VALUE self, VALUE io) { + rb_io_t* fp; + GetOpenFile(io, fp); + rb_io_check_closed(fp); + return Qnil; +} +#endif + +#ifdef HAVE_RB_IO_TAINT_CHECK +VALUE io_spec_rb_io_taint_check(VALUE self, VALUE io) { + /*rb_io_t* fp; + GetOpenFile(io, fp);*/ + rb_io_taint_check(io); + return io; +} +#endif + +typedef int wait_bool; +#define wait_bool_to_ruby_bool(x) (x ? Qtrue : Qfalse) + +#ifdef HAVE_RB_IO_WAIT_READABLE +#define RB_IO_WAIT_READABLE_BUF 13 + +VALUE io_spec_rb_io_wait_readable(VALUE self, VALUE io, VALUE read_p) { + int fd = io_spec_get_fd(io); + char buf[RB_IO_WAIT_READABLE_BUF]; + wait_bool ret; + + set_non_blocking(fd); + + if(RTEST(read_p)) { + rb_ivar_set(self, rb_intern("@write_data"), Qtrue); + if(read(fd, buf, RB_IO_WAIT_READABLE_BUF) != -1) { + return Qnil; + } + } + + ret = rb_io_wait_readable(fd); + + if(RTEST(read_p)) { + if(read(fd, buf, RB_IO_WAIT_READABLE_BUF) != 13) { + return Qnil; + } + rb_ivar_set(self, rb_intern("@read_data"), + rb_str_new(buf, RB_IO_WAIT_READABLE_BUF)); + } + + return wait_bool_to_ruby_bool(ret); +} +#endif + +#ifdef HAVE_RB_IO_WAIT_WRITABLE +VALUE io_spec_rb_io_wait_writable(VALUE self, VALUE io) { + wait_bool ret; + ret = rb_io_wait_writable(io_spec_get_fd(io)); + return wait_bool_to_ruby_bool(ret); +} +#endif + +#ifdef HAVE_RB_THREAD_WAIT_FD +VALUE io_spec_rb_thread_wait_fd(VALUE self, VALUE io) { + rb_thread_wait_fd(io_spec_get_fd(io)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_THREAD_FD_WRITABLE +VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) { + rb_thread_fd_writable(io_spec_get_fd(io)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_IO_BINMODE +VALUE io_spec_rb_io_binmode(VALUE self, VALUE io) { + return rb_io_binmode(io); +} +#endif + +#ifdef HAVE_RB_FD_FIX_CLOEXEC +VALUE io_spec_rb_fd_fix_cloexec(VALUE self, VALUE io) { + rb_fd_fix_cloexec(io_spec_get_fd(io)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_CLOEXEC_OPEN +VALUE io_spec_rb_cloexec_open(VALUE self, VALUE path, VALUE flags, VALUE mode) { + const char *pathname = StringValuePtr(path); + int fd = rb_cloexec_open(pathname, FIX2INT(flags), FIX2INT(mode)); + return rb_funcall(rb_cIO, rb_intern("for_fd"), 1, INT2FIX(fd)); +} +#endif + +#ifdef HAVE_RB_IO_CLOSE +VALUE io_spec_rb_io_close(VALUE self, VALUE io) { + return rb_io_close(io); +} +#endif + +void Init_io_spec(void) { + VALUE cls = rb_define_class("CApiIOSpecs", rb_cObject); + +#ifdef HAVE_GET_OPEN_FILE + rb_define_method(cls, "GetOpenFile_fd", io_spec_GetOpenFile_fd, 1); +#endif + +#ifdef HAVE_RB_IO_ADDSTR + rb_define_method(cls, "rb_io_addstr", io_spec_rb_io_addstr, 2); +#endif + +#ifdef HAVE_RB_IO_PRINTF + rb_define_method(cls, "rb_io_printf", io_spec_rb_io_printf, 2); +#endif + +#ifdef HAVE_RB_IO_PRINT + rb_define_method(cls, "rb_io_print", io_spec_rb_io_print, 2); +#endif + +#ifdef HAVE_RB_IO_PUTS + rb_define_method(cls, "rb_io_puts", io_spec_rb_io_puts, 2); +#endif + +#ifdef HAVE_RB_IO_WRITE + rb_define_method(cls, "rb_io_write", io_spec_rb_io_write, 2); +#endif + +#ifdef HAVE_RB_IO_CLOSE + rb_define_method(cls, "rb_io_close", io_spec_rb_io_close, 1); +#endif + +#ifdef HAVE_RB_IO_CHECK_IO + rb_define_method(cls, "rb_io_check_io", io_spec_rb_io_check_io, 1); +#endif + +#ifdef HAVE_RB_IO_CHECK_READABLE + rb_define_method(cls, "rb_io_check_readable", io_spec_rb_io_check_readable, 1); +#endif + +#ifdef HAVE_RB_IO_CHECK_WRITABLE + rb_define_method(cls, "rb_io_check_writable", io_spec_rb_io_check_writable, 1); +#endif + +#ifdef HAVE_RB_IO_CHECK_CLOSED + rb_define_method(cls, "rb_io_check_closed", io_spec_rb_io_check_closed, 1); +#endif + +#ifdef HAVE_RB_IO_TAINT_CHECK + rb_define_method(cls, "rb_io_taint_check", io_spec_rb_io_taint_check, 1); +#endif + +#ifdef HAVE_RB_IO_WAIT_READABLE + rb_define_method(cls, "rb_io_wait_readable", io_spec_rb_io_wait_readable, 2); +#endif + +#ifdef HAVE_RB_IO_WAIT_WRITABLE + rb_define_method(cls, "rb_io_wait_writable", io_spec_rb_io_wait_writable, 1); +#endif + +#ifdef HAVE_RB_THREAD_WAIT_FD + rb_define_method(cls, "rb_thread_wait_fd", io_spec_rb_thread_wait_fd, 1); +#endif + +#ifdef HAVE_RB_THREAD_FD_WRITABLE + rb_define_method(cls, "rb_thread_fd_writable", io_spec_rb_thread_fd_writable, 1); +#endif + +#ifdef HAVE_RB_IO_BINMODE + rb_define_method(cls, "rb_io_binmode", io_spec_rb_io_binmode, 1); +#endif + +#ifdef HAVE_RB_FD_FIX_CLOEXEC + rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1); +#endif + +#ifdef HAVE_RB_CLOEXEC_OPEN + rb_define_method(cls, "rb_cloexec_open", io_spec_rb_cloexec_open, 3); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/jruby.h b/spec/rubyspec/optional/capi/ext/jruby.h new file mode 100644 index 0000000000..00a9789f14 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/jruby.h @@ -0,0 +1,10 @@ +#ifndef RUBYSPEC_CAPI_JRUBY_H +#define RUBYSPEC_CAPI_JRUBY_H + +/* #undef any HAVE_ defines that JRuby does not have. */ +#undef HAVE_RB_DEFINE_HOOKED_VARIABLE +#undef HAVE_RB_DEFINE_VARIABLE + +#undef HAVE_RB_EXEC_RECURSIVE + +#endif diff --git a/spec/rubyspec/optional/capi/ext/kernel_spec.c b/spec/rubyspec/optional/capi/ext/kernel_spec.c new file mode 100644 index 0000000000..d855b5689f --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/kernel_spec.c @@ -0,0 +1,404 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +VALUE kernel_spec_call_proc(VALUE arg_array) { + VALUE arg = rb_ary_pop(arg_array); + VALUE proc = rb_ary_pop(arg_array); + return rb_funcall(proc, rb_intern("call"), 1, arg); +} + +#ifdef HAVE_RB_BLOCK_GIVEN_P +static VALUE kernel_spec_rb_block_given_p(VALUE self) { + return rb_block_given_p() ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_NEED_BLOCK +VALUE kernel_spec_rb_need_block(VALUE self) { + rb_need_block(); + return Qnil; +} +#endif + +#ifdef HAVE_RB_BLOCK_PROC +VALUE kernel_spec_rb_block_proc(VALUE self) { + return rb_block_proc(); +} +#endif + +#ifdef HAVE_RB_BLOCK_CALL + +VALUE block_call_inject(VALUE yield_value, VALUE data2) { + /* yield_value yields the first block argument */ + VALUE elem = yield_value; + VALUE elem_incr = INT2FIX(FIX2INT(elem) + 1); + return elem_incr; +} + +VALUE kernel_spec_rb_block_call(VALUE self, VALUE ary) { + return rb_block_call(ary, rb_intern("map"), 0, NULL, block_call_inject, Qnil); +} + +VALUE block_call_inject_multi_arg(VALUE yield_value, VALUE data2, int argc, VALUE argv[]) { + /* yield_value yields the first block argument */ + VALUE sum = yield_value; + VALUE elem = argv[1]; + + return INT2FIX(FIX2INT(sum) + FIX2INT(elem)); +} + +VALUE kernel_spec_rb_block_call_multi_arg(VALUE self, VALUE ary) { + VALUE method_args[1]; + method_args[0] = INT2FIX(0); + return rb_block_call(ary, rb_intern("inject"), 1, method_args, block_call_inject_multi_arg, Qnil); +} + +VALUE kernel_spec_rb_block_call_no_func(VALUE self, VALUE ary) { + return rb_block_call(ary, rb_intern("map"), 0, NULL, NULL, Qnil); +} + +#endif + +#ifdef HAVE_RB_ENSURE +VALUE kernel_spec_rb_ensure(VALUE self, VALUE main_proc, VALUE arg, + VALUE ensure_proc, VALUE arg2) { + VALUE main_array, ensure_array; + + main_array = rb_ary_new(); + rb_ary_push(main_array, main_proc); + rb_ary_push(main_array, arg); + + ensure_array = rb_ary_new(); + rb_ary_push(ensure_array, ensure_proc); + rb_ary_push(ensure_array, arg2); + + return rb_ensure(kernel_spec_call_proc, main_array, + kernel_spec_call_proc, ensure_array); +} +#endif + +#ifdef HAVE_RB_CATCH +VALUE kernel_spec_call_proc_with_catch(VALUE arg, VALUE data) { + return rb_funcall(data, rb_intern("call"), 0); +} + +VALUE kernel_spec_rb_catch(VALUE self, VALUE sym, VALUE main_proc) { + return rb_catch(StringValuePtr(sym), kernel_spec_call_proc_with_catch, main_proc); +} +#endif + +#ifdef HAVE_RB_CATCH_OBJ +VALUE kernel_spec_call_proc_with_catch_obj(VALUE arg, VALUE data) { + return rb_funcall(data, rb_intern("call"), 0); +} + +VALUE kernel_spec_rb_catch_obj(VALUE self, VALUE obj, VALUE main_proc) { + return rb_catch_obj(obj, kernel_spec_call_proc_with_catch, main_proc); +} +#endif + +#ifdef HAVE_RB_EVAL_STRING +VALUE kernel_spec_rb_eval_string(VALUE self, VALUE str) { + return rb_eval_string(RSTRING_PTR(str)); +} +#endif + +#ifdef HAVE_RB_RAISE +VALUE kernel_spec_rb_raise(VALUE self, VALUE hash) { + rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("before"))); + if (self != Qundef) + rb_raise(rb_eTypeError, "Wrong argument type %s (expected %s)", "Integer", "String"); + rb_hash_aset(hash, ID2SYM(rb_intern("stage")), ID2SYM(rb_intern("after"))); + return Qnil; +} +#endif + +#ifdef HAVE_RB_THROW +VALUE kernel_spec_rb_throw(VALUE self, VALUE result) { + if (self != Qundef) rb_throw("foo", result); + return ID2SYM(rb_intern("rb_throw_failed")); +} +#endif + +#ifdef HAVE_RB_THROW_OBJ +VALUE kernel_spec_rb_throw_obj(VALUE self, VALUE obj, VALUE result) { + if (self != Qundef) rb_throw_obj(obj, result); + return ID2SYM(rb_intern("rb_throw_failed")); +} +#endif + +#ifdef HAVE_RB_RESCUE +VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) { + VALUE argv[2]; + int argc; + + VALUE arg = rb_ary_pop(arg_array); + VALUE proc = rb_ary_pop(arg_array); + + argv[0] = arg; + argv[1] = raised_exc; + + argc = 2; + + return rb_funcall2(proc, rb_intern("call"), argc, argv); +} + +VALUE kernel_spec_rb_rescue(VALUE self, VALUE main_proc, VALUE arg, + VALUE raise_proc, VALUE arg2) { + VALUE main_array, raise_array; + + main_array = rb_ary_new(); + rb_ary_push(main_array, main_proc); + rb_ary_push(main_array, arg); + + raise_array = rb_ary_new(); + rb_ary_push(raise_array, raise_proc); + rb_ary_push(raise_array, arg2); + + return rb_rescue(kernel_spec_call_proc, main_array, + kernel_spec_call_proc_with_raised_exc, raise_array); +} +#endif + +#ifdef HAVE_RB_RESCUE2 +VALUE kernel_spec_rb_rescue2(int argc, VALUE *args, VALUE self) { + VALUE main_array, raise_array; + + main_array = rb_ary_new(); + rb_ary_push(main_array, args[0]); + rb_ary_push(main_array, args[1]); + + raise_array = rb_ary_new(); + rb_ary_push(raise_array, args[2]); + rb_ary_push(raise_array, args[3]); + + return rb_rescue2(kernel_spec_call_proc, main_array, + kernel_spec_call_proc, raise_array, args[4], args[5], (VALUE)0); +} +#endif + +#ifdef HAVE_RB_SYS_FAIL +VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) { + errno = 1; + if(msg == Qnil) { + rb_sys_fail(0); + } else if (self != Qundef) { + rb_sys_fail(StringValuePtr(msg)); + } + return Qnil; +} +#endif + +#ifdef HAVE_RB_SYSERR_FAIL +VALUE kernel_spec_rb_syserr_fail(VALUE self, VALUE err, VALUE msg) { + if(msg == Qnil) { + rb_syserr_fail(NUM2INT(err), NULL); + } else if (self != Qundef) { + rb_syserr_fail(NUM2INT(err), StringValuePtr(msg)); + } + return Qnil; +} +#endif + +#ifdef HAVE_RB_WARN +VALUE kernel_spec_rb_warn(VALUE self, VALUE msg) { + rb_warn("%s", StringValuePtr(msg)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_YIELD +static VALUE kernel_spec_rb_yield(VALUE self, VALUE obj) { + return rb_yield(obj); +} +#endif + +#ifdef HAVE_RB_YIELD_SPLAT +static VALUE kernel_spec_rb_yield_splat(VALUE self, VALUE ary) { + return rb_yield_splat(ary); +} +#endif + +#ifdef HAVE_RB_YIELD_VALUES +static VALUE kernel_spec_rb_yield_values(VALUE self, VALUE obj1, VALUE obj2) { + return rb_yield_values(2, obj1, obj2); +} +#endif + +#ifdef HAVE_RB_EXEC_RECURSIVE +static VALUE do_rec(VALUE obj, VALUE arg, int is_rec) { + if(is_rec) { + return obj; + } else if(arg == Qtrue) { + return rb_exec_recursive(do_rec, obj, Qnil); + } else { + return Qnil; + } +} + +static VALUE kernel_spec_rb_exec_recursive(VALUE self, VALUE obj) { + return rb_exec_recursive(do_rec, obj, Qtrue); +} +#endif + +#ifdef HAVE_RB_SET_END_PROC +static void write_io(VALUE io) { + rb_funcall(io, rb_intern("write"), 1, rb_str_new2("e")); +} + +static VALUE kernel_spec_rb_set_end_proc(VALUE self, VALUE io) { + rb_set_end_proc(write_io, io); + return Qnil; +} +#endif + +#ifdef HAVE_RB_F_SPRINTF +static VALUE kernel_spec_rb_f_sprintf(VALUE self, VALUE ary) { + return rb_f_sprintf((int)RARRAY_LEN(ary), RARRAY_PTR(ary)); +} +#endif + +#ifdef HAVE_RB_MAKE_BACKTRACE +static VALUE kernel_spec_rb_make_backtrace(VALUE self) { + return rb_make_backtrace(); +} +#endif + +#ifdef HAVE_RB_OBJ_METHOD +static VALUE kernel_spec_rb_obj_method(VALUE self, VALUE obj, VALUE method) { + return rb_obj_method(obj, method); +} +#endif + +#ifdef HAVE_RB_FUNCALL3 +static VALUE kernel_spec_rb_funcall3(VALUE self, VALUE obj, VALUE method) { + return rb_funcall3(obj, SYM2ID(method), 0, NULL); +} +#endif + +#ifdef HAVE_RB_FUNCALL_WITH_BLOCK +static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE block) { + return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block); +} +#endif + +void Init_kernel_spec(void) { + VALUE cls; + cls = rb_define_class("CApiKernelSpecs", rb_cObject); + +#ifdef HAVE_RB_BLOCK_GIVEN_P + rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0); +#endif + +#ifdef HAVE_RB_NEED_BLOCK + rb_define_method(cls, "rb_need_block", kernel_spec_rb_need_block, 0); +#endif + +#ifdef HAVE_RB_BLOCK_CALL + rb_define_method(cls, "rb_block_call", kernel_spec_rb_block_call, 1); + rb_define_method(cls, "rb_block_call_multi_arg", kernel_spec_rb_block_call_multi_arg, 1); + rb_define_method(cls, "rb_block_call_no_func", kernel_spec_rb_block_call_no_func, 1); +#endif + +#ifdef HAVE_RB_BLOCK_PROC + rb_define_method(cls, "rb_block_proc", kernel_spec_rb_block_proc, 0); +#endif + +#ifdef HAVE_RB_ENSURE + rb_define_method(cls, "rb_ensure", kernel_spec_rb_ensure, 4); +#endif + +#ifdef HAVE_RB_EVAL_STRING + rb_define_method(cls, "rb_eval_string", kernel_spec_rb_eval_string, 1); +#endif + +#ifdef HAVE_RB_RAISE + rb_define_method(cls, "rb_raise", kernel_spec_rb_raise, 1); +#endif + +#ifdef HAVE_RB_THROW + rb_define_method(cls, "rb_throw", kernel_spec_rb_throw, 1); +#endif + +#ifdef HAVE_RB_THROW_OBJ + rb_define_method(cls, "rb_throw_obj", kernel_spec_rb_throw_obj, 2); +#endif + +#ifdef HAVE_RB_RESCUE + rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4); +#endif + +#ifdef HAVE_RB_RESCUE2 + rb_define_method(cls, "rb_rescue2", kernel_spec_rb_rescue2, -1); +#endif + +#ifdef HAVE_RB_CATCH + rb_define_method(cls, "rb_catch", kernel_spec_rb_catch, 2); +#endif + +#ifdef HAVE_RB_CATCH_OBJ + rb_define_method(cls, "rb_catch_obj", kernel_spec_rb_catch_obj, 2); +#endif + +#ifdef HAVE_RB_SYS_FAIL + rb_define_method(cls, "rb_sys_fail", kernel_spec_rb_sys_fail, 1); +#endif + +#ifdef HAVE_RB_SYSERR_FAIL + rb_define_method(cls, "rb_syserr_fail", kernel_spec_rb_syserr_fail, 2); +#endif + +#ifdef HAVE_RB_WARN + rb_define_method(cls, "rb_warn", kernel_spec_rb_warn, 1); +#endif + +#ifdef HAVE_RB_YIELD + rb_define_method(cls, "rb_yield", kernel_spec_rb_yield, 1); +#endif + +#ifdef HAVE_RB_YIELD_VALUES + rb_define_method(cls, "rb_yield_values", kernel_spec_rb_yield_values, 2); +#endif + +#ifdef HAVE_RB_YIELD_SPLAT + rb_define_method(cls, "rb_yield_splat", kernel_spec_rb_yield_splat, 1); +#endif + +#ifdef HAVE_RB_EXEC_RECURSIVE + rb_define_method(cls, "rb_exec_recursive", kernel_spec_rb_exec_recursive, 1); +#endif + +#ifdef HAVE_RB_SET_END_PROC + rb_define_method(cls, "rb_set_end_proc", kernel_spec_rb_set_end_proc, 1); +#endif + +#ifdef HAVE_RB_F_SPRINTF + rb_define_method(cls, "rb_f_sprintf", kernel_spec_rb_f_sprintf, 1); +#endif + +#ifdef HAVE_RB_MAKE_BACKTRACE + rb_define_method(cls, "rb_make_backtrace", kernel_spec_rb_make_backtrace, 0); +#endif + +#ifdef HAVE_RB_OBJ_METHOD + rb_define_method(cls, "rb_obj_method", kernel_spec_rb_obj_method, 2); +#endif + +#ifdef HAVE_RB_FUNCALL3 + rb_define_method(cls, "rb_funcall3", kernel_spec_rb_funcall3, 2); +#endif + +#ifdef HAVE_RB_FUNCALL_WITH_BLOCK + rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/marshal_spec.c b/spec/rubyspec/optional/capi/ext/marshal_spec.c new file mode 100644 index 0000000000..dbb6e71abf --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/marshal_spec.c @@ -0,0 +1,36 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_MARSHAL_DUMP +VALUE marshal_spec_rb_marshal_dump(VALUE self, VALUE obj, VALUE port) { + return rb_marshal_dump(obj, port); +} +#endif + +#ifdef HAVE_RB_MARSHAL_LOAD +VALUE marshal_spec_rb_marshal_load(VALUE self, VALUE data) { + return rb_marshal_load(data); +} +#endif + +void Init_marshal_spec(void) { + VALUE cls; + cls = rb_define_class("CApiMarshalSpecs", rb_cObject); + +#ifdef HAVE_RB_MARSHAL_DUMP + rb_define_method(cls, "rb_marshal_dump", marshal_spec_rb_marshal_dump, 2); +#endif + +#ifdef HAVE_RB_MARSHAL_LOAD + rb_define_method(cls, "rb_marshal_load", marshal_spec_rb_marshal_load, 1); +#endif + +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/module_spec.c b/spec/rubyspec/optional/capi/ext/module_spec.c new file mode 100644 index 0000000000..275bc4da32 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/module_spec.c @@ -0,0 +1,252 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static VALUE module_specs_test_method(VALUE self) { + return ID2SYM(rb_intern("test_method")); +} + +#ifdef HAVE_RB_CONST_DEFINED +static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) { + return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_CONST_DEFINED_AT +static VALUE module_specs_const_defined_at(VALUE self, VALUE klass, VALUE id) { + return rb_const_defined_at(klass, SYM2ID(id)) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_CONST_GET +static VALUE module_specs_const_get(VALUE self, VALUE klass, VALUE val) { + return rb_const_get(klass, SYM2ID(val)); +} +#endif + +#ifdef HAVE_RB_CONST_GET_AT +static VALUE module_specs_const_get_at(VALUE self, VALUE klass, VALUE val) { + return rb_const_get_at(klass, SYM2ID(val)); +} +#endif + +#ifdef HAVE_RB_CONST_GET_FROM +static VALUE module_specs_const_get_from(VALUE self, VALUE klass, VALUE val) { + return rb_const_get_from(klass, SYM2ID(val)); +} +#endif + +#ifdef HAVE_RB_CONST_SET +static VALUE module_specs_const_set(VALUE self, VALUE klass, VALUE name, VALUE val) { + rb_const_set(klass, SYM2ID(name), val); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_ALIAS +static VALUE module_specs_rb_define_alias(VALUE self, VALUE obj, + VALUE new_name, VALUE old_name) { + + rb_define_alias(obj, RSTRING_PTR(new_name), RSTRING_PTR(old_name)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_ALIAS +static VALUE module_specs_rb_alias(VALUE self, VALUE obj, + VALUE new_name, VALUE old_name) { + + rb_alias(obj, SYM2ID(new_name), SYM2ID(old_name)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_MODULE +static VALUE module_specs_rb_define_module(VALUE self, VALUE name) { + return rb_define_module(RSTRING_PTR(name)); +} +#endif + +#ifdef HAVE_RB_DEFINE_MODULE_UNDER +static VALUE module_specs_rb_define_module_under(VALUE self, VALUE outer, VALUE name) { + return rb_define_module_under(outer, RSTRING_PTR(name)); +} +#endif + +#ifdef HAVE_RB_DEFINE_CONST +static VALUE module_specs_define_const(VALUE self, VALUE klass, VALUE str_name, VALUE val) { + rb_define_const(klass, RSTRING_PTR(str_name), val); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_GLOBAL_CONST +static VALUE module_specs_define_global_const(VALUE self, VALUE str_name, VALUE obj) { + rb_define_global_const(RSTRING_PTR(str_name), obj); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_GLOBAL_FUNCTION +static VALUE module_specs_rb_define_global_function(VALUE self, VALUE str_name) { + rb_define_global_function(RSTRING_PTR(str_name), module_specs_test_method, 0); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_METHOD +static VALUE module_specs_rb_define_method(VALUE self, VALUE cls, VALUE str_name) { + rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_MODULE_FUNCTION +static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) { + rb_define_module_function(cls, RSTRING_PTR(str_name), module_specs_test_method, 0); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_PRIVATE_METHOD +static VALUE module_specs_rb_define_private_method(VALUE self, VALUE cls, VALUE str_name) { + rb_define_private_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_PROTECTED_METHOD +static VALUE module_specs_rb_define_protected_method(VALUE self, VALUE cls, VALUE str_name) { + rb_define_protected_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0); + return Qnil; +} +#endif + +#ifdef HAVE_RB_DEFINE_SINGLETON_METHOD +static VALUE module_specs_rb_define_singleton_method(VALUE self, VALUE cls, VALUE str_name) { + rb_define_singleton_method(cls, RSTRING_PTR(str_name), module_specs_test_method, 0); + return Qnil; +} +#endif + +#ifdef HAVE_RB_UNDEF_METHOD +static VALUE module_specs_rb_undef_method(VALUE self, VALUE cls, VALUE str_name) { + rb_undef_method(cls, RSTRING_PTR(str_name)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_UNDEF +static VALUE module_specs_rb_undef(VALUE self, VALUE cls, VALUE symbol_name) { + rb_undef(cls, SYM2ID(symbol_name)); + return Qnil; +} +#endif + +#ifdef HAVE_RB_CLASS2NAME +static VALUE module_specs_rbclass2name(VALUE self, VALUE klass) { + return rb_str_new2(rb_class2name(klass)); +} +#endif + +void Init_module_spec(void) { + VALUE cls; + + cls = rb_define_class("CApiModuleSpecs", rb_cObject); + +#ifdef HAVE_RB_CONST_DEFINED + rb_define_method(cls, "rb_const_defined", module_specs_const_defined, 2); +#endif + +#ifdef HAVE_RB_CONST_DEFINED_AT + rb_define_method(cls, "rb_const_defined_at", module_specs_const_defined_at, 2); +#endif + +#ifdef HAVE_RB_CONST_GET + rb_define_method(cls, "rb_const_get", module_specs_const_get, 2); +#endif + +#ifdef HAVE_RB_CONST_GET_AT + rb_define_method(cls, "rb_const_get_at", module_specs_const_get_at, 2); +#endif + +#ifdef HAVE_RB_CONST_GET_FROM + rb_define_method(cls, "rb_const_get_from", module_specs_const_get_from, 2); +#endif + +#ifdef HAVE_RB_CONST_SET + rb_define_method(cls, "rb_const_set", module_specs_const_set, 3); +#endif + +#ifdef HAVE_RB_DEFINE_ALIAS + rb_define_method(cls, "rb_define_alias", module_specs_rb_define_alias, 3); +#endif + +#ifdef HAVE_RB_ALIAS + rb_define_method(cls, "rb_alias", module_specs_rb_alias, 3); +#endif + +#ifdef HAVE_RB_DEFINE_MODULE + rb_define_method(cls, "rb_define_module", module_specs_rb_define_module, 1); +#endif + +#ifdef HAVE_RB_DEFINE_MODULE_UNDER + rb_define_method(cls, "rb_define_module_under", module_specs_rb_define_module_under, 2); +#endif + +#ifdef HAVE_RB_DEFINE_CONST + rb_define_method(cls, "rb_define_const", module_specs_define_const, 3); +#endif + +#ifdef HAVE_RB_DEFINE_GLOBAL_CONST + rb_define_method(cls, "rb_define_global_const", module_specs_define_global_const, 2); +#endif + +#ifdef HAVE_RB_DEFINE_GLOBAL_FUNCTION + rb_define_method(cls, "rb_define_global_function", + module_specs_rb_define_global_function, 1); +#endif + +#ifdef HAVE_RB_DEFINE_METHOD + rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2); +#endif + +#ifdef HAVE_RB_DEFINE_MODULE_FUNCTION + rb_define_method(cls, "rb_define_module_function", + module_specs_rb_define_module_function, 2); +#endif + +#ifdef HAVE_RB_DEFINE_PRIVATE_METHOD + rb_define_method(cls, "rb_define_private_method", + module_specs_rb_define_private_method, 2); +#endif + +#ifdef HAVE_RB_DEFINE_PROTECTED_METHOD + rb_define_method(cls, "rb_define_protected_method", + module_specs_rb_define_protected_method, 2); +#endif + +#ifdef HAVE_RB_DEFINE_SINGLETON_METHOD + rb_define_method(cls, "rb_define_singleton_method", + module_specs_rb_define_singleton_method, 2); +#endif + +#ifdef HAVE_RB_UNDEF_METHOD + rb_define_method(cls, "rb_undef_method", module_specs_rb_undef_method, 2); +#endif + +#ifdef HAVE_RB_UNDEF + rb_define_method(cls, "rb_undef", module_specs_rb_undef, 2); +#endif + +#ifdef HAVE_RB_CLASS2NAME + rb_define_method(cls, "rb_class2name", module_specs_rbclass2name, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/module_under_autoload_spec.c b/spec/rubyspec/optional/capi/ext/module_under_autoload_spec.c new file mode 100644 index 0000000000..c8f19a287b --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/module_under_autoload_spec.c @@ -0,0 +1,7 @@ +#include "ruby.h" + +void Init_module_under_autoload_spec(void) { + VALUE specs = rb_const_get(rb_cObject, rb_intern("CApiModuleSpecs")); + rb_define_module_under(specs, "ModuleUnderAutoload"); + rb_define_module_under(specs, "RubyUnderAutoload"); +} diff --git a/spec/rubyspec/optional/capi/ext/mutex_spec.c b/spec/rubyspec/optional/capi/ext/mutex_spec.c new file mode 100644 index 0000000000..d5ce06e124 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/mutex_spec.c @@ -0,0 +1,91 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_MUTEX_NEW +VALUE mutex_spec_rb_mutex_new(VALUE self) { + return rb_mutex_new(); +} +#endif + +#ifdef HAVE_RB_MUTEX_LOCKED_P +VALUE mutex_spec_rb_mutex_locked_p(VALUE self, VALUE mutex) { + return rb_mutex_locked_p(mutex); +} +#endif + +#ifdef HAVE_RB_MUTEX_TRYLOCK +VALUE mutex_spec_rb_mutex_trylock(VALUE self, VALUE mutex) { + return rb_mutex_trylock(mutex); +} +#endif + +#ifdef HAVE_RB_MUTEX_LOCK +VALUE mutex_spec_rb_mutex_lock(VALUE self, VALUE mutex) { + return rb_mutex_lock(mutex); +} +#endif + +#ifdef HAVE_RB_MUTEX_UNLOCK +VALUE mutex_spec_rb_mutex_unlock(VALUE self, VALUE mutex) { + return rb_mutex_unlock(mutex); +} +#endif + +#ifdef HAVE_RB_MUTEX_SLEEP +VALUE mutex_spec_rb_mutex_sleep(VALUE self, VALUE mutex, VALUE timeout) { + return rb_mutex_sleep(mutex, timeout); +} +#endif + +#ifdef HAVE_RB_MUTEX_SYNCHRONIZE + +VALUE mutex_spec_rb_mutex_callback(VALUE arg) { + return rb_funcall(arg, rb_intern("call"), 0); +} + +VALUE mutex_spec_rb_mutex_synchronize(VALUE self, VALUE mutex, VALUE value) { + return rb_mutex_synchronize(mutex, mutex_spec_rb_mutex_callback, value); +} +#endif + +void Init_mutex_spec(void) { + VALUE cls; + cls = rb_define_class("CApiMutexSpecs", rb_cObject); + +#ifdef HAVE_RB_MUTEX_NEW + rb_define_method(cls, "rb_mutex_new", mutex_spec_rb_mutex_new, 0); +#endif + +#ifdef HAVE_RB_MUTEX_LOCKED_P + rb_define_method(cls, "rb_mutex_locked_p", mutex_spec_rb_mutex_locked_p, 1); +#endif + +#ifdef HAVE_RB_MUTEX_TRYLOCK + rb_define_method(cls, "rb_mutex_trylock", mutex_spec_rb_mutex_trylock, 1); +#endif + +#ifdef HAVE_RB_MUTEX_LOCK + rb_define_method(cls, "rb_mutex_lock", mutex_spec_rb_mutex_lock, 1); +#endif + +#ifdef HAVE_RB_MUTEX_UNLOCK + rb_define_method(cls, "rb_mutex_unlock", mutex_spec_rb_mutex_unlock, 1); +#endif + +#ifdef HAVE_RB_MUTEX_SLEEP + rb_define_method(cls, "rb_mutex_sleep", mutex_spec_rb_mutex_sleep, 2); +#endif + +#ifdef HAVE_RB_MUTEX_SYNCHRONIZE + rb_define_method(cls, "rb_mutex_synchronize", mutex_spec_rb_mutex_synchronize, 2); +#endif +} + +#ifdef __cplusplus +} +#endif + diff --git a/spec/rubyspec/optional/capi/ext/numeric_spec.c b/spec/rubyspec/optional/capi/ext/numeric_spec.c new file mode 100644 index 0000000000..cd643cc66f --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/numeric_spec.c @@ -0,0 +1,166 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_NUM2CHR +static VALUE numeric_spec_NUM2CHR(VALUE self, VALUE value) { + return INT2FIX(NUM2CHR(value)); +} +#endif + +#ifdef HAVE_RB_INT2INUM +static VALUE numeric_spec_rb_int2inum_14(VALUE self) { + return rb_int2inum(14); +} +#endif + +#ifdef HAVE_RB_INTEGER +static VALUE numeric_spec_rb_Integer(VALUE self, VALUE str) { + return rb_Integer(str); +} +#endif + +#ifdef HAVE_RB_LL2INUM +static VALUE numeric_spec_rb_ll2inum_14(VALUE self) { + return rb_ll2inum(14); +} +#endif + +#ifdef HAVE_RB_NUM2DBL +static VALUE numeric_spec_rb_num2dbl(VALUE self, VALUE num) { + return rb_float_new(rb_num2dbl(num)); +} +#endif + +#ifdef HAVE_RB_NUM2INT +static VALUE numeric_spec_rb_num2int(VALUE self, VALUE num) { + return LONG2NUM(rb_num2int(num)); +} +#endif + +#ifdef HAVE_RB_INT2NUM +static VALUE numeric_spec_rb_int2num(VALUE self, VALUE num) { + return INT2NUM(rb_num2long(num)); +} +#endif + +#ifdef HAVE_RB_NUM2LONG +static VALUE numeric_spec_rb_num2long(VALUE self, VALUE num) { + return LONG2NUM(rb_num2long(num)); +} +#endif + +#ifdef HAVE_RB_NUM2UINT +static VALUE numeric_spec_rb_num2uint(VALUE self, VALUE num) { + return ULONG2NUM(rb_num2uint(num)); +} +#endif + +#ifdef HAVE_RB_NUM2ULONG +static VALUE numeric_spec_rb_num2ulong(VALUE self, VALUE num) { + return ULONG2NUM(rb_num2ulong(num)); +} +#endif + +#ifdef HAVE_RB_NUM_ZERODIV +static VALUE numeric_spec_rb_num_zerodiv(VALUE self) { + rb_num_zerodiv(); + return Qnil; +} +#endif + +#ifdef HAVE_RB_CMPINT +static VALUE numeric_spec_rb_cmpint(VALUE self, VALUE val, VALUE b) { + return INT2FIX(rb_cmpint(val, val, b)); +} +#endif + +#ifdef HAVE_RB_NUM_COERCE_BIN +static VALUE numeric_spec_rb_num_coerce_bin(VALUE self, VALUE x, VALUE y, VALUE op) { + return rb_num_coerce_bin(x, y, SYM2ID(op)); +} +#endif + +#ifdef HAVE_RB_NUM_COERCE_CMP +static VALUE numeric_spec_rb_num_coerce_cmp(VALUE self, VALUE x, VALUE y, VALUE op) { + return rb_num_coerce_cmp(x, y, SYM2ID(op)); +} +#endif + +#ifdef HAVE_RB_NUM_COERCE_RELOP +static VALUE numeric_spec_rb_num_coerce_relop(VALUE self, VALUE x, VALUE y, VALUE op) { + return rb_num_coerce_relop(x, y, SYM2ID(op)); +} +#endif + +void Init_numeric_spec(void) { + VALUE cls; + cls = rb_define_class("CApiNumericSpecs", rb_cObject); + +#ifdef HAVE_NUM2CHR + rb_define_method(cls, "NUM2CHR", numeric_spec_NUM2CHR, 1); +#endif + +#ifdef HAVE_RB_INT2INUM + rb_define_method(cls, "rb_int2inum_14", numeric_spec_rb_int2inum_14, 0); +#endif + +#ifdef HAVE_RB_INTEGER + rb_define_method(cls, "rb_Integer", numeric_spec_rb_Integer, 1); +#endif + +#ifdef HAVE_RB_LL2INUM + rb_define_method(cls, "rb_ll2inum_14", numeric_spec_rb_ll2inum_14, 0); +#endif + +#ifdef HAVE_RB_NUM2DBL + rb_define_method(cls, "rb_num2dbl", numeric_spec_rb_num2dbl, 1); +#endif + +#ifdef HAVE_RB_NUM2INT + rb_define_method(cls, "rb_num2int", numeric_spec_rb_num2int, 1); +#endif + +#ifdef HAVE_RB_NUM2LONG + rb_define_method(cls, "rb_num2long", numeric_spec_rb_num2long, 1); +#endif + +#ifdef HAVE_RB_INT2NUM + rb_define_method(cls, "rb_int2num", numeric_spec_rb_int2num, 1); +#endif + +#ifdef HAVE_RB_NUM2UINT + rb_define_method(cls, "rb_num2uint", numeric_spec_rb_num2uint, 1); +#endif + +#ifdef HAVE_RB_NUM2ULONG + rb_define_method(cls, "rb_num2ulong", numeric_spec_rb_num2ulong, 1); +#endif + +#ifdef HAVE_RB_NUM_ZERODIV + rb_define_method(cls, "rb_num_zerodiv", numeric_spec_rb_num_zerodiv, 0); +#endif + +#ifdef HAVE_RB_CMPINT + rb_define_method(cls, "rb_cmpint", numeric_spec_rb_cmpint, 2); +#endif + +#ifdef HAVE_RB_NUM_COERCE_BIN + rb_define_method(cls, "rb_num_coerce_bin", numeric_spec_rb_num_coerce_bin, 3); +#endif + +#ifdef HAVE_RB_NUM_COERCE_CMP + rb_define_method(cls, "rb_num_coerce_cmp", numeric_spec_rb_num_coerce_cmp, 3); +#endif + +#ifdef HAVE_RB_NUM_COERCE_RELOP + rb_define_method(cls, "rb_num_coerce_relop", numeric_spec_rb_num_coerce_relop, 3); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/object_spec.c b/spec/rubyspec/optional/capi/ext/object_spec.c new file mode 100644 index 0000000000..ad1ebecc78 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/object_spec.c @@ -0,0 +1,608 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_OBJ_TAINT +static VALUE object_spec_OBJ_TAINT(VALUE self, VALUE obj) { + OBJ_TAINT(obj); + return Qnil; +} +#endif + +#ifdef HAVE_OBJ_TAINTED +static VALUE object_spec_OBJ_TAINTED(VALUE self, VALUE obj) { + return OBJ_TAINTED(obj) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_OBJ_INFECT +static VALUE object_spec_OBJ_INFECT(VALUE self, VALUE host, VALUE source) { + OBJ_INFECT(host, source); + return Qnil; +} +#endif + +#ifdef HAVE_RB_ANY_TO_S +static VALUE object_spec_rb_any_to_s(VALUE self, VALUE obj) { + return rb_any_to_s(obj); +} +#endif + +#ifdef HAVE_RB_ATTR_GET +static VALUE so_attr_get(VALUE self, VALUE obj, VALUE attr) { + return rb_attr_get(obj, SYM2ID(attr)); +} +#endif + +#ifdef HAVE_RB_OBJ_INSTANCE_VARIABLES +static VALUE object_spec_rb_obj_instance_variables(VALUE self, VALUE obj) { + return rb_obj_instance_variables(obj); +} +#endif + +#ifdef HAVE_RB_CHECK_ARRAY_TYPE +static VALUE so_check_array_type(VALUE self, VALUE ary) { + return rb_check_array_type(ary); +} +#endif + +#ifdef HAVE_RB_CHECK_CONVERT_TYPE +static VALUE so_check_convert_type(VALUE self, VALUE obj, VALUE klass, VALUE method) { + return rb_check_convert_type(obj, T_ARRAY, RSTRING_PTR(klass), RSTRING_PTR(method)); +} +#endif + +#ifdef HAVE_RB_CHECK_TO_INTEGER +static VALUE so_check_to_integer(VALUE self, VALUE obj, VALUE method) { + return rb_check_to_integer(obj, RSTRING_PTR(method)); +} +#endif + +#ifdef HAVE_RB_CHECK_FROZEN +static VALUE object_spec_rb_check_frozen(VALUE self, VALUE obj) { + rb_check_frozen(obj); + return Qnil; +} +#endif + +#ifdef HAVE_RB_CHECK_STRING_TYPE +static VALUE so_check_string_type(VALUE self, VALUE str) { + return rb_check_string_type(str); +} +#endif + +#ifdef HAVE_RB_CLASS_OF +static VALUE so_rbclassof(VALUE self, VALUE obj) { + return rb_class_of(obj); +} +#endif + +#ifdef HAVE_RB_CONVERT_TYPE +static VALUE so_convert_type(VALUE self, VALUE obj, VALUE klass, VALUE method) { + return rb_convert_type(obj, T_ARRAY, RSTRING_PTR(klass), RSTRING_PTR(method)); +} +#endif + +#ifdef HAVE_RB_EXTEND_OBJECT +static VALUE object_spec_rb_extend_object(VALUE self, VALUE obj, VALUE mod) { + rb_extend_object(obj, mod); + return obj; +} +#endif + +#ifdef HAVE_RB_INSPECT +static VALUE so_inspect(VALUE self, VALUE obj) { + return rb_inspect(obj); +} +#endif + +#ifdef HAVE_RB_OBJ_ALLOC +static VALUE so_rb_obj_alloc(VALUE self, VALUE klass) { + return rb_obj_alloc(klass); +} +#endif + +#ifdef HAVE_RB_OBJ_DUP +static VALUE so_rb_obj_dup(VALUE self, VALUE klass) { + return rb_obj_dup(klass); +} +#endif + +#ifdef HAVE_RB_OBJ_CALL_INIT +static VALUE so_rb_obj_call_init(VALUE self, VALUE object, + VALUE nargs, VALUE args) { + int c_nargs = FIX2INT(nargs); + VALUE *c_args = alloca(sizeof(VALUE) * c_nargs); + int i; + + for (i = 0; i < c_nargs; i++) + c_args[i] = rb_ary_entry(args, i); + + rb_obj_call_init(object, c_nargs, c_args); + + return Qnil; +} +#endif + +#ifdef HAVE_RB_OBJ_CLASSNAME +static VALUE so_rbobjclassname(VALUE self, VALUE obj) { + return rb_str_new2(rb_obj_classname(obj)); +} + +#endif + +#ifdef HAVE_RB_OBJ_FREEZE +static VALUE object_spec_rb_obj_freeze(VALUE self, VALUE obj) { + return rb_obj_freeze(obj); +} +#endif + +#ifdef HAVE_RB_OBJ_FROZEN_P +static VALUE object_spec_rb_obj_frozen_p(VALUE self, VALUE obj) { + return rb_obj_frozen_p(obj); +} +#endif + +#ifdef HAVE_RB_OBJ_ID +static VALUE object_spec_rb_obj_id(VALUE self, VALUE obj) { + return rb_obj_id(obj); +} +#endif + +#ifdef HAVE_RB_OBJ_IS_INSTANCE_OF +static VALUE so_instance_of(VALUE self, VALUE obj, VALUE klass) { + return rb_obj_is_instance_of(obj, klass); +} +#endif + +#ifdef HAVE_RB_OBJ_IS_KIND_OF +static VALUE so_kind_of(VALUE self, VALUE obj, VALUE klass) { + return rb_obj_is_kind_of(obj, klass); +} +#endif + +#ifdef HAVE_RB_OBJ_METHOD_ARITY +static VALUE object_specs_rb_obj_method_arity(VALUE self, VALUE obj, VALUE mid) { + return INT2FIX(rb_obj_method_arity(obj, SYM2ID(mid))); +} +#endif + +#ifdef HAVE_RB_OBJ_TAINT +static VALUE object_spec_rb_obj_taint(VALUE self, VALUE obj) { + return rb_obj_taint(obj); +} +#endif + +#ifdef HAVE_RB_REQUIRE +static VALUE so_require(VALUE self) { + rb_require("fixtures/foo"); + return Qnil; +} +#endif + +#ifdef HAVE_RB_RESPOND_TO +static VALUE so_respond_to(VALUE self, VALUE obj, VALUE sym) { + return rb_respond_to(obj, SYM2ID(sym)) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_OBJ_RESPOND_TO +static VALUE so_obj_respond_to(VALUE self, VALUE obj, VALUE sym, VALUE priv) { + return rb_obj_respond_to(obj, SYM2ID(sym), priv == Qtrue ? 1 : 0) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_METHOD_BOUNDP +static VALUE object_spec_rb_method_boundp(VALUE self, VALUE obj, VALUE method, VALUE exclude_private) { + ID id = SYM2ID(method); + return rb_method_boundp(obj, id, exclude_private == Qtrue ? 1 : 0) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_SPECIAL_CONST_P +static VALUE object_spec_rb_special_const_p(VALUE self, VALUE value) { + return rb_special_const_p(value); +} +#endif + +#ifdef HAVE_RB_TO_ID +static VALUE so_to_id(VALUE self, VALUE obj) { + return ID2SYM(rb_to_id(obj)); +} +#endif + +#ifdef HAVE_RTEST +static VALUE object_spec_RTEST(VALUE self, VALUE value) { + return RTEST(value) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_TYPE +static VALUE so_is_type_nil(VALUE self, VALUE obj) { + if(TYPE(obj) == T_NIL) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_type_object(VALUE self, VALUE obj) { + if(TYPE(obj) == T_OBJECT) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_type_array(VALUE self, VALUE obj) { + if(TYPE(obj) == T_ARRAY) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_type_module(VALUE self, VALUE obj) { + if(TYPE(obj) == T_MODULE) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_type_class(VALUE self, VALUE obj) { + if(TYPE(obj) == T_CLASS) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_type_data(VALUE self, VALUE obj) { + if(TYPE(obj) == T_DATA) { + return Qtrue; + } + return Qfalse; +} +#endif + +#ifdef HAVE_RB_TYPE_P +static VALUE so_is_rb_type_p_nil(VALUE self, VALUE obj) { + if(rb_type_p(obj, T_NIL)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_rb_type_p_object(VALUE self, VALUE obj) { + if(rb_type_p(obj, T_OBJECT)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_rb_type_p_array(VALUE self, VALUE obj) { + if(rb_type_p(obj, T_ARRAY)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_rb_type_p_module(VALUE self, VALUE obj) { + if(rb_type_p(obj, T_MODULE)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_rb_type_p_class(VALUE self, VALUE obj) { + if(rb_type_p(obj, T_CLASS)) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_rb_type_p_data(VALUE self, VALUE obj) { + if(rb_type_p(obj, T_DATA)) { + return Qtrue; + } + return Qfalse; +} +#endif + +#ifdef HAVE_BUILTIN_TYPE +static VALUE so_is_builtin_type_object(VALUE self, VALUE obj) { + if(BUILTIN_TYPE(obj) == T_OBJECT) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_builtin_type_array(VALUE self, VALUE obj) { + if(BUILTIN_TYPE(obj) == T_ARRAY) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_builtin_type_module(VALUE self, VALUE obj) { + if(BUILTIN_TYPE(obj) == T_MODULE) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_builtin_type_class(VALUE self, VALUE obj) { + if(BUILTIN_TYPE(obj) == T_CLASS) { + return Qtrue; + } + return Qfalse; +} + +static VALUE so_is_builtin_type_data(VALUE self, VALUE obj) { + if(BUILTIN_TYPE(obj) == T_DATA) { + return Qtrue; + } + return Qfalse; +} +#endif + +#ifdef HAVE_RB_TO_INT +static VALUE object_spec_rb_to_int(VALUE self, VALUE obj) { + return rb_to_int(obj); +} +#endif + +#ifdef HAVE_RB_OBJ_INSTANCE_EVAL +static VALUE object_spec_rb_obj_instance_eval(VALUE self, VALUE obj) { + return rb_obj_instance_eval(0, NULL, obj); +} +#endif + +#ifdef HAVE_RB_IV_GET +static VALUE object_spec_rb_iv_get(VALUE self, VALUE obj, VALUE name) { + return rb_iv_get(obj, RSTRING_PTR(name)); +} +#endif + +#ifdef HAVE_RB_IV_SET +static VALUE object_spec_rb_iv_set(VALUE self, VALUE obj, VALUE name, VALUE value) { + return rb_iv_set(obj, RSTRING_PTR(name), value); +} +#endif + +#ifdef HAVE_RB_IVAR_GET +static VALUE object_spec_rb_ivar_get(VALUE self, VALUE obj, VALUE sym_name) { + return rb_ivar_get(obj, SYM2ID(sym_name)); +} +#endif + +#ifdef HAVE_RB_IVAR_SET +static VALUE object_spec_rb_ivar_set(VALUE self, VALUE obj, VALUE sym_name, VALUE value) { + return rb_ivar_set(obj, SYM2ID(sym_name), value); +} +#endif + +#ifdef HAVE_RB_IVAR_DEFINED +static VALUE object_spec_rb_ivar_defined(VALUE self, VALUE obj, VALUE sym_name) { + return rb_ivar_defined(obj, SYM2ID(sym_name)); +} +#endif + +#ifdef HAVE_RB_EQUAL +static VALUE object_spec_rb_equal(VALUE self, VALUE a, VALUE b) { + return rb_equal(a, b); +} +#endif + +#ifdef HAVE_RB_CLASS_INHERITED_P +static VALUE object_spec_rb_class_inherited_p(VALUE self, VALUE mod, VALUE arg) { + return rb_class_inherited_p(mod, arg); +} +#endif + + +void Init_object_spec(void) { + VALUE cls; + cls = rb_define_class("CApiObjectSpecs", rb_cObject); + +#ifdef HAVE_OBJ_TAINT + rb_define_method(cls, "OBJ_TAINT", object_spec_OBJ_TAINT, 1); +#endif + +#ifdef HAVE_OBJ_TAINTED + rb_define_method(cls, "OBJ_TAINTED", object_spec_OBJ_TAINTED, 1); +#endif + +#ifdef HAVE_OBJ_INFECT + rb_define_method(cls, "OBJ_INFECT", object_spec_OBJ_INFECT, 2); +#endif + +#ifdef HAVE_RB_ANY_TO_S + rb_define_method(cls, "rb_any_to_s", object_spec_rb_any_to_s, 1); +#endif + +#ifdef HAVE_RB_ATTR_GET + rb_define_method(cls, "rb_attr_get", so_attr_get, 2); +#endif + +#ifdef HAVE_RB_OBJ_INSTANCE_VARIABLES + rb_define_method(cls, "rb_obj_instance_variables", object_spec_rb_obj_instance_variables, 1); +#endif + +#ifdef HAVE_RB_CHECK_ARRAY_TYPE + rb_define_method(cls, "rb_check_array_type", so_check_array_type, 1); +#endif + +#ifdef HAVE_RB_CHECK_CONVERT_TYPE + rb_define_method(cls, "rb_check_convert_type", so_check_convert_type, 3); +#endif + +#ifdef HAVE_RB_CHECK_TO_INTEGER + rb_define_method(cls, "rb_check_to_integer", so_check_to_integer, 2); +#endif + +#ifdef HAVE_RB_CHECK_FROZEN + rb_define_method(cls, "rb_check_frozen", object_spec_rb_check_frozen, 1); +#endif + +#ifdef HAVE_RB_CHECK_STRING_TYPE + rb_define_method(cls, "rb_check_string_type", so_check_string_type, 1); +#endif + +#ifdef HAVE_RB_CLASS_OF + rb_define_method(cls, "rb_class_of", so_rbclassof, 1); +#endif + +#ifdef HAVE_RB_CONVERT_TYPE + rb_define_method(cls, "rb_convert_type", so_convert_type, 3); +#endif + +#ifdef HAVE_RB_EXTEND_OBJECT + rb_define_method(cls, "rb_extend_object", object_spec_rb_extend_object, 2); +#endif + +#ifdef HAVE_RB_INSPECT + rb_define_method(cls, "rb_inspect", so_inspect, 1); +#endif + +#ifdef HAVE_RB_OBJ_ALLOC + rb_define_method(cls, "rb_obj_alloc", so_rb_obj_alloc, 1); +#endif + +#ifdef HAVE_RB_OBJ_ALLOC + rb_define_method(cls, "rb_obj_dup", so_rb_obj_dup, 1); +#endif + +#ifdef HAVE_RB_OBJ_CALL_INIT + rb_define_method(cls, "rb_obj_call_init", so_rb_obj_call_init, 3); +#endif + +#ifdef HAVE_RB_OBJ_CLASSNAME + rb_define_method(cls, "rb_obj_classname", so_rbobjclassname, 1); +#endif + +#ifdef HAVE_RB_OBJ_FREEZE + rb_define_method(cls, "rb_obj_freeze", object_spec_rb_obj_freeze, 1); +#endif + +#ifdef HAVE_RB_OBJ_FROZEN_P + rb_define_method(cls, "rb_obj_frozen_p", object_spec_rb_obj_frozen_p, 1); +#endif + +#ifdef HAVE_RB_OBJ_ID + rb_define_method(cls, "rb_obj_id", object_spec_rb_obj_id, 1); +#endif + +#ifdef HAVE_RB_OBJ_IS_INSTANCE_OF + rb_define_method(cls, "rb_obj_is_instance_of", so_instance_of, 2); +#endif + +#ifdef HAVE_RB_OBJ_IS_KIND_OF + rb_define_method(cls, "rb_obj_is_kind_of", so_kind_of, 2); +#endif + +#ifdef HAVE_RB_OBJ_METHOD_ARITY + rb_define_method(cls, "rb_obj_method_arity", object_specs_rb_obj_method_arity, 2); +#endif + +#ifdef HAVE_RB_OBJ_TAINT + rb_define_method(cls, "rb_obj_taint", object_spec_rb_obj_taint, 1); +#endif + +#ifdef HAVE_RB_REQUIRE + rb_define_method(cls, "rb_require", so_require, 0); +#endif + +#ifdef HAVE_RB_RESPOND_TO + rb_define_method(cls, "rb_respond_to", so_respond_to, 2); +#endif + +#ifdef HAVE_RB_METHOD_BOUNDP + rb_define_method(cls, "rb_method_boundp", object_spec_rb_method_boundp, 3); +#endif + +#ifdef HAVE_RB_OBJ_RESPOND_TO + rb_define_method(cls, "rb_obj_respond_to", so_obj_respond_to, 3); +#endif + +#ifdef HAVE_RB_SPECIAL_CONST_P + rb_define_method(cls, "rb_special_const_p", object_spec_rb_special_const_p, 1); +#endif + +#ifdef HAVE_RB_STR_NEW2 +#endif + +#ifdef HAVE_RB_TO_ID + rb_define_method(cls, "rb_to_id", so_to_id, 1); +#endif + +#ifdef HAVE_RTEST + rb_define_method(cls, "RTEST", object_spec_RTEST, 1); +#endif + +#ifdef HAVE_TYPE + rb_define_method(cls, "rb_is_type_nil", so_is_type_nil, 1); + rb_define_method(cls, "rb_is_type_object", so_is_type_object, 1); + rb_define_method(cls, "rb_is_type_array", so_is_type_array, 1); + rb_define_method(cls, "rb_is_type_module", so_is_type_module, 1); + rb_define_method(cls, "rb_is_type_class", so_is_type_class, 1); + rb_define_method(cls, "rb_is_type_data", so_is_type_data, 1); +#endif + +#ifdef HAVE_RB_TYPE_P + rb_define_method(cls, "rb_is_rb_type_p_nil", so_is_rb_type_p_nil, 1); + rb_define_method(cls, "rb_is_rb_type_p_object", so_is_rb_type_p_object, 1); + rb_define_method(cls, "rb_is_rb_type_p_array", so_is_rb_type_p_array, 1); + rb_define_method(cls, "rb_is_rb_type_p_module", so_is_rb_type_p_module, 1); + rb_define_method(cls, "rb_is_rb_type_p_class", so_is_rb_type_p_class, 1); + rb_define_method(cls, "rb_is_rb_type_p_data", so_is_rb_type_p_data, 1); +#endif + +#ifdef HAVE_BUILTIN_TYPE + rb_define_method(cls, "rb_is_builtin_type_object", so_is_builtin_type_object, 1); + rb_define_method(cls, "rb_is_builtin_type_array", so_is_builtin_type_array, 1); + rb_define_method(cls, "rb_is_builtin_type_module", so_is_builtin_type_module, 1); + rb_define_method(cls, "rb_is_builtin_type_class", so_is_builtin_type_class, 1); + rb_define_method(cls, "rb_is_builtin_type_data", so_is_builtin_type_data, 1); +#endif + +#ifdef HAVE_RB_TO_INT + rb_define_method(cls, "rb_to_int", object_spec_rb_to_int, 1); +#endif + +#ifdef HAVE_RB_EQUAL + rb_define_method(cls, "rb_equal", object_spec_rb_equal, 2); +#endif + +#ifdef HAVE_RB_CLASS_INHERITED_P + rb_define_method(cls, "rb_class_inherited_p", object_spec_rb_class_inherited_p, 2); +#endif + +#ifdef HAVE_RB_OBJ_INSTANCE_EVAL + rb_define_method(cls, "rb_obj_instance_eval", object_spec_rb_obj_instance_eval, 1); +#endif + +#ifdef HAVE_RB_IV_GET + rb_define_method(cls, "rb_iv_get", object_spec_rb_iv_get, 2); +#endif + +#ifdef HAVE_RB_IV_SET + rb_define_method(cls, "rb_iv_set", object_spec_rb_iv_set, 3); +#endif + +#ifdef HAVE_RB_IVAR_GET + rb_define_method(cls, "rb_ivar_get", object_spec_rb_ivar_get, 2); +#endif + +#ifdef HAVE_RB_IVAR_SET + rb_define_method(cls, "rb_ivar_set", object_spec_rb_ivar_set, 3); +#endif + +#ifdef HAVE_RB_IVAR_DEFINED + rb_define_method(cls, "rb_ivar_defined", object_spec_rb_ivar_defined, 2); +#endif + +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/proc_spec.c b/spec/rubyspec/optional/capi/ext/proc_spec.c new file mode 100644 index 0000000000..b7a47536d9 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/proc_spec.c @@ -0,0 +1,65 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_PROC_NEW +VALUE proc_spec_rb_proc_new_function(VALUE args) { + return rb_funcall(args, rb_intern("inspect"), 0); +} + +VALUE proc_spec_rb_proc_new(VALUE self) { + return rb_proc_new(proc_spec_rb_proc_new_function, Qnil); +} +#endif + +/* This helper is not strictly necessary but reflects the code in wxRuby that + * originally exposed issues with this Proc.new behavior. + */ +VALUE proc_spec_rb_Proc_new_helper(void) { + return rb_funcall(rb_cProc, rb_intern("new"), 0); +} + +VALUE proc_spec_rb_Proc_new(VALUE self, VALUE scenario) { + switch(FIX2INT(scenario)) { + case 0: + return proc_spec_rb_Proc_new_helper(); + case 1: + rb_funcall(self, rb_intern("call_nothing"), 0); + return proc_spec_rb_Proc_new_helper(); + case 2: + return rb_funcall(self, rb_intern("call_Proc_new"), 0); + case 3: + return rb_funcall(self, rb_intern("call_rb_Proc_new"), 0); + case 4: + return rb_funcall(self, rb_intern("call_rb_Proc_new_with_block"), 0); + case 5: + rb_funcall(self, rb_intern("call_rb_Proc_new_with_block"), 0); + return proc_spec_rb_Proc_new_helper(); + case 6: + return rb_funcall(self, rb_intern("call_block_given?"), 0); + default: + rb_raise(rb_eException, "invalid scenario"); + } + + return Qnil; +} + +void Init_proc_spec(void) { + VALUE cls; + cls = rb_define_class("CApiProcSpecs", rb_cObject); + +#ifdef HAVE_RB_PROC_NEW + rb_define_method(cls, "rb_proc_new", proc_spec_rb_proc_new, 0); +#endif + + rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1); +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/range_spec.c b/spec/rubyspec/optional/capi/ext/range_spec.c new file mode 100644 index 0000000000..02aff5a5c2 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/range_spec.c @@ -0,0 +1,47 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_RANGE_NEW +VALUE range_spec_rb_range_new(int argc, VALUE* argv, VALUE self) { + int exclude_end = 0; + if(argc == 3) { + exclude_end = RTEST(argv[2]); + } + return rb_range_new(argv[0], argv[1], exclude_end); +} +#endif + +#ifdef HAVE_RB_RANGE_VALUES +VALUE range_spec_rb_range_values(VALUE self, VALUE range) { + VALUE beg; + VALUE end; + int excl; + VALUE ary = rb_ary_new(); + rb_range_values(range, &beg, &end, &excl); + rb_ary_store(ary, 0, beg); + rb_ary_store(ary, 1, end); + rb_ary_store(ary, 2, excl ? Qtrue : Qfalse); + return ary; +} +#endif + +void Init_range_spec(void) { + VALUE cls; + cls = rb_define_class("CApiRangeSpecs", rb_cObject); + +#ifdef HAVE_RB_RANGE_NEW + rb_define_method(cls, "rb_range_new", range_spec_rb_range_new, -1); +#endif + +#ifdef HAVE_RB_RANGE_VALUES + rb_define_method(cls, "rb_range_values", range_spec_rb_range_values, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/rational_spec.c b/spec/rubyspec/optional/capi/ext/rational_spec.c new file mode 100644 index 0000000000..9f349261a0 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/rational_spec.c @@ -0,0 +1,95 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_RATIONAL +static VALUE rational_spec_rb_Rational(VALUE self, VALUE num, VALUE den) { + return rb_Rational(num, den); +} +#endif + +#ifdef HAVE_RB_RATIONAL1 +static VALUE rational_spec_rb_Rational1(VALUE self, VALUE num) { + return rb_Rational1(num); +} +#endif + +#ifdef HAVE_RB_RATIONAL2 +static VALUE rational_spec_rb_Rational2(VALUE self, VALUE num, VALUE den) { + return rb_Rational2(num, den); +} +#endif + +#ifdef HAVE_RB_RATIONAL_NEW +static VALUE rational_spec_rb_rational_new(VALUE self, VALUE num, VALUE den) { + return rb_rational_new(num, den); +} +#endif + +#ifdef HAVE_RB_RATIONAL_NEW1 +static VALUE rational_spec_rb_rational_new1(VALUE self, VALUE num) { + return rb_rational_new1(num); +} +#endif + +#ifdef HAVE_RB_RATIONAL_NEW2 +static VALUE rational_spec_rb_rational_new2(VALUE self, VALUE num, VALUE den) { + return rb_rational_new2(num, den); +} +#endif + +#ifdef HAVE_RB_RATIONAL_NUM +static VALUE rational_spec_rb_rational_num(VALUE self, VALUE rational) { + return rb_rational_num(rational); +} +#endif + +#ifdef HAVE_RB_RATIONAL_DEN +static VALUE rational_spec_rb_rational_den(VALUE self, VALUE rational) { + return rb_rational_den(rational); +} +#endif + +void Init_rational_spec(void) { + VALUE cls; + cls = rb_define_class("CApiRationalSpecs", rb_cObject); + +#ifdef HAVE_RB_RATIONAL + rb_define_method(cls, "rb_Rational", rational_spec_rb_Rational, 2); +#endif + +#ifdef HAVE_RB_RATIONAL1 + rb_define_method(cls, "rb_Rational1", rational_spec_rb_Rational1, 1); +#endif + +#ifdef HAVE_RB_RATIONAL2 + rb_define_method(cls, "rb_Rational2", rational_spec_rb_Rational2, 2); +#endif + +#ifdef HAVE_RB_RATIONAL_NEW + rb_define_method(cls, "rb_rational_new", rational_spec_rb_rational_new, 2); +#endif + +#ifdef HAVE_RB_RATIONAL_NEW1 + rb_define_method(cls, "rb_rational_new1", rational_spec_rb_rational_new1, 1); +#endif + +#ifdef HAVE_RB_RATIONAL_NEW2 + rb_define_method(cls, "rb_rational_new2", rational_spec_rb_rational_new2, 2); +#endif + +#ifdef HAVE_RB_RATIONAL_NUM + rb_define_method(cls, "rb_rational_num", rational_spec_rb_rational_num, 1); +#endif + +#ifdef HAVE_RB_RATIONAL_DEN + rb_define_method(cls, "rb_rational_den", rational_spec_rb_rational_den, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/regexp_spec.c b/spec/rubyspec/optional/capi/ext/regexp_spec.c new file mode 100644 index 0000000000..1058293444 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/regexp_spec.c @@ -0,0 +1,84 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include "ruby/re.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_REG_NEW +VALUE regexp_spec_re(VALUE self) { + return rb_reg_new("a", 1, 0); +} +#endif + +#ifdef HAVE_RB_REG_NTH_MATCH +VALUE regexp_spec_reg_1st_match(VALUE self, VALUE md) { + return rb_reg_nth_match(1, md); +} +#endif + +#ifdef HAVE_RB_REG_OPTIONS +VALUE regexp_spec_rb_reg_options(VALUE self, VALUE regexp) { + return INT2FIX(rb_reg_options(regexp)); +} +#endif + +#ifdef HAVE_RB_REG_REGCOMP +VALUE regexp_spec_rb_reg_regcomp(VALUE self, VALUE str) { + return rb_reg_regcomp(str); +} +#endif + +#ifdef HAVE_RB_REG_MATCH +VALUE regexp_spec_reg_match(VALUE self, VALUE re, VALUE str) { + return rb_reg_match(re, str); +} +#endif + +#ifdef HAVE_RB_BACKREF_GET +VALUE regexp_spec_backref_get(VALUE self) { + return rb_backref_get(); +} +#endif + +VALUE regexp_spec_match(VALUE self, VALUE regexp, VALUE str) { + return rb_funcall(regexp, rb_intern("match"), 1, str); +} + +void Init_regexp_spec(void) { + VALUE cls = rb_define_class("CApiRegexpSpecs", rb_cObject); + + rb_define_method(cls, "match", regexp_spec_match, 2); + +#ifdef HAVE_RB_REG_NEW + rb_define_method(cls, "a_re", regexp_spec_re, 0); +#endif + +#ifdef HAVE_RB_REG_NTH_MATCH + rb_define_method(cls, "a_re_1st_match", regexp_spec_reg_1st_match, 1); +#endif + +#ifdef HAVE_RB_REG_MATCH + rb_define_method(cls, "rb_reg_match", regexp_spec_reg_match, 2); +#endif + +#ifdef HAVE_RB_BACKREF_GET + rb_define_method(cls, "rb_backref_get", regexp_spec_backref_get, 0); +#endif + +#ifdef HAVE_RB_REG_OPTIONS + rb_define_method(cls, "rb_reg_options", regexp_spec_rb_reg_options, 1); +#endif + +#ifdef HAVE_RB_REG_REGCOMP + rb_define_method(cls, "rb_reg_regcomp", regexp_spec_rb_reg_regcomp, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/rubinius.h b/spec/rubyspec/optional/capi/ext/rubinius.h new file mode 100644 index 0000000000..7ddf73790d --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/rubinius.h @@ -0,0 +1,8 @@ +#ifndef RUBYSPEC_CAPI_RUBINIUS_H +#define RUBYSPEC_CAPI_RUBINIUS_H + +/* #undef any HAVE_ defines that Rubinius does not have. */ +#undef HAVE_RB_DEFINE_HOOKED_VARIABLE +#undef HAVE_RB_DEFINE_VARIABLE + +#endif diff --git a/spec/rubyspec/optional/capi/ext/rubyspec.h b/spec/rubyspec/optional/capi/ext/rubyspec.h new file mode 100644 index 0000000000..f20bdde37c --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/rubyspec.h @@ -0,0 +1,612 @@ +#ifndef RUBYSPEC_H +#define RUBYSPEC_H + +/* Define convenience macros similar to the mspec guards to assist + * with version incompatibilities. + */ + +#include +#ifdef HAVE_RUBY_VERSION_H +# include +#else +# include +#endif + +#ifndef RUBY_VERSION_MAJOR +#define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR +#define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR +#define RUBY_VERSION_TEENY RUBY_API_VERSION_TEENY +#endif + +#define RUBY_VERSION_BEFORE(major,minor,teeny) \ + ((RUBY_VERSION_MAJOR < (major)) || \ + (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR < (minor)) || \ + (RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR == (minor) && RUBY_VERSION_TEENY < (teeny))) + +#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 4) +#define RUBY_VERSION_IS_2_4 +#endif + +#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 3) +#define RUBY_VERSION_IS_2_3 +#endif + +/* Define all function flags */ + +/* Array */ +#define HAVE_RB_ARRAY 1 +#define HAVE_RARRAY_AREF 1 +#define HAVE_RARRAY_LEN 1 +#define HAVE_RARRAY_PTR 1 +#define HAVE_RB_ARY_AREF 1 +#define HAVE_RB_ARY_CLEAR 1 +#define HAVE_RB_ARY_DELETE 1 +#define HAVE_RB_ARY_DELETE_AT 1 +#define HAVE_RB_ARY_DUP 1 +#define HAVE_RB_ARY_ENTRY 1 +#define HAVE_RB_ARY_FREEZE 1 +#define HAVE_RB_ARY_INCLUDES 1 +#define HAVE_RB_ARY_JOIN 1 +#define HAVE_RB_ARY_NEW 1 +#define HAVE_RB_ARY_NEW2 1 +#define HAVE_RB_ARY_NEW_CAPA 1 +#define HAVE_RB_ARY_NEW3 1 +#define HAVE_RB_ARY_NEW_FROM_ARGS 1 +#define HAVE_RB_ARY_NEW4 1 +#define HAVE_RB_ARY_NEW_FROM_VALUES 1 +#define HAVE_RB_ARY_POP 1 +#define HAVE_RB_ARY_PUSH 1 +#define HAVE_RB_ARY_CAT 1 +#define HAVE_RB_ARY_REVERSE 1 +#define HAVE_RB_ARY_ROTATE 1 +#define HAVE_RB_ARY_SHIFT 1 +#define HAVE_RB_ARY_STORE 1 +#define HAVE_RB_ARY_CONCAT 1 +#define HAVE_RB_ARY_PLUS 1 +#define HAVE_RB_ARY_TO_ARY 1 +#define HAVE_RB_ARY_SUBSEQ 1 +#define HAVE_RB_ARY_TO_S 1 +#define HAVE_RB_ARY_UNSHIFT 1 +#define HAVE_RB_ASSOC_NEW 1 + +#define HAVE_RB_EACH 1 +#define HAVE_RB_ITERATE 1 +#define HAVE_RB_MEM_CLEAR 1 + +/* Bignum */ +#define HAVE_ABSINT_SIZE 1 +#define HAVE_RB_BIG2DBL 1 +#define HAVE_RB_DBL2BIG 1 +#define HAVE_RB_BIG2LL 1 +#define HAVE_RB_BIG2LONG 1 +#define HAVE_RB_BIG2STR 1 +#define HAVE_RB_BIG2ULONG 1 +#define HAVE_RB_BIG_CMP 1 +#define HAVE_RB_BIG_PACK 1 + +/* Class */ +#define HAVE_RB_CALL_SUPER 1 +#define HAVE_RB_CLASS2NAME 1 +#define HAVE_RB_CLASS_NAME 1 +#define HAVE_RB_CLASS_NEW 1 +#define HAVE_RB_CLASS_NEW_INSTANCE 1 +#define HAVE_RB_CLASS_PATH 1 +#define HAVE_RB_CLASS_REAL 1 +#define HAVE_RB_CVAR_DEFINED 1 +#define HAVE_RB_CVAR_GET 1 +#define HAVE_RB_CVAR_SET 1 +#define HAVE_RB_CV_GET 1 +#define HAVE_RB_CV_SET 1 +#define HAVE_RB_DEFINE_ATTR 1 +#define HAVE_RB_DEFINE_CLASS_VARIABLE 1 +#define HAVE_RB_INCLUDE_MODULE 1 +#define HAVE_RB_PATH2CLASS 1 +#define HAVE_RB_PATH_TO_CLASS 1 +#define HAVE_RB_CLASS_SUPERCLASS 1 + +/* Complex */ +#define HAVE_RB_COMPLEX 1 +#define HAVE_RB_COMPLEX1 1 +#define HAVE_RB_COMPLEX2 1 +#define HAVE_RB_COMPLEX_NEW 1 +#define HAVE_RB_COMPLEX_NEW1 1 +#define HAVE_RB_COMPLEX_NEW2 1 + +/* Constants */ +#define HAVE_RB_CARRAY 1 +#ifndef RUBY_INTEGER_UNIFICATION +#define HAVE_RB_CBIGNUM 1 +#endif +#define HAVE_RB_CCLASS 1 +#define HAVE_RB_CDATA 1 +#define HAVE_RB_CFALSECLASS 1 +#define HAVE_RB_CFILE 1 +#ifndef RUBY_INTEGER_UNIFICATION +#define HAVE_RB_CFIXNUM 1 +#endif +#define HAVE_RB_CFLOAT 1 +#define HAVE_RB_CHASH 1 +#define HAVE_RB_CINTEGER 1 +#define HAVE_RB_CIO 1 +#define HAVE_RB_CMATCH 1 +#define HAVE_RB_CMODULE 1 +#define HAVE_RB_CNILCLASS 1 +#define HAVE_RB_CNUMERIC 1 +#define HAVE_RB_COBJECT 1 +#define HAVE_RB_CPROC 1 +#define HAVE_RB_CMETHOD 1 +#define HAVE_RB_CRANGE 1 +#define HAVE_RB_CREGEXP 1 +#define HAVE_RB_CSTRING 1 +#define HAVE_RB_CSTRUCT 1 +#define HAVE_RB_CSYMBOL 1 +#define HAVE_RB_CTIME 1 +#define HAVE_RB_CTHREAD 1 +#define HAVE_RB_CTRUECLASS 1 +#define HAVE_RB_CNUMERATOR 1 +#define HAVE_RB_EARGERROR 1 +#define HAVE_RB_EEOFERROR 1 +#define HAVE_RB_EEXCEPTION 1 +#define HAVE_RB_EFLOATDOMAINERROR 1 +#define HAVE_RB_EINDEXERROR 1 +#define HAVE_RB_EINTERRUPT 1 +#define HAVE_RB_EIOERROR 1 +#define HAVE_RB_ELOADERROR 1 +#define HAVE_RB_ELOCALJUMPERROR 1 +#define HAVE_RB_EMATHDOMAINERROR 1 +#define HAVE_RB_ENAMEERROR 1 +#define HAVE_RB_ENOMEMERROR 1 +#define HAVE_RB_ENOMETHODERROR 1 +#define HAVE_RB_ENOTIMPERROR 1 +#define HAVE_RB_ERANGEERROR 1 +#define HAVE_RB_EREGEXPERROR 1 +#define HAVE_RB_ERUNTIMEERROR 1 +#define HAVE_RB_ESCRIPTERROR 1 +#define HAVE_RB_ESECURITYERROR 1 +#define HAVE_RB_ESIGNAL 1 +#define HAVE_RB_ESTANDARDERROR 1 +#define HAVE_RB_ESYNTAXERROR 1 +#define HAVE_RB_ESYSSTACKERROR 1 +#define HAVE_RB_ESYSTEMCALLERROR 1 +#define HAVE_RB_ESYSTEMEXIT 1 +#define HAVE_RB_ETHREADERROR 1 +#define HAVE_RB_ETYPEERROR 1 +#define HAVE_RB_EZERODIVERROR 1 +#define HAVE_RB_MCOMPARABLE 1 +#define HAVE_RB_MENUMERABLE 1 +#define HAVE_RB_MERRNO 1 +#define HAVE_RB_MKERNEL 1 +#define HAVE_RB_CDIR 1 + +/* Data */ +#define HAVE_DATA_WRAP_STRUCT 1 +#define HAVE_RDATA 1 + +#define HAVE_TYPEDDATA_WRAP_STRUCT 1 +#define HAVE_RTYPEDDATA + +/* Encoding */ +#define HAVE_ENCODING_GET 1 +#define HAVE_ENCODING_SET 1 +#define HAVE_ENC_CODERANGE_ASCIIONLY 1 + +#define HAVE_RB_ASCII8BIT_ENCODING 1 +#define HAVE_RB_ASCII8BIT_ENCINDEX 1 +#define HAVE_RB_USASCII_ENCODING 1 +#define HAVE_RB_USASCII_ENCINDEX 1 +#define HAVE_RB_UTF8_ENCODING 1 +#define HAVE_RB_UTF8_ENCINDEX 1 +#define HAVE_RB_LOCALE_ENCODING 1 +#define HAVE_RB_LOCALE_ENCINDEX 1 +#define HAVE_RB_FILESYSTEM_ENCODING 1 +#define HAVE_RB_FILESYSTEM_ENCINDEX 1 + +#define HAVE_RB_DEFAULT_INTERNAL_ENCODING 1 +#define HAVE_RB_DEFAULT_EXTERNAL_ENCODING 1 + +#define HAVE_RB_ENCDB_ALIAS 1 +#define HAVE_RB_ENC_ASSOCIATE 1 +#define HAVE_RB_ENC_ASSOCIATE_INDEX 1 +#define HAVE_RB_ENC_CODEPOINT_LEN 1 +#define HAVE_RB_ENC_COMPATIBLE 1 +#define HAVE_RB_ENC_COPY 1 +#define HAVE_RB_ENC_FIND 1 +#define HAVE_RB_ENC_FIND_INDEX 1 +#define HAVE_RB_ENC_FROM_ENCODING 1 +#define HAVE_RB_ENC_FROM_INDEX 1 +#define HAVE_RB_ENC_GET 1 +#define HAVE_RB_ENC_GET_INDEX 1 +#define HAVE_RB_ENC_SET_INDEX 1 +#define HAVE_RB_ENC_STR_CODERANGE 1 +#define HAVE_RB_ENC_STR_NEW 1 +#define HAVE_RB_ENC_TO_INDEX 1 +#define HAVE_RB_OBJ_ENCODING 1 + +#define HAVE_RB_STR_ENCODE 1 +#define HAVE_RB_STR_NEW_CSTR 1 +#define HAVE_RB_USASCII_STR_NEW 1 +#define HAVE_RB_USASCII_STR_NEW_CSTR 1 +#define HAVE_RB_EXTERNAL_STR_NEW 1 +#define HAVE_RB_EXTERNAL_STR_NEW_CSTR 1 +#define HAVE_RB_EXTERNAL_STR_NEW_WITH_ENC 1 + +#define HAVE_RB_TO_ENCODING 1 +#define HAVE_RB_TO_ENCODING_INDEX 1 +#define HAVE_RB_ENC_NTH 1 + +#define HAVE_RB_EENCCOMPATERROR 1 + +#define HAVE_RB_MWAITREADABLE 1 +#define HAVE_RB_MWAITWRITABLE 1 + +#define HAVE_RSTRING_LENINT 1 +#define HAVE_TIMET2NUM 1 + +#define HAVE_RB_LONG2INT 1 +#define HAVE_RB_INTERN3 1 + +#define HAVE_RB_ITER_BREAK 1 +#define HAVE_RB_SOURCEFILE 1 +#define HAVE_RB_SOURCELINE 1 +#define HAVE_RB_METHOD_BOUNDP 1 + +/* Enumerable */ +#define HAVE_RB_ENUMERATORIZE 1 + +/* Exception */ +#define HAVE_RB_EXC_NEW 1 +#define HAVE_RB_EXC_NEW2 1 +#define HAVE_RB_EXC_NEW3 1 +#define HAVE_RB_EXC_RAISE 1 +#define HAVE_RB_SET_ERRINFO 1 + +/* File */ +#define HAVE_RB_FILE_OPEN 1 +#define HAVE_RB_FILE_OPEN_STR 1 +#define HAVE_FILEPATHVALUE 1 + +/* Float */ +#define HAVE_RB_FLOAT_NEW 1 +#define HAVE_RB_RFLOAT 1 +#define HAVE_RFLOAT_VALUE 1 + +/* Globals */ +#define HAVE_RB_DEFAULT_RS 1 +#define HAVE_RB_DEFINE_HOOKED_VARIABLE 1 +#define HAVE_RB_DEFINE_READONLY_VARIABLE 1 +#define HAVE_RB_DEFINE_VARIABLE 1 +#define HAVE_RB_F_GLOBAL_VARIABLES 1 +#define HAVE_RB_GV_GET 1 +#define HAVE_RB_GV_SET 1 +#define HAVE_RB_RS 1 +#define HAVE_RB_OUTPUT_RS 1 +#define HAVE_RB_OUTPUT_FS 1 +#define HAVE_RB_STDERR 1 +#define HAVE_RB_STDIN 1 +#define HAVE_RB_STDOUT 1 +#define HAVE_RB_DEFOUT 1 + +#define HAVE_RB_LASTLINE_SET 1 +#define HAVE_RB_LASTLINE_GET 1 + +/* Hash */ +#define HAVE_RB_HASH 1 +#define HAVE_RB_HASH2 1 +#define HAVE_RB_HASH_DUP 1 +#define HAVE_RB_HASH_FREEZE 1 +#define HAVE_RB_HASH_AREF 1 +#define HAVE_RB_HASH_ASET 1 +#define HAVE_RB_HASH_CLEAR 1 +#define HAVE_RB_HASH_DELETE 1 +#define HAVE_RB_HASH_DELETE_IF 1 +#define HAVE_RB_HASH_FOREACH 1 +#define HAVE_RB_HASH_LOOKUP 1 +#define HAVE_RB_HASH_LOOKUP2 1 +#define HAVE_RB_HASH_NEW 1 +#define HAVE_RB_HASH_SET_IFNONE 1 +#define HAVE_RB_HASH_SIZE 1 + +/* Integer */ +#define HAVE_RB_INTEGER_PACK 1 + +/* IO */ +#define HAVE_GET_OPEN_FILE 1 +#define HAVE_RB_IO_ADDSTR 1 +#define HAVE_RB_IO_CHECK_IO 1 +#define HAVE_RB_IO_CHECK_CLOSED 1 +#define HAVE_RB_IO_TAINT_CHECK 1 +#define HAVE_RB_IO_CHECK_READABLE 1 +#define HAVE_RB_IO_CHECK_WRITABLE 1 +#define HAVE_RB_IO_CLOSE 1 +#define HAVE_RB_IO_PRINT 1 +#define HAVE_RB_IO_PRINTF 1 +#define HAVE_RB_IO_PUTS 1 +#define HAVE_RB_IO_WAIT_READABLE 1 +#define HAVE_RB_IO_WAIT_WRITABLE 1 +#define HAVE_RB_IO_WRITE 1 +#define HAVE_RB_IO_BINMODE 1 + +#define HAVE_RB_THREAD_FD_WRITABLE 1 +#define HAVE_RB_THREAD_WAIT_FD 1 + +#define HAVE_RB_MUTEX_NEW 1 +#define HAVE_RB_MUTEX_LOCKED_P 1 +#define HAVE_RB_MUTEX_TRYLOCK 1 +#define HAVE_RB_MUTEX_LOCK 1 +#define HAVE_RB_MUTEX_UNLOCK 1 +#define HAVE_RB_MUTEX_SLEEP 1 +#define HAVE_RB_MUTEX_SYNCHRONIZE 1 + +#define HAVE_RB_FD_FIX_CLOEXEC 1 +#define HAVE_RB_CLOEXEC_OPEN 1 + +/* Kernel */ +#define HAVE_RB_BLOCK_GIVEN_P 1 +#define HAVE_RB_BLOCK_PROC 1 +#define HAVE_RB_BLOCK_CALL 1 +#define HAVE_RB_ENSURE 1 +#define HAVE_RB_EVAL_STRING 1 +#define HAVE_RB_EXEC_RECURSIVE 1 +#define HAVE_RB_F_SPRINTF 1 +#define HAVE_RB_NEED_BLOCK 1 +#define HAVE_RB_RAISE 1 +#define HAVE_RB_RESCUE 1 +#define HAVE_RB_RESCUE2 1 +#define HAVE_RB_SET_END_PROC 1 +#define HAVE_RB_SYS_FAIL 1 +#define HAVE_RB_SYSERR_FAIL 1 +#define HAVE_RB_MAKE_BACKTRACE 1 +#define HAVE_RB_THROW 1 +#define HAVE_RB_CATCH 1 +#define HAVE_RB_THROW_OBJ 1 +#define HAVE_RB_CATCH_OBJ 1 +#define HAVE_RB_WARN 1 +#define HAVE_RB_YIELD 1 +#define HAVE_RB_YIELD_SPLAT 1 +#define HAVE_RB_YIELD_VALUES 1 +#define HAVE_RB_FUNCALL3 1 +#define HAVE_RB_FUNCALL_WITH_BLOCK 1 + +/* GC */ +#define HAVE_RB_GC_REGISTER_ADDRESS 1 +#define HAVE_RB_GC_ENABLE 1 +#define HAVE_RB_GC_DISABLE 1 + +/* Marshal */ +#define HAVE_RB_MARSHAL_DUMP 1 +#define HAVE_RB_MARSHAL_LOAD 1 + +/* Module */ +#define HAVE_RB_ALIAS 1 +#define HAVE_RB_CONST_DEFINED 1 +#define HAVE_RB_CONST_DEFINED_AT 1 +#define HAVE_RB_CONST_GET 1 +#define HAVE_RB_CONST_GET_AT 1 +#define HAVE_RB_CONST_GET_FROM 1 +#define HAVE_RB_CONST_SET 1 +#define HAVE_RB_DEFINE_ALIAS 1 +#define HAVE_RB_DEFINE_CLASS 1 +#define HAVE_RB_DEFINE_CLASS_UNDER 1 +#define HAVE_RB_DEFINE_CLASS_ID_UNDER 1 +#define HAVE_RB_DEFINE_CONST 1 +#define HAVE_RB_DEFINE_GLOBAL_CONST 1 +#define HAVE_RB_DEFINE_GLOBAL_FUNCTION 1 +#define HAVE_RB_DEFINE_METHOD 1 +#define HAVE_RB_DEFINE_MODULE_FUNCTION 1 +#define HAVE_RB_DEFINE_MODULE 1 +#define HAVE_RB_DEFINE_MODULE_UNDER 1 +#define HAVE_RB_DEFINE_PRIVATE_METHOD 1 +#define HAVE_RB_DEFINE_PROTECTED_METHOD 1 +#define HAVE_RB_DEFINE_SINGLETON_METHOD 1 +#define HAVE_RB_UNDEF 1 +#define HAVE_RB_UNDEF_METHOD 1 + +/* Numeric */ +#define HAVE_NUM2CHR 1 +#define HAVE_RB_CMPINT 1 +#define HAVE_RB_INT2INUM 1 +#define HAVE_RB_INTEGER 1 +#define HAVE_RB_LL2INUM 1 +#define HAVE_RB_NUM2DBL 1 +#if SIZEOF_INT < SIZEOF_LONG +#define HAVE_RB_NUM2INT 1 +#define HAVE_RB_NUM2UINT 1 +#endif +#define HAVE_RB_NUM2LONG 1 +#define HAVE_RB_INT2NUM 1 +#define HAVE_RB_NUM2ULONG 1 +#define HAVE_RB_NUM_COERCE_BIN 1 +#define HAVE_RB_NUM_COERCE_CMP 1 +#define HAVE_RB_NUM_COERCE_RELOP 1 +#define HAVE_RB_NUM_ZERODIV 1 + +/* Fixnum */ +#if SIZEOF_INT < SIZEOF_LONG +#define HAVE_RB_FIX2UINT 1 +#define HAVE_RB_FIX2INT 1 +#endif + +/* Object */ +#define HAVE_OBJ_TAINT 1 +#define HAVE_OBJ_TAINTED 1 +#define HAVE_OBJ_INFECT 1 +#define HAVE_RB_ANY_TO_S 1 +#define HAVE_RB_ATTR_GET 1 +#define HAVE_RB_OBJ_INSTANCE_VARIABLES 1 +#define HAVE_RB_CHECK_ARRAY_TYPE 1 +#define HAVE_RB_CHECK_CONVERT_TYPE 1 +#define HAVE_RB_CHECK_TO_INTEGER 1 +#define HAVE_RB_CHECK_FROZEN 1 +#define HAVE_RB_CHECK_STRING_TYPE 1 +#define HAVE_RB_CLASS_OF 1 +#define HAVE_RB_CONVERT_TYPE 1 +#define HAVE_RB_EQUAL 1 +#define HAVE_RB_CLASS_INHERITED_P 1 +#define HAVE_RB_EXTEND_OBJECT 1 +#define HAVE_RB_INSPECT 1 +#define HAVE_RB_IVAR_DEFINED 1 +#define HAVE_RB_IVAR_GET 1 +#define HAVE_RB_IVAR_SET 1 +#define HAVE_RB_IV_GET 1 +#define HAVE_RB_IV_SET 1 +#define HAVE_RB_OBJ_ALLOC 1 +#define HAVE_RB_OBJ_CALL_INIT 1 +#define HAVE_RB_OBJ_CLASSNAME 1 +#define HAVE_RB_OBJ_DUP 1 +#define HAVE_RB_OBJ_FREEZE 1 +#define HAVE_RB_OBJ_FROZEN_P 1 +#define HAVE_RB_OBJ_ID 1 +#define HAVE_RB_OBJ_INSTANCE_EVAL 1 +#define HAVE_RB_OBJ_IS_INSTANCE_OF 1 +#define HAVE_RB_OBJ_IS_KIND_OF 1 +#define HAVE_RB_OBJ_TAINT 1 +#define HAVE_RB_OBJ_METHOD 1 +#define HAVE_RB_OBJ_METHOD_ARITY 1 +#define HAVE_RB_REQUIRE 1 +#define HAVE_RB_RESPOND_TO 1 +#define HAVE_RB_OBJ_RESPOND_TO 1 +#define HAVE_RB_SPECIAL_CONST_P 1 +#define HAVE_RB_TO_ID 1 +#define HAVE_RB_TO_INT 1 +#define HAVE_RTEST 1 +#define HAVE_TYPE 1 +#define HAVE_RB_TYPE_P 1 +#define HAVE_BUILTIN_TYPE 1 + +/* Proc */ +#define HAVE_RB_PROC_NEW 1 + +/* Range */ +#define HAVE_RB_RANGE_NEW 1 +#define HAVE_RB_RANGE_VALUES 1 + +/* Rational */ +#define HAVE_RB_RATIONAL 1 +#define HAVE_RB_RATIONAL1 1 +#define HAVE_RB_RATIONAL2 1 +#define HAVE_RB_RATIONAL_NEW 1 +#define HAVE_RB_RATIONAL_NEW1 1 +#define HAVE_RB_RATIONAL_NEW2 1 +#define HAVE_RB_RATIONAL_NUM 1 +#define HAVE_RB_RATIONAL_DEN 1 + +/* Regexp */ +#define HAVE_RB_BACKREF_GET 1 +#define HAVE_RB_REG_MATCH 1 +#define HAVE_RB_REG_NEW 1 +#define HAVE_RB_REG_NTH_MATCH 1 +#define HAVE_RB_REG_OPTIONS 1 +#define HAVE_RB_REG_REGCOMP 1 + +/* String */ +#define HAVE_RB_CSTR2INUM 1 +#define HAVE_RB_CSTR_TO_INUM 1 +#define HAVE_RB_STR2INUM 1 +#define HAVE_RB_STR_APPEND 1 +#define HAVE_RB_STR_BUF_CAT 1 +#define HAVE_RB_STR_BUF_NEW 1 +#define HAVE_RB_STR_BUF_NEW2 1 +#define HAVE_RB_STR_CAT 1 +#define HAVE_RB_STR_CAT2 1 +#define HAVE_RB_STR_CMP 1 +#define HAVE_RB_STR_DUP 1 +#define HAVE_RB_STR_FLUSH 1 +#define HAVE_RB_STR_FREEZE 1 +#define HAVE_RB_STR_HASH 1 +#define HAVE_RB_STR_UPDATE 1 +#define HAVE_RB_STR_INSPECT 1 +#define HAVE_RB_STR_INTERN 1 +#define HAVE_RB_STR_NEW 1 +#define HAVE_RB_STR_NEW2 1 +#define HAVE_RB_STR_NEW3 1 +#define HAVE_RB_STR_NEW4 1 +#define HAVE_RB_STR_NEW5 1 +#define HAVE_RB_STR_PLUS 1 +#define HAVE_RB_STR_TIMES 1 +#define HAVE_RB_STR_RESIZE 1 +#define HAVE_RB_STR_SET_LEN 1 +#define HAVE_RB_STR_SPLIT 1 +#define HAVE_RB_STR_SUBSTR 1 +#define HAVE_RB_STR_TO_STR 1 +#define HAVE_RSTRING_LEN 1 +#define HAVE_RSTRING_PTR 1 +#define HAVE_STRINGVALUE 1 + +#define HAVE_RB_STR_FREE 1 +#define HAVE_RB_SPRINTF 1 +#define HAVE_RB_LOCALE_STR_NEW 1 +#define HAVE_RB_LOCALE_STR_NEW_CSTR 1 +#define HAVE_RB_STR_CONV_ENC 1 +#define HAVE_RB_STR_CONV_ENC_OPTS 1 +#define HAVE_RB_STR_EXPORT 1 +#define HAVE_RB_STR_EXPORT_LOCALE 1 +#define HAVE_RB_STR_LENGTH 1 +#define HAVE_RB_STR_EQUAL 1 +#define HAVE_RB_STR_SUBSEQ 1 +#define HAVE_RB_VSPRINTF 1 +#define HAVE_RB_STRING 1 + +/* Struct */ +#define HAVE_RB_STRUCT_AREF 1 +#define HAVE_RB_STRUCT_ASET 1 +#define HAVE_RB_STRUCT_DEFINE 1 +#define HAVE_RB_STRUCT_DEFINE_UNDER 1 +#define HAVE_RB_STRUCT_NEW 1 +#define HAVE_RB_STRUCT_GETMEMBER 1 +#define HAVE_RB_STRUCT_S_MEMBERS 1 +#define HAVE_RB_STRUCT_MEMBERS 1 +#ifdef RUBY_VERSION_IS_2_4 +#define HAVE_RB_STRUCT_SIZE 1 +#endif + +/* Symbol */ +#define HAVE_RB_ID2NAME 1 +#define HAVE_RB_ID2STR 1 +#define HAVE_RB_INTERN_STR 1 +#define HAVE_RB_INTERN 1 +#define HAVE_RB_IS_CLASS_ID 1 +#define HAVE_RB_IS_CONST_ID 1 +#define HAVE_RB_IS_INSTANCE_ID 1 +#define HAVE_RB_SYM2STR 1 + +/* Thread */ +#define HAVE_RB_THREAD_ALONE 1 +#define HAVE_RB_THREAD_CALL_WITHOUT_GVL 1 +#define HAVE_RB_THREAD_CURRENT 1 +#define HAVE_RB_THREAD_LOCAL_AREF 1 +#define HAVE_RB_THREAD_LOCAL_ASET 1 +#define HAVE_RB_THREAD_WAIT_FOR 1 +#define HAVE_RB_THREAD_WAKEUP 1 +#define HAVE_RB_THREAD_CREATE 1 + +/* Time */ +#define HAVE_RB_TIME_NEW 1 +#define HAVE_RB_TIME_NANO_NEW 1 +#define HAVE_RB_TIME_NUM_NEW 1 +#define HAVE_RB_TIME_INTERVAL 1 +#define HAVE_RB_TIME_TIMEVAL 1 +#define HAVE_RB_TIME_TIMESPEC 1 +#ifdef RUBY_VERSION_IS_2_3 +#define HAVE_RB_TIMESPEC_NOW 1 +#define HAVE_RB_TIME_TIMESPEC_NEW 1 +#endif + +/* Util */ +#define HAVE_RB_SCAN_ARGS 1 + +/* Now, create the differential set. The format of the preprocessor directives + * is significant. The alternative implementations should define RUBY because + * some extensions depend on that. But only one alternative implementation + * macro should be defined at a time. The conditional is structured so that if + * no alternative implementation is defined then MRI is assumed. + */ + +#if defined(RUBINIUS) +#include "rubinius.h" +#elif defined(JRUBY) +#include "jruby.h" +#elif defined(TRUFFLERUBY) +#include "truffleruby.h" +#endif + +#endif diff --git a/spec/rubyspec/optional/capi/ext/string_spec.c b/spec/rubyspec/optional/capi/ext/string_spec.c new file mode 100644 index 0000000000..8d579f918f --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/string_spec.c @@ -0,0 +1,664 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include +#include + +#ifdef HAVE_RUBY_ENCODING_H +#include "ruby/encoding.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_CSTR2INUM +VALUE string_spec_rb_cstr2inum(VALUE self, VALUE str, VALUE inum) { + int num = FIX2INT(inum); + return rb_cstr2inum(RSTRING_PTR(str), num); +} +#endif + +#ifdef HAVE_RB_CSTR_TO_INUM +static VALUE string_spec_rb_cstr_to_inum(VALUE self, VALUE str, VALUE inum, VALUE badcheck) { + int num = FIX2INT(inum); + return rb_cstr_to_inum(RSTRING_PTR(str), num, RTEST(badcheck)); +} +#endif + +#ifdef HAVE_RB_STR2INUM +VALUE string_spec_rb_str2inum(VALUE self, VALUE str, VALUE inum) { + int num = FIX2INT(inum); + return rb_str2inum(str, num); +} +#endif + +#ifdef HAVE_RB_STR_APPEND +VALUE string_spec_rb_str_append(VALUE self, VALUE str, VALUE str2) { + return rb_str_append(str, str2); +} +#endif + +#ifdef HAVE_RB_STR_SET_LEN +VALUE string_spec_rb_str_set_len(VALUE self, VALUE str, VALUE len) { + rb_str_set_len(str, NUM2LONG(len)); + + return str; +} + +VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) { + rb_str_set_len(str, NUM2LONG(len)); + + return INT2FIX(RSTRING_LEN(str)); +} +#endif + +#ifdef HAVE_RB_STR_BUF_NEW +VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) { + VALUE buf; + + buf = rb_str_buf_new(NUM2LONG(len)); + + if(RTEST(str)) { + snprintf(RSTRING_PTR(buf), NUM2LONG(len), "%s", RSTRING_PTR(str)); + } + + return buf; +} +#endif + +#ifdef HAVE_RB_STR_BUF_NEW2 +VALUE string_spec_rb_str_buf_new2(VALUE self) { + return rb_str_buf_new2("hello\0invisible"); +} +#endif + +#ifdef HAVE_RB_STR_BUF_CAT +VALUE string_spec_rb_str_buf_cat(VALUE self, VALUE str) { + const char *question_mark = "?"; + rb_str_buf_cat(str, question_mark, strlen(question_mark)); + return str; +} +#endif + +#ifdef HAVE_RB_STR_CAT +VALUE string_spec_rb_str_cat(VALUE self, VALUE str) { + return rb_str_cat(str, "?", 1); +} +#endif + +#ifdef HAVE_RB_STR_CAT2 +VALUE string_spec_rb_str_cat2(VALUE self, VALUE str) { + return rb_str_cat2(str, "?"); +} +#endif + +#ifdef HAVE_RB_STR_CMP +VALUE string_spec_rb_str_cmp(VALUE self, VALUE str1, VALUE str2) { + return INT2NUM(rb_str_cmp(str1, str2)); +} +#endif + +#ifdef HAVE_RB_STR_CONV_ENC +VALUE string_spec_rb_str_conv_enc(VALUE self, VALUE str, VALUE from, VALUE to) { + rb_encoding* from_enc; + rb_encoding* to_enc; + + from_enc = rb_to_encoding(from); + + if(NIL_P(to)) { + to_enc = 0; + } else { + to_enc = rb_to_encoding(to); + } + + return rb_str_conv_enc(str, from_enc, to_enc); +} +#endif + +#ifdef HAVE_RB_STR_CONV_ENC_OPTS +VALUE string_spec_rb_str_conv_enc_opts(VALUE self, VALUE str, VALUE from, VALUE to, + VALUE ecflags, VALUE ecopts) +{ + rb_encoding* from_enc; + rb_encoding* to_enc; + + from_enc = rb_to_encoding(from); + + if(NIL_P(to)) { + to_enc = 0; + } else { + to_enc = rb_to_encoding(to); + } + + return rb_str_conv_enc_opts(str, from_enc, to_enc, FIX2INT(ecflags), ecopts); +} +#endif + +#ifdef HAVE_RB_STR_EXPORT +VALUE string_spec_rb_str_export(VALUE self, VALUE str) { + return rb_str_export(str); +} +#endif + +#ifdef HAVE_RB_STR_EXPORT_LOCALE +VALUE string_spec_rb_str_export_locale(VALUE self, VALUE str) { + return rb_str_export_locale(str); +} +#endif + +#ifdef HAVE_RB_STR_DUP +VALUE string_spec_rb_str_dup(VALUE self, VALUE str) { + return rb_str_dup(str); +} +#endif + +#ifdef HAVE_RB_STR_FREEZE +VALUE string_spec_rb_str_freeze(VALUE self, VALUE str) { + return rb_str_freeze(str); +} +#endif + +#ifdef HAVE_RB_STR_INSPECT +VALUE string_spec_rb_str_inspect(VALUE self, VALUE str) { + return rb_str_inspect(str); +} +#endif + +#ifdef HAVE_RB_STR_INTERN +VALUE string_spec_rb_str_intern(VALUE self, VALUE str) { + return rb_str_intern(str); +} +#endif + +#ifdef HAVE_RB_STR_LENGTH +VALUE string_spec_rb_str_length(VALUE self, VALUE str) { + return rb_str_length(str); +} +#endif + +#ifdef HAVE_RB_STR_NEW +VALUE string_spec_rb_str_new(VALUE self, VALUE str, VALUE len) { + return rb_str_new(RSTRING_PTR(str), FIX2INT(len)); +} +#endif + +#ifdef HAVE_RB_STR_NEW2 +VALUE string_spec_rb_str_new2(VALUE self, VALUE str) { + if(NIL_P(str)) { + return rb_str_new2(""); + } else { + return rb_str_new2(RSTRING_PTR(str)); + } +} +#endif + +#ifdef HAVE_RB_STR_ENCODE +VALUE string_spec_rb_str_encode(VALUE self, VALUE str, VALUE enc, VALUE flags, VALUE opts) { + return rb_str_encode(str, enc, FIX2INT(flags), opts); +} +#endif + +#ifdef HAVE_RB_STR_NEW_CSTR +VALUE string_spec_rb_str_new_cstr(VALUE self, VALUE str) { + if(NIL_P(str)) { + return rb_str_new_cstr(""); + } else { + return rb_str_new_cstr(RSTRING_PTR(str)); + } +} +#endif + +#ifdef HAVE_RB_EXTERNAL_STR_NEW +VALUE string_spec_rb_external_str_new(VALUE self, VALUE str) { + return rb_external_str_new(RSTRING_PTR(str), RSTRING_LEN(str)); +} +#endif + +#ifdef HAVE_RB_EXTERNAL_STR_NEW_CSTR +VALUE string_spec_rb_external_str_new_cstr(VALUE self, VALUE str) { + return rb_external_str_new_cstr(RSTRING_PTR(str)); +} +#endif + +#ifdef HAVE_RB_EXTERNAL_STR_NEW_WITH_ENC +VALUE string_spec_rb_external_str_new_with_enc(VALUE self, VALUE str, VALUE len, VALUE encoding) { + return rb_external_str_new_with_enc(RSTRING_PTR(str), FIX2LONG(len), rb_to_encoding(encoding)); +} +#endif + +#ifdef HAVE_RB_LOCALE_STR_NEW +VALUE string_spec_rb_locale_str_new(VALUE self, VALUE str, VALUE len) { + return rb_locale_str_new(RSTRING_PTR(str), FIX2INT(len)); +} +#endif + +#ifdef HAVE_RB_LOCALE_STR_NEW_CSTR +VALUE string_spec_rb_locale_str_new_cstr(VALUE self, VALUE str) { + return rb_locale_str_new_cstr(RSTRING_PTR(str)); +} +#endif + +#ifdef HAVE_RB_STR_NEW3 +VALUE string_spec_rb_str_new3(VALUE self, VALUE str) { + return rb_str_new3(str); +} +#endif + +#ifdef HAVE_RB_STR_NEW4 +VALUE string_spec_rb_str_new4(VALUE self, VALUE str) { + return rb_str_new4(str); +} +#endif + +#ifdef HAVE_RB_STR_NEW5 +VALUE string_spec_rb_str_new5(VALUE self, VALUE str, VALUE ptr, VALUE len) { + return rb_str_new5(str, RSTRING_PTR(ptr), FIX2INT(len)); +} +#endif + +#ifdef HAVE_RB_STR_PLUS +VALUE string_spec_rb_str_plus(VALUE self, VALUE str1, VALUE str2) { + return rb_str_plus(str1, str2); +} +#endif + +#ifdef HAVE_RB_STR_TIMES +VALUE string_spec_rb_str_times(VALUE self, VALUE str, VALUE times) { + return rb_str_times(str, times); +} +#endif + +#ifdef HAVE_RB_STR_RESIZE +VALUE string_spec_rb_str_resize(VALUE self, VALUE str, VALUE size) { + return rb_str_resize(str, FIX2INT(size)); +} + +VALUE string_spec_rb_str_resize_RSTRING_LEN(VALUE self, VALUE str, VALUE size) { + VALUE modified = rb_str_resize(str, FIX2INT(size)); + return INT2FIX(RSTRING_LEN(modified)); +} +#endif + +#ifdef HAVE_RB_STR_SPLIT +VALUE string_spec_rb_str_split(VALUE self, VALUE str) { + return rb_str_split(str, ","); +} +#endif + +#ifdef HAVE_RB_STR_SUBSEQ +VALUE string_spec_rb_str_subseq(VALUE self, VALUE str, VALUE beg, VALUE len) { + return rb_str_subseq(str, FIX2INT(beg), FIX2INT(len)); +} +#endif + +#ifdef HAVE_RB_STR_SUBSTR +VALUE string_spec_rb_str_substr(VALUE self, VALUE str, VALUE beg, VALUE len) { + return rb_str_substr(str, FIX2INT(beg), FIX2INT(len)); +} +#endif + +#ifdef HAVE_RB_STR_TO_STR +VALUE string_spec_rb_str_to_str(VALUE self, VALUE arg) { + return rb_str_to_str(arg); +} +#endif + +#ifdef HAVE_RSTRING_LEN +VALUE string_spec_RSTRING_LEN(VALUE self, VALUE str) { + return INT2FIX(RSTRING_LEN(str)); +} +#endif + +#ifdef HAVE_RSTRING_LENINT +VALUE string_spec_RSTRING_LENINT(VALUE self, VALUE str) { + return INT2FIX(RSTRING_LENINT(str)); +} +#endif + +#ifdef HAVE_RSTRING_PTR +VALUE string_spec_RSTRING_PTR_iterate(VALUE self, VALUE str) { + int i; + char* ptr; + + ptr = RSTRING_PTR(str); + for(i = 0; i < RSTRING_LEN(str); i++) { + rb_yield(CHR2FIX(ptr[i])); + } + return Qnil; +} + +VALUE string_spec_RSTRING_PTR_assign(VALUE self, VALUE str, VALUE chr) { + int i; + char c; + char* ptr; + + ptr = RSTRING_PTR(str); + c = FIX2INT(chr); + + for(i = 0; i < RSTRING_LEN(str); i++) { + ptr[i] = c; + } + return Qnil; +} + +VALUE string_spec_RSTRING_PTR_after_funcall(VALUE self, VALUE str, VALUE cb) { + /* Silence gcc 4.3.2 warning about computed value not used */ + if(RSTRING_PTR(str)) { /* force it out */ + rb_funcall(cb, rb_intern("call"), 1, str); + } + + return rb_str_new2(RSTRING_PTR(str)); +} +#endif + +#ifdef HAVE_STRINGVALUE +VALUE string_spec_StringValue(VALUE self, VALUE str) { + return StringValue(str); +} +#endif + +#ifdef HAVE_RB_STR_HASH +static VALUE string_spec_rb_str_hash(VALUE self, VALUE str) { + st_index_t val = rb_str_hash(str); + +#if SIZEOF_LONG == SIZEOF_VOIDP + return LONG2FIX((long)val); +#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP + return LL2NUM((LONG_LONG)val); +#else +# error unsupported platform +#endif +} +#endif + +#ifdef HAVE_RB_STR_UPDATE +static VALUE string_spec_rb_str_update(VALUE self, VALUE str, VALUE beg, VALUE end, VALUE replacement) { + rb_str_update(str, FIX2LONG(beg), FIX2LONG(end), replacement); + return str; +} +#endif + +#ifdef HAVE_RB_STR_FREE +static VALUE string_spec_rb_str_free(VALUE self, VALUE str) { + rb_str_free(str); + return Qnil; +} +#endif + +#ifdef HAVE_RB_SPRINTF +static VALUE string_spec_rb_sprintf1(VALUE self, VALUE str, VALUE repl) { + return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl)); +} +static VALUE string_spec_rb_sprintf2(VALUE self, VALUE str, VALUE repl1, VALUE repl2) { + return rb_sprintf(RSTRING_PTR(str), RSTRING_PTR(repl1), RSTRING_PTR(repl2)); +} +#endif + +#ifdef HAVE_RB_VSPRINTF +static VALUE string_spec_rb_vsprintf_worker(char* fmt, ...) { + va_list varargs; + VALUE str; + + va_start(varargs, fmt); + str = rb_vsprintf(fmt, varargs); + va_end(varargs); + + return str; +} + +static VALUE string_spec_rb_vsprintf(VALUE self, VALUE fmt, VALUE str, VALUE i, VALUE f) { + return string_spec_rb_vsprintf_worker(RSTRING_PTR(fmt), RSTRING_PTR(str), + FIX2INT(i), RFLOAT_VALUE(f)); +} +#endif + +#ifdef HAVE_RB_STR_EQUAL +VALUE string_spec_rb_str_equal(VALUE self, VALUE str1, VALUE str2) { + return rb_str_equal(str1, str2); +} +#endif + +#ifdef HAVE_RB_USASCII_STR_NEW +static VALUE string_spec_rb_usascii_str_new(VALUE self, VALUE str, VALUE len) { + return rb_usascii_str_new(RSTRING_PTR(str), NUM2INT(len)); +} +#endif + +#ifdef HAVE_RB_USASCII_STR_NEW_CSTR +static VALUE string_spec_rb_usascii_str_new_cstr(VALUE self, VALUE str) { + return rb_usascii_str_new_cstr(RSTRING_PTR(str)); +} +#endif + +#ifdef HAVE_RB_STRING +static VALUE string_spec_rb_String(VALUE self, VALUE val) { + return rb_String(val); +} +#endif + +void Init_string_spec(void) { + VALUE cls; + cls = rb_define_class("CApiStringSpecs", rb_cObject); + +#ifdef HAVE_RB_CSTR2INUM + rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2); +#endif + +#ifdef HAVE_RB_CSTR_TO_INUM + rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3); +#endif + +#ifdef HAVE_RB_STR2INUM + rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2); +#endif + +#ifdef HAVE_RB_STR_APPEND + rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2); +#endif + +#ifdef HAVE_RB_STR_BUF_NEW + rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2); +#endif + +#ifdef HAVE_RB_STR_BUF_NEW2 + rb_define_method(cls, "rb_str_buf_new2", string_spec_rb_str_buf_new2, 0); +#endif + +#ifdef HAVE_RB_STR_BUF_CAT + rb_define_method(cls, "rb_str_buf_cat", string_spec_rb_str_buf_cat, 1); +#endif + +#ifdef HAVE_RB_STR_CAT + rb_define_method(cls, "rb_str_cat", string_spec_rb_str_cat, 1); +#endif + +#ifdef HAVE_RB_STR_CAT2 + rb_define_method(cls, "rb_str_cat2", string_spec_rb_str_cat2, 1); +#endif + +#ifdef HAVE_RB_STR_CMP + rb_define_method(cls, "rb_str_cmp", string_spec_rb_str_cmp, 2); +#endif + +#ifdef HAVE_RB_STR_CONV_ENC + rb_define_method(cls, "rb_str_conv_enc", string_spec_rb_str_conv_enc, 3); +#endif + +#ifdef HAVE_RB_STR_CONV_ENC_OPTS + rb_define_method(cls, "rb_str_conv_enc_opts", string_spec_rb_str_conv_enc_opts, 5); +#endif + +#ifdef HAVE_RB_STR_EXPORT + rb_define_method(cls, "rb_str_export", string_spec_rb_str_export, 1); +#endif + +#ifdef HAVE_RB_STR_EXPORT_LOCALE + rb_define_method(cls, "rb_str_export_locale", string_spec_rb_str_export_locale, 1); +#endif + +#ifdef HAVE_RB_STR_DUP + rb_define_method(cls, "rb_str_dup", string_spec_rb_str_dup, 1); +#endif + +#ifdef HAVE_RB_STR_FREEZE + rb_define_method(cls, "rb_str_freeze", string_spec_rb_str_freeze, 1); +#endif + +#ifdef HAVE_RB_STR_INSPECT + rb_define_method(cls, "rb_str_inspect", string_spec_rb_str_inspect, 1); +#endif + +#ifdef HAVE_RB_STR_INTERN + rb_define_method(cls, "rb_str_intern", string_spec_rb_str_intern, 1); +#endif + +#ifdef HAVE_RB_STR_LENGTH + rb_define_method(cls, "rb_str_length", string_spec_rb_str_length, 1); +#endif + +#ifdef HAVE_RB_STR_NEW + rb_define_method(cls, "rb_str_new", string_spec_rb_str_new, 2); +#endif + +#ifdef HAVE_RB_STR_NEW2 + rb_define_method(cls, "rb_str_new2", string_spec_rb_str_new2, 1); +#endif + +#ifdef HAVE_RB_STR_ENCODE + rb_define_method(cls, "rb_str_encode", string_spec_rb_str_encode, 4); +#endif + +#ifdef HAVE_RB_STR_NEW_CSTR + rb_define_method(cls, "rb_str_new_cstr", string_spec_rb_str_new_cstr, 1); +#endif + +#ifdef HAVE_RB_EXTERNAL_STR_NEW + rb_define_method(cls, "rb_external_str_new", string_spec_rb_external_str_new, 1); +#endif + +#ifdef HAVE_RB_EXTERNAL_STR_NEW_CSTR + rb_define_method(cls, "rb_external_str_new_cstr", + string_spec_rb_external_str_new_cstr, 1); +#endif + +#ifdef HAVE_RB_EXTERNAL_STR_NEW_WITH_ENC + rb_define_method(cls, "rb_external_str_new_with_enc", string_spec_rb_external_str_new_with_enc, 3); +#endif + +#ifdef HAVE_RB_LOCALE_STR_NEW + rb_define_method(cls, "rb_locale_str_new", string_spec_rb_locale_str_new, 2); +#endif + +#ifdef HAVE_RB_LOCALE_STR_NEW_CSTR + rb_define_method(cls, "rb_locale_str_new_cstr", string_spec_rb_locale_str_new_cstr, 1); +#endif + +#ifdef HAVE_RB_STR_NEW3 + rb_define_method(cls, "rb_str_new3", string_spec_rb_str_new3, 1); +#endif + +#ifdef HAVE_RB_STR_NEW4 + rb_define_method(cls, "rb_str_new4", string_spec_rb_str_new4, 1); +#endif + +#ifdef HAVE_RB_STR_NEW5 + rb_define_method(cls, "rb_str_new5", string_spec_rb_str_new5, 3); +#endif + +#ifdef HAVE_RB_STR_PLUS + rb_define_method(cls, "rb_str_plus", string_spec_rb_str_plus, 2); +#endif + +#ifdef HAVE_RB_STR_TIMES + rb_define_method(cls, "rb_str_times", string_spec_rb_str_times, 2); +#endif + +#ifdef HAVE_RB_STR_RESIZE + rb_define_method(cls, "rb_str_resize", string_spec_rb_str_resize, 2); + rb_define_method(cls, "rb_str_resize_RSTRING_LEN", + string_spec_rb_str_resize_RSTRING_LEN, 2); +#endif + +#ifdef HAVE_RB_STR_SET_LEN + rb_define_method(cls, "rb_str_set_len", string_spec_rb_str_set_len, 2); + rb_define_method(cls, "rb_str_set_len_RSTRING_LEN", + string_spec_rb_str_set_len_RSTRING_LEN, 2); +#endif + +#ifdef HAVE_RB_STR_SPLIT + rb_define_method(cls, "rb_str_split", string_spec_rb_str_split, 1); +#endif + +#ifdef HAVE_RB_STR_SUBSEQ + rb_define_method(cls, "rb_str_subseq", string_spec_rb_str_subseq, 3); +#endif + +#ifdef HAVE_RB_STR_SUBSTR + rb_define_method(cls, "rb_str_substr", string_spec_rb_str_substr, 3); +#endif + +#ifdef HAVE_RB_STR_TO_STR + rb_define_method(cls, "rb_str_to_str", string_spec_rb_str_to_str, 1); +#endif + +#ifdef HAVE_RSTRING_LEN + rb_define_method(cls, "RSTRING_LEN", string_spec_RSTRING_LEN, 1); +#endif + +#ifdef HAVE_RSTRING_LENINT + rb_define_method(cls, "RSTRING_LENINT", string_spec_RSTRING_LENINT, 1); +#endif + +#ifdef HAVE_RSTRING_PTR + rb_define_method(cls, "RSTRING_PTR_iterate", string_spec_RSTRING_PTR_iterate, 1); + rb_define_method(cls, "RSTRING_PTR_assign", string_spec_RSTRING_PTR_assign, 2); + rb_define_method(cls, "RSTRING_PTR_after_funcall", + string_spec_RSTRING_PTR_after_funcall, 2); +#endif + +#ifdef HAVE_STRINGVALUE + rb_define_method(cls, "StringValue", string_spec_StringValue, 1); +#endif + +#ifdef HAVE_RB_STR_HASH + rb_define_method(cls, "rb_str_hash", string_spec_rb_str_hash, 1); +#endif + +#ifdef HAVE_RB_STR_UPDATE + rb_define_method(cls, "rb_str_update", string_spec_rb_str_update, 4); +#endif + +#ifdef HAVE_RB_STR_FREE + rb_define_method(cls, "rb_str_free", string_spec_rb_str_free, 1); +#endif + +#ifdef HAVE_RB_SPRINTF + rb_define_method(cls, "rb_sprintf1", string_spec_rb_sprintf1, 2); + rb_define_method(cls, "rb_sprintf2", string_spec_rb_sprintf2, 3); +#endif + +#ifdef HAVE_RB_VSPRINTF + rb_define_method(cls, "rb_vsprintf", string_spec_rb_vsprintf, 4); +#endif + +#ifdef HAVE_RB_STR_EQUAL + rb_define_method(cls, "rb_str_equal", string_spec_rb_str_equal, 2); +#endif + +#ifdef HAVE_RB_USASCII_STR_NEW + rb_define_method(cls, "rb_usascii_str_new", string_spec_rb_usascii_str_new, 2); +#endif + +#ifdef HAVE_RB_USASCII_STR_NEW_CSTR + rb_define_method(cls, "rb_usascii_str_new_cstr", string_spec_rb_usascii_str_new_cstr, 1); +#endif + +#ifdef HAVE_RB_STRING + rb_define_method(cls, "rb_String", string_spec_rb_String, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/struct_spec.c b/spec/rubyspec/optional/capi/ext/struct_spec.c new file mode 100644 index 0000000000..8f373d9f48 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/struct_spec.c @@ -0,0 +1,131 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include "ruby/intern.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_STRUCT_AREF +static VALUE struct_spec_rb_struct_aref(VALUE self, VALUE st, VALUE key) { + return rb_struct_aref(st, key); +} +#endif + +#ifdef HAVE_RB_STRUCT_GETMEMBER +static VALUE struct_spec_rb_struct_getmember(VALUE self, VALUE st, VALUE key) { + return rb_struct_getmember(st, SYM2ID(key)); +} +#endif + +#ifdef HAVE_RB_STRUCT_S_MEMBERS +static VALUE struct_spec_rb_struct_s_members(VALUE self, VALUE klass) +{ + return rb_ary_dup(rb_struct_s_members(klass)); +} +#endif + +#ifdef HAVE_RB_STRUCT_MEMBERS +static VALUE struct_spec_rb_struct_members(VALUE self, VALUE st) +{ + return rb_ary_dup(rb_struct_members(st)); +} +#endif + +#ifdef HAVE_RB_STRUCT_ASET +static VALUE struct_spec_rb_struct_aset(VALUE self, VALUE st, VALUE key, VALUE value) { + return rb_struct_aset(st, key, value); +} +#endif + +#ifdef HAVE_RB_STRUCT_DEFINE +/* Only allow setting three attributes, should be sufficient for testing. */ +static VALUE struct_spec_struct_define(VALUE self, VALUE name, + VALUE attr1, VALUE attr2, VALUE attr3) { + + const char *a1 = StringValuePtr(attr1); + const char *a2 = StringValuePtr(attr2); + const char *a3 = StringValuePtr(attr3); + char *nm = NULL; + + if (name != Qnil) nm = StringValuePtr(name); + + return rb_struct_define(nm, a1, a2, a3, NULL); +} +#endif + +#ifdef HAVE_RB_STRUCT_DEFINE_UNDER +/* Only allow setting three attributes, should be sufficient for testing. */ +static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer, + VALUE name, VALUE attr1, VALUE attr2, VALUE attr3) { + + const char *nm = StringValuePtr(name); + const char *a1 = StringValuePtr(attr1); + const char *a2 = StringValuePtr(attr2); + const char *a3 = StringValuePtr(attr3); + + return rb_struct_define_under(outer, nm, a1, a2, a3, NULL); +} +#endif + +#ifdef HAVE_RB_STRUCT_NEW +static VALUE struct_spec_rb_struct_new(VALUE self, VALUE klass, + VALUE a, VALUE b, VALUE c) +{ + + return rb_struct_new(klass, a, b, c); +} +#endif + +#ifdef HAVE_RB_STRUCT_SIZE +static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st) +{ + return rb_struct_size(st); +} +#endif + +void Init_struct_spec(void) { + VALUE cls; + cls = rb_define_class("CApiStructSpecs", rb_cObject); + +#ifdef HAVE_RB_STRUCT_AREF + rb_define_method(cls, "rb_struct_aref", struct_spec_rb_struct_aref, 2); +#endif + +#ifdef HAVE_RB_STRUCT_GETMEMBER + rb_define_method(cls, "rb_struct_getmember", struct_spec_rb_struct_getmember, 2); +#endif + +#ifdef HAVE_RB_STRUCT_S_MEMBERS + rb_define_method(cls, "rb_struct_s_members", struct_spec_rb_struct_s_members, 1); +#endif + +#ifdef HAVE_RB_STRUCT_MEMBERS + rb_define_method(cls, "rb_struct_members", struct_spec_rb_struct_members, 1); +#endif + +#ifdef HAVE_RB_STRUCT_ASET + rb_define_method(cls, "rb_struct_aset", struct_spec_rb_struct_aset, 3); +#endif + +#ifdef HAVE_RB_STRUCT_DEFINE + rb_define_method(cls, "rb_struct_define", struct_spec_struct_define, 4); +#endif + +#ifdef HAVE_RB_STRUCT_DEFINE_UNDER + rb_define_method(cls, "rb_struct_define_under", struct_spec_struct_define_under, 5); +#endif + +#ifdef HAVE_RB_STRUCT_NEW + rb_define_method(cls, "rb_struct_new", struct_spec_rb_struct_new, 4); +#endif + +#ifdef HAVE_RB_STRUCT_SIZE + rb_define_method(cls, "rb_struct_size", struct_spec_rb_struct_size, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/symbol_spec.c b/spec/rubyspec/optional/capi/ext/symbol_spec.c new file mode 100644 index 0000000000..7ffa7cf9b1 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/symbol_spec.c @@ -0,0 +1,138 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef HAVE_RUBY_ENCODING_H +#include "ruby/encoding.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_INTERN +VALUE symbol_spec_rb_intern(VALUE self, VALUE string) { + return ID2SYM(rb_intern(RSTRING_PTR(string))); +} + +VALUE symbol_spec_rb_intern2(VALUE self, VALUE string, VALUE len) { + return ID2SYM(rb_intern2(RSTRING_PTR(string), FIX2LONG(len))); +} + +VALUE symbol_spec_rb_intern_const(VALUE self, VALUE string) { + return ID2SYM(rb_intern_const(RSTRING_PTR(string))); +} + +VALUE symbol_spec_rb_intern_c_compare(VALUE self, VALUE string, VALUE sym) { + ID symbol = rb_intern(RSTRING_PTR(string)); + return (SYM2ID(sym) == symbol) ? Qtrue : Qfalse; +} + +VALUE symbol_spec_rb_intern2_c_compare(VALUE self, VALUE string, VALUE len, VALUE sym) { + ID symbol = rb_intern2(RSTRING_PTR(string), FIX2LONG(len)); + return (SYM2ID(sym) == symbol) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_INTERN3 +VALUE symbol_spec_rb_intern3(VALUE self, VALUE string, VALUE len, VALUE enc) { + return ID2SYM(rb_intern3(RSTRING_PTR(string), FIX2LONG(len), rb_enc_get(enc))); +} + +VALUE symbol_spec_rb_intern3_c_compare(VALUE self, VALUE string, VALUE len, VALUE enc, VALUE sym) { + ID symbol = rb_intern3(RSTRING_PTR(string), FIX2LONG(len), rb_enc_get(enc)); + return (SYM2ID(sym) == symbol) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_ID2NAME +VALUE symbol_spec_rb_id2name(VALUE self, VALUE symbol) { + const char* c_str = rb_id2name(SYM2ID(symbol)); + return rb_str_new(c_str, strlen(c_str)); +} +#endif + +#ifdef HAVE_RB_ID2STR +VALUE symbol_spec_rb_id2str(VALUE self, VALUE symbol) { + return rb_id2str(SYM2ID(symbol)); +} +#endif + +#ifdef HAVE_RB_INTERN_STR +VALUE symbol_spec_rb_intern_str(VALUE self, VALUE str) { + return ID2SYM(rb_intern_str(str)); +} +#endif + +#ifdef HAVE_RB_IS_CLASS_ID +VALUE symbol_spec_rb_is_class_id(VALUE self, VALUE sym) { + return rb_is_class_id(SYM2ID(sym)) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_IS_CONST_ID +VALUE symbol_spec_rb_is_const_id(VALUE self, VALUE sym) { + return rb_is_const_id(SYM2ID(sym)) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_IS_INSTANCE_ID +VALUE symbol_spec_rb_is_instance_id(VALUE self, VALUE sym) { + return rb_is_instance_id(SYM2ID(sym)) ? Qtrue : Qfalse; +} +#endif + +#ifdef HAVE_RB_SYM2STR +VALUE symbol_spec_rb_sym2str(VALUE self, VALUE sym) { + return rb_sym2str(sym); +} +#endif + +void Init_symbol_spec(void) { + VALUE cls; + cls = rb_define_class("CApiSymbolSpecs", rb_cObject); + +#ifdef HAVE_RB_INTERN + rb_define_method(cls, "rb_intern", symbol_spec_rb_intern, 1); + rb_define_method(cls, "rb_intern2", symbol_spec_rb_intern2, 2); + rb_define_method(cls, "rb_intern_const", symbol_spec_rb_intern_const, 1); + rb_define_method(cls, "rb_intern_c_compare", symbol_spec_rb_intern_c_compare, 2); + rb_define_method(cls, "rb_intern2_c_compare", symbol_spec_rb_intern2_c_compare, 3); +#endif + +#ifdef HAVE_RB_INTERN3 + rb_define_method(cls, "rb_intern3", symbol_spec_rb_intern3, 3); + rb_define_method(cls, "rb_intern3_c_compare", symbol_spec_rb_intern3_c_compare, 4); +#endif + +#ifdef HAVE_RB_ID2NAME + rb_define_method(cls, "rb_id2name", symbol_spec_rb_id2name, 1); +#endif + +#ifdef HAVE_RB_ID2STR + rb_define_method(cls, "rb_id2str", symbol_spec_rb_id2str, 1); +#endif + +#ifdef HAVE_RB_INTERN_STR + rb_define_method(cls, "rb_intern_str", symbol_spec_rb_intern_str, 1); +#endif + +#ifdef HAVE_RB_IS_CLASS_ID + rb_define_method(cls, "rb_is_class_id", symbol_spec_rb_is_class_id, 1); +#endif + +#ifdef HAVE_RB_IS_CONST_ID + rb_define_method(cls, "rb_is_const_id", symbol_spec_rb_is_const_id, 1); +#endif + +#ifdef HAVE_RB_IS_INSTANCE_ID + rb_define_method(cls, "rb_is_instance_id", symbol_spec_rb_is_instance_id, 1); +#endif + +#ifdef HAVE_RB_SYM2STR + rb_define_method(cls, "rb_sym2str", symbol_spec_rb_sym2str, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/thread_spec.c b/spec/rubyspec/optional/capi/ext/thread_spec.c new file mode 100644 index 0000000000..5ea96fe3d6 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/thread_spec.c @@ -0,0 +1,181 @@ +#include "ruby.h" +#include "ruby/thread.h" +#include "rubyspec.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_THREAD_ALONE +static VALUE thread_spec_rb_thread_alone() { + return rb_thread_alone() ? Qtrue : Qfalse; +} +#endif + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL +/* This is unblocked by unblock_func(). */ +static void* blocking_gvl_func(void* data) { + int rfd = *(int *)data; + char dummy; + ssize_t rv; + + do { + rv = read(rfd, &dummy, 1); + } while (rv == -1 && errno == EINTR); + + return (void*)((rv == 1) ? Qtrue : Qfalse); +} + +static void unblock_gvl_func(void *data) { + int wfd = *(int *)data; + char dummy = 0; + ssize_t rv; + + do { + rv = write(wfd, &dummy, 1); + } while (rv == -1 && errno == EINTR); +} + +/* Returns true if the thread is interrupted. */ +static VALUE thread_spec_rb_thread_call_without_gvl(VALUE self) { + int fds[2]; + void* ret; + + if (pipe(fds) == -1) { + return Qfalse; + } + ret = rb_thread_call_without_gvl(blocking_gvl_func, &fds[0], + unblock_gvl_func, &fds[1]); + close(fds[0]); + close(fds[1]); + return (VALUE)ret; +} + +/* This is unblocked by a signal. */ +static void* blocking_gvl_func_for_udf_io(void *data) { + int rfd = (int)(size_t)data; + char dummy; + + if (read(rfd, &dummy, 1) == -1 && errno == EINTR) { + return (void*)Qtrue; + } else { + return (void*)Qfalse; + } +} + +/* Returns true if the thread is interrupted. */ +static VALUE thread_spec_rb_thread_call_without_gvl_with_ubf_io(VALUE self) { + int fds[2]; + void* ret; + + if (pipe(fds) == -1) { + return Qfalse; + } + + ret = rb_thread_call_without_gvl(blocking_gvl_func_for_udf_io, + (void*)(size_t)fds[0], RUBY_UBF_IO, 0); + close(fds[0]); + close(fds[1]); + return (VALUE)ret; +} +#endif + +#ifdef HAVE_RB_THREAD_CURRENT +static VALUE thread_spec_rb_thread_current() { + return rb_thread_current(); +} +#endif + +#ifdef HAVE_RB_THREAD_LOCAL_AREF +static VALUE thread_spec_rb_thread_local_aref(VALUE self, VALUE thr, VALUE sym) { + return rb_thread_local_aref(thr, SYM2ID(sym)); +} +#endif + +#ifdef HAVE_RB_THREAD_LOCAL_ASET +static VALUE thread_spec_rb_thread_local_aset(VALUE self, VALUE thr, VALUE sym, VALUE value) { + return rb_thread_local_aset(thr, SYM2ID(sym), value); +} +#endif + +#ifdef HAVE_RB_THREAD_WAKEUP +static VALUE thread_spec_rb_thread_wakeup(VALUE self, VALUE thr) { + return rb_thread_wakeup(thr); +} +#endif + +#ifdef HAVE_RB_THREAD_WAIT_FOR +static VALUE thread_spec_rb_thread_wait_for(VALUE self, VALUE s, VALUE ms) { + struct timeval tv; + tv.tv_sec = NUM2INT(s); + tv.tv_usec = NUM2INT(ms); + rb_thread_wait_for(tv); + return Qnil; +} +#endif + +#ifdef HAVE_RB_THREAD_CREATE + +VALUE thread_spec_call_proc(VALUE arg_array) { + VALUE arg = rb_ary_pop(arg_array); + VALUE proc = rb_ary_pop(arg_array); + return rb_funcall(proc, rb_intern("call"), 1, arg); +} + +static VALUE thread_spec_rb_thread_create(VALUE self, VALUE proc, VALUE arg) { + VALUE args = rb_ary_new(); + rb_ary_push(args, proc); + rb_ary_push(args, arg); + + return rb_thread_create(thread_spec_call_proc, (void*)args); +} +#endif + + +void Init_thread_spec(void) { + VALUE cls; + cls = rb_define_class("CApiThreadSpecs", rb_cObject); + +#ifdef HAVE_RB_THREAD_ALONE + rb_define_method(cls, "rb_thread_alone", thread_spec_rb_thread_alone, 0); +#endif + +#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL + rb_define_method(cls, "rb_thread_call_without_gvl", thread_spec_rb_thread_call_without_gvl, 0); + rb_define_method(cls, "rb_thread_call_without_gvl_with_ubf_io", thread_spec_rb_thread_call_without_gvl_with_ubf_io, 0); +#endif + +#ifdef HAVE_RB_THREAD_CURRENT + rb_define_method(cls, "rb_thread_current", thread_spec_rb_thread_current, 0); +#endif + +#ifdef HAVE_RB_THREAD_LOCAL_AREF + rb_define_method(cls, "rb_thread_local_aref", thread_spec_rb_thread_local_aref, 2); +#endif + +#ifdef HAVE_RB_THREAD_LOCAL_ASET + rb_define_method(cls, "rb_thread_local_aset", thread_spec_rb_thread_local_aset, 3); +#endif + +#ifdef HAVE_RB_THREAD_WAKEUP + rb_define_method(cls, "rb_thread_wakeup", thread_spec_rb_thread_wakeup, 1); +#endif + +#ifdef HAVE_RB_THREAD_WAIT_FOR + rb_define_method(cls, "rb_thread_wait_for", thread_spec_rb_thread_wait_for, 2); +#endif + +#ifdef HAVE_RB_THREAD_CREATE + rb_define_method(cls, "rb_thread_create", thread_spec_rb_thread_create, 2); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/time_spec.c b/spec/rubyspec/optional/capi/ext/time_spec.c new file mode 100644 index 0000000000..6b51120f1b --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/time_spec.c @@ -0,0 +1,127 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_TIME_NEW +static VALUE time_spec_rb_time_new(VALUE self, VALUE sec, VALUE usec) { + return rb_time_new(NUM2TIMET(sec), NUM2LONG(usec)); +} +#endif + +#ifdef HAVE_RB_TIME_NANO_NEW +static VALUE time_spec_rb_time_nano_new(VALUE self, VALUE sec, VALUE nsec) { + return rb_time_nano_new(NUM2TIMET(sec), NUM2LONG(nsec)); +} +#endif + +#ifdef HAVE_RB_TIME_NUM_NEW +static VALUE time_spec_rb_time_num_new(VALUE self, VALUE ts, VALUE offset) { + return rb_time_num_new(ts, offset); +} +#endif + +#ifdef HAVE_RB_TIME_INTERVAL +static VALUE time_spec_rb_time_interval(VALUE self, VALUE ts) { + struct timeval interval = rb_time_interval(ts); + VALUE ary = rb_ary_new(); + rb_ary_push(ary, TIMET2NUM(interval.tv_sec)); + rb_ary_push(ary, TIMET2NUM(interval.tv_usec)); + return ary; +} +#endif + +#ifdef HAVE_RB_TIME_TIMEVAL +static VALUE time_spec_rb_time_timeval(VALUE self, VALUE ts) { + struct timeval tv = rb_time_timeval(ts); + VALUE ary = rb_ary_new(); + rb_ary_push(ary, TIMET2NUM(tv.tv_sec)); + rb_ary_push(ary, TIMET2NUM(tv.tv_usec)); + return ary; +} +#endif + +#ifdef HAVE_RB_TIME_TIMESPEC +static VALUE time_spec_rb_time_timespec(VALUE self, VALUE time) { + struct timespec ts = rb_time_timespec(time); + VALUE ary = rb_ary_new(); + rb_ary_push(ary, TIMET2NUM(ts.tv_sec)); + rb_ary_push(ary, TIMET2NUM(ts.tv_nsec)); + return ary; +} +#endif + +#ifdef HAVE_RB_TIME_TIMESPEC_NEW +static VALUE time_spec_rb_time_timespec_new(VALUE self, VALUE sec, VALUE nsec, VALUE offset) { + struct timespec ts; + ts.tv_sec = NUM2TIMET(sec); + ts.tv_nsec = NUM2LONG(nsec); + + return rb_time_timespec_new(&ts, NUM2INT(offset)); +} +#endif + +#ifdef HAVE_RB_TIMESPEC_NOW +static VALUE time_spec_rb_time_from_timspec_now(VALUE self, VALUE offset) { + struct timespec ts; + rb_timespec_now(&ts); + + return rb_time_timespec_new(&ts, NUM2INT(offset)); +} +#endif + +#ifdef HAVE_TIMET2NUM +static VALUE time_spec_TIMET2NUM(VALUE self) { + time_t t = 10; + return TIMET2NUM(t); +} +#endif + +void Init_time_spec(void) { + VALUE cls; + cls = rb_define_class("CApiTimeSpecs", rb_cObject); + +#ifdef HAVE_RB_TIME_NEW + rb_define_method(cls, "rb_time_new", time_spec_rb_time_new, 2); +#endif + +#ifdef HAVE_TIMET2NUM + rb_define_method(cls, "TIMET2NUM", time_spec_TIMET2NUM, 0); +#endif + +#ifdef HAVE_RB_TIME_NANO_NEW + rb_define_method(cls, "rb_time_nano_new", time_spec_rb_time_nano_new, 2); +#endif + +#ifdef HAVE_RB_TIME_NUM_NEW + rb_define_method(cls, "rb_time_num_new", time_spec_rb_time_num_new, 2); +#endif + +#ifdef HAVE_RB_TIME_INTERVAL + rb_define_method(cls, "rb_time_interval", time_spec_rb_time_interval, 1); +#endif + +#ifdef HAVE_RB_TIME_TIMEVAL + rb_define_method(cls, "rb_time_timeval", time_spec_rb_time_timeval, 1); +#endif + +#ifdef HAVE_RB_TIME_TIMESPEC + rb_define_method(cls, "rb_time_timespec", time_spec_rb_time_timespec, 1); +#endif + +#ifdef HAVE_RB_TIME_TIMESPEC_NEW + rb_define_method(cls, "rb_time_timespec_new", time_spec_rb_time_timespec_new, 3); +#endif + +#ifdef HAVE_RB_TIMESPEC_NOW + rb_define_method(cls, "rb_time_from_timespec", time_spec_rb_time_from_timspec_now, 1); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/ext/truffleruby.h b/spec/rubyspec/optional/capi/ext/truffleruby.h new file mode 100644 index 0000000000..99976a18a4 --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/truffleruby.h @@ -0,0 +1,6 @@ +#ifndef RUBYSPEC_CAPI_TRUFFLERUBY_H +#undef RUBYSPEC_CAPI_TRUFFLERUBY_H + +// All features are available + +#endif diff --git a/spec/rubyspec/optional/capi/ext/typed_data_spec.c b/spec/rubyspec/optional/capi/ext/typed_data_spec.c new file mode 100644 index 0000000000..8436c527be --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/typed_data_spec.c @@ -0,0 +1,177 @@ +#include "ruby.h" +#include "rubyspec.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(HAVE_RTYPEDDATA) && defined(HAVE_TYPEDDATA_WRAP_STRUCT) +struct sample_typed_wrapped_struct_parent { + int foo; +}; + +void sample_typed_wrapped_struct_parent_free(void* st) { + free(st); +} + +void sample_typed_wrapped_struct_parent_mark(void* st) { +} + +size_t sample_typed_wrapped_struct_parent_memsize(const void* st) { + return sizeof(struct sample_typed_wrapped_struct_parent); +} + +static const rb_data_type_t sample_typed_wrapped_struct_parent_data_type = { + "sample_typed_wrapped_struct_parent", + { + sample_typed_wrapped_struct_parent_mark, + sample_typed_wrapped_struct_parent_free, + sample_typed_wrapped_struct_parent_memsize, + }, +}; + +struct sample_typed_wrapped_struct { + int foo; +}; + +void sample_typed_wrapped_struct_free(void* st) { + free(st); +} + +void sample_typed_wrapped_struct_mark(void* st) { +} + +size_t sample_typed_wrapped_struct_memsize(const void* st) { + return sizeof(struct sample_typed_wrapped_struct); +} + +static const rb_data_type_t sample_typed_wrapped_struct_data_type = { + "sample_typed_wrapped_struct", + { + sample_typed_wrapped_struct_mark, + sample_typed_wrapped_struct_free, + sample_typed_wrapped_struct_memsize, + }, + &sample_typed_wrapped_struct_parent_data_type, +}; + +struct sample_typed_wrapped_struct_other { + int foo; +}; + +void sample_typed_wrapped_struct_other_free(void* st) { + free(st); +} + +void sample_typed_wrapped_struct_other_mark(void* st) { +} + +size_t sample_typed_wrapped_struct_other_memsize(const void* st) { + return sizeof(struct sample_typed_wrapped_struct_other); +} + +static const rb_data_type_t sample_typed_wrapped_struct_other_data_type = { + "sample_typed_wrapped_struct_other", + { + sample_typed_wrapped_struct_other_mark, + sample_typed_wrapped_struct_other_free, + sample_typed_wrapped_struct_other_memsize, + }, +}; + + +VALUE sdaf_alloc_typed_func(VALUE klass) { + struct sample_typed_wrapped_struct* bar = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct)); + bar->foo = 42; + return TypedData_Wrap_Struct(klass, &sample_typed_wrapped_struct_data_type, bar); +} + +VALUE sdaf_typed_get_struct(VALUE self) { + struct sample_typed_wrapped_struct* bar; + TypedData_Get_Struct(self, struct sample_typed_wrapped_struct, &sample_typed_wrapped_struct_data_type, bar); + + return INT2FIX((*bar).foo); +} + +VALUE sws_typed_wrap_struct(VALUE self, VALUE val) { + struct sample_typed_wrapped_struct* bar = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct)); + bar->foo = FIX2INT(val); + return TypedData_Wrap_Struct(rb_cObject, &sample_typed_wrapped_struct_data_type, bar); +} + +VALUE sws_typed_wrap_struct_null(VALUE self, VALUE val) { + struct sample_typed_wrapped_struct* bar = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct)); + bar->foo = FIX2INT(val); + return TypedData_Wrap_Struct(0, &sample_typed_wrapped_struct_data_type, bar); +} + +VALUE sws_typed_get_struct(VALUE self, VALUE obj) { + struct sample_typed_wrapped_struct* bar; + TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct, &sample_typed_wrapped_struct_data_type, bar); + + return INT2FIX((*bar).foo); +} + +VALUE sws_typed_get_struct_different_type(VALUE self, VALUE obj) { + struct sample_typed_wrapped_struct_other* bar; + TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct_other, &sample_typed_wrapped_struct_other_data_type, bar); + + return INT2FIX((*bar).foo); +} + +VALUE sws_typed_get_struct_parent_type(VALUE self, VALUE obj) { + struct sample_typed_wrapped_struct_parent* bar; + TypedData_Get_Struct(obj, struct sample_typed_wrapped_struct_parent, &sample_typed_wrapped_struct_parent_data_type, bar); + + return INT2FIX((*bar).foo); +} + +VALUE sws_typed_get_struct_rdata(VALUE self, VALUE obj) { + struct sample_typed_wrapped_struct* bar; + bar = (struct sample_typed_wrapped_struct*) RTYPEDDATA(obj)->data; + return INT2FIX(bar->foo); +} + +VALUE sws_typed_get_struct_data_ptr(VALUE self, VALUE obj) { + struct sample_typed_wrapped_struct* bar; + bar = (struct sample_typed_wrapped_struct*) DATA_PTR(obj); + return INT2FIX(bar->foo); +} + +VALUE sws_typed_change_struct(VALUE self, VALUE obj, VALUE new_val) { + struct sample_typed_wrapped_struct *old_struct, *new_struct; + new_struct = (struct sample_typed_wrapped_struct *)malloc(sizeof(struct sample_typed_wrapped_struct)); + new_struct->foo = FIX2INT(new_val); + old_struct = RTYPEDDATA(obj)->data; + free(old_struct); + RTYPEDDATA(obj)->data = new_struct; + return Qnil; +} +#endif + +void Init_typed_data_spec(void) { + VALUE cls; + cls = rb_define_class("CApiAllocTypedSpecs", rb_cObject); + +#if defined(HAVE_RTYPEDDATA) && defined(HAVE_TYPEDDATA_WRAP_STRUCT) + rb_define_alloc_func(cls, sdaf_alloc_typed_func); + rb_define_method(cls, "typed_wrapped_data", sdaf_typed_get_struct, 0); + + cls = rb_define_class("CApiWrappedTypedStructSpecs", rb_cObject); + rb_define_method(cls, "typed_wrap_struct", sws_typed_wrap_struct, 1); + rb_define_method(cls, "typed_wrap_struct_null", sws_typed_wrap_struct_null, 1); + rb_define_method(cls, "typed_get_struct", sws_typed_get_struct, 1); + rb_define_method(cls, "typed_get_struct_other", sws_typed_get_struct_different_type, 1); + rb_define_method(cls, "typed_get_struct_parent", sws_typed_get_struct_parent_type, 1); + rb_define_method(cls, "typed_get_struct_rdata", sws_typed_get_struct_rdata, 1); + rb_define_method(cls, "typed_get_struct_data_ptr", sws_typed_get_struct_data_ptr, 1); + rb_define_method(cls, "typed_change_struct", sws_typed_change_struct, 2); +#endif +} + +#ifdef __cplusplus +} +#endif + diff --git a/spec/rubyspec/optional/capi/ext/util_spec.c b/spec/rubyspec/optional/capi/ext/util_spec.c new file mode 100644 index 0000000000..50795b51af --- /dev/null +++ b/spec/rubyspec/optional/capi/ext/util_spec.c @@ -0,0 +1,95 @@ +#include "ruby.h" +#include "rubyspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_RB_SCAN_ARGS +VALUE util_spec_rb_scan_args(VALUE self, VALUE argv, VALUE fmt, VALUE expected, VALUE acc) { + int i, result, argc = (int)RARRAY_LEN(argv); + VALUE args[6], failed, a1, a2, a3, a4, a5, a6; + + failed = rb_intern("failed"); + a1 = a2 = a3 = a4 = a5 = a6 = failed; + + for(i = 0; i < argc; i++) { + args[i] = rb_ary_entry(argv, i); + } + + result = rb_scan_args(argc, args, RSTRING_PTR(fmt), &a1, &a2, &a3, &a4, &a5, &a6); + + switch(NUM2INT(expected)) { + case 6: + rb_ary_unshift(acc, a6); + case 5: + rb_ary_unshift(acc, a5); + case 4: + rb_ary_unshift(acc, a4); + case 3: + rb_ary_unshift(acc, a3); + case 2: + rb_ary_unshift(acc, a2); + case 1: + rb_ary_unshift(acc, a1); + break; + default: + rb_raise(rb_eException, "unexpected number of arguments returned by rb_scan_args"); + } + + return INT2NUM(result); +} +#endif + +#ifdef HAVE_RB_LONG2INT +static VALUE util_spec_rb_long2int(VALUE self, VALUE n) { + return INT2NUM(rb_long2int(NUM2LONG(n))); +} +#endif + +#ifdef HAVE_RB_ITER_BREAK +static VALUE util_spec_rb_iter_break(VALUE self) { + rb_iter_break(); + return Qnil; +} +#endif + +#ifdef HAVE_RB_SOURCEFILE +static VALUE util_spec_rb_sourcefile(VALUE self) { + return rb_str_new2(rb_sourcefile()); +} +#endif + +#ifdef HAVE_RB_SOURCELINE +static VALUE util_spec_rb_sourceline(VALUE self) { + return INT2NUM(rb_sourceline()); +} +#endif + +void Init_util_spec(void) { + VALUE cls = rb_define_class("CApiUtilSpecs", rb_cObject); + +#ifdef HAVE_RB_SCAN_ARGS + rb_define_method(cls, "rb_scan_args", util_spec_rb_scan_args, 4); +#endif + +#ifdef HAVE_RB_LONG2INT + rb_define_method(cls, "rb_long2int", util_spec_rb_long2int, 1); +#endif + +#ifdef HAVE_RB_ITER_BREAK + rb_define_method(cls, "rb_iter_break", util_spec_rb_iter_break, 0); +#endif + +#ifdef HAVE_RB_SOURCEFILE + rb_define_method(cls, "rb_sourcefile", util_spec_rb_sourcefile, 0); +#endif + +#ifdef HAVE_RB_SOURCELINE + rb_define_method(cls, "rb_sourceline", util_spec_rb_sourceline, 0); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/spec/rubyspec/optional/capi/false_spec.rb b/spec/rubyspec/optional/capi/false_spec.rb new file mode 100644 index 0000000000..8c0d94cf70 --- /dev/null +++ b/spec/rubyspec/optional/capi/false_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("boolean") + +describe "CApiFalseSpecs" do + before :each do + @b = CApiBooleanSpecs.new + end + + describe "a false value from Ruby" do + it "is falsey in C" do + @b.is_true(false).should == 2 + end + end + + describe "a false value from Qfalse" do + it "is falsey in C" do + @b.is_true(@b.q_false).should == 2 + end + end +end diff --git a/spec/rubyspec/optional/capi/file_spec.rb b/spec/rubyspec/optional/capi/file_spec.rb new file mode 100644 index 0000000000..2459dba979 --- /dev/null +++ b/spec/rubyspec/optional/capi/file_spec.rb @@ -0,0 +1,89 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension('file') + +describe :rb_file_open, shared: true do + it "raises an ArgumentError if passed an empty mode string" do + touch @name + lambda { @s.rb_file_open(@name, "") }.should raise_error(ArgumentError) + end + + it "opens a file in read-only mode with 'r'" do + touch(@name) { |f| f.puts "readable" } + @file = @s.send(@method, @name, "r") + @file.should be_an_instance_of(File) + @file.read.chomp.should == "readable" + end + + it "creates and opens a non-existent file with 'w'" do + @file = @s.send(@method, @name, "w") + @file.write "writable" + @file.flush + File.read(@name).should == "writable" + end + + it "truncates an existing file with 'w'" do + touch(@name) { |f| f.puts "existing" } + @file = @s.send(@method, @name, "w") + File.read(@name).should == "" + end +end + +describe "C-API File function" do + before :each do + @s = CApiFileSpecs.new + @name = tmp("rb_file_open") + end + + after :each do + @file.close if @file and !@file.closed? + rm_r @name + end + + describe "rb_file_open" do + it_behaves_like :rb_file_open, :rb_file_open + end + + describe "rb_file_open_str" do + it_behaves_like :rb_file_open, :rb_file_open_str + end + + describe "rb_file_open_str" do + it "calls #to_path to convert on object to a path" do + path = mock("rb_file_open_str to_path") + path.should_receive(:to_path).and_return(@name) + @file = @s.rb_file_open_str(path, "w") + end + + it "calls #to_str to convert an object to a path if #to_path isn't defined" do + path = mock("rb_file_open_str to_str") + path.should_receive(:to_str).and_return(@name) + @file = @s.rb_file_open_str(path, "w") + end + end + + describe "FilePathValue" do + it "returns a String argument unchanged" do + obj = "path" + @s.FilePathValue(obj).should eql(obj) + end + + it "does not call #to_str on a String" do + obj = "path" + obj.should_not_receive(:to_str) + @s.FilePathValue(obj).should eql(obj) + end + + it "calls #to_path to convert an object to a String" do + obj = mock("FilePathValue to_path") + obj.should_receive(:to_path).and_return("path") + @s.FilePathValue(obj).should == "path" + end + + it "calls #to_str to convert an object to a String if #to_path isn't defined" do + obj = mock("FilePathValue to_str") + obj.should_receive(:to_str).and_return("path") + @s.FilePathValue(obj).should == "path" + end + end +end diff --git a/spec/rubyspec/optional/capi/fixnum_spec.rb b/spec/rubyspec/optional/capi/fixnum_spec.rb new file mode 100644 index 0000000000..87f257fec7 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixnum_spec.rb @@ -0,0 +1,74 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("fixnum") + +describe "CApiFixnumSpecs" do + before :each do + @s = CApiFixnumSpecs.new + end + + platform_is wordsize: 64 do + describe "rb_fix2uint" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_fix2uint(nil) }.should raise_error(TypeError) + end + + it "converts a Fixnum" do + @s.rb_fix2uint(1).should == 1 + end + + it "converts the maximum uint value" do + @s.rb_fix2uint(0xffff_ffff).should == 0xffff_ffff + end + + it "converts a Float" do + @s.rb_fix2uint(25.4567).should == 25 + end + + it "raises a RangeError if the value is more than 32bits" do + lambda { @s.rb_fix2uint(0xffff_ffff+1) }.should raise_error(RangeError) + end + + it "raises a RangeError if the value is more than 64bits" do + lambda { @s.rb_fix2uint(0xffff_ffff_ffff_ffff+1) }.should raise_error(RangeError) + end + end + + describe "rb_fix2int" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_fix2int(nil) }.should raise_error(TypeError) + end + + it "converts a Fixnum" do + @s.rb_fix2int(-1).should == -1 + @s.rb_fix2int(1).should == 1 + end + + it "converts the maximum uint value" do + @s.rb_fix2int(0x7fff_ffff).should == 0x7fff_ffff + end + + it "converts a Float" do + @s.rb_fix2int(25.4567).should == 25 + end + + it "converts a negative Bignum into an signed number" do + @s.rb_fix2int(-2147442171).should == -2147442171 + end + + it "raises a RangeError if the value is more than 32bits" do + lambda { @s.rb_fix2int(0xffff_ffff+1) }.should raise_error(RangeError) + end + + it "raises a RangeError if the value is more than 64bits" do + lambda { @s.rb_fix2int(0xffff_ffff_ffff_ffff+1) }.should raise_error(RangeError) + end + + it "calls #to_int to coerce the value" do + obj = mock("number") + obj.should_receive(:to_int).and_return(2) + @s.rb_fix2int(obj).should == 2 + end + end + end +end diff --git a/spec/rubyspec/optional/capi/fixtures/class.rb b/spec/rubyspec/optional/capi/fixtures/class.rb new file mode 100644 index 0000000000..de824b3ab0 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/class.rb @@ -0,0 +1,82 @@ +class CApiClassSpecs + module M + def included? + true + end + end + + class Alloc + attr_reader :initialized + attr_reader :arguments + + def initialize(*args) + @initialized = true + @arguments = args + end + end + + class Attr + def initialize + @foo, @bar, @baz = 1, 2, 3 + end + end + + class CVars + @@cvar = :cvar + @c_ivar = :c_ivar + + def new_cv + @@new_cv if defined? @@new_cv + end + + def new_cvar + @@new_cvar if defined? @@new_cvar + end + + def rbdcv_cvar + @@rbdcv_cvar if defined? @@rbdcv_cvar + end + end + + class Inherited + def self.inherited(klass) + klass + end + end + + class NewClass + def self.inherited(klass) + raise "#{name}.inherited called" + end + end + + class Super + def call_super_method + :super_method + end + end + + class Sub < Super + def call_super_method + :subclass_method + end + end + + class SubM < Super + include M + end + + class SubSub < Sub + def call_super_method + :subclass_method + end + end + + class A + C = 1 + autoload :D, File.expand_path('../path_to_class.rb', __FILE__) + + class B + end + end +end diff --git a/spec/rubyspec/optional/capi/fixtures/const_get.rb b/spec/rubyspec/optional/capi/fixtures/const_get.rb new file mode 100644 index 0000000000..b261a07f7e --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/const_get.rb @@ -0,0 +1,5 @@ +class CApiModuleSpecs + class A + D = 123 + end +end diff --git a/spec/rubyspec/optional/capi/fixtures/const_get_at.rb b/spec/rubyspec/optional/capi/fixtures/const_get_at.rb new file mode 100644 index 0000000000..700b570607 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/const_get_at.rb @@ -0,0 +1,5 @@ +class CApiModuleSpecs + class A + B = 123 + end +end diff --git a/spec/rubyspec/optional/capi/fixtures/const_get_from.rb b/spec/rubyspec/optional/capi/fixtures/const_get_from.rb new file mode 100644 index 0000000000..9e297daba7 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/const_get_from.rb @@ -0,0 +1,5 @@ +class CApiModuleSpecs + class A + C = 123 + end +end diff --git a/spec/rubyspec/optional/capi/fixtures/const_get_object.rb b/spec/rubyspec/optional/capi/fixtures/const_get_object.rb new file mode 100644 index 0000000000..03d9365a3e --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/const_get_object.rb @@ -0,0 +1,3 @@ +class Object + CApiModuleSpecsAutoload = 123 +end diff --git a/spec/rubyspec/optional/capi/fixtures/encoding.rb b/spec/rubyspec/optional/capi/fixtures/encoding.rb new file mode 100644 index 0000000000..0858807aa0 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/encoding.rb @@ -0,0 +1,3 @@ +class CApiEncodingSpecs + class S < String; end +end diff --git a/spec/rubyspec/optional/capi/fixtures/foo.rb b/spec/rubyspec/optional/capi/fixtures/foo.rb new file mode 100644 index 0000000000..bc4e8f3f7d --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/foo.rb @@ -0,0 +1 @@ +$foo = 7 diff --git a/spec/rubyspec/optional/capi/fixtures/module.rb b/spec/rubyspec/optional/capi/fixtures/module.rb new file mode 100644 index 0000000000..ba90eb7181 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/module.rb @@ -0,0 +1,35 @@ +class Object + autoload :CApiModuleSpecsAutoload, File.expand_path('../const_get_object.rb', __FILE__) + + module CApiModuleSpecsModuleA + X = 1 + end +end + +class CApiModuleSpecs + class A + autoload :B, File.expand_path('../const_get_at.rb', __FILE__) + autoload :C, File.expand_path('../const_get_from.rb', __FILE__) + autoload :D, File.expand_path('../const_get.rb', __FILE__) + + X = 1 + end + + class B < A + Y = 2 + end + + class C + Z = 3 + end + + module M + end + + class Super + end + + autoload :ModuleUnderAutoload, "#{object_path}/module_under_autoload_spec" + autoload :RubyUnderAutoload, File.expand_path('../module_autoload', __FILE__) + +end diff --git a/spec/rubyspec/optional/capi/fixtures/module_autoload.rb b/spec/rubyspec/optional/capi/fixtures/module_autoload.rb new file mode 100644 index 0000000000..8130a24421 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/module_autoload.rb @@ -0,0 +1,4 @@ +class CApiModuleSpecs + module RubyUnderAutoload + end +end diff --git a/spec/rubyspec/optional/capi/fixtures/path_to_class.rb b/spec/rubyspec/optional/capi/fixtures/path_to_class.rb new file mode 100644 index 0000000000..acd577ff24 --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/path_to_class.rb @@ -0,0 +1,6 @@ +class CApiClassSpecs + class A + module D + end + end +end diff --git a/spec/rubyspec/optional/capi/fixtures/proc.rb b/spec/rubyspec/optional/capi/fixtures/proc.rb new file mode 100644 index 0000000000..fbe37312da --- /dev/null +++ b/spec/rubyspec/optional/capi/fixtures/proc.rb @@ -0,0 +1,20 @@ +class CApiProcSpecs + def call_nothing + end + + def call_Proc_new + Proc.new + end + + def call_block_given? + block_given? + end + + def call_rb_Proc_new + rb_Proc_new(0) + end + + def call_rb_Proc_new_with_block + rb_Proc_new(0) { :calling_with_block } + end +end diff --git a/spec/rubyspec/optional/capi/float_spec.rb b/spec/rubyspec/optional/capi/float_spec.rb new file mode 100644 index 0000000000..8d709b2b82 --- /dev/null +++ b/spec/rubyspec/optional/capi/float_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("float") + +describe "CApiFloatSpecs" do + before :each do + @f = CApiFloatSpecs.new + end + + describe "rb_float_new" do + it "creates a new float" do + ((@f.new_zero - 0).abs < 0.000001).should == true + ((@f.new_point_five - 0.555).abs < 0.000001).should == true + end + end + + describe "RFLOAT_VALUE" do + it "returns the C double value of the Float" do + @f.RFLOAT_VALUE(2.3).should == 2.3 + end + end + + describe "rb_Float" do + it "creates a new Float from a String" do + f = @f.rb_Float("101.99") + f.should be_kind_of(Float) + f.should eql(101.99) + end + end +end diff --git a/spec/rubyspec/optional/capi/gc_spec.rb b/spec/rubyspec/optional/capi/gc_spec.rb new file mode 100644 index 0000000000..71efef3fad --- /dev/null +++ b/spec/rubyspec/optional/capi/gc_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("gc") + +describe "CApiGCSpecs" do + before :each do + @f = CApiGCSpecs.new + end + + it "correctly gets the value from a registered address" do + @f.registered_tagged_address.should == 10 + @f.registered_tagged_address.object_id.should == @f.registered_tagged_address.object_id + @f.registered_reference_address.should == "Globally registered data" + @f.registered_reference_address.object_id.should == @f.registered_reference_address.object_id + end + + describe "rb_gc_enable" do + + after do + GC.enable + end + + it "enables GC when disabled" do + GC.disable + @f.rb_gc_enable.should be_true + end + + it "GC stays enabled when enabled" do + GC.enable + @f.rb_gc_enable.should be_false + end + + it "disables GC when enabled" do + GC.enable + @f.rb_gc_disable.should be_false + end + + it "GC stays disabled when disabled" do + GC.disable + @f.rb_gc_disable.should be_true + end + end + +end diff --git a/spec/rubyspec/optional/capi/globals_spec.rb b/spec/rubyspec/optional/capi/globals_spec.rb new file mode 100644 index 0000000000..2e748e78ac --- /dev/null +++ b/spec/rubyspec/optional/capi/globals_spec.rb @@ -0,0 +1,224 @@ +require File.expand_path('../spec_helper', __FILE__) +require "stringio" + +load_extension("globals") + +describe "CApiGlobalSpecs" do + before :each do + @f = CApiGlobalSpecs.new + end + + it "correctly gets global values" do + @f.sb_gv_get("$BLAH").should == nil + @f.sb_gv_get("$\\").should == nil + @f.sb_gv_get("\\").should == nil # rb_gv_get should change \ to $\ + end + + it "returns $~" do + 'a' =~ /a/ + @f.sb_gv_get("$~").to_a.should == ['a'] + @f.sb_gv_get("~").to_a.should == ['a'] + end + + it "correctly sets global values" do + @f.sb_gv_get("$BLAH").should == nil + @f.sb_gv_set("$BLAH", 10) + begin + @f.sb_gv_get("$BLAH").should == 10 + ensure + @f.sb_gv_set("$BLAH", nil) + end + end + + it "lists all global variables" do + @f.rb_f_global_variables.should == Kernel.global_variables + end + + it "rb_define_variable should define a new global variable" do + @f.rb_define_variable("my_gvar", "ABC") + $my_gvar.should == "ABC" + $my_gvar = "XYZ" + @f.sb_get_global_value.should == "XYZ" + end + + it "rb_define_readonly_variable should define a new readonly global variable" do + @f.rb_define_readonly_variable("ro_gvar", 15) + $ro_gvar.should == 15 + lambda { $ro_gvar = 10 }.should raise_error(NameError) + end + + it "rb_define_hooked_variable should define a C hooked global variable" do + @f.rb_define_hooked_variable_2x("$hooked_gvar") + $hooked_gvar = 2 + $hooked_gvar.should == 4 + end + + describe "rb_rs" do + before :each do + @dollar_slash = $/ + end + + after :each do + $/ = @dollar_slash + end + + it "returns \\n by default" do + @f.rb_rs.should == "\n" + end + + it "returns the value of $/" do + $/ = "foo" + @f.rb_rs.should == "foo" + end + end + + context "rb_std streams" do + before :each do + @name = tmp("rb_std_streams") + @stream = new_io @name + end + + after :each do + @stream.close + rm_r @name + end + + describe "rb_stdin" do + after :each do + $stdin = STDIN + end + + it "returns $stdin" do + $stdin = @stream + @f.rb_stdin.should equal($stdin) + end + end + + describe "rb_stdout" do + after :each do + $stdout = STDOUT + end + + it "returns $stdout" do + $stdout = @stream + @f.rb_stdout.should equal($stdout) + end + end + + describe "rb_stderr" do + after :each do + $stderr = STDERR + end + + it "returns $stderr" do + $stderr = @stream + @f.rb_stderr.should equal($stderr) + end + end + + describe "rb_defout" do + after :each do + $stdout = STDOUT + end + + it "returns $stdout" do + $stdout = @stream + @f.rb_defout.should equal($stdout) + end + end + end + + describe "rb_default_rs" do + it "returns \\n" do + @f.rb_default_rs.should == "\n" + end + end + + describe "rb_output_rs" do + before :each do + @dollar_backslash = $\ + end + + after :each do + $\ = @dollar_backslash + end + + it "returns nil by default" do + @f.rb_output_rs.should be_nil + end + + it "returns the value of $\\" do + $\ = "foo" + @f.rb_output_rs.should == "foo" + end + end + + describe "rb_output_fs" do + before :each do + @dollar_comma = $, + end + + after :each do + $, = @dollar_comma + end + + it "returns nil by default" do + @f.rb_output_fs.should be_nil + end + + it "returns the value of $\\" do + $, = "foo" + @f.rb_output_fs.should == "foo" + end + end + + describe "rb_lastline_set" do + it "sets the value of $_" do + @f.rb_lastline_set("last line") + $_.should == "last line" + end + + it "sets a Thread-local value" do + $_ = nil + running = false + + thr = Thread.new do + @f.rb_lastline_set("last line") + $_.should == "last line" + running = true + end + + Thread.pass until running + $_.should be_nil + + thr.join + end + end + + describe "rb_lastline_get" do + before do + @io = StringIO.new("last line") + end + + it "gets the value of $_" do + @io.gets + @f.rb_lastline_get.should == "last line" + end + + it "gets a Thread-local value" do + $_ = nil + running = false + + thr = Thread.new do + @io.gets + @f.rb_lastline_get.should == "last line" + running = true + end + + Thread.pass until running + $_.should be_nil + + thr.join + end + end +end diff --git a/spec/rubyspec/optional/capi/hash_spec.rb b/spec/rubyspec/optional/capi/hash_spec.rb new file mode 100644 index 0000000000..15284566f1 --- /dev/null +++ b/spec/rubyspec/optional/capi/hash_spec.rb @@ -0,0 +1,226 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("hash") + +describe "C-API Hash function" do + before :each do + @s = CApiHashSpecs.new + end + + describe "rb_hash" do + it "calls #hash on the object" do + obj = mock("rb_hash") + obj.should_receive(:hash).and_return(5) + @s.rb_hash(obj).should == 5 + end + + it "converts a Bignum returned by #hash to a Fixnum" do + obj = mock("rb_hash bignum") + obj.should_receive(:hash).and_return(bignum_value) + + # The actual conversion is an implementation detail. + # We only care that ultimately we get a Fixnum instance. + @s.rb_hash(obj).should be_an_instance_of(Fixnum) + end + + it "calls #to_int to converts a value returned by #hash to a Fixnum" do + obj = mock("rb_hash to_int") + obj.should_receive(:hash).and_return(obj) + obj.should_receive(:to_int).and_return(12) + + @s.rb_hash(obj).should == 12 + end + + it "raises a TypeError if the object does not implement #to_int" do + obj = mock("rb_hash no to_int") + obj.should_receive(:hash).and_return(nil) + + lambda { @s.rb_hash(obj) }.should raise_error(TypeError) + end + end + + describe "rb_hash_new" do + it "returns a new hash" do + @s.rb_hash_new.should == {} + end + + it "creates a hash with no default proc" do + @s.rb_hash_new {}.default_proc.should be_nil + end + end + + describe "rb_hash_dup" do + it "returns a copy of the hash" do + hsh = {} + dup = @s.rb_hash_dup(hsh) + dup.should == hsh + dup.should_not equal(hsh) + end + end + + describe "rb_hash_freeze" do + it "freezes the hash" do + @s.rb_hash_freeze({}).frozen?.should be_true + end + end + + describe "rb_hash_aref" do + it "returns the value associated with the key" do + hsh = {chunky: 'bacon'} + @s.rb_hash_aref(hsh, :chunky).should == 'bacon' + end + + it "returns the default value if it exists" do + hsh = Hash.new(0) + @s.rb_hash_aref(hsh, :chunky).should == 0 + @s.rb_hash_aref_nil(hsh, :chunky).should be_false + end + + it "returns nil if the key does not exist" do + hsh = { } + @s.rb_hash_aref(hsh, :chunky).should be_nil + @s.rb_hash_aref_nil(hsh, :chunky).should be_true + end + end + + describe "rb_hash_aset" do + it "adds the key/value pair and returns the value" do + hsh = {} + @s.rb_hash_aset(hsh, :chunky, 'bacon').should == 'bacon' + hsh.should == {chunky: 'bacon'} + end + end + + describe "rb_hash_clear" do + it "returns self that cleared keys and values" do + hsh = { :key => 'value' } + @s.rb_hash_clear(hsh).should equal(hsh) + hsh.should == {} + end + end + + describe "rb_hash_delete" do + it "removes the key and returns the value" do + hsh = {chunky: 'bacon'} + @s.rb_hash_delete(hsh, :chunky).should == 'bacon' + hsh.should == {} + end + end + + describe "rb_hash_delete_if" do + it "removes an entry if the block returns true" do + h = { a: 1, b: 2, c: 3 } + @s.rb_hash_delete_if(h) { |k, v| v == 2 } + h.should == { a: 1, c: 3 } + end + + it "returns an Enumerator when no block is passed" do + @s.rb_hash_delete_if({a: 1}).should be_an_instance_of(Enumerator) + end + end + + describe "rb_hash_foreach" do + it "iterates over the hash" do + hsh = {name: "Evan", sign: :libra} + + out = @s.rb_hash_foreach(hsh) + out.equal?(hsh).should == false + out.should == hsh + end + + it "stops via the callback" do + hsh = {name: "Evan", sign: :libra} + + out = @s.rb_hash_foreach_stop(hsh) + out.size.should == 1 + end + + it "deletes via the callback" do + hsh = {name: "Evan", sign: :libra} + + out = @s.rb_hash_foreach_delete(hsh) + out.should == {name: "Evan", sign: :libra} + hsh.should == {} + end + end + + describe "rb_hash_size" do + it "returns the size of the hash" do + hsh = {fast: 'car', good: 'music'} + @s.rb_hash_size(hsh).should == 2 + end + + it "returns zero for an empty hash" do + @s.rb_hash_size({}).should == 0 + end + end + + describe "rb_hash_lookup" do + it "returns the value associated with the key" do + hsh = {chunky: 'bacon'} + @s.rb_hash_lookup(hsh, :chunky).should == 'bacon' + end + + it "does not return the default value if it exists" do + hsh = Hash.new(0) + @s.rb_hash_lookup(hsh, :chunky).should be_nil + @s.rb_hash_lookup_nil(hsh, :chunky).should be_true + end + + it "returns nil if the key does not exist" do + hsh = { } + @s.rb_hash_lookup(hsh, :chunky).should be_nil + @s.rb_hash_lookup_nil(hsh, :chunky).should be_true + end + + describe "rb_hash_lookup2" do + it "returns the value associated with the key" do + hash = {chunky: 'bacon'} + + @s.rb_hash_lookup2(hash, :chunky, nil).should == 'bacon' + end + + it "returns the default value if the key does not exist" do + hash = {} + + @s.rb_hash_lookup2(hash, :chunky, 10).should == 10 + end + end + end + + describe "rb_hash_set_ifnone" do + it "sets the default value of non existing keys" do + hash = {} + + @s.rb_hash_set_ifnone(hash, 10) + + hash[:chunky].should == 10 + end + end + + describe "rb_Hash" do + it "returns an empty hash when the argument is nil" do + @s.rb_Hash(nil).should == {} + end + + it "returns an empty hash when the argument is []" do + @s.rb_Hash([]).should == {} + end + + it "tries to convert the passed argument to a hash by calling #to_hash" do + h = BasicObject.new + def h.to_hash; {"bar" => "foo"}; end + @s.rb_Hash(h).should == {"bar" => "foo"} + end + + it "raises a TypeError if the argument does not respond to #to_hash" do + lambda { @s.rb_Hash(42) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_hash does not return a hash" do + h = BasicObject.new + def h.to_hash; 42; end + lambda { @s.rb_Hash(h) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/optional/capi/integer_spec.rb b/spec/rubyspec/optional/capi/integer_spec.rb new file mode 100644 index 0000000000..9a660cdb5c --- /dev/null +++ b/spec/rubyspec/optional/capi/integer_spec.rb @@ -0,0 +1,275 @@ +# -*- encoding: binary -*- +require File.expand_path('../spec_helper', __FILE__) + +load_extension("integer") + +describe "CApiIntegerSpecs" do + before :each do + @s = CApiIntegerSpecs.new + end + + describe "rb_integer_pack" do + it "converts zero" do + words = "\000" * 9 + result = @s.rb_integer_pack(0, words, 1, 9, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == 0 + words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + + describe "without two's complement flag" do + before :each do + @value = 0x9876_abcd_4532_ef01_0123_4567_89ab_cdef + @words = "\000" * 16 + end + + describe "with big endian output" do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 2, 8, 0, + CApiIntegerSpecs::BIG_ENDIAN) + result.should == 1 + @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 2, 8, 0, + CApiIntegerSpecs::BIG_ENDIAN) + result.should == -1 + @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF" + end + + it "converts a negative number exactly -2**(numwords*wordsize*8)" do + result = @s.rb_integer_pack(-2**(2*8*8), @words, 2, 8, 0, + CApiIntegerSpecs::BIG_ENDIAN) + result.should == -2 + @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + end + + describe "with little endian output" do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 2, 8, 0, + CApiIntegerSpecs::LITTLE_ENDIAN) + result.should == 1 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 2, 8, 0, + CApiIntegerSpecs::LITTLE_ENDIAN) + result.should == -1 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98" + end + + it "converts a negative number exactly -2**(numwords*wordsize*8)" do + result = @s.rb_integer_pack(-2**(2*8*8), @words, 2, 8, 0, + CApiIntegerSpecs::LITTLE_ENDIAN) + result.should == -2 + @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + end + end + + describe "with two's complement flag" do + describe "with input less than 64 bits" do + before :each do + @value = 0x0123_4567_89ab_cdef + @words = "\000" * 8 + end + + describe "with big endian output" do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 8, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 8, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\xFE\xDC\xBA\x98\x76\x54\x32\x11" + end + end + + describe "with little endian output" do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 8, 0, + CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 8, 0, + CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE" + end + end + + describe "with native endian output" do + big_endian do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 8, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\x01\x23\x45\x67\x89\xAB\xCD\xEF" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 8, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\xFE\xDC\xBA\x98\x76\x54\x32\x11" + end + end + + little_endian do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 8, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 8, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE" + end + end + end + end + + describe "with input greater than 64 bits" do + before :each do + @value = 0x9876_abcd_4532_ef01_0123_4567_89ab_cdef + @words = "\000" * 16 + end + + describe "with big endian output" do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 2, 8, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 2, 8, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x67\x89\x54\x32\xBA\xCD\x10\xFE\xFE\xDC\xBA\x98\x76\x54\x32\x11" + end + + describe "with overflow" do + before :each do + @words = "\000" * 9 + end + + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 9, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == 2 + @words.should == "\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 9, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -2 + @words.should == "\xFE\xFE\xDC\xBA\x98\x76\x54\x32\x11" + end + + it "converts a negative number exactly -2**(numwords*wordsize*8)" do + result = @s.rb_integer_pack(-2**(9*8), @words, 1, 9, 0, + CApiIntegerSpecs::BIG_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + end + end + + describe "with little endian output" do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 2, 8, 0, + CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 2, 8, 0, + CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE\xFE\x10\xCD\xBA\x32\x54\x89\x67" + end + + describe "with overflow" do + before :each do + @words = "\000" * 9 + end + + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 9, 0, + CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == 2 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 9, 0, + CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -2 + @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE\xFE" + end + + it "converts a negative number exactly -2**(numwords*wordsize*8)" do + result = @s.rb_integer_pack(-2**(9*8), @words, 1, 9, 0, + CApiIntegerSpecs::LITTLE_ENDIAN|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + end + end + + describe "with native endian output" do + big_endian do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 16, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\x98\x76\xAB\xCD\x45\x32\xEF\x01\x01\x23\x45\x67\x89\xAB\xCD\xEF" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 16, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x67\x89\x54\x32\xBA\xCD\x10\xFE\xFE\xDC\xBA\x98\x76\x54\x32\x11" + end + end + + little_endian do + it "converts a positive number" do + result = @s.rb_integer_pack(@value, @words, 1, 16, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == 1 + @words.should == "\xEF\xCD\xAB\x89\x67\x45\x23\x01\x01\xEF\x32\x45\xCD\xAB\x76\x98" + end + + it "converts a negative number" do + result = @s.rb_integer_pack(-@value, @words, 1, 16, 0, + CApiIntegerSpecs::NATIVE|CApiIntegerSpecs::PACK_2COMP) + result.should == -1 + @words.should == "\x11\x32\x54\x76\x98\xBA\xDC\xFE\xFE\x10\xCD\xBA\x32\x54\x89\x67" + end + end + end + end + end + end +end diff --git a/spec/rubyspec/optional/capi/io_spec.rb b/spec/rubyspec/optional/capi/io_spec.rb new file mode 100644 index 0000000000..66bbc98a42 --- /dev/null +++ b/spec/rubyspec/optional/capi/io_spec.rb @@ -0,0 +1,342 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension('io') + +describe "C-API IO function" do + before :each do + @o = CApiIOSpecs.new + + @name = tmp("c_api_rb_io_specs") + touch @name + + @io = new_io @name, fmode("w:utf-8") + @io.sync = true + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + describe "rb_io_addstr" do + it "calls #to_s to convert the object to a String" do + obj = mock("rb_io_addstr string") + obj.should_receive(:to_s).and_return("rb_io_addstr data") + + @o.rb_io_addstr(@io, obj) + File.read(@name).should == "rb_io_addstr data" + end + + it "writes the String to the IO" do + @o.rb_io_addstr(@io, "rb_io_addstr data") + File.read(@name).should == "rb_io_addstr data" + end + + it "returns the io" do + @o.rb_io_addstr(@io, "rb_io_addstr data").should eql(@io) + end + end + + describe "rb_io_printf" do + it "calls #to_str to convert the format object to a String" do + obj = mock("rb_io_printf format") + obj.should_receive(:to_str).and_return("%s") + + @o.rb_io_printf(@io, [obj, "rb_io_printf"]) + File.read(@name).should == "rb_io_printf" + end + + it "calls #to_s to convert the object to a String" do + obj = mock("rb_io_printf string") + obj.should_receive(:to_s).and_return("rb_io_printf") + + @o.rb_io_printf(@io, ["%s", obj]) + File.read(@name).should == "rb_io_printf" + end + + it "writes the Strings to the IO" do + @o.rb_io_printf(@io, ["%s_%s_%s", "rb", "io", "printf"]) + File.read(@name).should == "rb_io_printf" + end + end + + describe "rb_io_print" do + it "calls #to_s to convert the object to a String" do + obj = mock("rb_io_print string") + obj.should_receive(:to_s).and_return("rb_io_print") + + @o.rb_io_print(@io, [obj]) + File.read(@name).should == "rb_io_print" + end + + it "writes the Strings to the IO with no separator" do + @o.rb_io_print(@io, ["rb_", "io_", "print"]) + File.read(@name).should == "rb_io_print" + end + end + + describe "rb_io_puts" do + it "calls #to_s to convert the object to a String" do + obj = mock("rb_io_puts string") + obj.should_receive(:to_s).and_return("rb_io_puts") + + @o.rb_io_puts(@io, [obj]) + File.read(@name).should == "rb_io_puts\n" + end + + it "writes the Strings to the IO separated by newlines" do + @o.rb_io_puts(@io, ["rb", "io", "write"]) + File.read(@name).should == "rb\nio\nwrite\n" + end + end + + describe "rb_io_write" do + it "calls #to_s to convert the object to a String" do + obj = mock("rb_io_write string") + obj.should_receive(:to_s).and_return("rb_io_write") + + @o.rb_io_write(@io, obj) + File.read(@name).should == "rb_io_write" + end + + it "writes the String to the IO" do + @o.rb_io_write(@io, "rb_io_write") + File.read(@name).should == "rb_io_write" + end + end +end + +describe "C-API IO function" do + before :each do + @o = CApiIOSpecs.new + + @name = tmp("c_api_io_specs") + touch @name + + @io = new_io @name, fmode("r:utf-8") + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + describe "rb_io_close" do + it "closes an IO object" do + @io.closed?.should be_false + @o.rb_io_close(@io) + @io.closed?.should be_true + end + end + + describe "rb_io_check_io" do + it "returns the IO object if it is valid" do + @o.rb_io_check_io(@io).should == @io + end + + it "returns nil for non IO objects" do + @o.rb_io_check_io({}).should be_nil + end + end + + describe "rb_io_check_closed" do + it "does not raise an exception if the IO is not closed" do + # The MRI function is void, so we use should_not raise_error + lambda { @o.rb_io_check_closed(@io) }.should_not raise_error + end + + it "raises an error if the IO is closed" do + @io.close + lambda { @o.rb_io_check_closed(@io) }.should raise_error(IOError) + end + end + + # NOTE: unlike the name might suggest in MRI this function checks if an + # object is frozen, *not* if it's tainted. + describe "rb_io_taint_check" do + it "does not raise an exception if the IO is not frozen" do + lambda { @o.rb_io_taint_check(@io) }.should_not raise_error + end + + it "raises an exception if the IO is frozen" do + @io.freeze + + lambda { @o.rb_io_taint_check(@io) }.should raise_error(RuntimeError) + end + end + + describe "GetOpenFile" do + it "allows access to the system fileno" do + @o.GetOpenFile_fd($stdin).should == 0 + @o.GetOpenFile_fd($stdout).should == 1 + @o.GetOpenFile_fd($stderr).should == 2 + @o.GetOpenFile_fd(@io).should == @io.fileno + end + end + + describe "rb_io_binmode" do + it "returns self" do + @o.rb_io_binmode(@io).should == @io + end + + it "sets binmode" do + @o.rb_io_binmode(@io) + @io.binmode?.should be_true + end + end +end + +describe "C-API IO function" do + before :each do + @o = CApiIOSpecs.new + @r_io, @w_io = IO.pipe + + @name = tmp("c_api_io_specs") + touch @name + @rw_io = new_io @name, fmode("w+") + end + + after :each do + @r_io.close unless @r_io.closed? + @w_io.close unless @w_io.closed? + @rw_io.close unless @rw_io.closed? + rm_r @name + end + + describe "rb_io_check_readable" do + it "does not raise an exception if the IO is opened for reading" do + # The MRI function is void, so we use should_not raise_error + lambda { @o.rb_io_check_readable(@r_io) }.should_not raise_error + end + + it "does not raise an exception if the IO is opened for read and write" do + lambda { @o.rb_io_check_readable(@rw_io) }.should_not raise_error + end + + it "raises an IOError if the IO is not opened for reading" do + lambda { @o.rb_io_check_readable(@w_io) }.should raise_error(IOError) + end + + end + + describe "rb_io_check_writable" do + it "does not raise an exeption if the IO is opened for writing" do + # The MRI function is void, so we use should_not raise_error + lambda { @o.rb_io_check_writable(@w_io) }.should_not raise_error + end + + it "does not raise an exception if the IO is opened for read and write" do + lambda { @o.rb_io_check_writable(@rw_io) }.should_not raise_error + end + + it "raises an IOError if the IO is not opened for reading" do + lambda { @o.rb_io_check_writable(@r_io) }.should raise_error(IOError) + end + end + + describe "rb_io_wait_writeable" do + it "returns false if there is no error condition" do + @o.rb_io_wait_writable(@w_io).should be_false + end + + it "raises an IOError if the IO is closed" do + @w_io.close + lambda { @o.rb_io_wait_writable(@w_io) }.should raise_error(IOError) + end + end + + describe "rb_thread_fd_writable" do + it "waits til an fd is ready for writing" do + @o.rb_thread_fd_writable(@w_io).should be_nil + end + end + + describe "rb_io_wait_readable" do + it "returns false if there is no error condition" do + @o.rb_io_wait_readable(@r_io, false).should be_false + end + + it "raises and IOError if passed a closed stream" do + @r_io.close + lambda { @o.rb_io_wait_readable(@r_io, false) }.should raise_error(IOError) + end + + it "blocks until the io is readable and returns true" do + @o.instance_variable_set :@write_data, false + thr = Thread.new do + sleep 0.1 until @o.instance_variable_get(:@write_data) + @w_io.write "rb_io_wait_readable" + end + + Thread.pass until thr.alive? + + @o.rb_io_wait_readable(@r_io, true).should be_true + @o.instance_variable_get(:@read_data).should == "rb_io_wait_re" + + thr.join + end + end + + describe "rb_thread_wait_fd" do + it "waits til an fd is ready for reading" do + start = false + thr = Thread.new do + start = true + sleep 0.5 + @w_io.write "rb_io_wait_readable" + end + + Thread.pass until start + + @o.rb_thread_wait_fd(@r_io).should be_nil + + thr.join + end + end + +end + +describe "rb_fd_fix_cloexec" do + + before :each do + @o = CApiIOSpecs.new + + @name = tmp("c_api_rb_io_specs") + touch @name + + @io = new_io @name, fmode("w:utf-8") + @io.close_on_exec = false + @io.sync = true + end + + after :each do + @io.close unless @io.closed? + rm_r @name + end + + it "sets close_on_exec on the IO" do + @o.rb_fd_fix_cloexec(@io) + @io.close_on_exec?.should be_true + end + +end + +describe "rb_cloexec_open" do + before :each do + @o = CApiIOSpecs.new + @name = tmp("c_api_rb_io_specs") + touch @name + + @io = nil + end + + after :each do + @io.close unless @io.nil? || @io.closed? + rm_r @name + end + + it "sets close_on_exec on the newly-opened IO" do + @io = @o.rb_cloexec_open(@name, 0, 0) + @io.close_on_exec?.should be_true + end +end diff --git a/spec/rubyspec/optional/capi/kernel_spec.rb b/spec/rubyspec/optional/capi/kernel_spec.rb new file mode 100644 index 0000000000..4fbebdd680 --- /dev/null +++ b/spec/rubyspec/optional/capi/kernel_spec.rb @@ -0,0 +1,476 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("kernel") + +describe "C-API Kernel function" do + before :each do + @s = CApiKernelSpecs.new + end + + describe "rb_block_given_p" do + it "returns false if no block is passed" do + @s.rb_block_given_p.should == false + end + + it "returns true if a block is passed" do + (@s.rb_block_given_p { puts "FOO" } ).should == true + end + end + + describe "rb_need_block" do + it "raises a LocalJumpError if no block is given" do + lambda { @s.rb_need_block }.should raise_error(LocalJumpError) + end + + it "does not raise a LocalJumpError if a block is given" do + @s.rb_need_block { }.should == nil + end + end + + describe "rb_block_call" do + before :each do + ScratchPad.record [] + end + + it "calls the block with a single argument" do + ary = [1, 3, 5] + @s.rb_block_call(ary).should == [2, 4, 6] + end + + it "calls the block with multiple arguments in argc / argv" do + ary = [1, 3, 5] + @s.rb_block_call_multi_arg(ary).should == 9 + end + + it "calls the method with no function callback and no block" do + ary = [1, 3, 5] + @s.rb_block_call_no_func(ary).should be_kind_of(Enumerator) + end + + it "calls the method with no function callback and a block" do + ary = [1, 3, 5] + @s.rb_block_call_no_func(ary) do |i| + i + 1 + end.should == [2, 4, 6] + end + end + + describe "rb_raise" do + it "raises an exception" do + lambda { @s.rb_raise({}) }.should raise_error(TypeError) + end + + it "terminates the function at the point it was called" do + h = {} + lambda { @s.rb_raise(h) }.should raise_error(TypeError) + h[:stage].should == :before + end + end + + describe "rb_throw" do + before :each do + ScratchPad.record [] + end + + it "sets the return value of the catch block to the specified value" do + catch(:foo) do + @s.rb_throw(:return_value) + end.should == :return_value + end + + it "terminates the function at the point it was called" do + catch(:foo) do + ScratchPad << :before_throw + @s.rb_throw(:thrown_value) + ScratchPad << :after_throw + end.should == :thrown_value + ScratchPad.recorded.should == [:before_throw] + end + + it "raises an ArgumentError if there is no catch block for the symbol" do + lambda { @s.rb_throw(nil) }.should raise_error(ArgumentError) + end + end + + describe "rb_throw_obj" do + before :each do + ScratchPad.record [] + @tag = Object.new + end + + it "sets the return value of the catch block to the specified value" do + catch(@tag) do + @s.rb_throw_obj(@tag, :thrown_value) + end.should == :thrown_value + end + + it "terminates the function at the point it was called" do + catch(@tag) do + ScratchPad << :before_throw + @s.rb_throw_obj(@tag, :thrown_value) + ScratchPad << :after_throw + end.should == :thrown_value + ScratchPad.recorded.should == [:before_throw] + end + + it "raises an ArgumentError if there is no catch block for the symbol" do + lambda { @s.rb_throw(nil) }.should raise_error(ArgumentError) + end + end + + describe "rb_warn" do + before :each do + @stderr, $stderr = $stderr, IOStub.new + @verbose = $VERBOSE + end + + after :each do + $stderr = @stderr + $VERBOSE = @verbose + end + + it "prints a message to $stderr if $VERBOSE evaluates to true" do + $VERBOSE = true + @s.rb_warn("This is a warning") + $stderr.should =~ /This is a warning/ + end + + it "prints a message to $stderr if $VERBOSE evaluates to false" do + $VERBOSE = false + @s.rb_warn("This is a warning") + $stderr.should =~ /This is a warning/ + end + end + + describe "rb_sys_fail" do + it "raises an exception from the value of errno" do + lambda do + @s.rb_sys_fail("additional info") + end.should raise_error(SystemCallError, /additional info/) + end + + it "can take a NULL message" do + lambda do + @s.rb_sys_fail(nil) + end.should raise_error(Errno::EPERM) + end + end + + describe "rb_syserr_fail" do + it "raises an exception from the given error" do + lambda do + @s.rb_syserr_fail(Errno::EINVAL::Errno, "additional info") + end.should raise_error(Errno::EINVAL, /additional info/) + end + + it "can take a NULL message" do + lambda do + @s.rb_syserr_fail(Errno::EINVAL::Errno, nil) + end.should raise_error(Errno::EINVAL) + end + end + + describe "rb_yield" do + it "yields passed argument" do + ret = nil + @s.rb_yield(1) { |z| ret = z } + ret.should == 1 + end + + it "returns the result from block evaluation" do + @s.rb_yield(1) { |z| z * 1000 }.should == 1000 + end + + it "raises LocalJumpError when no block is given" do + lambda { @s.rb_yield(1) }.should raise_error(LocalJumpError) + end + end + + describe "rb_yield_values" do + it "yields passed arguments" do + ret = nil + @s.rb_yield_values(1, 2) { |x, y| ret = x + y } + ret.should == 3 + end + + it "returns the result from block evaluation" do + @s.rb_yield_values(1, 2) { |x, y| x + y }.should == 3 + end + + it "raises LocalJumpError when no block is given" do + lambda { @s.rb_yield_splat([1, 2]) }.should raise_error(LocalJumpError) + end + end + + describe "rb_yield_splat" do + it "yields with passed array's contents" do + ret = nil + @s.rb_yield_splat([1, 2]) { |x, y| ret = x + y } + ret.should == 3 + end + + it "returns the result from block evaluation" do + @s.rb_yield_splat([1, 2]) { |x, y| x + y }.should == 3 + end + + it "raises LocalJumpError when no block is given" do + lambda { @s.rb_yield_splat([1, 2]) }.should raise_error(LocalJumpError) + end + end + + describe "rb_rescue" do + before :each do + @proc = lambda { |x| x } + @raise_proc_returns_sentinel = lambda {|*_| :raise_proc_executed } + @raise_proc_returns_arg = lambda {|*a| a } + @arg_error_proc = lambda { |*_| raise ArgumentError, '' } + @std_error_proc = lambda { |*_| raise StandardError, '' } + @exc_error_proc = lambda { |*_| raise Exception, '' } + end + + it "executes passed function" do + @s.rb_rescue(@proc, :no_exc, @raise_proc_returns_arg, :exc).should == :no_exc + end + + it "executes passed 'raise function' if a StandardError exception is raised" do + @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_sentinel, :exc).should == :raise_proc_executed + @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_sentinel, :exc).should == :raise_proc_executed + end + + it "passes the user supplied argument to the 'raise function' if a StandardError exception is raised" do + arg1, _ = @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_arg, :exc1) + arg1.should == :exc1 + + arg2, _ = @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_arg, :exc2) + arg2.should == :exc2 + end + + it "passes the raised exception to the 'raise function' if a StandardError exception is raised" do + _, exc1 = @s.rb_rescue(@arg_error_proc, nil, @raise_proc_returns_arg, :exc) + exc1.class.should == ArgumentError + + _, exc2 = @s.rb_rescue(@std_error_proc, nil, @raise_proc_returns_arg, :exc) + exc2.class.should == StandardError + end + + it "raises an exception if passed function raises an exception other than StandardError" do + lambda { @s.rb_rescue(@exc_error_proc, nil, @raise_proc_returns_arg, nil) }.should raise_error(Exception) + end + + it "raises an exception if any exception is raised inside 'raise function'" do + lambda { @s.rb_rescue(@std_error_proc, nil, @std_error_proc, nil) }.should raise_error(StandardError) + end + + it "makes $! available only during 'raise function' execution" do + @s.rb_rescue(@std_error_proc, nil, lambda { |*_| $! }, nil).class.should == StandardError + $!.should == nil + end + + it "returns the break value if the passed function yields to a block with a break" do + def proc_caller + @s.rb_rescue(lambda { |*_| yield }, nil, @proc, nil) + end + + proc_caller { break :value }.should == :value + end + end + + describe "rb_rescue2" do + it "only rescues if one of the passed exceptions is raised" do + proc = lambda { |x| x } + arg_error_proc = lambda { |*_| raise ArgumentError, '' } + run_error_proc = lambda { |*_| raise RuntimeError, '' } + type_error_proc = lambda { |*_| raise TypeError, '' } + @s.rb_rescue2(arg_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError).should == :exc + @s.rb_rescue2(run_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError).should == :exc + lambda { + @s.rb_rescue2(type_error_proc, :no_exc, proc, :exc, ArgumentError, RuntimeError) + }.should raise_error(TypeError) + end + end + + describe "rb_catch" do + before :each do + ScratchPad.record [] + end + + it "executes passed function" do + @s.rb_catch("foo", lambda { 1 }).should == 1 + end + + it "terminates the function at the point it was called" do + proc = lambda do + ScratchPad << :before_throw + throw :thrown_value + ScratchPad << :after_throw + end + @s.rb_catch("thrown_value", proc).should be_nil + ScratchPad.recorded.should == [:before_throw] + end + + it "raises an ArgumentError if the throw symbol isn't caught" do + lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(ArgumentError) + end + end + + describe "rb_catch_obj" do + + before :each do + ScratchPad.record [] + @tag = Object.new + end + + it "executes passed function" do + @s.rb_catch_obj(@tag, lambda { 1 }).should == 1 + end + + it "terminates the function at the point it was called" do + proc = lambda do + ScratchPad << :before_throw + throw @tag + ScratchPad << :after_throw + end + @s.rb_catch_obj(@tag, proc).should be_nil + ScratchPad.recorded.should == [:before_throw] + end + + it "raises an ArgumentError if the throw symbol isn't caught" do + lambda { @s.rb_catch("foo", lambda { throw :bar }) }.should raise_error(ArgumentError) + end + end + + describe "rb_ensure" do + it "executes passed function and returns its value" do + proc = lambda { |x| x } + @s.rb_ensure(proc, :proc, proc, :ensure_proc).should == :proc + end + + it "executes passed 'ensure function' when no exception is raised" do + foo = nil + proc = lambda { |*_| } + ensure_proc = lambda { |x| foo = x } + @s.rb_ensure(proc, nil, ensure_proc, :foo) + foo.should == :foo + end + + it "executes passed 'ensure function' when an exception is raised" do + foo = nil + raise_proc = lambda { raise '' } + ensure_proc = lambda { |x| foo = x } + @s.rb_ensure(raise_proc, nil, ensure_proc, :foo) rescue nil + foo.should == :foo + end + + it "raises the same exception raised inside passed function" do + raise_proc = lambda { |*_| raise RuntimeError, 'foo' } + proc = lambda { |*_| } + lambda { @s.rb_ensure(raise_proc, nil, proc, nil) }.should raise_error(RuntimeError, 'foo') + end + end + + describe "rb_eval_string" do + it "evaluates a string of ruby code" do + @s.rb_eval_string("1+1").should == 2 + end + end + + describe "rb_block_proc" do + it "converts the implicit block into a proc" do + proc = @s.rb_block_proc() { 1+1 } + proc.should be_kind_of(Proc) + proc.call.should == 2 + end + end + + describe "rb_exec_recursive" do + it "detects recursive invocations of a method and indicates as such" do + s = "hello" + @s.rb_exec_recursive(s).should == s + end + end + + describe "rb_set_end_proc" do + before :each do + @r, @w = IO.pipe + end + + after :each do + @r.close + @w.close + Process.wait @pid + end + + it "runs a C function on shutdown" do + @pid = fork { + @s.rb_set_end_proc(@w) + } + + @r.read(1).should == "e" + end + end + + describe "rb_f_sprintf" do + it "returns a string according to format and arguments" do + @s.rb_f_sprintf(["%d %f %s", 10, 2.5, "test"]).should == "10 2.500000 test" + end + end + + describe "rb_make_backtrace" do + it "returns a caller backtrace" do + backtrace = @s.rb_make_backtrace + lines = backtrace.select {|l| l =~ /#{__FILE__}/ } + lines.should_not be_empty + end + end + + describe "rb_obj_method" do + it "returns the method object for a symbol" do + method = @s.rb_obj_method("test", :size) + method.owner.should == String + method.name.to_sym.should == :size + end + + it "returns the method object for a string" do + method = @s.rb_obj_method("test", "size") + method.owner.should == String + method.name.to_sym.should == :size + end + end + + describe "rb_funcall3" do + before :each do + @obj = Object.new + class << @obj + def method_public; :method_public end + def method_private; :method_private end + private :method_private + end + end + + it "calls a public method" do + @s.rb_funcall3(@obj, :method_public).should == :method_public + end + it "does not call a private method" do + lambda { @s.rb_funcall3(@obj, :method_private) }.should raise_error(NoMethodError, /private/) + end + end + + describe 'rb_funcall_with_block' do + before :each do + @obj = Object.new + class << @obj + def method_public; yield end + def method_private; yield end + private :method_private + end + end + + it "calls a method with block" do + @s.rb_funcall_with_block(@obj, :method_public, proc { :result }).should == :result + end + + it "does not call a private method" do + lambda { @s.rb_funcall_with_block(@obj, :method_private, proc { :result }) }.should raise_error(NoMethodError, /private/) + end + end +end diff --git a/spec/rubyspec/optional/capi/marshal_spec.rb b/spec/rubyspec/optional/capi/marshal_spec.rb new file mode 100644 index 0000000000..ee06f96b72 --- /dev/null +++ b/spec/rubyspec/optional/capi/marshal_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("marshal") + +describe "CApiMarshalSpecs" do + before :each do + @s = CApiMarshalSpecs.new + end + + describe "rb_marshal_dump" do + before :each do + @obj = "foo" + end + + it "marshals an object" do + expected = Marshal.dump(@obj) + + @s.rb_marshal_dump(@obj, nil).should == expected + end + + it "marshals an object and write to an IO when passed" do + expected_io = IOStub.new + test_io = IOStub.new + + Marshal.dump(@obj, expected_io) + + @s.rb_marshal_dump(@obj, test_io) + + test_io.should == expected_io + end + + end + + describe "rb_marshal_load" do + before :each do + @obj = "foo" + @data = Marshal.dump(@obj) + end + + it "unmarshals an object" do + @s.rb_marshal_load(@data).should == @obj + end + + end + +end diff --git a/spec/rubyspec/optional/capi/module_spec.rb b/spec/rubyspec/optional/capi/module_spec.rb new file mode 100644 index 0000000000..1c21ad2e7c --- /dev/null +++ b/spec/rubyspec/optional/capi/module_spec.rb @@ -0,0 +1,340 @@ +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/module', __FILE__) + +load_extension('module') + +describe "CApiModule" do + + before :each do + @m = CApiModuleSpecs.new + end + + describe "rb_define_global_const" do + it "defines a constant on Object" do + @m.rb_define_global_const("CApiModuleSpecsGlobalConst", 7) + ::CApiModuleSpecsGlobalConst.should == 7 + Object.send :remove_const, :CApiModuleSpecsGlobalConst + end + end + + describe "rb_const_set given a symbol name and a value" do + it "sets a new constant on a module" do + @m.rb_const_set(CApiModuleSpecs::C, :W, 7) + CApiModuleSpecs::C::W.should == 7 + end + + it "sets an existing constant's value" do + -> { + @m.rb_const_set(CApiModuleSpecs::C, :Z, 8) + }.should complain(/already initialized constant/) + CApiModuleSpecs::C::Z.should == 8 + end + end + + describe "rb_define_module" do + it "returns the module if it is already defined" do + mod = @m.rb_define_module("CApiModuleSpecsModuleA") + mod.const_get(:X).should == 1 + end + + it "raises a TypeError if the constant is not a module" do + ::CApiModuleSpecsGlobalConst = 7 + lambda { @m.rb_define_module("CApiModuleSpecsGlobalConst") }.should raise_error(TypeError) + Object.send :remove_const, :CApiModuleSpecsGlobalConst + end + + it "defines a new module at toplevel" do + mod = @m.rb_define_module("CApiModuleSpecsModuleB") + mod.should be_kind_of(Module) + mod.name.should == "CApiModuleSpecsModuleB" + ::CApiModuleSpecsModuleB.should be_kind_of(Module) + Object.send :remove_const, :CApiModuleSpecsModuleB + end + end + + describe "rb_define_module_under" do + it "creates a new module inside the inner class" do + mod = @m.rb_define_module_under(CApiModuleSpecs, "ModuleSpecsModuleUnder1") + mod.should be_kind_of(Module) + end + + it "sets the module name" do + mod = @m.rb_define_module_under(CApiModuleSpecs, "ModuleSpecsModuleUnder2") + mod.name.should == "CApiModuleSpecs::ModuleSpecsModuleUnder2" + end + + it "defines a module for an existing Autoload with an extension" do + compile_extension("module_under_autoload") + + CApiModuleSpecs::ModuleUnderAutoload.name.should == "CApiModuleSpecs::ModuleUnderAutoload" + end + + it "defines a module for an existing Autoload with a ruby object" do + compile_extension("module_under_autoload") + + CApiModuleSpecs::RubyUnderAutoload.name.should == "CApiModuleSpecs::RubyUnderAutoload" + end + end + + describe "rb_define_const given a String name and a value" do + it "defines a new constant on a module" do + @m.rb_define_const(CApiModuleSpecs::C, "V", 7) + CApiModuleSpecs::C::V.should == 7 + end + + it "sets an existing constant's value" do + -> { + @m.rb_define_const(CApiModuleSpecs::C, "Z", 9) + }.should complain(/already initialized constant/) + CApiModuleSpecs::C::Z.should == 9 + end + end + + describe "rb_const_defined" do + # The fixture converts C boolean test to Ruby 'true' / 'false' + it "returns C non-zero if a constant is defined" do + @m.rb_const_defined(CApiModuleSpecs::A, :X).should be_true + end + + it "returns C non-zero if a constant is defined in Object" do + @m.rb_const_defined(CApiModuleSpecs::A, :Module).should be_true + end + end + + describe "rb_const_defined_at" do + # The fixture converts C boolean test to Ruby 'true' / 'false' + it "returns C non-zero if a constant is defined" do + @m.rb_const_defined_at(CApiModuleSpecs::A, :X).should be_true + end + + it "does not search in ancestors for the constant" do + @m.rb_const_defined_at(CApiModuleSpecs::B, :X).should be_false + end + + it "does not search in Object" do + @m.rb_const_defined_at(CApiModuleSpecs::A, :Module).should be_false + end + end + + describe "rb_const_get" do + it "returns a constant defined in the module" do + @m.rb_const_get(CApiModuleSpecs::A, :X).should == 1 + end + + it "returns a constant defined at toplevel" do + @m.rb_const_get(CApiModuleSpecs::A, :Fixnum).should == Fixnum + end + + it "returns a constant defined in a superclass" do + @m.rb_const_get(CApiModuleSpecs::B, :X).should == 1 + end + + it "calls #const_missing if the constant is not defined in the class or ancestors" do + CApiModuleSpecs::A.should_receive(:const_missing).with(:CApiModuleSpecsUndefined) + @m.rb_const_get(CApiModuleSpecs::A, :CApiModuleSpecsUndefined) + end + + it "resolves autoload constants in classes" do + @m.rb_const_get(CApiModuleSpecs::A, :D).should == 123 + end + + it "resolves autoload constants in Object" do + @m.rb_const_get(Object, :CApiModuleSpecsAutoload).should == 123 + end + end + + describe "rb_const_get_from" do + it "returns a constant defined in the module" do + @m.rb_const_get_from(CApiModuleSpecs::B, :Y).should == 2 + end + + it "returns a constant defined in a superclass" do + @m.rb_const_get_from(CApiModuleSpecs::B, :X).should == 1 + end + + it "calls #const_missing if the constant is not defined in the class or ancestors" do + CApiModuleSpecs::M.should_receive(:const_missing).with(:Fixnum) + @m.rb_const_get_from(CApiModuleSpecs::M, :Fixnum) + end + + it "resolves autoload constants" do + @m.rb_const_get_from(CApiModuleSpecs::A, :C).should == 123 + end + end + + describe "rb_const_get_at" do + it "returns a constant defined in the module" do + @m.rb_const_get_at(CApiModuleSpecs::B, :Y).should == 2 + end + + it "resolves autoload constants" do + @m.rb_const_get_at(CApiModuleSpecs::A, :B).should == 123 + end + + it "calls #const_missing if the constant is not defined in the module" do + CApiModuleSpecs::B.should_receive(:const_missing).with(:X) + @m.rb_const_get_at(CApiModuleSpecs::B, :X) + end + end + + describe "rb_define_alias" do + it "defines an alias for an existing method" do + cls = Class.new do + def method_to_be_aliased + :method_to_be_aliased + end + end + + @m.rb_define_alias cls, "method_alias", "method_to_be_aliased" + cls.new.method_alias.should == :method_to_be_aliased + end + end + + describe "rb_alias" do + it "defines an alias for an existing method" do + cls = Class.new do + def method_to_be_aliased + :method_to_be_aliased + end + end + + @m.rb_alias cls, :method_alias, :method_to_be_aliased + cls.new.method_alias.should == :method_to_be_aliased + end + end + + describe "rb_define_global_function" do + it "defines a method on Kernel" do + @m.rb_define_global_function("module_specs_global_function") + Kernel.should have_method(:module_specs_global_function) + module_specs_global_function.should == :test_method + end + end + + describe "rb_define_method" do + it "defines a method on a class" do + cls = Class.new + @m.rb_define_method(cls, "test_method") + cls.should have_instance_method(:test_method) + cls.new.test_method.should == :test_method + end + + it "defines a method on a module" do + mod = Module.new + @m.rb_define_method(mod, "test_method") + mod.should have_instance_method(:test_method) + end + end + + describe "rb_define_module_function" do + before :each do + @mod = Module.new + @m.rb_define_module_function @mod, "test_module_function" + end + + it "defines a module function" do + @mod.test_module_function.should == :test_method + end + + it "defines a private instance method" do + cls = Class.new + cls.include(@mod) + + cls.should have_private_instance_method(:test_module_function) + end + end + + describe "rb_define_private_method" do + it "defines a private method on a class" do + cls = Class.new + @m.rb_define_private_method(cls, "test_method") + cls.should have_private_instance_method(:test_method) + cls.new.send(:test_method).should == :test_method + end + + it "defines a private method on a module" do + mod = Module.new + @m.rb_define_private_method(mod, "test_method") + mod.should have_private_instance_method(:test_method) + end + end + + describe "rb_define_protected_method" do + it "defines a protected method on a class" do + cls = Class.new + @m.rb_define_protected_method(cls, "test_method") + cls.should have_protected_instance_method(:test_method) + cls.new.send(:test_method).should == :test_method + end + + it "defines a protected method on a module" do + mod = Module.new + @m.rb_define_protected_method(mod, "test_method") + mod.should have_protected_instance_method(:test_method) + end + end + + describe "rb_define_singleton_method" do + it "defines a method on the singleton class" do + cls = Class.new + a = cls.new + @m.rb_define_singleton_method a, "module_specs_singleton_method" + a.module_specs_singleton_method.should == :test_method + lambda { cls.new.module_specs_singleton_method }.should raise_error(NoMethodError) + end + end + + describe "rb_undef_method" do + before :each do + @class = Class.new do + def ruby_test_method + :ruby_test_method + end + end + end + + it "undef'ines a method on a class" do + @class.new.ruby_test_method.should == :ruby_test_method + @m.rb_undef_method @class, "ruby_test_method" + @class.should_not have_instance_method(:ruby_test_method) + end + + it "does not raise exceptions when passed a missing name" do + lambda { @m.rb_undef_method @class, "not_exist" }.should_not raise_error + end + + describe "when given a frozen Class" do + before :each do + @frozen = @class.dup.freeze + end + + it "raises a RuntimeError when passed a name" do + lambda { @m.rb_undef_method @frozen, "ruby_test_method" }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when passed a missing name" do + lambda { @m.rb_undef_method @frozen, "not_exist" }.should raise_error(RuntimeError) + end + end + end + + describe "rb_undef" do + it "undef'ines a method on a class" do + cls = Class.new do + def ruby_test_method + :ruby_test_method + end + end + + cls.new.ruby_test_method.should == :ruby_test_method + @m.rb_undef cls, :ruby_test_method + cls.should_not have_instance_method(:ruby_test_method) + end + end + + describe "rb_class2name" do + it "returns the module name" do + @m.rb_class2name(CApiModuleSpecs::M).should == "CApiModuleSpecs::M" + end + end +end diff --git a/spec/rubyspec/optional/capi/mutex_spec.rb b/spec/rubyspec/optional/capi/mutex_spec.rb new file mode 100644 index 0000000000..dae300f002 --- /dev/null +++ b/spec/rubyspec/optional/capi/mutex_spec.rb @@ -0,0 +1,88 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("mutex") + +describe "C-API Mutex functions" do + before :each do + @s = CApiMutexSpecs.new + @m = Mutex.new + end + + describe "rb_mutex_new" do + it "creates a new mutex" do + @s.rb_mutex_new.should be_an_instance_of(Mutex) + end + end + + describe "rb_mutex_locked_p" do + it "returns true if the mutex is locked" do + @s.rb_mutex_locked_p(@m).should be_false + end + + it "returns true if the mutex is locked" do + @m.lock + @s.rb_mutex_locked_p(@m).should be_true + end + end + + describe "rb_mutex_trylock" do + it "locks the mutex if not locked" do + @s.rb_mutex_trylock(@m).should be_true + @m.locked?.should be_true + end + + it "returns false if the mutex is already locked" do + @m.lock + @s.rb_mutex_trylock(@m).should be_false + @m.locked?.should be_true + end + end + + describe "rb_mutex_lock" do + it "returns when the mutex isn't locked" do + @s.rb_mutex_lock(@m).should == @m + @m.locked?.should be_true + end + + it "throws an exception when already locked in the same thread" do + @m.lock + lambda { @s.rb_mutex_lock(@m) }.should raise_error(ThreadError) + @m.locked?.should be_true + end + end + + describe "rb_mutex_unlock" do + it "raises an exception when not locked" do + lambda { @s.rb_mutex_unlock(@m) }.should raise_error(ThreadError) + @m.locked?.should be_false + end + + it "unlocks the mutex when locked" do + @m.lock + @s.rb_mutex_unlock(@m).should == @m + @m.locked?.should be_false + end + end + + describe "rb_mutex_sleep" do + it "throws an exception when the mutex is not locked" do + lambda { @s.rb_mutex_sleep(@m, 0.1) }.should raise_error(ThreadError) + @m.locked?.should be_false + end + + it "sleeps when the mutex is locked" do + @m.lock + start = Time.now + @s.rb_mutex_sleep(@m, 0.1) + (Time.now - start).should be_close(0.1, 0.2) + @m.locked?.should be_true + end + end + + describe "rb_mutex_synchronize" do + it "calls the function while the mutex is locked" do + callback = lambda { @m.locked?.should be_true } + @s.rb_mutex_synchronize(@m, callback) + end + end +end diff --git a/spec/rubyspec/optional/capi/numeric_spec.rb b/spec/rubyspec/optional/capi/numeric_spec.rb new file mode 100644 index 0000000000..fdf794247d --- /dev/null +++ b/spec/rubyspec/optional/capi/numeric_spec.rb @@ -0,0 +1,432 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("numeric") + +describe "CApiNumericSpecs" do + before :each do + @s = CApiNumericSpecs.new + end + + platform_is wordsize: 64 do + describe "rb_num2int" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_num2int(nil) }.should raise_error(TypeError) + end + + it "converts a Float" do + @s.rb_num2int(4.2).should == 4 + end + + it "converts a Bignum" do + @s.rb_num2int(0x7fff_ffff).should == 0x7fff_ffff + end + + it "converts a Fixnum" do + @s.rb_num2int(5).should == 5 + end + + it "converts -1 to an signed number" do + @s.rb_num2int(-1).should == -1 + end + + it "converts a negative Bignum into an signed number" do + @s.rb_num2int(-2147442171).should == -2147442171 + end + + it "raises a RangeError if the value is more than 32bits" do + lambda { @s.rb_num2int(0xffff_ffff+1) }.should raise_error(RangeError) + end + + it "calls #to_int to coerce the value" do + obj = mock("number") + obj.should_receive(:to_int).and_return(2) + @s.rb_num2long(obj).should == 2 + end + end + end + + platform_is wordsize: 64 do + describe "rb_num2uint" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_num2uint(nil) }.should raise_error(TypeError) + end + + it "converts a Float" do + @s.rb_num2uint(4.2).should == 4 + end + + it "converts a Bignum" do + @s.rb_num2uint(0xffff_ffff).should == 0xffff_ffff + end + + it "converts a Fixnum" do + @s.rb_num2uint(5).should == 5 + end + + it "converts a negative number to the complement" do + @s.rb_num2uint(-1).should == 18446744073709551615 + end + + it "converts a signed int value to the complement" do + @s.rb_num2uint(-0x8000_0000).should == 18446744071562067968 + end + + it "raises a RangeError if the value is more than 32bits" do + lambda { @s.rb_num2uint(0xffff_ffff+1) }.should raise_error(RangeError) + end + + it "raises a RangeError if the value is less than 32bits negative" do + lambda { @s.rb_num2uint(-0x8000_0000-1) }.should raise_error(RangeError) + end + + it "raises a RangeError if the value is more than 64bits" do + lambda do + @s.rb_num2uint(0xffff_ffff_ffff_ffff+1) + end.should raise_error(RangeError) + end + + it "calls #to_int to coerce the value" do + obj = mock("number") + obj.should_receive(:to_int).and_return(2) + @s.rb_num2uint(obj).should == 2 + end + end + end + + describe "rb_num2long" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_num2long(nil) }.should raise_error(TypeError) + end + + it "converts a Float" do + @s.rb_num2long(4.2).should == 4 + end + + it "converts a Bignum" do + @s.rb_num2long(0x7fff_ffff).should == 0x7fff_ffff + end + + it "converts a Fixnum" do + @s.rb_num2long(5).should == 5 + end + + platform_is wordsize: 32 do + it "converts -1 to an signed number" do + @s.rb_num2long(-1).should == -1 + end + + it "converts a negative Bignum into an signed number" do + @s.rb_num2long(-2147442171).should == -2147442171 + end + + it "raises a RangeError if the value is more than 32bits" do + lambda { @s.rb_num2long(0xffff_ffff+1) }.should raise_error(RangeError) + end + end + + platform_is wordsize: 64 do + it "converts -1 to an signed number" do + @s.rb_num2long(-1).should == -1 + end + + it "converts a negative Bignum into an signed number" do + @s.rb_num2long(-9223372036854734331).should == -9223372036854734331 + end + + it "raises a RangeError if the value is more than 64bits" do + lambda do + @s.rb_num2long(0xffff_ffff_ffff_ffff+1) + end.should raise_error(RangeError) + end + end + + it "calls #to_int to coerce the value" do + obj = mock("number") + obj.should_receive(:to_int).and_return(2) + @s.rb_num2long(obj).should == 2 + end + end + + describe "rb_int2num" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_int2num(nil) }.should raise_error(TypeError) + end + + it "converts a Float" do + @s.rb_int2num(4.2).should == 4 + end + + it "raises a RangeError when passed a Bignum" do + lambda { @s.rb_int2num(bignum_value) }.should raise_error(RangeError) + end + + it "converts a Fixnum" do + @s.rb_int2num(5).should == 5 + end + + it "converts a negative Fixnum" do + @s.rb_int2num(-11).should == -11 + end + end + + describe "rb_num2ulong" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_num2ulong(nil) }.should raise_error(TypeError) + end + + it "converts a Float" do + @s.rb_num2ulong(4.2).should == 4 + end + + it "converts a Bignum" do + @s.rb_num2ulong(0xffff_ffff).should == 0xffff_ffff + end + + it "converts a Fixnum" do + @s.rb_num2ulong(5).should == 5 + end + + platform_is wordsize: 32 do + it "converts -1 to an unsigned number" do + @s.rb_num2ulong(-1).should == 4294967295 + end + + it "converts a negative Bignum into an unsigned number" do + @s.rb_num2ulong(-2147442171).should == 2147525125 + end + + it "raises a RangeError if the value is more than 32bits" do + lambda { @s.rb_num2ulong(0xffff_ffff+1) }.should raise_error(RangeError) + end + end + + platform_is wordsize: 64 do + it "converts -1 to an unsigned number" do + @s.rb_num2ulong(-1).should == 18446744073709551615 + end + + it "converts a negative Bignum into an unsigned number" do + @s.rb_num2ulong(-9223372036854734331).should == 9223372036854817285 + end + + it "raises a RangeError if the value is more than 64bits" do + lambda do + @s.rb_num2ulong(0xffff_ffff_ffff_ffff+1) + end.should raise_error(RangeError) + end + end + + it "calls #to_int to coerce the value" do + obj = mock("number") + obj.should_receive(:to_int).and_return(2) + @s.rb_num2ulong(obj).should == 2 + end + end + + describe "rb_Integer" do + it "creates a new Integer from a String" do + i = @s.rb_Integer("8675309") + i.should be_kind_of(Integer) + i.should eql(8675309) + end + end + + describe "rb_ll2inum" do + it "creates a new Fixnum from a small signed long long" do + i = @s.rb_ll2inum_14() + i.should be_kind_of(Fixnum) + i.should eql(14) + end + end + + describe "rb_int2inum" do + it "creates a new Fixnum from a long" do + i = @s.rb_int2inum_14() + i.should be_kind_of(Fixnum) + i.should eql(14) + end + end + + describe "rb_num2dbl" do + it "raises a TypeError if passed nil" do + lambda { @s.rb_num2dbl(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a String" do + lambda { @s.rb_num2dbl("1.2") }.should raise_error(TypeError) + end + + it "converts a Float" do + @s.rb_num2dbl(4.2).should == 4.2 + end + + it "converts a Bignum" do + @s.rb_num2dbl(2**70).should == (2**70).to_f + end + + it "converts a Fixnum" do + @s.rb_num2dbl(5).should == 5.0 + end + + it "calls #to_f to coerce the value" do + obj = mock("number") + obj.should_receive(:to_f).and_return(2.0) + @s.rb_num2dbl(obj).should == 2.0 + end + end + + describe "NUM2CHR" do + it "returns the first character of a String" do + @s.NUM2CHR("Abc").should == 65 + end + + it "returns the least significant byte of an Integer" do + @s.NUM2CHR(0xa7c).should == 0x07c + end + + it "returns the least significant byte of a Float converted to an Integer" do + @s.NUM2CHR(0xa7c.to_f).should == 0x07c + end + + it "raises a TypeError when passed an empty String" do + lambda { @s.NUM2CHR("") }.should raise_error(TypeError) + end + end + + describe "rb_num_zerodiv" do + it "raises a RuntimeError" do + lambda { @s.rb_num_zerodiv() }.should raise_error(ZeroDivisionError, 'divided by 0') + end + end + + describe "rb_cmpint" do + it "returns a Fixnum if passed one" do + @s.rb_cmpint(1, 2).should == 1 + end + + it "uses > to check if the value is greater than 1" do + m = mock("number") + m.should_receive(:>).and_return(true) + @s.rb_cmpint(m, 4).should == 1 + end + + it "uses < to check if the value is less than 1" do + m = mock("number") + m.should_receive(:>).and_return(false) + m.should_receive(:<).and_return(true) + @s.rb_cmpint(m, 4).should == -1 + end + + it "returns 0 if < and > are false" do + m = mock("number") + m.should_receive(:>).and_return(false) + m.should_receive(:<).and_return(false) + @s.rb_cmpint(m, 4).should == 0 + end + + it "raises an ArgumentError when passed nil" do + lambda { + @s.rb_cmpint(nil, 4) + }.should raise_error(ArgumentError) + end + end + + describe "rb_num_coerce_bin" do + it "calls #coerce on the first argument" do + obj = mock("rb_num_coerce_bin") + obj.should_receive(:coerce).with(2).and_return([1, 2]) + + @s.rb_num_coerce_bin(2, obj, :+).should == 3 + end + + it "calls the specified method on the first argument returned by #coerce" do + obj = mock("rb_num_coerce_bin") + obj.should_receive(:coerce).with(2).and_return([obj, 2]) + obj.should_receive(:+).with(2).and_return(3) + + @s.rb_num_coerce_bin(2, obj, :+).should == 3 + end + + it "raises a TypeError if #coerce does not return an Array" do + obj = mock("rb_num_coerce_bin") + obj.should_receive(:coerce).with(2).and_return(nil) + + lambda { @s.rb_num_coerce_bin(2, obj, :+) }.should raise_error(TypeError) + end + end + + describe "rb_num_coerce_cmp" do + it "calls #coerce on the first argument" do + obj = mock("rb_num_coerce_cmp") + obj.should_receive(:coerce).with(2).and_return([1, 2]) + + @s.rb_num_coerce_cmp(2, obj, :<=>).should == -1 + end + + it "calls the specified method on the first argument returned by #coerce" do + obj = mock("rb_num_coerce_cmp") + obj.should_receive(:coerce).with(2).and_return([obj, 2]) + obj.should_receive(:<=>).with(2).and_return(-1) + + @s.rb_num_coerce_cmp(2, obj, :<=>).should == -1 + end + + ruby_version_is ""..."2.5" do + it "returns nil if passed nil" do + -> { + @result = @s.rb_num_coerce_cmp(nil, 2, :<=>) + }.should complain(/comparison operators will no more rescue exceptions/) + @result.should be_nil + end + end + + ruby_version_is "2.5" do + it "lets the exception go through if #coerce raises an exception" do + obj = mock("rb_num_coerce_cmp") + obj.should_receive(:coerce).with(2).and_raise(RuntimeError.new("my error")) + -> { + @s.rb_num_coerce_cmp(2, obj, :<=>) + }.should raise_error(RuntimeError, "my error") + end + end + + it "returns nil if #coerce does not return an Array" do + obj = mock("rb_num_coerce_cmp") + obj.should_receive(:coerce).with(2).and_return(nil) + + @s.rb_num_coerce_cmp(2, obj, :<=>).should be_nil + end + end + + describe "rb_num_coerce_relop" do + it "calls #coerce on the first argument" do + obj = mock("rb_num_coerce_relop") + obj.should_receive(:coerce).with(2).and_return([1, 2]) + + @s.rb_num_coerce_relop(2, obj, :<).should be_true + end + + it "calls the specified method on the first argument returned by #coerce" do + obj = mock("rb_num_coerce_relop") + obj.should_receive(:coerce).with(2).and_return([obj, 2]) + obj.should_receive(:<).with(2).and_return(false) + + @s.rb_num_coerce_relop(2, obj, :<).should be_false + end + + it "raises an ArgumentError if # returns nil" do + obj = mock("rb_num_coerce_relop") + obj.should_receive(:coerce).with(2).and_return([obj, 2]) + obj.should_receive(:<).with(2).and_return(nil) + + lambda { @s.rb_num_coerce_relop(2, obj, :<) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if #coerce does not return an Array" do + obj = mock("rb_num_coerce_relop") + obj.should_receive(:coerce).with(2).and_return(nil) + + lambda { @s.rb_num_coerce_relop(2, obj, :<) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/optional/capi/object_spec.rb b/spec/rubyspec/optional/capi/object_spec.rb new file mode 100644 index 0000000000..41224d0de3 --- /dev/null +++ b/spec/rubyspec/optional/capi/object_spec.rb @@ -0,0 +1,803 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("object") + +# TODO: fix all these specs + +class CApiObjectSpecs + class Alloc + attr_reader :initialized, :arguments + + def initialize(*args) + @initialized = true + @arguments = args + end + end + + class SubArray < ::Array + def to_array + self + end + end +end + +describe "CApiObject" do + + before do + @o = CApiObjectSpecs.new + end + + class ObjectTest + def initialize + @foo = 7 + end + + def foo + end + + def private_foo + end + private :private_foo + end + + class AryChild < Array + end + + class StrChild < String + end + + class DescObjectTest < ObjectTest + end + + class MethodArity + def one; end + def two(a); end + def three(*a); end + def four(a, b); end + def five(a, b, *c); end + def six(a, b, *c, &d); end + end + + describe "rb_obj_alloc" do + it "allocates a new uninitialized object" do + o = @o.rb_obj_alloc(CApiObjectSpecs::Alloc) + o.class.should == CApiObjectSpecs::Alloc + o.initialized.should be_nil + end + end + + describe "rb_obj_dup" do + it "duplicates an object" do + obj1 = ObjectTest.new + obj2 = @o.rb_obj_dup(obj1) + + obj2.class.should == obj1.class + + obj2.foo.should == obj1.foo + + obj2.should_not equal(obj1) + end + end + + describe "rb_obj_call_init" do + it "sends #initialize" do + o = @o.rb_obj_alloc(CApiObjectSpecs::Alloc) + o.initialized.should be_nil + + @o.rb_obj_call_init(o, 2, [:one, :two]) + o.initialized.should be_true + o.arguments.should == [:one, :two] + end + end + + describe "rb_is_instance_of" do + it "returns true if an object is an instance" do + @o.rb_obj_is_instance_of(ObjectTest.new, ObjectTest).should == true + @o.rb_obj_is_instance_of(DescObjectTest.new, ObjectTest).should == false + end + end + + describe "rb_is_kind_of" do + it "returns true if an object is an instance or descendent" do + @o.rb_obj_is_kind_of(ObjectTest.new, ObjectTest).should == true + @o.rb_obj_is_kind_of(DescObjectTest.new, ObjectTest).should == true + @o.rb_obj_is_kind_of(Object.new, ObjectTest).should == false + end + end + + describe "rb_respond_to" do + it "returns 1 if respond_to? is true and 0 if respond_to? is false" do + @o.rb_respond_to(ObjectTest.new, :foo).should == true + @o.rb_respond_to(ObjectTest.new, :bar).should == false + end + end + + describe "rb_obj_respond_to" do + it "returns true if respond_to? is true and false if respond_to? is false" do + @o.rb_obj_respond_to(ObjectTest.new, :foo, true).should == true + @o.rb_obj_respond_to(ObjectTest.new, :bar, true).should == false + @o.rb_obj_respond_to(ObjectTest.new, :private_foo, false).should == false + @o.rb_obj_respond_to(ObjectTest.new, :private_foo, true).should == true + end + end + + describe "rb_obj_method_arity" do + before :each do + @obj = MethodArity.new + end + + it "returns 0 when the method takes no arguments" do + @o.rb_obj_method_arity(@obj, :one).should == 0 + end + + it "returns 1 when the method takes a single, required argument" do + @o.rb_obj_method_arity(@obj, :two).should == 1 + end + + it "returns -1 when the method takes a variable number of arguments" do + @o.rb_obj_method_arity(@obj, :three).should == -1 + end + + it "returns 2 when the method takes two required arguments" do + @o.rb_obj_method_arity(@obj, :four).should == 2 + end + + it "returns -N-1 when the method takes N required and variable additional arguments" do + @o.rb_obj_method_arity(@obj, :five).should == -3 + end + + it "returns -N-1 when the method takes N required, variable additional, and a block argument" do + @o.rb_obj_method_arity(@obj, :six).should == -3 + end + end + + describe "rb_method_boundp" do + it "returns true when the given method is bound" do + @o.rb_method_boundp(Object, :class, true).should == true + @o.rb_method_boundp(Object, :class, false).should == true + @o.rb_method_boundp(Object, :initialize, true).should == false + @o.rb_method_boundp(Object, :initialize, false).should == true + end + + it "returns false when the given method is not bound" do + @o.rb_method_boundp(Object, :foo, true).should == false + @o.rb_method_boundp(Object, :foo, false).should == false + end + end + + describe "rb_to_id" do + it "returns a symbol representation of the object" do + @o.rb_to_id("foo").should == :foo + @o.rb_to_id(:foo).should == :foo + end + end + + describe "rb_require" do + before :each do + @saved_loaded_features = $LOADED_FEATURES.dup + end + + after :each do + $foo = nil + $LOADED_FEATURES.replace @saved_loaded_features + end + + it "requires a ruby file" do + $foo.should == nil + $:.unshift File.dirname(__FILE__) + @o.rb_require() + $foo.should == 7 + end + end + + describe "rb_attr_get" do + it "gets an instance variable" do + o = ObjectTest.new + @o.rb_attr_get(o, :@foo).should == 7 + end + end + + describe "rb_obj_instance_variables" do + it "returns an array with instance variable names as symbols" do + o = ObjectTest.new + @o.rb_obj_instance_variables(o).should include(:@foo) + end + end + + describe "rb_check_convert_type" do + it "returns the passed object and does not call the converting method if the object is the specified type" do + ary = [1, 2] + ary.should_not_receive(:to_ary) + + @o.rb_check_convert_type(ary, "Array", "to_ary").should equal(ary) + end + + it "returns the passed object and does not call the converting method if the object is a subclass of the specified type" do + obj = CApiObjectSpecs::SubArray.new + obj.should_not_receive(:to_array) + + @o.rb_check_convert_type(obj, "Array", "to_array").should equal(obj) + end + + it "returns nil if the converting method returns nil" do + obj = mock("rb_check_convert_type") + obj.should_receive(:to_array).and_return(nil) + + @o.rb_check_convert_type(obj, "Array", "to_array").should be_nil + end + + it "raises a TypeError if the converting method returns an object that is not the specified type" do + obj = mock("rb_check_convert_type") + obj.should_receive(:to_array).and_return("string") + + lambda do + @o.rb_check_convert_type(obj, "Array", "to_array") + end.should raise_error(TypeError) + end + end + + describe "rb_convert_type" do + it "returns the passed object and does not call the converting method if the object is the specified type" do + ary = [1, 2] + ary.should_not_receive(:to_ary) + + @o.rb_convert_type(ary, "Array", "to_ary").should equal(ary) + end + + it "returns the passed object and does not call the converting method if the object is a subclass of the specified type" do + obj = CApiObjectSpecs::SubArray.new + obj.should_not_receive(:to_array) + + @o.rb_convert_type(obj, "Array", "to_array").should equal(obj) + end + + it "raises a TypeError if the converting method returns nil" do + obj = mock("rb_convert_type") + obj.should_receive(:to_array).and_return(nil) + + lambda do + @o.rb_convert_type(obj, "Array", "to_array") + end.should raise_error(TypeError) + end + + it "raises a TypeError if the converting method returns an object that is not the specified type" do + obj = mock("rb_convert_type") + obj.should_receive(:to_array).and_return("string") + + lambda do + @o.rb_convert_type(obj, "Array", "to_array") + end.should raise_error(TypeError) + end + end + + describe "rb_check_array_type" do + it "returns the argument if it's an Array" do + x = Array.new + @o.rb_check_array_type(x).should equal(x) + end + + it "returns the argument if it's a kind of Array" do + x = AryChild.new + @o.rb_check_array_type(x).should equal(x) + end + + it "returns nil when the argument does not respond to #to_ary" do + @o.rb_check_array_type(Object.new).should be_nil + end + + it "sends #to_ary to the argument and returns the result if it's nil" do + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(nil) + @o.rb_check_array_type(obj).should be_nil + end + + it "sends #to_ary to the argument and returns the result if it's an Array" do + x = Array.new + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(x) + @o.rb_check_array_type(obj).should equal(x) + end + + it "sends #to_ary to the argument and returns the result if it's a kind of Array" do + x = AryChild.new + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(x) + @o.rb_check_array_type(obj).should equal(x) + end + + it "sends #to_ary to the argument and raises TypeError if it's not a kind of Array" do + obj = mock("to_ary") + obj.should_receive(:to_ary).and_return(Object.new) + lambda { @o.rb_check_array_type obj }.should raise_error(TypeError) + end + + it "does not rescue exceptions raised by #to_ary" do + obj = mock("to_ary") + obj.should_receive(:to_ary).and_raise(RuntimeError) + lambda { @o.rb_check_array_type obj }.should raise_error(RuntimeError) + end + end + + describe "rb_check_string_type" do + it "returns the argument if it's a String" do + x = String.new + @o.rb_check_string_type(x).should equal(x) + end + + it "returns the argument if it's a kind of String" do + x = StrChild.new + @o.rb_check_string_type(x).should equal(x) + end + + it "returns nil when the argument does not respond to #to_str" do + @o.rb_check_string_type(Object.new).should be_nil + end + + it "sends #to_str to the argument and returns the result if it's nil" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(nil) + @o.rb_check_string_type(obj).should be_nil + end + + it "sends #to_str to the argument and returns the result if it's a String" do + x = String.new + obj = mock("to_str") + obj.should_receive(:to_str).and_return(x) + @o.rb_check_string_type(obj).should equal(x) + end + + it "sends #to_str to the argument and returns the result if it's a kind of String" do + x = StrChild.new + obj = mock("to_str") + obj.should_receive(:to_str).and_return(x) + @o.rb_check_string_type(obj).should equal(x) + end + + it "sends #to_str to the argument and raises TypeError if it's not a kind of String" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return(Object.new) + lambda { @o.rb_check_string_type obj }.should raise_error(TypeError) + end + + it "does not rescue exceptions raised by #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_raise(RuntimeError) + lambda { @o.rb_check_string_type obj }.should raise_error(RuntimeError) + end + end + + describe "rb_check_to_integer" do + it "returns the object when passed a Fixnum" do + @o.rb_check_to_integer(5, "to_int").should equal(5) + end + + it "returns the object when passed a Bignum" do + @o.rb_check_to_integer(bignum_value, "to_int").should == bignum_value + end + + it "calls the converting method and returns a Fixnum value" do + obj = mock("rb_check_to_integer") + obj.should_receive(:to_integer).and_return(10) + + @o.rb_check_to_integer(obj, "to_integer").should equal(10) + end + + it "calls the converting method and returns a Bignum value" do + obj = mock("rb_check_to_integer") + obj.should_receive(:to_integer).and_return(bignum_value) + + @o.rb_check_to_integer(obj, "to_integer").should == bignum_value + end + + it "returns nil when the converting method returns nil" do + obj = mock("rb_check_to_integer") + obj.should_receive(:to_integer).and_return(nil) + + @o.rb_check_to_integer(obj, "to_integer").should be_nil + end + + it "returns nil when the converting method does not return an Integer" do + obj = mock("rb_check_to_integer") + obj.should_receive(:to_integer).and_return("string") + + @o.rb_check_to_integer(obj, "to_integer").should be_nil + end + end + + describe "rb_inspect" do + it "returns a string with the inspect representation" do + @o.rb_inspect(nil).should == "nil" + @o.rb_inspect(0).should == '0' + @o.rb_inspect([1,2,3]).should == '[1, 2, 3]' + @o.rb_inspect("0").should == '"0"' + end + end + + describe "rb_class_of" do + it "returns the class of an object" do + @o.rb_class_of(nil).should == NilClass + @o.rb_class_of(0).should == Fixnum + @o.rb_class_of(0.1).should == Float + @o.rb_class_of(ObjectTest.new).should == ObjectTest + end + end + + describe "rb_obj_classname" do + it "returns the class name of an object" do + @o.rb_obj_classname(nil).should == 'NilClass' + @o.rb_obj_classname(0).should == Fixnum.to_s + @o.rb_obj_classname(0.1).should == 'Float' + @o.rb_obj_classname(ObjectTest.new).should == 'ObjectTest' + end + end + + describe "rb_type" do + it "returns the type constant for the object" do + class DescArray < Array + end + @o.rb_is_type_nil(nil).should == true + @o.rb_is_type_object([]).should == false + @o.rb_is_type_object(ObjectTest.new).should == true + @o.rb_is_type_array([]).should == true + @o.rb_is_type_array(DescArray.new).should == true + @o.rb_is_type_module(ObjectTest).should == false + @o.rb_is_type_class(ObjectTest).should == true + @o.rb_is_type_data(Time.now).should == true + end + end + + describe "rb_type_p" do + it "returns whether object is of the given type" do + class DescArray < Array + end + @o.rb_is_rb_type_p_nil(nil).should == true + @o.rb_is_rb_type_p_object([]).should == false + @o.rb_is_rb_type_p_object(ObjectTest.new).should == true + @o.rb_is_rb_type_p_array([]).should == true + @o.rb_is_rb_type_p_array(DescArray.new).should == true + @o.rb_is_rb_type_p_module(ObjectTest).should == false + @o.rb_is_rb_type_p_class(ObjectTest).should == true + @o.rb_is_rb_type_p_data(Time.now).should == true + end + end + + describe "BUILTIN_TYPE" do + it "returns the type constant for the object" do + class DescArray < Array + end + @o.rb_is_builtin_type_object([]).should == false + @o.rb_is_builtin_type_object(ObjectTest.new).should == true + @o.rb_is_builtin_type_array([]).should == true + @o.rb_is_builtin_type_array(DescArray.new).should == true + @o.rb_is_builtin_type_module(ObjectTest).should == false + @o.rb_is_builtin_type_class(ObjectTest).should == true + @o.rb_is_builtin_type_data(Time.now).should == true + end + end + + describe "RTEST" do + it "returns C false if passed Qfalse" do + @o.RTEST(false).should be_false + end + + it "returns C false if passed Qnil" do + @o.RTEST(nil).should be_false + end + + it "returns C true if passed Qtrue" do + @o.RTEST(true).should be_true + end + + it "returns C true if passed a Symbol" do + @o.RTEST(:test).should be_true + end + + it "returns C true if passed an Object" do + @o.RTEST(Object.new).should be_true + end + end + + describe "rb_special_const_p" do + it "returns true if passed Qfalse" do + @o.rb_special_const_p(false).should be_true + end + + it "returns true if passed Qtrue" do + @o.rb_special_const_p(true).should be_true + end + + it "returns true if passed Qnil" do + @o.rb_special_const_p(nil).should be_true + end + + it "returns true if passed a Symbol" do + @o.rb_special_const_p(:test).should be_true + end + + it "returns true if passed a Fixnum" do + @o.rb_special_const_p(10).should be_true + end + + it "returns false if passed an Object" do + @o.rb_special_const_p(Object.new).should be_false + end + end + + describe "rb_extend_object" do + it "adds the module's instance methods to the object" do + module CApiObjectSpecs::Extend + def reach + :extended + end + end + + obj = mock("extended object") + @o.rb_extend_object(obj, CApiObjectSpecs::Extend) + obj.reach.should == :extended + end + end + + describe "OBJ_TAINT" do + it "taints the object" do + obj = mock("tainted") + @o.OBJ_TAINT(obj) + obj.tainted?.should be_true + end + end + + describe "OBJ_TAINTED" do + it "returns C true if the object is tainted" do + obj = mock("tainted") + obj.taint + @o.OBJ_TAINTED(obj).should be_true + end + + it "returns C false if the object is not tainted" do + obj = mock("untainted") + @o.OBJ_TAINTED(obj).should be_false + end + end + + describe "OBJ_INFECT" do + it "does not taint the first argument if the second argument is not tainted" do + host = mock("host") + source = mock("source") + @o.OBJ_INFECT(host, source) + host.tainted?.should be_false + end + + it "taints the first argument if the second argument is tainted" do + host = mock("host") + source = mock("source").taint + @o.OBJ_INFECT(host, source) + host.tainted?.should be_true + end + + it "does not untrust the first argument if the second argument is trusted" do + host = mock("host") + source = mock("source") + @o.OBJ_INFECT(host, source) + host.untrusted?.should be_false + end + + it "untrusts the first argument if the second argument is untrusted" do + host = mock("host") + source = mock("source").untrust + @o.OBJ_INFECT(host, source) + host.untrusted?.should be_true + end + + it "propagates both taint and distrust" do + host = mock("host") + source = mock("source").taint.untrust + @o.OBJ_INFECT(host, source) + host.tainted?.should be_true + host.untrusted?.should be_true + end + end + + describe "rb_obj_freeze" do + it "freezes the object passed to it" do + obj = "" + @o.rb_obj_freeze(obj).should == obj + obj.frozen?.should be_true + end + end + + describe "rb_obj_instance_eval" do + it "evaluates the block in the object context, that includes private methods" do + obj = ObjectTest + lambda do + @o.rb_obj_instance_eval(obj) { include Kernel } + end.should_not raise_error(NoMethodError) + end + end + + describe "rb_obj_frozen_p" do + it "returns true if object passed to it is frozen" do + obj = "" + obj.freeze + @o.rb_obj_frozen_p(obj).should == true + end + + it "returns false if object passed to it is not frozen" do + obj = "" + @o.rb_obj_frozen_p(obj).should == false + end + end + + describe "rb_obj_taint" do + it "marks the object passed as tainted" do + obj = "" + obj.tainted?.should == false + @o.rb_obj_taint(obj) + obj.tainted?.should == true + end + + it "raises a RuntimeError if the object passed is frozen" do + lambda { @o.rb_obj_taint("".freeze) }.should raise_error(RuntimeError) + end + end + + describe "rb_check_frozen" do + it "raises a RuntimeError if the obj is frozen" do + lambda { @o.rb_check_frozen("".freeze) }.should raise_error(RuntimeError) + end + + it "does nothing when object isn't frozen" do + obj = "" + lambda { @o.rb_check_frozen(obj) }.should_not raise_error(TypeError) + end + end + + describe "rb_any_to_s" do + it "converts an Integer to string" do + obj = 1 + i = @o.rb_any_to_s(obj) + i.should be_kind_of(String) + end + + it "converts an Object to string" do + obj = Object.new + i = @o.rb_any_to_s(obj) + i.should be_kind_of(String) + end + end + + describe "rb_to_int" do + it "returns self when called on an Integer" do + @o.rb_to_int(5).should == 5 + end + + it "returns self when called on a Bignum" do + @o.rb_to_int(bignum_value).should == bignum_value + end + + it "calls #to_int to convert and object to an integer" do + x = mock("to_int") + x.should_receive(:to_int).and_return(5) + @o.rb_to_int(x).should == 5 + end + + it "converts a Float to an Integer by truncation" do + @o.rb_to_int(1.35).should == 1 + end + + it "raises a TypeError if #to_int does not return an Integer" do + x = mock("to_int") + x.should_receive(:to_int).and_return("5") + lambda { @o.rb_to_int(x) }.should raise_error(TypeError) + end + + it "raises a TypeError if called with nil" do + lambda { @o.rb_to_int(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if called with true" do + lambda { @o.rb_to_int(true) }.should raise_error(TypeError) + end + + it "raises a TypeError if called with false" do + lambda { @o.rb_to_int(false) }.should raise_error(TypeError) + end + + it "raises a TypeError if called with a String" do + lambda { @o.rb_to_int("1") }.should raise_error(TypeError) + end + end + + describe "rb_equal" do + it "returns true if the arguments are the same exact object" do + s = "hello" + @o.rb_equal(s, s).should be_true + end + + it "calls == to check equality and coerces to true/false" do + m = mock("string") + m.should_receive(:==).and_return(8) + @o.rb_equal(m, "hello").should be_true + + m2 = mock("string") + m2.should_receive(:==).and_return(nil) + @o.rb_equal(m2, "hello").should be_false + end + end + + describe "rb_class_inherited_p" do + + it "returns true if mod equals arg" do + @o.rb_class_inherited_p(Array, Array).should be_true + end + + it "returns true if mod is a subclass of arg" do + @o.rb_class_inherited_p(Array, Object).should be_true + end + + it "returns nil if mod is not a subclass of arg" do + @o.rb_class_inherited_p(Array, Hash).should be_nil + end + + it "raises a TypeError if arg is no class or module" do + lambda{ + @o.rb_class_inherited_p(1, 2) + }.should raise_error(TypeError) + end + + end + + describe "instance variable access" do + before do + @test = ObjectTest.new + end + + describe "rb_iv_get" do + it "returns the instance variable on an object" do + @o.rb_iv_get(@test, "@foo").should == @test.instance_eval { @foo } + end + + it "returns nil if the instance variable has not been initialized" do + @o.rb_iv_get(@test, "@bar").should == nil + end + end + + describe "rb_iv_set" do + it "sets and returns the instance variable on an object" do + @o.rb_iv_set(@test, "@foo", 42).should == 42 + @test.instance_eval { @foo }.should == 42 + end + + it "sets and returns the instance variable with a bare name" do + @o.rb_iv_set(@test, "foo", 42).should == 42 + @o.rb_iv_get(@test, "foo").should == 42 + @test.instance_eval { @foo }.should == 7 + end + end + + describe "rb_ivar_get" do + it "returns the instance variable on an object" do + @o.rb_ivar_get(@test, :@foo).should == @test.instance_eval { @foo } + end + + it "returns nil if the instance variable has not been initialized" do + @o.rb_ivar_get(@test, :@bar).should == nil + end + end + + describe "rb_ivar_set" do + it "sets and returns the instance variable on an object" do + @o.rb_ivar_set(@test, :@foo, 42).should == 42 + @test.instance_eval { @foo }.should == 42 + end + end + + describe "rb_ivar_defined" do + it "returns true if the instance variable is defined" do + @o.rb_ivar_defined(@test, :@foo).should == true + end + + it "returns false if the instance variable is not defined" do + @o.rb_ivar_defined(@test, :@bar).should == false + end + end + end +end diff --git a/spec/rubyspec/optional/capi/proc_spec.rb b/spec/rubyspec/optional/capi/proc_spec.rb new file mode 100644 index 0000000000..c325e0fdfc --- /dev/null +++ b/spec/rubyspec/optional/capi/proc_spec.rb @@ -0,0 +1,98 @@ +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../fixtures/proc', __FILE__) + +load_extension("proc") + +describe "C-API Proc function" do + before :each do + @p = CApiProcSpecs.new + @prc = @p.rb_proc_new + end + + describe "rb_proc_new" do + it "returns a new valid Proc" do + @prc.kind_of?(Proc).should == true + end + + it "calls the C function wrapped by the Proc instance when sent #call" do + @prc.call(:foo_bar).should == ":foo_bar" + @prc.call([:foo, :bar]).should == "[:foo, :bar]" + end + + it "calls the C function wrapped by the Proc instance when sent #[]" do + @prc[:foo_bar].should == ":foo_bar" + @prc[[:foo, :bar]].should == "[:foo, :bar]" + end + + it "returns a Proc instance correctly described in #inspect without source location" do + @prc.inspect.should =~ /^#$/ + end + + it "returns a Proc instance with #arity == -1" do + @prc.arity.should == -1 + end + + it "shouldn't be equal to another one" do + @prc.should_not == @p.rb_proc_new + end + + it "returns a Proc instance with #source_location == nil" do + @prc.source_location.should == nil + end + end +end + +describe "C-API when calling Proc.new from a C function" do + before :each do + @p = CApiProcSpecs.new + end + + # In the scenarios below: X -> Y means execution context X called to Y. + # For example: Ruby -> C means a Ruby code called a C function. + # + # X -> Y <- X -> Z means exection context X called Y which returned to X, + # then X called Z. + # For example: C -> Ruby <- C -> Ruby means a C function called into Ruby + # code which returned to C, then C called into Ruby code again. + + # Ruby -> C -> rb_funcall(Proc.new) + it "returns the Proc passed by the Ruby code calling the C function" do + prc = @p.rb_Proc_new(0) { :called } + prc.call.should == :called + end + + # Ruby -> C -> Ruby <- C -> rb_funcall(Proc.new) + it "returns the Proc passed to the Ruby method when the C function calls other Ruby methods before calling Proc.new" do + prc = @p.rb_Proc_new(1) { :called } + prc.call.should == :called + end + + # Ruby -> C -> Ruby -> Proc.new + it "raises an ArgumentError when the C function calls a Ruby method that calls Proc.new" do + def @p.Proc_new() Proc.new end + lambda { @p.rb_Proc_new(2) { :called } }.should raise_error(ArgumentError) + end + + # Ruby -> C -> Ruby -> C -> rb_funcall(Proc.new) + it "raises an ArgumentError when the C function calls a Ruby method and that method calls a C function that calls Proc.new" do + def @p.redispatch() rb_Proc_new(0) end + lambda { @p.rb_Proc_new(3) { :called } }.should raise_error(ArgumentError) + end + + # Ruby -> C -> Ruby -> C (with new block) -> rb_funcall(Proc.new) + it "returns the most recent Proc passed when the Ruby method called the C function" do + prc = @p.rb_Proc_new(4) { :called } + prc.call.should == :calling_with_block + end + + # Ruby -> C -> Ruby -> C (with new block) <- Ruby <- C -> # rb_funcall(Proc.new) + it "returns the Proc passed from the original Ruby call to the C function" do + prc = @p.rb_Proc_new(5) { :called } + prc.call.should == :called + end + + # Ruby -> C -> Ruby -> block_given? + it "returns false from block_given? in a Ruby method called by the C function" do + @p.rb_Proc_new(6).should be_false + end +end diff --git a/spec/rubyspec/optional/capi/rake_helper.rb b/spec/rubyspec/optional/capi/rake_helper.rb new file mode 100644 index 0000000000..2b7a6ccbe3 --- /dev/null +++ b/spec/rubyspec/optional/capi/rake_helper.rb @@ -0,0 +1,23 @@ +require 'rake' +require 'rake/clean' + +input = "#{$cwd}/#{$ext_name}.c" +common = "-I shotgun/lib/subtend -g #{input}" + +case PLATFORM +when /darwin/ + output = "#{$cwd}/#{$ext_name}.bundle" + build_cmd = "cc -bundle -undefined suppress -flat_namespace #{common} -o #{output}" +else + output = "#{$cwd}/#{$ext_name}.so" + build_cmd = "cc -shared #{common} -o #{output}" +end + +CLOBBER.include(output) + +task default: [output] + +file output => [input] do + sh build_cmd +end + diff --git a/spec/rubyspec/optional/capi/range_spec.rb b/spec/rubyspec/optional/capi/range_spec.rb new file mode 100644 index 0000000000..b145079f39 --- /dev/null +++ b/spec/rubyspec/optional/capi/range_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("range") + +describe "C-API Range function" do + before :each do + @s = CApiRangeSpecs.new + end + + describe "rb_range_new" do + it "constructs a range using the given start and end" do + range = @s.rb_range_new('a', 'c') + range.should == ('a'..'c') + + range.first.should == 'a' + range.last.should == 'c' + end + + it "includes the end object when the third parameter is omitted or false" do + @s.rb_range_new('a', 'c').to_a.should == ['a', 'b', 'c'] + @s.rb_range_new(1, 3).to_a.should == [1, 2, 3] + + @s.rb_range_new('a', 'c', false).to_a.should == ['a', 'b', 'c'] + @s.rb_range_new(1, 3, false).to_a.should == [1, 2, 3] + + @s.rb_range_new('a', 'c', true).to_a.should == ['a', 'b'] + @s.rb_range_new(1, 3, 1).to_a.should == [1, 2] + + @s.rb_range_new(1, 3, mock('[1,2]')).to_a.should == [1, 2] + @s.rb_range_new(1, 3, :test).to_a.should == [1, 2] + end + + it "raises an ArgumentError when the given start and end can't be compared by using #<=>" do + lambda { @s.rb_range_new(1, mock('x')) }.should raise_error(ArgumentError) + lambda { @s.rb_range_new(mock('x'), mock('y')) }.should raise_error(ArgumentError) + end + end + + describe "rb_range_values" do + it "stores the range properties" do + beg, fin, excl = @s.rb_range_values(10..20) + beg.should == 10 + fin.should == 20 + excl.should be_false + end + + it "stores the range properties of non-Range object" do + range_like = mock('range') + + def range_like.begin + 10 + end + + def range_like.end + 20 + end + + def range_like.exclude_end? + false + end + + beg, fin, excl = @s.rb_range_values(range_like) + beg.should == 10 + fin.should == 20 + excl.should be_false + end + end +end diff --git a/spec/rubyspec/optional/capi/rational_spec.rb b/spec/rubyspec/optional/capi/rational_spec.rb new file mode 100644 index 0000000000..e3144666d3 --- /dev/null +++ b/spec/rubyspec/optional/capi/rational_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("rational") + +describe :rb_Rational, shared: true do + it "creates a new Rational with numerator and denominator" do + @r.send(@method, 1, 2).should == Rational(1, 2) + end +end + +describe :rb_rational_new, shared: true do + it "creates a normalized Rational" do + r = @r.send(@method, 10, 4) + r.numerator.should == 5 + r.denominator.should == 2 + end +end + +describe "CApiRationalSpecs" do + before :each do + @r = CApiRationalSpecs.new + end + + describe "rb_Rational" do + it_behaves_like :rb_Rational, :rb_Rational + end + + describe "rb_Rational2" do + it_behaves_like :rb_Rational, :rb_Rational2 + end + + describe "rb_Rational1" do + it "creates a new Rational with numerator and denominator of 1" do + @r.rb_Rational1(5).should == Rational(5, 1) + end + end + + describe "rb_rational_new" do + it_behaves_like :rb_rational_new, :rb_rational_new + end + + describe "rb_rational_new2" do + it_behaves_like :rb_rational_new, :rb_rational_new2 + end + + describe "rb_rational_num" do + it "returns the numerator of a Rational" do + @r.rb_rational_num(Rational(7, 2)).should == 7 + end + end + + describe "rb_rational_den" do + it "returns the denominator of a Rational" do + @r.rb_rational_den(Rational(7, 2)).should == 2 + end + end +end diff --git a/spec/rubyspec/optional/capi/regexp_spec.rb b/spec/rubyspec/optional/capi/regexp_spec.rb new file mode 100644 index 0000000000..27b1627c50 --- /dev/null +++ b/spec/rubyspec/optional/capi/regexp_spec.rb @@ -0,0 +1,71 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("regexp") + +describe "C-API Regexp function" do + before :each do + @p = CApiRegexpSpecs.new + end + + describe "rb_reg_new" do + it "returns a new valid Regexp" do + my_re = @p.a_re + my_re.kind_of?(Regexp).should == true + ('1a' =~ my_re).should == 1 + ('1b' =~ my_re).should == nil + my_re.source.should == 'a' + end + end + + describe "rb_reg_nth_match" do + it "returns a the appropriate match data entry" do + @p.a_re_1st_match(/([ab])/.match("a")).should == 'a' + @p.a_re_1st_match(/([ab])/.match("b")).should == 'b' + @p.a_re_1st_match(/[ab]/.match("a")).should == nil + @p.a_re_1st_match(/[ab]/.match("c")).should == nil + end + end + + describe "rb_reg_options" do + it "returns the options used to create the regexp" do + @p.rb_reg_options(/42/im).should == //im.options + @p.rb_reg_options(/42/i).should == //i.options + @p.rb_reg_options(/42/m).should == //m.options + end + end + + describe "rb_reg_regcomp" do + it "creates a valid regexp from a string" do + regexp = /\b([A-Z0-9._%+-]+)\.{2,4}/ + @p.rb_reg_regcomp(regexp.source).should == regexp + end + end + + it "allows matching in C, properly setting the back references" do + mail_regexp = /\b([A-Z0-9._%+-]+)@([A-Z0-9.-]+\.[A-Z]{2,4})\b/i + name = "john.doe" + domain = "example.com" + @p.match(mail_regexp, "#{name}@#{domain}") + $1.should == name + $2.should == domain + end + + describe "rb_reg_match" do + it "returns the matched position or nil" do + @p.rb_reg_match(/a/, 'ab').should == 0 + @p.rb_reg_match(/b/, 'ab').should == 1 + @p.rb_reg_match(/c/, 'ab').should == nil + end + end + + describe "rb_backref_get" do + it "returns the last MatchData" do + md = /a/.match('ab') + @p.rb_backref_get.should == md + md = /b/.match('ab') + @p.rb_backref_get.should == md + md = /c/.match('ab') + @p.rb_backref_get.should == md + end + end +end diff --git a/spec/rubyspec/optional/capi/spec_helper.rb b/spec/rubyspec/optional/capi/spec_helper.rb new file mode 100644 index 0000000000..ce8a07aeba --- /dev/null +++ b/spec/rubyspec/optional/capi/spec_helper.rb @@ -0,0 +1,160 @@ +require File.expand_path('../../../spec_helper', __FILE__) +$extmk = false + +require 'rbconfig' +require 'fileutils' +require 'tmpdir' + +OBJDIR ||= File.expand_path("../../../ext/#{RUBY_NAME}/#{RUBY_VERSION}", __FILE__) +FileUtils.makedirs(OBJDIR) + +def extension_path + File.expand_path("../ext", __FILE__) +end + +def object_path + OBJDIR +end + +def compile_extension(name) + preloadenv = RbConfig::CONFIG["PRELOADENV"] || "LD_PRELOAD" + preload, ENV[preloadenv] = ENV[preloadenv], nil if preloadenv + + path = extension_path + objdir = object_path + + # TODO use rakelib/ext_helper.rb? + arch_hdrdir = nil + ruby_hdrdir = nil + + if RUBY_NAME == 'rbx' + hdrdir = RbConfig::CONFIG["rubyhdrdir"] + elsif RUBY_NAME =~ /^ruby/ + if hdrdir = RbConfig::CONFIG["rubyhdrdir"] + arch_hdrdir = RbConfig::CONFIG["rubyarchhdrdir"] || + File.join(hdrdir, RbConfig::CONFIG["arch"]) + ruby_hdrdir = File.join hdrdir, "ruby" + else + hdrdir = RbConfig::CONFIG["archdir"] + end + elsif RUBY_NAME == 'jruby' + require 'mkmf' + hdrdir = $hdrdir + elsif RUBY_NAME == "maglev" + require 'mkmf' + hdrdir = $hdrdir + elsif RUBY_NAME == 'truffleruby' + return compile_truffleruby_extconf_make(name, path, objdir) + else + raise "Don't know how to build C extensions with #{RUBY_NAME}" + end + + ext = "#{name}_spec" + source = File.join(path, "#{ext}.c") + obj = File.join(objdir, "#{ext}.#{RbConfig::CONFIG['OBJEXT']}") + lib = File.join(objdir, "#{ext}.#{RbConfig::CONFIG['DLEXT']}") + + ruby_header = File.join(hdrdir, "ruby.h") + rubyspec_header = File.join(path, "rubyspec.h") + + return lib if File.exist?(lib) and File.mtime(lib) > File.mtime(source) and + File.mtime(lib) > File.mtime(ruby_header) and + File.mtime(lib) > File.mtime(rubyspec_header) and + true # sentinel + + # avoid problems where compilation failed but previous shlib exists + File.delete lib if File.exist? lib + + cc = RbConfig::CONFIG["CC"] + cflags = (ENV["CFLAGS"] || RbConfig::CONFIG["CFLAGS"]).dup + cflags += " #{RbConfig::CONFIG["ARCH_FLAG"]}" if RbConfig::CONFIG["ARCH_FLAG"] + cflags += " #{RbConfig::CONFIG["CCDLFLAGS"]}" if RbConfig::CONFIG["CCDLFLAGS"] + incflags = "-I#{path} -I#{hdrdir}" + incflags << " -I#{arch_hdrdir}" if arch_hdrdir + incflags << " -I#{ruby_hdrdir}" if ruby_hdrdir + + output = `#{cc} #{incflags} #{cflags} -c #{source} -o #{obj}` + + unless $?.success? and File.exist?(obj) + puts "ERROR:\n#{output}" + puts "incflags=#{incflags}" + puts "cflags=#{cflags}" + raise "Unable to compile \"#{source}\"" + end + + ldshared = RbConfig::CONFIG["LDSHARED"] + ldshared += " #{RbConfig::CONFIG["ARCH_FLAG"]}" if RbConfig::CONFIG["ARCH_FLAG"] + libpath = "-L#{path}" + libs = RbConfig::CONFIG["LIBS"] + dldflags = "#{RbConfig::CONFIG["LDFLAGS"]} #{RbConfig::CONFIG["DLDFLAGS"]} #{RbConfig::CONFIG["EXTDLDFLAGS"]}" + dldflags.sub!(/-Wl,-soname,\S+/, '') + dldflags.sub!("$(TARGET_ENTRY)", "Init_#{ext}") + + link_cmd = "#{ldshared} #{obj} #{libpath} #{dldflags} #{libs} -o #{lib}" + output = `#{link_cmd}` + + unless $?.success? + puts "ERROR:\n#{link_cmd}\n#{output}" + raise "Unable to link \"#{source}\"" + end + + lib +ensure + ENV[preloadenv] = preload if preloadenv +end + +def compile_extension_truffleruby(name) + sulong_config_file = File.join(extension_path, '.jruby-cext-build.yml') + output_file = File.join(object_path, "#{name}_spec.#{RbConfig::CONFIG['DLEXT']}") + + File.open(sulong_config_file, 'w') do |f| + f.puts "src: #{name}_spec.c" + f.puts "out: #{output_file}" + end + + command = ["#{RbConfig::CONFIG['bindir']}/../tool/jt.rb", 'cextc', extension_path] + system(*command) + raise "Compilation of #{extension_path} failed: #{$?}\n#{command.join(' ')}" unless $?.success? + + output_file +ensure + File.delete(sulong_config_file) if File.exist?(sulong_config_file) +end + +def compile_truffleruby_extconf_make(name, path, objdir) + ext = "#{name}_spec" + file = "#{ext}.c" + source = "#{path}/#{file}" + lib_target = "#{objdir}/#{ext}.#{RbConfig::CONFIG['DLEXT']}" + temp_dir = Dir.mktmpdir + begin + copy = "#{temp_dir}/#{file}" + FileUtils.cp "#{path}/rubyspec.h", temp_dir + FileUtils.cp "#{path}/truffleruby.h", temp_dir + FileUtils.cp source, copy + extconf_src = "require 'mkmf'\n" + + "create_makefile('#{ext}', '#{temp_dir}')" + File.write("#{temp_dir}/extconf.rb", extconf_src) + Dir.chdir(temp_dir) do + system "#{RbConfig.ruby} extconf.rb" + system "make" # run make in temp dir + FileUtils.cp "#{ext}.su", lib_target # copy to .su file to library dir + FileUtils.cp "#{ext}.bc", objdir # copy to .bc file to library dir + end + ensure + FileUtils.remove_entry temp_dir + end + lib_target +end + +def load_extension(name) + require compile_extension(name) +rescue LoadError + if %r{/usr/sbin/execerror ruby "\(ld 3 1 main ([/a-zA-Z0-9_\-.]+_spec\.so)"} =~ $!.message + system('/usr/sbin/execerror', "#{RbConfig::CONFIG["bindir"]}/ruby", "(ld 3 1 main #{$1}") + end + raise +end + +# Constants +CAPI_SIZEOF_LONG = [0].pack('l!').size diff --git a/spec/rubyspec/optional/capi/string_spec.rb b/spec/rubyspec/optional/capi/string_spec.rb new file mode 100644 index 0000000000..ce0485e8af --- /dev/null +++ b/spec/rubyspec/optional/capi/string_spec.rb @@ -0,0 +1,771 @@ +# encoding: utf-8 +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../../../shared/string/times', __FILE__) + +load_extension('string') + +describe :rb_str_new2, shared: true do + it "returns a new string object calling strlen on the passed C string" do + # Hardcoded to pass const char * = "hello\0invisible" + @s.send(@method, "hello\0not used").should == "hello" + end + + it "encodes the string with ASCII_8BIT" do + @s.send(@method, "hello").encoding.should == Encoding::ASCII_8BIT + end +end + +describe "C-API String function" do + before :each do + @s = CApiStringSpecs.new + end + + class ValidTostrTest + def to_str + "ruby" + end + end + + class InvalidTostrTest + def to_str + [] + end + end + + describe "rb_str_set_len" do + before :each do + # Make a completely new copy of the string + # for every example (#dup doesn't cut it). + @str = "abcdefghij"[0..-1] + end + + it "reduces the size of the string" do + @s.rb_str_set_len(@str, 5).should == "abcde" + end + + it "inserts a NULL byte at the length" do + @s.rb_str_set_len(@str, 5).should == "abcde" + @s.rb_str_set_len(@str, 8).should == "abcde\x00gh" + end + + it "updates the string's attributes visible in C code" do + @s.rb_str_set_len_RSTRING_LEN(@str, 4).should == 4 + end + end + + describe "rb_str_buf_new" do + it "returns the equivalent of an empty string" do + @s.rb_str_buf_new(10, nil).should == "" + end + + it "returns a string that can be appended to" do + str = @s.rb_str_buf_new(10, "defg") + str << "abcde" + str.should == "abcde" + end + + it "returns a string that can be concatenated to another string" do + str = @s.rb_str_buf_new(10, "defg") + ("abcde" + str).should == "abcde" + end + + it "returns a string whose bytes can be accessed by RSTRING_PTR" do + str = @s.rb_str_buf_new(10, "abcdefghi") + @s.rb_str_new(str, 10).should == "abcdefghi\x00" + end + + it "returns a string that can be modified by rb_str_set_len" do + str = @s.rb_str_buf_new(10, "abcdef") + @s.rb_str_set_len(str, 4) + str.should == "abcd" + + @s.rb_str_set_len(str, 8) + str[0, 6].should == "abcd\x00f" + @s.RSTRING_LEN(str).should == 8 + end + end + + describe "rb_str_buf_new2" do + it "returns a new string object calling strlen on the passed C string" do + # Hardcoded to pass const char * = "hello\0invisible" + @s.rb_str_buf_new2.should == "hello" + end + end + + describe "rb_str_new" do + it "returns a new string object from a char buffer of len characters" do + @s.rb_str_new("hello", 3).should == "hel" + end + + it "returns an empty string if len is 0" do + @s.rb_str_new("hello", 0).should == "" + end + end + + describe "rb_str_new2" do + it_behaves_like :rb_str_new2, :rb_str_new2 + end + + describe "rb_str_new" do + it "creates a new String with ASCII-8BIT Encoding" do + @s.rb_str_new("", 0).encoding.should == Encoding::ASCII_8BIT + end + end + + describe "rb_str_new_cstr" do + it_behaves_like :rb_str_new2, :rb_str_new_cstr + end + + describe "rb_usascii_str_new" do + it "creates a new String with US-ASCII Encoding from a char buffer of len characters" do + str = "abc".force_encoding("us-ascii") + result = @s.rb_usascii_str_new("abcdef", 3) + result.should == str + result.encoding.should == Encoding::US_ASCII + end + end + + describe "rb_usascii_str_new_cstr" do + it "creates a new String with US-ASCII Encoding" do + str = "abc".force_encoding("us-ascii") + result = @s.rb_usascii_str_new_cstr("abc") + result.should == str + result.encoding.should == Encoding::US_ASCII + end + end + + describe "rb_str_encode" do + it "returns a String in the destination encoding" do + result = @s.rb_str_encode("abc", Encoding::ISO_8859_1, 0, nil) + result.encoding.should == Encoding::ISO_8859_1 + end + + it "transcodes the String" do + result = @s.rb_str_encode("ありがとう", "euc-jp", 0, nil) + euc_jp = [0xa4, 0xa2, 0xa4, 0xea, 0xa4, 0xac, 0xa4, 0xc8, 0xa4, 0xa6].pack('C*').force_encoding("euc-jp") + result.should == euc_jp + result.encoding.should == Encoding::EUC_JP + end + + it "returns a dup of the original String" do + a = "abc" + b = @s.rb_str_encode("abc", "us-ascii", 0, nil) + a.should_not equal(b) + end + + it "returns a duplicate of the original when the encoding doesn't change" do + a = "abc" + b = @s.rb_str_encode("abc", Encoding::UTF_8, 0, nil) + a.should_not equal(b) + end + + it "accepts encoding flags" do + xFF = [0xFF].pack('C').force_encoding('utf-8') + result = @s.rb_str_encode("a#{xFF}c", "us-ascii", + Encoding::Converter::INVALID_REPLACE, nil) + result.should == "a?c" + result.encoding.should == Encoding::US_ASCII + end + + it "accepts an encoding options Hash specifying replacement String" do + # Yeah, MRI aborts with rb_bug() if the options Hash is not frozen + options = { replace: "b" }.freeze + xFF = [0xFF].pack('C').force_encoding('utf-8') + result = @s.rb_str_encode("a#{xFF}c", "us-ascii", + Encoding::Converter::INVALID_REPLACE, + options) + result.should == "abc" + result.encoding.should == Encoding::US_ASCII + end + end + + describe "rb_str_new3" do + it "returns a copy of the string" do + str1 = "hi" + str2 = @s.rb_str_new3 str1 + str1.should == str2 + str1.object_id.should_not == str2.object_id + end + end + + describe "rb_str_new4" do + it "returns the original string if it is already frozen" do + str1 = "hi" + str1.freeze + str2 = @s.rb_str_new4 str1 + str1.should == str2 + str1.should equal(str2) + str1.frozen?.should == true + str2.frozen?.should == true + end + + it "returns a frozen copy of the string" do + str1 = "hi" + str2 = @s.rb_str_new4 str1 + str1.should == str2 + str1.should_not equal(str2) + str2.frozen?.should == true + end + end + + describe "rb_str_dup" do + it "returns a copy of the string" do + str1 = "hi" + str2 = @s.rb_str_dup str1 + str1.should == str2 + str1.object_id.should_not == str2.object_id + end + end + + describe "rb_str_new5" do + it "returns a new string with the same class as the passed string" do + string_class = Class.new(String) + template_string = string_class.new("hello world") + new_string = @s.rb_str_new5(template_string, "hello world", 11) + + new_string.should == "hello world" + new_string.class.should == string_class + end + end + + describe "rb_str_append" do + it "appends a string to another string" do + @s.rb_str_append("Hello", " Goodbye").should == "Hello Goodbye" + end + + it "raises a TypeError trying to append non-String-like object" do + lambda { @s.rb_str_append("Hello", 32323)}.should raise_error(TypeError) + end + + it "changes Encoding if a string is appended to an empty string" do + string = "パスタ".encode(Encoding::ISO_2022_JP) + @s.rb_str_append("", string).encoding.should == Encoding::ISO_2022_JP + end + end + + describe "rb_str_plus" do + it "returns a new string from concatenating two other strings" do + @s.rb_str_plus("Hello", " Goodbye").should == "Hello Goodbye" + end + end + + describe "rb_str_times" do + it_behaves_like :string_times, :rb_str_times, ->(str, times) { @s.rb_str_times(str, times) } + end + + describe "rb_str_buf_cat" do + it "concatenates a C string to a ruby string" do + @s.rb_str_buf_cat("Your house is on fire").should == "Your house is on fire?" + end + end + + describe "rb_str_cat" do + it "concatenates a C string to ruby string" do + @s.rb_str_cat("Your house is on fire").should == "Your house is on fire?" + end + end + + describe "rb_str_cat2" do + it "concatenates a C string to a ruby string" do + @s.rb_str_cat2("Your house is on fire").should == "Your house is on fire?" + end + end + + describe "rb_str_cmp" do + it "returns 0 if two strings are identical" do + @s.rb_str_cmp("ppp", "ppp").should == 0 + end + + it "returns -1 if the first string is shorter than the second" do + @s.rb_str_cmp("xxx", "xxxx").should == -1 + end + + it "returns -1 if the first string is lexically less than the second" do + @s.rb_str_cmp("xxx", "yyy").should == -1 + end + + it "returns 1 if the first string is longer than the second" do + @s.rb_str_cmp("xxxx", "xxx").should == 1 + end + + it "returns 1 if the first string is lexically greater than the second" do + @s.rb_str_cmp("yyy", "xxx").should == 1 + end + end + + describe "rb_str_split" do + it "splits strings over a splitter" do + @s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"] + end + end + + describe "rb_str2inum" do + it "converts a string to a number given a base" do + @s.rb_str2inum("10", 10).should == 10 + @s.rb_str2inum("A", 16).should == 10 + end + end + + describe "rb_cstr2inum" do + it "converts a C string to a Fixnum given a base" do + @s.rb_cstr2inum("10", 10).should == 10 + @s.rb_cstr2inum("10", 16).should == 16 + end + + it "converts a C string to a Bignum given a base" do + @s.rb_cstr2inum(bignum_value.to_s, 10).should == bignum_value + end + + it "converts a C string to a Fixnum non-strictly if base is not 0" do + @s.rb_cstr2inum("1234a", 10).should == 1234 + end + + it "converts a C string to a Fixnum strictly if base is 0" do + lambda { @s.rb_cstr2inum("1234a", 0) }.should raise_error(ArgumentError) + end + end + + describe "rb_cstr_to_inum" do + it "converts a C string to a Fixnum given a base" do + @s.rb_cstr_to_inum("1234", 10, true).should == 1234 + end + + it "converts a C string to a Bignum given a base" do + @s.rb_cstr_to_inum(bignum_value.to_s, 10, true).should == bignum_value + end + + it "converts a C string to a Fixnum non-strictly" do + @s.rb_cstr_to_inum("1234a", 10, false).should == 1234 + end + + it "converts a C string to a Fixnum strictly" do + lambda { @s.rb_cstr_to_inum("1234a", 10, true) }.should raise_error(ArgumentError) + end + end + + describe "rb_str_subseq" do + it "returns a byte-indexed substring" do + str = "\x00\x01\x02\x03\x04".force_encoding("binary") + @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".force_encoding("binary") + end + end + + describe "rb_str_substr" do + it "returns a substring" do + "hello".length.times do |time| + @s.rb_str_substr("hello", 0, time + 1).should == "hello"[0..time] + end + end + end + + describe "rb_str_to_str" do + it "calls #to_str to coerce the value to a String" do + @s.rb_str_to_str("foo").should == "foo" + @s.rb_str_to_str(ValidTostrTest.new).should == "ruby" + end + + it "raises a TypeError if coercion fails" do + lambda { @s.rb_str_to_str(0) }.should raise_error(TypeError) + lambda { @s.rb_str_to_str(InvalidTostrTest.new) }.should raise_error(TypeError) + end + end + + describe "RSTRING_PTR" do + it "returns a pointer to the string's contents" do + str = "abc" + chars = [] + @s.RSTRING_PTR_iterate(str) do |c| + chars << c + end + chars.should == [97, 98, 99] + end + + it "allows changing the characters in the string" do + str = "abc" + @s.RSTRING_PTR_assign(str, 65) + str.should == "AAA" + end + + it "reflects changes after a rb_funcall" do + lamb = proc { |s| s.replace "NEW CONTENT" } + + str = "beforebefore" + + ret = @s.RSTRING_PTR_after_funcall(str, lamb) + + str.should == "NEW CONTENT" + ret.should == str + end + + it "returns a pointer to the contents of encoded pointer-sized string" do + s = "70パク". + encode(Encoding::UTF_16LE). + force_encoding(Encoding::UTF_16LE). + encode(Encoding::UTF_8) + + chars = [] + @s.RSTRING_PTR_iterate(s) do |c| + chars << c + end + chars.should == [55, 48, 227, 131, 145, 227, 130, 175] + end + end + + describe "RSTRING_LEN" do + it "returns the size of the string" do + @s.RSTRING_LEN("gumdrops").should == 8 + end + end + + describe "RSTRING_LENINT" do + it "returns the size of a string" do + @s.RSTRING_LENINT("silly").should == 5 + end + end + + describe "StringValue" do + it "does not call #to_str on a String" do + str = "genuine" + str.should_not_receive(:to_str) + @s.StringValue(str) + end + + it "does not call #to_s on a String" do + str = "genuine" + str.should_not_receive(:to_str) + @s.StringValue(str) + end + + it "calls #to_str on non-String objects" do + str = mock("fake") + str.should_receive(:to_str).and_return("wannabe") + @s.StringValue(str) + end + + it "does not call #to_s on non-String objects" do + str = mock("fake") + str.should_not_receive(:to_s) + lambda { @s.StringValue(str) }.should raise_error(TypeError) + end + end + + describe "rb_str_resize" do + it "reduces the size of the string" do + str = @s.rb_str_resize("test", 2) + str.size.should == 2 + @s.RSTRING_LEN(str).should == 2 + str.should == "te" + end + + it "updates the string's attributes visible in C code" do + @s.rb_str_resize_RSTRING_LEN("test", 2).should == 2 + end + + it "increases the size of the string" do + expected = "test".force_encoding("US-ASCII") + str = @s.rb_str_resize(expected.dup, 12) + str.size.should == 12 + @s.RSTRING_LEN(str).should == 12 + str[0, 4].should == expected + end + end + + describe "rb_str_inspect" do + it "returns the equivalent of calling #inspect on the String" do + @s.rb_str_inspect("value").should == %["value"] + end + end + + describe "rb_str_intern" do + it "returns a symbol created from the string" do + @s.rb_str_intern("symbol").should == :symbol + end + + it "returns a symbol even if passed an empty string" do + @s.rb_str_intern("").should == "".to_sym + end + + it "returns a symbol even if the passed string contains NULL characters" do + @s.rb_str_intern("no\0no").should == "no\0no".to_sym + end + end + + describe "rb_str_freeze" do + it "freezes the string" do + s = "" + @s.rb_str_freeze(s).should == s + s.frozen?.should be_true + end + end + + describe "rb_str_hash" do + it "hashes the string into a number" do + s = "hello" + @s.rb_str_hash(s).should == s.hash + end + end + + describe "rb_str_update" do + it "splices the replacement string into the original at the given location" do + @s.rb_str_update("hello", 2, 3, "wuh").should == "hewuh" + end + end +end + +describe "rb_str_free" do + # This spec only really exists to make sure the symbol + # is available. There is no guarantee this even does + # anything at all + it "indicates data for a string might be freed" do + @s.rb_str_free("xyz").should be_nil + end +end + +describe :rb_external_str_new, shared: true do + it "returns a String in the default external encoding" do + Encoding.default_external = "UTF-8" + @s.send(@method, "abc").encoding.should == Encoding::UTF_8 + end + + it "returns an ASCII-8BIT encoded string if any non-ascii bytes are present and default external is US-ASCII" do + Encoding.default_external = "US-ASCII" + x80 = [0x80].pack('C') + @s.send(@method, "#{x80}abc").encoding.should == Encoding::ASCII_8BIT + end + + it "returns a tainted String" do + @s.send(@method, "abc").tainted?.should be_true + end +end + +describe "C-API String function" do + before :each do + @s = CApiStringSpecs.new + @external = Encoding.default_external + @internal = Encoding.default_internal + end + + after :each do + Encoding.default_external = @external + Encoding.default_internal = @internal + end + + describe "rb_str_length" do + it "returns the string's length" do + @s.rb_str_length("dewdrops").should == 8 + end + + it "counts characters in multi byte encodings" do + @s.rb_str_length("düwdrops").should == 8 + end + end + + describe "rb_str_equal" do + it "compares two same strings" do + s = "hello" + @s.rb_str_equal(s, "hello").should be_true + end + + it "compares two different strings" do + s = "hello" + @s.rb_str_equal(s, "hella").should be_false + end + end + + describe "rb_external_str_new" do + it_behaves_like :rb_external_str_new, :rb_external_str_new + end + + describe "rb_external_str_new_cstr" do + it_behaves_like :rb_external_str_new, :rb_external_str_new_cstr + end + + describe "rb_external_str_new_with_enc" do + it "returns a String in the specified encoding" do + s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::UTF_8) + s.encoding.should == Encoding::UTF_8 + end + + it "returns an ASCII-8BIT encoded String if any non-ascii bytes are present and the specified encoding is US-ASCII" do + x80 = [0x80].pack('C') + s = @s.rb_external_str_new_with_enc("#{x80}abc", 4, Encoding::US_ASCII) + s.encoding.should == Encoding::ASCII_8BIT + end + + +# it "transcodes a String to Encoding.default_internal if it is set" do +# Encoding.default_internal = Encoding::EUC_JP +# +# - a = "\xE3\x81\x82\xe3\x82\x8c".force_encoding("utf-8") +# + a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8") +# s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8) +# - +# - s.should == "\xA4\xA2\xA4\xEC".force_encoding("euc-jp") +# + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('ascii-8bit') +# + s.should == x +# s.encoding.should equal(Encoding::EUC_JP) +# end + + it "transcodes a String to Encoding.default_internal if it is set" do + Encoding.default_internal = Encoding::EUC_JP + + a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8") + s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8) + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('euc-jp') + s.should == x + s.encoding.should equal(Encoding::EUC_JP) + end + + it "returns a tainted String" do + s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::US_ASCII) + s.tainted?.should be_true + end + end + + describe "rb_locale_str_new" do + it "returns a String with 'locale' encoding" do + s = @s.rb_locale_str_new("abc", 3) + s.should == "abc".force_encoding(Encoding.find("locale")) + s.encoding.should equal(Encoding.find("locale")) + end + end + + describe "rb_locale_str_new_cstr" do + it "returns a String with 'locale' encoding" do + s = @s.rb_locale_str_new_cstr("abc") + s.should == "abc".force_encoding(Encoding.find("locale")) + s.encoding.should equal(Encoding.find("locale")) + end + end + + describe "rb_str_conv_enc" do + it "returns the original String when to encoding is not specified" do + a = "abc".force_encoding("us-ascii") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should equal(a) + end + + it "returns the original String if a transcoding error occurs" do + a = [0xEE].pack('C').force_encoding("utf-8") + @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should equal(a) + end + + it "returns a transcoded String" do + a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8") + result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP) + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8') + result.should == x.force_encoding("euc-jp") + result.encoding.should equal(Encoding::EUC_JP) + end + + describe "when the String encoding is equal to the destination encoding" do + it "returns the original String" do + a = "abc".force_encoding("us-ascii") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a) + end + + it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do + a = "abc".encode("us-ascii") + @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::US_ASCII).should equal(a) + end + + it "returns the origin String if the destination encoding is ASCII-8BIT" do + a = "abc".force_encoding("ascii-8bit") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::ASCII_8BIT).should equal(a) + end + end + end + + describe "rb_str_conv_enc_opts" do + it "returns the original String when to encoding is not specified" do + a = "abc".force_encoding("us-ascii") + @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should equal(a) + end + + it "returns the original String if a transcoding error occurs" do + a = [0xEE].pack('C').force_encoding("utf-8") + @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, + Encoding::EUC_JP, 0, nil).should equal(a) + end + + it "returns a transcoded String" do + a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8") + result = @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, Encoding::EUC_JP, 0, nil) + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8') + result.should == x.force_encoding("euc-jp") + result.encoding.should equal(Encoding::EUC_JP) + end + + describe "when the String encoding is equal to the destination encoding" do + it "returns the original String" do + a = "abc".force_encoding("us-ascii") + @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, + Encoding::US_ASCII, 0, nil).should equal(a) + end + + it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do + a = "abc".encode("us-ascii") + @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, + Encoding::US_ASCII, 0, nil).should equal(a) + end + + it "returns the origin String if the destination encoding is ASCII-8BIT" do + a = "abc".force_encoding("ascii-8bit") + @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, + Encoding::ASCII_8BIT, 0, nil).should equal(a) + end + end + end + + describe "rb_str_export" do + it "returns the original String with the external encoding" do + Encoding.default_external = Encoding::ISO_8859_1 + s = @s.rb_str_export("Hëllo") + s.encoding.should equal(Encoding::ISO_8859_1) + end + end + + describe "rb_str_export_locale" do + it "returns the original String with the locale encoding" do + s = @s.rb_str_export_locale("abc") + s.should == "abc".force_encoding(Encoding.find("locale")) + s.encoding.should equal(Encoding.find("locale")) + end + end + + describe "rb_sprintf" do + it "replaces the parts like sprintf" do + @s.rb_sprintf1("Awesome %s is replaced", "string").should == "Awesome string is replaced" + @s.rb_sprintf1("%s", "TestFoobarTest").should == "TestFoobarTest" + end + + it "accepts multiple arguments" do + s = "Awesome %s is here with %s" + @s.rb_sprintf2(s, "string", "content").should == "Awesome string is here with content" + end + end + + describe "rb_vsprintf" do + it "returns a formatted String from a variable number of arguments" do + s = @s.rb_vsprintf("%s, %d, %.2f", "abc", 42, 2.7); + s.should == "abc, 42, 2.70" + end + end + + describe "rb_String" do + it "returns the passed argument if it is a string" do + @s.rb_String("a").should == "a" + end + + it "tries to convert the passed argument to a string by calling #to_str first" do + @s.rb_String(ValidTostrTest.new).should == "ruby" + end + + it "raises a TypeError if #to_str does not return a string" do + lambda { @s.rb_String(InvalidTostrTest.new) }.should raise_error(TypeError) + end + + it "tries to convert the passed argument to a string by calling #to_s" do + @s.rb_String({"bar" => "foo"}).should == '{"bar"=>"foo"}' + end + end +end diff --git a/spec/rubyspec/optional/capi/struct_spec.rb b/spec/rubyspec/optional/capi/struct_spec.rb new file mode 100644 index 0000000000..9a0eafeb7b --- /dev/null +++ b/spec/rubyspec/optional/capi/struct_spec.rb @@ -0,0 +1,209 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("struct") + +describe "C-API Struct function" do + before :each do + @s = CApiStructSpecs.new + @struct = @s.rb_struct_define("CAPIStruct", "a", "b", "c") + end + + after :each do + Struct.send(:remove_const, :CAPIStruct) + end + + describe "rb_struct_define" do + it "creates accessors for the struct members" do + instance = @struct.new + instance.a = 1 + instance.b = 2 + instance.c = 3 + instance.a.should == 1 + instance.b.should == 2 + instance.c.should == 3 + end + + it "has a value of nil for the member of a newly created instance" do + # Verify that attributes are on an instance basis + Struct::CAPIStruct.new.b.should be_nil + end + + it "creates a constant scoped under Struct for the named Struct" do + Struct.should have_constant(:CAPIStruct) + end + + it "returns the member names as Symbols" do + @struct.members.should == [:a, :b, :c] + end + end +end + +describe "C-API Struct function" do + before :each do + @s = CApiStructSpecs.new + @struct = @s.rb_struct_define(nil, "a", "b", "c") + end + + describe "rb_struct_define for an anonymous struct" do + it "creates accessors for the struct members" do + instance = @struct.new + instance.a = 1 + instance.b = 2 + instance.c = 3 + instance.a.should == 1 + instance.b.should == 2 + instance.c.should == 3 + end + + it "returns the member names as Symbols" do + @struct.members.should == [:a, :b, :c] + end + end +end + +describe "C-API Struct function" do + before :each do + @s = CApiStructSpecs.new + @struct = @s.rb_struct_define_under(CApiStructSpecs, "CAPIStruct", "a", "b", "c") + end + + describe "rb_struct_define_under" do + it "creates accessors for the struct members" do + instance = @struct.new + instance.a = 1 + instance.b = 2 + instance.c = 3 + instance.a.should == 1 + instance.b.should == 2 + instance.c.should == 3 + end + + it "has a value of nil for the member of a newly created instance" do + # Verify that attributes are on an instance basis + CApiStructSpecs::CAPIStruct.new.b.should be_nil + end + + it "creates a constant scoped under the namespace of the given class" do + CApiStructSpecs.should have_constant(:CAPIStruct) + end + + it "returns the member names as Symbols" do + @struct.members.should == [:a, :b, :c] + end + end +end + +describe "C-API Struct function" do + before :each do + @s = CApiStructSpecs.new + @klass = Struct.new(:a, :b, :c) + @struct = @klass.new + end + + describe "rb_struct_define" do + it "raises an ArgumentError if arguments contain duplicate member name" do + lambda { @s.rb_struct_define(nil, "a", "b", "a") }.should raise_error(ArgumentError) + end + + it "raises a NameError if an invalid constant name is given" do + lambda { @s.rb_struct_define("foo", "a", "b", "c") }.should raise_error(NameError) + end + end + + describe "rb_struct_aref" do + it "returns the value of a struct member with a symbol key" do + @struct[:a] = 2 + @s.rb_struct_aref(@struct, :a).should == 2 + end + + it "returns the value of a struct member with a string key" do + @struct[:b] = 2 + @s.rb_struct_aref(@struct, "b").should == 2 + end + + it "returns the value of a struct member by index" do + @struct[:c] = 3 + @s.rb_struct_aref(@struct, 2).should == 3 + end + + it "raises a NameError if the struct member does not exist" do + lambda { @s.rb_struct_aref(@struct, :d) }.should raise_error(NameError) + end + + it "raises an IndexError if the given index is out of range" do + lambda { @s.rb_struct_aref(@struct, -4) }.should raise_error(IndexError) + lambda { @s.rb_struct_aref(@struct, 3) }.should raise_error(IndexError) + end + end + + describe "rb_struct_getmember" do + it "returns the value of a struct member" do + @struct[:a] = 2 + @s.rb_struct_getmember(@struct, :a).should == 2 + end + + it "raises a NameError if the struct member does not exist" do + lambda { @s.rb_struct_getmember(@struct, :d) }.should raise_error(NameError) + end + end + + describe "rb_struct_s_members" do + it "returns the struct members as an array of symbols" do + @s.rb_struct_s_members(@klass).should == [:a, :b, :c] + end + end + + describe "rb_struct_members" do + it "returns the struct members as an array of symbols" do + @s.rb_struct_members(@struct).should == [:a, :b, :c] + end + end + + describe "rb_struct_aset" do + it "sets the value of a struct member with a symbol key" do + @s.rb_struct_aset(@struct, :a, 1) + @struct[:a].should == 1 + end + + it "sets the value of a struct member with a string key" do + @s.rb_struct_aset(@struct, "b", 1) + @struct[:b].should == 1 + end + + it "sets the value of a struct member by index" do + @s.rb_struct_aset(@struct, 2, 1) + @struct[:c].should == 1 + end + + it "raises a NameError if the struct member does not exist" do + lambda { @s.rb_struct_aset(@struct, :d, 1) }.should raise_error(NameError) + end + + it "raises an IndexError if the given index is out of range" do + lambda { @s.rb_struct_aset(@struct, -4, 1) }.should raise_error(IndexError) + lambda { @s.rb_struct_aset(@struct, 3, 1) }.should raise_error(IndexError) + end + + it "raises a RuntimeError if the struct is frozen" do + @struct.freeze + lambda { @s.rb_struct_aset(@struct, :a, 1) }.should raise_error(RuntimeError) + end + end + + describe "rb_struct_new" do + it "creates a new instance of a struct" do + i = @s.rb_struct_new(@klass, 1, 2, 3) + i.a.should == 1 + i.b.should == 2 + i.c.should == 3 + end + end + + ruby_version_is "2.4" do + describe "rb_struct_size" do + it "returns the number of struct members" do + @s.rb_struct_size(@struct).should == 3 + end + end + end +end diff --git a/spec/rubyspec/optional/capi/symbol_spec.rb b/spec/rubyspec/optional/capi/symbol_spec.rb new file mode 100644 index 0000000000..b6532f4a4e --- /dev/null +++ b/spec/rubyspec/optional/capi/symbol_spec.rb @@ -0,0 +1,133 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../spec_helper', __FILE__) + +load_extension('symbol') + +describe "C-API Symbol function" do + before :each do + @s = CApiSymbolSpecs.new + end + + describe "rb_intern" do + it "converts a string to a symbol, uniquely" do + @s.rb_intern("test_symbol").should == :test_symbol + @s.rb_intern_c_compare("test_symbol", :test_symbol).should == true + end + end + + describe "rb_intern2" do + it "converts a string to a symbol, uniquely, for a string of given length" do + @s.rb_intern2("test_symbol", 4).should == :test + @s.rb_intern2_c_compare("test_symbol", 4, :test).should == true + end + end + + describe "rb_intern3" do + it "converts a multibyte symbol with the encoding" do + sym = @s.rb_intern3("Ω", 2, Encoding::UTF_8) + sym.encoding.should == Encoding::UTF_8 + sym.should == :Ω + @s.rb_intern3_c_compare("Ω", 2, Encoding::UTF_8, :Ω).should == true + end + + it "converts an ascii compatible symbol with the ascii encoding" do + sym = @s.rb_intern3("foo", 3, Encoding::UTF_8) + sym.encoding.should == Encoding::US_ASCII + sym.should == :foo + end + + it "should respect the symbol encoding via rb_intern3" do + :Ω.to_s.encoding.should == Encoding::UTF_8 + end + end + + describe "rb_intern_const" do + it "converts a string to a Symbol" do + @s.rb_intern_const("test").should == :test + end + end + + describe "rb_id2name" do + it "converts a symbol to a C char array" do + @s.rb_id2name(:test_symbol).should == "test_symbol" + end + end + + describe "rb_id2str" do + it "converts a symbol to a Ruby string" do + @s.rb_id2str(:test_symbol).should == "test_symbol" + end + + it "creates a string with the same encoding as the symbol" do + str = "test_symbol".encode(Encoding::UTF_16LE) + @s.rb_id2str(str.to_sym).encoding.should == Encoding::UTF_16LE + end + end + + describe "rb_intern_str" do + it "converts a Ruby String to a Symbol" do + str = "test_symbol" + @s.rb_intern_str(str).should == :test_symbol + end + end + + describe "rb_is_const_id" do + it "returns true given a const-like symbol" do + @s.rb_is_const_id(:Foo).should == true + end + + it "returns false given an ivar-like symbol" do + @s.rb_is_const_id(:@foo).should == false + end + + it "returns false given a cvar-like symbol" do + @s.rb_is_const_id(:@@foo).should == false + end + + it "returns false given an undecorated symbol" do + @s.rb_is_const_id(:foo).should == false + end + end + + describe "rb_is_instance_id" do + it "returns false given a const-like symbol" do + @s.rb_is_instance_id(:Foo).should == false + end + + it "returns true given an ivar-like symbol" do + @s.rb_is_instance_id(:@foo).should == true + end + + it "returns false given a cvar-like symbol" do + @s.rb_is_instance_id(:@@foo).should == false + end + + it "returns false given an undecorated symbol" do + @s.rb_is_instance_id(:foo).should == false + end + end + + describe "rb_is_class_id" do + it "returns false given a const-like symbol" do + @s.rb_is_class_id(:Foo).should == false + end + + it "returns false given an ivar-like symbol" do + @s.rb_is_class_id(:@foo).should == false + end + + it "returns true given a cvar-like symbol" do + @s.rb_is_class_id(:@@foo).should == true + end + + it "returns false given an undecorated symbol" do + @s.rb_is_class_id(:foo).should == false + end + end + + describe "rb_sym2str" do + it "converts a Symbol to a String" do + @s.rb_sym2str(:bacon).should == "bacon" + end + end +end diff --git a/spec/rubyspec/optional/capi/thread_spec.rb b/spec/rubyspec/optional/capi/thread_spec.rb new file mode 100644 index 0000000000..fbce8016a2 --- /dev/null +++ b/spec/rubyspec/optional/capi/thread_spec.rb @@ -0,0 +1,120 @@ +require File.expand_path('../spec_helper', __FILE__) +require File.expand_path('../../../core/thread/shared/wakeup', __FILE__) + +load_extension("thread") + +class Thread + def self.capi_thread_specs=(t) + @@capi_thread_specs = t + end + + def call_capi_rb_thread_wakeup + @@capi_thread_specs.rb_thread_wakeup(self) + end +end + +describe "C-API Thread function" do + before :each do + @t = CApiThreadSpecs.new + ScratchPad.clear + Thread.capi_thread_specs = @t + end + + describe "rb_thread_wait_for" do + it "sleeps the current thread for the give ammount of time" do + start = Time.now + @t.rb_thread_wait_for(0, 100_000) + (Time.now - start).should be_close(0.1, 0.2) + end + end + + describe "rb_thread_alone" do + it "returns true if there is only one thread" do + pred = Thread.list.size == 1 + @t.rb_thread_alone.should == pred + end + end + + describe "rb_thread_current" do + it "equals Thread.current" do + @t.rb_thread_current.should == Thread.current + end + end + + describe "rb_thread_local_aref" do + it "returns the value of a thread-local variable" do + thr = Thread.current + sym = :thread_capi_specs_aref + thr[sym] = 1 + @t.rb_thread_local_aref(thr, sym).should == 1 + end + + it "returns nil if the value has not been set" do + @t.rb_thread_local_aref(Thread.current, :thread_capi_specs_undefined).should be_nil + end + end + + describe "rb_thread_local_aset" do + it "sets the value of a thread-local variable" do + thr = Thread.current + sym = :thread_capi_specs_aset + @t.rb_thread_local_aset(thr, sym, 2).should == 2 + thr[sym].should == 2 + end + end + + describe "rb_thread_wakeup" do + it_behaves_like :thread_wakeup, :call_capi_rb_thread_wakeup + end + + describe "rb_thread_create" do + it "creates a new thread" do + obj = Object.new + proc = lambda { |x| ScratchPad.record x } + thr = @t.rb_thread_create(proc, obj) + thr.should be_kind_of(Thread) + thr.join + ScratchPad.recorded.should == obj + end + + it "handles throwing an exception in the thread" do + proc = lambda { |x| raise "my error" } + thr = @t.rb_thread_create(proc, nil) + thr.should be_kind_of(Thread) + + lambda { + thr.join + }.should raise_error(RuntimeError, "my error") + end + + it "sets the thread's group" do + thr = @t.rb_thread_create(lambda { |x| }, nil) + begin + thread_group = thr.group + thread_group.should be_an_instance_of(ThreadGroup) + ensure + thr.join + end + end + end + + describe "rb_thread_call_without_gvl" do + it "runs a C function with the global lock unlocked" do + thr = Thread.new do + @t.rb_thread_call_without_gvl + end + + # Wait until it's blocking... + Thread.pass while thr.status and thr.status != "sleep" + + # Wake it up, causing the unblock function to be run. + thr.wakeup + + # Make sure it stopped + thr.join(1).should_not be_nil + + # And we got a proper value + thr.value.should be_true + end + end +end diff --git a/spec/rubyspec/optional/capi/time_spec.rb b/spec/rubyspec/optional/capi/time_spec.rb new file mode 100644 index 0000000000..21f408d52b --- /dev/null +++ b/spec/rubyspec/optional/capi/time_spec.rb @@ -0,0 +1,296 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("time") + +describe "CApiTimeSpecs" do + before :each do + @s = CApiTimeSpecs.new + end + + describe "rb_time_new" do + it "creates a Time from the sec and usec" do + usec = CAPI_SIZEOF_LONG == 8 ? 4611686018427387903 : 1413123123 + @s.rb_time_new(1232141421, usec).should == Time.at(1232141421, usec) + end + end + + describe "TIMET2NUM" do + it "returns an Integer" do + @s.TIMET2NUM.should be_kind_of(Integer) + end + end + + describe "rb_time_nano_new" do + it "creates a Time from the sec and usec" do + time = @s.rb_time_nano_new(1232141421, 1413123123) + time.to_i.should == 1232141422 + time.nsec.should == 413123123 + end + end + + describe "rb_time_num_new" do + it "creates a Time in the local zone with only a timestamp" do + with_timezone("Europe/Amsterdam") do + time = @s.rb_time_num_new(1232141421, nil) + time.should be_an_instance_of(Time) + time.to_i.should == 1232141421 + time.gmt_offset.should == 3600 + end + end + + it "creates a Time with the given offset" do + with_timezone("Europe/Amsterdam") do + time = @s.rb_time_num_new(1232141421, 7200) + time.should be_an_instance_of(Time) + time.to_i.should == 1232141421 + time.gmt_offset.should == 7200 + end + end + + it "creates a Time with a Float timestamp" do + with_timezone("Europe/Amsterdam") do + time = @s.rb_time_num_new(1.5, 7200) + time.should be_an_instance_of(Time) + time.to_i.should == 1 + time.nsec.should == 500000000 + time.gmt_offset.should == 7200 + end + end + + it "creates a Time with a Rational timestamp" do + with_timezone("Europe/Amsterdam") do + time = @s.rb_time_num_new(Rational(3, 2), 7200) + time.should be_an_instance_of(Time) + time.to_i.should == 1 + time.nsec.should == 500000000 + time.gmt_offset.should == 7200 + end + end + end + + describe "rb_time_interval" do + it "creates a timeval interval for a Fixnum" do + sec, usec = @s.rb_time_interval(1232141421) + sec.should be_kind_of(Integer) + sec.should == 1232141421 + usec.should be_kind_of(Integer) + usec.should == 0 + end + + it "creates a timeval interval for a Float" do + sec, usec = @s.rb_time_interval(1.5) + sec.should be_kind_of(Integer) + sec.should == 1 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "creates a timeval interval for a Rational" do + sec, usec = @s.rb_time_interval(Rational(3, 2)) + sec.should be_kind_of(Integer) + sec.should == 1 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "throws an argument error for a negative value" do + lambda { @s.rb_time_interval(-1232141421) }.should raise_error(ArgumentError) + lambda { @s.rb_time_interval(Rational(-3, 2)) }.should raise_error(ArgumentError) + lambda { @s.rb_time_interval(-1.5) }.should raise_error(ArgumentError) + end + + end + + describe "rb_time_interval" do + it "creates a timeval interval for a Fixnum" do + sec, usec = @s.rb_time_interval(1232141421) + sec.should be_kind_of(Integer) + sec.should == 1232141421 + usec.should be_kind_of(Integer) + usec.should == 0 + end + + it "creates a timeval interval for a Float" do + sec, usec = @s.rb_time_interval(1.5) + sec.should be_kind_of(Integer) + sec.should == 1 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "creates a timeval interval for a Rational" do + sec, usec = @s.rb_time_interval(Rational(3, 2)) + sec.should be_kind_of(Integer) + sec.should == 1 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "throws an argument error for a negative value" do + lambda { @s.rb_time_interval(-1232141421) }.should raise_error(ArgumentError) + lambda { @s.rb_time_interval(Rational(-3, 2)) }.should raise_error(ArgumentError) + lambda { @s.rb_time_interval(-1.5) }.should raise_error(ArgumentError) + end + + it "throws an argument error when given a Time instance" do + lambda { @s.rb_time_interval(Time.now) }.should raise_error(TypeError) + end + + end + + describe "rb_time_timeval" do + it "creates a timeval for a Fixnum" do + sec, usec = @s.rb_time_timeval(1232141421) + sec.should be_kind_of(Integer) + sec.should == 1232141421 + usec.should be_kind_of(Integer) + usec.should == 0 + end + + it "creates a timeval for a Float" do + sec, usec = @s.rb_time_timeval(1.5) + sec.should be_kind_of(Integer) + sec.should == 1 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "creates a timeval for a Rational" do + sec, usec = @s.rb_time_timeval(Rational(3, 2)) + sec.should be_kind_of(Integer) + sec.should == 1 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "creates a timeval for a negative Fixnum" do + sec, usec = @s.rb_time_timeval(-1232141421) + sec.should be_kind_of(Integer) + sec.should == -1232141421 + usec.should be_kind_of(Integer) + usec.should == 0 + end + + it "creates a timeval for a negative Float" do + sec, usec = @s.rb_time_timeval(-1.5) + sec.should be_kind_of(Integer) + sec.should == -2 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "creates a timeval for a negative Rational" do + sec, usec = @s.rb_time_timeval(Rational(-3, 2)) + sec.should be_kind_of(Integer) + sec.should == -2 + usec.should be_kind_of(Integer) + usec.should == 500000 + end + + it "creates a timeval from a Time object" do + t = Time.now + sec, usec = @s.rb_time_timeval(t) + sec.should == t.to_i + usec.should == t.nsec.div(1000) + end + end + + describe "rb_time_timespec" do + it "creates a timespec for a Fixnum" do + sec, nsec = @s.rb_time_timespec(1232141421) + sec.should be_kind_of(Integer) + sec.should == 1232141421 + nsec.should be_kind_of(Integer) + nsec.should == 0 + end + + it "creates a timespec for a Float" do + sec, nsec = @s.rb_time_timespec(1.5) + sec.should be_kind_of(Integer) + sec.should == 1 + nsec.should be_kind_of(Integer) + nsec.should == 500000000 + end + + it "creates a timespec for a Rational" do + sec, nsec = @s.rb_time_timespec(Rational(3, 2)) + sec.should be_kind_of(Integer) + sec.should == 1 + nsec.should be_kind_of(Integer) + nsec.should == 500000000 + end + + it "creates a timespec for a negative Fixnum" do + sec, nsec = @s.rb_time_timespec(-1232141421) + sec.should be_kind_of(Integer) + sec.should == -1232141421 + nsec.should be_kind_of(Integer) + nsec.should == 0 + end + + it "creates a timespec for a negative Float" do + sec, nsec = @s.rb_time_timespec(-1.5) + sec.should be_kind_of(Integer) + sec.should == -2 + nsec.should be_kind_of(Integer) + nsec.should == 500000000 + end + + it "creates a timespec for a negative Rational" do + sec, nsec = @s.rb_time_timespec(Rational(-3, 2)) + sec.should be_kind_of(Integer) + sec.should == -2 + nsec.should be_kind_of(Integer) + nsec.should == 500000000 + end + + it "creates a timespec from a Time object" do + t = Time.now + sec, nsec = @s.rb_time_timespec(t) + sec.should == t.to_i + nsec.should == t.nsec + end + end + + ruby_version_is "2.3" do + describe "rb_time_timespec_new" do + it "returns a time object with the given timespec and UTC offset" do + @s.rb_time_timespec_new(1447087832, 476451125, 32400).should == Time.at(1447087832, 476451.125).localtime(32400) + end + + describe "when offset given is within range of -86400 and 86400 (exclusive)" do + it "sets time's is_gmt to false" do + @s.rb_time_timespec_new(1447087832, 476451125, 0).gmt?.should be_false + end + + it "sets time's offset to the offset given" do + @s.rb_time_timespec_new(1447087832, 476451125, 86399).gmtoff.should == 86399 + end + end + + it "returns time object in UTC if offset given equals INT_MAX - 1" do + @s.rb_time_timespec_new(1447087832, 476451125, 0x7ffffffe).utc?.should be_true + end + + it "returns time object in localtime if offset given equals INT_MAX" do + @s.rb_time_timespec_new(1447087832, 476451125, 0x7fffffff).should == Time.at(1447087832, 476451.125).localtime + t = Time.now + @s.rb_time_timespec_new(t.tv_sec, t.tv_nsec, 0x7fffffff).gmtoff.should == t.gmtoff + end + + it "raises an ArgumentError if offset passed is not within range of -86400 and 86400 (exclusive)" do + lambda { @s.rb_time_timespec_new(1447087832, 476451125, 86400) }.should raise_error(ArgumentError) + lambda { @s.rb_time_timespec_new(1447087832, 476451125, -86400) }.should raise_error(ArgumentError) + end + end + + describe "rb_timespec_now" do + it "fills a struct timespec with the current time" do + now = Time.now + time = @s.rb_time_from_timespec(now.utc_offset) + time.should be_an_instance_of(Time) + (time - now).should be_close(0, 10) + end + end + end +end diff --git a/spec/rubyspec/optional/capi/true_spec.rb b/spec/rubyspec/optional/capi/true_spec.rb new file mode 100644 index 0000000000..fcb57567ec --- /dev/null +++ b/spec/rubyspec/optional/capi/true_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("boolean") + +describe "CApiTrueSpecs" do + before :each do + @b = CApiBooleanSpecs.new + end + + describe "a true value from Ruby" do + it "is truthy in C" do + @b.is_true(true).should == 1 + end + end + + describe "a true value from Qtrue" do + it "is truthy in C" do + @b.is_true(@b.q_true).should == 1 + end + end +end diff --git a/spec/rubyspec/optional/capi/typed_data_spec.rb b/spec/rubyspec/optional/capi/typed_data_spec.rb new file mode 100644 index 0000000000..1b2852d349 --- /dev/null +++ b/spec/rubyspec/optional/capi/typed_data_spec.rb @@ -0,0 +1,56 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension("typed_data") + +describe "CApiAllocTypedSpecs (a class with an alloc func defined)" do + it "calls the alloc func" do + @s = CApiAllocTypedSpecs.new + @s.typed_wrapped_data.should == 42 # not defined in initialize + end +end + +describe "CApiWrappedTypedStruct" do + before :each do + @s = CApiWrappedTypedStructSpecs.new + end + + it "wraps and unwraps data" do + a = @s.typed_wrap_struct(1024) + @s.typed_get_struct(a).should == 1024 + end + + it "throws an exception for a wrong type" do + a = @s.typed_wrap_struct(1024) + lambda { @s.typed_get_struct_other(a) }.should raise_error(TypeError) + end + + it "unwraps data for a parent type" do + a = @s.typed_wrap_struct(1024) + @s.typed_get_struct_parent(a).should == 1024 + end + + it "allows for using NULL as the klass for Data_Wrap_Struct" do + a = @s.typed_wrap_struct_null(1024) + @s.typed_get_struct(a).should == 1024 + end + + describe "RTYPEDATA" do + it "returns the struct data" do + a = @s.typed_wrap_struct(1024) + @s.typed_get_struct_rdata(a).should == 1024 + end + + it "can be used to change the wrapped struct" do + a = @s.typed_wrap_struct(1024) + @s.typed_change_struct(a, 100) + @s.typed_get_struct(a).should == 100 + end + end + + describe "DATA_PTR" do + it "returns the struct data" do + a = @s.typed_wrap_struct(1024) + @s.typed_get_struct_data_ptr(a).should == 1024 + end + end +end diff --git a/spec/rubyspec/optional/capi/util_spec.rb b/spec/rubyspec/optional/capi/util_spec.rb new file mode 100644 index 0000000000..c8d0401cbc --- /dev/null +++ b/spec/rubyspec/optional/capi/util_spec.rb @@ -0,0 +1,191 @@ +require File.expand_path('../spec_helper', __FILE__) + +load_extension('util') + +describe "C-API Util function" do + before :each do + @o = CApiUtilSpecs.new + end + + describe "rb_scan_args" do + before :each do + @prc = lambda { 1 } + @acc = [] + ScratchPad.record @acc + end + + it "assigns the required arguments scanned" do + @o.rb_scan_args([1, 2], "2", 2, @acc).should == 2 + ScratchPad.recorded.should == [1, 2] + end + + it "raises an ArgumentError if there are insufficient arguments" do + lambda { @o.rb_scan_args([1, 2], "3", 0, @acc) }.should raise_error(ArgumentError) + end + + it "assigns the required and optional arguments scanned" do + @o.rb_scan_args([1, 2], "11", 2, @acc).should == 2 + ScratchPad.recorded.should == [1, 2] + end + + it "assigns the optional arguments scanned" do + @o.rb_scan_args([1, 2], "02", 2, @acc).should == 2 + ScratchPad.recorded.should == [1, 2] + end + + it "assigns nil for optional arguments that are not present" do + @o.rb_scan_args([1], "03", 3, @acc).should == 1 + ScratchPad.recorded.should == [1, nil, nil] + end + + it "assigns the required and optional arguments and splats the rest" do + @o.rb_scan_args([1, 2, 3, 4], "11*", 3, @acc).should == 4 + ScratchPad.recorded.should == [1, 2, [3, 4]] + end + + it "assigns the required and optional arguments and and empty Array when there are no arguments to splat" do + @o.rb_scan_args([1, 2], "11*", 3, @acc).should == 2 + ScratchPad.recorded.should == [1, 2, []] + end + + it "assigns required, optional arguments scanned and the passed block" do + @o.rb_scan_args([1, 2], "11&", 3, @acc, &@prc).should == 2 + ScratchPad.recorded.should == [1, 2, @prc] + end + + it "assigns required, optional, splatted arguments scanned and the passed block" do + @o.rb_scan_args([1, 2, 3, 4], "11*&", 4, @acc, &@prc).should == 4 + ScratchPad.recorded.should == [1, 2, [3, 4], @prc] + end + + it "assigns required arguments, nil for missing optional arguments and the passed block" do + @o.rb_scan_args([1], "12&", 4, @acc, &@prc).should == 1 + ScratchPad.recorded.should == [1, nil, nil, @prc] + end + + it "assigns required, splatted arguments and the passed block" do + @o.rb_scan_args([1, 2, 3], "1*&", 3, @acc, &@prc).should == 3 + ScratchPad.recorded.should == [1, [2, 3], @prc] + end + + it "assigns post-splat arguments" do + @o.rb_scan_args([1, 2, 3], "00*1", 2, @acc).should == 3 + ScratchPad.recorded.should == [[1, 2], 3] + end + + it "assigns required, optional, splat and post-splat arguments" do + @o.rb_scan_args([1, 2, 3, 4, 5], "11*1", 4, @acc).should == 5 + ScratchPad.recorded.should == [1, 2, [3, 4], 5] + end + + it "assigns required, splat, post-splat arguments" do + @o.rb_scan_args([1, 2, 3, 4], "10*1", 3, @acc).should == 4 + ScratchPad.recorded.should == [1, [2, 3], 4] + end + + it "assigns optional, splat, post-splat arguments" do + @o.rb_scan_args([1, 2, 3, 4], "01*1", 3, @acc).should == 4 + ScratchPad.recorded.should == [1, [2, 3], 4] + end + + it "assigns required, optional, splat, post-splat and block arguments" do + @o.rb_scan_args([1, 2, 3, 4, 5], "11*1&", 5, @acc, &@prc).should == 5 + ScratchPad.recorded.should == [1, 2, [3, 4], 5, @prc] + end + + it "assigns Hash arguments" do + h = {a: 1, b: 2} + @o.rb_scan_args([h], "0:", 1, @acc).should == 0 + ScratchPad.recorded.should == [h] + end + + it "assigns required and Hash arguments" do + h = {a: 1, b: 2} + @o.rb_scan_args([1, h], "1:", 2, @acc).should == 1 + ScratchPad.recorded.should == [1, h] + end + + it "assigns required, optional, splat, post-splat, Hash and block arguments" do + h = {a: 1, b: 2} + @o.rb_scan_args([1, 2, 3, 4, 5, h], "11*1:&", 6, @acc, &@prc).should == 5 + ScratchPad.recorded.should == [1, 2, [3, 4], 5, h, @prc] + end + + # r43934 + it "rejects non-keyword arguments" do + h = {1 => 2, 3 => 4} + lambda { + @o.rb_scan_args([h], "0:", 1, @acc) + }.should raise_error(ArgumentError) + ScratchPad.recorded.should == [] + end + + it "rejects required and non-keyword arguments" do + h = {1 => 2, 3 => 4} + lambda { + @o.rb_scan_args([1, h], "1:", 2, @acc) + }.should raise_error(ArgumentError) + ScratchPad.recorded.should == [] + end + + it "considers the hash as a post argument when there is a splat" do + h = {1 => 2, 3 => 4} + @o.rb_scan_args([1, 2, 3, 4, 5, h], "11*1:&", 6, @acc, &@prc).should == 6 + ScratchPad.recorded.should == [1, 2, [3, 4, 5], h, nil, @prc] + end + end + + platform_is wordsize: 64 do + describe "rb_long2int" do + it "raises a RangeError if the value is outside the range of a C int" do + lambda { @o.rb_long2int(0xffff_ffff_ffff) }.should raise_error(RangeError) + end + end + + it "returns the C int value" do + @o.rb_long2int(1234).should == 1234 + end + end + + # #7896 + describe "rb_iter_break" do + before :each do + ScratchPad.record [] + end + + it "breaks a loop" do + 3.times do |i| + if i == 2 + @o.rb_iter_break + end + ScratchPad << i + end + ScratchPad.recorded.should == [0, 1] + end + + it "breaks the inner loop" do + 3.times do |i| + 3.times do |j| + if i == 1 + @o.rb_iter_break + end + ScratchPad << [i, j] + end + end + ScratchPad.recorded.should == [[0, 0], [0, 1], [0, 2], [2, 0], [2, 1], [2, 2]] + end + end + + describe "rb_sourcefile" do + it "returns the current ruby file" do + @o.rb_sourcefile.should == __FILE__ + end + end + + describe "rb_sourceline" do + it "returns the current ruby file" do + @o.rb_sourceline.should be_kind_of(Fixnum) + end + end + +end diff --git a/spec/rubyspec/security/cve_2011_4815_spec.rb b/spec/rubyspec/security/cve_2011_4815_spec.rb new file mode 100644 index 0000000000..0a353b742a --- /dev/null +++ b/spec/rubyspec/security/cve_2011_4815_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../spec_helper', __FILE__) + +describe :resists_cve_2011_4815, shared: true do + it "resists CVE-2011-4815 by having different hash codes in different processes" do + eval("(#{@method}).hash.to_s").should_not == ruby_exe("print (#{@method}).hash") + end +end + +describe "Object#hash" do + it_behaves_like :resists_cve_2011_4815, 'Object.new' +end + +describe "Integer#hash with a small value" do + it_behaves_like :resists_cve_2011_4815, '14' +end + +describe "Integer#hash with a large value" do + it_behaves_like :resists_cve_2011_4815, '100000000000000000000000000000' +end + +describe "Float#hash" do + it_behaves_like :resists_cve_2011_4815, '3.14' +end + +describe "String#hash" do + it_behaves_like :resists_cve_2011_4815, '"abc"' +end + +describe "Symbol#hash" do + ruby_bug "#13376", "2.3.0"..."2.3.4" do + it_behaves_like :resists_cve_2011_4815, ':a' + end +end + +describe "Array#hash" do + it_behaves_like :resists_cve_2011_4815, '[1, 2, 3]' +end + +describe "Hash#hash" do + it_behaves_like :resists_cve_2011_4815, '{a: 1, b: 2, c: 3}' +end diff --git a/spec/rubyspec/security/cve_2013_4164_spec.rb b/spec/rubyspec/security/cve_2013_4164_spec.rb new file mode 100644 index 0000000000..de5973b780 --- /dev/null +++ b/spec/rubyspec/security/cve_2013_4164_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../spec_helper', __FILE__) + +require 'json' + +describe "String#to_f" do + + it "resists CVE-2013-4164 by converting very long Strings to a Float" do + "1.#{'1'*1000000}".to_f.should be_close(1.1111111111111112, TOLERANCE) + end + +end + +describe "JSON.parse" do + + it "resists CVE-2013-4164 by converting very long Strings to a Float" do + JSON.parse("[1.#{'1'*1000000}]").first.should be_close(1.1111111111111112, TOLERANCE) + end + +end diff --git a/spec/rubyspec/security/cve_2014_8080_spec.rb b/spec/rubyspec/security/cve_2014_8080_spec.rb new file mode 100644 index 0000000000..ae87de2346 --- /dev/null +++ b/spec/rubyspec/security/cve_2014_8080_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../spec_helper', __FILE__) + +require 'rexml/document' + +describe "REXML::Document.new" do + + it "resists CVE-2014-8080 by raising an exception when entity expansion has grown too large" do + xml = < + + + + + + + + + + + ]> + + %x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9;%x9; + +XML + + lambda { REXML::Document.new(xml).doctype.entities['x9'].value }.should raise_error(REXML::ParseException) { |e| + e.message.should =~ /entity expansion has grown too large/ + } + end + +end diff --git a/spec/rubyspec/shared/basicobject/method_missing.rb b/spec/rubyspec/shared/basicobject/method_missing.rb new file mode 100644 index 0000000000..97ece14c03 --- /dev/null +++ b/spec/rubyspec/shared/basicobject/method_missing.rb @@ -0,0 +1,126 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/basicobject/method_missing', __FILE__) + +describe :method_missing_defined_module, shared: true do + describe "for a Module with #method_missing defined" do + it "is not called when a defined method is called" do + @object.method_public.should == :module_public_method + end + + it "is called when a not defined method is called" do + @object.not_defined_method.should == :module_method_missing + end + + it "is called when a protected method is called" do + @object.method_protected.should == :module_method_missing + end + + it "is called when a private method is called" do + @object.method_private.should == :module_method_missing + end + end +end + +describe :method_missing_module, shared: true do + describe "for a Module" do + it "raises a NoMethodError when an undefined method is called" do + lambda { @object.no_such_method }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError when a protected method is called" do + lambda { @object.method_protected }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError when a private method is called" do + lambda { @object.method_private }.should raise_error(NoMethodError) + end + end +end + +describe :method_missing_defined_class, shared: true do + describe "for a Class with #method_missing defined" do + it "is not called when a defined method is called" do + @object.method_public.should == :class_public_method + end + + it "is called when an undefined method is called" do + @object.no_such_method.should == :class_method_missing + end + + it "is called when an protected method is called" do + @object.method_protected.should == :class_method_missing + end + + it "is called when an private method is called" do + @object.method_private.should == :class_method_missing + end + end +end + +describe :method_missing_class, shared: true do + describe "for a Class" do + it "raises a NoMethodError when an undefined method is called" do + lambda { @object.no_such_method }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError when a protected method is called" do + lambda { @object.method_protected }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError when a private method is called" do + lambda { @object.method_private }.should raise_error(NoMethodError) + end + end +end + +describe :method_missing_defined_instance, shared: true do + describe "for an instance with #method_missing defined" do + before :each do + @instance = @object.new + end + + it "is not called when a defined method is called" do + @instance.method_public.should == :instance_public_method + end + + it "is called when an undefined method is called" do + @instance.no_such_method.should == :instance_method_missing + end + + it "is called when an protected method is called" do + @instance.method_protected.should == :instance_method_missing + end + + it "is called when an private method is called" do + @instance.method_private.should == :instance_method_missing + end + end +end + +describe :method_missing_instance, shared: true do + describe "for an instance" do + it "raises a NoMethodError when an undefined method is called" do + lambda { @object.new.no_such_method }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError when a protected method is called" do + lambda { @object.new.method_protected }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError when a private method is called" do + lambda { @object.new.method_private }.should raise_error(NoMethodError) + end + + ruby_version_is "2.3" do + it 'sets the receiver of the raised NoMethodError' do + obj = @object.new + + begin + obj.method_private + rescue NoMethodError => error + (error.receiver == obj).should == true + end + end + end + end +end diff --git a/spec/rubyspec/shared/basicobject/send.rb b/spec/rubyspec/shared/basicobject/send.rb new file mode 100644 index 0000000000..f8c63c5522 --- /dev/null +++ b/spec/rubyspec/shared/basicobject/send.rb @@ -0,0 +1,110 @@ +module SendSpecs +end + +describe :basicobject_send, shared: true do + it "invokes the named method" do + class SendSpecs::Foo + def bar + 'done' + end + end + SendSpecs::Foo.new.send(@method, :bar).should == 'done' + end + + it "accepts a String method name" do + class SendSpecs::Foo + def bar + 'done' + end + end + SendSpecs::Foo.new.send(@method, 'bar').should == 'done' + end + + it "invokes a class method if called on a class" do + class SendSpecs::Foo + def self.bar + 'done' + end + end + SendSpecs::Foo.send(@method, :bar).should == 'done' + end + + it "raises a NameError if the corresponding method can't be found" do + class SendSpecs::Foo + def bar + 'done' + end + end + lambda { SendSpecs::Foo.new.send(@method, :syegsywhwua) }.should raise_error(NameError) + end + + it "raises a NameError if the corresponding singleton method can't be found" do + class SendSpecs::Foo + def self.bar + 'done' + end + end + lambda { SendSpecs::Foo.send(@method, :baz) }.should raise_error(NameError) + end + + it "raises an ArgumentError if no arguments are given" do + class SendSpecs::Foo; end + lambda { SendSpecs::Foo.new.send @method }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if called with more arguments than available parameters" do + class SendSpecs::Foo + def bar; end + end + + lambda { SendSpecs::Foo.new.send(@method, :bar, :arg) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if called with fewer arguments than required parameters" do + class SendSpecs::Foo + def foo(arg); end + end + + lambda { SendSpecs::Foo.new.send(@method, :foo) }.should raise_error(ArgumentError) + end + + it "succeeds if passed an arbitrary number of arguments as a splat parameter" do + class SendSpecs::Foo + def baz(*args) args end + end + + begin + SendSpecs::Foo.new.send(@method, :baz).should == [] + SendSpecs::Foo.new.send(@method, :baz, :quux).should == [:quux] + SendSpecs::Foo.new.send(@method, :baz, :quux, :foo).should == [:quux, :foo] + rescue + fail + end + end + + it "succeeds when passing 1 or more arguments as a required and a splat parameter" do + class SendSpecs::Foo + def baz(first, *rest) [first, *rest] end + end + + SendSpecs::Foo.new.send(@method, :baz, :quux).should == [:quux] + SendSpecs::Foo.new.send(@method, :baz, :quux, :foo).should == [:quux, :foo] + end + + it "succeeds when passing 0 arguments to a method with one parameter with a default" do + class SendSpecs::Foo + def foo(first = true) first end + end + + begin + SendSpecs::Foo.new.send(@method, :foo).should == true + SendSpecs::Foo.new.send(@method, :foo, :arg).should == :arg + rescue + fail + end + end + + it "has a negative arity" do + method(@method).arity.should < 0 + end +end diff --git a/spec/rubyspec/shared/complex/Complex.rb b/spec/rubyspec/shared/complex/Complex.rb new file mode 100644 index 0000000000..5a9715b161 --- /dev/null +++ b/spec/rubyspec/shared/complex/Complex.rb @@ -0,0 +1,133 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :kernel_Complex, shared: true do + describe "when passed [Complex, Complex]" do + it "returns a new Complex number based on the two given numbers" do + Complex(Complex(3, 4), Complex(5, 6)).should == Complex(3 - 6, 4 + 5) + Complex(Complex(1.5, 2), Complex(-5, 6.3)).should == Complex(1.5 - 6.3, 2 - 5) + end + end + + describe "when passed [Complex]" do + it "returns the passed Complex number" do + Complex(Complex(1, 2)).should == Complex(1, 2) + Complex(Complex(-3.4, bignum_value)).should == Complex(-3.4, bignum_value) + end + end + + describe "when passed [Integer, Integer]" do + it "returns a new Complex number" do + Complex(1, 2).should be_an_instance_of(Complex) + Complex(1, 2).real.should == 1 + Complex(1, 2).imag.should == 2 + + Complex(-3, -5).should be_an_instance_of(Complex) + Complex(-3, -5).real.should == -3 + Complex(-3, -5).imag.should == -5 + + Complex(3.5, -4.5).should be_an_instance_of(Complex) + Complex(3.5, -4.5).real.should == 3.5 + Complex(3.5, -4.5).imag.should == -4.5 + + Complex(bignum_value, 30).should be_an_instance_of(Complex) + Complex(bignum_value, 30).real.should == bignum_value + Complex(bignum_value, 30).imag.should == 30 + end + end + + describe "when passed [Integer]" do + it "returns a new Complex number with 0 as the imaginary component" do + # Guard against the Mathn library + conflicts_with :Prime do + Complex(1).should be_an_instance_of(Complex) + Complex(1).imag.should == 0 + Complex(1).real.should == 1 + + Complex(-3).should be_an_instance_of(Complex) + Complex(-3).imag.should == 0 + Complex(-3).real.should == -3 + + Complex(-4.5).should be_an_instance_of(Complex) + Complex(-4.5).imag.should == 0 + Complex(-4.5).real.should == -4.5 + + Complex(bignum_value).should be_an_instance_of(Complex) + Complex(bignum_value).imag.should == 0 + Complex(bignum_value).real.should == bignum_value + end + end + end + + describe "when passed a String" do + it "needs to be reviewed for spec completeness" + end + + describe "when passed an Objectc which responds to #to_c" do + it "returns the passed argument" do + obj = Object.new; def obj.to_c; 1i end + Complex(obj).should == Complex(0, 1) + end + end + + describe "when passed a Numeric which responds to #real? with false" do + it "returns the passed argument" do + n = mock_numeric("unreal") + n.should_receive(:real?).and_return(false) + Complex(n).should equal(n) + end + end + + describe "when passed a Numeric which responds to #real? with true" do + it "returns a Complex with the passed argument as the real component and 0 as the imaginary component" do + n = mock_numeric("real") + n.should_receive(:real?).any_number_of_times.and_return(true) + result = Complex(n) + result.real.should equal(n) + result.imag.should equal(0) + end + end + + describe "when passed Numerics n1 and n2 and at least one responds to #real? with false" do + [[false, false], [false, true], [true, false]].each do |r1, r2| + it "returns n1 + n2 * Complex(0, 1)" do + n1 = mock_numeric("n1") + n2 = mock_numeric("n2") + n3 = mock_numeric("n3") + n4 = mock_numeric("n4") + n1.should_receive(:real?).any_number_of_times.and_return(r1) + n2.should_receive(:real?).any_number_of_times.and_return(r2) + n2.should_receive(:*).with(Complex(0, 1)).and_return(n3) + n1.should_receive(:+).with(n3).and_return(n4) + Complex(n1, n2).should equal(n4) + end + end + end + + describe "when passed two Numerics and both respond to #real? with true" do + it "returns a Complex with the passed arguments as real and imaginary components respectively" do + n1 = mock_numeric("n1") + n2 = mock_numeric("n2") + n1.should_receive(:real?).any_number_of_times.and_return(true) + n2.should_receive(:real?).any_number_of_times.and_return(true) + result = Complex(n1, n2) + result.real.should equal(n1) + result.imag.should equal(n2) + end + end + + describe "when passed a single non-Numeric" do + it "coerces the passed argument using #to_c" do + n = mock("n") + c = Complex(0, 0) + n.should_receive(:to_c).and_return(c) + Complex(n).should equal(c) + end + end + + describe "when passed a non-Numeric second argument" do + it "raises TypeError" do + lambda { Complex.send(@method, :sym, :sym) }.should raise_error(TypeError) + lambda { Complex.send(@method, 0, :sym) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/shared/complex/abs.rb b/spec/rubyspec/shared/complex/abs.rb new file mode 100644 index 0000000000..1f8d861f65 --- /dev/null +++ b/spec/rubyspec/shared/complex/abs.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_abs, shared: true do + it "returns the modulus: |a + bi| = sqrt((a ^ 2) + (b ^ 2))" do + Complex(0, 0).send(@method).should == 0 + Complex(3, 4).send(@method).should == 5 # well-known integer case + Complex(-3, 4).send(@method).should == 5 + Complex(1, -1).send(@method).should be_close(Math.sqrt(2), TOLERANCE) + Complex(6.5, 0).send(@method).should be_close(6.5, TOLERANCE) + Complex(0, -7.2).send(@method).should be_close(7.2, TOLERANCE) + end +end diff --git a/spec/rubyspec/shared/complex/abs2.rb b/spec/rubyspec/shared/complex/abs2.rb new file mode 100644 index 0000000000..f899a41a3e --- /dev/null +++ b/spec/rubyspec/shared/complex/abs2.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_abs2, shared: true do + it "returns the sum of the squares of the real and imaginary parts" do + Complex(1, -2).abs2.should == 1 + 4 + Complex(-0.1, 0.2).abs2.should be_close(0.01 + 0.04, TOLERANCE) + # Guard against Mathn library + conflicts_with :Prime do + Complex(0).abs2.should == 0 + end + end +end diff --git a/spec/rubyspec/shared/complex/arg.rb b/spec/rubyspec/shared/complex/arg.rb new file mode 100644 index 0000000000..c81f197433 --- /dev/null +++ b/spec/rubyspec/shared/complex/arg.rb @@ -0,0 +1,9 @@ +describe :complex_arg, shared: true do + it "returns the argument -- i.e., the angle from (1, 0) in the complex plane" do + two_pi = 2 * Math::PI + (Complex(1, 0).send(@method) % two_pi).should be_close(0, TOLERANCE) + (Complex(0, 2).send(@method) % two_pi).should be_close(Math::PI * 0.5, TOLERANCE) + (Complex(-100, 0).send(@method) % two_pi).should be_close(Math::PI, TOLERANCE) + (Complex(0, -75.3).send(@method) % two_pi).should be_close(Math::PI * 1.5, TOLERANCE) + end +end diff --git a/spec/rubyspec/shared/complex/coerce.rb b/spec/rubyspec/shared/complex/coerce.rb new file mode 100644 index 0000000000..b8a230dfb5 --- /dev/null +++ b/spec/rubyspec/shared/complex/coerce.rb @@ -0,0 +1,70 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_coerce, shared: true do + before :each do + @one = Complex(1) + end + + it "returns an array containing other and self as Complex when other is an Integer" do + result = @one.coerce(2) + result.should == [2, 1] + result.first.should be_kind_of(Complex) + result.last.should be_kind_of(Complex) + end + + it "returns an array containing other and self as Complex when other is a Float" do + result = @one.coerce(20.5) + result.should == [20.5, 1] + result.first.should be_kind_of(Complex) + result.last.should be_kind_of(Complex) + end + + it "returns an array containing other and self as Complex when other is a Bignum" do + result = @one.coerce(4294967296) + result.should == [4294967296, 1] + result.first.should be_kind_of(Complex) + result.last.should be_kind_of(Complex) + end + + it "returns an array containing other and self as Complex when other is a Rational" do + result = @one.coerce(Rational(5,6)) + result.should == [Rational(5,6), 1] + result.first.should be_kind_of(Complex) + result.last.should be_kind_of(Complex) + end + + it "returns an array containing other and self when other is a Complex" do + other = Complex(2) + result = @one.coerce(other) + result.should == [other, @one] + result.first.should equal(other) + result.last.should equal(@one) + end + + it "returns an array containing other as Complex and self when other is a Numeric which responds to #real? with true" do + other = mock_numeric('other') + other.should_receive(:real?).any_number_of_times.and_return(true) + result = @one.coerce(other) + result.should == [other, @one] + result.first.should eql(Complex(other)) + result.last.should equal(@one) + end + + it "raises TypeError when other is a Numeric which responds to #real? with false" do + other = mock_numeric('other') + other.should_receive(:real?).any_number_of_times.and_return(false) + lambda { @one.coerce(other) }.should raise_error(TypeError) + end + + it "raises a TypeError when other is a String" do + lambda { @one.coerce("20") }.should raise_error(TypeError) + end + + it "raises a TypeError when other is nil" do + lambda { @one.coerce(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when other is false" do + lambda { @one.coerce(false) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/shared/complex/conjugate.rb b/spec/rubyspec/shared/complex/conjugate.rb new file mode 100644 index 0000000000..d1ae47bcb6 --- /dev/null +++ b/spec/rubyspec/shared/complex/conjugate.rb @@ -0,0 +1,8 @@ +describe :complex_conjugate, shared: true do + it "returns the complex conjugate: conj a + bi = a - bi" do + Complex(3, 5).send(@method).should == Complex(3, -5) + Complex(3, -5).send(@method).should == Complex(3, 5) + Complex(-3.0, 5.2).send(@method).should be_close(Complex(-3.0, -5.2), TOLERANCE) + Complex(3.0, -5.2).send(@method).should be_close(Complex(3.0, 5.2), TOLERANCE) + end +end diff --git a/spec/rubyspec/shared/complex/constants.rb b/spec/rubyspec/shared/complex/constants.rb new file mode 100644 index 0000000000..e8bb5fc907 --- /dev/null +++ b/spec/rubyspec/shared/complex/constants.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_I, shared: true do + it "is Complex(0, 1)" do + Complex::I.should eql(Complex(0, 1)) + end +end diff --git a/spec/rubyspec/shared/complex/denominator.rb b/spec/rubyspec/shared/complex/denominator.rb new file mode 100644 index 0000000000..6084cbf672 --- /dev/null +++ b/spec/rubyspec/shared/complex/denominator.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_denominator, shared: true do + it "returns the least common multiple denominator of the real and imaginary parts" do + Complex(3, 4).denominator.should == 1 + Complex(3, bignum_value).denominator.should == 1 + + Complex(3, Rational(3,4)).denominator.should == 4 + + Complex(Rational(4,8), Rational(3,4)).denominator.should == 4 + Complex(Rational(3,8), Rational(3,4)).denominator.should == 8 + end +end diff --git a/spec/rubyspec/shared/complex/divide.rb b/spec/rubyspec/shared/complex/divide.rb new file mode 100644 index 0000000000..0bd88f197e --- /dev/null +++ b/spec/rubyspec/shared/complex/divide.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_divide, shared: true do + describe "with Complex" do + it "divides according to the usual rule for complex numbers" do + a = Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10)) + b = Complex(1, 2) + a.send(@method, b).should == Complex(10, 20) + + c = Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2)) + d = Complex(1.5, 2.1) + # remember the floating-point arithmetic + c.send(@method, d).should be_close(Complex(100.2, -30.3), TOLERANCE) + end + end + + describe "with Fixnum" do + it "divides both parts of the Complex number" do + Complex(20, 40).send(@method, 2).should == Complex(10, 20) + Complex(30, 30).send(@method, 10).should == Complex(3, 3) + end + + it "raises a ZeroDivisionError when given zero" do + lambda { Complex(20, 40).send(@method, 0) }.should raise_error(ZeroDivisionError) + end + + it "produces Rational parts" do + Complex(5, 9).send(@method, 2).should eql(Complex(Rational(5,2), Rational(9,2))) + end + end + + describe "with Bignum" do + it "divides both parts of the Complex number" do + Complex(20, 40).send(@method, 2).should == Complex(10, 20) + Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE) + end + end + + describe "with Float" do + it "divides both parts of the Complex number" do + Complex(3, 9).send(@method, 1.5).should == Complex(2, 6) + Complex(15, 16).send(@method, 2.0).should be_close(Complex(7.5, 8), TOLERANCE) + end + + it "returns Complex(Infinity, Infinity) when given zero" do + Complex(20, 40).send(@method, 0.0).real.infinite?.should == 1 + Complex(20, 40).send(@method, 0.0).imag.infinite?.should == 1 + Complex(-20, 40).send(@method, 0.0).real.infinite?.should == -1 + Complex(-20, 40).send(@method, 0.0).imag.infinite?.should == 1 + end + end + + describe "with Object" do + it "tries to coerce self into other" do + value = Complex(3, 9) + + obj = mock("Object") + obj.should_receive(:coerce).with(value).and_return([4, 2]) + value.send(@method, obj).should == 2 + end + end + + describe "with a Numeric which responds to #real? with true" do + it "returns Complex(real.quo(other), imag.quo(other))" do + other = mock_numeric('other') + real = mock_numeric('real') + imag = mock_numeric('imag') + other.should_receive(:real?).and_return(true) + real.should_receive(:quo).with(other).and_return(1) + imag.should_receive(:quo).with(other).and_return(2) + Complex(real, imag).send(@method, other).should == Complex(1, 2) + end + end + + describe "with a Numeric which responds to #real? with false" do + it "coerces the passed argument to Complex and divides the resulting elements" do + complex = Complex(3, 0) + other = mock_numeric('other') + other.should_receive(:real?).any_number_of_times.and_return(false) + other.should_receive(:coerce).with(complex).and_return([5, 2]) + complex.send(@method, other).should eql(Rational(5, 2)) + end + end +end diff --git a/spec/rubyspec/shared/complex/equal_value.rb b/spec/rubyspec/shared/complex/equal_value.rb new file mode 100644 index 0000000000..d944698878 --- /dev/null +++ b/spec/rubyspec/shared/complex/equal_value.rb @@ -0,0 +1,93 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_equal_value, shared: true do + describe "with Complex" do + it "returns true when self and other have numerical equality" do + Complex(1, 2).should == Complex(1, 2) + Complex(3, 9).should == Complex(3, 9) + Complex(-3, -9).should == Complex(-3, -9) + + Complex(1, 2).should_not == Complex(3, 4) + Complex(3, 9).should_not == Complex(9, 3) + + Complex(1.0, 2.0).should == Complex(1, 2) + Complex(3.0, 9.0).should_not == Complex(9.0, 3.0) + + Complex(1.5, 2.5).should == Complex(1.5, 2.5) + Complex(1.5, 2.5).should == Complex(1.5, 2.5) + Complex(-1.5, 2.5).should == Complex(-1.5, 2.5) + + Complex(1.5, 2.5).should_not == Complex(2.5, 1.5) + Complex(3.75, 2.5).should_not == Complex(1.5, 2.5) + + Complex(bignum_value, 2.5).should == Complex(bignum_value, 2.5) + Complex(3.75, bignum_value).should_not == Complex(1.5, bignum_value) + + Complex(nan_value).should_not == Complex(nan_value) + end + end + + describe "with Numeric" do + it "returns true when self's imaginary part is 0 and the real part and other have numerical equality" do + Complex(3, 0).should == 3 + Complex(-3, 0).should == -3 + + Complex(3.5, 0).should == 3.5 + Complex(-3.5, 0).should == -3.5 + + Complex(bignum_value, 0).should == bignum_value + Complex(-bignum_value, 0).should == -bignum_value + + Complex(3.0, 0).should == 3 + Complex(-3.0, 0).should == -3 + + Complex(3, 0).should_not == 4 + Complex(-3, 0).should_not == -4 + + Complex(3.5, 0).should_not == -4.5 + Complex(-3.5, 0).should_not == 2.5 + + Complex(bignum_value, 0).should_not == bignum_value(10) + Complex(-bignum_value, 0).should_not == -bignum_value(20) + end + end + + describe "with Object" do + # Fixnum#==, Float#== and Bignum#== only return booleans - Bug? + it "calls other#== with self" do + value = Complex(3, 0) + + obj = mock("Object") + obj.should_receive(:==).with(value).and_return(:expected) + + (value == obj).should_not be_false + end + end + + describe "with a Numeric which responds to #real? with true" do + before do + @other = mock_numeric('other') + @other.should_receive(:real?).any_number_of_times.and_return(true) + end + + it "returns real == other when the imaginary part is zero" do + real = mock_numeric('real') + real.should_receive(:==).with(@other).and_return(true) + (Complex(real, 0) == @other).should be_true + end + + it "returns false when when the imaginary part is not zero" do + (Complex(3, 1) == @other).should be_false + end + end + + describe "with a Numeric which responds to #real? with false" do + it "returns other == self" do + complex = Complex(3, 0) + other = mock_numeric('other') + other.should_receive(:real?).any_number_of_times.and_return(false) + other.should_receive(:==).with(complex).and_return(true) + (complex == other).should be_true + end + end +end diff --git a/spec/rubyspec/shared/complex/exponent.rb b/spec/rubyspec/shared/complex/exponent.rb new file mode 100644 index 0000000000..8261db872a --- /dev/null +++ b/spec/rubyspec/shared/complex/exponent.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_exponent, shared: true do + describe "with Fixnum 0" do + it "returns Complex(1)" do + (Complex(3, 4) ** 0).should eql(Complex(1)) + end + end + + describe "with Float 0.0" do + it "returns Complex(1.0, 0.0)" do + (Complex(3, 4) ** 0.0).should eql(Complex(1.0, 0.0)) + end + end + + describe "with Complex" do + it "returns self raised to the given power" do + (Complex(2, 1) ** Complex(2, 1)).should be_close(Complex(-0.504824688978319, 3.10414407699553), TOLERANCE) + (Complex(2, 1) ** Complex(3, 4)).should be_close(Complex(-0.179174656916581, -1.74071656397662), TOLERANCE) + + (Complex(2, 1) ** Complex(-2, -1)).should be_close(Complex(-0.051041070450869, -0.313849223270419), TOLERANCE) + (Complex(-2, -1) ** Complex(2, 1)).should be_close(Complex(-11.6819929610857, 71.8320439736158), TOLERANCE) + end + end + + describe "with Integer" do + it "returns self raised to the given power" do + (Complex(2, 1) ** 2).should == Complex(3, 4) + (Complex(3, 4) ** 2).should == Complex(-7, 24) + (Complex(3, 4) ** -2).should be_close(Complex(-0.0112, -0.0384), TOLERANCE) + + + (Complex(2, 1) ** 2.5).should be_close(Complex(2.99179707178602, 6.85206901006896), TOLERANCE) + (Complex(3, 4) ** 2.5).should be_close(Complex(-38.0, 41.0), TOLERANCE) + (Complex(3, 4) ** -2.5).should be_close(Complex(-0.01216, -0.01312), TOLERANCE) + + (Complex(1) ** 1).should == Complex(1) + + # NOTE: Takes way too long... + #(Complex(2, 1) ** bignum_value) + end + end + + describe "with Rational" do + it "returns self raised to the given power" do + (Complex(2, 1) ** Rational(3, 4)).should be_close(Complex(1.71913265276568, 0.623124744394697), TOLERANCE) + (Complex(2, 1) ** Rational(4, 3)).should be_close(Complex(2.3828547125173, 1.69466313833091), TOLERANCE) + (Complex(2, 1) ** Rational(-4, 3)).should be_close(Complex(0.278700377879388, -0.198209003071003), TOLERANCE) + end + end + + describe "with Object" do + it "tries to coerce self into other" do + value = Complex(3, 9) + + obj = mock("Object") + obj.should_receive(:coerce).with(value).and_return([2, 5]) + (value ** obj).should == 2 ** 5 + end + end +end diff --git a/spec/rubyspec/shared/complex/float/arg.rb b/spec/rubyspec/shared/complex/float/arg.rb new file mode 100644 index 0000000000..ca29796610 --- /dev/null +++ b/spec/rubyspec/shared/complex/float/arg.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :float_arg, shared: true do + it "returns NaN if NaN" do + f = nan_value + f.send(@method).nan?.should be_true + end + + it "returns self if NaN" do + f = nan_value + f.send(@method).should equal(f) + end + + it "returns 0 if positive" do + 1.0.send(@method).should == 0 + end + + it "returns 0 if +0.0" do + 0.0.send(@method).should == 0 + end + + it "returns 0 if +Infinity" do + infinity_value.send(@method).should == 0 + end + + it "returns Pi if negative" do + (-1.0).send(@method).should == Math::PI + end + + # This was established in r23960 + it "returns Pi if -0.0" do + (-0.0).send(@method).should == Math::PI + end + + it "returns Pi if -Infinity" do + (-infinity_value).send(@method).should == Math::PI + end +end diff --git a/spec/rubyspec/shared/complex/hash.rb b/spec/rubyspec/shared/complex/hash.rb new file mode 100644 index 0000000000..26ca59aeaf --- /dev/null +++ b/spec/rubyspec/shared/complex/hash.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_hash, shared: true do + it "is static" do + Complex(1).hash.should == Complex(1).hash + Complex(1, 0).hash.should == Complex(1).hash + Complex(1, 1).hash.should == Complex(1, 1).hash + end + + it "is different for different instances" do + Complex(1, 2).hash.should_not == Complex(1, 1).hash + Complex(2, 1).hash.should_not == Complex(1, 1).hash + + Complex(1, 2).hash.should_not == Complex(2, 1).hash + end +end diff --git a/spec/rubyspec/shared/complex/image.rb b/spec/rubyspec/shared/complex/image.rb new file mode 100644 index 0000000000..5d45210b45 --- /dev/null +++ b/spec/rubyspec/shared/complex/image.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_image, shared: true do + it "returns the imaginary part of self" do + Complex(1, 0).send(@method).should == 0 + Complex(2, 1).send(@method).should == 1 + Complex(6.7, 8.9).send(@method).should == 8.9 + Complex(1, bignum_value).send(@method).should == bignum_value + end +end diff --git a/spec/rubyspec/shared/complex/inspect.rb b/spec/rubyspec/shared/complex/inspect.rb new file mode 100644 index 0000000000..7c0c3d6b9c --- /dev/null +++ b/spec/rubyspec/shared/complex/inspect.rb @@ -0,0 +1,14 @@ +describe :complex_inspect, shared: true do + it "returns (${real}+${image}i) for positive imaginary parts" do + Complex(1).inspect.should == "(1+0i)" + Complex(7).inspect.should == "(7+0i)" + Complex(-1, 4).inspect.should == "(-1+4i)" + Complex(-7, 6.7).inspect.should == "(-7+6.7i)" + end + + it "returns (${real}-${image}i) for negative imaginary parts" do + Complex(0, -1).inspect.should == "(0-1i)" + Complex(-1, -4).inspect.should == "(-1-4i)" + Complex(-7, -6.7).inspect.should == "(-7-6.7i)" + end +end diff --git a/spec/rubyspec/shared/complex/minus.rb b/spec/rubyspec/shared/complex/minus.rb new file mode 100644 index 0000000000..c28d08ad2e --- /dev/null +++ b/spec/rubyspec/shared/complex/minus.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_minus, shared: true do + describe "with Complex" do + it "subtracts both the real and imaginary components" do + (Complex(1, 2) - Complex(10, 20)).should == Complex(1 - 10, 2 - 20) + (Complex(1.5, 2.1) - Complex(100.2, -30.3)).should == Complex(1.5 - 100.2, 2.1 - (-30.3)) + end + end + + describe "with Integer" do + it "subtracts the real number from the real component of self" do + (Complex(1, 2) - 50).should == Complex(-49, 2) + (Complex(1, 2) - 50.5).should == Complex(-49.5, 2) + end + end + + describe "with Object" do + it "tries to coerce self into other" do + value = Complex(3, 9) + + obj = mock("Object") + obj.should_receive(:coerce).with(value).and_return([2, 5]) + (value - obj).should == 2 - 5 + end + end + + describe "passed Numeric which responds to #real? with true" do + it "coerces the passed argument to the type of the real part and subtracts the resulting elements" do + n = mock_numeric('n') + n.should_receive(:real?).and_return(true) + n.should_receive(:coerce).with(1).and_return([1, 4]) + (Complex(1, 2) - n).should == Complex(-3, 2) + end + end + + describe "passed Numeric which responds to #real? with false" do + it "coerces the passed argument to Complex and subtracts the resulting elements" do + n = mock_numeric('n') + n.should_receive(:real?).and_return(false) + n.should_receive(:coerce).with(Complex(1, 2)).and_return([Complex(1, 2), Complex(3, 4)]) + (Complex(1, 2) - n).should == Complex(-2, -2) + end + end +end diff --git a/spec/rubyspec/shared/complex/multiply.rb b/spec/rubyspec/shared/complex/multiply.rb new file mode 100644 index 0000000000..4d94ef2ce3 --- /dev/null +++ b/spec/rubyspec/shared/complex/multiply.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_multiply, shared: true do + describe "with Complex" do + it "multiplies according to the usual rule for complex numbers: (a + bi) * (c + di) = ac - bd + (ad + bc)i" do + (Complex(1, 2) * Complex(10, 20)).should == Complex((1 * 10) - (2 * 20), (1 * 20) + (2 * 10)) + (Complex(1.5, 2.1) * Complex(100.2, -30.3)).should == Complex((1.5 * 100.2) - (2.1 * -30.3), (1.5 * -30.3) + (2.1 * 100.2)) + end + end + + describe "with Integer" do + it "multiplies both parts of self by the given Integer" do + (Complex(3, 2) * 50).should == Complex(150, 100) + (Complex(-3, 2) * 50.5).should == Complex(-151.5, 101) + end + end + + describe "with Object" do + it "tries to coerce self into other" do + value = Complex(3, 9) + + obj = mock("Object") + obj.should_receive(:coerce).with(value).and_return([2, 5]) + (value * obj).should == 2 * 5 + end + end + + describe "with a Numeric which responds to #real? with true" do + it "multiples both parts of self by other" do + other = mock_numeric('other') + real = mock_numeric('real') + imag = mock_numeric('imag') + other.should_receive(:real?).and_return(true) + real.should_receive(:*).with(other).and_return(1) + imag.should_receive(:*).with(other).and_return(2) + (Complex(real, imag) * other).should == Complex(1, 2) + end + + describe "with a Numeric which responds to #real? with false" do + it "coerces the passed argument to Complex and multiplies the resulting elements" do + complex = Complex(3, 0) + other = mock_numeric('other') + other.should_receive(:real?).any_number_of_times.and_return(false) + other.should_receive(:coerce).with(complex).and_return([5, 2]) + (complex * other).should == 10 + end + end + end +end diff --git a/spec/rubyspec/shared/complex/numerator.rb b/spec/rubyspec/shared/complex/numerator.rb new file mode 100644 index 0000000000..b8384e4a93 --- /dev/null +++ b/spec/rubyspec/shared/complex/numerator.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_numerator, shared: true do + it "returns self's numerator" do + Complex(2).numerator.should == Complex(2) + Complex(3, 4).numerator.should == Complex(3, 4) + + Complex(Rational(3, 4), Rational(3, 4)).numerator.should == Complex(3, 3) + Complex(Rational(7, 4), Rational(8, 4)).numerator.should == Complex(7, 8) + + Complex(Rational(7, 8), Rational(8, 4)).numerator.should == Complex(7, 16) + Complex(Rational(7, 4), Rational(8, 8)).numerator.should == Complex(7, 4) + + # NOTE: + # Bug? - Fails with a MethodMissingError + # (undefined method `denominator' for 3.5:Float) + # Complex(3.5, 3.7).numerator + end +end diff --git a/spec/rubyspec/shared/complex/numeric/arg.rb b/spec/rubyspec/shared/complex/numeric/arg.rb new file mode 100644 index 0000000000..b7eb1f2e2d --- /dev/null +++ b/spec/rubyspec/shared/complex/numeric/arg.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :numeric_arg, shared: true do + before :each do + @numbers = [ + 20, + Rational(3, 4), + bignum_value, + infinity_value + ] + end + + it "returns 0 if positive" do + @numbers.each do |number| + number.send(@method).should == 0 + end + end + + it "returns Pi if negative" do + @numbers.each do |number| + (0-number).send(@method).should == Math::PI + end + end + + describe "with a Numeric subclass" do + it "returns 0 if self#<(0) returns false" do + numeric = mock_numeric('positive') + numeric.should_receive(:<).with(0).and_return(false) + numeric.send(@method).should == 0 + end + + it "returns Pi if self#<(0) returns true" do + numeric = mock_numeric('positive') + numeric.should_receive(:<).with(0).and_return(true) + numeric.send(@method).should == Math::PI + end + end +end diff --git a/spec/rubyspec/shared/complex/numeric/conj.rb b/spec/rubyspec/shared/complex/numeric/conj.rb new file mode 100644 index 0000000000..50cb060442 --- /dev/null +++ b/spec/rubyspec/shared/complex/numeric/conj.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :numeric_conj, shared: true do + before :each do + @numbers = [ + 20, # Integer + 398.72, # Float + Rational(3, 4), # Rational + bignum_value, + infinity_value, + nan_value + ] + end + + it "returns self" do + @numbers.each do |number| + number.send(@method).should equal(number) + end + end +end diff --git a/spec/rubyspec/shared/complex/numeric/imag.rb b/spec/rubyspec/shared/complex/numeric/imag.rb new file mode 100644 index 0000000000..caf54e2cf9 --- /dev/null +++ b/spec/rubyspec/shared/complex/numeric/imag.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :numeric_imag, shared: true do + before :each do + @numbers = [ + 20, # Integer + 398.72, # Float + Rational(3, 4), # Rational + bignum_value, # Bignum + infinity_value, + nan_value + ].map{|n| [n,-n]}.flatten + end + + it "returns 0" do + @numbers.each do |number| + number.send(@method).should == 0 + end + end + + it "raises an ArgumentError if given any arguments" do + @numbers.each do |number| + lambda { number.send(@method, number) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/shared/complex/numeric/polar.rb b/spec/rubyspec/shared/complex/numeric/polar.rb new file mode 100644 index 0000000000..952b65c1b6 --- /dev/null +++ b/spec/rubyspec/shared/complex/numeric/polar.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :numeric_polar, shared: true do + before :each do + @pos_numbers = [ + 1, + 3898172610**9, + 987.18273, + Float::MAX, + Rational(13,7), + infinity_value, + ] + @neg_numbers = @pos_numbers.map {|n| -n} + @numbers = @pos_numbers + @neg_numbers + @numbers.push(0, 0.0) + end + + it "returns a two-element Array" do + @numbers.each do |number| + number.polar.should be_an_instance_of(Array) + number.polar.size.should == 2 + end + end + + it "sets the first value to the absolute value of self" do + @numbers.each do |number| + number.polar.first.should == number.abs + end + end + + it "sets the last value to 0 if self is positive" do + (@numbers - @neg_numbers).each do |number| + number.should >= 0 + number.polar.last.should == 0 + end + end + + it "sets the last value to Pi if self is negative" do + @neg_numbers.each do |number| + number.should < 0 + number.polar.last.should == Math::PI + end + end + + it "returns [NaN, NaN] if self is NaN" do + nan_value.polar.size.should == 2 + nan_value.polar.first.nan?.should be_true + nan_value.polar.last.nan?.should be_true + end +end diff --git a/spec/rubyspec/shared/complex/numeric/real.rb b/spec/rubyspec/shared/complex/numeric/real.rb new file mode 100644 index 0000000000..0dcf2e8381 --- /dev/null +++ b/spec/rubyspec/shared/complex/numeric/real.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :numeric_real, shared: true do + before :each do + @numbers = [ + 20, # Integer + 398.72, # Float + Rational(3, 4), # Rational + bignum_value, # Bignum + infinity_value, + nan_value + ].map{|n| [n,-n]}.flatten + end + + it "returns self" do + @numbers.each do |number| + if number.to_f.nan? + number.real.nan?.should be_true + else + number.real.should == number + end + end + end + + it "raises an ArgumentError if given any arguments" do + @numbers.each do |number| + lambda { number.real(number) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/shared/complex/plus.rb b/spec/rubyspec/shared/complex/plus.rb new file mode 100644 index 0000000000..47e362d886 --- /dev/null +++ b/spec/rubyspec/shared/complex/plus.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_plus, shared: true do + describe "with Complex" do + it "adds both the real and imaginary components" do + (Complex(1, 2) + Complex(10, 20)).should == Complex(1 + 10, 2 + 20) + (Complex(1.5, 2.1) + Complex(100.2, -30.3)).should == Complex(1.5 + 100.2, 2.1 + (-30.3)) + end + end + + describe "with Integer" do + it "adds the real number to the real component of self" do + (Complex(1, 2) + 50).should == Complex(51, 2) + (Complex(1, 2) + 50.5).should == Complex(51.5, 2) + end + end + + describe "with Object" do + it "tries to coerce self into other" do + value = Complex(3, 9) + + obj = mock("Object") + obj.should_receive(:coerce).with(value).and_return([2, 5]) + (value + obj).should == 2 + 5 + end + end + + describe "passed Numeric which responds to #real? with true" do + it "coerces the passed argument to the type of the real part and adds the resulting elements" do + n = mock_numeric('n') + n.should_receive(:real?).and_return(true) + n.should_receive(:coerce).with(1).and_return([1, 4]) + (Complex(1, 2) + n).should == Complex(5, 2) + end + end + + describe "passed Numeric which responds to #real? with false" do + it "coerces the passed argument to Complex and adds the resulting elements" do + n = mock_numeric('n') + n.should_receive(:real?).and_return(false) + n.should_receive(:coerce).with(Complex(1, 2)).and_return([Complex(1, 2), Complex(3, 4)]) + (Complex(1, 2) + n).should == Complex(4, 6) + end + end +end diff --git a/spec/rubyspec/shared/complex/polar.rb b/spec/rubyspec/shared/complex/polar.rb new file mode 100644 index 0000000000..acc063d89f --- /dev/null +++ b/spec/rubyspec/shared/complex/polar.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_polar_class, shared: true do + it "returns a complex number in terms of radius and angle" do + Complex.polar(50, 60).should be_close(Complex(-47.6206490207578, -15.2405310551108), TOLERANCE) + Complex.polar(-10, -20).should be_close(Complex(-4.08082061813392, 9.12945250727628), TOLERANCE) + end +end + +describe :complex_polar, shared: true do + it "returns the absolute value and the argument" do + a = Complex(3, 4) + a.polar.size.should == 2 + a.polar.first.should == 5.0 + a.polar.last.should be_close(0.927295218001612, TOLERANCE) + + b = Complex(-3.5, 4.7) + b.polar.size.should == 2 + b.polar.first.should be_close(5.86003412959345, TOLERANCE) + b.polar.last.should be_close(2.21088447955664, TOLERANCE) + end +end diff --git a/spec/rubyspec/shared/complex/real.rb b/spec/rubyspec/shared/complex/real.rb new file mode 100644 index 0000000000..ab8ed07a4d --- /dev/null +++ b/spec/rubyspec/shared/complex/real.rb @@ -0,0 +1,8 @@ +describe :complex_real, shared: true do + it "returns the real part of self" do + Complex(1, 0).real.should == 1 + Complex(2, 1).real.should == 2 + Complex(6.7, 8.9).real.should == 6.7 + Complex(bignum_value, 3).real.should == bignum_value + end +end diff --git a/spec/rubyspec/shared/complex/rect.rb b/spec/rubyspec/shared/complex/rect.rb new file mode 100644 index 0000000000..8a59d873eb --- /dev/null +++ b/spec/rubyspec/shared/complex/rect.rb @@ -0,0 +1,96 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_rect, shared: true do + before :each do + @numbers = [ + Complex(1), + Complex(0, 20), + Complex(0, 0), + Complex(0.0), + Complex(9999999**99), + Complex(-20), + Complex.polar(76, 10) + ] + end + + it "returns an Array" do + @numbers.each do |number| + number.send(@method).should be_an_instance_of(Array) + end + end + + it "returns a two-element Array" do + @numbers.each do |number| + number.send(@method).size.should == 2 + end + end + + it "returns the real part of self as the first element" do + @numbers.each do |number| + number.send(@method).first.should == number.real + end + end + + it "returns the imaginary part of self as the last element" do + @numbers.each do |number| + number.send(@method).last.should == number.imaginary + end + end + + it "raises an ArgumentError if given any arguments" do + @numbers.each do |number| + lambda { number.send(@method, number) }.should raise_error(ArgumentError) + end + end +end + +describe :complex_rect_class, shared: true do + describe "passed a Numeric n which responds to #real? with true" do + it "returns a Complex with real part n and imaginary part 0" do + n = mock_numeric('n') + n.should_receive(:real?).any_number_of_times.and_return(true) + result = Complex.send(@method, n) + result.real.should == n + result.imag.should == 0 + end + end + + describe "passed a Numeric which responds to #real? with false" do + it "raises TypeError" do + n = mock_numeric('n') + n.should_receive(:real?).any_number_of_times.and_return(false) + lambda { Complex.send(@method, n) }.should raise_error(TypeError) + end + end + + describe "passed Numerics n1 and n2 and at least one responds to #real? with false" do + [[false, false], [false, true], [true, false]].each do |r1, r2| + it "raises TypeError" do + n1 = mock_numeric('n1') + n2 = mock_numeric('n2') + n1.should_receive(:real?).any_number_of_times.and_return(r1) + n2.should_receive(:real?).any_number_of_times.and_return(r2) + lambda { Complex.send(@method, n1, n2) }.should raise_error(TypeError) + end + end + end + + describe "passed Numerics n1 and n2 and both respond to #real? with true" do + it "returns a Complex with real part n1 and imaginary part n2" do + n1 = mock_numeric('n1') + n2 = mock_numeric('n2') + n1.should_receive(:real?).any_number_of_times.and_return(true) + n2.should_receive(:real?).any_number_of_times.and_return(true) + result = Complex.send(@method, n1, n2) + result.real.should == n1 + result.imag.should == n2 + end + end + + describe "passed a non-Numeric" do + it "raises TypeError" do + lambda { Complex.send(@method, :sym) }.should raise_error(TypeError) + lambda { Complex.send(@method, 0, :sym) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/shared/complex/to_s.rb b/spec/rubyspec/shared/complex/to_s.rb new file mode 100644 index 0000000000..03f4f98b84 --- /dev/null +++ b/spec/rubyspec/shared/complex/to_s.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :complex_to_s, shared: true do + describe "when self's real component is 0" do + it "returns both the real and imaginary component even when the real is 0" do + Complex(0, 5).to_s.should == "0+5i" + Complex(0, -3.2).to_s.should == "0-3.2i" + end + end + + it "returns self as String" do + Complex(1, 5).to_s.should == "1+5i" + Complex(-2.5, 1.5).to_s.should == "-2.5+1.5i" + + Complex(1, -5).to_s.should == "1-5i" + Complex(-2.5, -1.5).to_s.should == "-2.5-1.5i" + + # Guard against the Mathn library + conflicts_with :Prime do + Complex(1, 0).to_s.should == "1+0i" + Complex(1, -0).to_s.should == "1+0i" + end + end + + it "returns 1+0.0i for Complex(1, 0.0)" do + Complex(1, 0.0).to_s.should == "1+0.0i" + end + + it "returns 1-0.0i for Complex(1, -0.0)" do + Complex(1, -0.0).to_s.should == "1-0.0i" + end + + it "returns 1+Infinity*i for Complex(1, Infinity)" do + Complex(1, infinity_value).to_s.should == "1+Infinity*i" + end + + it "returns 1-Infinity*i for Complex(1, -Infinity)" do + Complex(1, -infinity_value).to_s.should == "1-Infinity*i" + end + + it "returns 1+NaN*i for Complex(1, NaN)" do + Complex(1, nan_value).to_s.should == "1+NaN*i" + end +end diff --git a/spec/rubyspec/shared/enumerator/each.rb b/spec/rubyspec/shared/enumerator/each.rb new file mode 100644 index 0000000000..bbab86fed7 --- /dev/null +++ b/spec/rubyspec/shared/enumerator/each.rb @@ -0,0 +1,89 @@ +# -*- encoding: us-ascii -*- + +describe :enum_each, shared: true do + before :each do + object_each_with_arguments = Object.new + def object_each_with_arguments.each_with_arguments(arg, *args) + yield arg, *args + :method_returned + end + + @enum_with_arguments = object_each_with_arguments.to_enum(:each_with_arguments, :arg0, :arg1, :arg2) + + @enum_with_yielder = Enumerator.new {|y| y.yield :ok} + end + + it "yields each element of self to the given block" do + acc = [] + [1,2,3].to_enum.each {|e| acc << e } + acc.should == [1,2,3] + end + + it "calls #each on the object given in the constructor by default" do + each = mock('each') + each.should_receive(:each) + each.to_enum.each {|e| e } + end + + it "calls #each on the underlying object until it's exhausted" do + each = mock('each') + each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3) + acc = [] + each.to_enum.each {|e| acc << e } + acc.should == [1,2,3] + end + + it "calls the method given in the constructor instead of #each" do + each = mock('peach') + each.should_receive(:peach) + each.to_enum(:peach).each {|e| e } + end + + it "calls the method given in the constructor until it's exhausted" do + each = mock('each') + each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3) + acc = [] + each.to_enum.each {|e| acc << e } + acc.should == [1,2,3] + end + + it "raises a NoMethodError if the object doesn't respond to #each" do + enum = Object.new.to_enum + lambda do + enum.each { |e| e } + end.should raise_error(NoMethodError) + end + + it "returns self if not given arguments and not given a block" do + @enum_with_arguments.each.should equal(@enum_with_arguments) + + @enum_with_yielder.each.should equal(@enum_with_yielder) + end + + it "returns the same value from receiver.each if block is given" do + @enum_with_arguments.each {}.should equal(:method_returned) + end + + it "passes given arguments at initialized to receiver.each" do + @enum_with_arguments.each.to_a.should == [[:arg0, :arg1, :arg2]] + end + + it "requires multiple arguments" do + Enumerator.instance_method(:each).arity.should < 0 + end + + it "appends given arguments to receiver.each" do + @enum_with_arguments.each(:each0, :each1).to_a.should == [[:arg0, :arg1, :arg2, :each0, :each1]] + @enum_with_arguments.each(:each2, :each3).to_a.should == [[:arg0, :arg1, :arg2, :each2, :each3]] + end + + it "returns the same value from receiver.each if block and arguments are given" do + @enum_with_arguments.each(:each1, :each2) {}.should equal(:method_returned) + end + + it "returns new Enumerator if given arguments but not given a block" do + ret = @enum_with_arguments.each 1 + ret.should be_an_instance_of(Enumerator) + ret.should_not equal(@enum_with_arguments) + end +end diff --git a/spec/rubyspec/shared/enumerator/enum_cons.rb b/spec/rubyspec/shared/enumerator/enum_cons.rb new file mode 100644 index 0000000000..59eed949d8 --- /dev/null +++ b/spec/rubyspec/shared/enumerator/enum_cons.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/enumerator/classes', __FILE__) + +describe :enum_cons, shared: true do + it "returns an enumerator of the receiver with iteration of each_cons for each array of n concecutive elements" do + a = [] + enum = EnumSpecs::Numerous.new.enum_cons(3) + enum.each {|x| a << x} + enum.should be_an_instance_of(Enumerator) + a.should == [[2, 5, 3], [5, 3, 6], [3, 6, 1], [6, 1, 4]] + end +end diff --git a/spec/rubyspec/shared/enumerator/enum_for.rb b/spec/rubyspec/shared/enumerator/enum_for.rb new file mode 100644 index 0000000000..9030ffbd7d --- /dev/null +++ b/spec/rubyspec/shared/enumerator/enum_for.rb @@ -0,0 +1,50 @@ +describe :enum_for, shared: true do + it "is defined in Kernel" do + Kernel.method_defined?(@method).should be_true + end + + it "returns a new enumerator" do + "abc".send(@method).should be_an_instance_of(Enumerator) + end + + it "defaults the first argument to :each" do + enum = [1,2].send(@method) + enum.map { |v| v }.should == [1,2].each { |v| v } + end + + it "exposes multi-arg yields as an array" do + o = Object.new + def o.each + yield :a + yield :b1, :b2 + yield [:c] + yield :d1, :d2 + yield :e1, :e2, :e3 + end + + enum = o.send(@method) + enum.next.should == :a + enum.next.should == [:b1, :b2] + enum.next.should == [:c] + enum.next.should == [:d1, :d2] + enum.next.should == [:e1, :e2, :e3] + end + + it "uses the passed block's value to calculate the size of the enumerator" do + Object.new.enum_for { 100 }.size.should == 100 + end + + it "defers the evaluation of the passed block until #size is called" do + ScratchPad.record [] + + enum = Object.new.enum_for do + ScratchPad << :called + 100 + end + + ScratchPad.recorded.should be_empty + + enum.size + ScratchPad.recorded.should == [:called] + end +end diff --git a/spec/rubyspec/shared/enumerator/new.rb b/spec/rubyspec/shared/enumerator/new.rb new file mode 100644 index 0000000000..23ace99816 --- /dev/null +++ b/spec/rubyspec/shared/enumerator/new.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :enum_new, shared: true do + it "creates a new custom enumerator with the given object, iterator and arguments" do + enum = Enumerator.new(1, :upto, 3) + enum.should be_an_instance_of(Enumerator) + end + + it "creates a new custom enumerator that responds to #each" do + enum = Enumerator.new(1, :upto, 3) + enum.respond_to?(:each).should == true + end + + it "creates a new custom enumerator that runs correctly" do + Enumerator.new(1, :upto, 3).map{|x|x}.should == [1,2,3] + end + + it "aliases the second argument to :each" do + Enumerator.new(1..2).to_a.should == Enumerator.new(1..2, :each).to_a + end + + it "doesn't check for the presence of the iterator method" do + Enumerator.new(nil).should be_an_instance_of(Enumerator) + end + + it "uses the latest define iterator method" do + class StrangeEach + def each + yield :foo + end + end + enum = Enumerator.new(StrangeEach.new) + enum.to_a.should == [:foo] + class StrangeEach + def each + yield :bar + end + end + enum.to_a.should == [:bar] + end + +end diff --git a/spec/rubyspec/shared/enumerator/next.rb b/spec/rubyspec/shared/enumerator/next.rb new file mode 100644 index 0000000000..d8ae6d9673 --- /dev/null +++ b/spec/rubyspec/shared/enumerator/next.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :enum_next, shared: true do + + before :each do + @enum = 1.upto(3) + end + + it "returns the next element of the enumeration" do + @enum.next.should == 1 + @enum.next.should == 2 + @enum.next.should == 3 + end + + it "raises a StopIteration exception at the end of the stream" do + 3.times { @enum.next } + lambda { @enum.next }.should raise_error(StopIteration) + end + + it "cannot be called again until the enumerator is rewound" do + 3.times { @enum.next } + lambda { @enum.next }.should raise_error(StopIteration) + lambda { @enum.next }.should raise_error(StopIteration) + lambda { @enum.next }.should raise_error(StopIteration) + @enum.rewind + @enum.next.should == 1 + end +end diff --git a/spec/rubyspec/shared/enumerator/rewind.rb b/spec/rubyspec/shared/enumerator/rewind.rb new file mode 100644 index 0000000000..4d4139204c --- /dev/null +++ b/spec/rubyspec/shared/enumerator/rewind.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :enum_rewind, shared: true do + + before :each do + @enum = 1.upto(3) + end + + it "resets the enumerator to its initial state" do + @enum.next.should == 1 + @enum.next.should == 2 + @enum.rewind + @enum.next.should == 1 + end + + it "returns self" do + @enum.rewind.should == @enum + end + + it "has no effect on a new enumerator" do + @enum.rewind + @enum.next.should == 1 + end + + it "has no effect if called multiple, consecutive times" do + @enum.next.should == 1 + @enum.rewind + @enum.rewind + @enum.next.should == 1 + end + + it "works with peek to reset the position" do + @enum.next + @enum.next + @enum.rewind + @enum.next + @enum.peek.should == 2 + end +end diff --git a/spec/rubyspec/shared/enumerator/with_index.rb b/spec/rubyspec/shared/enumerator/with_index.rb new file mode 100644 index 0000000000..37048a7b52 --- /dev/null +++ b/spec/rubyspec/shared/enumerator/with_index.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :enum_with_index, shared: true do + + require File.expand_path('../../../fixtures/enumerator/classes', __FILE__) + + before :each do + @enum = [1, 2, 3, 4].to_enum + end + + it "passes each element and its index to block" do + @a = [] + @enum.send(@method) { |o, i| @a << [o, i] } + @a.should == [[1, 0], [2, 1], [3, 2], [4, 3]] + end + + it "returns the object being enumerated when given a block" do + [1, 2, 3, 4].should == @enum.send(@method) { |o, i| :glark } + end + + it "binds splat arguments properly" do + acc = [] + @enum.send(@method) { |*b| c,d = b; acc << c; acc << d } + [1, 0, 2, 1, 3, 2, 4, 3].should == acc + end + + it "returns an enumerator if no block is supplied" do + ewi = @enum.send(@method) + ewi.should be_an_instance_of(Enumerator) + ewi.to_a.should == [[1, 0], [2, 1], [3, 2], [4, 3]] + end +end diff --git a/spec/rubyspec/shared/enumerator/with_object.rb b/spec/rubyspec/shared/enumerator/with_object.rb new file mode 100644 index 0000000000..1830736713 --- /dev/null +++ b/spec/rubyspec/shared/enumerator/with_object.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :enum_with_object, shared: true do + before :each do + @enum = [:a, :b].to_enum + @memo = '' + @block_params = @enum.send(@method, @memo).to_a + end + + it "receives an argument" do + @enum.method(@method).arity.should == 1 + end + + context "with block" do + it "returns the given object" do + ret = @enum.send(@method, @memo) do |elm, memo| + # nothing + end + ret.should equal(@memo) + end + + context "the block parameter" do + it "passes each element to first parameter" do + @block_params[0][0].should equal(:a) + @block_params[1][0].should equal(:b) + end + + it "passes the given object to last parameter" do + @block_params[0][1].should equal(@memo) + @block_params[1][1].should equal(@memo) + end + end + end + + context "without block" do + it "returns new Enumerator" do + ret = @enum.send(@method, @memo) + ret.should be_an_instance_of(Enumerator) + ret.should_not equal(@enum) + end + end +end diff --git a/spec/rubyspec/shared/fiber/resume.rb b/spec/rubyspec/shared/fiber/resume.rb new file mode 100644 index 0000000000..304cd88de1 --- /dev/null +++ b/spec/rubyspec/shared/fiber/resume.rb @@ -0,0 +1,77 @@ +describe :fiber_resume, shared: true do + it "can be invoked from the root Fiber" do + fiber = Fiber.new { :fiber } + fiber.send(@method).should == :fiber + end + + it "raises a FiberError if invoked from a different Thread" do + fiber = Fiber.new { } + lambda do + Thread.new do + fiber.resume + end.join + end.should raise_error(FiberError) + fiber.resume + end + + it "passes control to the beginning of the block on first invocation" do + invoked = false + fiber = Fiber.new { invoked = true } + fiber.send(@method) + invoked.should be_true + end + + it "returns the last value encountered on first invocation" do + fiber = Fiber.new { 1+1; true } + fiber.send(@method).should be_true + end + + it "runs until the end of the block" do + obj = mock('obj') + obj.should_receive(:do).once + fiber = Fiber.new { 1 + 2; a = "glark"; obj.do } + fiber.send(@method) + end + + it "runs until Fiber.yield" do + obj = mock('obj') + obj.should_not_receive(:do) + fiber = Fiber.new { 1 + 2; Fiber.yield; obj.do } + fiber.send(@method) + end + + it "resumes from the last call to Fiber.yield on subsequent invocations" do + fiber = Fiber.new { Fiber.yield :first; :second } + fiber.send(@method).should == :first + fiber.send(@method).should == :second + end + + it "accepts any number of arguments" do + fiber = Fiber.new { |a| } + lambda { fiber.send(@method, *(1..10).to_a) }.should_not raise_error + end + + it "sets the block parameters to its arguments on the first invocation" do + first = mock('first') + first.should_receive(:arg).with(:first).twice + fiber = Fiber.new { |arg| first.arg arg; Fiber.yield; first.arg arg; } + fiber.send(@method, :first) + fiber.send(@method, :second) + end + + it "raises a FiberError if the Fiber is dead" do + fiber = Fiber.new { true } + fiber.send(@method) + lambda { fiber.send(@method) }.should raise_error(FiberError) + end + + it "raises a LocalJumpError if the block includes a return statement" do + fiber = Fiber.new { return; } + lambda { fiber.send(@method) }.should raise_error(LocalJumpError) + end + + it "raises a LocalJumpError if the block includes a break statement" do + fiber = Fiber.new { break; } + lambda { fiber.send(@method) }.should raise_error(LocalJumpError) + end +end diff --git a/spec/rubyspec/shared/file/blockdev.rb b/spec/rubyspec/shared/file/blockdev.rb new file mode 100644 index 0000000000..b0b3ea040a --- /dev/null +++ b/spec/rubyspec/shared/file/blockdev.rb @@ -0,0 +1,9 @@ +describe :file_blockdev, shared: true do + it "returns true/false depending if the named file is a block device" do + @object.send(@method, tmp("")).should == false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(tmp(""))).should == false + end +end diff --git a/spec/rubyspec/shared/file/chardev.rb b/spec/rubyspec/shared/file/chardev.rb new file mode 100644 index 0000000000..8a7a89fd05 --- /dev/null +++ b/spec/rubyspec/shared/file/chardev.rb @@ -0,0 +1,9 @@ +describe :file_chardev, shared: true do + it "returns true/false depending if the named file is a char device" do + @object.send(@method, tmp("")).should == false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(tmp(""))).should == false + end +end diff --git a/spec/rubyspec/shared/file/directory.rb b/spec/rubyspec/shared/file/directory.rb new file mode 100644 index 0000000000..67e939bf16 --- /dev/null +++ b/spec/rubyspec/shared/file/directory.rb @@ -0,0 +1,66 @@ +describe :file_directory, shared: true do + before :each do + @dir = tmp("file_directory") + @file = tmp("file_directory.txt") + + mkdir_p @dir + touch @file + end + + after :each do + rm_r @dir, @file + end + + it "returns true if the argument is a directory" do + @object.send(@method, @dir).should be_true + end + + it "returns false if the argument is not a directory" do + @object.send(@method, @file).should be_false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@dir)).should be_true + end + + it "raises a TypeError when passed an Integer" do + lambda { @object.send(@method, 1) }.should raise_error(TypeError) + lambda { @object.send(@method, bignum_value) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + end +end + +describe :file_directory_io, shared: true do + before :each do + @dir = tmp("file_directory_io") + @file = tmp("file_directory_io.txt") + + mkdir_p @dir + touch @file + end + + after :each do + rm_r @dir, @file + end + + it "returns false if the argument is an IO that's not a directory" do + @object.send(@method, STDIN).should be_false + end + + platform_is_not :windows do + it "returns true if the argument is an IO that is a directory" do + File.open(@dir, "r") do |f| + @object.send(@method, f).should be_true + end + end + end + + it "calls #to_io to convert a non-IO object" do + io = mock('FileDirectoryIO') + io.should_receive(:to_io).and_return(STDIN) + @object.send(@method, io).should be_false + end +end diff --git a/spec/rubyspec/shared/file/executable.rb b/spec/rubyspec/shared/file/executable.rb new file mode 100644 index 0000000000..5b21fb0d97 --- /dev/null +++ b/spec/rubyspec/shared/file/executable.rb @@ -0,0 +1,48 @@ +describe :file_executable, shared: true do + before :each do + @file1 = tmp('temp1.txt') + @file2 = tmp('temp2.txt') + + touch @file1 + touch @file2 + + File.chmod(0755, @file1) + end + + after :each do + rm_r @file1, @file2 + end + + platform_is_not :windows do + it "returns true if named file is executable by the effective user id of the process, otherwise false" do + @object.send(@method, '/etc/passwd').should == false + @object.send(@method, @file1).should == true + @object.send(@method, @file2).should == false + end + + it "returns true if the argument is an executable file" do + @object.send(@method, @file1).should == true + @object.send(@method, @file2).should == false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@file1)).should == true + end + end + + it "raises an ArgumentError if not passed one argument" do + lambda { @object.send(@method) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed a String type" do + lambda { @object.send(@method, 1) }.should raise_error(TypeError) + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + lambda { @object.send(@method, false) }.should raise_error(TypeError) + end +end + +describe :file_executable_missing, shared: true do + it "returns false if the file does not exist" do + @object.send(@method, 'fake_file').should == false + end +end diff --git a/spec/rubyspec/shared/file/executable_real.rb b/spec/rubyspec/shared/file/executable_real.rb new file mode 100644 index 0000000000..2b1bf8f585 --- /dev/null +++ b/spec/rubyspec/shared/file/executable_real.rb @@ -0,0 +1,46 @@ +describe :file_executable_real, shared: true do + before :each do + @file1 = tmp('temp1.txt.exe') + @file2 = tmp('temp2.txt') + + touch @file1 + touch @file2 + + File.chmod(0755, @file1) + end + + after :each do + rm_r @file1, @file2 + end + + platform_is_not :windows do + it "returns true if the file its an executable" do + @object.send(@method, @file1).should == true + @object.send(@method, @file2).should == false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@file1)).should == true + end + end + + it "returns true if named file is readable by the real user id of the process, otherwise false" do + @object.send(@method, @file1).should == true + end + + it "raises an ArgumentError if not passed one argument" do + lambda { @object.send(@method) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed a String type" do + lambda { @object.send(@method, 1) }.should raise_error(TypeError) + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + lambda { @object.send(@method, false) }.should raise_error(TypeError) + end +end + +describe :file_executable_real_missing, shared: true do + it "returns false if the file does not exist" do + @object.send(@method, 'fake_file').should == false + end +end diff --git a/spec/rubyspec/shared/file/exist.rb b/spec/rubyspec/shared/file/exist.rb new file mode 100644 index 0000000000..1557f01a82 --- /dev/null +++ b/spec/rubyspec/shared/file/exist.rb @@ -0,0 +1,24 @@ +describe :file_exist, shared: true do + it "returns true if the file exist" do + @object.send(@method, __FILE__).should == true + @object.send(@method, 'a_fake_file').should == false + end + + it "returns true if the file exist using the alias exists?" do + @object.send(@method, __FILE__).should == true + @object.send(@method, 'a_fake_file').should == false + end + + it "raises an ArgumentError if not passed one argument" do + lambda { @object.send(@method) }.should raise_error(ArgumentError) + lambda { @object.send(@method, __FILE__, __FILE__) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed a String type" do + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(__FILE__)).should == true + end +end diff --git a/spec/rubyspec/shared/file/file.rb b/spec/rubyspec/shared/file/file.rb new file mode 100644 index 0000000000..095bd63fff --- /dev/null +++ b/spec/rubyspec/shared/file/file.rb @@ -0,0 +1,45 @@ +describe :file_file, shared: true do + before :each do + platform_is :windows do + @null = "NUL" + @dir = "C:\\" + end + + platform_is_not :windows do + @null = "/dev/null" + @dir = "/bin" + end + + @file = tmp("test.txt") + touch @file + end + + after :each do + rm_r @file + end + + it "returns true if the named file exists and is a regular file." do + @object.send(@method, @file).should == true + @object.send(@method, @dir).should == false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@file)).should == true + end + + platform_is_not :windows do + it "returns true if the null device exists and is a regular file." do + @object.send(@method, @null).should == false # May fail on MS Windows + end + end + + it "raises an ArgumentError if not passed one argument" do + lambda { @object.send(@method) }.should raise_error(ArgumentError) + lambda { @object.send(@method, @null, @file) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed a String type" do + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + lambda { @object.send(@method, 1) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/shared/file/grpowned.rb b/spec/rubyspec/shared/file/grpowned.rb new file mode 100644 index 0000000000..91a6483030 --- /dev/null +++ b/spec/rubyspec/shared/file/grpowned.rb @@ -0,0 +1,40 @@ +describe :file_grpowned, shared: true do + before :each do + @file = tmp('i_exist') + touch(@file) { |f| f.puts "file_content" } + File.chown(nil, Process.gid, @file) rescue nil + end + + after :each do + rm_r @file + end + + platform_is_not :windows do + it "returns true if the file exist" do + @object.send(@method, @file).should be_true + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@file)).should be_true + end + + it 'takes non primary groups into account' do + group = (Process.groups - [Process.egid]).first + + if group + File.chown(nil, group, @file) + + @object.send(@method, @file).should == true + else + # No supplementary groups + 1.should == 1 + end + end + end + + platform_is :windows do + it "returns false if the file exist" do + @object.send(@method, @file).should be_false + end + end +end diff --git a/spec/rubyspec/shared/file/identical.rb b/spec/rubyspec/shared/file/identical.rb new file mode 100644 index 0000000000..e89cd309ea --- /dev/null +++ b/spec/rubyspec/shared/file/identical.rb @@ -0,0 +1,47 @@ +describe :file_identical, shared: true do + before :each do + @file1 = tmp('file_identical.txt') + @file2 = tmp('file_identical2.txt') + @link = tmp('file_identical.lnk') + @non_exist = 'non_exist' + + touch(@file1) { |f| f.puts "file1" } + touch(@file2) { |f| f.puts "file2" } + + rm_r @link + File.link(@file1, @link) + end + + after :each do + rm_r @link, @file1, @file2 + end + + it "returns true for a file and its link" do + @object.send(@method, @file1, @link).should == true + end + + it "returns false if any of the files doesn't exist" do + @object.send(@method, @file1, @non_exist).should be_false + @object.send(@method, @non_exist, @file1).should be_false + @object.send(@method, @non_exist, @non_exist).should be_false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@file1), mock_to_path(@link)).should == true + end + + it "raises an ArgumentError if not passed two arguments" do + lambda { @object.send(@method, @file1, @file2, @link) }.should raise_error(ArgumentError) + lambda { @object.send(@method, @file1) }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed String types" do + lambda { @object.send(@method, 1,1) }.should raise_error(TypeError) + end + + it "returns true if both named files are identical" do + @object.send(@method, @file1, @file1).should be_true + @object.send(@method, @link, @link).should be_true + @object.send(@method, @file1, @file2).should be_false + end +end diff --git a/spec/rubyspec/shared/file/owned.rb b/spec/rubyspec/shared/file/owned.rb new file mode 100644 index 0000000000..4a08a4ed89 --- /dev/null +++ b/spec/rubyspec/shared/file/owned.rb @@ -0,0 +1,3 @@ +describe :file_owned, shared: true do + it "accepts an object that has a #to_path method" +end diff --git a/spec/rubyspec/shared/file/pipe.rb b/spec/rubyspec/shared/file/pipe.rb new file mode 100644 index 0000000000..7e150b9167 --- /dev/null +++ b/spec/rubyspec/shared/file/pipe.rb @@ -0,0 +1,3 @@ +describe :file_pipe, shared: true do + it "accepts an object that has a #to_path method" +end diff --git a/spec/rubyspec/shared/file/readable.rb b/spec/rubyspec/shared/file/readable.rb new file mode 100644 index 0000000000..240c378d95 --- /dev/null +++ b/spec/rubyspec/shared/file/readable.rb @@ -0,0 +1,30 @@ +describe :file_readable, shared: true do + before :each do + @file = tmp('i_exist') + platform_is :windows do + @file2 = "C:\\windows\\notepad.exe" + end + platform_is_not :windows do + @file2 = "/etc/passwd" + end + end + + after :each do + rm_r @file + end + + it "returns true if named file is readable by the effective user id of the process, otherwise false" do + @object.send(@method, @file2).should == true + File.open(@file,'w') { @object.send(@method, @file).should == true } + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@file2)).should == true + end +end + +describe :file_readable_missing, shared: true do + it "returns false if the file does not exist" do + @object.send(@method, 'fake_file').should == false + end +end diff --git a/spec/rubyspec/shared/file/readable_real.rb b/spec/rubyspec/shared/file/readable_real.rb new file mode 100644 index 0000000000..b6e53ac76d --- /dev/null +++ b/spec/rubyspec/shared/file/readable_real.rb @@ -0,0 +1,23 @@ +describe :file_readable_real, shared: true do + before :each do + @file = tmp('i_exist') + end + + after :each do + rm_r @file + end + + it "returns true if named file is readable by the real user id of the process, otherwise false" do + File.open(@file,'w') { @object.send(@method, @file).should == true } + end + + it "accepts an object that has a #to_path method" do + File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true } + end +end + +describe :file_readable_real_missing, shared: true do + it "returns false if the file does not exist" do + @object.send(@method, 'fake_file').should == false + end +end diff --git a/spec/rubyspec/shared/file/setgid.rb b/spec/rubyspec/shared/file/setgid.rb new file mode 100644 index 0000000000..9893795832 --- /dev/null +++ b/spec/rubyspec/shared/file/setgid.rb @@ -0,0 +1,2 @@ +describe :file_setgid, shared: true do +end diff --git a/spec/rubyspec/shared/file/setuid.rb b/spec/rubyspec/shared/file/setuid.rb new file mode 100644 index 0000000000..6401674a94 --- /dev/null +++ b/spec/rubyspec/shared/file/setuid.rb @@ -0,0 +1,2 @@ +describe :file_setuid, shared: true do +end diff --git a/spec/rubyspec/shared/file/size.rb b/spec/rubyspec/shared/file/size.rb new file mode 100644 index 0000000000..bb95190fc0 --- /dev/null +++ b/spec/rubyspec/shared/file/size.rb @@ -0,0 +1,124 @@ +describe :file_size, shared: true do + before :each do + @exists = tmp('i_exist') + touch(@exists) { |f| f.write 'rubinius' } + end + + after :each do + rm_r @exists + end + + it "returns the size of the file if it exists and is not empty" do + @object.send(@method, @exists).should == 8 + end + + it "accepts a String-like (to_str) parameter" do + obj = mock("file") + obj.should_receive(:to_str).and_return(@exists) + + @object.send(@method, obj).should == 8 + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@exists)).should == 8 + end +end + +describe :file_size_to_io, shared: true do + before :each do + @exists = tmp('i_exist') + touch(@exists) { |f| f.write 'rubinius' } + @file = File.open(@exists, 'r') + end + + after :each do + @file.close unless @file.closed? + rm_r @exists + end + + it "calls #to_io to convert the argument to an IO" do + obj = mock("io like") + obj.should_receive(:to_io).and_return(@file) + + @object.send(@method, obj).should == 8 + end +end + +describe :file_size_raise_when_missing, shared: true do + before :each do + # TODO: missing_file + @missing = tmp("i_dont_exist") + rm_r @missing + end + + after :each do + rm_r @missing + end + + it "raises an error if file_name doesn't exist" do + lambda {@object.send(@method, @missing)}.should raise_error(Errno::ENOENT) + end +end + +describe :file_size_nil_when_missing, shared: true do + before :each do + # TODO: missing_file + @missing = tmp("i_dont_exist") + rm_r @missing + end + + after :each do + rm_r @missing + end + + it "returns nil if file_name doesn't exist or has 0 size" do + @object.send(@method, @missing).should == nil + end +end + +describe :file_size_0_when_empty, shared: true do + before :each do + @empty = tmp("i_am_empty") + touch @empty + end + + after :each do + rm_r @empty + end + + it "returns 0 if the file is empty" do + @object.send(@method, @empty).should == 0 + end +end + +describe :file_size_nil_when_empty, shared: true do + before :each do + @empty = tmp("i_am_empt") + touch @empty + end + + after :each do + rm_r @empty + end + + it "returns nil if file_name is empty" do + @object.send(@method, @empty).should == nil + end +end + +describe :file_size_with_file_argument, shared: true do + before :each do + @exists = tmp('i_exist') + touch(@exists) { |f| f.write 'rubinius' } + end + + after :each do + rm_r @exists + end + + it "accepts a File argument" do + File.open(@exists) do |f| + @object.send(@method, f).should == 8 + end + end +end diff --git a/spec/rubyspec/shared/file/socket.rb b/spec/rubyspec/shared/file/socket.rb new file mode 100644 index 0000000000..55a1cfd284 --- /dev/null +++ b/spec/rubyspec/shared/file/socket.rb @@ -0,0 +1,3 @@ +describe :file_socket, shared: true do + it "accepts an object that has a #to_path method" +end diff --git a/spec/rubyspec/shared/file/sticky.rb b/spec/rubyspec/shared/file/sticky.rb new file mode 100644 index 0000000000..38bb6ed26b --- /dev/null +++ b/spec/rubyspec/shared/file/sticky.rb @@ -0,0 +1,29 @@ +describe :file_sticky, shared: true do + before :each do + @dir = tmp('sticky_dir') + Dir.rmdir(@dir) if File.exist?(@dir) + end + + after :each do + Dir.rmdir(@dir) if File.exist?(@dir) + end + + platform_is_not :windows, :darwin, :freebsd, :netbsd, :openbsd, :solaris, :aix do + it "returns true if the named file has the sticky bit, otherwise false" do + Dir.mkdir @dir, 01755 + + @object.send(@method, @dir).should == true + @object.send(@method, '/').should == false + end + end + + it "accepts an object that has a #to_path method" +end + +describe :file_sticky_missing, shared: true do + platform_is_not :windows do + it "returns false if the file dies not exist" do + @object.send(@method, 'fake_file').should == false + end + end +end diff --git a/spec/rubyspec/shared/file/symlink.rb b/spec/rubyspec/shared/file/symlink.rb new file mode 100644 index 0000000000..d1c1dc94df --- /dev/null +++ b/spec/rubyspec/shared/file/symlink.rb @@ -0,0 +1,46 @@ +describe :file_symlink, shared: true do + before :each do + @file = tmp("test.txt") + @link = tmp("test.lnk") + + rm_r @link + touch @file + end + + after :each do + rm_r @link, @file + end + + platform_is_not :windows do + it "returns true if the file is a link" do + File.symlink(@file, @link) + @object.send(@method, @link).should == true + end + + it "accepts an object that has a #to_path method" do + File.symlink(@file, @link) + @object.send(@method, mock_to_path(@link)).should == true + end + end +end + +describe :file_symlink_nonexistent, shared: true do + before :each do + @file = tmp("test.txt") + @link = tmp("test.lnk") + + rm_r @link + touch @file + end + + after :each do + rm_r @link + rm_r @file + end + + platform_is_not :windows do + it "returns false if the file does not exist" do + @object.send(@method, "non_existent_link").should == false + end + end +end diff --git a/spec/rubyspec/shared/file/world_readable.rb b/spec/rubyspec/shared/file/world_readable.rb new file mode 100644 index 0000000000..0fddf98b73 --- /dev/null +++ b/spec/rubyspec/shared/file/world_readable.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :file_world_readable, shared: true do + + before :each do + @file = tmp('world-readable') + touch @file + end + + after :each do + rm_r @file + end + + platform_is_not :windows do + it "returns nil if the file is chmod 600" do + File.chmod(0600, @file) + @object.world_readable?(@file).should be_nil + end + + it "returns nil if the file is chmod 000" do + File.chmod(0000, @file) + @object.world_readable?(@file).should be_nil + end + + it "returns nil if the file is chmod 700" do + File.chmod(0700, @file) + @object.world_readable?(@file).should be_nil + end + end + + # We don't specify what the Fixnum is because it's system dependent + it "returns a Fixnum if the file is chmod 644" do + File.chmod(0644, @file) + @object.world_readable?(@file).should be_an_instance_of(Fixnum) + end + + it "returns a Fixnum if the file is a directory and chmod 644" do + dir = rand().to_s + '-ww' + Dir.mkdir(dir) + Dir.exist?(dir).should be_true + File.chmod(0644, dir) + @object.world_readable?(dir).should be_an_instance_of(Fixnum) + Dir.rmdir(dir) + end + + it "coerces the argument with #to_path" do + @object.world_readable?(mock_to_path(@file)) + end +end diff --git a/spec/rubyspec/shared/file/world_writable.rb b/spec/rubyspec/shared/file/world_writable.rb new file mode 100644 index 0000000000..43ac23a997 --- /dev/null +++ b/spec/rubyspec/shared/file/world_writable.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :file_world_writable, shared: true do + + before :each do + @file = tmp('world-writable') + touch @file + end + + after :each do + rm_r @file + end + + platform_is_not :windows do + it "returns nil if the file is chmod 600" do + File.chmod(0600, @file) + @object.world_writable?(@file).should be_nil + end + + it "returns nil if the file is chmod 000" do + File.chmod(0000, @file) + @object.world_writable?(@file).should be_nil + end + + it "returns nil if the file is chmod 700" do + File.chmod(0700, @file) + @object.world_writable?(@file).should be_nil + end + + # We don't specify what the Fixnum is because it's system dependent + it "returns a Fixnum if the file is chmod 777" do + File.chmod(0777, @file) + @object.world_writable?(@file).should be_an_instance_of(Fixnum) + end + + it "returns a Fixnum if the file is a directory and chmod 777" do + dir = rand().to_s + '-ww' + Dir.mkdir(dir) + Dir.exist?(dir).should be_true + File.chmod(0777, dir) + @object.world_writable?(dir).should be_an_instance_of(Fixnum) + Dir.rmdir(dir) + end + end + + it "coerces the argument with #to_path" do + @object.world_writable?(mock_to_path(@file)) + end +end diff --git a/spec/rubyspec/shared/file/writable.rb b/spec/rubyspec/shared/file/writable.rb new file mode 100644 index 0000000000..e8296928f3 --- /dev/null +++ b/spec/rubyspec/shared/file/writable.rb @@ -0,0 +1,26 @@ +describe :file_writable, shared: true do + before :each do + @file = tmp('i_exist') + end + + after :each do + rm_r @file + end + + it "returns true if named file is writable by the effective user id of the process, otherwise false" do + platform_is_not :windows do + @object.send(@method, "/etc/passwd").should == false + end + File.open(@file,'w') { @object.send(@method, @file).should == true } + end + + it "accepts an object that has a #to_path method" do + File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true } + end +end + +describe :file_writable_missing, shared: true do + it "returns false if the file does not exist" do + @object.send(@method, 'fake_file').should == false + end +end diff --git a/spec/rubyspec/shared/file/writable_real.rb b/spec/rubyspec/shared/file/writable_real.rb new file mode 100644 index 0000000000..3730befb7a --- /dev/null +++ b/spec/rubyspec/shared/file/writable_real.rb @@ -0,0 +1,33 @@ +describe :file_writable_real, shared: true do + before :each do + @file = tmp('i_exist') + end + + after :each do + rm_r @file + end + + it "returns true if named file is writable by the real user id of the process, otherwise false" do + File.open(@file,'w') { @object.send(@method, @file).should == true } + end + + it "accepts an object that has a #to_path method" do + File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true } + end + + it "raises an ArgumentError if not passed one argument" do + lambda { File.writable_real? }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed a String type" do + lambda { @object.send(@method, 1) }.should raise_error(TypeError) + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + lambda { @object.send(@method, false) }.should raise_error(TypeError) + end +end + +describe :file_writable_real_missing, shared: true do + it "returns false if the file does not exist" do + @object.send(@method, 'fake_file').should == false + end +end diff --git a/spec/rubyspec/shared/file/zero.rb b/spec/rubyspec/shared/file/zero.rb new file mode 100644 index 0000000000..bb5eea57ad --- /dev/null +++ b/spec/rubyspec/shared/file/zero.rb @@ -0,0 +1,76 @@ +describe :file_zero, shared: true do + before :each do + @zero_file = tmp("test.txt") + @nonzero_file = tmp("test2.txt") + @dir = tmp("dir") + + Dir.mkdir @dir + touch @zero_file + touch(@nonzero_file) { |f| f.puts "hello" } + end + + after :each do + rm_r @zero_file, @nonzero_file + rm_r @dir + end + + it "returns true if the file is empty" do + @object.send(@method, @zero_file).should == true + end + + it "returns false if the file is not empty" do + @object.send(@method, @nonzero_file).should == false + end + + it "accepts an object that has a #to_path method" do + @object.send(@method, mock_to_path(@zero_file)).should == true + end + + platform_is :windows do + it "returns true for NUL" do + @object.send(@method, 'NUL').should == true + @object.send(@method, 'nul').should == true + end + end + + platform_is_not :windows do + it "returns true for /dev/null" do + @object.send(@method, File.realpath('/dev/null')).should == true + end + end + + it "raises an ArgumentError if not passed one argument" do + lambda { File.zero? }.should raise_error(ArgumentError) + end + + it "raises a TypeError if not passed a String type" do + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + lambda { @object.send(@method, true) }.should raise_error(TypeError) + lambda { @object.send(@method, false) }.should raise_error(TypeError) + end + + it "returns true inside a block opening a file if it is empty" do + File.open(@zero_file,'w') do + @object.send(@method, @zero_file).should == true + end + end + + platform_is_not :windows do + it "returns false for a directory" do + @object.send(@method, @dir).should == false + end + end + + platform_is :windows do + # see http://redmine.ruby-lang.org/issues/show/449 for background + it "returns true for a directory" do + @object.send(@method, @dir).should == true + end + end +end + +describe :file_zero_missing, shared: true do + it "returns false if the file does not exist" do + @object.send(@method, 'fake_file').should == false + end +end diff --git a/spec/rubyspec/shared/io/putc.rb b/spec/rubyspec/shared/io/putc.rb new file mode 100644 index 0000000000..5f620f183f --- /dev/null +++ b/spec/rubyspec/shared/io/putc.rb @@ -0,0 +1,57 @@ +# -*- encoding: ascii-8bit -*- +describe :io_putc, shared: true do + after :each do + @io.close if @io && !@io.closed? + @io_object = nil + rm_r @name + end + + describe "with a Fixnum argument" do + it "writes one character as a String" do + @io.should_receive(:write).with("A") + @io_object.send(@method, 65).should == 65 + end + + it "writes the low byte as a String" do + @io.should_receive(:write).with("A") + @io_object.send(@method, 0x2441).should == 0x2441 + end + end + + describe "with a String argument" do + it "writes one character" do + @io.should_receive(:write).with("B") + @io_object.send(@method, "B").should == "B" + end + + it "writes the first character" do + @io.should_receive(:write).with("R") + @io_object.send(@method, "Ruby").should == "Ruby" + end + end + + it "calls #to_int to convert an object to an Integer" do + obj = mock("kernel putc") + obj.should_receive(:to_int).and_return(65) + + @io.should_receive(:write).with("A") + @io_object.send(@method, obj).should == obj + end + + it "raises IOError on a closed stream" do + @io.close + lambda { @io_object.send(@method, "a") }.should raise_error(IOError) + end + + it "raises a TypeError when passed nil" do + lambda { @io_object.send(@method, nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed false" do + lambda { @io_object.send(@method, false) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed true" do + lambda { @io_object.send(@method, true) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/shared/kernel/equal.rb b/spec/rubyspec/shared/kernel/equal.rb new file mode 100644 index 0000000000..0a70aec639 --- /dev/null +++ b/spec/rubyspec/shared/kernel/equal.rb @@ -0,0 +1,54 @@ +# These examples hold for BasicObject#equal?, BasicObject#== and Kernel#eql? +describe :object_equal, shared: true do + it "returns true if other is identical to self" do + obj = Object.new + obj.__send__(@method, obj).should be_true + end + + it "returns false if other is not identical to self" do + a = Object.new + b = Object.new + a.__send__(@method, b).should be_false + end + + it "returns true only if self and other are the same object" do + o1 = mock('o1') + o2 = mock('o2') + o1.__send__(@method, o1).should == true + o2.__send__(@method, o2).should == true + o1.__send__(@method, o2).should == false + end + + it "returns true for the same immediate object" do + o1 = 1 + o2 = :hola + 1.__send__(@method, o1).should == true + :hola.__send__(@method, o2).should == true + end + + it "returns false for nil and any other object" do + o1 = mock('o1') + nil.__send__(@method, nil).should == true + o1.__send__(@method, nil).should == false + nil.__send__(@method, o1).should == false + end + + it "returns false for objects of different classes" do + :hola.__send__(@method, 1).should == false + end + + it "returns true only if self and other are the same boolean" do + true.__send__(@method, true).should == true + false.__send__(@method, false).should == true + + true.__send__(@method, false).should == false + false.__send__(@method, true).should == false + end + + it "returns true for integers of initially different ranges" do + big42 = (bignum_value * 42 / bignum_value) + 42.__send__(@method, big42).should == true + long42 = (1 << 35) * 42 / (1 << 35) + 42.__send__(@method, long42).should == true + end +end diff --git a/spec/rubyspec/shared/kernel/object_id.rb b/spec/rubyspec/shared/kernel/object_id.rb new file mode 100644 index 0000000000..7acdb27554 --- /dev/null +++ b/spec/rubyspec/shared/kernel/object_id.rb @@ -0,0 +1,80 @@ +# These examples hold for both BasicObject#__id__ and Kernel#object_id. +describe :object_id, shared: true do + it "returns an integer" do + o1 = @object.new + o1.__send__(@method).should be_kind_of(Integer) + end + + it "returns the same value on all calls to id for a given object" do + o1 = @object.new + o1.__send__(@method).should == o1.__send__(@method) + end + + it "returns different values for different objects" do + o1 = @object.new + o2 = @object.new + o1.__send__(@method).should_not == o2.__send__(@method) + end + + it "returns the same value for two Fixnums with the same value" do + o1 = 1 + o2 = 1 + o1.send(@method).should == o2.send(@method) + end + + it "returns the same value for two Symbol literals" do + o1 = :hello + o2 = :hello + o1.send(@method).should == o2.send(@method) + end + + it "returns the same value for two true literals" do + o1 = true + o2 = true + o1.send(@method).should == o2.send(@method) + end + + it "returns the same value for two false literals" do + o1 = false + o2 = false + o1.send(@method).should == o2.send(@method) + end + + it "returns the same value for two nil literals" do + o1 = nil + o2 = nil + o1.send(@method).should == o2.send(@method) + end + + it "returns a different value for two Bignum literals" do + o1 = 2e100.to_i + o2 = 2e100.to_i + o1.send(@method).should_not == o2.send(@method) + end + + it "returns a different value for two String literals" do + o1 = "hello" + o2 = "hello" + o1.send(@method).should_not == o2.send(@method) + end + + it "returns a different value for an object and its dup" do + o1 = mock("object") + o2 = o1.dup + o1.send(@method).should_not == o2.send(@method) + end + + it "returns a different value for two numbers near the 32 bit Fixnum limit" do + o1 = -1 + o2 = 2 ** 30 - 1 + + o1.send(@method).should_not == o2.send(@method) + end + + it "returns a different value for two numbers near the 64 bit Fixnum limit" do + o1 = -1 + o2 = 2 ** 62 - 1 + + o1.send(@method).should_not == o2.send(@method) + end +end diff --git a/spec/rubyspec/shared/kernel/raise.rb b/spec/rubyspec/shared/kernel/raise.rb new file mode 100644 index 0000000000..70d638fff9 --- /dev/null +++ b/spec/rubyspec/shared/kernel/raise.rb @@ -0,0 +1,68 @@ +describe :kernel_raise, shared: true do + before :each do + ScratchPad.clear + end + + it "aborts execution" do + lambda do + @object.raise Exception, "abort" + ScratchPad.record :no_abort + end.should raise_error(Exception, "abort") + + ScratchPad.recorded.should be_nil + end + + it "raises RuntimeError if no exception class is given" do + lambda { @object.raise }.should raise_error(RuntimeError) + end + + it "raises a given Exception instance" do + error = RuntimeError.new + lambda { @object.raise(error) }.should raise_error(error) + end + + it "raises a RuntimeError if string given" do + lambda { @object.raise("a bad thing") }.should raise_error(RuntimeError) + end + + it "raises a TypeError when passed a non-Exception object" do + lambda { @object.raise(Object.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed true" do + lambda { @object.raise(true) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed false" do + lambda { @object.raise(false) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed nil" do + lambda { @object.raise(nil) }.should raise_error(TypeError) + end + + it "re-raises the rescued exception" do + lambda do + begin + raise Exception, "outer" + ScratchPad.record :no_abort + rescue + begin + raise StandardError, "inner" + rescue + end + + @object.raise + ScratchPad.record :no_reraise + end + end.should raise_error(Exception, "outer") + + ScratchPad.recorded.should be_nil + end + + it "allows Exception, message, and backtrace parameters" do + lambda do + @object.raise(ArgumentError, "message", caller) + end.should raise_error(ArgumentError, "message") + end +end diff --git a/spec/rubyspec/shared/math/atanh.rb b/spec/rubyspec/shared/math/atanh.rb new file mode 100644 index 0000000000..1d1a6ebd74 --- /dev/null +++ b/spec/rubyspec/shared/math/atanh.rb @@ -0,0 +1,44 @@ +describe :math_atanh_base, shared: true do + it "returns a float" do + @object.send(@method, 0.5).should be_an_instance_of(Float) + end + + it "returns the inverse hyperbolic tangent of the argument" do + @object.send(@method, 0.0).should == 0.0 + @object.send(@method, -0.0).should == -0.0 + @object.send(@method, 0.5).should be_close(0.549306144334055, TOLERANCE) + @object.send(@method, -0.2).should be_close(-0.202732554054082, TOLERANCE) + end + + it "raises a TypeError if the argument is nil" do + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if the argument is not a Numeric" do + lambda { @object.send(@method, "test") }.should raise_error(TypeError) + end + + it "returns Infinity if x == 1.0" do + @object.send(@method, 1.0).should == Float::INFINITY + end + + it "return -Infinity if x == -1.0" do + @object.send(@method, -1.0).should == -Float::INFINITY + end +end + +describe :math_atanh_private, shared: true do + it "is a private instance method" do + Math.should have_private_instance_method(@method) + end +end + +describe :math_atanh_no_complex, shared: true do + it "raises a Math::DomainError for arguments greater than 1.0" do + lambda { @object.send(@method, 1.0 + Float::EPSILON) }.should raise_error(Math::DomainError) + end + + it "raises a Math::DomainError for arguments less than -1.0" do + lambda { @object.send(@method, -1.0 - Float::EPSILON) }.should raise_error(Math::DomainError) + end +end diff --git a/spec/rubyspec/shared/process/abort.rb b/spec/rubyspec/shared/process/abort.rb new file mode 100644 index 0000000000..1a25aeffc2 --- /dev/null +++ b/spec/rubyspec/shared/process/abort.rb @@ -0,0 +1,36 @@ +describe :process_abort, shared: true do + before :each do + @stderr, $stderr = $stderr, IOStub.new + end + + after :each do + $stderr = @stderr + end + + it "raises a SystemExit exception" do + lambda { @object.abort }.should raise_error(SystemExit) + end + + it "sets the exception message to the given message" do + lambda { @object.abort "message" }.should raise_error { |e| e.message.should == "message" } + end + + it "sets the exception status code of 1" do + lambda { @object.abort }.should raise_error { |e| e.status.should == 1 } + end + + it "prints the specified message to STDERR" do + lambda { @object.abort "a message" }.should raise_error(SystemExit) + $stderr.should =~ /a message/ + end + + it "coerces the argument with #to_str" do + str = mock('to_str') + str.should_receive(:to_str).any_number_of_times.and_return("message") + lambda { @object.abort str }.should raise_error(SystemExit, "message") + end + + it "raises TypeError when given a non-String object" do + lambda { @object.abort 123 }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/shared/process/exit.rb b/spec/rubyspec/shared/process/exit.rb new file mode 100644 index 0000000000..a5c9eca665 --- /dev/null +++ b/spec/rubyspec/shared/process/exit.rb @@ -0,0 +1,96 @@ +describe :process_exit, shared: true do + it "raises a SystemExit with status 0" do + lambda { @object.exit }.should raise_error(SystemExit) { |e| + e.status.should == 0 + } + end + + it "raises a SystemExit with the specified status" do + [-2**16, -2**8, -8, -1, 0, 1 , 8, 2**8, 2**16].each do |value| + lambda { @object.exit(value) }.should raise_error(SystemExit) { |e| + e.status.should == value + } + end + end + + it "raises a SystemExit with the specified boolean status" do + { true => 0, false => 1 }.each do |value, status| + lambda { @object.exit(value) }.should raise_error(SystemExit) { |e| + e.status.should == status + } + end + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('5') + obj.should_receive(:to_int).and_return(5) + lambda { @object.exit(obj) }.should raise_error(SystemExit) { |e| + e.status.should == 5 + } + end + + it "converts the passed Float argument to an Integer" do + { -2.2 => -2, -0.1 => 0, 5.5 => 5, 827.999 => 827 }.each do |value, status| + lambda { @object.exit(value) }.should raise_error(SystemExit) { |e| + e.status.should == status + } + end + end + + it "raises TypeError if can't convert the argument to an Integer" do + lambda { @object.exit(Object.new) }.should raise_error(TypeError) + lambda { @object.exit('0') }.should raise_error(TypeError) + lambda { @object.exit([0]) }.should raise_error(TypeError) + lambda { @object.exit(nil) }.should raise_error(TypeError) + end + + it "raises the SystemExit in the main thread if it reaches the top-level handler of another thread" do + ScratchPad.record [] + + ready = false + t = Thread.new { + Thread.pass until ready + + begin + @object.exit 42 + rescue SystemExit => e + ScratchPad << :in_thread + raise e + end + } + + begin + ready = true + sleep + rescue SystemExit + ScratchPad << :in_main + end + + ScratchPad.recorded.should == [:in_thread, :in_main] + + # the thread also keeps the exception as its value + lambda { t.value }.should raise_error(SystemExit) + end +end + +describe :process_exit!, shared: true do + with_feature :fork do + it "exits with the given status" do + pid = Process.fork { @object.exit!(1) } + pid, status = Process.waitpid2(pid) + status.exitstatus.should == 1 + end + + it "exits when called from a thread" do + pid = Process.fork do + Thread.new { @object.exit!(1) }.join + + # Do not let the main thread complete + sleep + end + + pid, status = Process.waitpid2(pid) + status.exitstatus.should == 1 + end + end +end diff --git a/spec/rubyspec/shared/process/fork.rb b/spec/rubyspec/shared/process/fork.rb new file mode 100644 index 0000000000..c2c2aee9bf --- /dev/null +++ b/spec/rubyspec/shared/process/fork.rb @@ -0,0 +1,90 @@ +describe :process_fork, shared: true do + platform_is :windows do + it "returns false from #respond_to?" do + # Workaround for Kernel::Method being public and losing the "non-respond_to? magic" + mod = @object.class.name == "KernelSpecs::Method" ? Object.new : @object + mod.respond_to?(:fork).should be_false + mod.respond_to?(:fork, true).should be_false + end + + it "raises a NotImplementedError when called" do + lambda { @object.fork }.should raise_error(NotImplementedError) + end + end + + platform_is_not :windows do + before :each do + @file = tmp('i_exist') + rm_r @file + end + + after :each do + rm_r @file + end + + it "returns status zero" do + pid = Process.fork { exit! 0 } + _, result = Process.wait2(pid) + result.exitstatus.should == 0 + end + + it "returns status zero" do + pid = Process.fork { exit 0 } + _, result = Process.wait2(pid) + result.exitstatus.should == 0 + end + + it "returns status zero" do + pid = Process.fork {} + _, result = Process.wait2(pid) + result.exitstatus.should == 0 + end + + it "returns status non-zero" do + pid = Process.fork { exit! 42 } + _, result = Process.wait2(pid) + result.exitstatus.should == 42 + end + + it "returns status non-zero" do + pid = Process.fork { exit 42 } + _, result = Process.wait2(pid) + result.exitstatus.should == 42 + end + + it "returns nil for the child process" do + child_id = @object.fork + if child_id == nil + touch(@file) { |f| f.write 'rubinius' } + Process.exit! + else + Process.waitpid(child_id) + end + File.exist?(@file).should == true + end + + it "runs a block in a child process" do + pid = @object.fork { + touch(@file) { |f| f.write 'rubinius' } + Process.exit! + } + Process.waitpid(pid) + File.exist?(@file).should == true + end + + it "marks threads from the parent as killed" do + t = Thread.new { sleep } + pid = @object.fork { + touch(@file) do |f| + f.write Thread.current.alive? + f.write t.alive? + end + Process.exit! + } + Process.waitpid(pid) + t.kill + t.join + File.read(@file).should == "truefalse" + end + end +end diff --git a/spec/rubyspec/shared/rational/Rational.rb b/spec/rubyspec/shared/rational/Rational.rb new file mode 100644 index 0000000000..0165fa769a --- /dev/null +++ b/spec/rubyspec/shared/rational/Rational.rb @@ -0,0 +1,103 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/rational', __FILE__) + +describe :kernel_Rational, shared: true do + describe "passed Integer" do + # Guard against the Mathn library + conflicts_with :Prime do + it "returns a new Rational number with 1 as the denominator" do + Rational(1).should eql(Rational(1, 1)) + Rational(-3).should eql(Rational(-3, 1)) + Rational(bignum_value).should eql(Rational(bignum_value, 1)) + end + end + end + + describe "passed two integers" do + it "returns a new Rational number" do + rat = Rational(1, 2) + rat.numerator.should == 1 + rat.denominator.should == 2 + rat.should be_an_instance_of(Rational) + + rat = Rational(-3, -5) + rat.numerator.should == 3 + rat.denominator.should == 5 + rat.should be_an_instance_of(Rational) + + rat = Rational(bignum_value, 3) + rat.numerator.should == bignum_value + rat.denominator.should == 3 + rat.should be_an_instance_of(Rational) + end + + it "reduces the Rational" do + rat = Rational(2, 4) + rat.numerator.should == 1 + rat.denominator.should == 2 + + rat = Rational(3, 9) + rat.numerator.should == 1 + rat.denominator.should == 3 + end + end + + describe "when passed a String" do + it "converts the String to a Rational using the same method as String#to_r" do + r = Rational(13, 25) + s_r = ".52".to_r + r_s = Rational(".52") + + r_s.should == r + r_s.should == s_r + end + + it "scales the Rational value of the first argument by the Rational value of the second" do + Rational(".52", ".6").should == Rational(13, 15) + Rational(".52", "1.6").should == Rational(13, 40) + end + + it "does not use the same method as Float#to_r" do + r = Rational(3, 5) + f_r = 0.6.to_r + r_s = Rational("0.6") + + r_s.should == r + r_s.should_not == f_r + end + + describe "when passed a Numeric" do + it "calls #to_r to convert the first argument to a Rational" do + num = RationalSpecs::SubNumeric.new(2) + + Rational(num).should == Rational(2) + end + end + + describe "when passed a Complex" do + it "returns a Rational from the real part if the imaginary part is 0" do + Rational(Complex(1, 0)).should == Rational(1) + end + + it "raises a RangeError if the imaginary part is not 0" do + lambda { Rational(Complex(1, 2)) }.should raise_error(RangeError) + end + end + + it "raises a TypeError if the first argument is nil" do + lambda { Rational(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if the second argument is nil" do + lambda { Rational(1, nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if the first argument is a Symbol" do + lambda { Rational(:sym) }.should raise_error(TypeError) + end + + it "raises a TypeError if the second argument is a Symbol" do + lambda { Rational(1, :sym) }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/shared/rational/abs.rb b/spec/rubyspec/shared/rational/abs.rb new file mode 100644 index 0000000000..aa1bdc4363 --- /dev/null +++ b/spec/rubyspec/shared/rational/abs.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_abs, shared: true do + it "returns self's absolute value" do + Rational(3, 4).send(@method).should == Rational(3, 4) + Rational(-3, 4).send(@method).should == Rational(3, 4) + Rational(3, -4).send(@method).should == Rational(3, 4) + + Rational(bignum_value, -bignum_value).send(@method).should == Rational(bignum_value, bignum_value) + end +end diff --git a/spec/rubyspec/shared/rational/ceil.rb b/spec/rubyspec/shared/rational/ceil.rb new file mode 100644 index 0000000000..cbb4ed0d08 --- /dev/null +++ b/spec/rubyspec/shared/rational/ceil.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_ceil, shared: true do + before do + @rational = Rational(2200, 7) + end + + describe "with no arguments (precision = 0)" do + it "returns an Integer" do + @rational.ceil.should be_kind_of(Integer) + end + + it "returns the truncated value toward positive infinity" do + @rational.ceil.should == 315 + Rational(1, 2).ceil.should == 1 + Rational(-1, 2).ceil.should == 0 + end + end + + describe "with a precision < 0" do + it "returns an Integer" do + @rational.ceil(-2).should be_kind_of(Integer) + @rational.ceil(-1).should be_kind_of(Integer) + end + + it "moves the truncation point n decimal places left" do + @rational.ceil(-3).should == 1000 + @rational.ceil(-2).should == 400 + @rational.ceil(-1).should == 320 + end + end + + describe "with precision > 0" do + it "returns a Rational" do + @rational.ceil(1).should be_kind_of(Rational) + @rational.ceil(2).should be_kind_of(Rational) + end + + it "moves the truncation point n decimal places right" do + @rational.ceil(1).should == Rational(3143, 10) + @rational.ceil(2).should == Rational(31429, 100) + @rational.ceil(3).should == Rational(157143, 500) + end + end +end diff --git a/spec/rubyspec/shared/rational/coerce.rb b/spec/rubyspec/shared/rational/coerce.rb new file mode 100644 index 0000000000..8bdd19aa2a --- /dev/null +++ b/spec/rubyspec/shared/rational/coerce.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_coerce, shared: true do + it "returns the passed argument, self as Float, when given a Float" do + result = Rational(3, 4).coerce(1.0) + result.should == [1.0, 0.75] + result.first.is_a?(Float).should be_true + result.last.is_a?(Float).should be_true + end + + it "returns the passed argument, self as Rational, when given an Integer" do + result = Rational(3, 4).coerce(10) + result.should == [Rational(10, 1), Rational(3, 4)] + result.first.is_a?(Rational).should be_true + result.last.is_a?(Rational).should be_true + end + + it "returns [argument, self] when given a Rational" do + Rational(3, 7).coerce(Rational(9, 2)).should == [Rational(9, 2), Rational(3, 7)] + end +end diff --git a/spec/rubyspec/shared/rational/comparison.rb b/spec/rubyspec/shared/rational/comparison.rb new file mode 100644 index 0000000000..c52363781f --- /dev/null +++ b/spec/rubyspec/shared/rational/comparison.rb @@ -0,0 +1,85 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_cmp_rat, shared: true do + it "returns 1 when self is greater than the passed argument" do + (Rational(4, 4) <=> Rational(3, 4)).should equal(1) + (Rational(-3, 4) <=> Rational(-4, 4)).should equal(1) + end + + it "returns 0 when self is equal to the passed argument" do + (Rational(4, 4) <=> Rational(4, 4)).should equal(0) + (Rational(-3, 4) <=> Rational(-3, 4)).should equal(0) + end + + it "returns -1 when self is less than the passed argument" do + (Rational(3, 4) <=> Rational(4, 4)).should equal(-1) + (Rational(-4, 4) <=> Rational(-3, 4)).should equal(-1) + end +end + +describe :rational_cmp_int, shared: true do + it "returns 1 when self is greater than the passed argument" do + (Rational(4, 4) <=> 0).should equal(1) + (Rational(4, 4) <=> -10).should equal(1) + (Rational(-3, 4) <=> -1).should equal(1) + end + + it "returns 0 when self is equal to the passed argument" do + (Rational(4, 4) <=> 1).should equal(0) + (Rational(-8, 4) <=> -2).should equal(0) + end + + it "returns -1 when self is less than the passed argument" do + (Rational(3, 4) <=> 1).should equal(-1) + (Rational(-4, 4) <=> 0).should equal(-1) + end +end + +describe :rational_cmp_float, shared: true do + it "returns 1 when self is greater than the passed argument" do + (Rational(4, 4) <=> 0.5).should equal(1) + (Rational(4, 4) <=> -1.5).should equal(1) + (Rational(-3, 4) <=> -0.8).should equal(1) + end + + it "returns 0 when self is equal to the passed argument" do + (Rational(4, 4) <=> 1.0).should equal(0) + (Rational(-6, 4) <=> -1.5).should equal(0) + end + + it "returns -1 when self is less than the passed argument" do + (Rational(3, 4) <=> 1.2).should equal(-1) + (Rational(-4, 4) <=> 0.5).should equal(-1) + end +end + +describe :rational_cmp_coerce, shared: true do + it "calls #coerce on the passed argument with self" do + rational = Rational(3, 4) + + obj = mock("Object") + obj.should_receive(:coerce).with(rational).and_return([1, 2]) + + rational <=> obj + end + + it "calls #<=> on the coerced Rational with the coerced Object" do + rational = Rational(3, 4) + + coerced_rational = mock("Coerced Rational") + coerced_rational.should_receive(:<=>).and_return(:result) + + coerced_obj = mock("Coerced Object") + + obj = mock("Object") + obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj]) + + (rational <=> obj).should == :result + end +end + +describe :rational_cmp_other, shared: true do + it "returns nil" do + (Rational <=> mock("Object")).should be_nil + end +end diff --git a/spec/rubyspec/shared/rational/denominator.rb b/spec/rubyspec/shared/rational/denominator.rb new file mode 100644 index 0000000000..74e464465d --- /dev/null +++ b/spec/rubyspec/shared/rational/denominator.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_denominator, shared: true do + it "returns the denominator" do + Rational(3, 4).denominator.should equal(4) + Rational(3, -4).denominator.should equal(4) + + Rational(1, bignum_value).denominator.should == bignum_value + end + + it "returns 1 if no denominator was given" do + Rational(80).denominator.should == 1 + end +end diff --git a/spec/rubyspec/shared/rational/div.rb b/spec/rubyspec/shared/rational/div.rb new file mode 100644 index 0000000000..23c11f5d12 --- /dev/null +++ b/spec/rubyspec/shared/rational/div.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_div_rat, shared: true do + it "performs integer division and returns the result" do + Rational(2, 3).div(Rational(2, 3)).should == 1 + Rational(-2, 9).div(Rational(-9, 2)).should == 0 + end + + it "raises a ZeroDivisionError when the argument has a numerator of 0" do + lambda { Rational(3, 4).div(Rational(0, 3)) }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the argument has a numerator of 0.0" do + lambda { Rational(3, 4).div(Rational(0.0, 3)) }.should raise_error(ZeroDivisionError) + end +end + +describe :rational_div_float, shared: true do + it "performs integer division and returns the result" do + Rational(2, 3).div(30.333).should == 0 + Rational(2, 9).div(Rational(-8.6)).should == -1 + Rational(3.12).div(0.5).should == 6 + end + + it "raises a ZeroDivisionError when the argument is 0.0" do + lambda { Rational(3, 4).div(0.0) }.should raise_error(ZeroDivisionError) + end +end + +describe :rational_div_int, shared: true do + it "performs integer division and returns the result" do + Rational(2, 1).div(1).should == 2 + Rational(25, 5).div(-50).should == -1 + end + + it "raises a ZeroDivisionError when the argument is 0" do + lambda { Rational(3, 4).div(0) }.should raise_error(ZeroDivisionError) + end +end + +describe :rational_div, shared: true do + it "returns an Integer" do + Rational(229, 21).div(82).should be_kind_of(Integer) + end + + it "raises an ArgumentError if passed more than one argument" do + lambda { Rational(3, 4).div(2,3) }.should raise_error(ArgumentError) + end + + # See http://redmine.ruby-lang.org/issues/show/1648 + it "raises a TypeError if passed a non-numeric argument" do + lambda { Rational(3, 4).div([]) }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/shared/rational/divide.rb b/spec/rubyspec/shared/rational/divide.rb new file mode 100644 index 0000000000..8c5bf666e6 --- /dev/null +++ b/spec/rubyspec/shared/rational/divide.rb @@ -0,0 +1,71 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_divide_rat, shared: true do + it "returns self divided by other as a Rational" do + Rational(3, 4).send(@method, Rational(3, 4)).should eql(Rational(1, 1)) + Rational(2, 4).send(@method, Rational(1, 4)).should eql(Rational(2, 1)) + + Rational(2, 4).send(@method, 2).should == Rational(1, 4) + Rational(6, 7).send(@method, -2).should == Rational(-3, 7) + end + + it "raises a ZeroDivisionError when passed a Rational with a numerator of 0" do + lambda { Rational(3, 4).send(@method, Rational(0, 1)) }.should raise_error(ZeroDivisionError) + end +end + +describe :rational_divide_int, shared: true do + it "returns self divided by other as a Rational" do + Rational(3, 4).send(@method, 2).should eql(Rational(3, 8)) + Rational(2, 4).send(@method, 2).should eql(Rational(1, 4)) + Rational(6, 7).send(@method, -2).should eql(Rational(-3, 7)) + end + + it "raises a ZeroDivisionError when passed 0" do + lambda { Rational(3, 4).send(@method, 0) }.should raise_error(ZeroDivisionError) + end +end + +describe :rational_divide_float, shared: true do + it "returns self divided by other as a Float" do + Rational(3, 4).send(@method, 0.75).should eql(1.0) + Rational(3, 4).send(@method, 0.25).should eql(3.0) + Rational(3, 4).send(@method, 0.3).should eql(2.5) + + Rational(-3, 4).send(@method, 0.3).should eql(-2.5) + Rational(3, -4).send(@method, 0.3).should eql(-2.5) + Rational(3, 4).send(@method, -0.3).should eql(-2.5) + end + + it "returns infinity when passed 0" do + Rational(3, 4).send(@method, 0.0).infinite?.should eql(1) + Rational(-3, -4).send(@method, 0.0).infinite?.should eql(1) + + Rational(-3, 4).send(@method, 0.0).infinite?.should eql(-1) + Rational(3, -4).send(@method, 0.0).infinite?.should eql(-1) + end +end + +describe :rational_divide, shared: true do + it "calls #coerce on the passed argument with self" do + rational = Rational(3, 4) + obj = mock("Object") + obj.should_receive(:coerce).with(rational).and_return([1, 2]) + + rational.send(@method, obj) + end + + it "calls #/ on the coerced Rational with the coerced Object" do + rational = Rational(3, 4) + + coerced_rational = mock("Coerced Rational") + coerced_rational.should_receive(:/).and_return(:result) + + coerced_obj = mock("Coerced Object") + + obj = mock("Object") + obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj]) + + rational.send(@method, obj).should == :result + end +end diff --git a/spec/rubyspec/shared/rational/divmod.rb b/spec/rubyspec/shared/rational/divmod.rb new file mode 100644 index 0000000000..607ae9d693 --- /dev/null +++ b/spec/rubyspec/shared/rational/divmod.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_divmod_rat, shared: true do + it "returns the quotient as Integer and the remainder as Rational" do + Rational(7, 4).divmod(Rational(1, 2)).should eql([3, Rational(1, 4)]) + Rational(7, 4).divmod(Rational(-1, 2)).should eql([-4, Rational(-1, 4)]) + Rational(0, 4).divmod(Rational(4, 3)).should eql([0, Rational(0, 1)]) + + Rational(bignum_value, 4).divmod(Rational(4, 3)).should eql([1729382256910270464, Rational(0, 1)]) + end + + it "raises a ZeroDivisonError when passed a Rational with a numerator of 0" do + lambda { Rational(7, 4).divmod(Rational(0, 3)) }.should raise_error(ZeroDivisionError) + end +end + +describe :rational_divmod_int, shared: true do + it "returns the quotient as Integer and the remainder as Rational" do + Rational(7, 4).divmod(2).should eql([0, Rational(7, 4)]) + Rational(7, 4).divmod(-2).should eql([-1, Rational(-1, 4)]) + + Rational(bignum_value, 4).divmod(3).should == [768614336404564650, Rational(2, 1)] + end + + it "raises a ZeroDivisionError when passed 0" do + lambda { Rational(7, 4).divmod(0) }.should raise_error(ZeroDivisionError) + end +end + +describe :rational_divmod_float, shared: true do + it "returns the quotient as Integer and the remainder as Float" do + Rational(7, 4).divmod(0.5).should eql([3, 0.25]) + end + + it "returns the quotient as Integer and the remainder as Float" do + Rational(7, 4).divmod(-0.5).should eql([-4, -0.25]) + end + + it "raises a ZeroDivisionError when passed 0" do + lambda { Rational(7, 4).divmod(0.0) }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/shared/rational/equal_value.rb b/spec/rubyspec/shared/rational/equal_value.rb new file mode 100644 index 0000000000..be4d5af598 --- /dev/null +++ b/spec/rubyspec/shared/rational/equal_value.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_equal_value_rat, shared: true do + it "returns true if self has the same numerator and denominator as the passed argument" do + (Rational(3, 4) == Rational(3, 4)).should be_true + (Rational(-3, -4) == Rational(3, 4)).should be_true + (Rational(-4, 5) == Rational(4, -5)).should be_true + + (Rational(bignum_value, 3) == Rational(bignum_value, 3)).should be_true + (Rational(-bignum_value, 3) == Rational(bignum_value, -3)).should be_true + end +end + +describe :rational_equal_value_int, shared: true do + it "returns true if self has the passed argument as numerator and a denominator of 1" do + # Rational(x, y) reduces x and y automatically + (Rational(4, 2) == 2).should be_true + (Rational(-4, 2) == -2).should be_true + (Rational(4, -2) == -2).should be_true + end +end + +describe :rational_equal_value_float, shared: true do + it "converts self to a Float and compares it with the passed argument" do + (Rational(3, 4) == 0.75).should be_true + (Rational(4, 2) == 2.0).should be_true + (Rational(-4, 2) == -2.0).should be_true + (Rational(4, -2) == -2.0).should be_true + end +end + +describe :rational_equal_value, shared: true do + it "returns the result of calling #== with self on the passed argument" do + obj = mock("Object") + obj.should_receive(:==).and_return(:result) + + (Rational(3, 4) == obj).should_not be_false + end +end diff --git a/spec/rubyspec/shared/rational/exponent.rb b/spec/rubyspec/shared/rational/exponent.rb new file mode 100644 index 0000000000..7e7c5be7c1 --- /dev/null +++ b/spec/rubyspec/shared/rational/exponent.rb @@ -0,0 +1,176 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_exponent, shared: true do + describe "when passed Rational" do + conflicts_with :Prime do + it "returns Rational(1) if the exponent is Rational(0)" do + (Rational(0) ** Rational(0)).should eql(Rational(1)) + (Rational(1) ** Rational(0)).should eql(Rational(1)) + (Rational(3, 4) ** Rational(0)).should eql(Rational(1)) + (Rational(-1) ** Rational(0)).should eql(Rational(1)) + (Rational(-3, 4) ** Rational(0)).should eql(Rational(1)) + (Rational(bignum_value) ** Rational(0)).should eql(Rational(1)) + (Rational(-bignum_value) ** Rational(0)).should eql(Rational(1)) + end + + it "returns self raised to the argument as a Rational if the exponent's denominator is 1" do + (Rational(3, 4) ** Rational(1, 1)).should eql(Rational(3, 4)) + (Rational(3, 4) ** Rational(2, 1)).should eql(Rational(9, 16)) + (Rational(3, 4) ** Rational(-1, 1)).should eql(Rational(4, 3)) + (Rational(3, 4) ** Rational(-2, 1)).should eql(Rational(16, 9)) + end + + it "returns self raised to the argument as a Float if the exponent's denominator is not 1" do + (Rational(3, 4) ** Rational(4, 3)).should be_close(0.681420222312052, TOLERANCE) + (Rational(3, 4) ** Rational(-4, 3)).should be_close(1.46752322173095, TOLERANCE) + (Rational(3, 4) ** Rational(4, -3)).should be_close(1.46752322173095, TOLERANCE) + end + + it "returns a complex number when self is negative and the passed argument is not 0" do + (Rational(-3, 4) ** Rational(-4, 3)).should be_close(Complex(-0.7337616108654732, 1.2709123906625817), TOLERANCE) + end + end + end + + describe "when passed Integer" do + it "returns the Rational value of self raised to the passed argument" do + (Rational(3, 4) ** 4).should == Rational(81, 256) + (Rational(3, 4) ** -4).should == Rational(256, 81) + (Rational(-3, 4) ** -4).should == Rational(256, 81) + (Rational(3, -4) ** -4).should == Rational(256, 81) + + (Rational(bignum_value, 4) ** 4).should == Rational(28269553036454149273332760011886696253239742350009903329945699220681916416, 1) + (Rational(3, bignum_value) ** -4).should == Rational(7237005577332262213973186563042994240829374041602535252466099000494570602496, 81) + (Rational(-bignum_value, 4) ** -4).should == Rational(1, 28269553036454149273332760011886696253239742350009903329945699220681916416) + (Rational(3, -bignum_value) ** -4).should == Rational(7237005577332262213973186563042994240829374041602535252466099000494570602496, 81) + end + + conflicts_with :Prime do + it "returns Rational(1, 1) when the passed argument is 0" do + (Rational(3, 4) ** 0).should eql(Rational(1, 1)) + (Rational(-3, 4) ** 0).should eql(Rational(1, 1)) + (Rational(3, -4) ** 0).should eql(Rational(1, 1)) + + (Rational(bignum_value, 4) ** 0).should eql(Rational(1, 1)) + (Rational(3, -bignum_value) ** 0).should eql(Rational(1, 1)) + end + end + end + + describe "when passed Bignum" do + # #5713 + it "returns Rational(0) when self is Rational(0) and the exponent is positive" do + (Rational(0) ** bignum_value).should eql(Rational(0)) + end + + it "raises ZeroDivisionError when self is Rational(0) and the exponent is negative" do + lambda { Rational(0) ** -bignum_value }.should raise_error(ZeroDivisionError) + end + + it "returns Rational(1) when self is Rational(1)" do + (Rational(1) ** bignum_value).should eql(Rational(1)) + (Rational(1) ** -bignum_value).should eql(Rational(1)) + end + + it "returns Rational(1) when self is Rational(-1) and the exponent is positive and even" do + (Rational(-1) ** bignum_value(0)).should eql(Rational(1)) + (Rational(-1) ** bignum_value(2)).should eql(Rational(1)) + end + + it "returns Rational(-1) when self is Rational(-1) and the exponent is positive and odd" do + (Rational(-1) ** bignum_value(1)).should eql(Rational(-1)) + (Rational(-1) ** bignum_value(3)).should eql(Rational(-1)) + end + + it "returns positive Infinity when self is > 1" do + (Rational(2) ** bignum_value).infinite?.should == 1 + (Rational(fixnum_max) ** bignum_value).infinite?.should == 1 + end + + it "returns 0.0 when self is > 1 and the exponent is negative" do + (Rational(2) ** -bignum_value).should eql(0.0) + (Rational(fixnum_max) ** -bignum_value).should eql(0.0) + end + + # Fails on linux due to pow() bugs in glibc: http://sources.redhat.com/bugzilla/show_bug.cgi?id=3866 + platform_is_not :linux do + it "returns positive Infinity when self < -1" do + (Rational(-2) ** bignum_value).infinite?.should == 1 + (Rational(-2) ** (bignum_value + 1)).infinite?.should == 1 + (Rational(fixnum_min) ** bignum_value).infinite?.should == 1 + end + + it "returns 0.0 when self is < -1 and the exponent is negative" do + (Rational(-2) ** -bignum_value).should eql(0.0) + (Rational(fixnum_min) ** -bignum_value).should eql(0.0) + end + end + end + + describe "when passed Float" do + it "returns self converted to Float and raised to the passed argument" do + (Rational(3, 1) ** 3.0).should eql(27.0) + (Rational(3, 1) ** 1.5).should be_close(5.19615242270663, TOLERANCE) + (Rational(3, 1) ** -1.5).should be_close(0.192450089729875, TOLERANCE) + end + + it "returns a complex number if self is negative and the passed argument is not 0" do + (Rational(-3, 2) ** 1.5).should be_close(Complex(-3.374618290464398e-16, -1.8371173070873836), TOLERANCE) + (Rational(3, -2) ** 1.5).should be_close(Complex(-3.374618290464398e-16, -1.8371173070873836), TOLERANCE) + (Rational(3, -2) ** -1.5).should be_close(Complex(-9.998869008783402e-17, 0.5443310539518174), TOLERANCE) + end + + it "returns Complex(1.0) when the passed argument is 0.0" do + (Rational(3, 4) ** 0.0).should == Complex(1.0) + (Rational(-3, 4) ** 0.0).should == Complex(1.0) + (Rational(-3, 4) ** 0.0).should == Complex(1.0) + end + end + + it "calls #coerce on the passed argument with self" do + rational = Rational(3, 4) + obj = mock("Object") + obj.should_receive(:coerce).with(rational).and_return([1, 2]) + + rational ** obj + end + + it "calls #** on the coerced Rational with the coerced Object" do + rational = Rational(3, 4) + + coerced_rational = mock("Coerced Rational") + coerced_rational.should_receive(:**).and_return(:result) + + coerced_obj = mock("Coerced Object") + + obj = mock("Object") + obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj]) + + (rational ** obj).should == :result + end + + it "raises ZeroDivisionError for Rational(0, 1) passed a negative Integer" do + [-1, -4, -9999].each do |exponent| + lambda { Rational(0, 1) ** exponent }.should raise_error(ZeroDivisionError, "divided by 0") + end + end + + it "raises ZeroDivisionError for Rational(0, 1) passed a negative Rational with denominator 1" do + [Rational(-1, 1), Rational(-3, 1)].each do |exponent| + lambda { Rational(0, 1) ** exponent }.should raise_error(ZeroDivisionError, "divided by 0") + end + end + + # #7513 + it "raises ZeroDivisionError for Rational(0, 1) passed a negative Rational" do + lambda { Rational(0, 1) ** Rational(-3, 2) }.should raise_error(ZeroDivisionError, "divided by 0") + end + + platform_is_not :solaris do # See https://github.com/ruby/spec/issues/134 + it "returns Infinity for Rational(0, 1) passed a negative Float" do + [-1.0, -3.0, -3.14].each do |exponent| + (Rational(0, 1) ** exponent).infinite?.should == 1 + end + end + end +end diff --git a/spec/rubyspec/shared/rational/fdiv.rb b/spec/rubyspec/shared/rational/fdiv.rb new file mode 100644 index 0000000000..22ca4b4ddc --- /dev/null +++ b/spec/rubyspec/shared/rational/fdiv.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_fdiv, shared: true do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/shared/rational/floor.rb b/spec/rubyspec/shared/rational/floor.rb new file mode 100644 index 0000000000..5106e0b4d9 --- /dev/null +++ b/spec/rubyspec/shared/rational/floor.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_floor, shared: true do + before do + @rational = Rational(2200, 7) + end + + describe "with no arguments (precision = 0)" do + it "returns an integer" do + @rational.floor.should be_kind_of(Integer) + end + + it "returns the truncated value toward negative infinity" do + @rational.floor.should == 314 + Rational(1, 2).floor.should == 0 + Rational(-1, 2).floor.should == -1 + end + end + + describe "with a precision < 0" do + it "returns an integer" do + @rational.floor(-2).should be_kind_of(Integer) + @rational.floor(-1).should be_kind_of(Integer) + end + + it "moves the truncation point n decimal places left" do + @rational.floor(-3).should == 0 + @rational.floor(-2).should == 300 + @rational.floor(-1).should == 310 + end + end + + describe "with a precision > 0" do + it "returns a Rational" do + @rational.floor(1).should be_kind_of(Rational) + @rational.floor(2).should be_kind_of(Rational) + end + + it "moves the truncation point n decimal places right" do + @rational.floor(1).should == Rational(1571, 5) + @rational.floor(2).should == Rational(7857, 25) + @rational.floor(3).should == Rational(62857, 200) + end + end +end diff --git a/spec/rubyspec/shared/rational/hash.rb b/spec/rubyspec/shared/rational/hash.rb new file mode 100644 index 0000000000..a83ce49afb --- /dev/null +++ b/spec/rubyspec/shared/rational/hash.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_hash, shared: true do + # BUG: Rational(2, 3).hash == Rational(3, 2).hash + it "is static" do + Rational(2, 3).hash.should == Rational(2, 3).hash + Rational(2, 4).hash.should_not == Rational(2, 3).hash + end +end diff --git a/spec/rubyspec/shared/rational/inspect.rb b/spec/rubyspec/shared/rational/inspect.rb new file mode 100644 index 0000000000..a641db8eb8 --- /dev/null +++ b/spec/rubyspec/shared/rational/inspect.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_inspect, shared: true do + conflicts_with :Prime do + it "returns a string representation of self" do + Rational(3, 4).inspect.should == "(3/4)" + Rational(-5, 8).inspect.should == "(-5/8)" + Rational(-1, -2).inspect.should == "(1/2)" + Rational(bignum_value, 1).inspect.should == "(#{bignum_value}/1)" + end + end +end diff --git a/spec/rubyspec/shared/rational/marshal_dump.rb b/spec/rubyspec/shared/rational/marshal_dump.rb new file mode 100644 index 0000000000..673c6c8327 --- /dev/null +++ b/spec/rubyspec/shared/rational/marshal_dump.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_marshal_dump, shared: true do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/shared/rational/marshal_load.rb b/spec/rubyspec/shared/rational/marshal_load.rb new file mode 100644 index 0000000000..7cf32ad1db --- /dev/null +++ b/spec/rubyspec/shared/rational/marshal_load.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_marshal_load, shared: true do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/shared/rational/minus.rb b/spec/rubyspec/shared/rational/minus.rb new file mode 100644 index 0000000000..01c743be72 --- /dev/null +++ b/spec/rubyspec/shared/rational/minus.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_minus_rat, shared: true do + it "returns the result of substracting other from self as a Rational" do + (Rational(3, 4) - Rational(0, 1)).should eql(Rational(3, 4)) + (Rational(3, 4) - Rational(1, 4)).should eql(Rational(1, 2)) + + (Rational(3, 4) - Rational(2, 1)).should eql(Rational(-5, 4)) + end +end + +describe :rational_minus_int, shared: true do + it "returns the result of substracting other from self as a Rational" do + (Rational(3, 4) - 1).should eql(Rational(-1, 4)) + (Rational(3, 4) - 2).should eql(Rational(-5, 4)) + end +end + +describe :rational_minus_float, shared: true do + it "returns the result of substracting other from self as a Float" do + (Rational(3, 4) - 0.2).should eql(0.55) + (Rational(3, 4) - 2.5).should eql(-1.75) + end +end + +describe :rational_minus, shared: true do + it "calls #coerce on the passed argument with self" do + rational = Rational(3, 4) + obj = mock("Object") + obj.should_receive(:coerce).with(rational).and_return([1, 2]) + + rational - obj + end + + it "calls #- on the coerced Rational with the coerced Object" do + rational = Rational(3, 4) + + coerced_rational = mock("Coerced Rational") + coerced_rational.should_receive(:-).and_return(:result) + + coerced_obj = mock("Coerced Object") + + obj = mock("Object") + obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj]) + + (rational - obj).should == :result + end +end diff --git a/spec/rubyspec/shared/rational/modulo.rb b/spec/rubyspec/shared/rational/modulo.rb new file mode 100644 index 0000000000..ca69650f20 --- /dev/null +++ b/spec/rubyspec/shared/rational/modulo.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_modulo, shared: true do + it "returns the remainder when this value is divided by other" do + Rational(2, 3).send(@method, Rational(2, 3)).should == Rational(0, 1) + Rational(4, 3).send(@method, Rational(2, 3)).should == Rational(0, 1) + Rational(2, -3).send(@method, Rational(-2, 3)).should == Rational(0, 1) + Rational(0, -1).send(@method, -1).should == Rational(0, 1) + + Rational(7, 4).send(@method, Rational(1, 2)).should == Rational(1, 4) + Rational(7, 4).send(@method, 1).should == Rational(3, 4) + Rational(7, 4).send(@method, Rational(1, 7)).should == Rational(1, 28) + + Rational(3, 4).send(@method, -1).should == Rational(-1, 4) + Rational(1, -5).send(@method, -1).should == Rational(-1, 5) + end + + it "returns a Float value when the argument is Float" do + Rational(7, 4).send(@method, 1.0).should be_kind_of(Float) + Rational(7, 4).send(@method, 1.0).should == 0.75 + Rational(7, 4).send(@method, 0.26).should be_close(0.19, 0.0001) + end + + it "raises ZeroDivisionError on zero denominator" do + lambda { + Rational(3, 5).send(@method, Rational(0, 1)) + }.should raise_error(ZeroDivisionError) + + lambda { + Rational(0, 1).send(@method, Rational(0, 1)) + }.should raise_error(ZeroDivisionError) + + lambda { + Rational(3, 5).send(@method, 0) + }.should raise_error(ZeroDivisionError) + end + + it "raises a ZeroDivisionError when the argument is 0.0" do + lambda { + Rational(3, 5).send(@method, 0.0) + }.should raise_error(ZeroDivisionError) + end +end diff --git a/spec/rubyspec/shared/rational/multiply.rb b/spec/rubyspec/shared/rational/multiply.rb new file mode 100644 index 0000000000..05a9cfc5c8 --- /dev/null +++ b/spec/rubyspec/shared/rational/multiply.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_multiply_rat, shared: true do + it "returns self divided by other as a Rational" do + (Rational(3, 4) * Rational(3, 4)).should eql(Rational(9, 16)) + (Rational(2, 4) * Rational(1, 4)).should eql(Rational(1, 8)) + + (Rational(3, 4) * Rational(0, 1)).should eql(Rational(0, 4)) + end +end + +describe :rational_multiply_int, shared: true do + it "returns self divided by other as a Rational" do + (Rational(3, 4) * 2).should eql(Rational(3, 2)) + (Rational(2, 4) * 2).should eql(Rational(1, 1)) + (Rational(6, 7) * -2).should eql(Rational(-12, 7)) + + (Rational(3, 4) * 0).should eql(Rational(0, 4)) + end +end + +describe :rational_multiply_float, shared: true do + it "returns self divided by other as a Float" do + (Rational(3, 4) * 0.75).should eql(0.5625) + (Rational(3, 4) * 0.25).should eql(0.1875) + (Rational(3, 4) * 0.3).should be_close(0.225, TOLERANCE) + + (Rational(-3, 4) * 0.3).should be_close(-0.225, TOLERANCE) + (Rational(3, -4) * 0.3).should be_close(-0.225, TOLERANCE) + (Rational(3, 4) * -0.3).should be_close(-0.225, TOLERANCE) + + (Rational(3, 4) * 0.0).should eql(0.0) + (Rational(-3, -4) * 0.0).should eql(0.0) + + (Rational(-3, 4) * 0.0).should eql(0.0) + (Rational(3, -4) * 0.0).should eql(0.0) + end +end + +describe :rational_multiply, shared: true do + it "calls #coerce on the passed argument with self" do + rational = Rational(3, 4) + obj = mock("Object") + obj.should_receive(:coerce).with(rational).and_return([1, 2]) + + rational * obj + end + + it "calls #* on the coerced Rational with the coerced Object" do + rational = Rational(3, 4) + + coerced_rational = mock("Coerced Rational") + coerced_rational.should_receive(:*).and_return(:result) + + coerced_obj = mock("Coerced Object") + + obj = mock("Object") + obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj]) + + (rational * obj).should == :result + end +end diff --git a/spec/rubyspec/shared/rational/numerator.rb b/spec/rubyspec/shared/rational/numerator.rb new file mode 100644 index 0000000000..e4868ef43c --- /dev/null +++ b/spec/rubyspec/shared/rational/numerator.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_numerator, shared: true do + it "returns the numerator" do + Rational(3, 4).numerator.should equal(3) + Rational(3, -4).numerator.should equal(-3) + + Rational(bignum_value, 1).numerator.should == bignum_value + end +end diff --git a/spec/rubyspec/shared/rational/plus.rb b/spec/rubyspec/shared/rational/plus.rb new file mode 100644 index 0000000000..d078a41425 --- /dev/null +++ b/spec/rubyspec/shared/rational/plus.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_plus_rat, shared: true do + it "returns the result of substracting other from self as a Rational" do + (Rational(3, 4) + Rational(0, 1)).should eql(Rational(3, 4)) + (Rational(3, 4) + Rational(1, 4)).should eql(Rational(1, 1)) + + (Rational(3, 4) + Rational(2, 1)).should eql(Rational(11, 4)) + end +end + +describe :rational_plus_int, shared: true do + it "returns the result of substracting other from self as a Rational" do + (Rational(3, 4) + 1).should eql(Rational(7, 4)) + (Rational(3, 4) + 2).should eql(Rational(11, 4)) + end +end + +describe :rational_plus_float, shared: true do + it "returns the result of substracting other from self as a Float" do + (Rational(3, 4) + 0.2).should eql(0.95) + (Rational(3, 4) + 2.5).should eql(3.25) + end +end + +describe :rational_plus, shared: true do + it "calls #coerce on the passed argument with self" do + rational = Rational(3, 4) + obj = mock("Object") + obj.should_receive(:coerce).with(rational).and_return([1, 2]) + + rational + obj + end + + it "calls #+ on the coerced Rational with the coerced Object" do + rational = Rational(3, 4) + + coerced_rational = mock("Coerced Rational") + coerced_rational.should_receive(:+).and_return(:result) + + coerced_obj = mock("Coerced Object") + + obj = mock("Object") + obj.should_receive(:coerce).and_return([coerced_rational, coerced_obj]) + + (rational + obj).should == :result + end +end diff --git a/spec/rubyspec/shared/rational/quo.rb b/spec/rubyspec/shared/rational/quo.rb new file mode 100644 index 0000000000..61bd3fad9d --- /dev/null +++ b/spec/rubyspec/shared/rational/quo.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_quo, shared: true do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/shared/rational/remainder.rb b/spec/rubyspec/shared/rational/remainder.rb new file mode 100644 index 0000000000..64aeb55e6c --- /dev/null +++ b/spec/rubyspec/shared/rational/remainder.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_remainder, shared: true do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/rubyspec/shared/rational/round.rb b/spec/rubyspec/shared/rational/round.rb new file mode 100644 index 0000000000..8874daceea --- /dev/null +++ b/spec/rubyspec/shared/rational/round.rb @@ -0,0 +1,71 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_round, shared: true do + before do + @rational = Rational(2200, 7) + end + + describe "with no arguments (precision = 0)" do + it "returns an integer" do + @rational.round.should be_kind_of(Integer) + Rational(0, 1).round(0).should be_kind_of(Integer) + Rational(124, 1).round(0).should be_kind_of(Integer) + end + + it "returns the truncated value toward the nearest integer" do + @rational.round.should == 314 + Rational(0, 1).round(0).should == 0 + Rational(2, 1).round(0).should == 2 + end + + it "returns the rounded value toward the nearest integer" do + Rational(1, 2).round.should == 1 + Rational(-1, 2).round.should == -1 + Rational(3, 2).round.should == 2 + Rational(-3, 2).round.should == -2 + Rational(5, 2).round.should == 3 + Rational(-5, 2).round.should == -3 + end + end + + describe "with a precision < 0" do + it "returns an integer" do + @rational.round(-2).should be_kind_of(Integer) + @rational.round(-1).should be_kind_of(Integer) + Rational(0, 1).round(-1).should be_kind_of(Integer) + Rational(2, 1).round(-1).should be_kind_of(Integer) + end + + it "moves the truncation point n decimal places left" do + @rational.round(-3).should == 0 + @rational.round(-2).should == 300 + @rational.round(-1).should == 310 + end + end + + describe "with a precision > 0" do + it "returns a Rational" do + @rational.round(1).should be_kind_of(Rational) + @rational.round(2).should be_kind_of(Rational) + Rational(0, 1).round(1).should be_kind_of(Rational) + Rational(2, 1).round(1).should be_kind_of(Rational) + end + + it "moves the truncation point n decimal places right" do + @rational.round(1).should == Rational(3143, 10) + @rational.round(2).should == Rational(31429, 100) + @rational.round(3).should == Rational(157143, 500) + Rational(0, 1).round(1).should == Rational(0, 1) + Rational(2, 1).round(1).should == Rational(2, 1) + end + + it "doesn't alter the value if the precision is too great" do + Rational(3, 2).round(10).should == Rational(3, 2).round(20) + end + + # #6605 + it "doesn't fail when rounding to an absurdly large positive precision" do + Rational(3, 2).round(2_097_171).should == Rational(3, 2) + end + end +end diff --git a/spec/rubyspec/shared/rational/to_f.rb b/spec/rubyspec/shared/rational/to_f.rb new file mode 100644 index 0000000000..2c9afdddda --- /dev/null +++ b/spec/rubyspec/shared/rational/to_f.rb @@ -0,0 +1,10 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_to_f, shared: true do + it "returns self converted to a Float" do + Rational(3, 4).to_f.should eql(0.75) + Rational(3, -4).to_f.should eql(-0.75) + Rational(-1, 4).to_f.should eql(-0.25) + Rational(-1, -4).to_f.should eql(0.25) + end +end diff --git a/spec/rubyspec/shared/rational/to_i.rb b/spec/rubyspec/shared/rational/to_i.rb new file mode 100644 index 0000000000..b0db78b3a8 --- /dev/null +++ b/spec/rubyspec/shared/rational/to_i.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_to_i, shared: true do + it "converts self to an Integer by truncation" do + Rational(7, 4).to_i.should eql(1) + Rational(11, 4).to_i.should eql(2) + end + + it "converts self to an Integer by truncation" do + Rational(-7, 4).to_i.should eql(-1) + end +end diff --git a/spec/rubyspec/shared/rational/to_r.rb b/spec/rubyspec/shared/rational/to_r.rb new file mode 100644 index 0000000000..646167614a --- /dev/null +++ b/spec/rubyspec/shared/rational/to_r.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_to_r, shared: true do + conflicts_with :Prime do + it "returns self" do + a = Rational(3, 4) + a.to_r.should equal(a) + + a = Rational(bignum_value, 4) + a.to_r.should equal(a) + end + end +end diff --git a/spec/rubyspec/shared/rational/to_s.rb b/spec/rubyspec/shared/rational/to_s.rb new file mode 100644 index 0000000000..429a021908 --- /dev/null +++ b/spec/rubyspec/shared/rational/to_s.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_to_s, shared: true do + it "returns a string representation of self" do + Rational(1, 1).to_s.should == "1/1" + Rational(2, 1).to_s.should == "2/1" + Rational(1, 2).to_s.should == "1/2" + Rational(-1, 3).to_s.should == "-1/3" + Rational(1, -3).to_s.should == "-1/3" + end +end diff --git a/spec/rubyspec/shared/rational/truncate.rb b/spec/rubyspec/shared/rational/truncate.rb new file mode 100644 index 0000000000..993c654c45 --- /dev/null +++ b/spec/rubyspec/shared/rational/truncate.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe :rational_truncate, shared: true do + before do + @rational = Rational(2200, 7) + end + + describe "with no arguments (precision = 0)" do + it "returns an integer" do + @rational.truncate.should be_kind_of(Integer) + end + + it "returns the truncated value toward 0" do + @rational.truncate.should == 314 + Rational(1, 2).truncate.should == 0 + Rational(-1, 2).truncate.should == 0 + end + end + + describe "with a precision < 0" do + it "returns an integer" do + @rational.truncate(-2).should be_kind_of(Integer) + @rational.truncate(-1).should be_kind_of(Integer) + end + + it "moves the truncation point n decimal places left" do + @rational.truncate(-3).should == 0 + @rational.truncate(-2).should == 300 + @rational.truncate(-1).should == 310 + end + end + + describe "with a precision > 0" do + it "returns a Rational" do + @rational.truncate(1).should be_kind_of(Rational) + @rational.truncate(2).should be_kind_of(Rational) + end + + it "moves the truncation point n decimal places right" do + @rational.truncate(1).should == Rational(1571, 5) + @rational.truncate(2).should == Rational(7857, 25) + @rational.truncate(3).should == Rational(62857, 200) + end + end +end diff --git a/spec/rubyspec/shared/string/times.rb b/spec/rubyspec/shared/string/times.rb new file mode 100644 index 0000000000..c44a76c9b7 --- /dev/null +++ b/spec/rubyspec/shared/string/times.rb @@ -0,0 +1,64 @@ +describe :string_times, shared: true do + class MyString < String; end + + it "returns a new string containing count copies of self" do + @object.call("cool", 0).should == "" + @object.call("cool", 1).should == "cool" + @object.call("cool", 3).should == "coolcoolcool" + end + + it "tries to convert the given argument to an integer using to_int" do + @object.call("cool", 3.1).should == "coolcoolcool" + @object.call("a", 3.999).should == "aaa" + + a = mock('4') + a.should_receive(:to_int).and_return(4) + + @object.call("a", a).should == "aaaa" + end + + it "raises an ArgumentError when given integer is negative" do + lambda { @object.call("cool", -3) }.should raise_error(ArgumentError) + lambda { @object.call("cool", -3.14) }.should raise_error(ArgumentError) + end + + it "raises a RangeError when given integer is a Bignum" do + lambda { @object.call("cool", 999999999999999999999) }.should raise_error(RangeError) + end + + it "returns subclass instances" do + @object.call(MyString.new("cool"), 0).should be_an_instance_of(MyString) + @object.call(MyString.new("cool"), 1).should be_an_instance_of(MyString) + @object.call(MyString.new("cool"), 2).should be_an_instance_of(MyString) + end + + it "always taints the result when self is tainted" do + ["", "OK", MyString.new(""), MyString.new("OK")].each do |str| + str.taint + + [0, 1, 2].each do |arg| + @object.call(str, arg).tainted?.should == true + end + end + end + + with_feature :encoding do + it "returns a String in the same encoding as self" do + str = "\xE3\x81\x82".force_encoding Encoding::UTF_8 + result = @object.call(str, 2) + result.encoding.should equal(Encoding::UTF_8) + end + end + + platform_is wordsize: 32 do + it "raises an ArgumentError if the length of the resulting string doesn't fit into a long" do + lambda { @object.call("abc", (2 ** 31) - 1) }.should raise_error(ArgumentError) + end + end + + platform_is wordsize: 64 do + it "raises an ArgumentError if the length of the resulting string doesn't fit into a long" do + lambda { @object.call("abc", (2 ** 63) - 1) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/shared/time/strftime_for_date.rb b/spec/rubyspec/shared/time/strftime_for_date.rb new file mode 100644 index 0000000000..8423a59774 --- /dev/null +++ b/spec/rubyspec/shared/time/strftime_for_date.rb @@ -0,0 +1,279 @@ +# Shared for Time, Date and DateTime, testing only date components (smallest unit is day) + +describe :strftime_date, shared: true do + before :all do + @d200_4_6 = @new_date[200, 4, 6] + @d2000_4_6 = @new_date[2000, 4, 6] + @d2000_4_9 = @new_date[2000, 4, 9] + @d2000_4_10 = @new_date[2000, 4, 10] + @d2009_9_18 = @new_date[2009, 9, 18] + end + + # Per conversion specifier, not combining + it "should be able to print the full day name" do + @d2000_4_6.strftime("%A").should == "Thursday" + @d2009_9_18.strftime('%A').should == 'Friday' + end + + it "should be able to print the short day name" do + @d2000_4_6.strftime("%a").should == "Thu" + @d2009_9_18.strftime('%a').should == 'Fri' + end + + it "should be able to print the full month name" do + @d2000_4_6.strftime("%B").should == "April" + @d2009_9_18.strftime('%B').should == 'September' + end + + it "should be able to print the short month name" do + @d2000_4_6.strftime("%b").should == "Apr" + @d2000_4_6.strftime("%h").should == "Apr" + @d2000_4_6.strftime("%b").should == @d2000_4_6.strftime("%h") + @d2009_9_18.strftime('%b').should == 'Sep' + end + + it "should be able to print the century" do + @d2000_4_6.strftime("%C").should == "20" + end + + it "should be able to print the month day with leading zeroes" do + @d2000_4_6.strftime("%d").should == "06" + @d2009_9_18.strftime('%d').should == '18' + end + + it "should be able to print the month day with leading spaces" do + @d2000_4_6.strftime("%e").should == " 6" + end + + it "should be able to print the commercial year with leading zeroes" do + @d2000_4_6.strftime("%G").should == "2000" + @d200_4_6.strftime("%G").should == "0200" + end + + it "should be able to print the commercial year with only two digits" do + @d2000_4_6.strftime("%g").should == "00" + @d200_4_6.strftime("%g").should == "00" + end + + it "should be able to print the hour with leading zeroes (hour is always 00)" do + @d2000_4_6.strftime("%H").should == "00" + end + + it "should be able to print the hour in 12 hour notation with leading zeroes" do + @d2000_4_6.strftime("%I").should == "12" + end + + it "should be able to print the julian day with leading zeroes" do + @d2000_4_6.strftime("%j").should == "097" + @d2009_9_18.strftime('%j').should == '261' + end + + it "should be able to print the hour in 24 hour notation with leading spaces" do + @d2000_4_6.strftime("%k").should == " 0" + end + + it "should be able to print the hour in 12 hour notation with leading spaces" do + @d2000_4_6.strftime("%l").should == "12" + end + + it "should be able to print the minutes with leading zeroes" do + @d2000_4_6.strftime("%M").should == "00" + end + + it "should be able to print the month with leading zeroes" do + @d2000_4_6.strftime("%m").should == "04" + @d2009_9_18.strftime('%m').should == '09' + end + + it "should be able to add a newline" do + @d2000_4_6.strftime("%n").should == "\n" + end + + it "should be able to show AM/PM" do + @d2000_4_6.strftime("%P").should == "am" + end + + it "should be able to show am/pm" do + @d2000_4_6.strftime("%p").should == "AM" + end + + it "should be able to show the number of seconds with leading zeroes" do + @d2000_4_6.strftime("%S").should == "00" + end + + it "should be able to show the number of seconds with leading zeroes" do + @d2000_4_6.strftime("%S").should == "00" + end + + it "should be able to show the number of seconds since the unix epoch" do + @d2000_4_6.strftime("%s").should == "954979200" + end + + it "should be able to add a tab" do + @d2000_4_6.strftime("%t").should == "\t" + end + + it "should be able to show the week number with the week starting on Sunday (%U) and Monday (%W)" do + @d2000_4_6.strftime("%U").should == "14" # Thursday + @d2000_4_6.strftime("%W").should == "14" + + @d2000_4_9.strftime("%U").should == "15" # Sunday + @d2000_4_9.strftime("%W").should == "14" + + @d2000_4_10.strftime("%U").should == "15" # Monday + @d2000_4_10.strftime("%W").should == "15" + + # start of the year + saturday_first = @new_date[2000,1,1] + saturday_first.strftime("%U").should == "00" + saturday_first.strftime("%W").should == "00" + + sunday_second = @new_date[2000,1,2] + sunday_second.strftime("%U").should == "01" + sunday_second.strftime("%W").should == "00" + + monday_third = @new_date[2000,1,3] + monday_third.strftime("%U").should == "01" + monday_third.strftime("%W").should == "01" + + sunday_9th = @new_date[2000,1,9] + sunday_9th.strftime("%U").should == "02" + sunday_9th.strftime("%W").should == "01" + + monday_10th = @new_date[2000,1,10] + monday_10th.strftime("%U").should == "02" + monday_10th.strftime("%W").should == "02" + + # middle of the year + some_sunday = @new_date[2000,8,6] + some_sunday.strftime("%U").should == "32" + some_sunday.strftime("%W").should == "31" + some_monday = @new_date[2000,8,7] + some_monday.strftime("%U").should == "32" + some_monday.strftime("%W").should == "32" + + # end of year, and start of next one + saturday_30th = @new_date[2000,12,30] + saturday_30th.strftime("%U").should == "52" + saturday_30th.strftime("%W").should == "52" + + sunday_last = @new_date[2000,12,31] + sunday_last.strftime("%U").should == "53" + sunday_last.strftime("%W").should == "52" + + monday_first = @new_date[2001,1,1] + monday_first.strftime("%U").should == "00" + monday_first.strftime("%W").should == "01" + end + + it "should be able to show the commercial week day" do + @d2000_4_9.strftime("%u").should == "7" + @d2000_4_10.strftime("%u").should == "1" + end + + it "should be able to show the commercial week" do + @d2000_4_9.strftime("%V").should == "14" + @d2000_4_10.strftime("%V").should == "15" + end + + # %W: see %U + + it "should be able to show the week day" do + @d2000_4_9.strftime("%w").should == "0" + @d2000_4_10.strftime("%w").should == "1" + @d2009_9_18.strftime('%w').should == '5' + end + + it "should be able to show the year in YYYY format" do + @d2000_4_9.strftime("%Y").should == "2000" + @d2009_9_18.strftime('%Y').should == '2009' + end + + it "should be able to show the year in YY format" do + @d2000_4_9.strftime("%y").should == "00" + @d2009_9_18.strftime('%y').should == '09' + end + + it "should be able to show the timezone of the date with a : separator" do + @d2000_4_9.strftime("%z").should == "+0000" + end + + it "should be able to escape the % character" do + @d2000_4_9.strftime("%%").should == "%" + end + + # Combining conversion specifiers + it "should be able to print the date in full" do + @d2000_4_6.strftime("%c").should == "Thu Apr 6 00:00:00 2000" + @d2000_4_6.strftime("%c").should == @d2000_4_6.strftime('%a %b %e %H:%M:%S %Y') + end + + it "should be able to print the date with slashes" do + @d2000_4_6.strftime("%D").should == "04/06/00" + @d2000_4_6.strftime("%D").should == @d2000_4_6.strftime('%m/%d/%y') + end + + it "should be able to print the date as YYYY-MM-DD" do + @d2000_4_6.strftime("%F").should == "2000-04-06" + @d2000_4_6.strftime("%F").should == @d2000_4_6.strftime('%Y-%m-%d') + end + + it "should be able to show HH:MM" do + @d2000_4_6.strftime("%R").should == "00:00" + @d2000_4_6.strftime("%R").should == @d2000_4_6.strftime('%H:%M') + end + + it "should be able to show HH:MM:SS AM/PM" do + @d2000_4_6.strftime("%r").should == "12:00:00 AM" + @d2000_4_6.strftime("%r").should == @d2000_4_6.strftime('%I:%M:%S %p') + end + + it "should be able to show HH:MM:SS" do + @d2000_4_6.strftime("%T").should == "00:00:00" + @d2000_4_6.strftime("%T").should == @d2000_4_6.strftime('%H:%M:%S') + end + + it "should be able to show HH:MM:SS" do + @d2000_4_6.strftime("%X").should == "00:00:00" + @d2000_4_6.strftime("%X").should == @d2000_4_6.strftime('%H:%M:%S') + end + + it "should be able to show MM/DD/YY" do + @d2000_4_6.strftime("%x").should == "04/06/00" + @d2000_4_6.strftime("%x").should == @d2000_4_6.strftime('%m/%d/%y') + end + + # GNU modificators + it "supports GNU modificators" do + time = @new_date[2001, 2, 3] + + time.strftime('%^h').should == 'FEB' + time.strftime('%^_5h').should == ' FEB' + time.strftime('%0^5h').should == '00FEB' + time.strftime('%04m').should == '0002' + time.strftime('%0-^5h').should == 'FEB' + time.strftime('%_-^5h').should == 'FEB' + time.strftime('%^ha').should == 'FEBa' + + time.strftime("%10h").should == ' Feb' + time.strftime("%^10h").should == ' FEB' + time.strftime("%_10h").should == ' Feb' + time.strftime("%_010h").should == '0000000Feb' + time.strftime("%0_10h").should == ' Feb' + time.strftime("%0_-10h").should == 'Feb' + time.strftime("%0-_10h").should == 'Feb' + end + + it "supports the '-' modifier to drop leading zeros" do + @new_date[2001,3,22].strftime("%-m/%-d/%-y").should == "3/22/1" + end + + with_feature :encoding do + it "passes the format string's encoding to the result string" do + result = @new_date[2010,3,8].strftime("%d. März %Y") + + result.encoding.should == Encoding::UTF_8 + result.should == "08. März 2010" + end + end +end diff --git a/spec/rubyspec/shared/time/strftime_for_time.rb b/spec/rubyspec/shared/time/strftime_for_time.rb new file mode 100644 index 0000000000..49cd09051a --- /dev/null +++ b/spec/rubyspec/shared/time/strftime_for_time.rb @@ -0,0 +1,173 @@ +# Shared for Time and DateTime, testing only time components (hours, minutes, seconds and smaller) + +describe :strftime_time, shared: true do + before :all do + @time = @new_time[2001, 2, 3, 4, 5, 6] + end + + it "formats time according to the directives in the given format string" do + @new_time[1970, 1, 1].strftime("There is %M minutes in epoch").should == "There is 00 minutes in epoch" + end + + # Per conversion specifier, not combining + it "returns the 24-based hour with %H" do + time = @new_time[2009, 9, 18, 18, 0, 0] + time.strftime('%H').should == '18' + end + + it "returns the 12-based hour with %I" do + time = @new_time[2009, 9, 18, 18, 0, 0] + time.strftime('%I').should == '06' + end + + it "supports 24-hr formatting with %l" do + time = @new_time[2004, 8, 26, 22, 38, 3] + time.strftime("%k").should == "22" + morning_time = @new_time[2004, 8, 26, 6, 38, 3] + morning_time.strftime("%k").should == " 6" + end + + describe "with %L" do + it "formats the milliseconds of the second" do + @new_time[2009, 1, 1, 0, 0, Rational(100, 1000)].strftime("%L").should == "100" + @new_time[2009, 1, 1, 0, 0, Rational(10, 1000)].strftime("%L").should == "010" + @new_time[2009, 1, 1, 0, 0, Rational(1, 1000)].strftime("%L").should == "001" + @new_time[2009, 1, 1, 0, 0, Rational(1, 10000)].strftime("%L").should == "000" + end + end + + it "supports 12-hr formatting with %l" do + time = @new_time[2004, 8, 26, 22, 38, 3] + time.strftime('%l').should == '10' + morning_time = @new_time[2004, 8, 26, 6, 38, 3] + morning_time.strftime('%l').should == ' 6' + end + + it "returns the minute with %M" do + time = @new_time[2009, 9, 18, 12, 6, 0] + time.strftime('%M').should == '06' + end + + describe "with %N" do + it "formats the nanoseconds of the second with %N" do + @new_time[2000, 4, 6, 0, 0, Rational(1234560, 1_000_000_000)].strftime("%N").should == "001234560" + end + + it "formats the milliseconds of the second with %3N" do + @new_time[2000, 4, 6, 0, 0, Rational(50, 1000)].strftime("%3N").should == "050" + end + + it "formats the microseconds of the second with %6N" do + @new_time[2000, 4, 6, 0, 0, Rational(42, 1000)].strftime("%6N").should == "042000" + end + + it "formats the nanoseconds of the second with %9N" do + @new_time[2000, 4, 6, 0, 0, Rational(1234, 1_000_000)].strftime("%9N").should == "001234000" + end + + it "formats the picoseconds of the second with %12N" do + @new_time[2000, 4, 6, 0, 0, Rational(999999999999, 1000_000_000_000)].strftime("%12N").should == "999999999999" + end + end + + it "supports am/pm formatting with %P" do + time = @new_time[2004, 8, 26, 22, 38, 3] + time.strftime('%P').should == 'pm' + time = @new_time[2004, 8, 26, 11, 38, 3] + time.strftime('%P').should == 'am' + end + + it "supports AM/PM formatting with %p" do + time = @new_time[2004, 8, 26, 22, 38, 3] + time.strftime('%p').should == 'PM' + time = @new_time[2004, 8, 26, 11, 38, 3] + time.strftime('%p').should == 'AM' + end + + it "returns the second with %S" do + time = @new_time[2009, 9, 18, 12, 0, 6] + time.strftime('%S').should == '06' + end + + it "should be able to show the number of seconds since the unix epoch" do + @new_time_in_zone["GMT", 0, 2005].strftime("%s").should == "1104537600" + end + + it "returns the timezone with %Z" do + time = @new_time[2009, 9, 18, 12, 0, 0] + zone = time.zone + time.strftime("%Z").should == zone + end + + describe "with %z" do + it "formats a UTC time offset as '+0000'" do + @new_time_in_zone["GMT", 0, 2005].strftime("%z").should == "+0000" + end + + it "formats a local time with positive UTC offset as '+HHMM'" do + @new_time_in_zone["CET", 1, 2005].strftime("%z").should == "+0100" + end + + it "formats a local time with negative UTC offset as '-HHMM'" do + @new_time_in_zone["PST", -8, 2005].strftime("%z").should == "-0800" + end + + it "formats a time with fixed positive offset as '+HHMM'" do + @new_time_with_offset[2012, 1, 1, 0, 0, 0, 3660].strftime("%z").should == "+0101" + end + + it "formats a time with fixed negative offset as '-HHMM'" do + @new_time_with_offset[2012, 1, 1, 0, 0, 0, -3660].strftime("%z").should == "-0101" + end + + it "formats a time with fixed offset as '+/-HH:MM' with ':' specifier" do + @new_time_with_offset[2012, 1, 1, 0, 0, 0, 3660].strftime("%:z").should == "+01:01" + end + + it "formats a time with fixed offset as '+/-HH:MM:SS' with '::' specifier" do + @new_time_with_offset[2012, 1, 1, 0, 0, 0, 3665].strftime("%::z").should == "+01:01:05" + end + end + + # Combining conversion specifiers + it "should be able to print the time in full" do + @time.strftime("%c").should == "Sat Feb 3 04:05:06 2001" + @time.strftime("%c").should == @time.strftime('%a %b %e %H:%M:%S %Y') + end + + it "should be able to show HH:MM" do + @time.strftime("%R").should == "04:05" + @time.strftime("%R").should == @time.strftime('%H:%M') + end + + it "should be able to show HH:MM:SS AM/PM" do + @time.strftime("%r").should == "04:05:06 AM" + @time.strftime("%r").should == @time.strftime('%I:%M:%S %p') + end + + it "supports HH:MM:SS formatting with %T" do + @time.strftime('%T').should == '04:05:06' + @time.strftime('%T').should == @time.strftime('%H:%M:%S') + end + + it "supports HH:MM:SS formatting with %X" do + @time.strftime('%X').should == '04:05:06' + @time.strftime('%X').should == @time.strftime('%H:%M:%S') + end + + # GNU modificators + it "supports the '-' modifier to drop leading zeros" do + time = @new_time[2001,1,1,14,01,42] + time.strftime("%-m/%-d/%-y %-I:%-M %p").should == "1/1/1 2:1 PM" + + time = @new_time[2010,10,10,12,10,42] + time.strftime("%-m/%-d/%-y %-I:%-M %p").should == "10/10/10 12:10 PM" + end + + it "supports the '-' modifier for padded format directives" do + time = @new_time[2010, 8, 8, 8, 10, 42] + time.strftime("%-e").should == "8" + time.strftime("%-k%p").should == "8AM" + time.strftime("%-l%p").should == "8AM" + end +end diff --git a/spec/rubyspec/spec_helper.rb b/spec/rubyspec/spec_helper.rb new file mode 100644 index 0000000000..c275e320ec --- /dev/null +++ b/spec/rubyspec/spec_helper.rb @@ -0,0 +1,18 @@ +use_realpath = File.respond_to?(:realpath) +root = File.dirname(__FILE__) +dir = "fixtures/code" +CODE_LOADING_DIR = use_realpath ? File.realpath(dir, root) : File.expand_path(dir, root) + +# Running directly with ruby some_spec.rb +unless ENV['MSPEC_RUNNER'] + begin + require 'mspec' + require 'mspec/commands/mspec-run' + rescue LoadError + puts "Please add -Ipath/to/mspec/lib or install the MSpec gem to run the specs." + exit 1 + end + + ARGV.unshift $0 + MSpecRun.main +end -- cgit v1.2.3